Browse Source

风险测评

kobe6258 7 months ago
parent
commit
47d00b8ffb

+ 52 - 24
common/component/log/log_plugin.go

@@ -5,6 +5,7 @@ import (
 	stringUtils "eta/eta_mini_ht_api/common/utils/string"
 	"fmt"
 	"github.com/beego/beego/v2/core/logs"
+	"github.com/beego/beego/v2/server/web/context"
 	"log"
 	"os"
 	"path"
@@ -58,35 +59,62 @@ func Warn(msg string, v ...interface{}) {
 func Debug(msg string, v ...interface{}) {
 	loggerHandler.Debug(msg, v...)
 }
+
+func InfoWithTraceId(ctx *context.Context, msg string, v ...interface{}) {
+	if traceId := ctx.Input.GetData("traceId"); traceId != "" {
+		msg = fmt.Sprintf("[traceId:%s,%v]", traceId, msg)
+	}
+	Info(msg, v...)
+}
+
+func ErrorWithTraceId(ctx *context.Context, msg string, v ...interface{}) {
+	if traceId := ctx.Input.GetData("traceId"); traceId != "" {
+		msg = fmt.Sprintf("[traceId:%s,%v]", traceId, msg)
+	}
+	Error(msg, v...)
+}
+
+func WarnWithTraceId(ctx *context.Context, msg string, v ...interface{}) {
+	if traceId := ctx.Input.GetData("traceId"); traceId != "" {
+		msg = fmt.Sprintf("[traceId:%s,%v]", traceId, msg)
+	}
+	Warn(msg, v...)
+}
+func DebugWithTraceId(ctx *context.Context, msg string, v ...interface{}) {
+	if traceId := ctx.Input.GetData("traceId"); traceId != "" {
+		msg = fmt.Sprintf("[traceId:%s,%v]", traceId, msg)
+	}
+	Debug(msg, v...)
+}
 func (c *CustomLogger) Debug(msg string, v ...interface{}) {
-	for _, logger := range c.logs {
-		if logger.GetLevel() >= logs.LevelInfo && logger.filter.ShouldLog(msg) {
-			logger.Debug(msg, v...)
+	for _, cusLogger := range c.logs {
+		if cusLogger.GetLevel() >= logs.LevelDebug && cusLogger.filter.ShouldLog(msg) {
+			cusLogger.Debug(msg, v...)
 		}
 
 	}
 }
 func (c *CustomLogger) Info(msg string, v ...interface{}) {
-	for _, logger := range c.logs {
-		if logger.GetLevel() >= logs.LevelInfo && logger.filter.ShouldLog(msg) {
-			logger.Info(msg, v...)
+	for _, cusLogger := range c.logs {
+		if cusLogger.GetLevel() >= logs.LevelInfo && cusLogger.filter.ShouldLog(msg) {
+			cusLogger.Info(msg, v...)
 		}
 
 	}
 }
 
 func (c *CustomLogger) Error(msg string, v ...interface{}) {
-	for _, logger := range c.logs {
-		if logger.GetLevel() >= logs.LevelError && logger.filter.ShouldLog(msg) {
-			logger.Error(msg, v...)
+	for _, cusLogger := range c.logs {
+		if cusLogger.GetLevel() >= logs.LevelError && cusLogger.filter.ShouldLog(msg) {
+			cusLogger.Error(msg, v...)
 		}
 	}
 }
 
 func (c *CustomLogger) Warn(msg string, v ...interface{}) {
-	for _, logger := range c.logs {
-		if logger.GetLevel() >= logs.LevelWarning && logger.filter.ShouldLog(msg) {
-			logger.Warn(msg, v...)
+	for _, cusLogger := range c.logs {
+		if cusLogger.GetLevel() >= logs.LevelWarning && cusLogger.filter.ShouldLog(msg) {
+			cusLogger.Warn(msg, v...)
 		}
 	}
 }
@@ -130,41 +158,41 @@ func initLogger(logCfg logConfig) {
 	if stringUtils.IsEmptyOrNil(logCfg.FilePath) {
 		logCfg.FilePath = DefalutLogFilePath
 	}
-	for _, appender := range logCfg.Appenders {
-		terminalType, ok := terminalType[appender.Type]
+	for _, appenderItem := range logCfg.Appenders {
+		terminal, ok := terminalType[appenderItem.Type]
 		if !ok {
-			fmt.Println("初始化日志执行器失败:{%s},终端类型不支持{type:%s}", appender.FileName, appender.Type)
+			fmt.Println("初始化日志执行器失败:{%s},终端类型不支持{type:%s}", appenderItem.FileName, appenderItem.Type)
 			continue
 		}
 		var beeLogger *logs.BeeLogger
-		if terminalType == logs.AdapterConsole {
+		if terminal == logs.AdapterConsole {
 			beeLogger = logs.NewLogger(LogChannelLen)
 			err := beeLogger.SetLogger(logs.AdapterConsole)
 			if err != nil {
-				fmt.Println("创建日志执行器失败:{%s} %v", appender.FileName, err)
+				fmt.Println("创建日志执行器失败:{%s} %v", appenderItem.FileName, err)
 				continue
 			}
 		} else {
-			logFile := appender.FileName
+			logFile := appenderItem.FileName
 			os.MkdirAll(logCfg.FilePath, os.ModePerm)
 			// 打开文件
-			appender.FileName = path.Join(logCfg.FilePath, logFile)
-			logProps, err := convertAppenderToLog(&appender)
+			appenderItem.FileName = path.Join(logCfg.FilePath, logFile)
+			props, err := convertAppenderToLog(&appenderItem)
 			if err != nil {
-				fmt.Println("初始化日志执行器失败:{%s} %v", appender.FileName, err)
+				fmt.Println("初始化日志执行器失败:{%s} %v", appenderItem.FileName, err)
 				continue
 			}
-			b, _ := json.Marshal(logProps)
+			b, _ := json.Marshal(props)
 			beeLogger = logs.NewLogger(LogChannelLen)
 			err = beeLogger.SetLogger(logs.AdapterFile, string(b))
 			if err != nil {
-				fmt.Println("设置日志执行器失败:{%s} %v", appender.FileName, err)
+				fmt.Println("设置日志执行器失败:{%s} %v", appenderItem.FileName, err)
 				continue
 			}
 			beeLogger.EnableFuncCallDepth(true)
 		}
 		loggerHandler.logs = append(loggerHandler.logs, &logger{BeeLogger: beeLogger,
-			filter: NewLevelFilter(appender.Filter)})
+			filter: NewLevelFilter(appenderItem.Filter)})
 	}
 }
 

+ 86 - 0
common/utils/auth/aes_utils.go

@@ -0,0 +1,86 @@
+package auth
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+	"errors"
+	"io"
+)
+
+// GenerateAESKey 生成 AES 密钥
+func GenerateAESKey() ([]byte, error) {
+	key := make([]byte, 16)
+	_, err := io.ReadFull(rand.Reader, key)
+	if err != nil {
+		return nil, err
+	}
+	return key, nil
+}
+
+// EncryptWithAES 使用 AES 加密数据
+func EncryptWithAES(key []byte, plaintext []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+
+	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
+	iv := ciphertext[:aes.BlockSize]
+	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+		return nil, err
+	}
+	stream := cipher.NewCFBEncrypter(block, iv)
+	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
+	return ciphertext, nil
+}
+
+// DecryptWithAES 使用 AES 解密数据
+func DecryptWithAES(key []byte, ciphertext []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	if len(ciphertext) < aes.BlockSize {
+		return nil, errors.New("ciphertext too short")
+	}
+	iv := ciphertext[:aes.BlockSize]
+	ciphertext = ciphertext[aes.BlockSize:]
+	stream := cipher.NewCBCDecrypter(block, iv)
+	stream.CryptBlocks(ciphertext, ciphertext)
+	//去填充数据
+	unpadded, err := unPad(ciphertext)
+	if err != nil {
+		return nil, err
+	}
+	return unpadded, nil
+}
+func unPad(buf []byte) ([]byte, error) {
+	if len(buf) == 0 {
+		return nil, errors.New("输入缓冲区为空")
+	}
+
+	// 获取最后一个字节作为填充长度
+	padding := int(buf[len(buf)-1])
+
+	// 检查填充是否有效
+	if padding > len(buf) || padding == 0 {
+		return nil, errors.New("无效的填充")
+	}
+
+	// 验证填充是否一致
+	for i := len(buf) - padding; i < len(buf); i++ {
+		if buf[i] != byte(padding) {
+			return nil, errors.New("无效的填充")
+		}
+	}
+
+	// 返回未填充的数据
+	return buf[:len(buf)-padding], nil
+}
+func pad(buf []byte, blockSize int) []byte {
+	padding := blockSize - (len(buf) % blockSize)
+	padText := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(buf, padText...)
+}

+ 27 - 79
common/utils/auth/encrypt_utils.go

@@ -1,99 +1,47 @@
 package auth
 
 import (
-	"crypto/aes"
-	"crypto/cipher"
-	"crypto/rand"
-	"encoding/base64"
-	"fmt"
-	"io"
+	logger "eta/eta_mini_ht_api/common/component/log"
+	"strings"
 )
 
-// GenerateAESKey 生成一个随机的AES密钥
-func GenerateAESKey(keySize int) ([]byte, error) {
-	if keySize != 16 && keySize != 24 && keySize != 32 {
-		return nil, fmt.Errorf("unsupported key size: %d", keySize)
-	}
-
-	key := make([]byte, keySize)
-	_, err := io.ReadFull(rand.Reader, key)
+func Encrypt(plaintext []byte) (encrypted string, err error) {
+	aesKey, err := GenerateAESKey()
 	if err != nil {
-		return nil, err
+		logger.Error("生成AES密钥失败", err)
+		return
 	}
-	return key, nil
-}
-
-// AESEncrypt 使用AES CBC模式加密数据
-func AESEncrypt(key []byte, plaintext []byte) (string, error) {
-	block, err := aes.NewCipher(key)
+	//数据加密
+	encryptData, err := EncryptWithAES(aesKey, plaintext)
 	if err != nil {
-		return "", err
-	}
-
-	// 生成一个随机的初始化向量
-	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
-	iv := ciphertext[:aes.BlockSize]
-	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
-		return "", err
+		logger.Error("ASE加密数据失败", err)
+		return
 	}
-
-	mode := cipher.NewCBCEncrypter(block, iv)
-	mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
-
-	// 返回Base64编码后的字符串
-	return base64.StdEncoding.EncodeToString(ciphertext), nil
-}
-
-// AESDecrypt 使用AES CBC模式解密数据
-func AESDecrypt(key []byte, ciphertext string) (string, error) {
-	ciphertextBytes, err := base64.StdEncoding.DecodeString(ciphertext)
+	publicKey, err := ParsePublicKeyFromPEM()
 	if err != nil {
-		return "", err
-	}
-
-	block, err := aes.NewCipher(key)
-	if err != nil {
-		return "", err
-	}
-
-	if len(ciphertextBytes) < aes.BlockSize {
-		return "", fmt.Errorf("ciphertext too short")
+		logger.Error("解析公钥失败", err)
+		return
 	}
-
-	iv := ciphertextBytes[:aes.BlockSize]
-	ciphertextBytes = ciphertextBytes[aes.BlockSize:]
-
-	mode := cipher.NewCBCDecrypter(block, iv)
-	mode.CryptBlocks(ciphertextBytes, ciphertextBytes)
-
-	// 去除PKCS#7填充
-	unPadded := unPad(ciphertextBytes)
-	return string(unPadded), nil
+	EncryptAesKey, err := EncryptWithRSA(publicKey, aesKey)
+	encrypted = string(encryptData) + "|" + string(EncryptAesKey)
+	return
 }
 
-// unPad 去除PKCS#7填充
-func unPad(src []byte) []byte {
-	padding := src[len(src)-1]
-	return src[:len(src)-int(padding)]
-}
-
-func main() {
-	key := []byte("this is a key123") // 16字节长的密钥
-	plaintext := []byte("Hello, World!")
-
-	// 加密
-	encrypted, err := AESEncrypt(key, plaintext)
+func Decrypt(ciphertext string) (decrypted []byte, err error) {
+	strParts := strings.Split(ciphertext, "|")
+	privateKey, err := ParsePrivateKeyFromPEM()
 	if err != nil {
-		fmt.Println("Error encrypting:", err)
+		logger.Error("解析私钥失败", err)
 		return
 	}
-	fmt.Println("Encrypted:", encrypted)
-
-	// 解密
-	decrypted, err := AESDecrypt(key, encrypted)
+	aesKey, err := DecryptWithRSA(privateKey, strParts[1])
 	if err != nil {
-		fmt.Println("Error decrypting:", err)
+		logger.Error("RSA解密AES秘钥失败", err)
 		return
 	}
-	fmt.Println("Decrypted:", decrypted)
+	decrypted, err = DecryptWithAES(aesKey, []byte(strParts[0]))
+	if err != nil {
+		logger.Error("AES解密数据失败", err)
+	}
+	return
 }

+ 64 - 0
common/utils/auth/rsa_utils.go

@@ -0,0 +1,64 @@
+package auth
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha256"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/pem"
+	"errors"
+	logger "eta/eta_mini_ht_api/common/component/log"
+	"os"
+)
+
+// EncryptWithRSA 使用 RSA 公钥加密数据
+func EncryptWithRSA(publicKey *rsa.PublicKey, data []byte) ([]byte, error) {
+	hash := sha256.Sum256(data)
+	encrypted, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, hash[:], nil)
+	if err != nil {
+		return nil, err
+	}
+	return encrypted, nil
+}
+
+// DecryptWithRSA 使用 RSA 私钥解密数据
+func DecryptWithRSA(privateKey *rsa.PrivateKey, encrypted string) ([]byte, error) {
+	encKey, _ := base64.StdEncoding.DecodeString(encrypted)
+	hash, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, encKey)
+	if err != nil {
+		return nil, err
+	}
+	return hash, nil
+}
+
+// ParsePrivateKeyFromPEM 解析RSA公钥
+func ParsePrivateKeyFromPEM() (privateKey *rsa.PrivateKey, err error) {
+	pemBlock, err := os.ReadFile("./conf/rsa_private_key.pem")
+	block, _ := pem.Decode(pemBlock)
+	if block == nil {
+		logger.Error("私钥解析失败")
+		return nil, errors.New("私钥解析失败")
+	}
+	privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
+	if err != nil {
+		return nil, err
+	}
+	return
+}
+
+// ParsePublicKeyFromPEM 解析RSA公钥
+func ParsePublicKeyFromPEM() (publicKey *rsa.PublicKey, err error) {
+	pemBlock, err := os.ReadFile("./conf/rsa_public_key.pem")
+	block, _ := pem.Decode(pemBlock)
+	if block == nil {
+		logger.Error("公钥解析失败")
+		return nil, errors.New("公钥解析失败")
+	}
+	key, err := x509.ParsePKIXPublicKey(block.Bytes)
+	if err != nil {
+		return nil, err
+	}
+	publicKey = key.(*rsa.PublicKey)
+	return
+}

+ 12 - 0
conf/log/log_config.json

@@ -52,6 +52,18 @@
       "rotate": true,
       "level": "info",
       "color": true
+    },
+    {
+      "filter": "webhookRequest",
+      "type": "file",
+      "filename": "api/webhook.log",
+      "maxlines": 1000000,
+      "maxsize": 268435456,
+      "daily": false,
+      "maxdays": 30,
+      "rotate": true,
+      "level": "info",
+      "color": true
     }
   ]
 }

+ 27 - 0
conf/rsa_private_key.pem

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA0Gh3c2fki27yLKMUPUqZhDa0vGRp01ca5Rbpd6RoZURIA4Ti
+1k/zf2jW0tJ1OUnkBiBtcfZ4d+6gPr1kdsdpxSjlV1PQfzaMtZg0ZKiHTw4xhJ+P
+/XCzIPJaUKAwKqb8U0gsXfZVcF0OEwWAgNxLzMhPlTiSAWaRUOumOHNexSRzG9UR
+y+v/UIVkuDXFwzb1aly93S0Elp7cDPQA0FCLqiwofnNdPTJ1BiXa1OO8UFXuV16H
+w0JeYdl+GWUf8Q4uTKUesclnBkLgOUaXSJQqfNwqSBj39H4vRTBKX1eiqhCwt3/l
+wBEpWW8YHkfEssclh0x2xf0714e/H3BuwLwdWwIDAQABAoIBAQCyQvkRfKcvYOnC
+poc0We/v+D8l+ZnPTO1TUQLH9JfbLsmOQQcqG18C9zDxSVU9eGeTmf8jgJfQtgrZ
+P3SEaNOe8fkhdi6b8ZPv7E28X67FPzW55CXsuY5uuv3ngu1QPl8L+WsBwCeaoe3c
+9VLSZhxsAFaaI7Y3fn0Dw1z9YimI47ZSHra1xo9JM9EZybVYi8HaIoame9fs+TaK
+ulr5nDjuRqPFhvVGpa/+S0S75ArW4VKrWp97cQ3D1xw2Fz0jP3eHndCErTYhj8MB
+10AFCndNKEIrwMPN5M426TbBLJY0vcIPYg0v4lSzwwAhYnNNmxMmroRLnD22ohdf
+687g52vRAoGBAOy6iiJxqW1mLNDKtwANI2XJBnoHbh8lPR3ZYlNybLeR49dFC7Qw
+5pv//rMybvWNG8obacQ0GiLXmlaajXFrpL9K+7qkg4W1VvjCvUo/Qk7He/sLOiXW
+/HuAOKZOhuLBoOzbDjDBHYy+eXDjzgqCrHpnI0wZ8uLahSUeTNSSgiNjAoGBAOFf
+uLnIMeV5kaNssvz+2iZ2W3aEW90fgNz8gy3VwzEarEGo7nalcq9vODwFHQNlZuE5
+BdzYXWE8Fjn/yJLipPxGXzPvGyZP4qUKgN8Mek39RJqvV3Mgtt0WG6IZpo0E0by0
+TmThlI0xJzi4Px01ip6fdhlJJPPC+yFayLzqtIupAoGBAJf7DtIcPNUSgvUtIB31
+UWN4kyLnAEkYpEs0lA7U6H1WOXusRV5TAsrmqEOtvlUBWfUAcn/Xn2v9FYZCRUqq
+/CMf5csm8ZV3HbYpeFNhl5VyNuOuio9encxAPp5uzeRowYMvA8ZDRVBlvRu/9TkE
+Oe1/p8ak5i3EczSoZlUXFlvXAoGBANx5N80G+0jx1Hd7Lt4wslhOEMuwT+3Rk86b
+d/iu1XSEE19oU1/eGoNk2i5dEjTwTeSmVAXn4/HrRhMXFrAa6Cui7B9yGa0xGRc7
+XzbUjhEdYq+wOGx35GwD5KR/U19BB60C9m3Z/+jf3O6vz45TgngKpw21cGDGrcXD
+efzV3imBAoGAI3Md0ap1zmLVpM8NLCA9JxPPQiQAq0VmCGrEjeEk+sVn09lkESdb
+VDiu0VNGfWKsY+7hUC1qjBQ1ZCR4NFkwD6QwBsovrni6rK3f/4qRRmP8ORK9ASIi
+Kw8stl7KvZdCltTobaVPFA90s+zgUsr9W6Q+gUPv2I/rmFWqpbJRARc=
+-----END RSA PRIVATE KEY-----

+ 9 - 0
conf/rsa_public_key.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Gh3c2fki27yLKMUPUqZ
+hDa0vGRp01ca5Rbpd6RoZURIA4Ti1k/zf2jW0tJ1OUnkBiBtcfZ4d+6gPr1kdsdp
+xSjlV1PQfzaMtZg0ZKiHTw4xhJ+P/XCzIPJaUKAwKqb8U0gsXfZVcF0OEwWAgNxL
+zMhPlTiSAWaRUOumOHNexSRzG9URy+v/UIVkuDXFwzb1aly93S0Elp7cDPQA0FCL
+qiwofnNdPTJ1BiXa1OO8UFXuV16Hw0JeYdl+GWUf8Q4uTKUesclnBkLgOUaXSJQq
+fNwqSBj39H4vRTBKX1eiqhCwt3/lwBEpWW8YHkfEssclh0x2xf0714e/H3BuwLwd
+WwIDAQAB
+-----END PUBLIC KEY-----

+ 0 - 3
controllers/base_controller.go

@@ -38,7 +38,6 @@ type BaseController struct {
 }
 
 func (b *BaseController) FailResponse(errInfo error, msg string) {
-
 	var retData BaseResponse
 	var etaError *exception.EtaError
 	if !errors.As(errInfo, &etaError) {
@@ -168,8 +167,6 @@ func (b *BaseController) ServeJSON(encoding ...bool) {
 
 func (b *BaseController) JSON(data interface{}, hasIndent bool, coding bool) error {
 	b.Ctx.Output.Header("Content-Type", "application/json; charset=utf-8")
-	//desEncrypt := utils.DesBase64Encrypt([]byte(utils.DesKey), utils.DesKeySalt)
-	//c.Ctx.Output.Header("Dk", string(desEncrypt)) // des3加解密key
 	// 设置Cookie为HTTPOnly
 	b.Ctx.SetCookie("", "", -1, "/", "", false, true, "")
 

+ 49 - 3
controllers/web_hook/htfutures_account_controller.go

@@ -1,6 +1,10 @@
 package web_hook
 
-import "eta/eta_mini_ht_api/controllers"
+import (
+	logger "eta/eta_mini_ht_api/common/component/log"
+	"eta/eta_mini_ht_api/controllers"
+	userService "eta/eta_mini_ht_api/domian/user"
+)
 
 type HTFuturesAccountController struct {
 	controllers.WebHookController
@@ -10,7 +14,49 @@ type HTFuturesAccountController struct {
 // @Summary 风险测评同步接口
 // @Description 风险测评同步接口
 // @Success 200 {object} controllers.BaseResponse
-// @router /v1/syncRiskLevel/ [get]
+// @router /v1/syncRiskLevel/ [post]
 func (h *HTFuturesAccountController) SyncCustomerRiskLevel() {
-	
+	controllers.WrapWebhook(&h.WebHookController, func() (result *controllers.WrapData, err error) {
+		result = h.InitWrapData("同步风险等级")
+		syncCustomerRiskLevelReq := new(SyncCustomerRiskLevelReq)
+		h.GetPostParams(syncCustomerRiskLevelReq)
+		if syncCustomerRiskLevelReq.Name == "" {
+			h.FailedResult("用户名字不能为空", result)
+			return
+		}
+		if syncCustomerRiskLevelReq.PhoneNumber == "" {
+			h.FailedResult("手机号码不能为空", result)
+			return
+		}
+		if syncCustomerRiskLevelReq.RiskLevel == "" {
+			h.FailedResult("风险等级不能为空", result)
+			return
+		}
+		if syncCustomerRiskLevelReq.RiskValidEndDate == "" {
+			h.FailedResult("风险测评有效期不能为空", result)
+			return
+		}
+		err = userService.UpdateRiskLevelInfo(userService.RiskLevelInfoDTO{
+			Name:             syncCustomerRiskLevelReq.Name,
+			PhoneNumber:      syncCustomerRiskLevelReq.PhoneNumber,
+			RiskLevel:        syncCustomerRiskLevelReq.RiskLevel,
+			RiskValidEndDate: syncCustomerRiskLevelReq.RiskValidEndDate,
+		})
+		if err != nil {
+			logger.ErrorWithTraceId(h.Ctx, err.Error())
+			h.FailedResult(err.Error(), result)
+			return
+		}
+		logger.InfoWithTraceId(h.Ctx, err.Error())
+		result = h.InitWrapData("同步风险等级成功")
+		h.SuccessResult("success", syncCustomerRiskLevelReq, result)
+		return
+	})
+}
+
+type SyncCustomerRiskLevelReq struct {
+	Name             string `json:"name"`
+	PhoneNumber      string `json:"phoneNumber"`
+	RiskLevel        string `json:"riskLevel"`
+	RiskValidEndDate string `json:"riskValidEndDate"`
 }

+ 140 - 3
controllers/webhook_controller.go

@@ -1,9 +1,146 @@
 package controllers
 
+import (
+	"encoding/json"
+	"errors"
+	logger "eta/eta_mini_ht_api/common/component/log"
+	"eta/eta_mini_ht_api/common/exception"
+	"eta/eta_mini_ht_api/common/http"
+	"github.com/beego/beego/v2/server/web"
+	"github.com/google/uuid"
+	"net/url"
+	"strings"
+)
+
+const (
+	TraceHeaderKey = "X-Trace-ID"
+	MDCTraceId     = "traceId"
+)
+
+type WebhookResponse struct {
+	Ret     int `description:"返回状态码"`
+	Msg     string
+	ErrMsg  string
+	ErrCode int
+	Data    interface{} `json:"data,omitempty"`
+	Success bool        `description:"true 执行成功,false 执行失败"`
+	TraceId string      `json:"traceId"`
+}
 type WebHookController struct {
-	BaseController
+	web.Controller
+}
+
+type WebhookRequest struct {
+	Data       string
+	EncryptKey string
 }
 
-func (c *WebHookController) Prepare() {
-	c.BaseController.Prepare()
+// JsonResult /*
+func (b *WebHookController) JsonResult(status int, errCode int, errMsg string, msg string, success bool, data interface{}) {
+	traceId := b.Ctx.Input.GetData(MDCTraceId).(string)
+	retData := WebhookResponse{
+		Ret:     status,
+		Msg:     msg,
+		ErrMsg:  errMsg,
+		ErrCode: errCode,
+		Data:    data,
+		Success: success,
+		TraceId: traceId}
+
+	b.Ctx.Output.SetStatus(status)
+	//content, err := json.Marshal(retData)
+	//if err != nil {
+	//	logger.Error("加密失败")
+	//} else {
+	//	if htConfig.NeedEncode() {
+	//		content = auth.DesBase64Encrypt(content, htConfig.GetDesCode())
+	//	}
+	//}
+	//fmt.Printf("%s", content)
+	//b.Data["json"] = content
+	b.Data["json"] = retData
+	b.ServeJSON()
+}
+
+// Wrap ControllerWrap 是一个用于封装控制器方法的函数
+func WrapWebhook(a *WebHookController, fn func() (*WrapData, error)) {
+	result, err := fn()
+	if err != nil {
+		logger.Error("%v", err)
+		a.FailResponse(err, result.Msg)
+		return
+	}
+	a.JsonResult(http.GetHttpStatusByAlias("ok"), http.ErrOK, "", result.Msg, http.Success, result.Data)
+}
+
+func (b *WebHookController) InitWrapData(msg string) *WrapData {
+	return &WrapData{Msg: msg}
+}
+func (b *WebHookController) SuccessResult(msg string, data interface{}, wrapData *WrapData) {
+	wrapData.Msg = msg
+	wrapData.Data = data
+}
+func (b *WebHookController) FailedResult(msg string, wrapData *WrapData) {
+	wrapData.Msg = msg
+}
+
+func (b *WebHookController) FailResponse(errInfo error, msg string) {
+	var retData WebhookResponse
+	var etaError *exception.EtaError
+	traceId := b.Ctx.Input.GetData(MDCTraceId).(string)
+	if !errors.As(errInfo, &etaError) {
+		etaError = exception.New(exception.UnknownError)
+	}
+	retData = WebhookResponse{
+		Ret:     200,
+		Msg:     msg,
+		ErrMsg:  etaError.ErrorMsg,
+		ErrCode: etaError.ErrorCode,
+		Data:    nil,
+		TraceId: traceId}
+	b.Data["json"] = retData
+	b.ServeJSON()
+}
+func (b *WebHookController) GetPostParams(data interface{}) {
+	err := json.Unmarshal(b.Ctx.Input.RequestBody, data)
+	if err != nil {
+		logger.Error("解析请求参数失败:%v", err)
+		data = nil
+	}
+}
+func (b *WebHookController) Prepare() {
+	var requestBody string
+	uri := b.Ctx.Input.URI()
+	method := b.Ctx.Input.Method()
+	if method == "GET" {
+		requestBody = b.Ctx.Request.RequestURI
+	} else {
+		requestBody, _ = url.QueryUnescape(string(b.Ctx.Input.RequestBody))
+	}
+	ip := b.Ctx.Input.IP()
+	b.Ctx.Input.URL()
+	traceId := uuid.NewString()
+	traceId = strings.ReplaceAll(traceId, "-", "")
+	b.Ctx.Output.Header(TraceHeaderKey, traceId)
+	//// 设置MDC中的traceId
+	b.Ctx.Input.SetData(MDCTraceId, traceId)
+	// 清理MDC中的traceId
+	logger.InfoWithTraceId(b.Ctx, "webhookRequest:[uri:%s, requestBody:%s, ip:%s]", uri, requestBody, ip)
+}
+func (b *WebHookController) Finish() {
+	runMode := web.BConfig.RunMode
+	if b.Data["json"] == nil {
+		logger.WarnWithTraceId(b.Ctx, "webhookRequest:[异常提醒:%v 接口:URI:%v;无返回值]", runMode, b.Ctx.Input.URI())
+		return
+	}
+	baseRes := b.Data["json"].(WebhookResponse)
+	content, err := json.Marshal(baseRes)
+	if err != nil {
+		logger.Error("webhookRequest:[应答json格式化失败:%s]", err)
+	}
+	if !baseRes.Success {
+		logger.InfoWithTraceId(b.Ctx, "webhookRequest:[异常提醒:%v接口:URI:%v;ErrMsg:&v;Msg:%v]", b.Ctx.Input.URI(), baseRes.ErrMsg, baseRes.Msg)
+	} else {
+		logger.InfoWithTraceId(b.Ctx, "webhookRequest:[uri:%s, resData:%s, ip:%s]", b.Ctx.Input.URI(), content)
+	}
 }

+ 36 - 0
domian/user/template_user.go

@@ -0,0 +1,36 @@
+package user
+
+import (
+	"errors"
+	logger "eta/eta_mini_ht_api/common/component/log"
+	userDao "eta/eta_mini_ht_api/models/user"
+	"gorm.io/gorm"
+)
+
+type RiskLevelInfoDTO struct {
+	Name             string `json:"name"`
+	PhoneNumber      string `json:"phoneNumber"`
+	RiskLevel        string `json:"riskLevel"`
+	RiskValidEndDate string `json:"riskValidEndDate"`
+}
+
+func UpdateRiskLevelInfo(dto RiskLevelInfoDTO) (err error) {
+	user, err := userDao.GetUserByMobile(dto.PhoneNumber)
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			logger.Error("获取用户信息不存在:%v,用户手机号码:%s", err, dto.PhoneNumber)
+			return errors.New("用户不存在,手机号码:" + dto.PhoneNumber)
+		}
+		logger.Error("获取用户信息失败:%v,用户手机号码:%s", err, dto.PhoneNumber)
+		return errors.New("获取用户信息失败,手机号码:" + dto.PhoneNumber)
+	}
+	user.RiskLevel = dto.RiskLevel
+	user.Username = dto.Name
+	user.RiskValidEndDate = dto.RiskValidEndDate
+	err = userDao.UpdateRiskLevelInfo(user)
+	if err != nil {
+		logger.Error("更新用户风险等级信息失败:%v,用户手机号码:%s", err, user.Mobile)
+		return errors.New("更新用户风险等级信息失败,手机号码:" + user.Mobile)
+	}
+	return
+}

+ 14 - 8
middleware/auth_middleware.go

@@ -8,7 +8,6 @@ import (
 	"eta/eta_mini_ht_api/common/utils/redis"
 	stringUtils "eta/eta_mini_ht_api/common/utils/string"
 	"eta/eta_mini_ht_api/controllers"
-	userService "eta/eta_mini_ht_api/domian/user"
 	"eta/eta_mini_ht_api/service/user"
 	"fmt"
 	"github.com/beego/beego/v2/server/web"
@@ -108,13 +107,13 @@ func AuthMiddleware() web.FilterFunc {
 			//校验redis中是否合法
 			redisToken := rd().GetString(redis.GenerateTokenKey(info.OpenId))
 			if redisToken == "" {
-				logger.Error("token无效:token已失效,自动退出登录")
+				logger.Error("token无效:token已失效")
 				//重置用户状态为登出
-				err = userService.UserLogout(userInfo.Id)
-				if err != nil {
-					logger.Error("重置用户状态失败:%v", err)
-				}
-				_ = ctx.JSONResp(LoginRequired())
+				//err = userService.UserLogout(userInfo.Id)
+				//if err != nil {
+				//	logger.Error("重置用户状态失败:%v", err)
+				//}
+				_ = ctx.JSONResp(tokenExpired())
 				return
 			}
 			if redisToken != parts[1] {
@@ -156,9 +155,16 @@ func unAuthorized() controllers.BaseResponse {
 		ErrMsg: exception.GetMsg(exception.Unauthorized),
 	}
 }
+func webhookUnauthorized(message string) controllers.BaseResponse {
+	return controllers.BaseResponse{
+		Ret:    401,
+		Msg:    message,
+		ErrMsg: exception.GetMsg(exception.Unauthorized),
+	}
+}
 func tokenExpired() controllers.BaseResponse {
 	return controllers.BaseResponse{
-		Ret:    408,
+		Ret:    401,
 		Msg:    TOKENEXPIRED,
 		ErrMsg: exception.GetMsg(exception.Unauthorized),
 	}

+ 57 - 0
middleware/webhook_middleware.go

@@ -0,0 +1,57 @@
+package middleware
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	logger "eta/eta_mini_ht_api/common/component/log"
+	"eta/eta_mini_ht_api/common/utils/auth"
+	"fmt"
+	"github.com/beego/beego/v2/server/web"
+	"github.com/beego/beego/v2/server/web/context"
+)
+
+func WebHookAuthMiddleware() web.FilterFunc {
+	return func(ctx *context.Context) {
+		body := ctx.Input.RequestBody
+		webhookRequest := new(WebhookRequest)
+		err := json.Unmarshal(body, &webhookRequest)
+		if err != nil {
+			rep := webhookUnauthorized("请求参数异常")
+			logger.Error("WebhookRequest解析失败: %v", err)
+			_ = ctx.JSONResp(rep)
+			return
+		}
+		privateKey, err := auth.ParsePrivateKeyFromPEM()
+		if err != nil {
+			rep := webhookUnauthorized("系统异常")
+			logger.Error("解析私钥失败: %v", err)
+			_ = ctx.JSONResp(rep)
+			return
+		}
+		aesKey, err := auth.DecryptWithRSA(privateKey, webhookRequest.EncryptKey)
+		if err != nil {
+			rep := webhookUnauthorized("解析AES秘钥")
+			logger.Error("解析AES秘钥失败: %v", err)
+			_ = ctx.JSONResp(rep)
+			return
+		}
+		decodeKey, _ := base64.StdEncoding.DecodeString(string(aesKey))
+		logger.Info("解码请求: %v", webhookRequest.Data)
+		data, err := base64.StdEncoding.DecodeString(webhookRequest.Data)
+		aes, err := auth.DecryptWithAES(decodeKey, data)
+		if err != nil {
+			rep := webhookUnauthorized("解密请求体失败")
+			logger.Error("解密请求体失败: %v", err)
+			_ = ctx.JSONResp(rep)
+			return
+		}
+		fmt.Printf("解密后的请求: %v", string(aes))
+		ctx.Input.RequestBody = aes
+		return
+	}
+}
+
+type WebhookRequest struct {
+	Data       string `json:"data"`
+	EncryptKey string `json:"encryptKey"`
+}

+ 21 - 16
models/user/template_user.go

@@ -12,24 +12,29 @@ const (
 )
 
 type TemplateUser struct {
-	Id             int       `gorm:"column:id;primaryKey;autoIncrement:'id'"`
-	Username       string    `gorm:"column:username;type:varchar(20);comment:用户名"`
-	Mobile         string    `gorm:"column:mobile;type:varchar(15);comment:手机号"`
-	OpenId         string    `gorm:"column:open_id;type:varchar(50);comment:open_id"`
-	GzhOpenId      string    `gorm:"column:gzh_open_id;type:varchar(255);comment:gzh_open_id"`
-	UnionId        string    `gorm:"column:union_id;type:varchar(50);comment:union_id"`
-	ReadCount      int       `gorm:"column:read_count;type:int(11);comment:阅读次数"`
-	FollowingGzh   int       `gorm:"column:following_gzh;type:int(1);comment:是否关注公众号"`
-	LastReadTime   time.Time `gorm:"column:last_read_time;type:timestamps;comment:最后阅读时间"`
-	LastLoginTime  time.Time `gorm:"column:last_login_time;type:timestamps;comment:最后登录时间"`
-	LastLogoutTime time.Time `gorm:"column:last_logout_time;type:timestamps;comment:最后登出时间"`
-	LoginStatus    LogType   `gorm:"column:login_status;type:enum('login','logout');comment:登录"`
-	RiskLevel      string    `gorm:"column:risk_level;type:varchar(255);comment:风险等级"`
-	IsDeleted      int       `gorm:"column:is_deleted;type:int(11);comment:是否删除"`
-	CreatedTime    time.Time `gorm:"column:created_time;type:timestamps;comment:创建时间"`
-	UpdatedTime    time.Time `gorm:"column:updated_time;type:timestamps;comment:更新时间"`
+	Id               int       `gorm:"column:id;primaryKey;autoIncrement:'id'"`
+	Username         string    `gorm:"column:username;type:varchar(20);comment:用户名"`
+	Mobile           string    `gorm:"column:mobile;type:varchar(15);comment:手机号"`
+	OpenId           string    `gorm:"column:open_id;type:varchar(50);comment:open_id"`
+	GzhOpenId        string    `gorm:"column:gzh_open_id;type:varchar(255);comment:gzh_open_id"`
+	UnionId          string    `gorm:"column:union_id;type:varchar(50);comment:union_id"`
+	ReadCount        int       `gorm:"column:read_count;type:int(11);comment:阅读次数"`
+	FollowingGzh     int       `gorm:"column:following_gzh;type:int(1);comment:是否关注公众号"`
+	LastReadTime     time.Time `gorm:"column:last_read_time;type:timestamps;comment:最后阅读时间"`
+	LastLoginTime    time.Time `gorm:"column:last_login_time;type:timestamps;comment:最后登录时间"`
+	LastLogoutTime   time.Time `gorm:"column:last_logout_time;type:timestamps;comment:最后登出时间"`
+	LoginStatus      LogType   `gorm:"column:login_status;type:enum('login','logout');comment:登录"`
+	RiskLevel        string    `gorm:"column:risk_level;type:varchar(255);comment:风险等级"`
+	RiskValidEndDate string    `gorm:"column:risk_valid_end_date;type:varchar(20);comment:风险等级有效期"`
+	IsDeleted        int       `gorm:"column:is_deleted;type:int(11);comment:是否删除"`
+	CreatedTime      time.Time `gorm:"column:created_time;type:timestamps;comment:创建时间"`
+	UpdatedTime      time.Time `gorm:"column:updated_time;type:timestamps;comment:更新时间"`
 }
 
+func UpdateRiskLevelInfo(user TemplateUser) (err error) {
+	db := models.Main()
+	return db.Save(user).Error
+}
 func (t *TemplateUser) BeforeCreate(tx *gorm.DB) (err error) {
 	t.CreatedTime = time.Now()
 	t.LoginStatus = Logout

+ 1 - 1
routers/commentsRouter.go

@@ -370,7 +370,7 @@ func init() {
         beego.ControllerComments{
             Method: "SyncCustomerRiskLevel",
             Router: `/v1/syncRiskLevel/`,
-            AllowHTTPMethods: []string{"get"},
+            AllowHTTPMethods: []string{"post"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})

+ 2 - 2
routers/router.go

@@ -23,7 +23,7 @@ func init() {
 	//增加授权拦截
 
 	web.InsertFilter("*", web.BeforeRouter, middleware.AuthMiddleware())
-
+	web.InsertFilter("/htapi/webhook/*", web.BeforeRouter, middleware.WebHookAuthMiddleware())
 	ns := web.NewNamespace("/htapi",
 		web.NSNamespace("/user",
 			web.NSInclude(
@@ -50,7 +50,7 @@ func init() {
 				&user.AnalystController{},
 			),
 		),
-		web.NSNamespace("/webhook/d ",
+		web.NSNamespace("/webhook ",
 			web.NSInclude(
 				&web_hook.HTFuturesAccountController{},
 			),