utils.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. package common
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/md5"
  6. "encoding/hex"
  7. "encoding/json"
  8. "fmt"
  9. "git.listensoft.net/tool/jspkit/common/lxhttp"
  10. "git.listensoft.net/tool/jspkit/common/models"
  11. "git.listensoft.net/tool/jspkit/common/variable"
  12. "git.listensoft.net/tool/jspkit/common/watermark"
  13. "git.listensoft.net/tool/jspkit/logger"
  14. "git.listensoft.net/tool/jspkit/taxerr"
  15. "github.com/go-kratos/kratos/v2/log"
  16. "github.com/go-rod/rod"
  17. "github.com/go-rod/rod/lib/input"
  18. "github.com/go-rod/rod/lib/utils"
  19. "github.com/tidwall/gjson"
  20. "io"
  21. "math/rand"
  22. "net/http"
  23. "net/url"
  24. "os"
  25. "reflect"
  26. "regexp"
  27. "strings"
  28. "time"
  29. )
  30. type LvName string
  31. const (
  32. CheckSuccess LvName = "申报成功" // 检查成功 ------ 检查完全通过
  33. CheckFail LvName = "检查失败" // 检查任务失败 ----- 网页打开失败等
  34. CheckOmit LvName = "有遗漏" // 检查有遗漏 ----- 未申报问题
  35. CheckAbnormal LvName = "有异常" //检查有异常 ----- 税款问题
  36. )
  37. func GeneratePath(suffix string) string {
  38. ps := os.PathSeparator
  39. //检查路径
  40. if !PathExists("captcha" + string(ps)) {
  41. _ = os.MkdirAll("captcha"+string(ps), os.ModePerm)
  42. }
  43. return "captcha" + string(ps) + GetTimestamp(13) + "." + suffix
  44. }
  45. func PathExists(path string) bool {
  46. _, err := os.Stat(path)
  47. if err == nil {
  48. return true
  49. }
  50. if os.IsNotExist(err) {
  51. return false
  52. }
  53. return false
  54. }
  55. // 返回一个32位md5加密后的字符串
  56. func Md5(data string) string {
  57. h := md5.New()
  58. h.Write([]byte(data))
  59. return hex.EncodeToString(h.Sum(nil))
  60. }
  61. /*
  62. 生成检查截图本地路径
  63. lv 图片对应的错误等级 用上面定义好的 Omit>Abnormal
  64. taxNo 税号
  65. period 账期
  66. location 截图位置,例如申报清册、申报结果查询、税款缴纳、缴费缴纳查询
  67. */
  68. func GenerateCheckImagePath(lv LvName, taxNo, period, location string) string {
  69. var path string
  70. path = "./data/check/" + taxNo + "/"
  71. if !PathExists(path) {
  72. os.MkdirAll(path, os.ModePerm)
  73. }
  74. return fmt.Sprintf(`%s-%s-%s-新版_%s_%s.png`, path+taxNo, period, GetTimestamp(13), location, lv)
  75. }
  76. func PostSbjt(taxno, period, path string) string {
  77. var params = map[string]string{
  78. "tax_no": taxno,
  79. "period": period,
  80. "type": "etax", //电子税务局
  81. }
  82. str, err := PostFile("https://file.listensoft.net/api/v1/uploadsbjt", map[string]string{
  83. "file": path,
  84. }, params)
  85. if err != nil {
  86. log.Warnf("【%s】文件上传失败", path)
  87. }
  88. return gjson.GetBytes(str, "path").String()
  89. }
  90. // 获取申报截图的path
  91. // taxNo 纳税人识别号 period 账期 taxType 任务名称
  92. func GetSbjtPath(taxNo, period string, taxType variable.TaskName) string {
  93. var path string
  94. path = "./data/" + taxNo + "/"
  95. if !PathExists(path) {
  96. os.MkdirAll(path, os.ModePerm)
  97. }
  98. path = path + period + string(taxType) + "_" + GetTimestamp(13) + ".png"
  99. return path
  100. }
  101. // 判断字符串是否全是英文
  102. func IsAllEnglish(s string) bool {
  103. // 检查字符串是否全部由ASCII字符组成
  104. for _, c := range s {
  105. if c > 127 {
  106. return false
  107. }
  108. }
  109. return true
  110. }
  111. // CreateFormReader 将 map 转换成 header
  112. func CreateFormReader(data map[string]string) io.Reader {
  113. form := url.Values{}
  114. for k, v := range data {
  115. form.Add(k, v)
  116. }
  117. return strings.NewReader(form.Encode())
  118. }
  119. func Unbracket(str string) string {
  120. str = strings.ReplaceAll(str, "(", "(")
  121. str = strings.ReplaceAll(str, ")", ")")
  122. str = strings.ReplaceAll(str, " ", "")
  123. return str
  124. }
  125. // 检查是否是手机号
  126. func CheckMobile(mobile string) bool {
  127. r := regexp.MustCompile(`^1[0-9]{10}$`)
  128. return r.Match([]byte(mobile))
  129. }
  130. func NewWebStuckTitle() *taxerr.SystemErr {
  131. return taxerr.NewSystemV3("电子税局页面卡顿", "系统将于30分钟后重试(可在\"通用设置\"关闭)")
  132. }
  133. // 查看设备是否在线
  134. func CheckEqmLive(tel string) bool {
  135. client := lxhttp.NewHttpClient()
  136. defer client.CloseIdleConnections()
  137. bys, err := lxhttp.POSTJson(client, "https://keep.jsptax.com"+"/api/v1/telDeviceStatus", map[string]string{"mobiles": tel}, map[string]string{})
  138. if err != nil {
  139. return false
  140. }
  141. if gjson.GetBytes(bys, `data.status`).Bool() {
  142. return true
  143. }
  144. return false
  145. }
  146. func GetData(key variable.SbKey, data string, target interface{}) bool {
  147. str := gjson.Get(data, string(key)).String()
  148. if err := MapToStruct(str, target); err != nil {
  149. logger.Info(err)
  150. return false
  151. }
  152. return true
  153. }
  154. // target 传指针
  155. func MapToStruct(source interface{}, target interface{}) error {
  156. resByre := []byte("")
  157. var resByteErr error
  158. if reflect.TypeOf(source).String() == "string" {
  159. str := source.(string)
  160. resByre = []byte(str)
  161. } else {
  162. resByre, resByteErr = json.Marshal(source)
  163. if resByteErr != nil {
  164. return resByteErr
  165. }
  166. }
  167. err := json.Unmarshal(resByre, target)
  168. if err != nil {
  169. return err
  170. }
  171. return nil
  172. }
  173. // CancelLeftTasks 撤销其他的任务,避免一直发验证码导致超限,仅新版登录使用
  174. func CancelLeftTasks(tel string) {
  175. c := lxhttp.NewHttpClient()
  176. lxhttp.POSTJson(c, variable.TaxTaskURL+"/api/v1/stopQueue", map[string]string{"tel": tel}, nil)
  177. }
  178. // GetTelTaskStatus 如果有任务正在进行 返回 true
  179. func GetTelTaskStatus(tel string) (bool, error) {
  180. c := lxhttp.NewHttpClient()
  181. bys, err := lxhttp.POSTJson(c, `https://task.listensoft.net/api/v1/other/telTaskDoi`, map[string]string{
  182. "tel": tel,
  183. }, nil)
  184. if err != nil {
  185. return false, err
  186. }
  187. if gjson.GetBytes(bys, `data.res`).Bool() {
  188. return true, nil
  189. } else {
  190. return false, nil
  191. }
  192. }
  193. var rng = rand.New(rand.NewSource(time.Now().UnixMicro()))
  194. // GenTracks 函数生成移动轨迹。先做加速运动,再做减速运动。
  195. //
  196. // 设滑块滑动的加速度为 a;当前速度为 v;初速度为 v0;位移为 x;所需时间为 t。
  197. // 根据加速度物理公式,它们满足以下关系:
  198. // v = v0 + a * t
  199. // x = v0 * t + 0.5 * a * t * t
  200. //
  201. // 参考:
  202. //
  203. // https://python3webspider.cuiqingcai.com/8.2-ji-yan-hua-dong-yan-zheng-ma-shi-bie
  204. func GenTracks(distance float64) []float64 {
  205. var track = make([]float64, 0) // 移动轨迹
  206. var current float64 = 0 // 当前位移
  207. var mid float64 = distance * 4 / 5 // 减速阈值
  208. var t float64 = 0.15 // 计算间隔
  209. var v float64 = 0 // 初速度
  210. var a1 = rng.Float64()*6 + 2 // 正加速度 [2,8)
  211. var a2 = -a1 - 2 // 负加速度 (-10,-4]
  212. for current < distance {
  213. // 加速度
  214. var a float64
  215. if current < mid {
  216. a = a1
  217. } else {
  218. a = -a2
  219. }
  220. // 初速度 v0
  221. v0 := v
  222. // 当前速度 v = v0 + at
  223. v = v0 + a*t
  224. // 移动距离 x = v0t + 1/2 * a * t^2
  225. x := v0*t + 1.0/2.0*a*t*t
  226. current += x
  227. track = append(track, x)
  228. }
  229. // 修正超标值
  230. track = append(track, distance-current)
  231. return track
  232. }
  233. func Trim(str interface{}) string {
  234. return strings.TrimSpace(fmt.Sprintf("%s", str))
  235. }
  236. // 生成随机数
  237. func RandSeq(n int) string {
  238. var letters = []rune("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  239. b := make([]rune, n)
  240. for i := range b {
  241. b[i] = letters[rand.Intn(len(letters))]
  242. }
  243. return string(b)
  244. }
  245. func SwitchCwbb(baseCwbb, targetCwbb variable.Kjze, basezcfz []models.KjZcfz, baseLrb []models.KjLrb) (zcfz []models.KjZcfz, lrb []models.KjLrb, err error) {
  246. uri := `https://daizhang.jsptax.com/api/public/kjzzConvert`
  247. payload := map[string]interface{}{
  248. "oldKjzz": string(baseCwbb),
  249. "newKjzz": string(targetCwbb),
  250. "dataLrb": baseLrb,
  251. "dataZcfz": basezcfz,
  252. }
  253. data, _ := json.Marshal(payload)
  254. p := "./data/kjzzConvert" + GetTimestamp(13) + ".json"
  255. if !PathExists("pdf" + string(p)) {
  256. os.MkdirAll("pdf"+string(p), os.ModePerm)
  257. }
  258. logger.Info(p)
  259. _ = utils.OutputFile(p, data)
  260. req, err := http.NewRequest("POST", uri, bytes.NewReader(data))
  261. if err != nil {
  262. fmt.Println(err)
  263. return
  264. }
  265. req.Header.Add("Content-Type", "application/json")
  266. client := &http.Client{}
  267. res, err := client.Do(req)
  268. if err != nil {
  269. fmt.Println(err)
  270. return
  271. }
  272. defer res.Body.Close()
  273. body, err := io.ReadAll(res.Body)
  274. if err != nil {
  275. fmt.Println(err)
  276. return
  277. }
  278. if gjson.GetBytes(body, "msg").String() != "success" {
  279. return nil, nil, taxerr.NewUserV3(gjson.GetBytes(body, "msg").String(), "请核实")
  280. }
  281. _ = json.Unmarshal([]byte(gjson.GetBytes(body, "data.lrb").String()), &lrb)
  282. _ = json.Unmarshal([]byte(gjson.GetBytes(body, "data.zcfz").String()), &zcfz)
  283. return
  284. }
  285. // 获取省份
  286. func GetProvince(url string) string {
  287. if strings.Contains(url, "sichuan") {
  288. return "四川省"
  289. } else if strings.Contains(url, "beijing") {
  290. return "北京市"
  291. } else if strings.Contains(url, "tianjin") {
  292. return "天津市"
  293. } else if strings.Contains(url, "hebei") {
  294. return "河北省"
  295. } else if strings.Contains(url, "shanxi") {
  296. return "山西省"
  297. } else if strings.Contains(url, "neimenggu") {
  298. return "内蒙古自治区"
  299. } else if strings.Contains(url, "liaoning") {
  300. return "辽宁省"
  301. } else if strings.Contains(url, "dalian") {
  302. return "大连市"
  303. } else if strings.Contains(url, "jilin") {
  304. return "吉林省"
  305. } else if strings.Contains(url, "heilongjiang") {
  306. return "黑龙江省"
  307. } else if strings.Contains(url, "shanghai") {
  308. return "上海市"
  309. } else if strings.Contains(url, "jiangsu") {
  310. return "江苏省"
  311. } else if strings.Contains(url, "zhejiang") {
  312. return "浙江省"
  313. } else if strings.Contains(url, "ningbo") {
  314. return "宁波市"
  315. } else if strings.Contains(url, "anhui") {
  316. return "安徽省"
  317. } else if strings.Contains(url, "fujian") {
  318. return "福建省"
  319. } else if strings.Contains(url, "xiamen") {
  320. return "厦门市"
  321. } else if strings.Contains(url, "jiangxi") {
  322. return "江西省"
  323. } else if strings.Contains(url, "shandong") {
  324. return "山东省"
  325. } else if strings.Contains(url, "qingdao") {
  326. return "青岛市"
  327. } else if strings.Contains(url, "henan") {
  328. return "河南省"
  329. } else if strings.Contains(url, "hubei") {
  330. return "湖北省"
  331. } else if strings.Contains(url, "hunan") {
  332. return "湖南省"
  333. } else if strings.Contains(url, "guangdong") {
  334. return "广东省"
  335. } else if strings.Contains(url, "shenzhen") {
  336. return "深圳市"
  337. } else if strings.Contains(url, "guangxi") {
  338. return "广西壮族自治区"
  339. } else if strings.Contains(url, "hainan") {
  340. return "海南省"
  341. } else if strings.Contains(url, "chongqing") {
  342. return "重庆市"
  343. } else if strings.Contains(url, "guizhou") {
  344. return "贵州省"
  345. } else if strings.Contains(url, "yunnan") {
  346. return "云南省"
  347. } else if strings.Contains(url, "xizang") {
  348. return "西藏自治区"
  349. } else if strings.Contains(url, "shaanxi") {
  350. return "陕西省"
  351. } else if strings.Contains(url, "gansu") {
  352. return "甘肃省"
  353. } else if strings.Contains(url, "qinghai") {
  354. return "青海省"
  355. } else if strings.Contains(url, "ningxia") {
  356. return "宁夏回族自治区"
  357. } else if strings.Contains(url, "xinjiang") {
  358. return "新疆维吾尔自治区"
  359. } else if strings.Contains(url, "taiwan") {
  360. return "台湾省"
  361. } else {
  362. return ""
  363. }
  364. }
  365. // 输入文字
  366. func InputX(p *rod.Page, xPath, inputVal string) {
  367. err := rod.Try(func() {
  368. if inputVal == "" {
  369. inputVal = "0"
  370. }
  371. _ = rod.Try(func() {
  372. elementX := MustElementX(p, xPath)
  373. disabled := elementX.MustAttribute(`disabled`)
  374. if disabled == nil {
  375. elementX.MustType(input.Backspace)
  376. elementX.MustSelectAllText().MustInput(inputVal)
  377. }
  378. })
  379. })
  380. if err != nil {
  381. logger.Info(err.Error() + "-" + xPath + "-" + inputVal)
  382. }
  383. }
  384. // 校验填写的对不对
  385. func CheckInput(p *rod.Page, xPath, inputVal string, name string) (errMsg string) {
  386. if inputVal == "" {
  387. inputVal = "0"
  388. }
  389. val := GetInputValue(p, xPath)
  390. if StrToFloat(val) != StrToFloat(inputVal) {
  391. errMsg = fmt.Sprintf("%s不一致,税局:%s,系统:%s;", name, val, inputVal)
  392. }
  393. return
  394. }
  395. func SaveCookiesNoPhone(key string) error {
  396. var a interface{}
  397. _ = lxhttp.PostJson(variable.SessionKeepURL+"/api/v1/session/save", map[string]interface{}{
  398. "sessionKey": key,
  399. "tsessionKey": key,
  400. }, &a)
  401. logger.Info("SaveSessionKey:", key)
  402. return nil
  403. }
  404. func SaveErrImgLv(lv LvName, p *rod.Page, info models.CompanyInfo, location string) string {
  405. checkPath := GenerateCheckImagePath(lv, info.TaxNo, info.Period, location)
  406. p.Timeout(ClickTimeOut).MustScreenshot(checkPath)
  407. _ = watermark.Add(checkPath)
  408. checkPath2 := PostSbjt(info.TaxNo, info.Period, checkPath)
  409. _ = os.Remove(checkPath)
  410. return checkPath2
  411. }
  412. func CheckError(ctx context.Context, task *models.TaxTask, err error) {
  413. errStr := HandleError(ctx, err).Error()
  414. if strings.Contains(errStr, "网页") {
  415. //task.Result.Status = variable.TaxFail
  416. //task.Result.ErrLog = errStr
  417. task.Result.BusinessStatus = variable.TaxFail
  418. task.Result.BusinessLog = errStr
  419. } else {
  420. task.Result.BusinessStatus = variable.TaxFail
  421. task.Result.BusinessLog = errStr
  422. }
  423. }
  424. // 时间格式化
  425. func TimeFormatM(str string, Type int) string {
  426. if str == "" {
  427. return ""
  428. }
  429. //1.str = 2035年6月23日
  430. if Type == 1 {
  431. data, _ := time.Parse("2006年1月2日", str)
  432. return data.Format("2006-01-02")
  433. }
  434. return str
  435. }
  436. // 去空格
  437. func DelSpace(str string) string {
  438. return strings.Replace(str, " ", "", -1)
  439. }