ila %!s(int64=4) %!d(string=hai) anos
achega
091f9f95c2
Modificáronse 30 ficheiros con 2008 adicións e 0 borrados
  1. 14 0
      .gitignore
  2. 13 0
      ReadMe.md
  3. 69 0
      core/archive.go
  4. 43 0
      core/chrome.go
  5. 13 0
      core/core.go
  6. 13 0
      core/data_format.go
  7. 91 0
      core/file.go
  8. 31 0
      core/log.go
  9. 27 0
      core/messagebox.go
  10. 19 0
      core/path.go
  11. 10 0
      core/string.go
  12. 14 0
      core/system.go
  13. 18 0
      core/win.go
  14. 14 0
      core/xdate.go
  15. 12 0
      go.mod
  16. 178 0
      go.sum
  17. 106 0
      image/img.go
  18. 28 0
      main.go
  19. 226 0
      office/excel.go
  20. 62 0
      office/pdf.go
  21. 107 0
      office/sharepoint.go
  22. 115 0
      xnet/chrome_driver.go
  23. 70 0
      xnet/cookie.go
  24. 95 0
      xnet/dingtalk_oa.go
  25. 79 0
      xnet/dingtalk_robot.go
  26. 80 0
      xnet/netflix.go
  27. 150 0
      xnet/powerbi_screenshot.go
  28. 149 0
      xnet/powerbi_url.go
  29. 1 0
      xnet/selenium.go
  30. 161 0
      xnet/xhttp.go

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+*.rar
+*.zip
+*.jpg
+*.jpeg
+*.png
+*.tiff
+*.HEIC
+*.doc
+*.docx
+*.wps
+*.pdf
+*.txt
+_gsdata_/
+.idea/

+ 13 - 0
ReadMe.md

@@ -0,0 +1,13 @@
+# GCore
+
+* 常用方法封装
+
+## 目录
+
+### core基础模块
+
+### image图像处理模块
+
+### net网络模块
+
+### office办公文档模块

+ 69 - 0
core/archive.go

@@ -0,0 +1,69 @@
+package core
+
+// @Title  archive
+// @Description  创建和释放压缩包
+// @Author  ila 2021-11-04 14:33:00
+// @Update  ila
+
+import (
+	"archive/zip"
+	"io"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// @title    Unzip
+// @description    解压zip文件到指定路径,只解压文件名包含contains字符串的文件.
+// @auth      ila 2021-11-04 14:33:00
+// @param     zipPath  zip文件路径,attachPath 解压路径,contains 包含的字符串
+// @return    error        是否成功
+func Unzip(zipPath, attachPath string, contains []string) error {
+
+	z, err := zip.OpenReader(zipPath)
+	defer z.Close()
+
+	if err != nil {
+		return err
+	}
+
+	for _, f := range z.File {
+		if f.FileInfo().IsDir() {
+			continue
+		}
+
+		//判断文件名包含所需字符
+		containFlag := false
+		for _, c := range contains {
+			if strings.Contains(f.Name, c) == true {
+				containFlag = true
+				break
+			}
+		}
+
+		//文件名没有包含所需字符,跳过
+		if containFlag == false {
+			continue
+		}
+		path := filepath.Join(attachPath, f.Name)
+
+		fo, err := f.Open()
+		defer fo.Close()
+		if err != nil {
+			log.Printf("zip file Open error :%v\n", err)
+			continue
+		}
+		fw, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, f.Mode())
+		defer fw.Close()
+		if err != nil {
+			continue
+		}
+		_, err = io.Copy(fw, fo)
+		if err != nil {
+			log.Printf("zip file write to attach error :%v\n", err)
+		}
+	}
+
+	return nil
+}

+ 43 - 0
core/chrome.go

@@ -0,0 +1,43 @@
+package core
+
+import (
+	"fmt"
+	"golang.org/x/sys/windows/registry"
+	"path/filepath"
+	"strconv"
+	"strings"
+)
+
+// @title    GetChromeUserDataDir
+// @description    获取windows当前用户的chrome用户目录
+// @param
+// @return chromeuserDataDir string
+func GetChromeUserDataDir() string {
+	return filepath.Join("C:/Users", GetWinUserName(), "AppData", "Local", "Google", "Chrome", "User Data")
+}
+
+func GetChromeVersionFromRegedit() int {
+	ChromeVersion := 90
+	key, err := registry.OpenKey(registry.CURRENT_USER, "Software\\Google\\Chrome\\BLBeacon", registry.ALL_ACCESS)
+
+	if err != nil {
+		LogWrite(fmt.Sprintf("GetChromeVersionFromRegedit OpenKey  error : %v\n", err))
+		return ChromeVersion
+	}
+	defer key.Close()
+
+	version, _, err := key.GetStringValue("version")
+	if err != nil {
+		LogWrite(fmt.Sprintf("GetChromeVersionFromRegedit OpenKey  error : %v\n", err))
+		return ChromeVersion
+	}
+
+	fmt.Printf("version======>%v\n", version)
+
+	rets := strings.Split(version, ".")
+	if len(rets) > 0 {
+		ChromeVersion, _ = strconv.Atoi(rets[0])
+	}
+	fmt.Printf("ChromeVersion======>%v\n", ChromeVersion)
+	return ChromeVersion
+}

+ 13 - 0
core/core.go

@@ -0,0 +1,13 @@
+package core
+
+import "sort"
+
+//In 判断字符串是否在数组里
+func In(target string, strArray []string) bool {
+	sort.Strings(strArray)
+	index := sort.SearchStrings(strArray, target)
+	if index < len(strArray) && strArray[index] == target {
+		return true
+	}
+	return false
+}

+ 13 - 0
core/data_format.go

@@ -0,0 +1,13 @@
+package core
+
+// @title StringInterfaceToStringString
+// @description 格式转换
+// @param
+// @return
+func StringInterfaceToStringString(srcData map[string]interface{}) (data map[string]string) {
+
+	for k, v := range srcData {
+		data[k] = v.(string)
+	}
+	return
+}

+ 91 - 0
core/file.go

@@ -0,0 +1,91 @@
+package core
+
+import (
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"sort"
+)
+
+// @title    FileExists
+// @description    检测文件是否存在
+// @param     filePath  文件路径
+// @return    bool 是否存在,error  是否成功
+func FileExists(filePath string) (bool, error) {
+	_, err := os.Stat(filePath)
+	if err == nil {
+		return true, nil
+	}
+	if os.IsNotExist(err) {
+		return false, nil
+	}
+	return false, err
+}
+
+// GetFileFromFolder 获取子文件夹里的文件名列表
+func GetFileFromFolder(folder string) ([]string, error) {
+	filePaths := []string{}
+	files, err := ioutil.ReadDir(folder)
+
+	if err != nil {
+		log.Printf("filepath.Glob error : %v\n", err)
+		return filePaths, err
+	}
+	for _, f := range files {
+		filePaths = append(filePaths, filepath.Join(folder, f.Name()))
+	}
+	return filePaths, nil
+}
+
+func RemoveFiles(folder, suffix string) error {
+	filePaths, err := GetFileFromFolder(folder)
+
+	if err != nil {
+		log.Printf("GetFileFromFolder error : %v\n", err)
+		return err
+	}
+
+	for _, filePath := range filePaths {
+
+		if filePath[len(filePath)-len(suffix):] == suffix {
+			err = os.Remove(filePath)
+			if err != nil {
+				log.Printf("Remove error : %v\n", err)
+			}
+
+		}
+	}
+
+	return nil
+}
+
+//2021-11-03
+func GetSubTitleByModTime(folder string) (string, error) {
+	filePaths := []string{}
+	files, err := ioutil.ReadDir(folder)
+
+	if err != nil {
+		log.Printf("filepath.Glob error : %v\n", err)
+		return filePaths[0], err
+	}
+
+	//按mod时间排序
+	sort.Slice(files, func(i, j int) bool {
+		flag := false
+		if files[i].ModTime().After(files[j].ModTime()) {
+			flag = true
+		} else if files[i].ModTime().Equal(files[j].ModTime()) {
+			if files[i].Name() < files[j].Name() {
+				flag = true
+			}
+		}
+		return flag
+	})
+
+	for _, f := range files {
+		filePaths = append(filePaths, filepath.Join(folder, f.Name()))
+	}
+
+	return filePaths[0], err
+}

+ 31 - 0
core/log.go

@@ -0,0 +1,31 @@
+package core
+
+import (
+	"log"
+	"os"
+	"strings"
+	"time"
+)
+
+// @title    XWarning
+// @description    warning日志输出
+// @param
+// @return
+func XWarning(content string) {
+	log.Printf(content)
+	LogWrite(content)
+}
+
+// @title    LogWrite
+// @description    log信息写入log文件
+// @param
+// @return
+func LogWrite(content string) {
+	//使用当前项目路径
+	fd, _ := os.OpenFile("log.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
+	fd_time := time.Now().Format("2006-01-02 15:04:05")
+	fd_content := strings.Join([]string{"======", fd_time, "=====", content, "\n"}, "")
+	buf := []byte(fd_content)
+	fd.Write(buf)
+	fd.Close()
+}

+ 27 - 0
core/messagebox.go

@@ -0,0 +1,27 @@
+package core
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+// @title    MsgBox
+// @description    弹出提示框
+// @param     title  提示框标题,content 提示框内容
+// @return
+func MsgBox(title, content string) {
+	user32dll, _ := syscall.LoadLibrary("user32.dll")
+	user32 := syscall.NewLazyDLL("user32.dll")
+	MessageBoxW := user32.NewProc("MessageBoxW")
+	MessageBoxW.Call(IntPtr(0), StrPtr(content), StrPtr(title), IntPtr(0))
+	defer syscall.FreeLibrary(user32dll)
+
+}
+
+func IntPtr(n int) uintptr {
+	return uintptr(n)
+}
+
+func StrPtr(s string) uintptr {
+	return uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s)))
+}

+ 19 - 0
core/path.go

@@ -0,0 +1,19 @@
+package core
+
+import "os"
+
+// @title    PathExists
+// @description     判断文件夹是否存在
+// @param    path string 文件夹路径
+// @return
+
+func PathExists(path string) (bool, error) {
+	_, err := os.Stat(path)
+	if err == nil {
+		return true, nil
+	}
+	if os.IsNotExist(err) {
+		return false, nil
+	}
+	return false, err
+}

+ 10 - 0
core/string.go

@@ -0,0 +1,10 @@
+package core
+
+func RuneFormat(data string) string {
+	content := ""
+	slen := []rune(data)
+	for i := 0; i < len(slen); i++ {
+		content += string(slen[i])
+	}
+	return content
+}

+ 14 - 0
core/system.go

@@ -0,0 +1,14 @@
+package core
+
+import (
+	"runtime"
+)
+
+// @title    GetSysType
+// @description    获取系统版本
+// @param
+// @return
+func GetSysType() string {
+
+	return runtime.GOOS
+}

+ 18 - 0
core/win.go

@@ -0,0 +1,18 @@
+package core
+
+import "syscall"
+
+// @title    GetWinUserName
+// @description    获取windows当前登录用户名
+// @param
+// @return username string 用户名
+func GetWinUserName() string {
+	var size uint32 = 128
+	var buffer = make([]uint16, size)
+	user := syscall.StringToUTF16Ptr("USERNAME")
+
+	//获取当前用户名
+	syscall.GetEnvironmentVariable(user, &buffer[0], size)
+
+	return syscall.UTF16ToString(buffer)
+}

+ 14 - 0
core/xdate.go

@@ -0,0 +1,14 @@
+package core
+
+import "time"
+
+func GetWorkDate(days int) (workData []string, err error) {
+	cts := time.Now().Unix()
+
+	for index, _ := range [30]int{} {
+		ts := cts - 60*60*24*int64(index)
+		workData = append([]string{time.Unix(ts, 0).Format("2006/01/02")}, workData...)
+	}
+
+	return
+}

+ 12 - 0
go.mod

@@ -0,0 +1,12 @@
+module gcore
+
+go 1.16
+
+require (
+	github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect
+	github.com/blinkbean/dingtalk v0.0.0-20210905093040-7d935c0f7e19 // indirect
+	github.com/go-ole/go-ole v1.2.6 // indirect
+	github.com/koltyakov/gosip v0.0.0-20211102125300-4724730f3af9 // indirect
+	github.com/tebeka/selenium v0.9.9 // indirect
+	github.com/zellyn/kooky v0.0.0-20210408152652-87b89e95f98f // indirect
+)

+ 178 - 0
go.sum

@@ -0,0 +1,178 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg=
+github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
+github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
+github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/blinkbean/dingtalk v0.0.0-20210905093040-7d935c0f7e19 h1:pamuM2sgLJLoMWfchc6y071z8ifalajU7btZmZNhoH4=
+github.com/blinkbean/dingtalk v0.0.0-20210905093040-7d935c0f7e19/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto=
+github.com/bobesa/go-domain-util v0.0.0-20190911083921-4033b5f7dd89/go.mod h1:/09nEjna1UMoasyyQDhOrIn8hi2v2kiJglPWed1idck=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
+github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
+github.com/go-ini/ini v1.62.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-sqlite/sqlite3 v0.0.0-20180313105335-53dd8e640ee7 h1:ow5vK9Q/DSKkxbEIJHBST6g+buBDwdaDIyk1dGGwpQo=
+github.com/go-sqlite/sqlite3 v0.0.0-20180313105335-53dd8e640ee7/go.mod h1:JxSQ+SvsjFb+p8Y+bn+GhTkiMfKVGBD0fq43ms2xw04=
+github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
+github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/gonuts/binary v0.2.0 h1:caITwMWAoQWlL0RNvv2lTU/AHqAJlVuu6nZmNgfbKW4=
+github.com/gonuts/binary v0.2.0/go.mod h1:kM+CtBrCGDSKdv8WXTuCUsw+loiy8f/QEI8YCCC0M/E=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d h1:gVjhBCfVGl32RIBooOANzfw+0UqX8HU+yPlMv8vypcg=
+github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d/go.mod h1:W6EbaYmb4RldPn0N3gvVHjY1wmU59kbymhW9NATWhwY=
+github.com/keybase/go.dbus v0.0.0-20200324223359-a94be52c0b03/go.mod h1:a8clEhrrGV/d76/f9r2I41BwANMihfZYV9C223vaxqE=
+github.com/koltyakov/gosip v0.0.0-20211102125300-4724730f3af9 h1:bihYsHjSJTB/gSe20XV9MJZYxkpbQD6DiBtHl0Edtwg=
+github.com/koltyakov/gosip v0.0.0-20211102125300-4724730f3af9/go.mod h1:4HuIfGaQJGf43ZkvltQoTjCLoTkCcbD0kLLx2G92hUs=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
+github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/tebeka/selenium v0.9.9 h1:cNziB+etNgyH/7KlNI7RMC1ua5aH1+5wUlFQyzeMh+w=
+github.com/tebeka/selenium v0.9.9/go.mod h1:5Fr8+pUvU6B1OiPfkdCKdXZyr5znvVkxuPd0NOdZCQc=
+github.com/zalando/go-keyring v0.1.0 h1:ffq972Aoa4iHNzBlUHgK5Y+k8+r/8GvcGd80/OFZb/k=
+github.com/zalando/go-keyring v0.1.0/go.mod h1:RaxNwUITJaHVdQ0VC7pELPZ3tOWn13nr0gZMZEhpVU0=
+github.com/zellyn/kooky v0.0.0-20210408152652-87b89e95f98f h1:Oxzmfe0Xoum87EDOWZkTZKVkhDnArW8t1OQ3KnYT600=
+github.com/zellyn/kooky v0.0.0-20210408152652-87b89e95f98f/go.mod h1:QM4+3a3KkwFXtuTz+w41LBKU+MW7KecHBn0bNalcPKA=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c h1:QOfDMdrf/UwlVR0UBq2Mpr58UzNtvgJRXA4BgPfFACs=
+golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

+ 106 - 0
image/img.go

@@ -0,0 +1,106 @@
+package image
+
+import (
+	"fmt"
+	"image"
+	"image/gif"
+	"image/jpeg"
+	"image/png"
+	"log"
+	"os"
+	"strings"
+)
+
+// @title   loadImage
+// @description    读取图片内容
+// @param
+// @return
+func loadImage(imgPath string) (image.Image, error) {
+	var img image.Image
+	var err error
+
+	f, err := os.Open(imgPath)
+	if err != nil {
+		log.Printf("os.Open error : %v\n", err)
+		return nil, err
+	}
+	defer f.Close()
+
+	if strings.HasSuffix(imgPath, "jpg") || strings.HasSuffix(imgPath, "jpeg") {
+		img, err = jpeg.Decode(f)
+	} else if strings.HasSuffix(imgPath, "png") {
+		img, err = png.Decode(f)
+	} else if strings.HasSuffix(imgPath, "gif") {
+		img, err = gif.Decode(f)
+	} else {
+		return nil, fmt.Errorf("当前图片格式不支持")
+	}
+
+	return img, err
+}
+
+// @title    WriteImage
+// @description    image数据写入图片文件
+// @param
+// @return
+func WriteImage(imgPath string, imgData *image.RGBA) error {
+
+	//打开新新图片文件
+	f, err := os.Create(imgPath)
+	if err != nil {
+		log.Printf("%v, Create error : %v\n", imgPath, err)
+		return err
+	}
+	defer f.Close()
+
+	//写入图片文件
+	png.Encode(f, imgData)
+	return err
+
+}
+
+// @title TrimImg
+// @description 裁剪图片,去除四周空白部分
+// @param
+// @return
+func TrimImg(imgPath, subImgPath string) error {
+	img, err := loadImage(imgPath)
+	if err != nil {
+		log.Printf("loadImage  error : %v\n", err)
+		return err
+	}
+
+	bounds := img.Bounds()
+	width, height := bounds.Max.X, bounds.Max.Y
+	fmt.Printf("width,height=============>%v,%v\n", width, height)
+	var x, y int
+	var wx, wy int //切割点的x和y
+	//遍历宽
+	for x = 0; x < width; x++ {
+		//遍历高
+		for y = 0; y < height; y++ {
+			r, g, b, _ := img.At(x, y).RGBA()
+			//fmt.Printf("r,g,b========>%v,%v,%v\n", int(r/257), int(g/257), int(b/257))
+			//判断当前坐标是否大于切割点坐标 , todo 为啥要除以257呢?
+			if int(r/257) < 255 && int(g/257) < 255 && int(b/257) < 255 && x >= wx && y >= wy {
+				wx = x
+				wy = y
+			}
+		}
+	}
+	fmt.Printf("wx,wy===========>%v,%v\n", wx, wy)
+	rgbImg := img.(*image.RGBA)
+
+	subImg := rgbImg.SubImage(image.Rect(0, 0, wx, wy)).(*image.RGBA)
+
+	WriteImage(subImgPath, subImg)
+	//fmt.Printf("%v %v %v\n", r, g, b)
+	//
+	//hex:=fmt.Sprintf("%s%s%s",
+	//	t2x(int64(r)),
+	//	t2x(int64(g)),
+	//	t2x(int64(b)),
+	//	)
+	//println(hex)
+	return nil
+}

+ 28 - 0
main.go

@@ -0,0 +1,28 @@
+package main
+
+func init() {
+	//devConf := &xnet.DevConf{
+	//	AppKey:             "ding8svryas6vm6ze5xh",
+	//	AppSecret:          "YF77J3t-bORqtZxNH7-RY8Q3znrgpsZDpTiXDqLj-Ns8XRIDZLn6fIwpukOQ1P1o",
+	//	GetTokenUrl:        "https://oapi.dingtalk.com/gettoken",
+	//	GetDeptUserListUrl: "https://oapi.dingtalk.com/topapi/user/listsimple",
+	//	Params:             map[string]string{},
+	//	Headers:            map[string]string{},
+	//	Cookie:             []*http.Cookie{},
+	//	DeptId:             554750022,
+	//}
+	//if err := devConf.GetToken(); err != nil {
+	//	log.Fatalln(err)
+	//}
+	//
+	//println(devConf.Token)
+	//
+	//deptUserList, _ := devConf.GetDeptUserList()
+	//fmt.Printf("%v\n", deptUserList)
+	//workData,_:=core.GetWorkData(30)
+	//fmt.Printf("%v\n", workData)
+}
+
+func main() {
+
+}

+ 226 - 0
office/excel.go

@@ -0,0 +1,226 @@
+package office
+
+import (
+	"fmt"
+	"log"
+
+	"github.com/go-ole/go-ole"
+	"github.com/go-ole/go-ole/oleutil"
+)
+
+// @title    ExcelSheetExportAsPdf
+// @description    把excel文件指定的sheet导出为pdf文件
+// @param xlsxPath string excel文件路径,pdfPath string pdf文件路径
+// @return
+func ExcelSheetExportAsPdf(xlsxPath, pdfPath string, sheetIndex int) error {
+
+	//CoInitialize是Windows提供的API函数,用来告诉 Windows以单线程的方式创建com对象。
+	//应用程序调用com库函数(除CoGetMalloc和内存分配函数)之前必须初始化com库。
+	ole.CoInitialize(0)
+	defer ole.CoUninitialize()
+
+	//创建com对象
+	iunk, err := oleutil.CreateObject("Excel.Application")
+	if err != nil {
+		log.Printf("error creating Wps object: %s", err)
+		return fmt.Errorf("error creating Wps object: %s", err)
+
+	}
+	defer iunk.Release()
+
+	// 获取能够遍历的对象
+	excel := iunk.MustQueryInterface(ole.IID_IDispatch)
+	defer excel.Release()
+
+	//获取工作簿对象
+	workbooks := oleutil.MustGetProperty(excel, "Workbooks").ToIDispatch()
+	defer workbooks.Release()
+
+	//打开指定工作簿
+	workbook := oleutil.MustCallMethod(workbooks, "Open", xlsxPath).ToIDispatch()
+	defer workbook.Release()
+
+	//获取指定sheet
+	worksheet := oleutil.MustGetProperty(workbook, "Worksheets", sheetIndex).ToIDispatch()
+	defer worksheet.Release()
+
+	//获取指定sheet的PageSetup
+	PageSetup, err := oleutil.GetProperty(worksheet, "PageSetup")
+	if err != nil {
+		log.Printf("Worksheets(1) get PageSetup error: %s", err)
+	} else {
+		////设置指定sheet的PageSetup.FitToPagesTall
+		_, err = oleutil.PutProperty(PageSetup.ToIDispatch(), "FitToPagesWide", 1)
+		if err != nil {
+			log.Printf("Worksheets(1).PageSetup put FitToPagesTall error: %s", err)
+		}
+		_, err = oleutil.PutProperty(PageSetup.ToIDispatch(), "LeftMargin", 0)
+		if err != nil {
+			log.Printf("Worksheets(1).PageSetup put LeftMargin error: %s", err)
+		}
+		_, err = oleutil.PutProperty(PageSetup.ToIDispatch(), "RightMargin", 0)
+		if err != nil {
+			log.Printf("Worksheets(1).PageSetup put RightMargin error: %s", err)
+		}
+		_, err = oleutil.PutProperty(PageSetup.ToIDispatch(), "TopMargin", 0)
+		if err != nil {
+			log.Printf("Worksheets(1).PageSetup put TopMargin error: %s", err)
+		}
+		_, err = oleutil.PutProperty(PageSetup.ToIDispatch(), "BottomMargin", 0)
+		if err != nil {
+			log.Printf("Worksheets(1).PageSetup put BottomMargin error: %s", err)
+		}
+	}
+
+	// 导出为指定格式
+	oleutil.MustCallMethod(worksheet, "ExportAsFixedFormat", 0, pdfPath)
+	//todo 如果不保存wps会弹窗,必须先保存.暂时未找到解决方案
+	oleutil.PutProperty(workbook, "Saved", true)
+	oleutil.MustCallMethod(workbook, "Close")
+	oleutil.MustCallMethod(excel, "Quit")
+	return nil
+}
+
+// @title    ExcelExportAsPdf
+// @description    excel文件导出为pdf文件
+// @param xlsxPath string excel文件路径,pdfPath string pdf文件路径
+// @return
+func ExcelExportAsPdf(xlsxPath, pdfPath string) error {
+
+	//CoInitialize是Windows提供的API函数,用来告诉 Windows以单线程的方式创建com对象。
+	//应用程序调用com库函数(除CoGetMalloc和内存分配函数)之前必须初始化com库。
+	ole.CoInitialize(0)
+	defer ole.CoUninitialize()
+
+	//创建com对象
+	iunk, err := oleutil.CreateObject("Excel.Application")
+	if err != nil {
+		log.Printf("error creating Wps object: %s", err)
+		return fmt.Errorf("error creating Wps object: %s", err)
+
+	}
+	defer iunk.Release()
+
+	// 获取能够遍历的对象
+	excel := iunk.MustQueryInterface(ole.IID_IDispatch)
+	defer excel.Release()
+
+	//获取工作簿对象
+	workbooks := oleutil.MustGetProperty(excel, "Workbooks").ToIDispatch()
+	defer workbooks.Release()
+
+	//打开指定工作簿
+	workbook := oleutil.MustCallMethod(workbooks, "Open", xlsxPath).ToIDispatch()
+	defer workbook.Release()
+
+	//获取指定sheet
+	worksheet := oleutil.MustGetProperty(workbook, "Worksheets", 1).ToIDispatch()
+	defer worksheet.Release()
+
+	//获取指定sheet的PageSetup
+	PageSetup, err := oleutil.GetProperty(worksheet, "PageSetup")
+	if err != nil {
+		log.Printf("Worksheets(1) get PageSetup error: %s", err)
+	} else {
+		////设置指定sheet的PageSetup.FitToPagesTall
+		_, err = oleutil.PutProperty(PageSetup.ToIDispatch(), "FitToPagesWide", 1)
+		if err != nil {
+			log.Printf("Worksheets(1).PageSetup put FitToPagesTall error: %s", err)
+		}
+		_, err = oleutil.PutProperty(PageSetup.ToIDispatch(), "LeftMargin", 0)
+		if err != nil {
+			log.Printf("Worksheets(1).PageSetup put LeftMargin error: %s", err)
+		}
+		_, err = oleutil.PutProperty(PageSetup.ToIDispatch(), "RightMargin", 0)
+		if err != nil {
+			log.Printf("Worksheets(1).PageSetup put RightMargin error: %s", err)
+		}
+		_, err = oleutil.PutProperty(PageSetup.ToIDispatch(), "TopMargin", 0)
+		if err != nil {
+			log.Printf("Worksheets(1).PageSetup put TopMargin error: %s", err)
+		}
+		_, err = oleutil.PutProperty(PageSetup.ToIDispatch(), "BottomMargin", 0)
+		if err != nil {
+			log.Printf("Worksheets(1).PageSetup put BottomMargin error: %s", err)
+		}
+	}
+
+	// 导出为指定格式
+	oleutil.MustCallMethod(worksheet, "ExportAsFixedFormat", 0, pdfPath)
+	//todo 如果不保存wps会弹窗,必须先保存.暂时未找到解决方案
+	oleutil.PutProperty(workbook, "Saved", true)
+	oleutil.MustCallMethod(workbook, "Close")
+	oleutil.MustCallMethod(excel, "Quit")
+	return nil
+}
+
+// @title    ExcelPrintOut
+// @description    调用系统的pdf虚拟打印机生成pdf文件.
+// @param xlsxPath string excel文档路径,pdfPath string 生成的pdf文档路径
+// @return
+//todo 由于打印完需要手动点击确认保存,未找到解决方法
+func ExcelPrintOut(xlsxPath, pdfPath string) error {
+	defer func() {
+		if err := recover(); err != nil {
+			log.Printf("ExcelPrintOut error: %v\n", err)
+		}
+	}()
+
+	ole.CoInitialize(0)
+	defer ole.CoUninitialize()
+
+	unknown, err := oleutil.CreateObject("KET.Application")
+	if err != nil {
+		log.Printf("creating Excel object error: %s", err)
+		return fmt.Errorf("please make sure wps installed.\ncreating Excel object error: %s\n", err)
+	}
+	defer unknown.Release()
+
+	excel := unknown.MustQueryInterface(ole.IID_IDispatch)
+	defer excel.Release()
+
+	oleutil.PutProperty(excel, "DisplayAlerts", false)
+	oleutil.PutProperty(excel, "Visible", false)
+
+	// opening then saving works due to the call to doc.Settings.SetUpdateFieldsOnOpen(true) above
+	workbooks := oleutil.MustGetProperty(excel, "Workbooks").ToIDispatch()
+	defer workbooks.Release()
+
+	workbook := oleutil.MustCallMethod(workbooks, "Open", xlsxPath).ToIDispatch()
+	defer workbook.Release()
+
+	//oleutil.MustPutProperty(books, "ActiveWorkBook",1)
+	// println("==============1")
+
+	worksheet := oleutil.MustGetProperty(workbook, "Worksheets", 1).ToIDispatch()
+	defer worksheet.Release()
+
+	// println("==============2")
+
+	// 如果 FitToPagesTall 此屬性為 False,Microsoft Excel 會根據 FitToPagesWide 屬性調整工作表。
+	ret, err := oleutil.GetProperty(worksheet, "", "PageSetup", "FitToPagesTall", 1)
+	fmt.Printf("FitToPagesTall %v,err==============>%v\n", ret, err)
+	_, err = oleutil.PutProperty(worksheet, "", "PageSetup", ".FitToPagesWide", 1)
+	fmt.Printf("FitToPagesWide err==============>%v\n", err)
+
+	//
+	// https://docs.microsoft.com/zh-cn/office/vba/api/excel.sheets.printout
+	/*
+		PrintOut(From, To, Copies, Preview, ActivePrinter, PrintToFile, Collate, PrToFileName)
+		From	打印的開始頁號。如果省略此參數,則從起始位置開始打印。
+		To	    打印的終止頁號。如果省略此參數,則打印至最後一頁。
+		Copies	打印份數。如果省略此參數,則只打印一份。
+		Preview	如果爲 True,Microsoft Excel 將在打印對象之前調用打印預覽。如果爲 False(或省略該參數),則立即打印對象。
+		ActivePrinter	設置活動打印機的名稱。
+		PrintToFile	如果爲 True,則打印到文件。如果沒有指定 PrToFileName,Microsoft Excel 將提示用戶輸入要使用的輸出文件的文件名。
+		Collate	如果爲 True,則逐份打印多個副本。
+		PrToFileName	如果 PrintToFile 設爲 True,則該參數指定要打印到的文件名。
+		注:From 和 To 所描述的“頁”指的是要打印的頁,並非指定工作表或工作簿中的全部頁。
+	*/
+	oleutil.MustCallMethod(worksheet, "PrintOut", 1, 1, 1, false, "导出为WPS PDF", true, pdfPath, true)
+
+	oleutil.MustCallMethod(workbook, "Close")
+	oleutil.MustCallMethod(excel, "Quit")
+	return nil
+
+}

+ 62 - 0
office/pdf.go

@@ -0,0 +1,62 @@
+package office
+
+import (
+	"bytes"
+	"fmt"
+	"gcore/core"
+	"github.com/axgle/mahonia" //编码转换
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+// @title XPdfToPng
+// @description 把整个pdf渲染成png
+// @param
+// @return
+func XPdfToPng(pdfPath string, subPath, fileName string) error {
+	currentDir, _ := os.Getwd()
+	xpdfPath := filepath.Join(currentDir, "tool", "pdftopng.exe")
+	//println(xpdfPath)
+	_, err := os.Stat(xpdfPath)
+	if os.IsNotExist(err) {
+		fmt.Println("file no exist.")
+		return fmt.Errorf("pdftopng.exe not found in ./tools")
+	}
+
+	//cmdLine := fmt.Sprintf("cd %s && %s  %s %s", subPath,xpdfPath,pdfPath, fileName)
+	//println(cmdLine)
+	cmdArgs := []string{
+		"/C",
+		"cd",
+		subPath,
+		"&&",
+		xpdfPath,
+		"-f",
+		"1",
+		"-l",
+		"1",
+		"-r",
+		"250",
+		pdfPath,
+		fileName,
+	}
+	println(strings.Join(cmdArgs, " "))
+	c := exec.Command("cmd.exe", cmdArgs...)
+	var stdout bytes.Buffer
+	var stderr bytes.Buffer
+	var enc mahonia.Decoder
+	enc = mahonia.NewDecoder("gbk")
+	c.Stdout = &stdout
+	c.Stderr = &stderr
+	err = c.Run()
+	if err != nil {
+		fmt.Printf("XPdfToPng Error: %s %s\n", err, enc.ConvertString(stderr.String()))
+		//go Xlog(logPath, strings.Join(cmdArgs, " "))
+		go core.LogWrite(fmt.Sprintf("%s,XPdfToPng Error: %s %s\n", pdfPath, err, enc.ConvertString(stderr.String())))
+		return fmt.Errorf("%s,XPdfToPng Error: %s %s\n", pdfPath, err, enc.ConvertString(stderr.String()))
+	}
+	//fmt.Printf("XPdfToPng Result: %s\n", stdout.String())
+	return nil
+}

+ 107 - 0
office/sharepoint.go

@@ -0,0 +1,107 @@
+package office
+
+import (
+	"github.com/koltyakov/gosip"
+	"github.com/koltyakov/gosip/api"
+	strategy "github.com/koltyakov/gosip/auth/saml"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+)
+
+type SPConf struct {
+	//ConfFilePath string          `json:"conf_file_path"` //sharepoint登录config文件路径
+	SiteURL  string          `json:"site_url"`
+	Username string          `json:"username"`
+	Password string          `json:"password"`
+	SPC      *gosip.SPClient `json:"spc"` //客户端认证连接对象
+}
+
+func (m *SPConf) GetAuthClient() error {
+	// configPath := "private.spo-user.json"
+	//为了安全,把siteurl等通过结构体传入参数
+	auth := &strategy.AuthCnfg{
+		SiteURL:  m.SiteURL,
+		Username: m.Username,
+		Password: m.Password,
+	}
+	//if err := auth.ReadConfig(m.ConfFilePath); err != nil {
+	//	return fmt.Errorf("unable to get config: %v", err)
+	//}
+	m.SPC = &gosip.SPClient{AuthCnfg: auth}
+	return nil
+}
+
+// @title    DownloadFileByRelativeURL
+// @description    根据相对url下载
+// @auth      ila 2021-11-10
+// @param     relativeURL  sharepoint相对URL,targetPath 本地路径
+// @return    error        是否成功
+//
+func (m *SPConf) DownloadFileByRelativeURL(relativeURL, targetPath string) error {
+
+	// Getting auth params and client
+	err := m.GetAuthClient()
+	if err != nil {
+		log.Fatalln(err)
+	}
+
+	// Binding SharePoint API
+	sp := api.NewSP(m.SPC)
+
+	data, err := sp.Web().GetFile(relativeURL).Download()
+	if err != nil {
+		log.Printf("Download file error : %v\n", err)
+		return err
+	}
+
+	file, err := os.Create(filepath.Join(targetPath, filepath.Base(relativeURL)))
+	if err != nil {
+		log.Printf("unable to create a file: %v\n", err)
+		return err
+	}
+	defer file.Close()
+	_, err = file.Write(data)
+	if err != nil {
+		log.Printf("unable to write to file: %v\n", err)
+		return err
+	}
+	file.Sync()
+
+	return nil
+}
+
+// @title    UploadFile
+// @description    上传本地文件到sharepoint服务器
+// @auth      ila 2021-11-10
+// @param     folder  sharepoint相对路径,filePath 本地文件完整路径,targetName 线上文件名
+// @return    error        是否成功
+func (m *SPConf) UploadFile(folder, filePath, targetName string) error {
+	// Getting auth params and client
+	err := m.GetAuthClient()
+	if err != nil {
+		log.Fatalln(err)
+	}
+
+	// Binding SharePoint API
+	sp := api.NewSP(m.SPC)
+
+	fdr := sp.Web().GetFolder(folder)
+
+	content, err := ioutil.ReadFile(filePath)
+	if err != nil {
+		log.Printf("ReadFile error: %v\n", err)
+		return err
+	}
+
+	fileAddResp, err := fdr.Files().Add(targetName, content, true) //是否覆盖
+	if err != nil {
+		log.Printf("NewFile upload error: %v\n", err)
+		return err
+	}
+
+	log.Printf("New file URL : %s\n", fileAddResp.Data().ServerRelativeURL)
+
+	return nil
+}

+ 115 - 0
xnet/chrome_driver.go

@@ -0,0 +1,115 @@
+package xnet
+
+import (
+	"fmt"
+	"gcore/core"
+	"github.com/tebeka/selenium"
+	"github.com/tebeka/selenium/chrome"
+	"log"
+)
+
+type ChromeDriverConf struct {
+	ChromeDriverPath    string             `ini:"chrome_driver_path"` //chromedriver.exe路径
+	ChromeDriverPort    int                `ini:"chrome_driver_port"` //chromedriver服务端口
+	ChromeDriverService *selenium.Service  `json:"chrome_driver_service"`
+	WebDriver           selenium.WebDriver `json:"web_driver"`
+}
+
+// @title ChromeDriverServiceStart
+// @description 开启服务
+// @param
+// @return
+func (m *ChromeDriverConf) ChromeDriverServiceStart() (err error) {
+	opts := []selenium.ServiceOption{}
+
+	//selenium.SetDebug(true)
+
+	m.ChromeDriverService, err = selenium.NewChromeDriverService(m.ChromeDriverPath, m.ChromeDriverPort, opts...)
+	if nil != err {
+		log.Printf("ChromeDriverServiceStart error:%v\n", err)
+		core.LogWrite(fmt.Sprintf("ChromeDriverServiceStart error:%v\n", err))
+		return
+	} else {
+		log.Println("ChromeDriverServiceStart success.")
+	}
+
+	return
+}
+
+// @title ChromeDriverServiceStop
+// @description 关闭服务
+// @param
+// @return
+func (m *ChromeDriverConf) ChromeDriverServiceStop() error {
+	//注意这里,server关闭之后,chrome窗口也会关闭
+	err := m.ChromeDriverService.Stop()
+	if err != nil {
+		log.Printf("ChromeDriverServiceStop error:%v\n", err)
+		core.LogWrite(fmt.Sprintf("ChromeDriverServiceStop error:%v\n", err))
+		return err
+	}
+
+	return nil
+
+}
+
+// @title WebDriverStart
+// @description 开启webdriver,和新增tab
+// @param
+// @return
+func (m *ChromeDriverConf) WebDriverStart() (WebDriver selenium.WebDriver, err error) {
+	//链接本地的浏览器 chrome
+	caps := selenium.Capabilities{
+		"browserName": "chrome",
+		//https://github.com/tebeka/selenium/issues/233
+		"chromeOptions": map[string]interface{}{
+			"excludeSwitches": [1]string{"enable-automation"},
+		},
+	}
+	prefCaps := map[string]interface{}{}
+	chromeCaps := chrome.Capabilities{
+		Prefs: prefCaps,
+		Path:  "",
+		Args: []string{
+			"--headless",        // 设置Chrome无头模式,在linux下运行,需要设置这个参数,否则会报错
+			"--kiosk",           // 加载启动项页面全屏效果,相当于F11
+			"--start-maximized", //  最大化运行(全屏窗口),不设置,取元素会报错
+			"--window-size=1920x1080",
+			"--disable-infobars", //  关闭左上方Chrome 正受到自动测试软件的控制的提示
+			//"--no-sandbox",// 沙盒,linux下要关闭沙盒模式.
+			"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36", // 模拟user-agent,防反爬
+		},
+	}
+	//以上是设置浏览器参数
+	caps.AddChrome(chromeCaps)
+	// 调起chrome浏览器
+	WebDriver, err = selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", m.ChromeDriverPort))
+	if err != nil {
+		//启用--user-data-dir参数后,同时只能打开一个chrome程序.
+		//如果已打开chrome程序,必须先关闭已打开的chrome,再运行程序
+		fmt.Printf("WebDriverStart connect to the webDriver error : %v\n", err)
+		core.LogWrite(fmt.Sprintf("WebDriverStart connect to the webDriver  error : %v", err))
+		return
+	} else {
+		log.Println("connect to the webDriver success.")
+	}
+
+	return
+}
+
+// @title ChromeDriverServiceStop
+// @description 关闭webdriver和打开的窗口
+// @param
+// @return
+func (m *ChromeDriverConf) WebDriverStop() error {
+	//关闭一个webDriver会对应关闭一个chrome窗口
+	//但是不会导致seleniumServer关闭
+	err := m.WebDriver.Quit()
+	if err != nil {
+		log.Printf("WebDriverStop error:%v\n", err)
+		core.LogWrite(fmt.Sprintf("WebDriverStop error:%v\n", err))
+		return err
+	}
+
+	return nil
+}

+ 70 - 0
xnet/cookie.go

@@ -0,0 +1,70 @@
+package xnet
+
+import (
+	"github.com/zellyn/kooky/chrome"
+	"log"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// @title    LoadChromeCookies
+// @description    从本地chrome路径加载指定domain的cookies
+// @param    domain string 需要提起cookie的domain
+// @return
+func LoadChromeCookies(domain string) ([]*http.Cookie, error) {
+
+	var cookies []*http.Cookie
+
+	dir, _ := os.UserHomeDir()
+
+	cookiesFile := filepath.Join(dir, "/AppData/Local/Google/Chrome/User Data/Default/Cookies")
+
+	chromeCookies, err := chrome.ReadCookies(cookiesFile)
+	if err != nil {
+		log.Printf("ReadCookies error : %v\n", err)
+		return cookies, err
+	}
+	for _, cookie := range chromeCookies {
+		if strings.Contains(cookie.Domain, domain) == true {
+			//fmt.Println(cookie)
+			cookies = append(cookies, &http.Cookie{
+				Name:    cookie.Name,
+				Value:   cookie.Value,
+				Path:    cookie.Path,
+				Domain:  cookie.Domain,
+				Expires: cookie.Expires,
+			})
+		}
+	}
+	return cookies, nil
+}
+
+// @title    LoadChromeCookiesVer1
+// @description    从本地chrome路径加载指定domain的cookies
+// @param    domain string 需要提起cookie的domain
+// @return
+func LoadChromeCookiesVer1(domain, cookiesPath string) ([]*http.Cookie, error) {
+
+	var cookies []*http.Cookie
+
+	chromeCookies, err := chrome.ReadCookies(cookiesPath)
+	if err != nil {
+		log.Printf("ReadCookies error : %v\n", err)
+		return cookies, err
+	}
+	for _, cookie := range chromeCookies {
+		if strings.Contains(cookie.Domain, domain) == true {
+			//fmt.Println(cookie)
+			cookies = append(cookies, &http.Cookie{
+				Name:    cookie.Name,
+				Value:   cookie.Value,
+				Path:    cookie.Path,
+				Domain:  cookie.Domain,
+				Expires: cookie.Expires,
+			})
+		}
+	}
+	return cookies, nil
+}

+ 95 - 0
xnet/dingtalk_oa.go

@@ -0,0 +1,95 @@
+package xnet
+
+import (
+	"encoding/json"
+	"gcore/core"
+	"log"
+	"net/http"
+)
+
+type DevConf struct {
+	AppKey             string
+	AppSecret          string
+	GetTokenUrl        string
+	GetDeptUserListUrl string
+	Token              string
+	Params             map[string]string
+	Headers            map[string]string
+	Cookie             []*http.Cookie
+	DeptId             int
+}
+
+// @title GetToken
+// @description 获取access token
+// @param
+// @return
+func (m *DevConf) GetToken() error {
+	params := map[string]string{"appkey": m.AppKey, "appsecret": m.AppSecret}
+	content, err := GetX(m.GetTokenUrl, params, m.Headers, m.Cookie)
+	if err != nil {
+		log.Printf("GetToken error : %v", err)
+	}
+	//fmt.Printf("%v\n", content)
+	ret := map[string]interface{}{}
+	err = json.Unmarshal([]byte(content), &ret)
+	if err != nil {
+		log.Printf("Unmarshal error : %v", err)
+		return err
+	}
+
+	m.Token = ret["access_token"].(string)
+	m.Params["access_token"] = ret["access_token"].(string)
+	m.Headers["Content-Type"] = "application/json"
+	return nil
+
+}
+
+// @title GetDeptUserList
+// @description 获取部门成员的姓名和userid
+// @param
+// @return
+func (m *DevConf) GetDeptUserList() (deptUserList []map[string]string, err error) {
+	cursor := 0
+	size := 100
+
+	for {
+		data := map[string]interface{}{
+			"dept_id": m.DeptId,
+			"cursor":  cursor,
+			"size":    size,
+		}
+		content, err := PostJson(m.GetDeptUserListUrl, m.Params, m.Headers, data, m.Cookie)
+		if err != nil {
+			log.Printf("GetToken error : %v", err)
+			continue
+		}
+
+		ret := map[string]interface{}{}
+		err = json.Unmarshal([]byte(content), &ret)
+		if err != nil {
+			log.Printf("Unmarshal error : %v", err)
+			continue
+		}
+		result := ret["result"]
+		for _, i := range result.(map[string]interface{})["list"].([]interface{}) {
+			tmpData := core.StringInterfaceToStringString(i.(map[string]interface{}))
+			deptUserList = append(deptUserList, tmpData)
+		}
+		//当前请求返回数据量小于size,跳出循环
+		if len(result.(map[string]interface{})["list"].([]interface{})) < size {
+			break
+		}
+
+		data["curosr"] = result.(map[string]interface{})["next_cursor"].(int64)
+	}
+
+	return
+}
+
+// @title GetDeptAttendanceData
+// @description 30天内部门考勤数据
+// @param
+// @return
+func GetDeptAttendanceData() {
+
+}

+ 79 - 0
xnet/dingtalk_robot.go

@@ -0,0 +1,79 @@
+package xnet
+
+import (
+	"fmt"
+	"github.com/blinkbean/dingtalk"
+	"log"
+	"strings"
+	"time"
+)
+
+// @title    SendMarkDownMsg
+// @description     推送包含图片链接的makrdown信息到钉钉群
+// @param
+// @return
+func SendMarkDownMsg(imgUrl, token, secret string) error {
+	cli := dingtalk.InitDingTalkWithSecret(token, secret)
+	title := fmt.Sprintf("看板%s", time.Now().Format("01/02 15:04"))
+	err := cli.SendMarkDownMessage(title, fmt.Sprintf("![screenshot](%s)", imgUrl))
+	if err != nil {
+		log.Printf("SendLinkMessage error : %v\n", err)
+		return err
+	}
+	return nil
+}
+
+// @title    Sendimgs
+// @description     推送包含多张图片链接的makrdown信息到钉钉群
+// @param imgUrls []string 多张图片url的slice
+// @return
+func Sendimgs(imgUrls []string, token, secret string) error {
+	cli := dingtalk.InitDingTalkWithSecret(token, secret)
+	title := fmt.Sprintf("看板%s", time.Now().Format("01/02 15:04"))
+	content := []string{}
+	for idx, imgUrl := range imgUrls {
+		content = append(content, fmt.Sprintf("![screenshot%v](%s)\n", idx, imgUrl))
+	}
+
+	err := cli.SendMarkDownMessage(title, strings.Join(content, ""))
+	if err != nil {
+		log.Printf("SendLinkMessage error : %v\n", err)
+		return err
+	}
+
+	return nil
+}
+
+// @title    Sendimgs,增加了title参数
+// @description     推送包含多张图片链接的makrdown信息到钉钉群
+// @param imgUrls []string 多张图片url的slice
+// @return
+func SendImgs(imgUrls []string, title, token, secret string) error {
+	cli := dingtalk.InitDingTalkWithSecret(token, secret)
+	content := []string{}
+	for idx, imgUrl := range imgUrls {
+		content = append(content, fmt.Sprintf("![screenshot%v](%s)\n", idx, imgUrl))
+	}
+
+	err := cli.SendMarkDownMessage(title, strings.Join(content, ""))
+	if err != nil {
+		log.Printf("SendLinkMessage error : %v\n", err)
+		return err
+	}
+
+	return nil
+}
+
+// @title    SendTextMsg
+// @description    推送文本信息到钉钉群
+// @param
+// @return
+func SendTextMsg(content, token, secret string) error {
+	cli := dingtalk.InitDingTalkWithSecret(token, secret)
+	err := cli.SendTextMessage(content)
+	if err != nil {
+		log.Printf("SendTextMsg error : %v\n", err)
+		return err
+	}
+	return nil
+}

+ 80 - 0
xnet/netflix.go

@@ -0,0 +1,80 @@
+package xnet
+
+import (
+	"fmt"
+	"gcore/core"
+	"log"
+	"net/http"
+	"net/http/cookiejar"
+	"net/url"
+	"strings"
+	"time"
+)
+
+// CheckAvailableCountry 判断当前区域能否播放指定的netflix url
+func CheckAvailableCountry(cookies []*http.Cookie, netflixUrl, ProxyOnOff, proxyUrl string) (bool, error) {
+	var err error
+	transport := &http.Transport{}
+
+	//是否使用代码的开关
+	if ProxyOnOff == "on" {
+		//根据参数判断是否需要设置proxy
+		proxy, err := url.Parse(proxyUrl)
+		if err != nil {
+			log.Printf("proxy url parse error : %v \n", err)
+			return false, err
+		}
+		transport.Proxy = http.ProxyURL(proxy)
+	}
+
+	//new个cookiejar罐子
+	jar, err := cookiejar.New(nil)
+
+	if err != nil {
+		log.Printf("cookiejar New error : %v\n", err)
+		return false, err
+	}
+
+	//原生的url,orz
+	parseUrl, err := url.Parse(netflixUrl)
+	if err != nil {
+		log.Printf("netflixUrl Parse error : %v\n", err)
+	}
+	//fmt.Printf("parseUrl===>%v\n", parseUrl)
+	//把传入的cookies装入罐
+	jar.SetCookies(parseUrl, cookies)
+
+	//初始化请求
+	client := &http.Client{Jar: jar, Transport: transport, Timeout: 15 * time.Second}
+	req, _ := http.NewRequest("GET", netflixUrl, nil)
+
+	//请求加入头部
+	req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36")
+
+	// todo 检测跳转
+	//client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
+	//	println(len(via))
+	//	return http.ErrUseLastResponse
+	//}
+
+	//发出请求
+	resp, err := client.Do(req)
+	defer func() {
+		if err1 := recover(); err1 != nil {
+			core.XWarning(fmt.Sprintf("connect to the netflix faild %v\n", err1))
+			err = fmt.Errorf("connect to the netflix faild %v\n", err1)
+		}
+	}()
+	if err != nil {
+		core.XWarning(fmt.Sprintf("client.Do error : %v\n", err))
+		return false, err
+	}
+
+	//根据response的url是否包含某些字符串,判断是否跳转
+	if strings.Contains(resp.Request.URL.RequestURI(), "/watch/0") == true {
+		return false, nil
+	}
+	defer resp.Body.Close()
+	return true, nil
+
+}

+ 150 - 0
xnet/powerbi_screenshot.go

@@ -0,0 +1,150 @@
+package xnet
+
+import (
+	"fmt"
+	"gcore/core"
+	"github.com/tebeka/selenium"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+// @title GetPBIScreenshots
+// @description 遍历获取PBI页面,并截图
+// @param PBIPageURL,PBI的首页
+// @return
+func (m *ChromeDriverConf) GetPBIScreenshots(PBIPageURL, screenshotPath, fileNameNotExt string) (screenshotFileNames []string, err error) {
+
+	err = m.WebDriver.Get(PBIPageURL)
+	if err != nil {
+		fmt.Printf("Get url error : %v\n", err)
+		return
+	}
+	handle, _ := m.WebDriver.CurrentWindowHandle()
+	m.WebDriver.ResizeWindow(handle, 1920, 1080)
+	m.WebDriver.MaximizeWindow(handle)
+
+	err = m.WebDriver.WaitWithTimeout(ShareExists, 30*time.Second)
+	if err != nil {
+		log.Printf("GetPBIScreenshots ShareExists error:%v", err)
+		core.LogWrite(fmt.Sprintf("GetPBIScreenshots ShareExists error:%v", err))
+		return
+	}
+
+	// todo 判断加载完成,暂时sleep30秒
+	time.Sleep(30 * time.Second)
+	//首页
+	data, err := m.WebDriver.Screenshot()
+	if err != nil {
+		log.Printf(" %s,Screenshot  error : %v\n", PBIPageURL, err)
+		core.LogWrite(fmt.Sprintf(" %s,Screenshot  error : %v", PBIPageURL, err))
+		return
+	}
+
+	screenshotFileName := fmt.Sprintf("%v_0.png", fileNameNotExt)
+	screenshotFilePath := filepath.Join(screenshotPath, screenshotFileName)
+	ScreenshotSaveTo(data, screenshotFilePath)
+
+	screenshotFileNames = append(screenshotFileNames, screenshotFileName)
+
+	for i := 1; i < 999; i++ {
+		log.Printf("page %v start===>", i+1)
+		core.LogWrite(fmt.Sprintf("page %v start===>", i+1))
+		//判断是否有下一页,没有则跳出循环
+		err = m.WebDriver.WaitWithTimeout(NextPageExists, 30*time.Second)
+		if err != nil {
+			log.Printf("GetPBIScreenshots NextPageExistsV2 error : %v\n", err)
+			core.LogWrite(fmt.Sprintf("GetPBIScreenshots NextPageExistsV2 error : %v", err))
+
+			break
+
+		}
+
+		//判断是否刷新完
+		//err = m.WebDriver.WaitWithTimeout(ShareExists, 30*time.Second)
+		//if err != nil {
+		//	return
+		//}
+
+		// todo 判断加载完成,暂时sleep30秒
+		time.Sleep(30 * time.Second)
+
+		//刷新完成,开始截图
+		data, err = m.WebDriver.Screenshot()
+		if err != nil {
+			log.Printf(" page %v,Screenshot  error : %v\n", i, err)
+			core.LogWrite(fmt.Sprintf(" page %v,Screenshot  error : %v\n", i, err))
+			break
+		}
+
+		screenshotFileName = fmt.Sprintf("%v_%v.png", fileNameNotExt, i)
+		ScreenshotSaveTo(data, filepath.Join(screenshotPath, screenshotFileName))
+		screenshotFileNames = append(screenshotFileNames, screenshotFileName)
+	}
+
+	return
+}
+
+// @title ScreenshotSaveTo
+// @description  PBI页面截图另存为图片文件
+// @param
+// @return
+func ScreenshotSaveTo(data []byte, screenshotFilePath string) error {
+
+	//currentDir, _ := os.Getwd()
+	//imgPath := filepath.Join(currentDir, cfg.DownloadFolderName, "pbi.png")
+	f, err := os.Create(screenshotFilePath)
+	if err != nil {
+		log.Printf("%v, Create error : %v\n", screenshotFilePath, err)
+		return err
+	}
+	f.Write(data)
+	defer f.Close()
+
+	return nil
+}
+
+// @title ShareExists
+// @description 从分享按钮,判断页面是否完成刷新,2021-12-07更新了cssselector的元素
+// @param
+// @return
+func ShareExists(driver selenium.WebDriver) (bool, error) {
+	_, err := driver.FindElement(selenium.ByCSSSelector, "button[aria-label='共享']")
+	if err != nil {
+		return false, err
+	}
+
+	return true, nil
+
+}
+
+// @title NextPageExists
+// @description 判断页面是否有下一页的按钮,并点击,2021-12-07更新了cssselector的元素
+// @param
+// @return
+func NextPageExists(driver selenium.WebDriver) (bool, error) {
+	elem, err := driver.FindElement(selenium.ByCSSSelector, "button[aria-label='下一页']")
+	if err != nil {
+		return false, err
+	}
+	elemi, err := elem.FindElement(selenium.ByCSSSelector, "i")
+	if err != nil {
+		return false, fmt.Errorf("找不到下一页button的标签i")
+	}
+	elemClass, err := elemi.GetAttribute("class")
+	if err != nil {
+		return false, fmt.Errorf("找不到下一页的class")
+	}
+	if strings.Contains(elemClass, " inactive") == true {
+		return false, fmt.Errorf("下一页禁止点击")
+	}
+	//排除了其它情况,尝试点击下一页
+	err = elem.Click()
+	if err != nil {
+		return false, err
+	}
+	return true, nil
+
+}

+ 149 - 0
xnet/powerbi_url.go

@@ -0,0 +1,149 @@
+package xnet
+
+import (
+	"fmt"
+	"github.com/tebeka/selenium"
+	"github.com/tebeka/selenium/chrome"
+	"log"
+	"net/url"
+	"regexp"
+	"strings"
+	"time"
+)
+
+// @title GetPBIPageURL
+// @description 获取多页PBI的所有页面url
+// @param
+// @return
+func GetPBIPageURL(chromeDriver string, driverPort int, PBIURL string) ([]string, error) {
+	PBIUrls := []string{}
+
+	if strings.Contains(PBIURL, "powerbi.com") == false {
+		return PBIUrls, fmt.Errorf("请正确的PBIURL.")
+	}
+	if strings.Contains(PBIURL, "pageName") == true {
+		return PBIUrls, fmt.Errorf("请输入PBI的第一页URL.")
+	}
+	opts := []selenium.ServiceOption{}
+
+	//selenium.SetDebug(true)
+	service, err := selenium.NewChromeDriverService(chromeDriver, driverPort, opts...)
+	if nil != err {
+		fmt.Println("start a chromedriver service falid", err.Error())
+		return PBIUrls, err
+	}
+	//注意这里,server关闭之后,chrome窗口也会关闭
+	defer service.Stop()
+
+	//链接本地的浏览器 chrome
+	caps := selenium.Capabilities{
+		"browserName": "chrome",
+	}
+	prefCaps := map[string]interface{}{}
+	chromeCaps := chrome.Capabilities{
+		Prefs: prefCaps,
+		Path:  "",
+		Args: []string{
+			//"--headless",         // 设置Chrome无头模式,在linux下运行,需要设置这个参数,否则会报错
+			"--kiosk",            // 加载启动项页面全屏效果,相当于F11
+			"--start-maximized",  //  最大化运行(全屏窗口),不设置,取元素会报错
+			"--disable-infobars", //  关闭左上方Chrome 正受到自动测试软件的控制的提示
+			//"--no-sandbox",// 沙盒,linux下要关闭沙盒模式.
+			"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36", // 模拟user-agent,防反爬
+		},
+	}
+	//以上是设置浏览器参数
+	caps.AddChrome(chromeCaps)
+
+	// 调起chrome浏览器
+	wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", driverPort))
+	if err != nil {
+		//启用--user-data-dir参数后,同时只能打开一个chrome程序.
+		//如果已打开chrome程序,必须先关闭已打开的chrome,再运行程序
+		fmt.Println("connect to the webDriver faild", err.Error())
+		return PBIUrls, err
+	} else {
+		log.Println("connect to the webDriver success.")
+	}
+	//关闭一个webDriver会对应关闭一个chrome窗口
+	//但是不会导致seleniumServer关闭
+	defer wd.Quit()
+
+	err = wd.Get(PBIURL)
+	if err != nil {
+		fmt.Printf("Get url error : %v\n", err)
+		return PBIUrls, err
+	}
+	handle, _ := wd.CurrentWindowHandle()
+	wd.ResizeWindow(handle, 1920, 1080)
+	wd.MaximizeWindow(handle)
+	err = wd.WaitWithTimeout(ShareExists, 30*time.Second)
+	if err != nil {
+		return PBIUrls, err
+	}
+
+	pageNames := []string{}
+
+	//pageSource, _ := wd.PageSource()
+	//cp := regexp.MustCompile("pageName%3D(.*?)\"")
+	//rets := cp.FindAllString(pageSource, -1)
+	//if len(rets)>0{
+	//	unescape,_:=url.QueryUnescape(rets[0])
+	//	splits:=strings.Split(unescape,"&pageName=")
+	//	for _,i:=range splits{
+	//		if i[len(i)-1]== '"'{
+	//			pageNames=append(pageNames,i[:len(i)-1])
+	//			break
+	//		}
+	//	}
+	//}
+
+	//time.Sleep(3*time.Second)
+	for i := 0; i < 999; i++ {
+		err = wd.WaitWithTimeout(NextPageExists, 30*time.Second)
+		if err != nil {
+			log.Printf("NextPageExists WaitWithTimeout error : %v\n", err)
+			break
+		}
+		pageSource, _ := wd.PageSource()
+		cp := regexp.MustCompile("pageName%3D(.*?)\"")
+		rets := cp.FindAllString(pageSource, -1)
+		if len(rets) > 0 {
+			unescape, _ := url.QueryUnescape(rets[0])
+			splits := strings.Split(unescape, "&pageName=")
+			for _, i := range splits {
+				if i[len(i)-1] == '"' {
+					pageNames = append(pageNames, i[:len(i)-1])
+					break
+				}
+			}
+		}
+
+		nextPage, err := wd.FindElement(selenium.ByCSSSelector, "i[title='下一页']")
+		if err != nil {
+			log.Printf("FindElement 下一页 error : %v\n", err)
+			break
+		}
+		nextPageClass, _ := nextPage.GetAttribute("class")
+		if strings.Contains(nextPageClass, " inactive") == true {
+			log.Println("nextPageClass inactive.\n")
+			break
+		}
+	}
+
+	//data, err := wd.Screenshot()
+	//
+	//f, err := os.Create("pbi.png")
+	//f.Write(data)
+	//if err != nil {
+	//	log.Printf("pbi.png, Create error : %v\n", err)
+	//	return PBIUrls, err
+	//}
+	//
+	//defer f.Close()
+	for _, pageName := range pageNames {
+		PBIUrls = append(PBIUrls, fmt.Sprintf("%s&%s", PBIURL, pageName))
+	}
+	fmt.Printf("PBIUrls==========>%v\n", PBIUrls)
+	return PBIUrls, nil
+}

+ 1 - 0
xnet/selenium.go

@@ -0,0 +1 @@
+package xnet

+ 161 - 0
xnet/xhttp.go

@@ -0,0 +1,161 @@
+package xnet
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"net/http/cookiejar"
+	"net/url"
+	"time"
+)
+
+func PostJson(srcUrl string, params, headers map[string]string, data map[string]interface{}, cookies []*http.Cookie) (string, error) {
+	var content string
+
+	//解析原生的url
+	parseUrl, _ := url.Parse(srcUrl)
+
+	//new个cookiejar罐子
+	jar, err := cookiejar.New(nil)
+
+	if err != nil {
+		log.Printf("cookiejar New error : %v\n", err)
+		return content, err
+	}
+	//把传入的cookies装入罐
+	jar.SetCookies(parseUrl, cookies)
+	//处理jsondata
+	byteData, err := json.Marshal(data)
+	if err != nil {
+		log.Printf("Marshal data error : %v", err)
+		return content, err
+	}
+
+	//初始化请求,加入15秒timeout
+	client := &http.Client{Jar: jar, Timeout: 15 * time.Second}
+	req, _ := http.NewRequest("POST", srcUrl, bytes.NewReader(byteData))
+
+	//处理query参数
+	q := req.URL.Query()
+	for k, v := range params {
+		q.Add(k, v)
+	}
+	req.URL.RawQuery = q.Encode()
+
+	//请求加入头部
+	for k, v := range headers {
+		req.Header.Add(k, v)
+	}
+
+	//发出请求
+	resp, err := client.Do(req)
+
+	if err != nil {
+		log.Printf("client.Do error : %v\n", err)
+		return content, err
+	}
+
+	//读取返回页面数据
+	body, _ := ioutil.ReadAll(resp.Body)
+	defer resp.Body.Close()
+	content = string(body)
+
+	return content, nil
+
+}
+
+// @title GetX
+// @description 带参数,头部,cookie的get请求封装
+// @param
+// @return
+func GetX(srcUrl string, params, headers map[string]string, cookies []*http.Cookie) (string, error) {
+	var content string
+
+	//解析原生的url
+	parseUrl, _ := url.Parse(srcUrl)
+
+	//new个cookiejar罐子
+	jar, err := cookiejar.New(nil)
+
+	if err != nil {
+		log.Printf("cookiejar New error : %v\n", err)
+		return content, err
+	}
+	//把传入的cookies装入罐
+	jar.SetCookies(parseUrl, cookies)
+
+	//初始化请求,加入15秒timeout
+	client := &http.Client{Jar: jar, Timeout: 15 * time.Second}
+	req, _ := http.NewRequest("GET", srcUrl, nil)
+
+	//处理query参数
+	q := req.URL.Query()
+	for k, v := range params {
+		q.Add(k, v)
+	}
+	req.URL.RawQuery = q.Encode()
+
+	//请求加入头部
+	for k, v := range headers {
+		req.Header.Add(k, v)
+	}
+
+	//发出请求
+	resp, err := client.Do(req)
+
+	if err != nil {
+		log.Printf("client.Do error : %v\n", err)
+		return content, err
+	}
+
+	//读取返回页面数据
+	body, _ := ioutil.ReadAll(resp.Body)
+	defer resp.Body.Close()
+	content = string(body)
+
+	return content, nil
+}
+
+// GetContentWithCookies 带cookie获取源地址的内容
+func GetContentWithCookies(srcUrl string, cookies []*http.Cookie) (string, error) {
+
+	var content string
+
+	//new个cookiejar罐子
+	jar, err := cookiejar.New(nil)
+
+	if err != nil {
+		log.Printf("cookiejar New error : %v\n", err)
+		return content, err
+	}
+
+	//原生的url
+	parseUrl, _ := url.Parse(srcUrl)
+
+	//把传入的cookies装入罐
+	jar.SetCookies(parseUrl, cookies)
+
+	//初始化请求
+	client := &http.Client{Jar: jar, Timeout: 15 * time.Second}
+	req, _ := http.NewRequest("GET", srcUrl, nil)
+
+	//请求加入头部
+	req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36")
+
+	//发出请求
+	resp, err := client.Do(req)
+
+	if err != nil {
+		log.Printf("client.Do error : %v\n", err)
+		return content, err
+	}
+
+	//读取返回页面数据
+	body, _ := ioutil.ReadAll(resp.Body)
+	defer resp.Body.Close()
+	content = string(body)
+
+	return content, nil
+}