✨ init project
This commit is contained in:
16
utils/desensitization.go
Normal file
16
utils/desensitization.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package utils
|
||||
|
||||
type desensitization struct{}
|
||||
|
||||
// Desensitization 暴露接口 - 脱敏相关
|
||||
func Desensitization() *desensitization {
|
||||
return &desensitization{}
|
||||
}
|
||||
|
||||
// Phone 脱敏手机号
|
||||
func (desensitization) Phone(phone string) string {
|
||||
if len(phone) == 11 {
|
||||
return phone[:3] + "****" + phone[7:]
|
||||
}
|
||||
return phone
|
||||
}
|
63
utils/file.go
Normal file
63
utils/file.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
type fileUtils struct{}
|
||||
|
||||
func FileUtils() *fileUtils {
|
||||
return &fileUtils{}
|
||||
}
|
||||
|
||||
func (f fileUtils) CheckIsExists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
// CheckAndCreate 判断文件名是否存在,不存在就创建
|
||||
func (f fileUtils) CheckAndCreate(path string) error {
|
||||
exists, err := f.CheckIsExists(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
err = os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelPath 删除目录下的所有文件和目录本身
|
||||
func (f fileUtils) DelPath(path string) error {
|
||||
files, err := fileutil.ListFileNames(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
// 如果是目录,则递归删除
|
||||
if fileutil.IsDir(file) {
|
||||
err = f.DelPath(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = fileutil.RemoveFile(fmt.Sprintf("%s/%s", path, file))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = fileutil.RemoveFile(path)
|
||||
return nil
|
||||
}
|
79
utils/http.go
Normal file
79
utils/http.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type httpUtil struct {
|
||||
}
|
||||
|
||||
// Http 暴露接口
|
||||
func Http() *httpUtil {
|
||||
return &httpUtil{}
|
||||
}
|
||||
|
||||
// NewProxy 重写创建代理函数,加入 Host 信息
|
||||
func (hu httpUtil) NewProxy(target *url.URL) *httputil.ReverseProxy {
|
||||
targetQuery := target.RawQuery
|
||||
director := func(req *http.Request) {
|
||||
req.Host = target.Host
|
||||
req.URL.Scheme = target.Scheme
|
||||
req.URL.Host = target.Host
|
||||
req.URL.Path, req.URL.RawPath = hu.joinURLPath(target, req.URL)
|
||||
// 过滤掉参数
|
||||
if uri, err := url.Parse(req.URL.Path); err == nil {
|
||||
req.URL.Path = uri.Path
|
||||
}
|
||||
|
||||
var rawQuery string
|
||||
if targetQuery == "" || req.URL.RawQuery == "" {
|
||||
rawQuery = targetQuery + req.URL.RawQuery
|
||||
} else {
|
||||
rawQuery = targetQuery + "&" + req.URL.RawQuery
|
||||
}
|
||||
req.URL.RawQuery = rawQuery
|
||||
if _, ok := req.Header["User-Agent"]; !ok {
|
||||
// explicitly disable User-Agent so it's not set to default value
|
||||
req.Header.Set("User-Agent", "")
|
||||
}
|
||||
// 补充内部调用Header
|
||||
req.Header.Set("X-Request-From", "internal")
|
||||
}
|
||||
return &httputil.ReverseProxy{Director: director}
|
||||
}
|
||||
|
||||
func (hu httpUtil) joinURLPath(a, b *url.URL) (path, rawpath string) {
|
||||
if a.RawPath == "" && b.RawPath == "" {
|
||||
return hu.singleJoiningSlash(a.Path, b.Path), ""
|
||||
}
|
||||
// Same as singleJoiningSlash, but uses EscapedPath to determine
|
||||
// whether a slash should be added
|
||||
apath := a.EscapedPath()
|
||||
bpath := b.EscapedPath()
|
||||
|
||||
aslash := strings.HasSuffix(apath, "/")
|
||||
bslash := strings.HasPrefix(bpath, "/")
|
||||
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a.Path + b.Path[1:], apath + bpath[1:]
|
||||
case !aslash && !bslash:
|
||||
return a.Path + "/" + b.Path, apath + "/" + bpath
|
||||
}
|
||||
return a.Path + b.Path, apath + bpath
|
||||
}
|
||||
|
||||
func (hu httpUtil) singleJoiningSlash(a, b string) string {
|
||||
aslash := strings.HasSuffix(a, "/")
|
||||
bslash := strings.HasPrefix(b, "/")
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a + b[1:]
|
||||
case !aslash && !bslash:
|
||||
return a + "/" + b
|
||||
}
|
||||
return a + b
|
||||
}
|
71
utils/jwt.go
Normal file
71
utils/jwt.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"Lee-WineList/config"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
type JWT struct {
|
||||
claims jwt.MapClaims
|
||||
}
|
||||
|
||||
// GenerateToken 根据UserId生成并返回Token
|
||||
func GenerateToken(userId uint64) string {
|
||||
jwtConfig := config.Scd.JWT
|
||||
now := time.Now().Unix()
|
||||
claims := make(jwt.MapClaims)
|
||||
claims["exp"] = now + jwtConfig.AccessExpire
|
||||
claims["iat"] = now
|
||||
claims["rft"] = now + jwtConfig.RefreshAfter
|
||||
claims["userId"] = userId
|
||||
|
||||
token := jwt.New(jwt.SigningMethodHS256)
|
||||
token.Claims = claims
|
||||
tokenStr, _ := token.SignedString([]byte(jwtConfig.AccessSecret))
|
||||
|
||||
return tokenStr
|
||||
}
|
||||
|
||||
// ParseToken 解析Token
|
||||
func ParseToken(tokenStr string) (*JWT, error) {
|
||||
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.Scd.JWT.AccessSecret), nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &JWT{claims: token.Claims.(jwt.MapClaims)}, nil
|
||||
}
|
||||
|
||||
// Valid 验证token是否有效
|
||||
func (jwt *JWT) Valid() bool {
|
||||
if err := jwt.claims.Valid(); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CheckRefresh 检查是否可以刷新
|
||||
func (jwt *JWT) CheckRefresh() bool {
|
||||
if time.Now().Unix() > jwt.GetRefreshTime() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (jwt *JWT) RefreshToken() string {
|
||||
return GenerateToken(jwt.GetUserId())
|
||||
}
|
||||
|
||||
// GetUserId 从Token中解析userId
|
||||
func (jwt *JWT) GetUserId() uint64 {
|
||||
return uint64(jwt.claims["userId"].(float64))
|
||||
}
|
||||
|
||||
// GetRefreshTime 从Token中解析refreshTime
|
||||
func (jwt *JWT) GetRefreshTime() int64 {
|
||||
return int64(jwt.claims["rft"].(float64))
|
||||
}
|
62
utils/oss.go
Normal file
62
utils/oss.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"Lee-WineList/config"
|
||||
"fmt"
|
||||
"git.echol.cn/loser/logger/log"
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"io"
|
||||
)
|
||||
|
||||
type ossUtils struct{}
|
||||
|
||||
func OssUtils() *ossUtils {
|
||||
return &ossUtils{}
|
||||
}
|
||||
|
||||
// UploadReader 上传文件 - io.Reader
|
||||
func (o ossUtils) UploadReader(fileName string, data io.Reader) (string, error) {
|
||||
// 创建OSSClient实例。
|
||||
client, err := oss.New(config.Scd.Aliyun.Oss.Endpoint, config.Scd.Aliyun.Oss.AccessKeyId, config.Scd.Aliyun.Oss.AccessKeySecret)
|
||||
if err != nil {
|
||||
log.Errorf("创建OSSClient实例失败: %s", err)
|
||||
return "", err
|
||||
}
|
||||
// 获取存储空间。
|
||||
bucket, err := client.Bucket(config.Scd.Aliyun.Oss.Bucket)
|
||||
if err != nil {
|
||||
log.Errorf("获取存储空间失败: %s", err)
|
||||
return "", err
|
||||
}
|
||||
// 指定存储类型为标准存储,默认为标准存储。
|
||||
storageType := oss.ObjectStorageClass(oss.StorageStandard)
|
||||
// 指定访问权限为公共读,默认为继承bucket的权限。
|
||||
objectAcl := oss.ObjectACL(oss.ACLPublicRead)
|
||||
// 上传文件
|
||||
err = bucket.PutObject(fileName, data, storageType, objectAcl)
|
||||
if err != nil {
|
||||
log.Errorf("上传文件失败: %s", err)
|
||||
return "", err
|
||||
}
|
||||
fileUrl := fmt.Sprintf("https://%s.%s/%s", config.Scd.Aliyun.Oss.Bucket, config.Scd.Aliyun.Oss.Endpoint, fileName)
|
||||
return fileUrl, err
|
||||
}
|
||||
|
||||
func (o ossUtils) GetFile(fileName, localFilePath string) error {
|
||||
client, err := oss.New(config.Scd.Aliyun.Oss.Endpoint, config.Scd.Aliyun.Oss.AccessKeyId, config.Scd.Aliyun.Oss.AccessKeySecret)
|
||||
if err != nil {
|
||||
log.Errorf("创建OSSClient实例失败: %s", err)
|
||||
return err
|
||||
}
|
||||
// 获取存储空间。
|
||||
bucket, err := client.Bucket(config.Scd.Aliyun.Oss.Bucket)
|
||||
if err != nil {
|
||||
log.Errorf("获取存储空间失败: %s", err)
|
||||
return err
|
||||
}
|
||||
err = bucket.GetObjectToFile(fileName, localFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
14
utils/page.go
Normal file
14
utils/page.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package utils
|
||||
|
||||
// GenTotalPage 计算总页数
|
||||
func GenTotalPage(count int64, size int) int {
|
||||
totalPage := 0
|
||||
if count > 0 {
|
||||
upPage := 0
|
||||
if int(count)%size > 0 {
|
||||
upPage = 1
|
||||
}
|
||||
totalPage = (int(count) / size) + upPage
|
||||
}
|
||||
return totalPage
|
||||
}
|
28
utils/time.go
Normal file
28
utils/time.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type timeUtils struct{}
|
||||
|
||||
func TimeUtils() *timeUtils {
|
||||
return &timeUtils{}
|
||||
}
|
||||
|
||||
// DurationString Duration转自定义String
|
||||
func (tu timeUtils) DurationString(d time.Duration) string {
|
||||
if d.Seconds() == 0 {
|
||||
return "00:00:00"
|
||||
}
|
||||
s := d.String()
|
||||
s = strings.ReplaceAll(s, "h", "小时")
|
||||
s = strings.ReplaceAll(s, "m", "分")
|
||||
idx := strings.Index(s, ".")
|
||||
if idx == -1 {
|
||||
return fmt.Sprintf("%s秒", s)
|
||||
}
|
||||
return fmt.Sprintf("%s秒", s[:idx])
|
||||
}
|
20
utils/token.go
Normal file
20
utils/token.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// 生成Token的密钥,写死,防止乱改
|
||||
//var jwtSecret = "qSxw4fCBBBecPsws"
|
||||
|
||||
// HashPassword 加密密码
|
||||
func HashPassword(pass *string) {
|
||||
bytePass := []byte(*pass)
|
||||
hPass, _ := bcrypt.GenerateFromPassword(bytePass, bcrypt.DefaultCost)
|
||||
*pass = string(hPass)
|
||||
}
|
||||
|
||||
// ComparePassword 校验密码
|
||||
func ComparePassword(dbPass, pass string) bool {
|
||||
return bcrypt.CompareHashAndPassword([]byte(dbPass), []byte(pass)) == nil
|
||||
}
|
70
utils/wechat.go
Normal file
70
utils/wechat.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"Lee-WineList/config"
|
||||
"Lee-WineList/model/param"
|
||||
"git.echol.cn/loser/logger/log"
|
||||
"github.com/medivhzhan/weapp/v3"
|
||||
"github.com/medivhzhan/weapp/v3/auth"
|
||||
"github.com/medivhzhan/weapp/v3/phonenumber"
|
||||
)
|
||||
|
||||
type wechat struct{}
|
||||
|
||||
func WeChatUtils() *wechat {
|
||||
return &wechat{}
|
||||
}
|
||||
|
||||
// GetWechatUnionId 获取微信用户基础信息
|
||||
func (w wechat) GetWechatUnionId(code string) (unionId, openId, sessionKey string, err error) {
|
||||
sdk := weapp.NewClient(config.Scd.Tencent.MiniApp.AppId, config.Scd.Tencent.MiniApp.AppSecret, weapp.WithLogger(nil))
|
||||
cli := sdk.NewAuth()
|
||||
|
||||
p := auth.Code2SessionRequest{
|
||||
Appid: config.Scd.Tencent.MiniApp.AppId,
|
||||
Secret: config.Scd.Tencent.MiniApp.AppSecret,
|
||||
JsCode: code,
|
||||
GrantType: "authorization_code",
|
||||
}
|
||||
|
||||
session, err := cli.Code2Session(&p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if session.GetResponseError() != nil {
|
||||
log.Errorf("Code解析失败: %v", session.GetResponseError())
|
||||
err = session.GetResponseError()
|
||||
return
|
||||
}
|
||||
// 设置UnionId值
|
||||
unionId = session.Unionid
|
||||
openId = session.Openid
|
||||
sessionKey = session.SessionKey
|
||||
return
|
||||
}
|
||||
|
||||
// GetWechatPhone 根据Code获取小程序用户手机号
|
||||
// return 不带区号的手机号
|
||||
func (w wechat) GetWechatPhone(param param.DecryptMobile) (string, error) {
|
||||
sdk := weapp.NewClient(config.Scd.Tencent.MiniApp.AppId, config.Scd.Tencent.MiniApp.AppSecret)
|
||||
mobile, err := sdk.DecryptMobile(param.SessionKey, param.EncryptedData, param.Iv)
|
||||
if err != nil {
|
||||
log.Errorf("解密手机号失败: %v", err)
|
||||
return "", err
|
||||
}
|
||||
log.Debugf("解密后的手机号: %+v", mobile)
|
||||
return mobile.PurePhoneNumber, nil
|
||||
}
|
||||
|
||||
// GetPhoneNumber 获取手机号
|
||||
func (w wechat) GetPhoneNumber(code string) (phone string, err error) {
|
||||
sdk := weapp.NewClient(config.Scd.Tencent.MiniApp.AppId, config.Scd.Tencent.MiniApp.AppSecret)
|
||||
resp, err := sdk.NewPhonenumber().GetPhoneNumber(&phonenumber.GetPhoneNumberRequest{Code: code})
|
||||
if err != nil {
|
||||
log.Errorf("获取手机号失败: %v", err)
|
||||
return
|
||||
}
|
||||
// 获取手机号
|
||||
phone = resp.Data.PurePhoneNumber
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user