Compare commits

..

No commits in common. "d2ae9e258be093121d0a37f3c42f639e8a0c7b5a" and "af3a8435b9a8683ea3285db1425298e39f023290" have entirely different histories.

10 changed files with 130 additions and 339 deletions

1
.gitignore vendored
View File

@ -10,4 +10,3 @@ test
go.sum go.sum
pkg pkg
test.go test.go
.claude

18
go.mod
View File

@ -7,9 +7,7 @@ require (
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf
github.com/eclipse/paho.mqtt.golang v1.5.1 github.com/eclipse/paho.mqtt.golang v1.5.1
github.com/gin-gonic/gin v1.11.0 github.com/gin-gonic/gin v1.11.0
github.com/go-redis/redis v6.15.9+incompatible
github.com/gomodule/redigo v1.9.2 github.com/gomodule/redigo v1.9.2
github.com/google/uuid v1.6.0
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
github.com/minio/minio-go v6.0.14+incompatible github.com/minio/minio-go v6.0.14+incompatible
github.com/qiniu/go-sdk/v7 v7.25.4 github.com/qiniu/go-sdk/v7 v7.25.4
@ -17,7 +15,7 @@ require (
github.com/spf13/cast v1.10.0 github.com/spf13/cast v1.10.0
github.com/tidwall/gjson v1.18.0 github.com/tidwall/gjson v1.18.0
github.com/wechatpay-apiv3/wechatpay-go v0.2.21 github.com/wechatpay-apiv3/wechatpay-go v0.2.21
golang.org/x/crypto v0.47.0 golang.org/x/crypto v0.42.0
gorm.io/gorm v1.31.0 gorm.io/gorm v1.31.0
) )
@ -49,8 +47,6 @@ require (
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.39.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect github.com/quic-go/quic-go v0.54.0 // indirect
@ -62,12 +58,12 @@ require (
go.uber.org/mock v0.5.0 // indirect go.uber.org/mock v0.5.0 // indirect
golang.org/x/arch v0.20.0 // indirect golang.org/x/arch v0.20.0 // indirect
golang.org/x/image v0.31.0 // indirect golang.org/x/image v0.31.0 // indirect
golang.org/x/mod v0.32.0 // indirect golang.org/x/mod v0.27.0 // indirect
golang.org/x/net v0.49.0 // indirect golang.org/x/net v0.44.0 // indirect
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.33.0 // indirect golang.org/x/text v0.29.0 // indirect
golang.org/x/tools v0.41.0 // indirect golang.org/x/tools v0.36.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect google.golang.org/protobuf v1.36.9 // indirect
modernc.org/fileutil v1.0.0 // indirect modernc.org/fileutil v1.0.0 // indirect
) )

View File

@ -316,11 +316,6 @@ func (a *App) Upload(path, kind string) (string, error) {
return cast.ToString(result["media_id"]), nil return cast.ToString(result["media_id"]), nil
} }
func (a *App) MediaUrl(mediaId string) string {
mUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s", a.GetToken(), mediaId)
return mUrl
}
func (q *App) GetJsapiConfig(url string) (*JsapiConfig, error) { func (q *App) GetJsapiConfig(url string) (*JsapiConfig, error) {
ticket, err := q.getJsapiTicket() ticket, err := q.getJsapiTicket()
if err != nil { if err != nil {

View File

@ -3,12 +3,11 @@ package qyweixin
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings"
"git.u8t.cn/open/gosdk/util" "git.u8t.cn/open/gosdk/util"
"git.u8t.cn/open/goutil" "git.u8t.cn/open/goutil"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cast" "github.com/spf13/cast"
"strings"
) )
type Applyer struct { type Applyer struct {
@ -91,8 +90,57 @@ type AppApprove struct {
} }
func (d *ApproveDetail) GetValue(title string) string { func (d *ApproveDetail) GetValue(title string) string {
data := d.GetData()
return data[title] for _, content := range d.ApplyData.Contents {
isEqual := false
for _, ti := range content.Title {
if ti.Text == title {
isEqual = true
}
}
if !isEqual {
continue
}
var value string
if content.Control == "Selector" {
for _, v := range content.Value.Selector.Options[0].Value {
if v.Text != "" {
value = v.Text
}
}
} else if content.Control == "Text" || content.Control == "Textarea" {
value = content.Value.Text
} else if content.Control == "Date" {
value = content.Value.Date.Timestamp
} else if content.Control == "Money" {
value = content.Value.NewMoney
} else if content.Control == "File" {
value = content.Value.Files[0].FileId
} else if content.Control == "Vacation" { //请假 请假类型,开始时间,结束时间,请假时长
tp := content.Value.Vacation.Selector.Options[0].Value[0].Text
value = tp + "," + cast.ToString(content.Value.Vacation.Attendance.DateRange.NewBegin) +
"," + cast.ToString(content.Value.Vacation.Attendance.DateRange.NewEnd) +
"," + cast.ToString(content.Value.Vacation.Attendance.DateRange.NewDuration)
} else if content.Control == "PunchCorrection" { //补卡:日期,时间,状态
mp := cast.ToStringMap(content.Value.PunchCorrection)
ddate := cast.ToString(mp["daymonthyear"])
dtime := cast.ToString(mp["time"])
if ddate == "" {
ddate = dtime
}
value = ddate + "," + dtime + "," + cast.ToString(mp["state"])
} else if content.Control == "BankAccount" {
mp := cast.ToStringMap(content.Value.BankAccount)
value = cast.ToString(mp["account_type"]) + "," + cast.ToString(mp["account_name"]) + "," + cast.ToString(mp["account_number"])
} else if content.Control == "Number" {
value = content.Value.NewNumber
}
return value
}
return ""
} }
func (d *ApproveDetail) GetData() map[string]string { func (d *ApproveDetail) GetData() map[string]string {
@ -117,11 +165,9 @@ func (d *ApproveDetail) GetData() map[string]string {
} else if content.Control == "Money" { } else if content.Control == "Money" {
value = content.Value.NewMoney value = content.Value.NewMoney
} else if content.Control == "File" { } else if content.Control == "File" {
fileIds := make([]string, 0) if len(content.Value.Files) > 0 {
for _, v := range content.Value.Files { value = content.Value.Files[0].FileId
fileIds = append(fileIds, v.FileId)
} }
value = strings.Join(fileIds, ",")
} else if content.Control == "Vacation" { //请假 请假类型,开始时间,结束时间,请假时长 } else if content.Control == "Vacation" { //请假 请假类型,开始时间,结束时间,请假时长
tp := content.Value.Vacation.Selector.Options[0].Value[0].Text tp := content.Value.Vacation.Selector.Options[0].Value[0].Text
value = tp + "," + cast.ToString(content.Value.Vacation.Attendance.DateRange.NewBegin) + value = tp + "," + cast.ToString(content.Value.Vacation.Attendance.DateRange.NewBegin) +
@ -174,7 +220,7 @@ func (q *AppApprove) GetDetail(spNo string) (*ApproveDetail, error) {
} }
var rsp ApproveDetailRsp var rsp ApproveDetailRsp
//fmt.Println("spno: %s, detail: %s", spNo, string(rspBody)) fmt.Println("spno: %s, detail: %s", spNo, string(rspBody))
if err := json.Unmarshal(rspBody, &rsp); err != nil { if err := json.Unmarshal(rspBody, &rsp); err != nil {
log.Errorf("get body[%s] json error :%s", string(rspBody), err.Error()) log.Errorf("get body[%s] json error :%s", string(rspBody), err.Error())
return nil, err return nil, err

View File

@ -2,12 +2,11 @@ package qyweixin
import ( import (
"fmt" "fmt"
"time"
"git.u8t.cn/open/gosdk/util" "git.u8t.cn/open/gosdk/util"
"git.u8t.cn/open/goutil"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"git.u8t.cn/open/goutil"
"github.com/spf13/cast" "github.com/spf13/cast"
"time"
) )
var ( var (
@ -15,7 +14,6 @@ var (
urlQyWeixinHrGetStaffInfo = "https://qyapi.weixin.qq.com/cgi-bin/hr/get_staff_info" urlQyWeixinHrGetStaffInfo = "https://qyapi.weixin.qq.com/cgi-bin/hr/get_staff_info"
urlQyWeixinHrGetDepartment = "https://qyapi.weixin.qq.com/cgi-bin/department/list" urlQyWeixinHrGetDepartment = "https://qyapi.weixin.qq.com/cgi-bin/department/list"
urlQyWeixinHrGetDepartmentUser = "https://qyapi.weixin.qq.com/cgi-bin/user/list" urlQyWeixinHrGetDepartmentUser = "https://qyapi.weixin.qq.com/cgi-bin/user/list"
urlQyWeixinHrGetContactInfo = "https://qyapi.weixin.qq.com/cgi-bin/user/get"
) )
type AppHr struct { type AppHr struct {
@ -28,7 +26,6 @@ type Department struct {
Pid int64 `json:"pid"` Pid int64 `json:"pid"`
Name string `json:"name"` Name string `json:"name"`
Leader []string `json:"leader"` Leader []string `json:"leader"`
Childchren []*Department `json:"childchren"`
} }
type StaffInfo struct { type StaffInfo struct {
@ -45,38 +42,12 @@ type StaffInfo struct {
BankCard string BankCard string
} }
type ContactInfo struct {
Realname string
DirectLeader []string
DepartmentId []int64
IsDepartmentLeader []int
}
func NewAppHr(cfg *AppConfig) *AppHr { func NewAppHr(cfg *AppConfig) *AppHr {
return &AppHr{ return &AppHr{
App: *NewApp(cfg), App: *NewApp(cfg),
} }
} }
func (h *AppHr) GetContactInfo(userId string) (*ContactInfo, error) {
reqUrl := fmt.Sprintf("%s?access_token=%s&userid=%s", urlQyWeixinHrGetContactInfo, h.GetToken(), userId)
rspBody, err := util.HttpGet(reqUrl, nil)
if err != nil {
return nil, err
}
contract := new(ContactInfo)
result, err := h.GetResult(rspBody)
if err != nil {
return nil, err
}
contract.Realname = cast.ToString(result["name"])
contract.DirectLeader = cast.ToStringSlice(result["direct_leader"])
contract.DepartmentId = cast.ToInt64Slice(result["department"])
contract.IsDepartmentLeader = cast.ToIntSlice(result["is_leader_in_dept"])
return contract, nil
}
func (h *AppHr) GetStaffInfo(userId string) (*StaffInfo, error) { func (h *AppHr) GetStaffInfo(userId string) (*StaffInfo, error) {
reqUrl := fmt.Sprintf("%s?access_token=%s", urlQyWeixinHrGetStaffInfo, h.GetToken()) reqUrl := fmt.Sprintf("%s?access_token=%s", urlQyWeixinHrGetStaffInfo, h.GetToken())
reqBody := make(map[string]interface{}) reqBody := make(map[string]interface{})
@ -137,7 +108,6 @@ func (h *AppHr) GetDepartment(id int64) ([]*Department, error) {
result := make([]*Department, 0) result := make([]*Department, 0)
departments := cast.ToSlice(resp["department"]) departments := cast.ToSlice(resp["department"])
for _, dd := range departments { for _, dd := range departments {
d := cast.ToStringMap(dd) d := cast.ToStringMap(dd)
r := new(Department) r := new(Department)
@ -146,9 +116,7 @@ func (h *AppHr) GetDepartment(id int64) ([]*Department, error) {
r.Id = cast.ToInt64(d["id"]) r.Id = cast.ToInt64(d["id"])
r.Pid = cast.ToInt64(d["parentid"]) r.Pid = cast.ToInt64(d["parentid"])
result = append(result, r) result = append(result, r)
} }
return result, nil return result, nil
} }

View File

@ -3,14 +3,12 @@ package storage
import ( import (
"context" "context"
"fmt" "fmt"
"net/http" "github.com/minio/minio-go"
"net/url" "net/url"
"path" "path"
"strings" "strings"
"time" "time"
"github.com/minio/minio-go"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -63,13 +61,9 @@ func (s *Minio) Put(fileName, objectName string, onProcess func(fsize, uploaded
objectName = strings.TrimLeft(objectName, "/ ") objectName = strings.TrimLeft(objectName, "/ ")
ext := strings.TrimLeft(path.Ext(fileName), ".") ext := strings.TrimLeft(path.Ext(fileName), ".")
contentType := ext2ContentType(ext)
if contentType == "" {
contentType = detectContentType(fileName)
}
_, err := s.client.FPutObjectWithContext(ctx, s.config.Bucket, objectName, fileName, _, err := s.client.FPutObjectWithContext(ctx, s.config.Bucket, objectName, fileName,
minio.PutObjectOptions{ContentType: contentType}) minio.PutObjectOptions{ContentType: ext2ContentType(ext)})
if err != nil { if err != nil {
log.Errorf("upload file to minio error:%s", err.Error()) log.Errorf("upload file to minio error:%s", err.Error())
return err return err
@ -114,7 +108,7 @@ func (s *Minio) List(objectPrefix string) ([]string, error) {
return result, nil return result, nil
} }
func (s *Minio) Url(objectName string, expire time.Duration, https ...bool) string { func (s *Minio) Url(objectName string, expire time.Duration) string {
if err := s.Init(); err != nil { if err := s.Init(); err != nil {
return err.Error() return err.Error()
} }
@ -122,19 +116,11 @@ func (s *Minio) Url(objectName string, expire time.Duration, https ...bool) stri
if expire > time.Hour*24*7 || expire == -1 { if expire > time.Hour*24*7 || expire == -1 {
expire = time.Hour * 24 * 7 expire = time.Hour * 24 * 7
} }
baseUrl := s.config.BaseUrl
if len(https) > 0 && https[0] {
if strings.HasPrefix(baseUrl, "http://") {
baseUrl = "https://" + strings.TrimPrefix(baseUrl, "http://")
}
}
var params url.Values var params url.Values
u, err := s.client.PresignedGetObject(s.config.Bucket, objectName, expire, params) u, err := s.client.PresignedGetObject(s.config.Bucket, objectName, expire, params)
if err != nil { if err != nil {
log.Errorf("error:%s", err.Error()) log.Errorf("error:%s", err.Error())
return fmt.Sprintf("%s/%s/%s", baseUrl, s.config.Bucket, objectName) return fmt.Sprintf("%s/%s/%s", s.config.BaseUrl, s.config.Bucket, objectName)
} }
return u.String() return u.String()
@ -142,78 +128,9 @@ func (s *Minio) Url(objectName string, expire time.Duration, https ...bool) stri
} }
func (s *Minio) Stat(objectName string) (*ObjectInfo, error) { func (s *Minio) Stat(objectName string) (*ObjectInfo, error) {
if err := s.Init(); err != nil {
return nil, err
}
objectName = strings.TrimLeft(objectName, "/ ")
stat, err := s.client.StatObject(s.config.Bucket, objectName, minio.StatObjectOptions{})
if err != nil {
// 文件不存在不算错误,返回 nil, nil
if isMinioNotFound(err) {
return nil, nil return nil, nil
}
log.Errorf("stat object from minio error:%s", err.Error())
return nil, err
}
info := &ObjectInfo{
Size: stat.Size,
Hash: stat.ETag,
MimeType: stat.ContentType,
PutTime: stat.LastModified.Unix(),
}
return info, nil
} }
// isMinioNotFound 检查是否为 MinIO 文件不存在错误 func (s *Minio) Fetch(url, objectName string) error {
func isMinioNotFound(err error) bool {
if err == nil {
return false
}
// MinIO 返回 NoSuchKey 表示文件不存在
if respErr, ok := err.(minio.ErrorResponse); ok {
return respErr.Code == "NoSuchKey" || respErr.Code == "NotFound"
}
return false
}
func (s *Minio) Fetch(urlStr, objectName string, local ...bool) error {
if err := s.Init(); err != nil {
return err
}
// 从 URL 下载文件
resp, err := http.Get(urlStr)
if err != nil {
log.Errorf("fetch file from url error:%s", err.Error())
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("fetch file from url failed, status: %d", resp.StatusCode)
}
// 获取文件名和 Content-Type
objectName = strings.TrimLeft(objectName, "/ ")
// 优先根据文件扩展名推断 Content-Type确保图片能正确预览
ext := strings.TrimLeft(path.Ext(objectName), ".")
contentType := ext2ContentType(ext)
if contentType == "" {
contentType = resp.Header.Get("Content-Type")
}
// 上传到 MinIO
ctx, cancel := context.WithTimeout(context.Background(), s.config.Timeout)
defer cancel()
_, err = s.client.PutObjectWithContext(ctx, s.config.Bucket, objectName, resp.Body, resp.ContentLength,
minio.PutObjectOptions{ContentType: contentType})
if err != nil {
log.Errorf("upload fetched file to minio error:%s", err.Error())
return err
}
return nil return nil
} }

View File

@ -4,13 +4,10 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"os"
"path"
"strings"
"time"
"github.com/qiniu/go-sdk/v7/auth/qbox" "github.com/qiniu/go-sdk/v7/auth/qbox"
"github.com/qiniu/go-sdk/v7/storage" "github.com/qiniu/go-sdk/v7/storage"
"os"
"time"
) )
type QiniuConfig struct { type QiniuConfig struct {
@ -155,7 +152,7 @@ func (s *Qiniu) Get(objectName, fileName string) error {
return Download(url, fileName) return Download(url, fileName)
} }
func (s *Qiniu) Url(objectName string, expire time.Duration, https ...bool) string { func (s *Qiniu) Url(objectName string, expire time.Duration) string {
mac := qbox.NewMac(s.config.AK, s.config.SK) mac := qbox.NewMac(s.config.AK, s.config.SK)
cfg := storage.Config{ cfg := storage.Config{
UseHTTPS: false, UseHTTPS: false,
@ -169,11 +166,8 @@ func (s *Qiniu) Url(objectName string, expire time.Duration, https ...bool) stri
if len(domains) <= 0 { if len(domains) <= 0 {
return "" return ""
} }
domain := "http://" + domains[0].Domain
if len(https) > 0 && https[0] {
domain = "https://" + domains[0].Domain
}
domain := "https://" + domains[0].Domain
if expire == 0 { if expire == 0 {
return storage.MakePublicURLv2(domain, objectName) return storage.MakePublicURLv2(domain, objectName)
} else { } else {
@ -191,10 +185,6 @@ func (s *Qiniu) Stat(objectName string) (*ObjectInfo, error) {
bucketManager := storage.NewBucketManager(mac, &cfg) bucketManager := storage.NewBucketManager(mac, &cfg)
fileInfo, err := bucketManager.Stat(s.config.Bucket, objectName) fileInfo, err := bucketManager.Stat(s.config.Bucket, objectName)
if err != nil { if err != nil {
// 文件不存在不算错误,返回 nil, nil
if isQiniuNotFound(err) {
return nil, nil
}
return nil, err return nil, err
} }
@ -206,22 +196,7 @@ func (s *Qiniu) Stat(objectName string) (*ObjectInfo, error) {
return info, nil return info, nil
} }
// isQiniuNotFound 检查是否为七牛云文件不存在错误 func (s *Qiniu) Fetch(url, objectName string) error {
func isQiniuNotFound(err error) bool {
if err == nil {
return false
}
// 七牛云错误码 612 表示文件不存在
if respErr, ok := err.(*storage.ErrorInfo); ok {
return respErr.Code == 612
}
return false
}
func (s *Qiniu) Fetch(url, objectName string, local ...bool) error {
if len(local) > 0 && local[0] == true {
return s.fetchLocal(url, objectName)
}
mac := qbox.NewMac(s.config.AK, s.config.SK) mac := qbox.NewMac(s.config.AK, s.config.SK)
cfg := storage.Config{ cfg := storage.Config{
UseHTTPS: false, UseHTTPS: false,
@ -236,19 +211,3 @@ func (s *Qiniu) Fetch(url, objectName string, local ...bool) error {
return nil return nil
} }
func (s *Qiniu) fetchLocal(url, objectName string) error {
objectName = strings.TrimLeft(objectName, "/ ")
ext := strings.TrimLeft(path.Ext(objectName), ".")
tmpFile := fmt.Sprintf("%d.%s", time.Now().UnixMilli(), ext)
if err := Download(url, tmpFile); err != nil {
return err
}
defer os.Remove(tmpFile)
if err := s.Put(tmpFile, objectName, nil); err != nil {
return err
}
return nil
}

View File

@ -13,8 +13,8 @@ type Storage interface {
Put(fileName, objectName string, onProcess func(fsize, uploaded int64)) error Put(fileName, objectName string, onProcess func(fsize, uploaded int64)) error
Get(objectName, fileName string) error Get(objectName, fileName string) error
Del(objectName string) error Del(objectName string) error
Url(objectName string, expire time.Duration, https ...bool) string //https参数是否生成https参数 Url(objectName string, expire time.Duration) string
Stat(objectName string) (*ObjectInfo, error) Stat(objectName string) (*ObjectInfo, error)
List(objectPrefix string) ([]string, error) List(objectPrefix string) ([]string, error)
Fetch(url, objectName string, local ...bool) error //local参数决定是否先下载到本地在上传七牛云下载企业微信的文件需要不能直接下载 Fetch(url, objectName string) error
} }

View File

@ -21,116 +21,16 @@ func contentType2Ext(contentType string) string {
func ext2ContentType(ext string) string { func ext2ContentType(ext string) string {
ext = strings.ToLower(ext) ext = strings.ToLower(ext)
switch ext { if ext == "jpg" || ext == "jpeg" {
// 图片格式
case "jpg", "jpeg":
return "image/jpeg" return "image/jpeg"
case "png": } else if ext == "png" {
return "image/png" return "image/png"
case "gif": } else if ext == "mp3" {
return "image/gif"
case "webp":
return "image/webp"
case "bmp":
return "image/bmp"
case "svg", "svgz":
return "image/svg+xml"
case "ico":
return "image/x-icon"
case "tiff", "tif":
return "image/tiff"
case "avif":
return "image/avif"
case "apng":
return "image/apng"
// 音频格式
case "mp3":
return "audio/mpeg" return "audio/mpeg"
case "wav": } else if ext == "mp4" {
return "audio/wav"
case "ogg":
return "audio/ogg"
case "flac":
return "audio/flac"
case "aac":
return "audio/aac"
case "m4a":
return "audio/mp4"
// 视频格式
case "mp4":
return "video/mp4" return "video/mp4"
case "avi":
return "video/x-msvideo"
case "mov", "qt":
return "video/quicktime"
case "webm":
return "video/webm"
case "flv":
return "video/x-flv"
case "mkv":
return "video/x-matroska"
case "wmv":
return "video/x-ms-wmv"
case "m3u8":
return "application/vnd.apple.mpegurl"
case "ts":
return "video/mp2t"
// 文档格式
case "pdf":
return "application/pdf"
case "doc":
return "application/msword"
case "docx":
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
case "xls":
return "application/vnd.ms-excel"
case "xlsx":
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
case "ppt":
return "application/vnd.ms-powerpoint"
case "pptx":
return "application/vnd.openxmlformats-officedocument.presentationml.presentation"
case "txt":
return "text/plain"
case "html", "htm":
return "text/html"
case "css":
return "text/css"
case "js":
return "application/javascript"
case "json":
return "application/json"
case "xml":
return "application/xml"
case "zip":
return "application/zip"
case "rar":
return "application/x-rar-compressed"
case "7z":
return "application/x-7z-compressed"
case "gz":
return "application/gzip"
default:
return ""
} }
}
// detectContentType 从文件内容检测 MIME 类型
func detectContentType(filePath string) string {
f, err := os.Open(filePath)
if err != nil {
return "" return ""
}
defer f.Close()
// 读取前 512 字节用于检测
buf := make([]byte, 512)
n, err := f.Read(buf)
if err != nil && err != io.EOF {
return ""
}
return http.DetectContentType(buf[:n])
} }
func Download(url, path string) error { func Download(url, path string) error {

View File

@ -9,36 +9,26 @@ import (
"time" "time"
) )
var (
httpClient *http.Client
)
func init() {
httpClient = &http.Client{
Timeout: 20 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 200,
MaxIdleConnsPerHost: 50,
MaxConnsPerHost: 500,
IdleConnTimeout: 90 * time.Second,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
}
// PostJson 请求 // PostJson 请求
func HttpPostJson(link string, header map[string]string, json []byte) ([]byte, error) { func HttpPostJson(link string, header map[string]string, json []byte) ([]byte, error) {
client := &http.Client{Timeout: 20 * time.Second}
//忽略https的证书
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
req, err := http.NewRequest("POST", link, bytes.NewBuffer(json)) req, err := http.NewRequest("POST", link, bytes.NewBuffer(json))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if header != nil {
for k, v := range header { for k, v := range header {
req.Header.Add(k, v) req.Header.Add(k, v)
} }
}
req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Type", "application/json")
resp, err := httpClient.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -51,17 +41,24 @@ func HttpPostJson(link string, header map[string]string, json []byte) ([]byte, e
// PostJson 请求 // PostJson 请求
func HttpPutJson(link string, header map[string]string, json []byte) ([]byte, error) { func HttpPutJson(link string, header map[string]string, json []byte) ([]byte, error) {
client := &http.Client{Timeout: 20 * time.Second}
//忽略https的证书
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
req, err := http.NewRequest("PUT", link, bytes.NewBuffer(json)) req, err := http.NewRequest("PUT", link, bytes.NewBuffer(json))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if header != nil {
for k, v := range header { for k, v := range header {
req.Header.Add(k, v) req.Header.Add(k, v)
} }
}
req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Type", "application/json")
resp, err := httpClient.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -74,15 +71,22 @@ func HttpPutJson(link string, header map[string]string, json []byte) ([]byte, er
// Get 请求 link请求url // Get 请求 link请求url
func HttpGet(link string, header map[string]string) ([]byte, error) { func HttpGet(link string, header map[string]string) ([]byte, error) {
client := &http.Client{Timeout: 20 * time.Second}
//忽略https的证书
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
req, err := http.NewRequest("GET", link, nil) req, err := http.NewRequest("GET", link, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if header != nil {
for k, v := range header { for k, v := range header {
req.Header.Add(k, v) req.Header.Add(k, v)
} }
resp, err := httpClient.Do(req) }
resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -95,15 +99,22 @@ func HttpGet(link string, header map[string]string) ([]byte, error) {
// Get 请求 link请求url // Get 请求 link请求url
func HttpDelete(link string, header map[string]string) ([]byte, error) { func HttpDelete(link string, header map[string]string) ([]byte, error) {
client := &http.Client{Timeout: 20 * time.Second}
//忽略https的证书
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
req, err := http.NewRequest("DELETE", link, nil) req, err := http.NewRequest("DELETE", link, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if header != nil {
for k, v := range header { for k, v := range header {
req.Header.Add(k, v) req.Header.Add(k, v)
} }
resp, err := httpClient.Do(req) }
resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }