新增了接口

This commit is contained in:
2024-12-30 16:34:26 +08:00
parent e6e78ad658
commit 60850d98da
153 changed files with 10293 additions and 5 deletions
+101
View File
@@ -0,0 +1,101 @@
package utils
import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"io"
)
// Encrypt 加密函数
func Encrypt(text, key string) (string, error) {
plaintext := []byte(text)
hashedKey := hashKey(key)
// 创建一个新的 AES cipher.Block
block, err := aes.NewCipher(hashedKey)
if err != nil {
return "", err
}
// 使用 GCM 模式加密
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
nonce := make([]byte, aesGCM.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return "", err
}
// 加密数据
ciphertext := aesGCM.Seal(nonce, nonce, plaintext, nil)
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// Decrypt 解密函数
func Decrypt(cryptoText, key string) (string, error) {
ciphertext, _ := base64.StdEncoding.DecodeString(cryptoText)
hashedKey := hashKey(key)
// 创建一个新的 AES cipher.Block
block, err := aes.NewCipher(hashedKey)
if err != nil {
return "", err
}
// 使用 GCM 模式解密
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
nonceSize := aesGCM.NonceSize()
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
// 解密数据
plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", err
}
return string(plaintext), nil
}
// HashString 使用SHA256算法对字符串进行哈希加密
func HashString(s string) string {
// 创建一个新的SHA256哈希实例
hasher := sha256.New()
// 写入要哈希的字符串的字节
hasher.Write([]byte(s))
// 获取哈希结果的字节切片
hashBytes := hasher.Sum(nil)
// 将字节切片转换成十六进制字符串表示
s = hex.EncodeToString(hashBytes)
return s
}
// HmacSHA256 计算 HMAC-SHA256 签名
func HmacSHA256(data string, key string) string {
// 创建新的 HMAC 使用 SHA256 哈希函数
h := hmac.New(sha256.New, []byte(key))
// 写入数据
h.Write([]byte(data))
// 计算 HMAC-SHA256 并返回其字节切片
return hex.EncodeToString(h.Sum(nil))
}
// 将任意长度的密钥转换为适合 AES 的长度(32 字节)
func hashKey(key string) []byte {
hash := sha256.Sum256([]byte(key))
return hash[:]
}
+51
View File
@@ -0,0 +1,51 @@
package utils
import (
"github.com/mojocn/base64Captcha"
"image/color"
"time"
)
// GenerateCaptcha 生成验证码-base64
func GenerateCaptcha() (id, b64s string, err error) {
var driver *base64Captcha.DriverString
// 配置验证码的参数
driverString := &base64Captcha.DriverString{
Height: 40,
Width: 100,
NoiseCount: 0,
ShowLineOptions: 0,
Length: 4,
Source: "1234567890",
BgColor: &color.RGBA{R: 3, G: 102, B: 214, A: 125},
Fonts: []string{"wqy-microhei.ttc"},
}
// ConvertFonts 按名称加载字体
driver = driverString.ConvertFonts()
base64Captcha.Expiration = 30 * time.Minute
store := base64Captcha.DefaultMemStore
captcha := base64Captcha.NewCaptcha(driver, store)
id, b64s, _, err = captcha.Generate()
if err != nil {
return "", "", err
}
return id, b64s, nil
}
// VerifyCaptcha 验证验证码
func VerifyCaptcha(id, answer string) bool {
// 创建验证码实例
base64Captcha.Expiration = 30 * time.Minute
store := base64Captcha.DefaultMemStore
captcha := base64Captcha.NewCaptcha(nil, store)
// 验证验证码
isValid := captcha.Verify(id, answer, true)
if !isValid {
return false
}
return true
}
+64
View File
@@ -0,0 +1,64 @@
package utils
import (
"time"
)
// 一些计算
// ComputeIndividualIncomeTax 计算个人所得税
func ComputeIndividualIncomeTax(income float64) float64 {
if income <= 800 {
return 0
}
if income <= 4000 {
income = income - 800
}
// 实际纳税金额
if income > 4000 {
income = income * 0.8
}
// 税率、速算扣除数
var taxRate, quickDeduction float64
if income <= 20000 {
taxRate = 0.2
quickDeduction = 0
} else if income <= 50000 {
taxRate = 0.3
quickDeduction = 2000
} else {
taxRate = 0.4
quickDeduction = 7000
}
incomeTax := income*taxRate - quickDeduction
return incomeTax
}
// CalculateAge 计算年龄
func CalculateAge(birthdate string) (int, error) {
// 解析出生日期字符串
layout := "2006-01-02"
birthTime, err := time.Parse(layout, birthdate)
if err != nil {
return 0, err
}
// 获取当前时间
now := time.Now()
// 计算年龄
years := now.Year() - birthTime.Year()
// 如果今年的生日还没到,年龄减一
if now.Month() < birthTime.Month() || (now.Month() == birthTime.Month() && now.Day() < birthTime.Day()) {
years--
}
return years, nil
}
+21
View File
@@ -0,0 +1,21 @@
package utils
import (
"errors"
"os"
)
// PathExists 文件是否存在
func PathExists(path string) (bool, error) {
fi, err := os.Stat(path)
if err == nil {
if fi.IsDir() {
return true, nil
}
return false, errors.New("存在同名文件")
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
+298
View File
@@ -0,0 +1,298 @@
package utils
import (
"bytes"
"fmt"
"github.com/xuri/excelize/v2"
"reflect"
"strconv"
)
// func Export(widths []int) (bool, error) {
// f := excelize.NewFile()
// defer func() {
// _ = f.Close()
// }()
//
// // 创建一个工作表
// index, err := f.NewSheet("Sheet1")
// if err != nil {
// return false, err
// }
//
// // 设置工作簿的默认工作表
// f.SetActiveSheet(index)
//
// // 单元格对齐样式
// alignment := &excelize.Alignment{
// Horizontal: "center",
// Vertical: "center",
// }
//
// // 单元格颜色填充样式
// fill := excelize.Fill{
// Type: "pattern",
// Pattern: 1,
// Color: []string{"#c9daf8"},
// Shading: 0,
// }
//
// // 第一行的左、右、下边框
// border := []excelize.Border{
// {
// Type: "left,right,bottom",
// Color: "",
// Style: 1,
// },
// }
//
// // 工作表样式
// style, err := f.NewStyle(
// &excelize.Style{
// Fill: fill,
// Alignment: alignment,
// Border: border,
// },
// )
// if err != nil {
// return false, err
// }
//
// // 依次设置每一列的列宽
// widths = []int{18, 18, 18, 18, 18, 20, 23, 46, 18, 30, 30, 18, 18, 30, 18, 30}
// for col, width := range widths {
// // 获取列名
// colName, err := excelize.ColumnNumberToName(col + 1)
// if err != nil {
// return false, err
// }
//
// // 设置列宽
// err = f.SetColWidth("Sheet1", colName, colName, float64(width))
// if err != nil {
// return false, err
// }
//
// // 设置列背景颜色
// err = f.SetCellStyle("Sheet1", colName+"1", colName+"1", style)
// if err != nil {
// return false, err
// }
// }
//
// // 设置行高
// err = f.SetRowStyle("Sheet1", 1, 10, style)
// if err != nil {
// return false, err
// }
//
// if err := f.SaveAs("output.xlsx"); err != nil {
// return false, err
// }
//
// return true, nil
// }
// HeaderCellData 表头内容
type HeaderCellData struct {
Value string // 值
CellType string // 类型
NumberFmt string // 格式化方式
ColWidth int // 列宽
}
func Export(header []HeaderCellData, data []interface{}) (*bytes.Buffer, error) {
sheetName := "Sheet1"
f := excelize.NewFile()
defer func() {
_ = f.Close()
}()
// 创建一个工作表
index, err := f.NewSheet(sheetName)
if err != nil {
return nil, err
}
// 设置工作簿的默认工作表
f.SetActiveSheet(index)
// 设置工作表默认字体
err = f.SetDefaultFont("宋体")
if err != nil {
return nil, err
}
// 统一单元格对齐样式
alignment := &excelize.Alignment{
Horizontal: "center",
Vertical: "center",
}
// 设置行高 35-第一行
err = f.SetRowHeight(sheetName, 1, 35)
if err != nil {
return nil, err
}
// 处理工作表表头
for c, cell := range header {
// 获取列名
colName, err := excelize.ColumnNumberToName(c + 1)
if err != nil {
return nil, err
}
// 添加单元格的值
err = f.SetCellValue(sheetName, colName+"1", cell.Value)
if err != nil {
return nil, err
}
// 单元格颜色填充样式
fill := excelize.Fill{
Type: "pattern",
Pattern: 1,
Color: []string{"#c9daf8"},
Shading: 0,
}
// 第一行的左、右、下边框
border := []excelize.Border{
{
Type: "left",
Color: "#000000",
Style: 1,
},
{
Type: "right",
Color: "#000000",
Style: 1,
},
{
Type: "bottom",
Color: "#000000",
Style: 1,
},
}
// 设置单元格值类型和格式
style, _ := f.NewStyle(&excelize.Style{
Alignment: alignment, // 字体居中
Fill: fill, // 背景颜色
Border: border, // 边框
})
err = f.SetCellStyle(sheetName, colName+"1", colName+"1", style)
if err != nil {
return nil, err
}
// 设置列宽
err = f.SetColWidth(sheetName, colName, colName, float64(cell.ColWidth))
if err != nil {
return nil, err
}
}
// 设置单元格格式
row := len(data)
for i, cell := range header {
// 获取列名
colName, err := excelize.ColumnNumberToName(i + 1)
if err != nil {
return nil, err
}
// 字体居中
style := &excelize.Style{}
style = &excelize.Style{
Alignment: alignment,
}
if cell.CellType == "float" {
style.NumFmt = 2
customNumFmt := "0.000"
style.CustomNumFmt = &customNumFmt
}
if cell.CellType == "date" {
style.NumFmt = 22
customNumFmt := "yyyy-mm-dd hh:mm:ss"
style.CustomNumFmt = &customNumFmt
}
newStyle, _ := f.NewStyle(style)
err = f.SetCellStyle(sheetName, colName+"2", colName+strconv.Itoa(row), newStyle)
if err != nil {
return nil, err
}
}
// 填充数据
for r, rowData := range data {
rv := reflect.ValueOf(rowData)
for c := 0; c < rv.NumField(); c++ {
cellValue := rv.Field(c).Interface()
// 获取列名
colName, err := excelize.ColumnNumberToName(c + 1)
if err != nil {
return nil, err
}
axis := colName + fmt.Sprintf("%d", r+2)
// 设置单元格值
err = f.SetCellValue(sheetName, axis, cellValue)
if err != nil {
return nil, err
}
// 设置单元格值类型
cellType := header[c].CellType
// 字体居中
style := &excelize.Style{}
style = &excelize.Style{
Alignment: alignment,
}
if cellType == "float" {
style.NumFmt = 2
customNumFmt := "0.000"
style.CustomNumFmt = &customNumFmt
}
if cellType == "date" {
style.NumFmt = 22
customNumFmt := "yyyy-mm-dd hh:mm:ss"
style.CustomNumFmt = &customNumFmt
}
newStyle, _ := f.NewStyle(style)
err = f.SetCellStyle(sheetName, axis, axis, newStyle)
if err != nil {
return nil, err
}
// 设置行高 35-第一行
err = f.SetRowHeight(sheetName, r+2, 35)
if err != nil {
return nil, err
}
}
}
buffer, err := f.WriteToBuffer()
if err != nil {
return nil, err
}
return buffer, nil
// 保存文件
// if err := f.SaveAs("output.xlsx"); err != nil {
// return nil, err
// }
// return nil, errors.New("已导出文件")
}
+124
View File
@@ -0,0 +1,124 @@
// Package utils 身份证处理
package utils
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"time"
"unicode/utf8"
)
// GetCardAge 获取身份证年龄
func GetCardAge(cardNum string) (int, error) {
// 获取当前时间
now := time.Now()
// 解析身份证号中的出生日期
birthDateStr := cardNum[6:14]
birthYear, err := strconv.Atoi(birthDateStr[0:4])
if err != nil {
return 0, err
}
birthMonth, err := strconv.Atoi(birthDateStr[4:6])
if err != nil {
return 0, err
}
// 计算年龄
age := now.Year() - birthYear
if now.Month() < time.Month(birthMonth) {
age--
}
return age, nil
}
// CheckCardNum 检测身份证号
func CheckCardNum(cardNum string) (bool, error) {
fmt.Println(cardNum)
regex := `^(?:1[1-5]|2[1-3]|3[1-7]|4[1-6]|5[0-4]|6[1-5])\d{4}(?:1[89]|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dxX]$`
match, err := regexp.MatchString(regex, cardNum)
fmt.Println(match)
if !match || err != nil {
return false, errors.New("身份证号错误")
}
return true, nil
}
// GetCardSex 获取身份证性别
func GetCardSex(cardNum string) (int, error) {
genderStr := cardNum[len(cardNum)-2 : len(cardNum)-1]
genderNum, err := strconv.Atoi(genderStr)
if err != nil {
return 0, err
}
// 判断性别
if genderNum%2 == 0 {
return 2, nil
} else {
return 1, nil
}
}
// GetMaskCardNum 身份证号码脱敏
func GetMaskCardNum(cardNum string) string {
if len(cardNum) != 18 {
return cardNum
}
// 获取身份证号前后部分
frontPart := cardNum[0:6]
backPart := cardNum[14:]
// 替换中间部分数字为 *
middlePart := "****"
// 拼接新的身份证号
maskedIDCard := frontPart + middlePart + backPart
return maskedIDCard
}
// GetMaskCardName 身份证名字脱敏
func GetMaskCardName(cardName string) string {
// 判断姓名长度
length := utf8.RuneCountInString(cardName)
// 判断是否是英文姓名
isEnglish := strings.ContainsAny(cardName, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
if length == 2 {
// 两个字符的姓名
if isEnglish {
// 英文姓名
return string(cardName[0]) + "*"
} else {
// 中文姓名
return string([]rune(cardName)[0]) + "*"
}
} else if length == 3 {
// 三个字符的姓名
if isEnglish {
// 英文姓名
return string(cardName[0]) + "*" + string(cardName[2])
} else {
// 中文姓名
return string([]rune(cardName)[0]) + "*" + string([]rune(cardName)[2])
}
} else if length >= 4 {
// 四个及以上字符的姓名
if isEnglish {
// 英文姓名
return string(cardName[0]) + "*" + string(cardName[length-1])
} else {
// 中文姓名
return string([]rune(cardName)[0]) + "*" + string([]rune(cardName)[length-1])
}
}
return cardName
}
+97
View File
@@ -0,0 +1,97 @@
package utils
// OrderCancelReasonToString 订单取消原因(1:主动取消 2:后台取消 3:支付超时取消)
func OrderCancelReasonToString(i int) string {
switch i {
case 1:
return "主动取消"
case 2:
return "后台取消"
case 3:
return "支付超时取消"
default:
return "取消"
}
}
// OrderSingleStatusToString 订单状态(1:待支付 2:已完成 3:已取消)
func OrderSingleStatusToString(i int) string {
switch i {
case 1:
return "待支付"
case 2:
return "已完成"
case 3:
return "已取消"
default:
return "未知"
}
}
// OrderSingleRefundStatusToString 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款)
func OrderSingleRefundStatusToString(i int) string {
switch i {
case 0:
return "无退款"
case 1:
return "申请退款"
case 2:
return "退款中"
case 3:
return "退款成功"
case 4:
return "拒绝退款"
case 5:
return "退款关闭"
case 6:
return "退款异常"
case 7:
return "部分退款"
default:
return "未知"
}
}
// OrderSinglePayStatusToString 订单支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款)
func OrderSinglePayStatusToString(i int) string {
switch i {
case 1:
return "未支付"
case 2:
return "已支付"
case 3:
return "支付中"
case 4:
return "支付失败"
case 5:
return "支付超时"
case 6:
return "支付关闭"
case 7:
return "已撤销"
case 8:
return "转入退款"
default:
return "未知"
}
}
// DoctorTitleToString 医生职称(1:主任医师 2:主任中医师 3:副主任医师 4:副主任中医师 5:主治医师 6:住院医师)
func DoctorTitleToString(i int) string {
switch i {
case 1:
return "主任医师"
case 2:
return "主任中医师"
case 3:
return "副主任医师"
case 4:
return "副主任中医师"
case 5:
return "主治医师"
case 6:
return "住院医师"
default:
return ""
}
}
+41
View File
@@ -0,0 +1,41 @@
package utils
import (
"case-open-api/config"
"github.com/golang-jwt/jwt/v5"
"time"
)
type Token struct {
UserId string `json:"user_id"` // 用户id
PlatformId string `json:"platform_id"` // 平台id
jwt.RegisteredClaims // v5版本新加的方法
}
// NewJWT GenerateJWT 生成JWT
func (t Token) NewJWT() (string, error) {
ttl := time.Duration(config.C.Jwt.Ttl)
t.RegisteredClaims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(ttl * time.Hour)) // 过期时间24小时
t.RegisteredClaims.IssuedAt = jwt.NewNumericDate(time.Now()) // 签发时间
t.RegisteredClaims.NotBefore = jwt.NewNumericDate(time.Now()) // 生效时间
// 使用HS256签名算法
token := jwt.NewWithClaims(jwt.SigningMethodHS256, t)
s, err := token.SignedString([]byte(config.C.Jwt.SignKey))
return s, err
}
// ParseJwt 解析JWT
func ParseJwt(authorization string) (*Token, error) {
t, err := jwt.ParseWithClaims(authorization, &Token{}, func(token *jwt.Token) (interface{}, error) {
return []byte(config.C.Jwt.SignKey), nil
})
if claims, ok := t.Claims.(*Token); ok && t.Valid {
return claims, nil
} else {
return nil, err
}
}
+28
View File
@@ -0,0 +1,28 @@
package utils
import (
"case-open-api/global"
"encoding/json"
"fmt"
"github.com/sirupsen/logrus"
)
func LogJsonInfo(msg string, v interface{}) {
jsonData, err := json.Marshal(v)
if err != nil {
fmt.Println("Error marshaling struct to JSON:", err)
return
}
jsonString := string(jsonData)
global.Logger.WithFields(logrus.Fields{
"data": jsonString,
}).Info(msg)
}
func LogJsonErr(msg string, v interface{}) {
global.Logger.WithFields(logrus.Fields{
"data": v,
}).Errorf(msg)
}
+90
View File
@@ -0,0 +1,90 @@
package utils
import (
"regexp"
"unicode/utf8"
)
// MaskNameStr 用户名掩码
// maskType 加密类型(1:三个字符或三个字符以上掐头取尾,中间用*代替 首字母保留,后两位用*代替)
func MaskNameStr(str string, maskType int) string {
if str == "" {
return str
}
// 使用正则表达式判断是否包含中文字符
chinesePattern := "[\u4e00-\u9fa5]+"
isChinese, _ := regexp.MatchString(chinesePattern, str)
// 使用正则表达式判断是否包含英文字母
englishPattern := "[A-Za-z]+"
isEnglish, _ := regexp.MatchString(englishPattern, str)
// 判断是否包含中文字符
if isChinese {
// 按照中文字符计算长度
strLen := utf8.RuneCountInString(str)
if strLen >= 3 {
if maskType == 1 {
// 三个字符或三个字符以上掐头取尾,中间用*代替
firstChar, _ := utf8.DecodeRuneInString(str)
lastChar, _ := utf8.DecodeLastRuneInString(str)
str = string(firstChar) + "*" + string(lastChar)
} else {
// 首字母保留,后两位用*代替
firstChar, _ := utf8.DecodeRuneInString(str)
str = string(firstChar) + "**"
}
} else if strLen == 2 {
// 两个字符
firstChar, _ := utf8.DecodeRuneInString(str)
str = string(firstChar) + "*"
}
} else if isEnglish {
// 按照英文字串计算长度
strLen := utf8.RuneCountInString(str)
if strLen >= 3 {
if maskType == 1 {
// 三个字符或三个字符以上掐头取尾,中间用*代替
firstChar, _ := utf8.DecodeRuneInString(str)
lastChar, _ := utf8.DecodeLastRuneInString(str)
str = string(firstChar) + "*" + string(lastChar)
} else {
// 首字母保留,后两位用*代替
firstChar, _ := utf8.DecodeRuneInString(str)
str = string(firstChar) + "**"
}
} else if strLen == 2 {
// 两个字符
firstChar, _ := utf8.DecodeRuneInString(str)
str = string(firstChar) + "*"
}
}
return str
}
// MaskPhoneStr 手机号、固话加密
// 固话:0510-89754815 0510-8****815
// 手机号:18221234158 18*******58
func MaskPhoneStr(phone string) string {
if phone == "" {
return phone
}
// 使用正则表达式匹配固定电话
phonePattern := `(0[0-9]{2,3}[\-]?[2-9][0-9]{6,7}[\-]?[0-9]?)`
isFixedLine := regexp.MustCompile(phonePattern).MatchString(phone)
if isFixedLine {
// 匹配到固定电话
// 替换匹配的部分为固定格式
return regexp.MustCompile(`(0[0-9]{2,3}[\-]?[2-9])[0-9]{3,4}([0-9]{3}[\-]?[0-9]?)`).ReplaceAllString(phone, "$1****$2")
} else {
// 匹配到手机号
// 替换匹配的部分为固定格式
return regexp.MustCompile(`(1[0-9]{1})[0-9]{7}([0-9]{2})`).ReplaceAllString(phone, "$1*******$2")
}
}
+40
View File
@@ -0,0 +1,40 @@
package utils
import "sort"
// SortMapParams 对map的key进行排序,包括多层嵌套的情况
func SortMapParams(data map[string]interface{}) map[string]interface{} {
sortedMap := make(map[string]interface{})
keys := make([]string, 0, len(data))
// 收集所有的key
for key := range data {
keys = append(keys, key)
}
// 对key进行排序
sort.Strings(keys)
// 通过排序后的key插入新map中
for _, key := range keys {
value := data[key]
switch valueTyped := value.(type) {
case map[string]interface{}:
// 如果是嵌套的map,递归调用
sortedMap[key] = SortMapParams(valueTyped)
case []interface{}:
// 如果是嵌套的slice,对其中的map进行递归调用
for i, v := range valueTyped {
if vMap, ok := v.(map[string]interface{}); ok {
valueTyped[i] = SortMapParams(vMap)
}
}
sortedMap[key] = valueTyped
default:
// 否则直接插入
sortedMap[key] = value
}
}
return sortedMap
}
+12
View File
@@ -0,0 +1,12 @@
package utils
import "regexp"
// RegexpMobile 手机号匹配
func RegexpMobile(mobile string) bool {
ok, err := regexp.MatchString(`^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$`, mobile)
if err != nil {
return false
}
return ok
}
+36
View File
@@ -0,0 +1,36 @@
package utils
import (
"case-open-api/config"
"math/rand"
"strings"
"time"
)
// RemoveOssDomain 去除oss地址中的前缀
func RemoveOssDomain(url string) string {
if url != "" {
url = strings.Replace(url, config.C.Oss.OssCustomDomainName, "", 1)
}
return url
}
// AddOssDomain 增加oss地址中的前缀
func AddOssDomain(url string) string {
if url == "" {
return ""
}
return config.C.Oss.OssCustomDomainName + url
}
// GenerateRandomString 生成随机字母字符串
func GenerateRandomString(n int) string {
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
r := rand.New(rand.NewSource(time.Now().UnixNano())) // 设置随机数种子
b := make([]byte, n) // 创建长度为 n 的字节切片
for i := range b {
b[i] = letters[r.Intn(len(letters))] // 从字母列表中随机选择字符
}
return string(b) // 转换为字符串返回
}
+57
View File
@@ -0,0 +1,57 @@
package utils
import "C"
import (
"case-open-api/config"
"encoding/json"
"errors"
"fmt"
"strconv"
"time"
)
// GenSignature 生成签名
func GenSignature(paramsMap map[string]interface{}, timestamp string, secret string) (string, error) {
// 对map的key进行排序,包括多层嵌套的情况
paramsData := SortMapParams(paramsMap)
paramsData["timestamp"] = timestamp
// 转换为JSON
jsonData, err := json.Marshal(paramsData)
if err != nil {
return "", err
}
sign := HmacSHA256(string(jsonData), secret)
return sign, err
}
// VerifySignature 验证签名
func VerifySignature(paramsMap map[string]interface{}, receivedTimestamp, receivedSignature, secret string) error {
// 验证时间戳是否超出 5 分钟-测试环境不验证
if config.C.Env != "dev" {
currentTime := time.Now().Unix() * 1000
timestamp, err := strconv.ParseInt(receivedTimestamp, 10, 64)
if err != nil {
return errors.New("签名错误")
}
if currentTime-timestamp > 300 {
return errors.New("签名超时")
}
}
// 生成签名
sign, err := GenSignature(paramsMap, receivedTimestamp, secret)
fmt.Println(sign)
if err != nil {
return errors.New("内部错误")
}
if receivedSignature != sign {
return errors.New("签名错误")
}
return nil
}
+21
View File
@@ -0,0 +1,21 @@
package utils
// DoctorTitleToInt 医生职称(0:未知 1:主任医师 2:主任中医师 3:副主任医师 4:副主任中医师 5:主治医师 6:住院医师)
func DoctorTitleToInt(s string) int {
switch s {
case "主任医师":
return 1
case "主任中医师":
return 2
case "副主任医师":
return 3
case "副主任中医师":
return 4
case "主治医师":
return 5
case "住院医师":
return 6
default:
return 0
}
}
+28
View File
@@ -0,0 +1,28 @@
package utils
import (
"strconv"
)
// StrToFloat32 字符串转float32
func StrToFloat32(s string) (f *float32, err error) {
f64, err := strconv.ParseFloat(s, 32)
if err != nil {
return nil, err
}
// 将float64转换为float32
f32 := float32(f64)
return &f32, nil
}
// StrToFloat64 字符串转float64
func StrToFloat64(s string) (f *float64, err error) {
f64, err := strconv.ParseFloat(s, 64)
if err != nil {
return nil, err
}
return &f64, nil
}
+50
View File
@@ -0,0 +1,50 @@
package utils
import (
"case-open-api/api/model"
"fmt"
"time"
)
// StrToLocalTime 字符串时间转localtime
func StrToLocalTime(s string) (*model.LocalTime, error) {
// 获取本地时区
location, err := time.LoadLocation("Local")
if err != nil {
return nil, err
}
timeParse, err := time.ParseInLocation("2006-01-02 15:04:05", s, location)
if err != nil {
return nil, err
}
t := model.LocalTime(timeParse)
return &t, nil
}
// TimeToAgoString 根据给定的时间返回一个描述性的字符串
func TimeToAgoString(t time.Time) string {
now := time.Now()
diffTime := now.Sub(t)
switch {
case diffTime < time.Minute:
return "1分钟前"
case diffTime < time.Hour:
minutes := int(diffTime.Minutes())
return fmt.Sprintf("%d分钟前", minutes)
case diffTime < 24*time.Hour:
return "1小时前"
case diffTime < 48*time.Hour:
return "2天前"
case diffTime < 72*time.Hour:
return "3天前"
case diffTime < 10*time.Hour*24:
days := int(diffTime.Hours()) / 24
return fmt.Sprintf("%d天前", days)
default:
return "10天前"
}
}
+17
View File
@@ -0,0 +1,17 @@
package utils
import (
"case-open-api/global"
"errors"
"github.com/go-playground/validator/v10"
)
// Translate 检验并返回检验错误信息
func Translate(err error) (errMsg string) {
var errs validator.ValidationErrors
errors.As(err, &errs)
for _, err := range errs {
errMsg = err.Translate(global.Trans)
}
return
}