This commit is contained in:
huangjin
2026-06-29 17:34:59 +08:00
commit fe3ad20fe2
271 changed files with 51767 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
// 从 gin.Context 提取中间件注入的数据 + 分页工具
package utils
import (
"strconv"
"github.com/gin-gonic/gin"
)
// GetUserID 从 Context 获取用户 ID支持 uint/float64/string 类型转换)
func GetUserID(c *gin.Context) uint {
userID, exists := c.Get("user_id")
if !exists {
return 0
}
switch v := userID.(type) {
case uint:
return v
case float64:
return uint(v)
case string:
id, _ := strconv.ParseUint(v, 10, 64)
return uint(id)
default:
return 0
}
}
// GetRoleCode 获取主角色编码
func GetRoleCode(c *gin.Context) string {
roleCode, exists := c.Get("role_code")
if !exists {
return ""
}
return roleCode.(string)
}
// HasRole 检查当前用户是否为指定角色
func HasRole(c *gin.Context, roleCode string) bool {
return GetRoleCode(c) == roleCode
}
// ParsePagination 解析分页参数,默认 page=1, pageSize=10, 最大 1000
func ParsePagination(c *gin.Context) (page, pageSize int) {
pageStr := c.DefaultQuery("page", "1")
pageSizeStr := c.DefaultQuery("page_size", "10")
page, _ = strconv.Atoi(pageStr)
pageSize, _ = strconv.Atoi(pageSizeStr)
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 1000 {
pageSize = 10
}
return
}
// GetOffset 计算 SQL offset
func GetOffset(page, pageSize int) int {
return (page - 1) * pageSize
}

View File

@@ -0,0 +1,101 @@
// JWT Token 生成与解析 + 业务工具函数
package utils
import (
"crypto/rand"
"encoding/hex"
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
// JWTClaims JWT 载荷,包含用户 ID 和角色列表
type JWTClaims struct {
UserID uint `json:"user_id"`
RoleCode string `json:"role_code"` // 角色(visitor/employee/guard)
jwt.RegisteredClaims
}
// AdminJWTClaims 管理员 JWT 载荷
type AdminJWTClaims struct {
AdminID uint `json:"admin_id"`
Account string `json:"account"`
jwt.RegisteredClaims
}
// GenerateAdminToken 生成管理员 JWT
func GenerateAdminToken(adminID uint, account string, secret string, expireHours int) (string, error) {
claims := AdminJWTClaims{
AdminID: adminID,
Account: account,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expireHours) * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "lktx-admin",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}
// ParseAdminToken 解析管理员 JWT
func ParseAdminToken(tokenString string, secret string) (*AdminJWTClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &AdminJWTClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(secret), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*AdminJWTClaims); ok && token.Valid {
return claims, nil
}
return nil, fmt.Errorf("invalid token")
}
// GenerateToken 生成 JWT单角色兼容旧版
func GenerateToken(userID uint, roleCode string, secret string, expireHours int) (string, error) {
claims := JWTClaims{
UserID: userID,
RoleCode: roleCode,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expireHours) * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "lktx-mp",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}
// ParseToken 解析并验证 JWT
func ParseToken(tokenString string, secret string) (*JWTClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(secret), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
return claims, nil
}
return nil, fmt.Errorf("invalid token")
}
// GenerateInviteCode 生成 16 位随机邀请码
func GenerateInviteCode() string {
b := make([]byte, 8)
rand.Read(b)
return hex.EncodeToString(b)
}
// GenerateQRCodeContent 生成二维码内容
func GenerateQRCodeContent(appointmentID uint) string {
return fmt.Sprintf("LKTX:APPT:%d:%d", appointmentID, time.Now().Unix())
}

View File

@@ -0,0 +1,22 @@
// 数据校验工具
package utils
import "regexp"
// ValidatePhone 验证中国大陆手机号1 开头 + 3-9 + 9 位数字)
func ValidatePhone(phone string) bool {
matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, phone)
return matched
}
// ValidateIDNumber 验证 18 位身份证号格式
func ValidateIDNumber(idNumber string) bool {
matched, _ := regexp.MatchString(`^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[\dXx]$`, idNumber)
return matched
}
// ValidateLicensePlate 验证中国大陆车牌号
func ValidateLicensePlate(plate string) bool {
matched, _ := regexp.MatchString(`^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤川青藏琼宁][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$`, plate)
return matched
}