315 lines
7.2 KiB
Go
315 lines
7.2 KiB
Go
|
|
package utils
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"crypto/md5"
|
|||
|
|
"crypto/rand"
|
|||
|
|
"encoding/hex"
|
|||
|
|
"fmt"
|
|||
|
|
"math"
|
|||
|
|
"strconv"
|
|||
|
|
"strings"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"golang.org/x/crypto/bcrypt"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// HashPassword 密码加密
|
|||
|
|
func HashPassword(password string) (string, error) {
|
|||
|
|
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|||
|
|
return string(bytes), err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CheckPassword 验证密码
|
|||
|
|
func CheckPassword(password, hash string) bool {
|
|||
|
|
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
|||
|
|
return err == nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MD5 MD5加密
|
|||
|
|
func MD5(str string) string {
|
|||
|
|
h := md5.New()
|
|||
|
|
h.Write([]byte(str))
|
|||
|
|
return hex.EncodeToString(h.Sum(nil))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GenerateRandomString 生成随机字符串
|
|||
|
|
func GenerateRandomString(length int) string {
|
|||
|
|
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|||
|
|
b := make([]byte, length)
|
|||
|
|
for i := range b {
|
|||
|
|
randomByte := make([]byte, 1)
|
|||
|
|
rand.Read(randomByte)
|
|||
|
|
b[i] = charset[randomByte[0]%byte(len(charset))]
|
|||
|
|
}
|
|||
|
|
return string(b)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GenerateOrderNo 生成订单号
|
|||
|
|
func GenerateOrderNo() string {
|
|||
|
|
now := time.Now()
|
|||
|
|
timestamp := now.Format("20060102150405")
|
|||
|
|
random := GenerateRandomString(6)
|
|||
|
|
return timestamp + strings.ToUpper(random)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GenerateWechatOutTradeNo 生成微信支付商户订单号
|
|||
|
|
// 格式:WX + 时间戳(精确到毫秒) + 8位随机字符串
|
|||
|
|
// 确保每次支付请求都有唯一的订单号,避免重复支付问题
|
|||
|
|
func GenerateWechatOutTradeNo() string {
|
|||
|
|
now := time.Now()
|
|||
|
|
// 使用纳秒时间戳确保唯一性
|
|||
|
|
timestamp := now.Format("20060102150405") + fmt.Sprintf("%03d", now.Nanosecond()/1000000)
|
|||
|
|
random := GenerateRandomString(8)
|
|||
|
|
return "WX" + timestamp + strings.ToUpper(random)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GenerateRefundNo 生成退款单号
|
|||
|
|
func GenerateRefundNo() string {
|
|||
|
|
now := time.Now()
|
|||
|
|
timestamp := now.Format("20060102150405")
|
|||
|
|
random := GenerateRandomString(6)
|
|||
|
|
return "RF" + timestamp + strings.ToUpper(random)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GenerateWechatOutRefundNo 生成微信退款单号
|
|||
|
|
// 格式:WXR + 时间戳(精确到毫秒) + 8位随机字符串
|
|||
|
|
func GenerateWechatOutRefundNo() string {
|
|||
|
|
now := time.Now()
|
|||
|
|
// 使用纳秒时间戳确保唯一性
|
|||
|
|
timestamp := now.Format("20060102150405") + fmt.Sprintf("%03d", now.Nanosecond()/1000000)
|
|||
|
|
random := GenerateRandomString(8)
|
|||
|
|
return "WXR" + timestamp + strings.ToUpper(random)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// StringToUint 字符串转uint
|
|||
|
|
func StringToUint(str string) uint {
|
|||
|
|
if str == "" {
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
num, err := strconv.ParseUint(str, 10, 32)
|
|||
|
|
if err != nil {
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
return uint(num)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// StringToInt 字符串转int
|
|||
|
|
func StringToInt(str string) int {
|
|||
|
|
if str == "" {
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
num, err := strconv.Atoi(str)
|
|||
|
|
if err != nil {
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
return num
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// StringToFloat64 字符串转float64
|
|||
|
|
func StringToFloat64(str string) float64 {
|
|||
|
|
if str == "" {
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
num, err := strconv.ParseFloat(str, 64)
|
|||
|
|
if err != nil {
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
return num
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UintToString uint转字符串
|
|||
|
|
func UintToString(num uint) string {
|
|||
|
|
return strconv.FormatUint(uint64(num), 10)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IntToString 整数转字符串
|
|||
|
|
func IntToString(num int) string {
|
|||
|
|
return strconv.Itoa(num)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// FloatToString 浮点数转字符串
|
|||
|
|
func FloatToString(num float64) string {
|
|||
|
|
return fmt.Sprintf("%.0f", num)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// BoolToInt bool转int
|
|||
|
|
func BoolToInt(b bool) int {
|
|||
|
|
if b {
|
|||
|
|
return 1
|
|||
|
|
}
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// FormatPrice 格式化价格(分转元)
|
|||
|
|
func FormatPrice(price uint) string {
|
|||
|
|
yuan := float64(price) / 100
|
|||
|
|
return fmt.Sprintf("%.2f", yuan)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ParsePrice 解析价格(元转分)
|
|||
|
|
func ParsePrice(priceStr string) uint {
|
|||
|
|
price, err := strconv.ParseFloat(priceStr, 64)
|
|||
|
|
if err != nil {
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
return uint(math.Round(price * 100))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// InArray 检查元素是否在数组中
|
|||
|
|
func InArray(needle interface{}, haystack []interface{}) bool {
|
|||
|
|
for _, item := range haystack {
|
|||
|
|
if item == needle {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// InStringArray 检查字符串是否在字符串数组中
|
|||
|
|
func InStringArray(needle string, haystack []string) bool {
|
|||
|
|
for _, item := range haystack {
|
|||
|
|
if item == needle {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// InUintArray 检查uint是否在uint数组中
|
|||
|
|
func InUintArray(needle uint, haystack []uint) bool {
|
|||
|
|
for _, item := range haystack {
|
|||
|
|
if item == needle {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// RemoveDuplicateStrings 去除字符串数组重复元素
|
|||
|
|
func RemoveDuplicateStrings(slice []string) []string {
|
|||
|
|
keys := make(map[string]bool)
|
|||
|
|
result := []string{}
|
|||
|
|
for _, item := range slice {
|
|||
|
|
if !keys[item] {
|
|||
|
|
keys[item] = true
|
|||
|
|
result = append(result, item)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return result
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// RemoveDuplicateUints 去除uint数组重复元素
|
|||
|
|
func RemoveDuplicateUints(slice []uint) []uint {
|
|||
|
|
keys := make(map[uint]bool)
|
|||
|
|
result := []uint{}
|
|||
|
|
for _, item := range slice {
|
|||
|
|
if !keys[item] {
|
|||
|
|
keys[item] = true
|
|||
|
|
result = append(result, item)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return result
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetClientIP 获取客户端IP
|
|||
|
|
func GetClientIP(remoteAddr, xForwardedFor, xRealIP string) string {
|
|||
|
|
if xRealIP != "" {
|
|||
|
|
return xRealIP
|
|||
|
|
}
|
|||
|
|
if xForwardedFor != "" {
|
|||
|
|
ips := strings.Split(xForwardedFor, ",")
|
|||
|
|
if len(ips) > 0 {
|
|||
|
|
return strings.TrimSpace(ips[0])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if remoteAddr != "" {
|
|||
|
|
ip := strings.Split(remoteAddr, ":")[0]
|
|||
|
|
return ip
|
|||
|
|
}
|
|||
|
|
return "unknown"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Pagination 分页计算
|
|||
|
|
type Pagination struct {
|
|||
|
|
Page int `json:"page"`
|
|||
|
|
PageSize int `json:"page_size"`
|
|||
|
|
Total int64 `json:"total"`
|
|||
|
|
Offset int `json:"offset"`
|
|||
|
|
Limit int `json:"limit"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewPagination 创建分页对象
|
|||
|
|
func NewPagination(page, pageSize int) *Pagination {
|
|||
|
|
if page <= 0 {
|
|||
|
|
page = 1
|
|||
|
|
}
|
|||
|
|
if pageSize <= 0 {
|
|||
|
|
pageSize = 10
|
|||
|
|
}
|
|||
|
|
if pageSize > 100 {
|
|||
|
|
pageSize = 100
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
offset := (page - 1) * pageSize
|
|||
|
|
return &Pagination{
|
|||
|
|
Page: page,
|
|||
|
|
PageSize: pageSize,
|
|||
|
|
Offset: offset,
|
|||
|
|
Limit: pageSize,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IsValidEmail 验证邮箱格式
|
|||
|
|
func IsValidEmail(email string) bool {
|
|||
|
|
// 简单的邮箱格式验证
|
|||
|
|
return strings.Contains(email, "@") && strings.Contains(email, ".")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IsValidPhone 验证手机号格式
|
|||
|
|
func IsValidPhone(phone string) bool {
|
|||
|
|
// 简单的手机号格式验证(中国大陆)
|
|||
|
|
if len(phone) != 11 {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
return strings.HasPrefix(phone, "1")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TruncateString 截断字符串
|
|||
|
|
func TruncateString(str string, length int) string {
|
|||
|
|
if len(str) <= length {
|
|||
|
|
return str
|
|||
|
|
}
|
|||
|
|
return str[:length] + "..."
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetTimeRange 获取时间范围
|
|||
|
|
func GetTimeRange(rangeType string) (time.Time, time.Time) {
|
|||
|
|
now := time.Now()
|
|||
|
|
var start, end time.Time
|
|||
|
|
|
|||
|
|
switch rangeType {
|
|||
|
|
case "today":
|
|||
|
|
start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
|||
|
|
end = start.Add(24 * time.Hour).Add(-time.Nanosecond)
|
|||
|
|
case "yesterday":
|
|||
|
|
yesterday := now.AddDate(0, 0, -1)
|
|||
|
|
start = time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, yesterday.Location())
|
|||
|
|
end = start.Add(24 * time.Hour).Add(-time.Nanosecond)
|
|||
|
|
case "week":
|
|||
|
|
weekday := int(now.Weekday())
|
|||
|
|
if weekday == 0 {
|
|||
|
|
weekday = 7
|
|||
|
|
}
|
|||
|
|
start = now.AddDate(0, 0, -weekday+1)
|
|||
|
|
start = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, start.Location())
|
|||
|
|
end = start.AddDate(0, 0, 7).Add(-time.Nanosecond)
|
|||
|
|
case "month":
|
|||
|
|
start = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
|||
|
|
end = start.AddDate(0, 1, 0).Add(-time.Nanosecond)
|
|||
|
|
default:
|
|||
|
|
start = now
|
|||
|
|
end = now
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return start, end
|
|||
|
|
}
|