utils.go 11 KB

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