method.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. package login
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "fmt"
  6. "git.listensoft.net/tool/jspkit/common"
  7. "git.listensoft.net/tool/jspkit/common/lxhttp"
  8. "git.listensoft.net/tool/jspkit/common/models"
  9. Rsvmp "git.listensoft.net/tool/jspkit/common/rsvmp"
  10. "git.listensoft.net/tool/jspkit/logger"
  11. "git.listensoft.net/tool/jspkit/taxerr"
  12. "github.com/go-rod/rod"
  13. "github.com/go-rod/rod/lib/proto"
  14. "github.com/go-rod/rod/lib/utils"
  15. "github.com/tidwall/gjson"
  16. "net/http"
  17. "net/url"
  18. "strings"
  19. "time"
  20. )
  21. // MakeLoginData 生成登录信息
  22. func MakeLoginData(ctx context.Context, info models.CompanyInfo) (jsonData map[string]interface{}) {
  23. loginMethod := "QRCODE"
  24. TaxNo := info.TaxNo
  25. if info.Dlfs == "代理登录" {
  26. loginMethod = "MOBILE"
  27. }
  28. website := "etax"
  29. if ctxValue := ctx.Value("website"); ctxValue != nil {
  30. if websiteStr, ok := ctxValue.(string); ok {
  31. website = websiteStr
  32. }
  33. }
  34. // 拆分登陆方式
  35. switch info.Area {
  36. case "hubei", "guangdong", "zhejiang", "tianjin":
  37. if info.Dlfs == "新版登录" {
  38. loginMethod = "ACCOUNT"
  39. }
  40. if info.Dlfs == "代理登录" && (info.Area == "guangdong" || info.Area == "zhejiang" || info.Area == "tianjin") {
  41. loginMethod = "ACCOUNT"
  42. }
  43. default:
  44. if info.Dlfs == "新版登录" {
  45. loginMethod = "QRCODE"
  46. }
  47. }
  48. changeCreditCode := ""
  49. loginType := "QIYE"
  50. if info.Dlfs == "代理登录" {
  51. changeCreditCode = info.TaxNo
  52. info.TaxNo = info.IdNo
  53. loginType = "DAILI"
  54. TaxNo = info.IdNo
  55. }
  56. jsonData = map[string]interface{}{
  57. "userInfo": map[string]string{
  58. "ComName": info.ComName,
  59. "Name": info.Area + ".pro",
  60. "CreditCode": TaxNo,
  61. "Account": info.Tel,
  62. "Password": info.Zzrmm,
  63. "dailiCreditCode": "",
  64. "changeCreditCode": changeCreditCode,
  65. },
  66. "areaName": info.Area,
  67. "loginMethod": loginMethod,
  68. "loginType": loginType,
  69. "website": website,
  70. "source": "taxRobot",
  71. //"proxies": proxies,
  72. }
  73. return
  74. }
  75. // PostLoin 登录函数
  76. func PostLoin(jsonR []byte, headder map[string]string, info models.CompanyInfo) (response []byte, err error) {
  77. f := 0
  78. //begin:
  79. //logger.Info(string(jsonR))
  80. c := lxhttp.NewHttpClient() //http://dppt.jsptax.com:81
  81. if info.Dlfs == "代理登录" {
  82. tr := &http.Transport{
  83. TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //忽略证书校验
  84. }
  85. c = &http.Client{
  86. Timeout: time.Duration(400 * time.Second),
  87. Transport: tr,
  88. }
  89. }
  90. response, err = lxhttp.PostByteReader(c, ETaxServe+"/api/login", jsonR, headder, nil)
  91. if err != nil {
  92. logger.Error(err.Error())
  93. if f < 6 {
  94. utils.Sleep(5)
  95. f++
  96. //goto begin
  97. }
  98. return response, taxerr.NewWebStuckTitle(info.Cscsts)
  99. }
  100. if strings.Contains(string(response), "Sorry, the page you are looking for is currently unavailable") || strings.Contains(string(response), "二维码已失效") ||
  101. strings.Contains(string(response), "ReadTimeoutError") || strings.Contains(string(response), "ProtocolError") {
  102. if f <= 3 {
  103. f++
  104. //goto begin
  105. }
  106. return nil, taxerr.New("新版税局登陆超时")
  107. }
  108. // 登录繁忙等30s
  109. if strings.Contains(string(response), "税局服务繁忙,请5分钟后重试") {
  110. utils.Sleep(30)
  111. if f <= 3 {
  112. f++
  113. //goto begin
  114. }
  115. }
  116. if (gjson.GetBytes(response, "msg").String() != "" && gjson.GetBytes(response, "msg").String() != "[登录成功~]") ||
  117. (gjson.GetBytes(response, "status").String() != "ok") {
  118. Str := gjson.GetBytes(response, "error").String()
  119. if Str == "" {
  120. // {"status":"false","msg":"[切换身份失败, 不存在用户91370203MA3RCTAU0X~]"}
  121. // {"status":"false","msg":"[切换身份失败, 连续认证错误次数过多,您的账号已被锁定。建议您直接使用“忘记密码”修改密码后重新登录或等待次日零时自动解锁。~]"}
  122. Str = gjson.GetBytes(response, "msg").String()
  123. }
  124. if Str == "" {
  125. Str = "新版税局登陆超时2"
  126. }
  127. if strings.Contains(Str, `请调用发送验证码`) && info.Dlfs == "新版登录" {
  128. common.DeleteTpassCookie(info)
  129. }
  130. if strings.Contains(Str, `请调用发送验证码`) && info.Dlfs == "代理登录" {
  131. err = taxerr.NewUserV3(`代理登录已掉线`, "请保证设备或app在线后重新发起任务")
  132. return
  133. }
  134. // 税局登录失败['bool' object is not subscriptable] 这个错误直接返回连续认证错误次数过多
  135. if strings.Contains(Str, `税局登录失败['bool' object is not subscriptable]`) {
  136. err = taxerr.FormatLoginError("税局登录失败[连续认证错误次数过多,您的账号已被锁定。建议您直接使用“忘记密码”修改密码后重新登录或等待次日零时自动解锁。]")
  137. return
  138. }
  139. err = taxerr.FormatLoginError(Str)
  140. return
  141. }
  142. return
  143. }
  144. func RsCookie(url string) (ck []*http.Cookie, uri string) {
  145. return nil, url
  146. if Rsvmp.NeedRsvmp(url) {
  147. ck, _ = Rsvmp.Rsvmp(url)
  148. return ck, url
  149. }
  150. return nil, url
  151. }
  152. // RefreshCookieBase 刷新增值税cookie和财报cookie
  153. func refreshCookieBase(client *http.Client, area, BaseIndex, api, new_key16, token string) {
  154. f := 0
  155. begin:
  156. headers := map[string]string{
  157. "Accept-Encoding": "gzip, deflate, br, zstd",
  158. "Accept-Language": "zh-CN,zh;q=0.9",
  159. "Accept": "application/json, text/plain, */*",
  160. "Content-Type": "application/json",
  161. "deviceIdentyNo": "740qhjjFG7xSNQoCFtSHA7AMzikTKeJ5",
  162. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
  163. "X-TICKET-ID": "null",
  164. "X-NATURE-IP": "",
  165. "X-SM4-INFO": "0",
  166. "X-TEMP-INFO": "null",
  167. "hUid": "",
  168. "X-APP-CLIENTID": "",
  169. "X-LANG-ID": "null",
  170. "Authorization": token,
  171. }
  172. logger.Info("埋点1")
  173. rst := lxhttp.Get_etax_clientId_redirectUri(client, api, RsCookie)
  174. logger.Info("埋点2")
  175. if rst == nil {
  176. return
  177. }
  178. logger.Info("埋点3")
  179. client_id := rst["client_id"].(string)
  180. redirect_uri := rst["redirect_uri"].(string)
  181. redirect_uri, _ = url.QueryUnescape(redirect_uri)
  182. code := lxhttp.VerifyLogin(client, area, BaseIndex, client_id, redirect_uri, new_key16, headers, RsCookie)
  183. logger.Info("埋点4")
  184. queryParams := url.Values{}
  185. queryParams.Add("code", code)
  186. if !strings.Contains(api, "invoice-query/invoice-query") {
  187. queryParams.Add("state", rst["state"].(string))
  188. }
  189. queryParams.Add("response_type", rst["response_type"].(string))
  190. hasQueryParams := strings.Contains(redirect_uri, "?")
  191. var finalRedirectURI string
  192. if hasQueryParams {
  193. finalRedirectURI = fmt.Sprintf("%s&%s", redirect_uri, queryParams.Encode())
  194. } else {
  195. finalRedirectURI = fmt.Sprintf("%s?%s", redirect_uri, queryParams.Encode())
  196. }
  197. fmt.Println("刷新cookie-uri:", finalRedirectURI)
  198. res, err := lxhttp.GET(client, finalRedirectURI, map[string]string{}, headers)
  199. if err != nil {
  200. _ = rod.Try(func() {
  201. logger.Info("finalRedirectURI失败:" + string(res))
  202. logger.Info("finalRedirectURI失败:" + err.Error())
  203. })
  204. if strings.Contains(BaseIndex, "neimeng") && f < 7 {
  205. f++
  206. goto begin
  207. }
  208. }
  209. fmt.Println(string(res))
  210. return
  211. }
  212. // RefreshCookieForAfterLogin RefreshCookie 主函数-对外开放 可以刷新两个cookie
  213. func RefreshCookieForAfterLogin(cookies map[string]string, area, BaseIndex, new_key16, token string, cwbb bool) (client *http.Client) {
  214. client = lxhttp.NewHttpClientForNoRedirects("", "")
  215. var cks []*http.Cookie
  216. for k, v := range cookies {
  217. cks = append(cks, &http.Cookie{
  218. Name: k,
  219. Value: v,
  220. })
  221. }
  222. u, _ := url.Parse(BaseIndex)
  223. client.Jar.SetCookies(u, cks)
  224. uri := BaseIndex + `/szc/szzh/sbss/ssmx/public/v1/checkLoginState`
  225. if !cwbb {
  226. uri = BaseIndex + "/szc/szzh/sjswszzh/sy/redirect?pageName=zhgl"
  227. }
  228. err := rod.Try(func() {
  229. refreshCookieBase(client, area, BaseIndex, uri, new_key16, token)
  230. })
  231. if err != nil {
  232. logger.Info(err.Error())
  233. return nil
  234. }
  235. return client
  236. }
  237. // RefreshCookieForAfterLoginRod rod使用替代afterlogin
  238. func RefreshCookieForAfterLoginRod(page *rod.Page, proxy, proxyAuth, area, BaseIndex string, cwbb bool) (client *http.Client, err error) {
  239. Tpassuri := fmt.Sprintf(`https://tpass.%s.chinatax.gov.cn:8443/#/identitySwitch/enterprise`, area)
  240. client = lxhttp.NewHttpClientForNoRedirects(proxy, proxyAuth)
  241. cookies := page.Browser().MustGetCookies()
  242. var cookiesArr []*http.Cookie
  243. for _, v := range cookies {
  244. var cookie http.Cookie
  245. cookie.Name = v.Name
  246. cookie.Value = v.Value
  247. cookie.Path = v.Path
  248. cookie.Domain = v.Domain
  249. cookiesArr = append(cookiesArr, &cookie)
  250. }
  251. u, _ := url.Parse(BaseIndex)
  252. client.Jar.SetCookies(u, cookiesArr)
  253. new_key16, token, err := GetTpassToken(page, Tpassuri)
  254. if err != nil {
  255. return
  256. }
  257. logger.Info("GetTpassToken完成")
  258. Api := BaseIndex + `/szc/szzh/sbss/ssmx/public/v1/checkLoginState`
  259. if !cwbb {
  260. Api = BaseIndex + "/szc/szzh/sjswszzh/sy/redirect?pageName=zhgl"
  261. }
  262. if area == "shanxi" {
  263. Api = `https://etax.shanxi.chinatax.gov.cn:8443/szc/szzh/sjswszzh/spHandler?cdlj=/szzh/szzh/`
  264. }
  265. err = rod.Try(func() {
  266. logger.Info("开始refreshCookieBase")
  267. refreshCookieBase(client, area, BaseIndex, Api, new_key16, token)
  268. logger.Info("结束refreshCookieBase")
  269. })
  270. if err != nil {
  271. logger.Info(err.Error())
  272. }
  273. return client, err
  274. }
  275. // RefreshCookieForAfterLogin RefreshCookie 主函数-对外开放 可以刷新两个cookie
  276. func RefreshDppt(page *rod.Page, area string) {
  277. e := rod.Try(func() {
  278. BaseIndex := GetBaseUri(area)
  279. client := lxhttp.NewHttpClientForNoRedirects("", "")
  280. {
  281. var cks []*http.Cookie
  282. for _, v := range page.Browser().MustGetCookies() {
  283. cks = append(cks, &http.Cookie{
  284. Name: v.Name,
  285. Value: v.Value,
  286. })
  287. }
  288. u, _ := url.Parse(strings.ReplaceAll(BaseIndex, "etax", "dppt"))
  289. client.Jar.SetCookies(u, cks)
  290. u, _ = url.Parse(BaseIndex)
  291. client.Jar.SetCookies(u, cks)
  292. }
  293. TpassUri := fmt.Sprintf(`https://tpass.%s.chinatax.gov.cn:8443/#/identitySwitch/enterprise`, area)
  294. new_key16, token, _ := GetTpassToken(page, TpassUri)
  295. if new_key16 == "" {
  296. return
  297. }
  298. uri := strings.ReplaceAll(GetBaseUri(area), "etax", "dppt") + "/szzhzz/spHandler?cdlj=third-menu/invoice-query/invoice-query"
  299. refreshCookieBase(client, area, BaseIndex, uri, new_key16, token)
  300. var rodCookies []*proto.NetworkCookieParam
  301. u, _ := url.Parse(BaseIndex)
  302. for _, v := range client.Jar.Cookies(u) {
  303. rodCookies = append(rodCookies,
  304. &proto.NetworkCookieParam{
  305. Name: v.Name,
  306. Value: v.Value,
  307. Domain: ".chinatax.gov.cn",
  308. Path: "/",
  309. },
  310. )
  311. }
  312. logger.Info("xxxx4")
  313. _ = page.Browser().SetCookies(rodCookies)
  314. })
  315. if e != nil {
  316. logger.Info(e.Error())
  317. }
  318. return
  319. }
  320. func GetTpassToken(page *rod.Page, TpassUrl string) (string, string, error) {
  321. var new_key16, token string
  322. err := rod.Try(func() {
  323. home := page.MustInfo().URL
  324. logger.Info(home)
  325. cookies := page.Browser().MustGetCookies()
  326. Ck := map[string]string{}
  327. for _, cookie := range cookies {
  328. Ck[cookie.Name] = cookie.Value
  329. }
  330. // 避免进不去
  331. for range "..." {
  332. page.MustNavigate(TpassUrl)
  333. utils.Sleep(2) // 加校验 //span[text()="身份切换"]
  334. page.MustWaitLoad()
  335. if common.MustHasXV(page, `//span[text()="身份切换"]`) {
  336. break
  337. } else {
  338. utils.Sleep(1.5)
  339. }
  340. }
  341. if !common.MustHasXV(page, `//span[text()="身份切换"]`) {
  342. panic(taxerr.New("获取企业所得税超时,系统将于30分钟后重试(可在\"通用设置\"关闭)"))
  343. }
  344. _ = rod.Try(func() {
  345. new_key16 = page.MustEval(`()=>localStorage.getItem('new_key16')`).String()
  346. })
  347. _ = rod.Try(func() {
  348. token = page.MustEval(`()=>localStorage.getItem('token')`).String()
  349. })
  350. logger.Info("New_key16:" + new_key16)
  351. logger.Info("Token:" + token)
  352. page.Navigate(home)
  353. })
  354. if new_key16 == "" {
  355. err = taxerr.NewWebStuckTitle(common.InfoCscsts)
  356. }
  357. return new_key16, token, err
  358. }