long2short.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "git.aionnect.com/aionnect/go-common/utils"
  6. "git.aionnect.com/aionnect/go-common/utils/jsonutil"
  7. "github.com/tealeg/xlsx"
  8. "net/http"
  9. "strings"
  10. "sync"
  11. )
  12. /*
  13. 调用微信公众平台接口,长链接批量转短链接小工具
  14. long2short.exe 是Windows版本
  15. long2short 是Mac版本
  16. 不是直接双击使用,需要在控制台或终端中输入命令调用
  17. 用 long2short -help 查看命令参数说明
  18. 使用步骤:
  19. 1. 准备好包含长链接的Excel文件,文件务必保存为xlsx格式
  20. 2. 问研发或管理员获取最新有效的微信公众号AccessToken
  21. 3. 打开Windows控制台或Mac终端,执行类似下面的命令
  22. Windows:
  23. .\long2short.exe -i .\input.xlsx -o .\output.xlsx -h 长链接 -t AccessToken的值
  24. Mac:
  25. ./long2short -i ./input.xlsx -o ./output.xlsx -h 长链接 -t AccessToken的值
  26. 4. 如果出错,会在命令窗口打印错误信息,如果顺利,会在程序所在目录下输出新的xlsx文件
  27. */
  28. func main() {
  29. var inputFileName string
  30. var accessToken string
  31. var headerName string
  32. var outputFileName string
  33. flag.StringVar(&inputFileName, "i", "input.xlsx", "xlsx输入文件路径,默认值\"input.xlsx\"")
  34. flag.StringVar(&accessToken, "t", "", "Access Token,必填")
  35. flag.StringVar(&headerName, "h", "合成链接", "长链接列名,默认值\"合成链接\"")
  36. flag.StringVar(&outputFileName, "o", "output.xlsx", "xlsx输出文件路径,会在文件名后加上时间戳,默认值\"output.xlsx\"")
  37. flag.Parse()
  38. if accessToken == "" {
  39. println("-t AccessToken 参数是必须的")
  40. return
  41. }
  42. reqUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/shorturl?access_token=%s", accessToken)
  43. file, err := xlsx.OpenFile(inputFileName)
  44. if err != nil {
  45. panic(fmt.Sprintf("读取输入文件%s失败 %s", inputFileName, err.Error()))
  46. }
  47. sheets := file.Sheets
  48. if nil == sheets || len(sheets) == 0 {
  49. println("输入文件中没有工作表")
  50. return
  51. }
  52. for _, sheet := range file.Sheets {
  53. rows := sheet.Rows
  54. if nil == rows || len(rows) < 2 {
  55. fmt.Printf("工作表%s中没有数据\n", sheet.Name)
  56. continue
  57. }
  58. header := rows[0]
  59. idx := -1
  60. for i, cell := range header.Cells {
  61. text := cell.String()
  62. if strings.TrimSpace(text) == headerName {
  63. idx = i
  64. }
  65. }
  66. if idx < 0 {
  67. fmt.Printf("工作表%s中没有找到长链接列名列名\"%s\"\n", sheet.Name, headerName)
  68. continue
  69. }
  70. newHeader := header.AddCell()
  71. newHeader.SetString("短链接")
  72. length := len(rows) - 1
  73. pNo := 1
  74. if length > 5 {
  75. pNo = 5
  76. }
  77. if length > 100 {
  78. pNo = 10
  79. }
  80. if length > 500 {
  81. pNo = 100
  82. }
  83. size := length / pNo
  84. var firstArr []*RowInfo
  85. rowGroup := [][]*RowInfo{firstArr}
  86. var j, ct int
  87. for i := 1; i < len(rows); i++ {
  88. rowGroup[j] = append(rowGroup[j], &RowInfo{Row: rows[i], RowNo: i + 1})
  89. ct++
  90. if ct >= size {
  91. var newArr []*RowInfo
  92. rowGroup = append(rowGroup, newArr)
  93. ct = 0
  94. j++
  95. }
  96. }
  97. var counter sync.WaitGroup
  98. counter.Add(length)
  99. for i := 0; i < len(rowGroup); i++ {
  100. arr := rowGroup[i]
  101. go func(reqUrl string, idx int, sheetName string, arr []*RowInfo) {
  102. for j := 0; j < len(arr); j++ {
  103. info := arr[j]
  104. call(reqUrl, idx, sheetName, info)
  105. counter.Done()
  106. }
  107. }(reqUrl, idx, sheet.Name, arr)
  108. }
  109. counter.Wait()
  110. }
  111. dotIdx := strings.LastIndex(outputFileName, ".")
  112. outputFileName = fmt.Sprintf("%s_%d.xlsx", outputFileName[:dotIdx], utils.NextId())
  113. err = file.Save(outputFileName)
  114. if nil != err {
  115. panic(fmt.Sprintf("写输出文件%s失败 %s", outputFileName, err.Error()))
  116. }
  117. }
  118. func call(reqUrl string, idx int, sheetName string, info *RowInfo) {
  119. row := info.Row
  120. var longUrl string
  121. if len(row.Cells) > idx {
  122. longUrl = row.Cells[idx].String()
  123. }
  124. longUrl = strings.TrimSpace(longUrl)
  125. if longUrl == "" {
  126. fmt.Printf("工作表%s中第%d行长链接值为空\n", sheetName, info.RowNo)
  127. return
  128. }
  129. reqBody := fmt.Sprintf(`{"action":"long2short","long_url":"%s"}`, longUrl)
  130. respBody, err := utils.NewRequest().Call(http.MethodPost, reqUrl, strings.NewReader(reqBody))
  131. if nil != err {
  132. fmt.Printf("工作表%s中第%d行请求短链接失败 %s\n", sheetName, info.RowNo, err.Error())
  133. return
  134. }
  135. var res ShortResult
  136. err = jsonutil.Unmarshal(respBody, &res)
  137. if nil != err {
  138. fmt.Printf("工作表%s中第%d行请求短链接返回了意外的结果 %s\n", sheetName, info.RowNo, string(respBody))
  139. return
  140. }
  141. res.ShortUrl = strings.TrimSpace(res.ShortUrl)
  142. if res.ErrCode != 0 || res.ShortUrl == "" {
  143. fmt.Printf("工作表%s中第%d行请求短链接出错 %s\n", sheetName, info.RowNo, string(respBody))
  144. return
  145. }
  146. newCell := row.AddCell()
  147. newCell.SetString(res.ShortUrl)
  148. }
  149. type RowInfo struct {
  150. Row *xlsx.Row
  151. RowNo int
  152. }
  153. type ShortResult struct {
  154. ErrCode int `json:"errcode"`
  155. ErrMsg string `json:"errmsg"`
  156. ShortUrl string `json:"short_url"`
  157. }