This commit is contained in:
2024-11-03 15:28:10 +08:00
commit cca66faed5
113 changed files with 4349 additions and 0 deletions

22
common/ctxdata/ctxData.go Normal file
View File

@@ -0,0 +1,22 @@
package ctxdata
import (
"context"
"encoding/json"
"github.com/zeromicro/go-zero/core/logx"
)
// CtxKeyJwtUserId get uid from ctx
var CtxKeyJwtUserId = "jwtUserId"
func GetUidFromCtx(ctx context.Context) int64 {
var uid int64
if jsonUid, ok := ctx.Value(CtxKeyJwtUserId).(json.Number); ok {
if int64Uid, err := jsonUid.Int64(); err == nil {
uid = int64Uid
} else {
logx.WithContext(ctx).Errorf("GetUidFromCtx err : %+v", err)
}
}
return uid
}

View File

@@ -0,0 +1,14 @@
package globalkey
/**
global constant key
*/
//软删除
var DelStateNo int64 = 0 //未删除
var DelStateYes int64 = 1 //已删除
//时间格式化模版
var DateTimeFormatTplStandardDateTime = "Y-m-d H:i:s"
var DateTimeFormatTplStandardDate = "Y-m-d"
var DateTimeFormatTplStandardTime = "H:i:s"

View File

@@ -0,0 +1,9 @@
package globalkey
/**
redis key except "model cache key" in here,
but "model cache key" in model
*/
// CacheUserTokenKey /** 用户登陆的token
const CacheUserTokenKey = "user_token:%d"

View File

@@ -0,0 +1,39 @@
package rpcserver
import (
"context"
"qnc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
/**
* @Description rpc service logger interceptor
* @Author Mikael
* @Date 2021/1/9 13:35
* @Version 1.0
**/
func LoggerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
resp, err = handler(ctx, req)
if err != nil {
causeErr := errors.Cause(err) // err类型
if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型
logx.WithContext(ctx).Errorf("【RPC-SRV-ERR】 %+v", err)
//转成grpc err
err = status.Error(codes.Code(e.GetErrCode()), e.GetErrMsg())
} else {
logx.WithContext(ctx).Errorf("【RPC-SRV-ERR】 %+v", err)
}
}
return resp, err
}

68
common/jwt/jwtx.go Normal file
View File

@@ -0,0 +1,68 @@
package jwtx
import (
"errors"
"github.com/golang-jwt/jwt/v4"
"strconv"
"time"
)
// Token 生成逻辑的函数,接收 userId、过期时间和密钥返回生成的 token
func GenerateJwtToken(userId int64, secret string, expireTime int64) (string, error) {
// 获取当前时间戳
now := time.Now().Unix()
// 定义 JWT Claims
claims := jwt.MapClaims{
"exp": now + expireTime, // token 过期时间
"iat": now, // 签发时间
"userId": userId, // 用户ID
}
// 创建新的 JWT token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 使用密钥对 token 签名
signedToken, err := token.SignedString([]byte(secret))
if err != nil {
return "", err
}
return signedToken, nil
}
func ParseJwtToken(tokenStr string, secret string) (int64, error) {
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
if err != nil || !token.Valid {
return 0, errors.New("invalid JWT")
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok || !token.Valid {
return 0, errors.New("invalid JWT claims")
}
// 从 claims 中提取 userId
userIdRaw, ok := claims["userId"]
if !ok {
return 0, errors.New("userId not found in JWT")
}
// 处理不同类型的 userId确保它被转换为 int64
switch userId := userIdRaw.(type) {
case float64:
return int64(userId), nil
case int64:
return userId, nil
case string:
// 如果 userId 是字符串,可以尝试将其转换为 int64
parsedId, err := strconv.ParseInt(userId, 10, 64)
if err != nil {
return 0, errors.New("invalid userId in JWT")
}
return parsedId, nil
default:
return 0, errors.New("unsupported userId type in JWT")
}
}

8
common/kqueue/message.go Normal file
View File

@@ -0,0 +1,8 @@
//KqMessage
package kqueue
//第三方支付回调更改支付状态通知
type ThirdPaymentUpdatePayStatusNotifyMessage struct {
PayStatus int64 `json:"payStatus"`
OrderSn string `json:"orderSn"`
}

View File

@@ -0,0 +1,31 @@
package middleware
import (
"github.com/zeromicro/go-zero/rest/handler"
"net/http"
)
// CommonJwtAuthMiddleware : with jwt on the verification, no jwt on the verification
type CommonJwtAuthMiddleware struct {
secret string
}
func NewCommonJwtAuthMiddleware(secret string) *CommonJwtAuthMiddleware {
return &CommonJwtAuthMiddleware{
secret: secret,
}
}
func (m *CommonJwtAuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if len(r.Header.Get("Authorization")) > 0 {
//has jwt Authorization
authHandler := handler.Authorize(m.secret)
authHandler(next).ServeHTTP(w, r)
return
} else {
//no jwt Authorization
next(w, r)
}
}
}

View File

@@ -0,0 +1,89 @@
package result
import (
"fmt"
"net/http"
"qnc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpx"
"google.golang.org/grpc/status"
)
// http返回
func HttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
if err == nil {
httpx.WriteJson(w, http.StatusOK, Success(resp))
} else {
//错误返回
errcode := xerr.SERVER_COMMON_ERROR
errmsg := "服务器开小差啦,稍后再来试一试"
causeErr := errors.Cause(err) // err类型
if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型
//自定义CodeError
errcode = e.GetErrCode()
errmsg = e.GetErrMsg()
} else {
if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
grpcCode := uint32(gstatus.Code())
if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误底层、db错误不能返回给前端
errcode = grpcCode
errmsg = gstatus.Message()
}
}
}
logx.WithContext(r.Context()).Errorf("【API-ERR】 : %+v ", err)
httpx.WriteJson(w, http.StatusBadRequest, Error(errcode, errmsg))
}
}
// 授权的http方法
func AuthHttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
if err == nil {
//成功返回
r := Success(resp)
httpx.WriteJson(w, http.StatusOK, r)
} else {
//错误返回
errcode := xerr.SERVER_COMMON_ERROR
errmsg := "服务器开小差啦,稍后再来试一试"
causeErr := errors.Cause(err) // err类型
if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型
//自定义CodeError
errcode = e.GetErrCode()
errmsg = e.GetErrMsg()
} else {
if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
grpcCode := uint32(gstatus.Code())
if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误底层、db错误不能返回给前端
errcode = grpcCode
errmsg = gstatus.Message()
}
}
}
logx.WithContext(r.Context()).Errorf("【GATEWAY-ERR】 : %+v ", err)
httpx.WriteJson(w, http.StatusUnauthorized, Error(errcode, errmsg))
}
}
// http 参数错误返回
func ParamErrorResult(r *http.Request, w http.ResponseWriter, err error) {
errMsg := fmt.Sprintf("%s,%s", xerr.MapErrMsg(xerr.REUQEST_PARAM_ERROR), err.Error())
httpx.WriteJson(w, http.StatusBadRequest, Error(xerr.REUQEST_PARAM_ERROR, errMsg))
}
// http 参数校验失败返回
func ParamValidateErrorResult(r *http.Request, w http.ResponseWriter, err error) {
//errMsg := fmt.Sprintf("%s,%s", xerr.MapErrMsg(xerr.REUQEST_PARAM_ERROR), err.Error())
httpx.WriteJson(w, http.StatusOK, Error(xerr.PARAM_VERIFICATION_ERROR, err.Error()))
}

View File

@@ -0,0 +1,44 @@
package result
import (
"context"
"qnc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc/status"
)
// job返回
func JobResult(ctx context.Context, resp interface{}, err error) {
if err == nil {
// 成功返回 ,只有dev环境下才会打印info线上不显示
if resp != nil {
logx.Infof("resp: %+v", resp)
}
return
} else {
errCode := xerr.SERVER_COMMON_ERROR
errMsg := "服务器开小差啦,稍后再来试一试"
// 错误返回
causeErr := errors.Cause(err) // err类型
if e, ok := causeErr.(*xerr.CodeError); ok { // 自定义错误类型
// 自定义CodeError
errCode = e.GetErrCode()
errMsg = e.GetErrMsg()
} else {
if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误
grpcCode := uint32(gstatus.Code())
if xerr.IsCodeErr(grpcCode) { // 区分自定义错误跟系统底层、db等错误底层、db错误不能返回给前端
errCode = grpcCode
errMsg = gstatus.Message()
}
}
}
logx.WithContext(ctx).Errorf("【JOB-ERR】 : %+v ,errCode:%d , errMsg:%s ", err, errCode, errMsg)
return
}
}

View File

@@ -0,0 +1,21 @@
package result
type ResponseSuccessBean struct {
Code uint32 `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
type NullJson struct{}
func Success(data interface{}) *ResponseSuccessBean {
return &ResponseSuccessBean{200, "OK", data}
}
type ResponseErrorBean struct {
Code uint32 `json:"code"`
Msg string `json:"msg"`
}
func Error(errCode uint32, errMsg string) *ResponseErrorBean {
return &ResponseErrorBean{errCode, errMsg}
}

View File

@@ -0,0 +1,19 @@
package tool
import "github.com/shopspring/decimal"
var oneHundredDecimal decimal.Decimal = decimal.NewFromInt(100)
//分转元
func Fen2Yuan(fen int64) float64 {
y, _ := decimal.NewFromInt(fen).Div(oneHundredDecimal).Truncate(2).Float64()
return y
}
//元转分
func Yuan2Fen(yuan float64) int64 {
f, _ := decimal.NewFromFloat(yuan).Mul(oneHundredDecimal).Truncate(0).Float64()
return int64(f)
}

23
common/tool/encryption.go Normal file
View File

@@ -0,0 +1,23 @@
package tool
import (
"crypto/md5"
"fmt"
"io"
)
/** 加密方式 **/
func Md5ByString(str string) string {
m := md5.New()
_, err := io.WriteString(m, str)
if err != nil {
panic(err)
}
arr := m.Sum(nil)
return fmt.Sprintf("%x", arr)
}
func Md5ByBytes(b []byte) string {
return fmt.Sprintf("%x", md5.Sum(b))
}

28
common/tool/krand.go Normal file
View File

@@ -0,0 +1,28 @@
package tool
import (
"math/rand"
"time"
)
const (
KC_RAND_KIND_NUM = 0 // 纯数字
KC_RAND_KIND_LOWER = 1 // 小写字母
KC_RAND_KIND_UPPER = 2 // 大写字母
KC_RAND_KIND_ALL = 3 // 数字、大小写字母
)
// 随机字符串
func Krand(size int, kind int) string {
ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size)
is_all := kind > 2 || kind < 0
rand.Seed(time.Now().UnixNano())
for i := 0; i < size; i++ {
if is_all { // random ikind
ikind = rand.Intn(3)
}
scope, base := kinds[ikind][0], kinds[ikind][1]
result[i] = uint8(base + rand.Intn(scope))
}
return string(result)
}

View File

@@ -0,0 +1,8 @@
package tool
import "testing"
func TestMd5ByString(t *testing.T) {
s := Md5ByString("AAA")
t.Log(s)
}

View File

@@ -0,0 +1,15 @@
package tool
import "strings"
//替换
func InPlaceholders(n int) string {
var b strings.Builder
for i := 0; i < n-1; i++ {
b.WriteString("?,")
}
if n > 0 {
b.WriteString("?")
}
return b.String()
}

20
common/uniqueid/sn.go Normal file
View File

@@ -0,0 +1,20 @@
package uniqueid
import (
"fmt"
"qnc-server/common/tool"
"time"
)
// 生成sn单号
type SnPrefix string
const (
SN_PREFIX_HOMESTAY_ORDER SnPrefix = "HSO" //民宿订单前缀 qnc-server_order/homestay_order
SN_PREFIX_THIRD_PAYMENT SnPrefix = "PMT" //第三方支付流水记录前缀 qnc-server_payment/third_payment
)
// 生成单号
func GenSn(snPrefix SnPrefix) string {
return fmt.Sprintf("%s%s%s", snPrefix, time.Now().Format("20060102150405"), tool.Krand(8, tool.KC_RAND_KIND_NUM))
}

View File

@@ -0,0 +1,7 @@
package uniqueid
import "testing"
func TestGenSn(t *testing.T) {
GenSn(SN_PREFIX_HOMESTAY_ORDER)
}

View File

@@ -0,0 +1,23 @@
package uniqueid
import (
"github.com/sony/sonyflake"
"github.com/zeromicro/go-zero/core/logx"
)
var flake *sonyflake.Sonyflake
func init() {
flake = sonyflake.NewSonyflake(sonyflake.Settings{})
}
func GenId() int64 {
id, err := flake.NextID()
if err != nil {
logx.Severef("flake NextID failed with %s \n", err)
panic(err)
}
return int64(id)
}

7
common/wxminisub/tpl.go Normal file
View File

@@ -0,0 +1,7 @@
package wxminisub
//订单支付成功
const OrderPaySuccessTemplateID = "QIJPmfxaNqYzSjOlXGk1T6Xfw94JwbSPuOd3u_hi3WE"
//支付成功入驻通知
const OrderPaySuccessLiveKnowTemplateID = "kmm-maRr6v_9eMxEPpj-5clJ2YW_EFpd8-ngyYk63e4"

18
common/xerr/errCode.go Normal file
View File

@@ -0,0 +1,18 @@
package xerr
// 成功返回
const OK uint32 = 200
/**(前3位代表业务,后三位代表具体功能)**/
// 全局错误码
const SERVER_COMMON_ERROR uint32 = 100001
const REUQEST_PARAM_ERROR uint32 = 100002
const TOKEN_EXPIRE_ERROR uint32 = 100003
const TOKEN_GENERATE_ERROR uint32 = 100004
const DB_ERROR uint32 = 100005
const DB_UPDATE_AFFECTED_ZERO_ERROR uint32 = 100006
const PARAM_VERIFICATION_ERROR uint32 = 100007
const CUSTOM_ERROR uint32 = 100008
//用户模块

30
common/xerr/errMsg.go Normal file
View File

@@ -0,0 +1,30 @@
package xerr
var message map[uint32]string
func init() {
message = make(map[uint32]string)
message[OK] = "SUCCESS"
message[SERVER_COMMON_ERROR] = "服务器开小差啦,稍后再来试一试"
message[REUQEST_PARAM_ERROR] = "参数错误"
message[TOKEN_EXPIRE_ERROR] = "token失效请重新登陆"
message[TOKEN_GENERATE_ERROR] = "生成token失败"
message[DB_ERROR] = "数据库繁忙,请稍后再试"
message[DB_UPDATE_AFFECTED_ZERO_ERROR] = "更新数据影响行数为0"
}
func MapErrMsg(errcode uint32) string {
if msg, ok := message[errcode]; ok {
return msg
} else {
return "服务器开小差啦,稍后再来试一试"
}
}
func IsCodeErr(errcode uint32) bool {
if _, ok := message[errcode]; ok {
return true
} else {
return false
}
}

39
common/xerr/errors.go Normal file
View File

@@ -0,0 +1,39 @@
package xerr
import (
"fmt"
)
/**
常用通用固定错误
*/
type CodeError struct {
errCode uint32
errMsg string
}
// 返回给前端的错误码
func (e *CodeError) GetErrCode() uint32 {
return e.errCode
}
// 返回给前端显示端错误信息
func (e *CodeError) GetErrMsg() string {
return e.errMsg
}
func (e *CodeError) Error() string {
return fmt.Sprintf("ErrCode:%dErrMsg:%s", e.errCode, e.errMsg)
}
func NewErrCodeMsg(errCode uint32, errMsg string) *CodeError {
return &CodeError{errCode: errCode, errMsg: errMsg}
}
func NewErrCode(errCode uint32) *CodeError {
return &CodeError{errCode: errCode, errMsg: MapErrMsg(errCode)}
}
func NewErrMsg(errMsg string) *CodeError {
return &CodeError{errCode: CUSTOM_ERROR, errMsg: errMsg}
}