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
|
||
} |