You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

389 lines
8.8 KiB

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
}