package utils
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/lianggx6/goutf16"
|
|
"io"
|
|
"math/rand"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
type HWND uintptr
|
|
|
|
const (
|
|
ERROR_FILE_NOT_FOUND = 2
|
|
ERROR_PATH_NOT_FOUND = 3
|
|
ERROR_BAD_FORMAT = 11
|
|
)
|
|
|
|
const (
|
|
SE_ERR_ACCESSDENIED = 5
|
|
SE_ERR_OOM = 8
|
|
SE_ERR_DLLNOTFOUND = 32
|
|
SE_ERR_SHARE = 26
|
|
SE_ERR_ASSOCINCOMPLETE = 27
|
|
SE_ERR_DDETIMEOUT = 28
|
|
SE_ERR_DDEFAIL = 29
|
|
SE_ERR_DDEBUSY = 30
|
|
SE_ERR_NOASSOC = 31
|
|
)
|
|
|
|
var (
|
|
modshell32 = syscall.NewLazyDLL("shell32.dll")
|
|
procShellExecute = modshell32.NewProc("ShellExecuteW")
|
|
)
|
|
|
|
// 判断文件夹是否存在
|
|
func PathExists(path string) (bool, error) {
|
|
var err error
|
|
if _, err = os.Stat(path); err == nil {
|
|
return true, nil
|
|
}
|
|
if os.IsNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
|
|
// 用b的所有字段覆盖a的
|
|
// 如果fields不为空, 表示用b的特定字段覆盖a的
|
|
// a应该为结构体指针
|
|
func CopyFields(a interface{}, b interface{}, fields ...string) (err error) {
|
|
at := reflect.TypeOf(a)
|
|
av := reflect.ValueOf(a)
|
|
bt := reflect.TypeOf(b)
|
|
bv := reflect.ValueOf(b)
|
|
|
|
// 简单判断下
|
|
if at.Kind() != reflect.Ptr {
|
|
err = fmt.Errorf("a must be a struct pointer")
|
|
return
|
|
}
|
|
av = reflect.ValueOf(av.Interface())
|
|
|
|
// 要复制哪些字段
|
|
_fields := make([]string, 0)
|
|
if len(fields) > 0 {
|
|
_fields = fields
|
|
} else {
|
|
for i := 0; i < bv.NumField(); i++ {
|
|
_fields = append(_fields, bt.Field(i).Name)
|
|
}
|
|
}
|
|
|
|
if len(_fields) == 0 {
|
|
fmt.Println("no fields to copy")
|
|
return
|
|
}
|
|
|
|
// 复制
|
|
for i := 0; i < len(_fields); i++ {
|
|
name := _fields[i]
|
|
f := av.Elem().FieldByName(name)
|
|
bValue := bv.FieldByName(name)
|
|
|
|
// a中有同名的字段并且类型一致才复制
|
|
if f.IsValid() && f.Kind() == bValue.Kind() {
|
|
f.Set(bValue)
|
|
} else {
|
|
//fmt.Printf("no such field or different kind, fieldName: %s\n", name)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
//获取默认打印机
|
|
func GetDefaultPrinterName() (string, []uint16) {
|
|
var (
|
|
dll = syscall.MustLoadDLL("winspool.drv")
|
|
getDefaultPrinter = dll.MustFindProc("GetDefaultPrinterW")
|
|
pn [256]uint16
|
|
)
|
|
|
|
plen := len(pn)
|
|
getDefaultPrinter.Call(uintptr(unsafe.Pointer(&pn)), uintptr(unsafe.Pointer(&plen)))
|
|
printerName := syscall.UTF16ToString(pn[:])
|
|
printer16 := goutf16.EncodeStringToUTF16(printerName)
|
|
return printerName, printer16
|
|
}
|
|
|
|
// 设置默认打印机
|
|
func SetDefaultPrinter(printerName string) error {
|
|
var (
|
|
dll = syscall.MustLoadDLL("winspool.drv")
|
|
setDefaultPrinter = dll.MustFindProc("SetDefaultPrinterW")
|
|
p []uint16
|
|
pn [256]uint16
|
|
)
|
|
|
|
p = goutf16.EncodeStringToUTF16(printerName)
|
|
pn = UintSliceTo256Uint(p)
|
|
ret, _, msg := setDefaultPrinter.Call(uintptr(unsafe.Pointer(&pn)))
|
|
if ret == 0 {
|
|
return msg
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func Uint16ToBytes(n uint16) []byte {
|
|
return []byte{
|
|
byte(n),
|
|
byte(n >> 8),
|
|
}
|
|
}
|
|
|
|
func BytesToUint16(array []byte) uint16 {
|
|
var data uint16 = 0
|
|
for i := 0; i < len(array); i++ {
|
|
data = data + uint16(uint(array[i])<<uint(8*i))
|
|
}
|
|
|
|
return data
|
|
}
|
|
|
|
func UintSliceTo256Uint(us []uint16) (res [256]uint16) {
|
|
var i int
|
|
|
|
for i = 0; i < len(us); i++ {
|
|
res[i] = us[i]
|
|
}
|
|
return res
|
|
}
|
|
|
|
func GetExcelCellIntPos(pos string) (row, col int, err error) {
|
|
var (
|
|
chareg, numreg *regexp.Regexp
|
|
cha, num [][]string
|
|
i int
|
|
uc []rune
|
|
rowstr string
|
|
)
|
|
|
|
chareg = regexp.MustCompile("[a-zA-Z]")
|
|
numreg = regexp.MustCompile("[0-9]")
|
|
cha = chareg.FindAllStringSubmatch(pos, -1)
|
|
num = numreg.FindAllStringSubmatch(pos, -1)
|
|
// 获取字符的列值
|
|
col = 0
|
|
for i = 0; i < len(cha); i++ {
|
|
uc = []rune(strings.ToUpper(cha[i][0]))
|
|
col = col + i*26 + (int(uc[0]) - 65)
|
|
}
|
|
|
|
// 获取行值
|
|
for i = 0; i < len(num); i++ {
|
|
rowstr = rowstr + num[i][0]
|
|
}
|
|
row, _ = strconv.Atoi(rowstr)
|
|
|
|
return row - 1, col, nil
|
|
}
|
|
|
|
func ShellExecute(hwnd HWND, lpOperation, lpFile, lpParameters, lpDirectory string, nShowCmd int) error {
|
|
var op, param, directory uintptr
|
|
if len(lpOperation) != 0 {
|
|
op = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpOperation)))
|
|
}
|
|
if len(lpParameters) != 0 {
|
|
param = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpParameters)))
|
|
}
|
|
if len(lpDirectory) != 0 {
|
|
directory = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpDirectory)))
|
|
}
|
|
|
|
ret, _, _ := procShellExecute.Call(
|
|
uintptr(hwnd),
|
|
op,
|
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpFile))),
|
|
param,
|
|
directory,
|
|
uintptr(nShowCmd))
|
|
|
|
errorMsg := ""
|
|
if ret != 0 && ret <= 32 {
|
|
switch int(ret) {
|
|
case ERROR_FILE_NOT_FOUND:
|
|
errorMsg = "The specified file was not found."
|
|
case ERROR_PATH_NOT_FOUND:
|
|
errorMsg = "The specified path was not found."
|
|
case ERROR_BAD_FORMAT:
|
|
errorMsg = "The .exe file is invalid (non-Win32 .exe or error in .exe image)."
|
|
case SE_ERR_ACCESSDENIED:
|
|
errorMsg = "The operating system denied access to the specified file."
|
|
case SE_ERR_ASSOCINCOMPLETE:
|
|
errorMsg = "The file name association is incomplete or invalid."
|
|
case SE_ERR_DDEBUSY:
|
|
errorMsg = "The DDE transaction could not be completed because other DDE transactions were being processed."
|
|
case SE_ERR_DDEFAIL:
|
|
errorMsg = "The DDE transaction failed."
|
|
case SE_ERR_DDETIMEOUT:
|
|
errorMsg = "The DDE transaction could not be completed because the request timed out."
|
|
case SE_ERR_DLLNOTFOUND:
|
|
errorMsg = "The specified DLL was not found."
|
|
case SE_ERR_NOASSOC:
|
|
errorMsg = "There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable."
|
|
case SE_ERR_OOM:
|
|
errorMsg = "There was not enough memory to complete the operation."
|
|
case SE_ERR_SHARE:
|
|
errorMsg = "A sharing violation occurred."
|
|
default:
|
|
errorMsg = fmt.Sprintf("Unknown error occurred with error code %v", ret)
|
|
}
|
|
} else {
|
|
return nil
|
|
}
|
|
|
|
return errors.New(errorMsg)
|
|
}
|
|
|
|
// RandNumber 生成min - max之间的随机数
|
|
// 如果min大于max, panic
|
|
func RandNumber(min, max int) int {
|
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
switch {
|
|
case min == max:
|
|
return min
|
|
case min > max:
|
|
panic("min must be less than or equal to max")
|
|
}
|
|
|
|
return min + r.Intn(max-min)
|
|
}
|
|
|
|
// PanicToError Panic转换为error
|
|
func PanicToError(f func()) (err error) {
|
|
defer func() {
|
|
if e := recover(); e != nil {
|
|
err = fmt.Errorf(PanicTrace(e))
|
|
}
|
|
}()
|
|
f()
|
|
return
|
|
}
|
|
|
|
// PanicTrace panic调用链跟踪
|
|
func PanicTrace(err interface{}) string {
|
|
stackBuf := make([]byte, 4096)
|
|
n := runtime.Stack(stackBuf, false)
|
|
|
|
return fmt.Sprintf("panic: %v %s", err, stackBuf[:n])
|
|
}
|
|
|
|
// DownloadFile 文件下载
|
|
func DownloadFile(filePath string, rw http.ResponseWriter) error {
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
filename := path.Base(filePath)
|
|
rw.Header().Set("Content-Type", "application/octet-stream")
|
|
rw.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
|
|
_, err = io.Copy(rw, file)
|
|
|
|
return err
|
|
}
|
|
|
|
// WorkDir 获取程序运行时根目录
|
|
func WorkDir() (string, error) {
|
|
execPath, err := os.Executable()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
wd := filepath.Dir(execPath)
|
|
if filepath.Base(wd) == "bin" {
|
|
wd = filepath.Dir(wd)
|
|
}
|
|
|
|
return wd, nil
|
|
}
|
|
|
|
// WaitGroupWrapper waitGroup包装
|
|
type WaitGroupWrapper struct {
|
|
sync.WaitGroup
|
|
}
|
|
|
|
// Wrap 包装Add, Done方法
|
|
func (w *WaitGroupWrapper) Wrap(f func()) {
|
|
w.Add(1)
|
|
go func() {
|
|
defer w.Done()
|
|
f()
|
|
}()
|
|
|
|
}
|
|
|
|
// 生成长度为length的随机字符串
|
|
func RandString(length int64) string {
|
|
sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
var result []byte
|
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
sourceLength := len(sources)
|
|
var i int64 = 0
|
|
for ; i < length; i++ {
|
|
result = append(result, sources[r.Intn(sourceLength)])
|
|
}
|
|
|
|
return string(result)
|
|
}
|
|
|
|
// 批量替换字符串
|
|
func ReplaceStrings(s string, old []string, replace []string) string {
|
|
if s == "" {
|
|
return s
|
|
}
|
|
if len(old) != len(replace) {
|
|
return s
|
|
}
|
|
|
|
for i, v := range old {
|
|
s = strings.Replace(s, v, replace[i], 1000)
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func InStringSlice(slice []string, element string) bool {
|
|
element = strings.TrimSpace(element)
|
|
for _, v := range slice {
|
|
if strings.TrimSpace(v) == element {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// 转义json特殊字符
|
|
func EscapeJson(s string) string {
|
|
specialChars := []string{"\\", "\b", "\f", "\n", "\r", "\t", "\""}
|
|
replaceChars := []string{"\\\\", "\\b", "\\f", "\\n", "\\r", "\\t", "\\\""}
|
|
|
|
return ReplaceStrings(s, specialChars, replaceChars)
|
|
}
|
|
|
|
// 判断文件是否存在及是否有权限访问
|
|
func FileExist(file string) bool {
|
|
_, err := os.Stat(file)
|
|
if os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
if os.IsPermission(err) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
|
|
func Max(vals ...int) int {
|
|
var max int
|
|
for _, val := range vals {
|
|
if val > max {
|
|
max = val
|
|
}
|
|
}
|
|
return max
|
|
}
|
|
|
|
func Min(vals ...int) int {
|
|
var min int
|
|
for _, val := range vals {
|
|
if min == 0 || val <= min {
|
|
min = val
|
|
}
|
|
}
|
|
return min
|
|
}
|
|
|
|
func MaxTime(vals ...time.Time) time.Time {
|
|
var max time.Time
|
|
|
|
if len(vals) > 0 {
|
|
max = vals[0]
|
|
}else{
|
|
// 默认返回一年前的时间
|
|
return time.Now().Add(24*265*time.Second)
|
|
}
|
|
|
|
for _, val := range vals {
|
|
if val.Unix() > max.Unix() {
|
|
max = val
|
|
}
|
|
}
|
|
return max
|
|
}
|
|
|
|
func MinTime(vals ...time.Time) time.Time {
|
|
var min time.Time
|
|
|
|
if len(vals) > 0 {
|
|
min = vals[0]
|
|
}else{
|
|
// 默认返回一年前的时间
|
|
return time.Now().Add(24*265*time.Second)
|
|
}
|
|
|
|
for _, val := range vals {
|
|
if val.Unix() <= min.Unix() {
|
|
min = val
|
|
}
|
|
}
|
|
return min
|
|
}
|
|
|