邹方成 a7a0f639e1 feat: 新增取消发货功能并优化任务中心
fix: 修复微信通知字段截断导致的编码错误
feat: 添加有效邀请相关字段和任务中心常量
refactor: 重构一番赏奖品格位逻辑
perf: 优化道具卡列表聚合显示
docs: 更新项目说明文档和API文档
test: 添加字符串截断工具测试
2025-12-23 22:26:07 +08:00

257 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package utils
import (
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/hex"
"fmt"
"net"
"net/http"
"strings"
"time"
"github.com/tealeg/xlsx"
"golang.org/x/crypto/bcrypt"
)
// GenerateAdminHashedPassword [管理端]生成密码
func GenerateAdminHashedPassword(password string) (string, error) {
salt := "7M&7p7euU=CC"
passwordWithSalt := password + salt
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(passwordWithSalt), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashedPassword), nil
}
// VerifyAdminHashedPassword [管理端]验证密码
func VerifyAdminHashedPassword(hashedPassword, password string) bool {
salt := "7M&7p7euU=CC"
passwordWithSalt := password + salt
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(passwordWithSalt))
return err == nil
}
// ToExcel 生成 Excel
func ToExcel(titleList []string, dataList []interface{}) []byte {
// 生成一个新的文件
file := xlsx.NewFile()
// 添加sheet页
sheet, _ := file.AddSheet("Sheet1")
// 插入表头
titleRow := sheet.AddRow()
for _, v := range titleList {
cell := titleRow.AddCell()
cell.Value = v
}
// 插入内容
for _, v := range dataList {
row := sheet.AddRow()
row.WriteStruct(v, -1)
}
var buffer bytes.Buffer
_ = file.Write(&buffer)
return buffer.Bytes()
}
func MD5(text string) string {
hash := md5.New()
hash.Write([]byte(text))
hashBytes := hash.Sum(nil)
return hex.EncodeToString(hashBytes)
}
// GetIP 尝试从 HTTP 请求中获取真实的客户端 IP 地址
func GetIP(r *http.Request) string {
// 从 X-Forwarded-For 头部获取IP地址
xForwardedFor := r.Header.Get("X-Forwarded-For")
if xForwardedFor != "" {
// 可能有多个IP地址通常第一个是客户端的真实IP
ips := strings.Split(xForwardedFor, ",")
if len(ips) > 0 {
return strings.TrimSpace(ips[0])
}
}
// 如果没有 X-Forwarded-For 头部,尝试从 X-Real-IP 获取
xRealIP := r.Header.Get("X-Real-IP")
if xRealIP != "" {
return strings.TrimSpace(xRealIP)
}
// 如果没有代理头部,最后退回到 RemoteAddr
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
// 如果处理RemoteAddr出错返回空字符串
return ""
}
return ip
}
// FriendlyTimeAgo 友好时间
func FriendlyTimeAgo(date time.Time) string {
now := time.Now()
if date.After(now) {
return "未来的日期"
}
diff := now.Sub(date)
minutes := int(diff.Minutes())
hours := int(diff.Hours())
days := int(hours / 24)
// 小于1分钟
if minutes < 1 {
return "刚刚"
}
// 小于1小时
if hours < 1 {
return fmt.Sprintf("%d分钟", minutes)
}
// 小于1天
if hours < 24 {
return fmt.Sprintf("%d小时", hours)
}
// 小于1周
if days < 7 {
return fmt.Sprintf("%d天", days)
}
// 小于1个月
if days < 30 {
return fmt.Sprintf("%d周", days/7)
}
// 小于1年
if days < 365 {
months := int(days / 30)
if months < 1 {
return fmt.Sprintf("%d天", days)
}
return fmt.Sprintf("%d个月", months)
}
// 大于等于1年
years := days / 365
remainingDays := days % 365
months := remainingDays / 30
if months == 0 {
return fmt.Sprintf("%d年", years)
}
return fmt.Sprintf("%d年%d个月", years, months)
}
// FriendlySubTime 时间相减
func FriendlySubTime(birthday, date time.Time) string {
diff := date.Sub(birthday)
minutes := int(diff.Minutes())
hours := int(diff.Hours())
days := int(hours / 24)
// 小于1分钟
if minutes < 1 {
return "刚刚"
}
// 小于1小时
if hours < 1 {
return fmt.Sprintf("%d分钟", minutes)
}
// 小于1天
if hours < 24 {
return fmt.Sprintf("%d小时", hours)
}
// 小于1周
if days < 7 {
return fmt.Sprintf("%d天", days)
}
// 小于1个月
if days < 30 {
return fmt.Sprintf("%d周", days/7)
}
// 小于1年
if days < 365 {
months := int(days / 30)
if months < 1 {
return fmt.Sprintf("%d天", days)
}
return fmt.Sprintf("%d个月", months)
}
// 大于等于1年
years := days / 365
remainingDays := days % 365
months := remainingDays / 30
if months == 0 {
return fmt.Sprintf("%d岁", years)
}
return fmt.Sprintf("%d岁%d个月", years, months)
}
func XorEncrypt(phone, key string) string {
result := make([]byte, len(phone))
for i := 0; i < len(phone); i++ {
result[i] = phone[i] ^ key[i%len(key)]
}
return base64.StdEncoding.EncodeToString(result)
}
func XorDecrypt(encrypted, key string) (string, error) {
data, err := base64.StdEncoding.DecodeString(encrypted)
if err != nil {
return "", err
}
result := make([]byte, len(data))
for i := 0; i < len(data); i++ {
result[i] = data[i] ^ key[i%len(key)]
}
return string(result), nil
}
// TruncateRunes truncates a string to the specified rune limit
func TruncateRunes(s string, limit int) string {
runes := []rune(s)
if len(runes) > limit {
return string(runes[:limit])
}
return s
}
// TruncateBytes truncates a string to the specified byte limit while ensuring valid UTF-8
func TruncateBytes(s string, limit int) string {
if len(s) <= limit {
return s
}
var b int
for i, r := range s {
rl := len(string(r))
if b+rl > limit {
return s[:i]
}
b += rl
}
return s
}