🚗💻进京证自动续签神器!Node版实现,Scriptable开发iPhone小组件助你轻松搞定!📱✨

avatar

node版自动续签进京证

适用场景

代码极其简单,主要是提供思路,需要使用的同学请具备以下条件:

  • 有办理进京证需求(避免公共资源浪费)
  • 会执行node代码,另外会定时任务更好(有node环境会执行即可)
  • 手机APP抓包(接口地址跟参数隐藏了,需要自己抓包,不会也没关系)

此版本用于6环外无限自动续签。

注释:jjzzl 01 6环内进京证 02 6环外进京证

6环内建议手动修改,手动执行,毕竟次数宝贵

注释:jjzzl 01 6环内进京证 02 6环外进京证

代码&运行部署

node版脚本代码,可以使用青龙面板定时运行脚本任务,或者可以自己电脑安装个定时跑脚本的工具,也可以手动运行。

  1. 自行复制提取,关键信息自己填一下
  2. header上的Authorization需要自己登录抓包拿到
  3. 其他信息都已预置

源码

// 这里使用的青龙面板引入依赖了axios,纯净版可自行XMLHttpRequest
const axios = require('axios');

function jjz() {
    // 初始地址,防止恶意访问,请求地址不提供,需要的自行抓包
    const url = "jjz.jtgl.beijing.gov.cn";
    // 查询状态接口
    const self = {};
    self.state_url = `https://${url}/pro/applyRecordController/stateList`;
    // 办理续签接口
    self.inster_apply_record_url = `https://${url}/pro/applyRecordController/insertApplyRecord`;
    // 访问凭证,通过抓包在请求头信息 Authorization 字段
    self.auth = "";
    // 续签接口数据只展示部分说明,其他的需要自行抓包研究
    // 车主姓名
    const user_name = "";
    // 车主身份证号
    const id_num = "";
    // 车牌号
    const plate_num = "";
    // 进京地址
    const address = "";
    const tomorrow = new Date(new Date().getTime() + 24*60*60*1000).toISOString().slice(0, 10);
    // jjzzl 01 6环内进京证 02 6环外进京证
    const payload = {
        "sqdzgdjd":"116.416427",
        "txrxx":[],
        "hpzl":"02",
        "applyIdOld":"",
        "sqdzgdwd":"40.038301",
        "jjmdmc":"自驾旅游,其它",
        "jsrxm" : user_name,
        "jszh" : id_num,
        "jjdq":"011",
        "jjmd":"01,06",
        "sqdzbdjd":"116.422833",
        "sqdzbdwd":"40.044525",
        "jjzzl":"02",
        "jjlk":"00401",
        "hphm" : plate_num,
        "vId":"",
        "jjrq" : tomorrow,
        "jjlkmc":"京藏高速",
        "xxdz" : address
    };
    const headers = {
        "Authorization": self.auth,
        "Content-Type": "application/json"
    };

    self.getRemainingTime = async function(self) {
        
        const resp = await axios.post(self.state_url, {}, {
            headers: headers
        });
        const state_result_json = resp.data;
        const status_code = state_result_json["code"];
        const msg = state_result_json["msg"];
        let current_state = '';
        let validity_period = '';
        let apply_id_old = '';
        if (status_code != 200) {
            console.log(`状态码不等于200,接口异常,状态码:${status_code},错误信息:${msg}`);
        }
        // 已经申请续签后,会有字段的数据显示审核通过(待生效)、以及最新的截止日期
        if (state_result_json.data.bzclxx[0]["ecbzxx"].length > 0) {
            current_state = state_result_json["data"]["bzclxx"][0]["ecbzxx"][0]["blztmc"];
            validity_period = state_result_json["data"]["bzclxx"][0]["ecbzxx"][0]["yxqz"];
        }
        else {
            current_state = state_result_json["data"]["bzclxx"][0]["bzxx"][0]["blztmc"];
            validity_period = state_result_json["data"]["bzclxx"][0]["bzxx"][0]["yxqz"];
        }
        apply_id_old = state_result_json["data"]["bzclxx"][0]["bzxx"][0]["applyId"]
        return { current_state, validity_period, apply_id_old }
    };

    self.autoRenew = async function(self, payload) {
        const resp = await axios.post(self.inster_apply_record_url, payload, {
            headers: headers
        });
        const renew_result_json = resp.data 
        status_code = renew_result_json["code"];
        msg = renew_result_json["msg"];
        console.log(`续签接口状态码:${status_code}`);
        console.log(`续签接口响应消息:${msg}`);
    }
    async function main(self, payload) {
        const { current_state, validity_period, apply_id_old } = await self.getRemainingTime(self);
        payload["applyIdOld"] = apply_id_old;
        payload = JSON.parse(JSON.stringify(payload));
        console.log(current_state)
        console.log(validity_period)
        if (current_state == "审核通过(生效中)") {
            today = new Date().toISOString().slice(0, 10);
            if (validity_period == today) {
                console.log("剩余时间小于1天,执行续签");
                self.autoRenew(self, payload);
            } else {
                console.log(`剩余时间大于1天,无法续签,到期时间:${validity_period}`);
            }
        } else if (current_state == "审核通过(待生效)"){
            console.log("审核通过(待生效),无需重新申请");
        } else {
            self.autoRenew(self, payload);
        }
    }
    main(self, payload)
}
jjz();

Scriptable实现iPhone小组件(进京证信息小组件)

效果预览图

未做样式优化,可自行美化,具体API详细见文档

使用步骤:

  • iphone下载scriptable,appstore下载即可
  • 进入软件后,右上角➕增加下面的源码,运行即可
  • 手机桌面上添加scriptable组件,长按编辑组件选择你新增的代码的组件即可
  • 具体API可参考官方文档

官方文档:docs.scriptable.app/request/ image.png

Scriptable版本源码

源码自行copy,Authorization自行抓包获取

// 背景图片,可自定义
const imgUrl = ''
const widgetUrl = "https://gpt.yqdsw.top/#/ai/home" // 点击小组件跳转URL
const req = await new Request(imgUrl)
// 全局字体颜色 
const fontColor = Color.white()
const dummyFont = Font.boldRoundedSystemFont(14)
const daysTextFONT = Font.boldRoundedSystemFont(16) //new Font("Chalkduster", 65)
const img = await req.loadImage()
const w = await createWidget()
const widget = w
// 设置边距(上,左,下,右)
// 上下间距
const padding = { top: 0, left: 0, bottom: 0, right: 0 }
widget.setPadding(padding.top, padding.left, padding.bottom, padding.right)
Script.setWidget(widget)
Script.complete()
w.presentMedium()

async function createWidget() {
    let daysText, dummyText, row, row2

    const widget = new ListWidget()
    widget.url = widgetUrl;
    widget.backgroundImage = img;
    const url = "jjz.jtgl.beijing.gov.cn";
    const result = await get({ url: `https://${url}/pro/applyRecordController/stateList` })
    const state_result_json = result
    let current_state = ''
    let title = ''
    let validity_period = ''
    // 已经申请续签后,会有字段的数据显示审核通过(待生效)、以及最新的截止日期
    if (state_result_json.data.bzclxx[0]["ecbzxx"].length > 0) {
        current_state = state_result_json["data"]["bzclxx"][0]["ecbzxx"][0]["blztmc"];
        title = state_result_json["data"]["bzclxx"][0]["ecbzxx"][0]["jjzzlmc"];
        validity_period = state_result_json["data"]["bzclxx"][0]["ecbzxx"][0]["yxqs"] + '到' + state_result_json["data"]["bzclxx"][0]["ecbzxx"][0]["yxqz"];
    }
    else {
        current_state = state_result_json["data"]["bzclxx"][0]["bzxx"][0]["blztmc"];
        title = state_result_json["data"]["bzclxx"][0]["bzxx"][0]["jjzzlmc"];
        validity_period = state_result_json["data"]["bzclxx"][0]["bzxx"][0]["yxqs"] + '到' + state_result_json["data"]["bzclxx"][0]["bzxx"][0]["yxqz"];
    }

    widget.addSpacer(10)

    row = widget.addStack()
    row.layoutHorizontally()
    row.centerAlignContent()
    row.size = new Size(190, 20)

    row.addSpacer()

    daysText = row.addText(title)
    daysText.textColor = fontColor
    daysText.font = daysTextFONT

    row.addSpacer()

    row1 = widget.addStack()
    row1.layoutHorizontally()
    row1.centerAlignContent()
    row1.size = new Size(190, 20)

    row1.addSpacer()

    daysText = row1.addText(current_state)
    daysText.textColor = fontColor
    daysText.font = daysTextFONT

    row1.addSpacer()

    row2 = widget.addStack()
    row2.layoutHorizontally()
    row2.centerAlignContent()
    row2.size = new Size(240, 20)

    row2.addSpacer()

    dummyText = row2.addText(validity_period)
    dummyText.textColor = fontColor
    dummyText.font = dummyFont

    row2.addSpacer()

    return widget
}

async function get(opts) {
  const request = new Request(opts.url)
  request.method = 'POST'
  // Authorization自行抓包获取
  request.headers = {
        "Authorization": "",
        "Content-Type": "application/json"
    }
  request.body = {}
  var result = await request.loadJSON()
  console.log(result)
  return result
}


后续

想手机上执行代码的话

可将上面node版本的调用续签的代码,自行改写成scriptable new request请求的方式,其他语法基本都一致。上面的代码只获取了信息。

参考链接

python版本的参考文章:zhuanlan.zhihu.com/p/631854961

Scriptable 脚本合集: github.com/Nicolasking…