package login import ( "context" "encoding/base64" "encoding/json" "fmt" "log" "strings" "time" "git.listensoft.net/tool/jspkit/common" "git.listensoft.net/tool/jspkit/common/models" "git.listensoft.net/tool/jspkit/logger" "git.listensoft.net/tool/jspkit/taxerr" "github.com/forgoer/openssl" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" "github.com/tidwall/gjson" ) func CheckSessionAndLogin(ctx context.Context, p *rod.Page, BaseUri string, info models.CompanyInfo) (err error) { //return taxerr.NewUser("税局代理机构登录存在风险,预计6号恢复,请等待税局修复后使用")//12.6 09:20左右 确认完了 if info.Dlfs == "代理登录" && info.IdNo == "" { return taxerr.DlTaxNoError } return AutoCheckSessionAndLogin(ctx, p, BaseUri, info) } func LoadCookie(sessionInfos models.SsoSession, p *rod.Page, BaseUri string, info models.CompanyInfo) (name string, err error) { err = rod.Try(func() { defer AddErr() p.Navigate("") _ = p.Timeout(common.ClickTimeOut).MustNavigate(areaURLmap[info.Area+".pro"]).StopLoading() // 此时必须得去tpass域名 要不然写入cookie后进不去发票 p.MustSetCookies(sessionInfos.Cookies...) { p.MustEval(`() => localStorage.clear()`) utils.Sleep(1) // 设置 localStorage 在第二个页面中,逐条设置 logger.Info("Setting localStorage in second browser...") for key, value := range sessionInfos.LocalStorage { if key == "logConfig" { continue } script := fmt.Sprintf(`() => localStorage.setItem('%s', '%s')`, key, value) _, err := p.Eval(script) if err != nil { log.Printf("Error setting localStorage %s=%s: %v", key, value, err) } } } utils.Sleep(1) if info.Area == "shaanxi" { //陕西地区必须带后面/才能登录上去 p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb/") } else { p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb") } utils.Sleep(4) _ = rod.Try(func() { p.Timeout(common.LoadTimeout * 2).MustWaitStable() p.Timeout(common.LoadTimeout * 2).MustWaitDOMStable() }) //p.MustStopLoading() logger.Info("加载完成") if info.Area == "hebei" && !common.MustHasXV(p, `//div[@class='leftMain']//div[contains(@class,'title')]`) { logger.Info("河北重新访问loginb") p.Timeout(common.ClickTimeOut).MustNavigate(`https://tpass.hebei.chinatax.gov.cn:8443/#/login?redirect_uri=https%3A%2F%2Fetax.hebei.chinatax.gov.cn%3A8443%2Fmhzx%2Fapi%2Fmh%2Ftpass%2Fcode&client_id=ap5bfdavp66a49979apa5848fpaa5353&response_type=code&state=8f8f3db7ecb948f08b1da177cda40854&client_pid=ap5bfdavp66a49979apa5848fpaa5353`) utils.Sleep(4) p.MustWaitStable() p.MustWaitDOMStable() } if common.MustHasXV(p, "//div[text()=' 关于电子税务局“新办智能开业”的通知 ']") { rod.Try(func() { p.Timeout(common.ClickTimeOut).MustSearch("//span[text()='我知道了']").MustClick() utils.Sleep(1) common.MustElementXV(p, "//span[text()='确认']").MustClick() utils.Sleep(40) //要转很久 p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb") }) } if p.MustHasX(`//div[@class='leftMain']//div[contains(@class,'title')]`) { _ = p.Timeout(common.ClickTimeOut).MustElementX(`//div[@class='leftMain']//div[contains(@class,'title')]`).WaitStable(300 * time.Millisecond) if *p.Timeout(common.ClickTimeOut).MustElementX(`//div[@class='leftMain']//div[contains(@class,'title')]`).MustAttribute(`title`) == "" { logger.Info("公司名空刷新") p.Reload() utils.Sleep(4) p.MustWaitDOMStable() } } if common.MustHasXV(p, `//div[@class='leftMain']//div[contains(@class,'title')]`) { name = *common.WaitElementX(p, `//div[@class='leftMain']//div[contains(@class,'title')]`, 0.5).MustAttribute(`title`) if name == "" { err = taxerr.New("代理登录切换公司后异常公司名") return } return } DlSy := false //登录完是代理登录的首页 if common.MustHasXV(p, `//a[@title="单户办理"]`) { common.MustElementX(p, `//a[@title="单户办理"]`).MustClick() DlSy = true utils.Sleep(3) } if !strings.Contains(p.MustInfo().URL, "tpass") { panic(taxerr.New("cookie失效")) } if !p.MustHasR(`[class="s_Breadcrumb"]`, "企业办税") { panic(taxerr.New("cookie失效")) } if !DlSy { r := p.MustWaitRequestIdle() p.Timeout(common.ClickTimeOut).MustSearch("返回").MustClick() r() } else { //这里需要手动切换到首页 common.MustInputX(p, `//input[@placeholder="请输入统一社会信用代码"]`, info.TaxNo) common.MustElementX(p, `//span[text()="查询"]`).MustClick() utils.Sleep(2) tbody := common.MustElementsX(p, `//div[@class="el-table__body-wrapper is-scrolling-none"]//table//tr`) if len(tbody) == 0 { common.MustInputX(p, `//input[@placeholder="请输入统一社会信用代码"]`, "") p.Timeout(common.ClickTimeOut).MustElementX(`//input[@placeholder="请输入纳税人名称"]`).MustInput(info.ComName) common.MustElementX(p, `//span[text()="查询"]`).MustClick() utils.Sleep(2) tbody = common.MustElementsX(p, `//div[@class="el-table__body-wrapper is-scrolling-none"]//table//tr`) if len(tbody) == 0 { panic(taxerr.NewUserV3("未在当前账号下["+info.Zzridno+"]找到该企业", "请检查手机号与企业的关联关系")) } panic(taxerr.NewUserV3("该公司账号跟公司名称不匹配", "请检查后重新发起")) } else if len(tbody) == 1 { common.MustElementX(p, `//span[text()='进入']`).MustClick() if common.MustHasXV(p, `//span[text()="身份切换"]/../../..//span[contains(text(),"确定")]`) { common.MustElementX(p, `//span[text()="身份切换"]/../../..//span[contains(text(),"确定")]`).MustClick() time.Sleep(5 * time.Second) } } else { panic(taxerr.NewSystemV3("未在当前税号下["+info.TaxNo+"]找到多家企业", "请联系运维处理")) } } utils.Sleep(2) p.MustWaitDOMStable() name = *common.WaitElementX(p, `//div[@class='leftMain']//div[contains(@class,'title')]`, 0.5).MustAttribute(`title`) }) // 这是panic的失效 if err != nil { rod.Try(func() { patjh := common.GeneratePath("png") p.MustScreenshot(patjh) logger.Info("代理登录切换公司后异常公司名或判定为失效截图:" + patjh) }) logger.Info(err.Error()) return "", err } if (err != nil) && name == "" { logger.Info("cookie失效,当前url:" + p.MustInfo().URL) return "", taxerr.NewSystemV3("税局加载异常", "系统将于30分钟后重试") } return } func LoadCookieFast(sessionInfos models.SsoSession, p *rod.Page, BaseUri string, info models.CompanyInfo) (name string, err error) { err = rod.Try(func() { defer AddErr() p.Navigate("") p.Timeout(common.ClickTimeOut).MustNavigate(areaURLmap[info.Area+".pro"]).MustStopLoading() // 此时必须得去tpass域名 要不然写入cookie后进不去发票 p.MustSetCookies(sessionInfos.Cookies...) { p.MustEval(`() => localStorage.clear()`) //utils.Sleep(1) // 设置 localStorage 在第二个页面中,逐条设置 logger.Info("Setting localStorage in second browser...") for key, value := range sessionInfos.LocalStorage { if key == "logConfig" { continue } script := fmt.Sprintf(`() => localStorage.setItem('%s', '%s')`, key, value) _, err := p.Eval(script) if err != nil { log.Printf("Error setting localStorage %s=%s: %v", key, value, err) } } } utils.Sleep(0.5) if info.Area == "shaanxi" { //陕西地区必须带后面/才能登录上去 p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb/") } else { p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb") } utils.Sleep(1) _ = rod.Try(func() { p.Timeout(2 * time.Second).MustWaitStable() p.Timeout(2 * time.Second).MustWaitDOMStable() }) //p.MustStopLoading() logger.Info("加载完成") if info.Area == "hebei" && !common.MustHasXV(p, `//div[@class='leftMain']//div[contains(@class,'title')]`) { logger.Info("河北重新访问loginb") p.Timeout(common.ClickTimeOut).MustNavigate(`https://tpass.hebei.chinatax.gov.cn:8443/#/login?redirect_uri=https%3A%2F%2Fetax.hebei.chinatax.gov.cn%3A8443%2Fmhzx%2Fapi%2Fmh%2Ftpass%2Fcode&client_id=ap5bfdavp66a49979apa5848fpaa5353&response_type=code&state=8f8f3db7ecb948f08b1da177cda40854&client_pid=ap5bfdavp66a49979apa5848fpaa5353`) utils.Sleep(4) p.MustWaitStable() p.MustWaitDOMStable() } if common.MustHasXV(p, "//div[text()=' 关于电子税务局“新办智能开业”的通知 ']") { rod.Try(func() { p.Timeout(common.ClickTimeOut).MustSearch("//span[text()='我知道了']").MustClick() utils.Sleep(1) common.MustElementXV(p, "//span[text()='确认']").MustClick() utils.Sleep(40) //要转很久 p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb") }) } if p.MustHasX(`//div[@class='leftMain']//div[contains(@class,'title')]`) { _ = p.Timeout(common.ClickTimeOut).MustElementX(`//div[@class='leftMain']//div[contains(@class,'title')]`).WaitStable(300 * time.Millisecond) if *p.Timeout(common.ClickTimeOut).MustElementX(`//div[@class='leftMain']//div[contains(@class,'title')]`).MustAttribute(`title`) == "" { logger.Info("公司名空刷新") p.Reload() utils.Sleep(4) p.MustWaitDOMStable() } } if common.MustHasXV(p, `//div[@class='leftMain']//div[contains(@class,'title')]`) { name = *common.WaitElementX(p, `//div[@class='leftMain']//div[contains(@class,'title')]`, 0.5).MustAttribute(`title`) if name == "" { err = taxerr.New("代理登录切换公司后异常公司名") return } return } DlSy := false //登录完是代理登录的首页 if common.MustHasXV(p, `//a[@title="单户办理"]`) { common.MustElementX(p, `//a[@title="单户办理"]`).MustClick() DlSy = true utils.Sleep(3) } if !strings.Contains(p.MustInfo().URL, "tpass") { panic(taxerr.New("cookie失效")) } if !p.MustHasR(`[class="s_Breadcrumb"]`, "企业办税") { panic(taxerr.New("cookie失效")) } if !DlSy { r := p.MustWaitRequestIdle() p.Timeout(common.ClickTimeOut).MustSearch("返回").MustClick() r() } else { //这里需要手动切换到首页 common.MustInputX(p, `//input[@placeholder="请输入统一社会信用代码"]`, info.TaxNo) common.MustElementX(p, `//span[text()="查询"]`).MustClick() utils.Sleep(2) tbody := common.MustElementsX(p, `//div[@class="el-table__body-wrapper is-scrolling-none"]//table//tr`) if len(tbody) == 0 { common.MustInputX(p, `//input[@placeholder="请输入统一社会信用代码"]`, "") p.Timeout(common.ClickTimeOut).MustElementX(`//input[@placeholder="请输入纳税人名称"]`).MustInput(info.ComName) common.MustElementX(p, `//span[text()="查询"]`).MustClick() utils.Sleep(2) tbody = common.MustElementsX(p, `//div[@class="el-table__body-wrapper is-scrolling-none"]//table//tr`) if len(tbody) == 0 { panic(taxerr.NewUserV3("未在当前账号下["+info.Zzridno+"]找到该企业", "请检查手机号与企业的关联关系")) } panic(taxerr.NewUserV3("该公司账号跟公司名称不匹配", "请检查后重新发起")) } else if len(tbody) == 1 { common.MustElementX(p, `//span[text()='进入']`).MustClick() if common.MustHasXV(p, `//span[text()="身份切换"]/../../..//span[contains(text(),"确定")]`) { common.MustElementX(p, `//span[text()="身份切换"]/../../..//span[contains(text(),"确定")]`).MustClick() time.Sleep(5 * time.Second) } } else { panic(taxerr.NewSystemV3("未在当前税号下["+info.TaxNo+"]找到多家企业", "请联系运维处理")) } } utils.Sleep(0.5) p.MustWaitDOMStable() name = *common.WaitElementX(p, `//div[@class='leftMain']//div[contains(@class,'title')]`, 0.5).MustAttribute(`title`) logger.Info(name) }) // 这是panic的失效 if err != nil { rod.Try(func() { patjh := common.GeneratePath("png") p.MustScreenshot(patjh) logger.Info("代理登录切换公司后异常公司名或判定为失效截图:" + patjh) }) logger.Info(err.Error()) return "", err } if (err != nil) && name == "" { logger.Info("cookie失效,当前url:" + p.MustInfo().URL) return "", taxerr.NewSystemV3("税局加载异常", "系统将于30分钟后重试") } if name == "" { logger.Info("公司名加载异常,当前url:" + p.MustInfo().URL) path := common.GeneratePath("png") p.MustScreenshot(path) logger.Info(path) } return } func AddErr() { if err := recover(); err != nil { e := err.(error) if _, ok := err.(*taxerr.UserErr); ok { err = taxerr.NewUser(e.Error() + "(代理登录失败)") } if _, ok := err.(*taxerr.SystemErr); ok { err = taxerr.New(e.Error() + "(代理登录失败)") } panic(err) } } func AutoCheckSessionAndLogin(ctx context.Context, p *rod.Page, BaseUri string, info models.CompanyInfo) (err error) { if info.Area == "hubei" && info.Dlfs == "代理登录" && len(info.Tel) != 11 { return taxerr.NewUserV3("湖北代理登录需要接受验证码", "请在登录信息中修改为手机号") } if info.Zzrmm == "" { return taxerr.PasswdEmpty } var LoginResponse []byte var Session models.SsoSession // 直接登录获取cookie { logger.Info("开始代理登陆获取cookie") LoginData := MakeLoginData(ctx, info) a, _ := json.Marshal(LoginData) logger.Info("代理登录加密前参数" + string(a)) R := AesECBEncrypt(LoginData) headder := map[string]string{ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", "content-type": "application/json", "x-token": XToken, } marshal, _ := json.Marshal(LoginData) logger.Info(fmt.Sprintf("PostLoin参数%s" + string(marshal))) LoginResponse, err = PostLoin(R, headder, info) logger.Info("代理登陆获取cookie成功") if err != nil { if info.Dlfs == "代理登录" { e := err.(error) if _, ok := err.(*taxerr.UserErr); ok { err = taxerr.NewUser(e.Error() + "(代理登录失败)") } if _, ok := err.(*taxerr.SystemErr); ok { err = taxerr.New(e.Error() + "(代理登录失败)") } } return err } } // 整理cookie { gdata := gjson.GetBytes(LoginResponse, "data") cookies := []*proto.NetworkCookieParam{} domain := "" gdata.Get("cookies").ForEach(func(key, value gjson.Result) bool { if key.String() == "lzkqow38189" { domain = "dppt." + info.Area + ".chinatax.gov.cn" } else if key.String() == "token" { domain = "tpass." + info.Area + ".chinatax.gov.cn" } else { domain = ".chinatax.gov.cn" } cookies = append(cookies, &proto.NetworkCookieParam{ Name: key.String(), Value: value.String(), Domain: domain, Path: "/", }) return true }) localStorage := map[string]string{} gdata.Get("localStorage").ForEach(func(key, value gjson.Result) bool { localStorage[key.String()] = value.String() return true }) Session.Cookies = cookies Session.LocalStorage = localStorage //dpptURL = gdata.Get("redirectUri").String() } //if ctxValue := ctx.Value("website"); ctxValue != nil { //只有发票任务会传website // // 写入浏览器 // name, err := LoadCookieFast(Session, p, BaseUri, info) // if err != nil { // fmt.Println(err.Error()) // return err // } // if name != info.ComName { // return taxerr.CompanyNameError(name) // } //} else { // 写入浏览器 name, err := LoadCookie(Session, p, BaseUri, info) if err != nil { return err } if name != info.ComName { return taxerr.CompanyNameError(name) } //} return } func AesECBEncrypt(data map[string]any) []byte { jsonData, _ := json.Marshal(data) fmt.Println(string(jsonData)) padded := make([]byte, 16) copy(padded, key) encryptData, err := openssl.AesECBEncrypt(jsonData, []byte(padded), openssl.PKCS5_PADDING) if err != nil { return []byte("") } jsonData, _ = json.Marshal(map[string]string{"data": base64.StdEncoding.EncodeToString(encryptData)}) return jsonData }