<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      使用 shell 腳本自動申請進京證 (六環外)

      問題背景

      外地車輛進入北京,需要辦理《進京證》,不辦理證件駛入后會被執法設備抓拍,一次罰 100 扣 1 分,目前唯一的線上辦理通道是下載《北京交警》App,注冊后添加車輛,就可以為自己的愛車隨時隨地辦理進京證了。注意如果有違法記錄,需要繳納罰款后才可以辦理,繳納罰款的線上辦理通道是《交管12123》。

      最早的時候,進京證只限制進五環的外地車輛,一年可以辦理 12 次,一次 7 天。后來擴大范圍到了六環,剛開始監控設備沒健全還能進一下,目前是不行了,基本一進六環就會被拍。那六環外是不是隨便跑呢?也不是的,像一些遠郊縣城如昌平、延慶、順義、密云、懷柔、平谷,雖然地處六環之外,但是縣城城區也有一個環,凡進此環者,也要辦理進京證,對我這種混跡在縣城周邊的外地車主就非常不友好了,周末去縣城里的購物中心、電影院也得辦理進京證。也許是傾聽了民意,后來改革了,進京證變為六環內與六環外兩種:六環內還是一年 12 次,每次 7 天;六環外是一次 7 天但不限次數。一個例外是通州,雖然在六環外,也得按六環內辦理,畢竟是城市副中心嘛!

      雖然不限次數了,但也不是隨意辦理的,主要有以下規則:

      • 有未繳納罰款不能辦理
      • 中午 12 點后不能申請當天進京證
      • 可以提前申請,最多提前 3 天
      • 一個帳戶可以掛多輛車,但只能為其中一輛車辦理
      • 已辦理六環外進京證,可以續辦六環內進京證,成功后六環外進京證失效;反之不行,也沒必要,因為持有六環內進京證可以跑六環外
      • 如果申請的不是當天的進京證,可以取消,但一天內只有一次取消機會,用完就不能再取消了
      • 進京證在最后一天有效期時可以辦理新的進京證,也稱續期
      • 特殊時期不能辦理,比如疫情管控期間 (已翻篇,只是舉例)

      線上辦理速度還是比較快的,基本一分鐘左右就能出結果。但對我這種健忘癥患者,往往臨出門才想起來要辦進京證,一看時間,已經過中午 12 點了,就比較尷尬了。

      這個時候就想,有一個定時辦理進京證的工具就好了,例如每周一凌晨自動辦理這一周的進京證。

      解決方案

      需求搞明白了,下面著手實現。

      抓包

      首先要搞明白服務端的接口,需要通過抓包對《北京交警》進行報文分析。

      之前抓包一直是用 mac 上的 Charles,后來偶然看到有個 VNET App,不需要安裝證書、不需要啟動代理就能抓包,拿來試了下,果然好用:

      注意登錄過程不要開啟抓包,否則《北京交警》的登錄界面會調不出來。除此之外,其它請求基本上都能抓到,從中挑選了兩個我們需要關注的接口。

      獲取當前進京證

      這個 stateList 接口可以獲取用戶賬戶下所有車輛的進京證信息,有些字段對于下一步申請進京證是必需的,另外了解當前進京證狀態也有利于決定是否申請新的進京證。

      header

      > POST /pro//applyRecordController/stateList HTTP/1.1
      > Host:jjz.jtgl.beijing.gov.cn
      > Accept: */*
      > Accept-Language:zh-CN,zh;q=0.8
      > User-Agent:okhttp-okgo/jeasonlzy
      > source:8724a2428c3f47358741f978fd082810
      > authorization:f36abdfa-8878-46bf-91d9-5666f808e9a4
      > Content-Type:application/json;charset=utf-8
      > Connection:Keep-Alive
      > Accept-Encoding:gzip
      > Content-Length:97

      比較重要的是 source 和 authorization 字段,前者標識了設備,后者標識了用戶 token,也是主要的抓取對象之一,會用于之后的請求中。

      data

      {
        "v": "3.4.1",
        "sfzmhm": "150121198603226428",
        "s-source": "bjjj-android",
        "timestamp": "1676016273000"
      }

      請求時發送的 json 數據,親測沒什么用,發個空也能得到響應,看起來身份信息通過 http 頭的 authorization 字段獲取的。

      響應

      查看代碼
      {
        "code": 200,
        "msg": "用戶辦證信息查詢成功!",
        "data": {
          "sfzmhm": "150121198603226428",
          "ylzqyms": "市界到二環 (不含二環路)+客車全年可辦理12次,每次限通行7天",
          "ylzmc": "進京證(六環內)",
          "elzqyms": "市界到六環 (含六環路、不含通州全域)+客車全年不限辦理次數,每次限通行7天",
          "elzmc": "進京證(六環外)",
          "bzclxx": [
            {
              "vId": "1479816562371952600",
              "hpzl": "52",
              "hphm": "津ADY1951",
              "ybcs": 0,
              "bzts": 0,
              "kjts": 0,
              "sycs": "12",
              "syts": "84",
              "ylzsfkb": true,
              "elzsfkb": true,
              "bnbzyy": "審核未通過,該車輛存在有效或待生效的進京證,請不要重復申請。",
              "qyzt": 1,
              "cllx": "01",
              "bzxx": [
                {
                  "vId": "1479816562371952600",
                  "applyId": "827854158244610048",
                  "blzt": 4,
                  "blztmc": "失敗(審核不通過)",
                  "sxrqmc": "02月02日",
                  "yxqs": "2023-02-02",
                  "yxqz": null,
                  "sxsyts": null,
                  "jjzzl": "02",
                  "jjzzlmc": "進京證(六環外)",
                  "jjzh": null,
                  "sqsj": "2023-02-02 10:36:52",
                  "jsrxm": "云海",
                  "jszh": "150121198603226428",
                  "sfzmhm": null,
                  "shsbyy": "10037",
                  "shsbyyms": "審核未通過,該車輛存在有效或待生效的進京證,請不要重復申請。",
                  "tphtml": null,
                  "hphm": "津ADY1951",
                  "hpzl": "52",
                  "vid": 1479816562371952600
                }
              ],
              "ecbzxx": [],
              "sfyecbzxx": false,
              "ecztbz": null
            },
            {
              "vId": "664980198424313900",
              "hpzl": "02",
              "hphm": "蒙AC728G",
              "ybcs": 0,
              "bzts": 0,
              "kjts": 0,
              "sycs": "12",
              "syts": "84",
              "ylzsfkb": true,
              "elzsfkb": true,
              "bnbzyy": null,
              "qyzt": 1,
              "cllx": "01",
              "bzxx": [
                {
                  "vId": "664980198424313900",
                  "applyId": "830460363400019969",
                  "blzt": 2,
                  "blztmc": "已取消",
                  "sxrqmc": "02月12日",
                  "yxqs": "2023-02-12",
                  "yxqz": "2023-02-18",
                  "sxsyts": null,
                  "jjzzl": "02",
                  "jjzzlmc": "進京證(六環外)",
                  "jjzh": "A23020958343645",
                  "sqsj": "2023-02-09 15:12:59",
                  "jsrxm": "云海",
                  "jszh": "150121198603226428",
                  "sfzmhm": null,
                  "shsbyy": null,
                  "shsbyyms": null,
                  "tphtml": null,
                  "hphm": "蒙AC728G",
                  "hpzl": "02",
                  "vid": 664980198424313900
                }
              ],
              "ecbzxx": [],
              "sfyecbzxx": false,
              "ecztbz": null
            }
          ]
        },
        "from": "v2"
      }

      返回的 json 比較大,基本按 data->bzclxx[]->bzxx[] 的結構組織,其中 data 存儲用戶信息;bzclxx 是車輛數組,存儲與車相關的信息;bzxx 是進京證數組,存儲與證相關的信息。

      下面對重點的字段個簡單說明:

      • data.sfzmhm:賬戶所有者身份證號
      • bzclxx[].vId:車輛唯一標識,后面申請時會用到
      • bzclxx[].hpzl:未知,后面申請時會用到
      • bzclxx[].hphm:車輛號牌,后面申請時會用到
      • bzclxx[].bzxx[].blztmc:進京證狀態,包括但不限于:
        • 審核通過(生效中)
        • 審核通過(待生效)
        • 審核通過(已失效)
        • 審核通過(已作廢)
        • 審核中
        • 失敗(審核不通過)
        • 取消辦理中
        • 已取消
        • ......
      • bzclxx[].bzxx[].jsrxm:駕駛者姓名,可以與賬戶所有者不同
      • bzclxx[].bzxx[].jszh:駕駛者身份證,必需與駕駛者姓名一致 (即查有此人)
      • bzclxx[].bzxx[].jjzzlmc:進京證類型,主要有:
        • 進京證(六環內)
        • 進京證(六環外)
      • bzclxx[].bzxx[].yxqs:進京證首次生效日期
      • bzclxx[].bzxx[].yxqz:進京證最后生效日期
      • bzclxx[].bzxx[].sxsyts:進京證剩余有效期,單位天

      其中后三個字段僅在某些狀態下才會有,例如 yxqs 和  yxqz 在生效中、待生效、已取消狀態有效,szsyts 在生效中、待生效狀態有效,無效值就是 null。如果車輛壓根沒有進京證,整個 bzxx[] 就是 null。

      理論上一個車輛最多只能有一個進京證存在,或者六環內,或者六環外,但在某些場景下,兩個證可以短暫的同時存在,這是我一開始認為 bzxx 要以數組形式存在的原因。這種場景主要分為兩種

      • 在有六環外進京證的前提下,辦理成功六環內進京證,此時六環外進京證會自動作廢,兩者會有短暫的共存期,過后就只保留生效的六環內進京證了
      • 在有進京證的前提下,如果進京證是最后一天有效期,此時續辦同樣類型的進京證,新的證件將處在待生效狀態,此時兩者會有一天的共存期,一天后舊的證件失效,新的證件生效

      下面看第一種場景下一個實際的例子:

      查看代碼
      {
        "code": 200,
        "msg": "用戶辦證信息查詢成功!",
        "data": {
          "sfzmhm": "150121198603226428",
          "ylzqyms": "市界到二環 (不含二環路)+客車全年可辦理12次,每次限通行7天",
          "ylzmc": "進京證(六環內)",
          "elzqyms": "市界到六環 (含六環路、不含通州全域)+客車全年不限辦理次數,每次限通行7天",
          "elzmc": "進京證(六環外)",
          "bzclxx": [
            {
              "vId": "1479816562371952642",
              "hpzl": "52",
              "hphm": "津ADY1951",
              "ybcs": 0,
              "bzts": 0,
              "kjts": 0,
              "sycs": "12",
              "syts": "84",
              "ylzsfkb": false,
              "elzsfkb": false,
              "bnbzyy": "每個用戶同一時間只能為一輛機動車申請辦理進京證。",
              "qyzt": 1,
              "cllx": "01",
              "bzxx": [
                {
                  "vId": "1479816562371952600",
                  "applyId": "827854158244610048",
                  "blzt": 4,
                  "blztmc": "失敗(審核不通過)",
                  "sxrqmc": "02月02日",
                  "yxqs": "2023-02-02",
                  "yxqz": null,
                  "sxsyts": null,
                  "jjzzl": "02",
                  "jjzzlmc": "進京證(六環外)",
                  "jjzh": null,
                  "sqsj": "2023-02-02 10:36:52",
                  "jsrxm": "云海",
                  "jszh": "150121198603226428",
                  "sfzmhm": null,
                  "shsbyy": "10037",
                  "shsbyyms": "審核未通過,該車輛存在有效或待生效的進京證,請不要重復申請。",
                  "tphtml": null,
                  "hphm": "津ADY1951",
                  "hpzl": "52",
                  "vid": 1479816562371952600
                }
              ],
              "ecbzxx": [],
              "sfyecbzxx": false,
              "ecztbz": null
            },
            {
              "vId": "664980198424313900",
              "hpzl": "02",
              "hphm": "蒙AC728G",
              "ybcs": 1,
              "bzts": 7,
              "kjts": 0,
              "sycs": "11",
              "syts": "77",
              "ylzsfkb": false,
              "elzsfkb": false,
              "bnbzyy": null,
              "qyzt": 1,
              "cllx": "01",
              "bzxx": [
                {
                  "vId": "664980198424313900",
                  "applyId": "835681535133745153",
                  "blzt": 1,
                  "blztmc": "審核通過(生效中)",
                  "sxrqmc": "02月25日",
                  "yxqs": "2023-02-25",
                  "yxqz": "2023-03-03",
                  "sxsyts": 1,
                  "jjzzl": "02",
                  "jjzzlmc": "進京證(六環外)",
                  "jjzh": "A23022460625295",
                  "sqsj": "2023-02-24 01:00:04",
                  "jsrxm": "云海",
                  "jszh": "150121198603226428",
                  "sfzmhm": null,
                  "shsbyy": null,
                  "shsbyyms": null,
                  "tphtml": "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",
                  "hphm": "蒙AC728G",
                  "hpzl": "02",
                  "vid": 664980198424313900
                }
              ],
              "ecbzxx": [
                {
                  "vId": "664980198424313900",
                  "applyId": "838218246271270912",
                  "blzt": 6,
                  "blztmc": "審核通過(待生效)",
                  "sxrqmc": "03月04日",
                  "yxqs": "2023-03-04",
                  "yxqz": "2023-03-10",
                  "sxsyts": null,
                  "jjzzl": "02",
                  "jjzzlmc": "進京證(六環外)",
                  "jjzh": "A23030361735625",
                  "sqsj": "2023-03-03 01:00:03",
                  "jsrxm": "云海",
                  "jszh": "150121198603226428",
                  "sfzmhm": null,
                  "shsbyy": null,
                  "shsbyyms": null,
                  "tphtml": null,
                  "hphm": "蒙AC728G",
                  "hpzl": "02",
                  "vid": 664980198424313900
                }
              ],
              "sfyecbzxx": true,
              "ecztbz": "1"
            }
          ]
        },
        "from": "v2"
      }

      可以看到待生效的進京證實際上是放在了和 bzxx 同級的 ecbzxx 中,而不是放在 bzxx 數組中,可見之前的猜測是錯誤的,雖然 bzxx 和 ecbzxx 都被設計為 json 數組,實際上它們最多只有一個元素,如果沒有對應的信息,保持 null。

      說句題外話,政務的接口特別鐘意用拼音縮寫為字段起名,光看字面意思費半天勁兒也猜不出,必需得結合取值情況,取值是字符串的還好些,遇上那種整數枚舉,根本就猜不透。不過換個角度看,起到了混淆加固的作用,哈哈。

      申請新的進京證

      下面進入正題:這個 insertApplyRecord 接口用來申請進京證。

      header

      從抓包數據看,http 頭和 stateList 請求完全一樣,參考上一節。

      data

      {
        "dabh": "null",
        "hphm": "津ADY1951",
        "hpzl": "52",
        "vId": "1479816562371952600",
        "jjdq": "海淀區",
        "jjlk": "00401",
        "jjlkmc": "京藏高速",
        "jjmd": "01",
        "jjmdmc": "自駕旅游",
        "jjrq": "2023-02-13",
        "jjzzl": "02",
        "jsrxm": "云海",
        "jszh": "150121198603226428",
        "sfzmhm": "150121198603226428",
        "xxdz": "百度大廈",
        "sqdzbdjd": 116.307393,
        "sqdzbdwd": 40.057771
      }

      這里的請求數據就不是可有可無的了,其中比較關鍵的是 hphm / hpzl / vId / jjrq / jsrxm / jszh / sfzmhm 等幾個字段,大部分在前一節介紹過了,新出現的只有 jjrq 一個,表示辦理進京證的起始時間。

      響應

      {
        "code": 200,
        "msg": "信息已提交,正在審核!",
        "data": [
          "溫馨提示",
          "1、請務必在進京之前,查看進京通行證是否審核通過;",
          "2、若審核未通過,請按提示信息調整并重新申請;",
          "3、若審核通過,可在證件生效之前申請取消,每位注冊用戶每天僅有1次取消機會;",
          "4、在進京通行證未生效的情況下,外埠機動車禁止在限行區域內行駛;"
        ],
        "from": "v2"
      }

      成功時返回上面的結果,再次調用 stateList 接口,進京證狀態將變為審核中。下面列幾種常見的錯誤響應:

      {
        "code": 500,
        "msg": "審核未通過,申請時間已超過中午12時,無法申請當日生效的進京證。",
        "data": "20015",
        "from": "v2"
      }

      時間不正確,過了中午 12 點申請當天進京證,或者 jjrq 使用了一個過去的時間。

      {
        "code": 500,
        "msg": "每個用戶同一時間只能為一輛機動車申請辦理進京證。",
        "data": "20005",
        "from": "v2"
      }

      已辦理了名下另一輛車的進京證時不能繼續辦理。

      注意身份證不匹配、車輛進京證已被其它帳戶辦理等錯誤情況,并不會立刻返回,而是在一段時間后再次調用 stateList 接口才會返回。

      "bzxx": [
        {
          "vId": "1479816562371952600",
          "applyId": "831666307626696704",
          "blzt": 4,
          "blztmc": "失敗(審核不通過)",
          "sxrqmc": "02月13日",
          "yxqs": "2023-02-13",
          "yxqz": null,
          "sxsyts": null,
          "jjzzl": "02",
          "jjzzlmc": "進京證(六環外)",
          "jjzh": null,
          "sqsj": "2023-02-12 23:04:59",
          "jsrxm": "云海",
          "jszh": "150121198603226428",
          "sfzmhm": null,
          "shsbyy": "10014",
          "shsbyyms": "審核未通過,駕駛人信息不正確,請核實后再次提交或到進京證辦證窗口辦理。",
          "tphtml": null,
          "hphm": "津ADY1951",
          "hpzl": "52",
          "vid": 1479816562371952600
        }
      ]

      像上面這種就是因為身份證與名稱不匹配出錯。

      {
        "msg": "目前辦理業務人數較多,請稍后再試。",
        "code": 500
      }

      如果返回上面的信息,說明是程序有問題了,一般是 header 或 data 沒設置對。

      模擬申請

      報文摸清楚后就可以用 shell 腳本模擬了,下面是腳本代碼:

      查看代碼
       #! /bin/sh
      
      # @brief: is MacOS platform
      #         mac date a little different with other 
      # @retval: 0 - no
      # @retval: 1 - yes
      function is_macos()
      {
          local os="${OSTYPE/"darwin"http://}"
          if [ "$os" != "$OSTYPE" ]; then 
              # darwin: macos
              return 1
          else 
              return 0
          fi
      }
      
      is_macos
      IS_MAC=$?
      
      
      # @brief: check command existent before run our script
      #         useful especially in msys2 on windows
      # @param: cmd to check
      # @retval: 0 - exist
      # @retval: 1 - not exist
      function check_cmd()
      {
          local cmd=$1
          type "${cmd}" >/dev/null 2>&1
          if [ $? -ne 0 ]; then 
              echo "please install ${cmd} before run this script, fatal error!"
              exit -1
          else 
              echo "check ${cmd} ok"
          fi
      }
      
      # do environment check first
      check_cmd "jq"
      check_cmd "curl"
      check_cmd "head"
      check_cmd "cat"
      check_cmd "awk"
      check_cmd "grep"
      check_cmd "date"
      
      function main()
      {
          # constant
          local stateurl="https://jjz.jtgl.beijing.gov.cn/pro//applyRecordController/stateList"
          local issueurl="https://jjz.jtgl.beijing.gov.cn/pro//applyRecordController/insertApplyRecord"
          local agent="okhttp-okgo/jeasonlzy"
          local host="jjz.jtgl.beijing.gov.cn"
          local content="application/json;charset=utf-8"
          local lang="zh-CN,zh;q=0.8"
          
          # read config
          local userid=$(grep '^userid=' config.ini | head -1 | awk -F'=' '{print $2}')
          local vehicle=$(grep '^vehicle=' config.ini | head -1 | awk -F'=' '{print $2}')
          local auth=$(grep '^authorization=' config.ini | head -1 | awk -F'=' '{print $2}')
          local source=$(grep '^source=' config.ini | head -1 | awk -F'=' '{print $2}')
          local drivername=$(grep '^drivername=' config.ini | head -1 | awk -F'=' '{print $2}')
          local driverid=$(grep '^driverid=' config.ini | head -1 | awk -F'=' '{print $2}')
      
          # query current status
          # note: s-source should be quoted to prevent jq complain:
          # jq: error: syntax error, unexpected '-', expecting '}' (Unix shell quoting issues?) at <top-level>, line 1:
          local statereq=$(cat statereq.json | jq --arg sfzmhm "${userid}" --arg timestamp $(date "+%s000") -c '{ v, sfzmhm: $sfzmhm, "s-source", timestamp: $timestamp }')
          echo "state req: ${statereq}" 1>&2
          local stateheader #=() adb shell not support =() initialize an array..
          stateheader[0]="Accept-Language:${lang}"
          stateheader[1]="User-Agent:${agent}"
          stateheader[2]="source:${source}"
          stateheader[3]="authorization:${auth}"
          stateheader[4]="Content-Type:${content}"
          stateheader[5]="Host:${host}"
          stateheader[6]="Connection:Keep-Alive"
          stateheader[7]="Accept-Encoding:gzip"
          # prevent whole time be truncated to only date
          # add time alone here..
          # stateheader[10]="time:$(date '+%Y-%m-%d %H:%M:%S')"
          # local time="time:$(date '+%Y-%m-%d %H:%M:%S')"
          # for reuse, add content-length alone here..
          local length="Content-Length:${#statereq}"
          # size, what does it mean? seem to be optional..
          # stateheader[11]="size:459"
          local headers=""
          for var in "${stateheader[@]}"; 
          do
              headers="${headers} -H ${var}"
          done
          echo "state headers: ${headers} -H ${length}" 1>&2
          local resp=$(curl -s -k ${headers} -H ${length} -d "${statereq}" "${stateurl}")
          echo "${resp}" | jq  '.'  1>&2
          # for debug purpose
          # resp=$(cat demo.txt)
          local ret=$(echo "${resp}" | jq -r '.code')
          local msg=$(echo "${resp}" | jq -r '.msg')
          if [ -z "${ret}" -o "${ret}" = "null" -o ${ret} -ne 200 ]; then 
              echo "query permits status failed, code: ${ret}, msg: ${msg}"
              exit 1
          fi
      
          echo "query permits status ok: ${msg}"
      
          # check cardid 
          local id=$(echo "${resp}" | jq -r '.data.sfzmhm')
          if [ -z "${id}" -o "${id}" = "null" -o "${id}" != "${userid}" ]; then 
              echo "id [${id}] from user token does not match given [${userid}], fatal error!"
              exit 1
          fi
      
          local outside6ring="進京證(六環外)"
          # get permit type form response differs with above result: 進京證(六環外)
          # mainly different is the brackets..
          #
          # local outside6ring=$(echo "${resp}" | jq -r '.data.elzmc')
          # if [ -z "${outside6ring}" -o "${outside6ring}" = "null" -o "${outside6ring}" != "進京證(六環外)" ]; then 
          #     echo "permit type [${outside6ring}] incorrect, fatal error!"
          #     exit 1
          # fi
      
          # check if any permits there
          local vsize=$(echo "${resp}" | jq -r '.data.bzclxx|length')
          if [ -z "${vsize}" -o "${vsize}" = "null" -o ${vsize} -eq 0 ]; then 
              echo "no vehicle (${vsize}) under [${userid}], please add vehicle first!"
              exit 0
          fi
      
          local vehicles=$(echo "${resp}" | jq -r '.data.bzclxx[].hphm')
          local find=0
          local index=0
          # echo "${#vehicles}"
          for var in ${vehicles}
          do
              echo "try ${var} "
              if [ "${var}" = "${vehicle}" ]; then 
                  # match
                  find=1
                  break; 
              fi
              index=$((index+1))
          done
      
          if [ ${find} -eq 0 ]; then 
              # match reach end
              echo "no vehicle named <${vehicle}> under [${userid}], fatal error!"
              exit 1
          fi
      
          echo "find match vehicle <${vehicle}> at index: ${index}"
          # vehicle info needed later in permit issue request..
          local vid=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].vId")
          local hpzl=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].hpzl")
          if [ -z "${vid}" -o "${vid}" = "null" -o \
               -z "${hpzl}" -o "${hpzl}" = "null" ]; then 
              echo "some vehicle fields in state response null, fatal error!"
              exit 1
          fi
      
          local hour_now=$(date '+%H')
          local issuedate=$(date '+%Y-%m-%d')
          if [ ${hour_now} -ge 12 ]; then 
              # can NOT issue new permit for today if afternoon
              if [ ${IS_MAC} -eq 1 ]; then 
                  issuedate=$(date -v+1d '+%Y-%m-%d')
              else 
                  issuedate=$(date '+%Y-%m-%d' -d "+1 days")
              fi
          fi
      
          local psize=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].bzxx|length")
          # echo "psize: ${psize}"
          if [ -n "${psize}" -a "${psize}" != "null" -a ${psize} -gt 0 ]; then 
              # if have more than one permit, one of them must be inside 6th ring
              # in that case, we can not issue new permit with type outside 6th ring..
              if [ ${psize} -gt 1 ]; then 
                  echo "have more than 1 permits, can not issue new permit!"
                  exit 1
              fi
      
              # has permits, check if in effect
              local status=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].bzxx[0].blztmc")
              local man=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].bzxx[0].jsrxm")
              local card=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].bzxx[0].jszh")
              local type=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].bzxx[0].jjzzlmc")
              local v=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].bzxx[0].hphm")
              if [ -z "${status}" -o "${status}" = "null" -o \
                   -z "${man}" -o "${man}" = "null" -o \
                   -z "${card}" -o "${card}" = "null" -o \
                   -z "${type}" -o "${type}" = "null" -o \
                   -z "$v" -o "$v" = "null" -o "$v" != "${vehicle}" ]; then 
                   echo "some permit fields in state response null, fatal error!"
                   exit 1
              fi
      
              if [ "${type}" != "${outside6ring}" ]; then 
                  echo "have permits with type <${type}> != <${outside6ring}>, can not issue new permit!"
                  exit 1
              fi
          
              echo "${man} [${card}] issue permits on <${vehicle}> with type '${type}' status: ${status}"
              # status may 審核通過(生效中) or 審核通過(待生效) or 審核通過(已失效) or 審核中 or 失敗(審核不通過) or 取消辦理中 or 已取消
              #if [ "${status:0:4}" = "審核通過" ]; then 
              case ${status} in
                  審核通過*) 
                      local daybeg=$(echo "${resp}" | jq -r  ".data.bzclxx[${index}].bzxx[0].yxqs")
                      local dayend=$(echo "${resp}" | jq -r  ".data.bzclxx[${index}].bzxx[0].yxqz")
                      if [ -z "${daybeg}" -o "${daybeg}" = "null" -o \
                           -z "${dayend}" -o "${dayend}" = "null" ]; then 
                          echo "some permit fields(valid/invalid) in state response null, fatal error!"
                          exit 1
                      fi
          
                      if [ "${status}" = "審核通過(已失效)" ]; then 
                          # treate invalid permit as no permit
                          echo "invalid permit find under <${vehicle}>, try issue new.."
                      else 
                          local expire=$(echo "${resp}" | jq -r  ".data.bzclxx[${index}].bzxx[0].sxsyts")
                          if [ -z "${expire}" -o "${expire}" = "null" ]; then 
                              echo "some permit fields(valid) in state response null, fatal error!"
                              exit 1
                          fi
      
                          echo "in effect from ${daybeg} to ${dayend}"
                          # can issue new permits in last day
                          if [ ${expire} -gt 1 ]; then 
                              echo "still in effect, try ${expire} days later .."
                              exit 0
                          fi
      
                          # mac date performs differs with other unix..
                          if [ ${IS_MAC} -eq 1 ]; then 
                              issuedate=$(date "-v+${expire}d" '+%Y-%m-%d')
                          else 
                              issuedate=$(date '+%Y-%m-%d' -d "+${expire} days")
                          fi
                      fi
                      ;;
                  審核中)
                      echo "still in verify, try later.."
                      exit 0
                      ;;
                  取消辦理中)
                      echo "still in cancel progress, try later.."
                      exit 0
                      ;;
                  "失敗(審核不通過)")
                      echo "previous issue rejected, try new permit"
                      ;;
                  已取消)
                      echo "previous issue cancelled, try new permit"
                      ;;
                  *)
                      echo "unknown status ${status}, fatal error!"
                      exit 1
                      ;;
              esac
          else 
              local bnbzyy=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].bnbzyy")
              if [ "${bnbzyy}" = "每個用戶同一時間只能為一輛機動車申請辦理進京證。" ]; then 
                  echo "no permit(${psize}) under <${vehicle}>, but some permits under other vehicles exist.."
                  echo "can only issue new permit when no permits exists under [${userid}], do a check"
                  exit 1
              fi
      
              echo "no permit(${psize}) under <${vehicle}>, and no permits under [${userid}], try issue new.."
          fi
      
          # issue new permit request
          echo "new permit will start from ${issuedate}"
          local issuereq=$(cat issuereq.json | jq --arg hphm "${vehicle}" --arg hpzl "${hpzl}" --arg vid "${vid}" --arg jjrq "${issuedate}" --arg jsrxm "${drivername}" --arg jszh "${driverid}" --arg sfzmhm "${userid}" --arg timestamp $(date "+%s000") -c '{ dabh, hphm: $hphm, hpzl: $hpzl, vId: $vid, jjdq, jjlk, jjlkmc, jjmd, jjmdmc, jjrq: $jjrq, jjzzl, jsrxm: $jsrxm, jszh: $jszh, sfzmhm: $sfzmhm, xxdz, sqdzbdjd, sqdzbdwd }')
          echo "issue req: ${issuereq}" 1>&2
          # time="time:$(date '+%Y-%m-%d %H:%M:%S')"
          length="Content-Length:${#issuereq}"
          echo "issue headers: ${headers} -H ${length}" 1>&2
          resp=$(curl -s -k ${headers} -H ${length} -d "${issuereq}" "${issueurl}")
          echo "${resp}" | jq  '.'  1>&2
          # resp=$(cat demo.txt)
          ret=$(echo "${resp}" | jq -r '.code')
          msg=$(echo "${resp}" | jq -r '.msg')
          if [ -z "${ret}" -o "${ret}" = "null" -o ${ret} -ne 200 ]; then 
              echo "issue new permit failed, code: ${ret}, msg: ${msg}"
              exit 1
          fi
      
          echo "issue new permit status ok: ${msg}"
          exit 0
      }
      
      main "$@"

      完整的代碼放在了 github 上:

      https://github.com/goodpaperman/jinjing365

      代碼倉庫中除了腳本,還包含了配置文件 (config.ini) 與請求模板 (*.json)。

      腳本不到 300 行,不太難讀,這里就不逐行解說了,撿其中的幾個關鍵點說明一下

      jq

      因為要解析 json,jq 是必不可少的,如果你的系統上缺少它,執行腳本會報一行錯誤:

      please install jq before run this script, fatal error!

      其它用到的命令如 curl、awk 也都做了檢查,防止在一些特殊的場合下依賴缺失。

      jq 在這里主要有兩種用法,一種是解析響應內容;一種是生成請求內容。

      解析

      解析比較簡單了,例如想取 data.sfzmhm 字段,直接用一行代碼搞定:

      local cardid=$(echo "${resp}" | jq -r '.data.sfzmhm') 

      腳本中大量使用,其中 -r 選項可以除去字符串的雙引號。

      內置管道線

      需要注意的是 jq 支持內置管道線,在某些場景中會很有用,例如:

      local vsize=$(echo "${resp}" | jq -r '.data.bzclxx|length')
      local psize=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].bzxx|length")

      分別獲取車輛個數和某個車輛下的進京證個數,其中 length 是 jq 內置函數,可以放在內置管道線右側使用。

      注意第二個例子中,直接在 jq 語句中嵌入了 shell 變量,此時要使用雙引號而不是單引號,否則 shell 變量無法展開。

      如果需要獲取數組中所有的值,光使用內置管道線就不夠了:

      local vehicles=$(echo "${resp}" | jq -r '.data.bzclxx[].hphm')
      local find=0
      local index=0
      # echo "${#vehicles}"
      for var in ${vehicles}
      do
          echo "try ${var} "
          if [ "${var}" = "${vehicle}" ]; then 
              # match
              find=1
              break; 
          fi
          index=$((index+1))
      done

      上面的例子中獲取所有車輛的車牌號到 shell 變量 vehicles,然后用 for..in 進行遍歷,以尋找指定車牌的車輛索引。

      內置變量

      jq 的第二種用法是生成請求內容,這里主要使用了 jq 內置變量:

       local statereq=$(cat statereq.json | jq --arg sfzmhm "${userid}" --arg timestamp $(date "+%s000") -c '{ v, sfzmhm: $sfzmhm, "s-source", timestamp: $timestamp }')

      通過 --arg 傳遞變量名和它的值,例如 --arg sfzmhm "${userid}" 為 jq 生成了一個名為 sfzmhm 的變量,它的值是 shell 變量 userid。

      在后面的 jq 腳本中 (通過 -c 指定),就可以直接使用$sfzmhm 來引用這個變量啦,注意使用$前綴的才是 jq 變量,否則就是字面值,表示 json 的字段名。

      結合 statereq.json 的內容,看看這句代碼到底做了什么:

      {
        "v": "3.4.1",
        "sfzmhm": "",
        "s-source": "bjjj-android",
        "timestamp": ""
      }

      將 json 模板讀入,并對指定了值的字段 (sfzmhm/timestamp) 進行設置,指定了字段名沒指定值的 (v/s-source) 延用模板中的值,沒指定字段名的不會出現在最終結果。

      這樣替換的好處是全交給 jq 處理,避免手動構造的字符串不符合 json 語法。有幾點需要注意:

      • jq 中的變量不能在 jq 外使用
      • jq 中的變量只能用 $xxx 形式引用,${xxx} 引用不了
      • jq 中的字段名如果包含特殊符號 (如 s-source),在使用時需要加雙引號,否則 jq 會報錯

      內置變量 vs shell 變量

      有的讀者比較細心,可能會問了,“內置管道線”第二個例子中不是可以直接在 jq 中使用 shell 變量嗎,那能否在構造請求時也直接使用 shell 變量?當然可以,就拿上一節的例子來說,用下面的 shell 腳本代替也是沒問題的:

      # local statereq=$(cat statereq.json | jq --arg sfzmhm "${userid}" --arg timestamp $(date "+%s000") -c '{ v, sfzmhm: $sfzmhm, "s-source", timestamp: $timestamp }')
      
      local statereq="{\"v\":\"3.4.1\",\"sfzmhm\":\"${userid}\",\"s-source\":\"bjjj-android\",\"timestamp\":\"$(date +%s000)\"}"

      得到的內容是一樣的。

      可以看到,因為要包含 shell 變量,整個 json 字符串需要被雙引號包圍,而其中大量的 json 字段名本身就有雙引號,不得不使用反斜杠進行轉義,

      這樣一來手工修改工作會特別多,可讀性也比較差。可能這個例子還不怎么直觀,換 issuereq 的構造過程對比一下:

      # local issuereq=$(cat issuereq.json | jq --arg hphm "${vehicle}" --arg hpzl "${hpzl}" --arg vid "${vid}" --arg jjrq "${issuedate}" --arg jsrxm "${drivername}" --arg jszh "${driverid}" --arg sfzmhm "${userid}" --arg timestamp $(date "+%s000") --arg data "${statereq}" -c '{ dabh, hphm: $hphm, hpzl: $hpzl, vId: $vid, jjdq, jjlk, jjlkmc, jjmd, jjmdmc, jjrq: $jjrq, jjzzl, jsrxm: $jsrxm, jszh: $jszh, sfzmhm: $sfzmhm, xxdz, sqdzbdjd, sqdzbdwd}')
      
      local issuereq="{\"dabh\":\"null\",\"hphm\":\"${vehicle}\",\"hpzl\":\"${hpzl}\",\"vId\":\"${vid}\",\"jjdq\":\"海淀區\",\"jjlk\":\"00401\",\"jjlkmc\":\"京藏高速\",\"jjmd\":\"01\",\"jjmdmc\":\"自駕旅游\",\"jjrq\":\"${issuedate}\",\"jjzzl\":\"02\",\"jsrxm\":\"${drivername}\",\"jszh\":\"${driverid}\",\"sfzmhm\":\"${userid}\",\"xxdz\":\"百度大廈\",\"sqdzbdjd\":116.307393,\"sqdzbdwd\":40.057771}"

      這個引號看得眼睛都花了,如果一個不小心配錯了,可夠人找半天的。

      其實使用 jq 內置變量的重要原因,可讀性只是一方面,正確性是另一方面。

      假設這樣一個場景,在 issuereq 中新增一個 data 字段,內容也是 json (為了簡化例子,直接使用 statereq 充任),那么兩種方式還會一致嗎?

      local issuereq=$(cat issuereq.json | jq --arg hphm "${vehicle}" --arg hpzl "${hpzl}" --arg vid "${vid}" --arg jjrq "${issuedate}" --arg jsrxm "${drivername}" --arg jszh "${driverid}" --arg sfzmhm "${userid}" --arg timestamp $(date "+%s000") --arg data "${statereq}" -c '{ dabh, hphm: $hphm, hpzl: $hpzl, vId: $vid, jjdq, jjlk, jjlkmc, jjmd, jjmdmc, jjrq: $jjrq, jjzzl, jsrxm: $jsrxm, jszh: $jszh, sfzmhm: $sfzmhm, xxdz, sqdzbdjd, sqdzbdwd, data: $data }')
      echo "issue req: ${issuereq}"
      
      issuereq="{\"dabh\":\"null\",\"hphm\":\"${vehicle}\",\"hpzl\":\"${hpzl}\",\"vId\":\"${vid}\",\"jjdq\":\"海淀區\",\"jjlk\":\"00401\",\"jjlkmc\":\"京藏高速\",\"jjmd\":\"01\",\"jjmdmc\":\"自駕旅游\",\"jjrq\":\"${issuedate}\",\"jjzzl\":\"02\",\"jsrxm\":\"${drivername}\",\"jszh\":\"${driverid}\",\"sfzmhm\":\"${userid}\",\"xxdz\":\"百度大廈\",\"sqdzbdjd\":116.307393,\"sqdzbdwd\":40.057771,\"data\":\"${statereq}\"}"
      echo "issue req: ${issuereq}"

      運行上面的腳本片段會得到如下的內容:

      issue req: {"dabh":"null","hphm":"津ADY1951","hpzl":"52","vId":"1480773467139342337","jjdq":"海淀區","jjlk":"00401","jjlkmc":"京藏高速","jjmd":"01","jjmdmc":"自駕旅游","jjrq":"2023-02-13","jjzzl":"02","jsrxm":"云海","jszh":"150121198603226428","sfzmhm":"150121198603226428","xxdz":"百度大廈","sqdzbdjd":116.307393,"sqdzbdwd":40.057771,"data":"{"v":"3.4.1","sfzmhm":"150121198603226428","s-source":"bjjj-android","timestamp":"1676214143000"}"}
      
      issue req: {"dabh":"null","hphm":"津ADY1951","hpzl":"52","vId":"1480773467139342337","jjdq":"海淀區","jjlk":"00401","jjlkmc":"京藏高速","jjmd":"01","jjmdmc":"自駕旅游","jjrq":"2023-02-13","jjzzl":"02","jsrxm":"云海","jszh":"150121198603226428","sfzmhm":"150121198603226428","xxdz":"百度大廈","sqdzbdjd":116.307393,"sqdzbdwd":40.057771,"data":"{\"v\":\"3.4.1\",\"sfzmhm\":\"150121198603226428\",\"s-source\":\"bjjj-android\",\"timestamp\":\"1676214296000\"}"}

      data 字段的對比非常明顯,第一種使用 shell 變量,直接將雙引號放入生成的 json 中了,導致引號匹配出錯;后一種使用 jq 內置變量,會將 data 內部的雙引號自動轉義,從而符合 json 語法。

      總結一下,使用 jq 變量和 json 模板構造請求將使生成的 json 字符串符合語法、腳本變得清晰、數據也便于維護,推薦指數五顆星。

      date

      腳本中大量的日期處理依賴 date 命令,其中比較有趣的一點是 mac date 和 unix date 的區別:

      # mac date performs differs with other unix..
      if [ ${IS_MAC} -eq 1 ]; then 
          issuedate=$(date "-v+${expire}d" '+%Y-%m-%d')
      else 
          issuedate=$(date '+%Y-%m-%d' -d "+${expire} days")
      fi

      當計算從今天開始的 N 天后日期時,mac date 使用的參數和 unix date 不同,它的形式是-v+Nd,unix date 的形式是-d "+N days"。為此在腳本一開頭增加了一個判斷當前平臺是否為 macOS 的函數:is_macos。

      另外需要注意的一點是請求參數中的 timestamp 是精確到毫秒的,date "+%s"只能精確到秒,這里采取了一個討巧的方法,直接加 3 個零完事:date "+%s000"

      curl

      curl 作為請求的主力沒什么可說的,一直是那么的可靠。這里主要注意兩個小點。

      headers

      請求頭是存放在 shell 數組中拼成一整個字符串的:

      local stateheader #=() adb shell not support =() initialize an array..
      stateheader[0]="Accept-Language:${lang}"
      stateheader[1]="User-Agent:${agent}"
      stateheader[2]="source:${source}"
      stateheader[3]="authorization:${auth}"
      stateheader[4]="Content-Type:${content}"
      stateheader[5]="Host:${host}"
      stateheader[6]="Connection:Keep-Alive"
      stateheader[7]="Accept-Encoding:gzip"
      local headers=""
      for var in "${stateheader[@]}"; 
      do
          headers="${headers} -H ${var}"
      done
      echo "state headers: ${headers} -H ${length}" 1>&2
      local resp=$(curl -s -k ${headers} -H ${length} -d "${statereq}" "${stateurl}")

      后面會將這個拼接后的字符串直接放在 curl 的參數列表中,為了防止 shell 通過空格自動切分參數,構建的 header 也不能存在空格,所以這里 Key 和 Value 之前是沒有空格的,這一點需要注意。

      另外因為 Content-Length 字段是隨請求變化的,為了可以重復使用這個 header 數組,沒有將它包含在內,而是成為一個獨立的請求頭:-H ${length}

      不檢查證書

      給 curl 添加 -k 選項,這對于某些通過代理訪問服務器的環境來說至關重要,沒有這個選項可能導致 curl 就直接失敗了:

      curl: (60) SSL certificate problem: self signed certificate in certificate chain
      More details here: https://curl.haxx.se/docs/sslcerts.html
      
      curl failed to verify the legitimacy of the server and therefore could not
      establish a secure connection to it. To learn more about this situation and
      how to fix it, please visit the web page mentioned above.

      相當于 wget 的 --no-check-certificate 選項。

      配置腳本

      與用戶相關的配置都存放在 config.ini 文件中:

      % cat config.ini
      # idcard who own the car
      userid=150121198603226428
      # car number to indicate  which car should I issue permit for,
      # especially when you have many cars
      vehicle=津ADY1951
      # user login tokens, get by network package capture..
      authorization=f36abdfa-8878-46bf-91d9-5666f808e9a4
      source=8724a2428c3f47358741f978fd082810
      # name & idcard who drive the car
      # can be same with the car owner
      drivername=云海
      driverid=150121198603226428

      使用時需要定制自己的配置,下面逐個字段說明:

      • userid:賬戶對應的身份證號,這個僅用來檢驗,如果和通過用戶憑證查出來的身份證不匹配,將導致腳本中斷執行,以防誤操作
      • vehicle:車牌號,用于區分名下有多輛車時為哪個辦理。如果只有一輛,也需要指定一下
      • authorization & source:用戶憑證,通過前面介紹的 VNET 抓包獲取
      • drivername & driverid:申請進京證的駕駛員信息,可以和賬戶不同,但必需"查有此人"

      申辦日期不在配置中,而是按最近原則確定:如果申請時間是當天中午 12 點前,那就申請當天進京證;否則申請第二天進京證。這樣便于周期性執行。

      定時執行

      有了上面的腳本和配置,定時執行就是小菜一碟了,在 unix 系統上,使用 crontab 加入定時調用:

      > crontab -e
      0 1 * * 1 cd /home/users/yunhai01/code/jinjing365; date >> jinjing.log; sh jinjing.sh >> jinjing.log 2>>verbose.log

      每周一凌晨一點執行。在 windows 上也可以加入計劃任務來實現定時調用,命令部分可以這樣寫:

      批處理 jinjing.bat 將直接調用 jinjing.sh:

      cd /d %~dp0
      bash.exe jinjing.sh >> jinjing.log 2>>verbose.log

      其中 %~dp0 表示腳本所在的目錄。

      能這樣寫的前提是已經安裝 git bash 和 jq for windows,并且將它們所在的路徑 (如 C:\Program Files\Git\bin) 放在 PATH 環境變量中。

      安裝 git bash 時如果指定 "Use Git and optional Unix tools from the Command Prompt“ 選項 ,可由安裝包自動設置 PATH 環境變量。

      結語

      其實在寫好這篇文章的時候,腳本運行還是有些問題的,總是返回 500 錯誤 (目前辦理業務人數較多,請稍后再試),如果直接拷貝文中的腳本,大概率是跑不通的。

      針對這個問題,追蹤的過程也是頗具戲劇性,限于篇幅我打算將它記錄為一篇單獨的文章,以后有時間再分享出來。

      因此最好直接 clone 代碼庫 (jinjing365),那里會包括最新的補丁,問題修復后會第一時間 push 到 github。

      歡迎提交 bug 反饋、特性 patch、小額贊賞,如果覺得它還不錯的話。

      后記

      在實現這個工具的過程中,參考了 github 上 woodheader/jjz 項目的一些思路,例如 VNET 的使用就是從這里 get 的。

      jjz 這個項目有很多過人之處,社交軟件通知功能就是其一,辦理結果時能第一時間得到通知,避免耽誤正事。

      因為不想安裝企業微信、盯盯等,目前沒有在 jinjing365 中添加類似功能。如果沒有自己的服務器可用,直接借用這個項目的通知群不失為一個簡便的辦法。

      jjz 的一個缺點是不支持一個人名下有多輛車的場景,而這個正是 jinjing365 的長處 (順便打個廣告)。

      另外在遇到 500 錯誤時,通過向 woodheader 提問得到了啟發,在此一并表示感謝。

       

      最后,也可以開發其它語言的進京證自動申請方案,python、lua、javascript...,沒有 shell 能做它們做不到的道理,選哪種語言主要考慮熟悉度和便捷度就行,這也是本文將進京證申請規則和報文分析安排在前面的原因。

      另外需要注意的一點是控制請求的頻率,一周一次足矣,最多不要超過一天一次。萬一太頻繁把服務器打掛了,可能就會被反作弊打擊,輕則加校驗參數,重則改接口、封帳號,那樣大家都跟著遭殃。

      -------------------- 20230724 更新 ---------------------

      最近突然發現腳本運行出錯了:

      Sat Jun  3 01:00:01 CST 2023
      check jq ok
      check curl ok
      check head ok
      check cat ok
      check awk ok
      check grep ok
      check date ok
      query permits status failed, code: 401, msg: 令牌為空,請重新登錄

      更新了一下 authentication 字段,就好了。查了下最早的腳本運行記錄:

      Tue Jan 31 12:00:02 CST 2023
      check jq ok
      check curl ok
      check head ok
      check cat ok
      check awk ok
      check grep ok
      check date ok
      query permits status ok: 用戶辦證信息查詢成功!
      ...

      從 01.31 運行到 06.02,token 有效期大概 4 個多月,大家到期記得換下~

      -------------------- 20230724 更新 ---------------------

      參考

      [1]. woodheader/jjz

      [2]. Shell下解析Json之jq

      [3]. shell 獲取n天前和n天后日期

      [4]. mac date命令

      [5]. Markdown 圖片語法

      [6]. jq 中文手冊(v1.5)

      [7]. linux定時任務crontab設置每分鐘、每小時、每天、每周、每月、每年定時執行 --- 2020-03-06

      posted @ 2023-03-07 15:01  goodcitizen  閱讀(1571)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 一本大道久久a久久综合| 人人妻人人狠人人爽天天综合网| 国产成人精品一区二三区| 亚洲gay片在线gv网站| 新竹市| 国产日女人视频在线观看| 国产露脸无套对白在线播放| 国产亚洲一区二区三区av| 国产精品v片在线观看不卡| 亚洲国产天堂久久综合226114| 婷婷丁香五月六月综合激情啪| 欧美怡春院一区二区三区| 国产片一区二区三区视频| 国产精品久久欧美久久一区| 在线免费观看毛片av| 色琪琪丁香婷婷综合久久| 4480yy亚洲午夜私人影院剧情 | 亚洲岛国av一区二区| 永久无码天堂网小说区| 美女禁区a级全片免费观看| 国产自在自线午夜精品| 亚洲日本中文字幕乱码中文| 成人午夜激情在线观看| 亚洲中文字幕精品久久| 精品综合久久久久久97| 《特殊的精油按摩》3| 久久亚洲精品11p| 日韩av一区二区三区不卡| 97se亚洲国产综合在线| 中国女人内谢69xxxx| 成人亚洲国产精品一区不卡| 国产成人精品白浆免费视频试看| 在线中文一区字幕对白| 国产综合精品91老熟女| 狠狠v日韩v欧美v| 国产熟睡乱子伦视频在线播放| 国产午夜福利不卡在线观看| 亚洲欧美电影在线一区二区| 亚洲精品成人片在线观看精品字幕 | 男女一级国产片免费视频| 中文字幕人妻无码一夲道|