123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- package common
- import (
- "bytes"
- "context"
- "crypto/md5"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "git.listensoft.net/tool/jspkit/common/lxhttp"
- "git.listensoft.net/tool/jspkit/common/models"
- "git.listensoft.net/tool/jspkit/common/variable"
- "git.listensoft.net/tool/jspkit/common/watermark"
- "git.listensoft.net/tool/jspkit/logger"
- "git.listensoft.net/tool/jspkit/taxerr"
- "github.com/go-kratos/kratos/v2/log"
- "github.com/go-rod/rod"
- "github.com/go-rod/rod/lib/input"
- "github.com/go-rod/rod/lib/utils"
- "github.com/tidwall/gjson"
- "io"
- "math/rand"
- "net/http"
- "net/url"
- "os"
- "reflect"
- "regexp"
- "strings"
- "time"
- )
- type LvName string
- const (
- CheckSuccess LvName = "申报成功" // 检查成功 ------ 检查完全通过
- CheckFail LvName = "检查失败" // 检查任务失败 ----- 网页打开失败等
- CheckOmit LvName = "有遗漏" // 检查有遗漏 ----- 未申报问题
- CheckAbnormal LvName = "有异常" //检查有异常 ----- 税款问题
- )
- func GeneratePath(suffix string) string {
- ps := os.PathSeparator
- //检查路径
- if !PathExists("captcha" + string(ps)) {
- _ = os.MkdirAll("captcha"+string(ps), os.ModePerm)
- }
- return "captcha" + string(ps) + GetTimestamp(13) + "." + suffix
- }
- func PathExists(path string) bool {
- _, err := os.Stat(path)
- if err == nil {
- return true
- }
- if os.IsNotExist(err) {
- return false
- }
- return false
- }
- // 返回一个32位md5加密后的字符串
- func Md5(data string) string {
- h := md5.New()
- h.Write([]byte(data))
- return hex.EncodeToString(h.Sum(nil))
- }
- /*
- 生成检查截图本地路径
- lv 图片对应的错误等级 用上面定义好的 Omit>Abnormal
- taxNo 税号
- period 账期
- location 截图位置,例如申报清册、申报结果查询、税款缴纳、缴费缴纳查询
- */
- func GenerateCheckImagePath(lv LvName, taxNo, period, location string) string {
- var path string
- path = "./data/check/" + taxNo + "/"
- if !PathExists(path) {
- os.MkdirAll(path, os.ModePerm)
- }
- return fmt.Sprintf(`%s-%s-%s-新版_%s_%s.png`, path+taxNo, period, GetTimestamp(13), location, lv)
- }
- func PostSbjt(taxno, period, path string) string {
- var params = map[string]string{
- "tax_no": taxno,
- "period": period,
- "type": "etax", //电子税务局
- }
- str, err := PostFile("https://file.listensoft.net/api/v1/uploadsbjt", map[string]string{
- "file": path,
- }, params)
- if err != nil {
- log.Warnf("【%s】文件上传失败", path)
- }
- return gjson.GetBytes(str, "path").String()
- }
- // 获取申报截图的path
- // taxNo 纳税人识别号 period 账期 taxType 任务名称
- func GetSbjtPath(taxNo, period string, taxType variable.TaskName) string {
- var path string
- path = "./data/" + taxNo + "/"
- if !PathExists(path) {
- os.MkdirAll(path, os.ModePerm)
- }
- path = path + period + string(taxType) + "_" + GetTimestamp(13) + ".png"
- return path
- }
- // 判断字符串是否全是英文
- func IsAllEnglish(s string) bool {
- // 检查字符串是否全部由ASCII字符组成
- for _, c := range s {
- if c > 127 {
- return false
- }
- }
- return true
- }
- // CreateFormReader 将 map 转换成 header
- func CreateFormReader(data map[string]string) io.Reader {
- form := url.Values{}
- for k, v := range data {
- form.Add(k, v)
- }
- return strings.NewReader(form.Encode())
- }
- func Unbracket(str string) string {
- str = strings.ReplaceAll(str, "(", "(")
- str = strings.ReplaceAll(str, ")", ")")
- str = strings.ReplaceAll(str, " ", "")
- return str
- }
- // 检查是否是手机号
- func CheckMobile(mobile string) bool {
- r := regexp.MustCompile(`^1[0-9]{10}$`)
- return r.Match([]byte(mobile))
- }
- func NewWebStuckTitle() *taxerr.SystemErr {
- return taxerr.NewSystemV3("电子税局页面卡顿", "系统将于30分钟后重试(可在\"通用设置\"关闭)")
- }
- // 查看设备是否在线
- func CheckEqmLive(tel string) bool {
- client := lxhttp.NewHttpClient()
- defer client.CloseIdleConnections()
- bys, err := lxhttp.POSTJson(client, "https://keep.jsptax.com"+"/api/v1/telDeviceStatus", map[string]string{"mobiles": tel}, map[string]string{})
- if err != nil {
- return false
- }
- if gjson.GetBytes(bys, `data.status`).Bool() {
- return true
- }
- return false
- }
- func GetData(key variable.SbKey, data string, target interface{}) bool {
- str := gjson.Get(data, string(key)).String()
- if err := MapToStruct(str, target); err != nil {
- logger.Info(err)
- return false
- }
- return true
- }
- // target 传指针
- func MapToStruct(source interface{}, target interface{}) error {
- resByre := []byte("")
- var resByteErr error
- if reflect.TypeOf(source).String() == "string" {
- str := source.(string)
- resByre = []byte(str)
- } else {
- resByre, resByteErr = json.Marshal(source)
- if resByteErr != nil {
- return resByteErr
- }
- }
- err := json.Unmarshal(resByre, target)
- if err != nil {
- return err
- }
- return nil
- }
- // CancelLeftTasks 撤销其他的任务,避免一直发验证码导致超限,仅新版登录使用
- func CancelLeftTasks(tel string) {
- c := lxhttp.NewHttpClient()
- lxhttp.POSTJson(c, variable.TaxTaskURL+"/api/v1/stopQueue", map[string]string{"tel": tel}, nil)
- }
- // GetTelTaskStatus 如果有任务正在进行 返回 true
- func GetTelTaskStatus(tel string) (bool, error) {
- c := lxhttp.NewHttpClient()
- bys, err := lxhttp.POSTJson(c, `https://task.listensoft.net/api/v1/other/telTaskDoi`, map[string]string{
- "tel": tel,
- }, nil)
- if err != nil {
- return false, err
- }
- if gjson.GetBytes(bys, `data.res`).Bool() {
- return true, nil
- } else {
- return false, nil
- }
- }
- var rng = rand.New(rand.NewSource(time.Now().UnixMicro()))
- // GenTracks 函数生成移动轨迹。先做加速运动,再做减速运动。
- //
- // 设滑块滑动的加速度为 a;当前速度为 v;初速度为 v0;位移为 x;所需时间为 t。
- // 根据加速度物理公式,它们满足以下关系:
- // v = v0 + a * t
- // x = v0 * t + 0.5 * a * t * t
- //
- // 参考:
- //
- // https://python3webspider.cuiqingcai.com/8.2-ji-yan-hua-dong-yan-zheng-ma-shi-bie
- func GenTracks(distance float64) []float64 {
- var track = make([]float64, 0) // 移动轨迹
- var current float64 = 0 // 当前位移
- var mid float64 = distance * 4 / 5 // 减速阈值
- var t float64 = 0.15 // 计算间隔
- var v float64 = 0 // 初速度
- var a1 = rng.Float64()*6 + 2 // 正加速度 [2,8)
- var a2 = -a1 - 2 // 负加速度 (-10,-4]
- for current < distance {
- // 加速度
- var a float64
- if current < mid {
- a = a1
- } else {
- a = -a2
- }
- // 初速度 v0
- v0 := v
- // 当前速度 v = v0 + at
- v = v0 + a*t
- // 移动距离 x = v0t + 1/2 * a * t^2
- x := v0*t + 1.0/2.0*a*t*t
- current += x
- track = append(track, x)
- }
- // 修正超标值
- track = append(track, distance-current)
- return track
- }
- func Trim(str interface{}) string {
- return strings.TrimSpace(fmt.Sprintf("%s", str))
- }
- // 生成随机数
- func RandSeq(n int) string {
- var letters = []rune("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ")
- b := make([]rune, n)
- for i := range b {
- b[i] = letters[rand.Intn(len(letters))]
- }
- return string(b)
- }
- func SwitchCwbb(baseCwbb, targetCwbb variable.Kjze, basezcfz []models.KjZcfz, baseLrb []models.KjLrb) (zcfz []models.KjZcfz, lrb []models.KjLrb, err error) {
- uri := `https://daizhang.jsptax.com/api/public/kjzzConvert`
- payload := map[string]interface{}{
- "oldKjzz": string(baseCwbb),
- "newKjzz": string(targetCwbb),
- "dataLrb": baseLrb,
- "dataZcfz": basezcfz,
- }
- data, _ := json.Marshal(payload)
- p := "./data/kjzzConvert" + GetTimestamp(13) + ".json"
- if !PathExists("pdf" + string(p)) {
- os.MkdirAll("pdf"+string(p), os.ModePerm)
- }
- logger.Info(p)
- _ = utils.OutputFile(p, data)
- req, err := http.NewRequest("POST", uri, bytes.NewReader(data))
- if err != nil {
- fmt.Println(err)
- return
- }
- req.Header.Add("Content-Type", "application/json")
- client := &http.Client{}
- res, err := client.Do(req)
- if err != nil {
- fmt.Println(err)
- return
- }
- defer res.Body.Close()
- body, err := io.ReadAll(res.Body)
- if err != nil {
- fmt.Println(err)
- return
- }
- if gjson.GetBytes(body, "msg").String() != "success" {
- return nil, nil, taxerr.NewUserV3(gjson.GetBytes(body, "msg").String(), "请核实")
- }
- _ = json.Unmarshal([]byte(gjson.GetBytes(body, "data.lrb").String()), &lrb)
- _ = json.Unmarshal([]byte(gjson.GetBytes(body, "data.zcfz").String()), &zcfz)
- return
- }
- // 获取省份
- func GetProvince(url string) string {
- if strings.Contains(url, "sichuan") {
- return "四川省"
- } else if strings.Contains(url, "beijing") {
- return "北京市"
- } else if strings.Contains(url, "tianjin") {
- return "天津市"
- } else if strings.Contains(url, "hebei") {
- return "河北省"
- } else if strings.Contains(url, "shanxi") {
- return "山西省"
- } else if strings.Contains(url, "neimenggu") {
- return "内蒙古自治区"
- } else if strings.Contains(url, "liaoning") {
- return "辽宁省"
- } else if strings.Contains(url, "dalian") {
- return "大连市"
- } else if strings.Contains(url, "jilin") {
- return "吉林省"
- } else if strings.Contains(url, "heilongjiang") {
- return "黑龙江省"
- } else if strings.Contains(url, "shanghai") {
- return "上海市"
- } else if strings.Contains(url, "jiangsu") {
- return "江苏省"
- } else if strings.Contains(url, "zhejiang") {
- return "浙江省"
- } else if strings.Contains(url, "ningbo") {
- return "宁波市"
- } else if strings.Contains(url, "anhui") {
- return "安徽省"
- } else if strings.Contains(url, "fujian") {
- return "福建省"
- } else if strings.Contains(url, "xiamen") {
- return "厦门市"
- } else if strings.Contains(url, "jiangxi") {
- return "江西省"
- } else if strings.Contains(url, "shandong") {
- return "山东省"
- } else if strings.Contains(url, "qingdao") {
- return "青岛市"
- } else if strings.Contains(url, "henan") {
- return "河南省"
- } else if strings.Contains(url, "hubei") {
- return "湖北省"
- } else if strings.Contains(url, "hunan") {
- return "湖南省"
- } else if strings.Contains(url, "guangdong") {
- return "广东省"
- } else if strings.Contains(url, "shenzhen") {
- return "深圳市"
- } else if strings.Contains(url, "guangxi") {
- return "广西壮族自治区"
- } else if strings.Contains(url, "hainan") {
- return "海南省"
- } else if strings.Contains(url, "chongqing") {
- return "重庆市"
- } else if strings.Contains(url, "guizhou") {
- return "贵州省"
- } else if strings.Contains(url, "yunnan") {
- return "云南省"
- } else if strings.Contains(url, "xizang") {
- return "西藏自治区"
- } else if strings.Contains(url, "shaanxi") {
- return "陕西省"
- } else if strings.Contains(url, "gansu") {
- return "甘肃省"
- } else if strings.Contains(url, "qinghai") {
- return "青海省"
- } else if strings.Contains(url, "ningxia") {
- return "宁夏回族自治区"
- } else if strings.Contains(url, "xinjiang") {
- return "新疆维吾尔自治区"
- } else if strings.Contains(url, "taiwan") {
- return "台湾省"
- } else {
- return ""
- }
- }
- // 输入文字
- func InputX(p *rod.Page, xPath, inputVal string) {
- err := rod.Try(func() {
- if inputVal == "" {
- inputVal = "0"
- }
- _ = rod.Try(func() {
- elementX := MustElementX(p, xPath)
- disabled := elementX.MustAttribute(`disabled`)
- if disabled == nil {
- elementX.MustType(input.Backspace)
- elementX.MustSelectAllText().MustInput(inputVal)
- }
- })
- })
- if err != nil {
- logger.Info(err.Error() + "-" + xPath + "-" + inputVal)
- }
- }
- // 校验填写的对不对
- func CheckInput(p *rod.Page, xPath, inputVal string, name string) (errMsg string) {
- if inputVal == "" {
- inputVal = "0"
- }
- val := GetInputValue(p, xPath)
- if StrToFloat(val) != StrToFloat(inputVal) {
- errMsg = fmt.Sprintf("%s不一致,税局:%s,系统:%s;", name, val, inputVal)
- }
- return
- }
- func SaveCookiesNoPhone(key string) error {
- var a interface{}
- _ = lxhttp.PostJson(variable.SessionKeepURL+"/api/v1/session/save", map[string]interface{}{
- "sessionKey": key,
- "tsessionKey": key,
- }, &a)
- logger.Info("SaveSessionKey:", key)
- return nil
- }
- func SaveErrImgLv(lv LvName, p *rod.Page, info models.CompanyInfo, location string) string {
- checkPath := GenerateCheckImagePath(lv, info.TaxNo, info.Period, location)
- p.Timeout(ClickTimeOut).MustScreenshot(checkPath)
- _ = watermark.Add(checkPath)
- checkPath2 := PostSbjt(info.TaxNo, info.Period, checkPath)
- _ = os.Remove(checkPath)
- return checkPath2
- }
- func CheckError(ctx context.Context, task *models.TaxTask, err error) {
- errStr := HandleError(ctx, err).Error()
- if strings.Contains(errStr, "网页") {
- //task.Result.Status = variable.TaxFail
- //task.Result.ErrLog = errStr
- task.Result.BusinessStatus = variable.TaxFail
- task.Result.BusinessLog = errStr
- } else {
- task.Result.BusinessStatus = variable.TaxFail
- task.Result.BusinessLog = errStr
- }
- }
- // 时间格式化
- func TimeFormatM(str string, Type int) string {
- if str == "" {
- return ""
- }
- //1.str = 2035年6月23日
- if Type == 1 {
- data, _ := time.Parse("2006年1月2日", str)
- return data.Format("2006-01-02")
- }
- return str
- }
- // 去空格
- func DelSpace(str string) string {
- return strings.Replace(str, " ", "", -1)
- }
- // 登录异常统一提示
- func LoginErr() *taxerr.SystemErr {
- return taxerr.NewSystemV3("登录出现异常", "系统将于30分钟后重试(可在\"通用设置\"关闭)")
- }
- func GetMobileOnline(mobile string) bool {
- type getAppHeartbeatSmsRes struct {
- Data struct {
- List []struct {
- ID int `json:"ID"`
- CreatedAt time.Time `json:"CreatedAt"`
- UpdatedAt time.Time `json:"UpdatedAt"`
- Mobile string `json:"Mobile"`
- Heartbeat time.Time `json:"Heartbeat"`
- Status string `json:"status"`
- } `json:"list"`
- Total int `json:"total"`
- } `json:"data"`
- ErrNo int `json:"errNo"`
- Msg string `json:"msg"`
- }
- resp, err := http.Post(`https://public.listensoft.net/api/getAppHeartbeatSms`, "application/json", bytes.NewBufferString(`{"mobile":"`+mobile+`"}`))
- if err != nil {
- return false
- }
- all, err := io.ReadAll(resp.Body)
- logger.Info("getAppHeartbeatSms:", string(all))
- res := getAppHeartbeatSmsRes{}
- _ = json.Unmarshal(all, &res)
- for _, m := range res.Data.List {
- if m.Mobile == mobile && m.Status == "ok" {
- return true
- }
- }
- return false
- }
|