commit
1b985f07cb
@ -0,0 +1,11 @@
|
||||
ckeditor5-build-classic/node_modules/
|
||||
./vscode
|
||||
GinSkeleton/.vscode
|
||||
GinSkeleton/.idea/
|
||||
.idea/
|
||||
.idea
|
||||
|
||||
ckeditor5/node_modules/*
|
||||
GinSkeleton/config/gorm_v2.yml
|
||||
GinSkeleton/public/storage
|
||||
node_modules/
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Package",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "~/WorkSpace/GinSkeleto",
|
||||
"env": {},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 张奇峰
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -0,0 +1,25 @@
|
||||
package destroy
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
"goskeleton/app/core/event_manage"
|
||||
"goskeleton/app/global/consts"
|
||||
"goskeleton/app/global/variable"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 用于系统信号的监听
|
||||
go func() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM) // 监听可能的退出信号
|
||||
received := <-c //接收信号管道中的值
|
||||
variable.ZapLog.Warn(consts.ProcessKilled, zap.String("信号值", received.String()))
|
||||
(event_manage.CreateEventManageFactory()).FuzzyCall(variable.EventDestroyPrefix)
|
||||
close(c)
|
||||
os.Exit(1)
|
||||
}()
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"goskeleton/app/global/consts"
|
||||
"goskeleton/app/service/ai_model_cli"
|
||||
"goskeleton/app/utils/response"
|
||||
)
|
||||
|
||||
type StyleGenerate struct {
|
||||
}
|
||||
type LayoutGenerate struct {
|
||||
}
|
||||
|
||||
// ai生成样式
|
||||
func (s *StyleGenerate) StyleGenerate(c *gin.Context) {
|
||||
// 非流式传输
|
||||
// if res, err := ai_model_cli.RequestStyle(c); err==nil {
|
||||
// response.Success(c, consts.CurdStatusOkMsg, res.(string))
|
||||
// } else {
|
||||
// response.Fail(c, consts.StyleGenerateFailCode, consts.StyleGenerateFailMsg, err)
|
||||
// }
|
||||
|
||||
// 设置 HTTP 头部为 SSE
|
||||
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||
c.Writer.Header().Set("Cache-Control", "no-cache")
|
||||
c.Writer.Header().Set("Connection", "keep-alive")
|
||||
|
||||
if err := ai_model_cli.RequestStyleStream(c); err != nil {
|
||||
response.Fail(c, consts.StyleGenerateFailCode, consts.StyleGenerateFailMsg, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ai排版
|
||||
func (l *LayoutGenerate) LayoutGenerate(c *gin.Context) {
|
||||
// 流式传输
|
||||
// 设置 HTTP 头部为 SSE
|
||||
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||
c.Writer.Header().Set("Cache-Control", "no-cache")
|
||||
c.Writer.Header().Set("Connection", "keep-alive")
|
||||
if err := ai_model_cli.RequestLayout(c); err != nil {
|
||||
response.Fail(c, consts.LayoutGenerateFailCode, consts.LayoutGenerateFailMsg, err)
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"goskeleton/app/global/consts"
|
||||
"goskeleton/app/service/ai_model_cli"
|
||||
"goskeleton/app/utils/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type DocRefine struct {
|
||||
}
|
||||
|
||||
// Ai 模型进行文档优化
|
||||
|
||||
func (u *DocRefine) DocRefine(context *gin.Context) {
|
||||
if r, recogWords := ai_model_cli.RequestQianFan(context); r {
|
||||
response.Success(context, consts.CurdStatusOkMsg, recogWords)
|
||||
} else {
|
||||
response.Fail(context, consts.DocRefineFailCode, consts.DocRefineFailMsg, "")
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"goskeleton/app/global/consts"
|
||||
"goskeleton/app/global/variable"
|
||||
"goskeleton/app/service/upload_file"
|
||||
"goskeleton/app/utils/response"
|
||||
)
|
||||
|
||||
type Upload struct {
|
||||
}
|
||||
|
||||
// 文件上传是一个独立模块,给任何业务返回文件上传后的存储路径即可。
|
||||
// 开始上传
|
||||
func (u *Upload) StartUpload(context *gin.Context) {
|
||||
savePath := variable.BasePath + variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath")
|
||||
if r, finnalSavePath := upload_file.Upload(context, savePath); r == true {
|
||||
response.Success(context, consts.CurdStatusOkMsg, finnalSavePath)
|
||||
} else {
|
||||
response.Fail(context, consts.FilesUploadFailCode, consts.FilesUploadFailMsg, "")
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package cors
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// 允许跨域
|
||||
func Next() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Headers", "Access-Control-Allow-Headers,Authorization,User-Agent, Keep-Alive, Content-Type, X-Requested-With,X-CSRF-Token,AccessToken,Token")
|
||||
c.Header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, OPTIONS")
|
||||
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
|
||||
c.Header("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
// 放行所有OPTIONS方法
|
||||
if method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusAccepted)
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package my_jwt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"goskeleton/app/global/my_errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 使用工厂创建一个 JWT 结构体
|
||||
func CreateMyJWT(signKey string) *JwtSign {
|
||||
if len(signKey) <= 0 {
|
||||
signKey = "goskeleton"
|
||||
}
|
||||
return &JwtSign{
|
||||
[]byte(signKey),
|
||||
}
|
||||
}
|
||||
|
||||
// 定义一个 JWT验签 结构体
|
||||
type JwtSign struct {
|
||||
SigningKey []byte
|
||||
}
|
||||
|
||||
// CreateToken 生成一个token
|
||||
func (j *JwtSign) CreateToken(claims CustomClaims) (string, error) {
|
||||
// 生成jwt格式的header、claims 部分
|
||||
tokenPartA := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
// 继续添加秘钥值,生成最后一部分
|
||||
return tokenPartA.SignedString(j.SigningKey)
|
||||
}
|
||||
|
||||
// 解析Token
|
||||
func (j *JwtSign) ParseToken(tokenString string) (*CustomClaims, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return j.SigningKey, nil
|
||||
})
|
||||
if token == nil {
|
||||
return nil, errors.New(my_errors.ErrorsTokenInvalid)
|
||||
}
|
||||
if err != nil {
|
||||
if ve, ok := err.(*jwt.ValidationError); ok {
|
||||
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
|
||||
return nil, errors.New(my_errors.ErrorsTokenMalFormed)
|
||||
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
|
||||
return nil, errors.New(my_errors.ErrorsTokenNotActiveYet)
|
||||
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
|
||||
// 如果 TokenExpired ,只是过期(格式都正确),我们认为他是有效的,接下可以允许刷新操作
|
||||
token.Valid = true
|
||||
goto labelHere
|
||||
} else {
|
||||
return nil, errors.New(my_errors.ErrorsTokenInvalid)
|
||||
}
|
||||
}
|
||||
}
|
||||
labelHere:
|
||||
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
|
||||
return claims, nil
|
||||
} else {
|
||||
return nil, errors.New(my_errors.ErrorsTokenInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新token
|
||||
func (j *JwtSign) RefreshToken(tokenString string, extraAddSeconds int64) (string, error) {
|
||||
|
||||
if CustomClaims, err := j.ParseToken(tokenString); err == nil {
|
||||
CustomClaims.ExpiresAt = time.Now().Unix() + extraAddSeconds
|
||||
return j.CreateToken(*CustomClaims)
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package data_type
|
||||
|
||||
type Page struct {
|
||||
Page float64 `form:"page" json:"page" binding:"min=1"` // 必填,页面值>=1
|
||||
Limit float64 `form:"limit" json:"limit" binding:"min=1"` // 必填,每页条数值>=1
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package register_validator
|
||||
|
||||
import (
|
||||
"goskeleton/app/core/container"
|
||||
"goskeleton/app/global/consts"
|
||||
"goskeleton/app/http/validator/api/home"
|
||||
)
|
||||
|
||||
// 各个业务模块验证器必须进行注册(初始化),程序启动时会自动加载到容器
|
||||
func ApiRegisterValidator() {
|
||||
//创建容器
|
||||
containers := container.CreateContainersFactory()
|
||||
|
||||
// key 按照前缀+模块+验证动作 格式,将各个模块验证注册在容器
|
||||
var key string
|
||||
|
||||
// 注册门户类表单参数验证器
|
||||
key = consts.ValidatorPrefix + "HomeNews"
|
||||
containers.Set(key, home.News{})
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package factory
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"goskeleton/app/core/container"
|
||||
"goskeleton/app/global/my_errors"
|
||||
"goskeleton/app/global/variable"
|
||||
"goskeleton/app/http/validator/core/interf"
|
||||
)
|
||||
|
||||
// 表单参数验证器工厂(请勿修改)
|
||||
func Create(key string) func(context *gin.Context) {
|
||||
|
||||
if value := container.CreateContainersFactory().Get(key); value != nil {
|
||||
if val, isOk := value.(interf.ValidatorInterface); isOk {
|
||||
return val.CheckParams
|
||||
}
|
||||
}
|
||||
variable.ZapLog.Error(my_errors.ErrorsValidatorNotExists + ", 验证器模块:" + key)
|
||||
return nil
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package interf
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// 验证器接口,每个验证器必须实现该接口,请勿修改
|
||||
type ValidatorInterface interface {
|
||||
CheckParams(context *gin.Context)
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package ai_doc
|
||||
|
||||
import (
|
||||
"goskeleton/app/global/consts"
|
||||
"goskeleton/app/http/controller/web"
|
||||
"goskeleton/app/http/validator/core/data_transfer"
|
||||
"goskeleton/app/utils/response"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type DocRefine struct {
|
||||
Type string `form:"type" json:"type" binding:"required"` // 必填、
|
||||
Doc string `form:"doc" json:"doc" binding:"required"` // 必填、对于文本,表示它的长度>=1
|
||||
Background string `form:"background" json:"background" `
|
||||
}
|
||||
|
||||
var Types = []string{"summary", "decoration", "extension", "correction", "translation"}
|
||||
|
||||
func (d DocRefine) CheckParams(context *gin.Context) {
|
||||
if err := context.ShouldBind(&d); err != nil {
|
||||
// 将表单参数验证器出现的错误直接交给错误翻译器统一处理即可
|
||||
response.ValidatorError(context, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断是否在类型中
|
||||
t := d.Type
|
||||
isExit := false
|
||||
for _, e := range Types {
|
||||
if strings.TrimSpace(t) == e {
|
||||
isExit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isExit {
|
||||
response.ErrorSystem(context, "DocRefine表单参数验证器json化失败", "")
|
||||
return
|
||||
}
|
||||
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
|
||||
extraAddBindDataContext := data_transfer.DataAddContext(d, consts.ValidatorPrefix, context)
|
||||
if extraAddBindDataContext == nil {
|
||||
response.ErrorSystem(context, "DocRefine表单参数验证器json化失败", "")
|
||||
return
|
||||
}
|
||||
(&web.DocRefine{}).DocRefine(extraAddBindDataContext)
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package ai_layout
|
||||
import(
|
||||
"github.com/gin-gonic/gin"
|
||||
"goskeleton/app/utils/response"
|
||||
"goskeleton/app/http/controller/web"
|
||||
"goskeleton/app/http/validator/core/data_transfer"
|
||||
"goskeleton/app/global/consts"
|
||||
)
|
||||
type LayoutGenerate struct {
|
||||
DocContent string `form:"doc_content" json:"doc_content" binging:"required"`
|
||||
}
|
||||
func (l LayoutGenerate) CheckParams(context *gin.Context) {
|
||||
if err:=context.ShouldBind(&l);err!=nil{
|
||||
response.ValidatorError(context,err)
|
||||
return
|
||||
}
|
||||
extraAddBindDataContext := data_transfer.DataAddContext(l, consts.ValidatorPrefix, context)
|
||||
if extraAddBindDataContext == nil {
|
||||
response.ErrorSystem(context, "LayoutGenerate表单参数验证器json化失败", "")
|
||||
return
|
||||
}
|
||||
(&web.LayoutGenerate{}).LayoutGenerate(extraAddBindDataContext)
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package ai_layout
|
||||
import(
|
||||
"github.com/gin-gonic/gin"
|
||||
"goskeleton/app/utils/response"
|
||||
"goskeleton/app/http/controller/web"
|
||||
"goskeleton/app/http/validator/core/data_transfer"
|
||||
"goskeleton/app/global/consts"
|
||||
)
|
||||
type ChatRecord struct {
|
||||
Role string `json:"role" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
}
|
||||
|
||||
type StyleGenerate struct {
|
||||
UserInput string `form:"user_input" json:"user_input" binding:"required"` // 必填
|
||||
ChatHistory []ChatRecord `form:"char_history" json:"chat_history"` // 非必填
|
||||
}
|
||||
|
||||
func (s StyleGenerate) CheckParams(context *gin.Context) {
|
||||
// 将表单参数验证器出现的错误直接交给错误翻译器统一处理即可
|
||||
if err := context.ShouldBind(&s); err != nil {
|
||||
response.ValidatorError(context, err)
|
||||
return
|
||||
}
|
||||
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
|
||||
extraAddBindDataContext := data_transfer.DataAddContext(s, consts.ValidatorPrefix, context)
|
||||
if extraAddBindDataContext == nil {
|
||||
response.ErrorSystem(context, "StyleGenerate表单参数验证器json化失败", "")
|
||||
return
|
||||
}
|
||||
(&web.StyleGenerate{}).StyleGenerate(extraAddBindDataContext)
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package ai_recognition
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"goskeleton/app/global/consts"
|
||||
"goskeleton/app/http/controller/web"
|
||||
"goskeleton/app/http/validator/core/data_transfer"
|
||||
"goskeleton/app/utils/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type VocRecognition struct {
|
||||
Voc string `form:"voc" json:"voc" binding:"required"` // 必填、对于文本,表示它的长度>=1
|
||||
}
|
||||
|
||||
func (v VocRecognition) CheckParams(context *gin.Context) {
|
||||
if err := context.ShouldBind(&v); err != nil {
|
||||
// 将表单参数验证器出现的错误直接交给错误翻译器统一处理即可
|
||||
response.ValidatorError(context, err)
|
||||
return
|
||||
}
|
||||
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
|
||||
extraAddBindDataContext := data_transfer.DataAddContext(v, consts.ValidatorPrefix, context)
|
||||
if extraAddBindDataContext == nil {
|
||||
response.ErrorSystem(context, "VocRecognition表单参数验证器json化失败", "")
|
||||
return
|
||||
}
|
||||
voc := v.Voc
|
||||
// 先进行 URL 解码
|
||||
// decodedVoc, err := url.QueryUnescape(voc)
|
||||
// if err != nil {
|
||||
// response.ErrorSystem(context, "VocRecognition表单参数验证器URL解码失败", "")
|
||||
// return
|
||||
// }
|
||||
// 再进行 Base64 解码
|
||||
_, err := base64.StdEncoding.DecodeString(voc)
|
||||
if err != nil {
|
||||
response.ErrorSystem(context, "VocRecognition表单参数验证器Base64解码失败", "")
|
||||
return
|
||||
}
|
||||
(&web.AiRecognition{}).VocRecognition(extraAddBindDataContext)
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package users
|
||||
|
||||
type BaseField struct {
|
||||
UserName string `form:"user_name" json:"user_name" binding:"required,min=3"` // 必填、对于文本,表示它的长度>=1
|
||||
Pass string `form:"pass" json:"pass" binding:"required,min=6"` // 密码为 必填,长度>=6
|
||||
}
|
||||
|
||||
type Id struct {
|
||||
Id float64 `form:"id" json:"id" binding:"required,min=1"`
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
package ai_model_cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"goskeleton/app/global/variable"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"goskeleton/app/global/consts"
|
||||
|
||||
"github.com/baidubce/bce-qianfan-sdk/go/qianfan"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RequestStyle(c *gin.Context) (interface{}, error) {
|
||||
|
||||
// userMsg := c.PostForm("user_input")
|
||||
userMsg := c.GetString(consts.ValidatorPrefix + "user_input")
|
||||
|
||||
qianfan.GetConfig().AccessKey = variable.ConfigYml.GetString("BaiduCE.QianFanAccessKey")
|
||||
qianfan.GetConfig().SecretKey = variable.ConfigYml.GetString("BaiduCE.QianFanSecretKey")
|
||||
|
||||
chat := qianfan.NewChatCompletion(
|
||||
qianfan.WithModel("ERNIE-4.0-8K"),
|
||||
)
|
||||
|
||||
chatHistory := []qianfan.ChatCompletionMessage{}
|
||||
|
||||
// 读取prompt文件
|
||||
systemMsgPath := variable.ConfigYml.GetString("BaiduCE.StyleGeneratePromptPath")
|
||||
// 读取文件内容
|
||||
prompt, err := os.ReadFile(variable.BasePath + systemMsgPath)
|
||||
if err != nil || len(prompt) == 0 {
|
||||
variable.ZapLog.Error(fmt.Sprintf("读取提示词文件失败: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// add user history to chat history
|
||||
userHistory, exist := c.Get(consts.ValidatorPrefix + "chat_history")
|
||||
if exist && userHistory != nil {
|
||||
// check if userHistory is of type []struct{Role string;Content string}
|
||||
historySlice, ok := userHistory.([]interface{})
|
||||
if !ok || len(historySlice)%2 != 0 {
|
||||
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v", userHistory))
|
||||
return nil, fmt.Errorf("用户历史对话格式错误")
|
||||
}
|
||||
|
||||
// convert userHistory to []qianfan.ChatCompletionMessage
|
||||
var chatHistoryConverted []qianfan.ChatCompletionMessage
|
||||
for _, item := range historySlice {
|
||||
if itemMap, ok := item.(map[string]interface{}); ok {
|
||||
role, roleOk := itemMap["role"].(string)
|
||||
content, contentOk := itemMap["content"].(string)
|
||||
if roleOk && contentOk {
|
||||
chatHistoryConverted = append(chatHistoryConverted, qianfan.ChatCompletionMessage{
|
||||
Role: role,
|
||||
Content: content,
|
||||
})
|
||||
} else {
|
||||
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\nrole 或 content 类型断言失败", userHistory))
|
||||
return nil, fmt.Errorf("用户历史对话格式错误")
|
||||
}
|
||||
} else {
|
||||
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\n无法将 item 转换为 map[string]interface{}", userHistory))
|
||||
return nil, fmt.Errorf("用户历史对话格式错误")
|
||||
}
|
||||
}
|
||||
|
||||
if len(chatHistoryConverted) > 0 && len(chatHistoryConverted)%2 == 0 {
|
||||
chatHistory = append(chatHistory, chatHistoryConverted...)
|
||||
}
|
||||
}
|
||||
|
||||
// add user input to chat history
|
||||
chatHistory = append(chatHistory, qianfan.ChatCompletionUserMessage(userMsg))
|
||||
|
||||
response, err := chat.Do(context.TODO(), &qianfan.ChatCompletionRequest{System: string(prompt), Messages: chatHistory})
|
||||
if err != nil {
|
||||
variable.ZapLog.Error(fmt.Sprintf("对话失败: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.Result, nil
|
||||
}
|
||||
|
||||
func RequestStyleStream(c *gin.Context) error {
|
||||
userMsg := c.GetString(consts.ValidatorPrefix + "user_input")
|
||||
|
||||
chatHistory := []qianfan.ChatCompletionMessage{}
|
||||
|
||||
systemMsgPath := variable.ConfigYml.GetString("BaiduCE.StyleGeneratePromptPath")
|
||||
prompt, err := os.ReadFile(variable.BasePath + systemMsgPath)
|
||||
if err != nil || len(prompt) == 0 {
|
||||
variable.ZapLog.Error(fmt.Sprintf("读取提示词文件失败: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
userHistory, exist := c.Get(consts.ValidatorPrefix + "chat_history")
|
||||
if exist && userHistory != nil {
|
||||
historySlice, ok := userHistory.([]interface{})
|
||||
if !ok || len(historySlice)%2 != 0 {
|
||||
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v", userHistory))
|
||||
return fmt.Errorf("用户历史对话格式错误")
|
||||
}
|
||||
|
||||
var chatHistoryConverted []qianfan.ChatCompletionMessage
|
||||
for _, item := range historySlice {
|
||||
if itemMap, ok := item.(map[string]interface{}); ok {
|
||||
role, roleOk := itemMap["role"].(string)
|
||||
content, contentOk := itemMap["content"].(string)
|
||||
if roleOk && contentOk {
|
||||
chatHistoryConverted = append(chatHistoryConverted, qianfan.ChatCompletionMessage{
|
||||
Role: role,
|
||||
Content: content,
|
||||
})
|
||||
} else {
|
||||
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\nrole 或 content 类型断言失败", userHistory))
|
||||
return fmt.Errorf("用户历史对话格式错误")
|
||||
}
|
||||
} else {
|
||||
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\n无法将 item 转换为 map[string]interface{}", userHistory))
|
||||
return fmt.Errorf("用户历史对话格式错误")
|
||||
}
|
||||
}
|
||||
|
||||
if len(chatHistoryConverted) > 0 && len(chatHistoryConverted)%2 == 0 {
|
||||
chatHistory = append(chatHistory, chatHistoryConverted...)
|
||||
}
|
||||
}
|
||||
|
||||
chatHistory = append(chatHistory, qianfan.ChatCompletionUserMessage(userMsg))
|
||||
return ChatByStream(c, string(prompt), chatHistory)
|
||||
}
|
||||
|
||||
func RequestLayout(c *gin.Context) error {
|
||||
doc_content := c.GetString(consts.ValidatorPrefix + "doc_content")
|
||||
|
||||
chatHistory := []qianfan.ChatCompletionMessage{}
|
||||
|
||||
systemMsgPath := variable.ConfigYml.GetString("BaiduCE.LayoutGeneratePromptPath")
|
||||
prompt, err := os.ReadFile(variable.BasePath + systemMsgPath)
|
||||
if err != nil || len(prompt) == 0 {
|
||||
variable.ZapLog.Error(fmt.Sprintf("读取提示词文件失败: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
chatHistory = append(chatHistory, qianfan.ChatCompletionUserMessage("待排版内容\n"+doc_content))
|
||||
return ChatByStream(c, string(prompt), chatHistory)
|
||||
}
|
||||
|
||||
func ChatByStream(c *gin.Context, prompt string, chatHistory []qianfan.ChatCompletionMessage) error{
|
||||
|
||||
qianfan.GetConfig().AccessKey = variable.ConfigYml.GetString("BaiduCE.QianFanAccessKey")
|
||||
qianfan.GetConfig().SecretKey = variable.ConfigYml.GetString("BaiduCE.QianFanSecretKey")
|
||||
|
||||
chat := qianfan.NewChatCompletion(
|
||||
qianfan.WithModel("ERNIE-4.0-8K"),
|
||||
)
|
||||
|
||||
stream, err := chat.Stream(context.TODO(), &qianfan.ChatCompletionRequest{System: string(prompt), Messages: chatHistory})
|
||||
if err != nil {
|
||||
variable.ZapLog.Error(fmt.Sprintf("对话失败: %v", err))
|
||||
return err
|
||||
}
|
||||
defer stream.Close()
|
||||
|
||||
c.Writer.Flush()
|
||||
defer c.Writer.Flush()
|
||||
outputMsg:=strings.Builder{}
|
||||
for {
|
||||
response, err := stream.Recv()
|
||||
if response.IsEnd {
|
||||
break // 流结束,退出循环
|
||||
}
|
||||
if err != nil {
|
||||
variable.ZapLog.Error(fmt.Sprintf("接收流失败: %v", err))
|
||||
return err
|
||||
}
|
||||
// 将结果写入到响应体
|
||||
outputMsg.WriteString(response.Result)
|
||||
if _, err := fmt.Fprintf(c.Writer, "%s", response.Result); err != nil {
|
||||
variable.ZapLog.Error(fmt.Sprintf("写入流失败: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 立即刷新缓冲区,以确保数据立即发送到客户端
|
||||
c.Writer.Flush()
|
||||
}
|
||||
return nil // 正常结束,返回 nil
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package curd
|
||||
|
||||
import (
|
||||
"goskeleton/app/model"
|
||||
"goskeleton/app/utils/md5_encrypt"
|
||||
)
|
||||
|
||||
func CreateUserCurdFactory() *UsersCurd {
|
||||
return &UsersCurd{model.CreateUserFactory("")}
|
||||
}
|
||||
|
||||
type UsersCurd struct {
|
||||
userModel *model.UsersModel
|
||||
}
|
||||
|
||||
// func (u *UsersCurd) Register(userName, pass, userIp string) bool {
|
||||
func (u *UsersCurd) Register(userName, pass string) bool {
|
||||
pass = md5_encrypt.Base64Md5(pass) // 预先处理密码加密,然后存储在数据库
|
||||
return u.userModel.Register(userName, pass)
|
||||
}
|
||||
|
||||
func (u *UsersCurd) Store(name string, pass string, realName string, phone string, remark string) bool {
|
||||
|
||||
pass = md5_encrypt.Base64Md5(pass) // 预先处理密码加密,然后存储在数据库
|
||||
return u.userModel.Store(name, pass, realName, phone, remark)
|
||||
}
|
||||
|
||||
func (u *UsersCurd) NameUpdate(id int, name string) bool {
|
||||
//预先处理密码加密等操作,然后进行更新
|
||||
// pass = md5_encrypt.Base64Md5(pass) // 预先处理密码加密,然后存储在数据库
|
||||
return u.userModel.NameUpdate(id, name)
|
||||
}
|
||||
func (u *UsersCurd) UpdatePassword(id int,userName,oldpass,newpass string) bool {
|
||||
//预先处理密码加密等操作,然后进行更新
|
||||
oldpass = md5_encrypt.Base64Md5(oldpass)
|
||||
newpass = md5_encrypt.Base64Md5(newpass) // 预先处理密码加密,然后存储在数据库
|
||||
// return u.userModel.UpdatePassword(id,oldpass,newpass)
|
||||
return u.userModel.UpdatePassword(id,userName,oldpass,newpass)!=nil
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package on_open_success
|
||||
|
||||
// ClientMoreParams 为客户端成功上线后设置更多的参数
|
||||
// ws 客户端成功上线以后,可以通过客户端携带的唯一参数,在数据库查询更多的其他关键信息,设置在 *Client 结构体上
|
||||
// 这样便于在后续获取在线客户端时快速获取其他关键信息,例如:进行消息广播时记录日志可能需要更多字段信息等
|
||||
type ClientMoreParams struct {
|
||||
UserParams1 string `json:"user_params_1"` // 字段名称以及类型由 开发者自己定义
|
||||
UserParams2 string `json:"user_params_2"`
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package casbin_v2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/model"
|
||||
gormadapter "github.com/casbin/gorm-adapter/v3"
|
||||
"gorm.io/gorm"
|
||||
"goskeleton/app/global/my_errors"
|
||||
"goskeleton/app/global/variable"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
//创建 casbin Enforcer(执行器)
|
||||
func InitCasbinEnforcer() (*casbin.SyncedEnforcer, error) {
|
||||
var tmpDbConn *gorm.DB
|
||||
var Enforcer *casbin.SyncedEnforcer
|
||||
switch strings.ToLower(variable.ConfigGormv2Yml.GetString("Gormv2.UseDbType")) {
|
||||
case "mysql":
|
||||
if variable.GormDbMysql == nil {
|
||||
return nil, errors.New(my_errors.ErrorCasbinCanNotUseDbPtr)
|
||||
}
|
||||
tmpDbConn = variable.GormDbMysql
|
||||
case "sqlserver", "mssql":
|
||||
if variable.GormDbSqlserver == nil {
|
||||
return nil, errors.New(my_errors.ErrorCasbinCanNotUseDbPtr)
|
||||
}
|
||||
tmpDbConn = variable.GormDbSqlserver
|
||||
case "postgre", "postgresql", "postgres":
|
||||
if variable.GormDbPostgreSql == nil {
|
||||
return nil, errors.New(my_errors.ErrorCasbinCanNotUseDbPtr)
|
||||
}
|
||||
tmpDbConn = variable.GormDbPostgreSql
|
||||
default:
|
||||
}
|
||||
|
||||
prefix := variable.ConfigYml.GetString("Casbin.TablePrefix")
|
||||
tbName := variable.ConfigYml.GetString("Casbin.TableName")
|
||||
|
||||
a, err := gormadapter.NewAdapterByDBUseTableName(tmpDbConn, prefix, tbName)
|
||||
if err != nil {
|
||||
return nil, errors.New(my_errors.ErrorCasbinCreateAdaptFail)
|
||||
}
|
||||
modelConfig := variable.ConfigYml.GetString("Casbin.ModelConfig")
|
||||
|
||||
if m, err := model.NewModelFromString(modelConfig); err != nil {
|
||||
return nil, errors.New(my_errors.ErrorCasbinNewModelFromStringFail + err.Error())
|
||||
} else {
|
||||
if Enforcer, err = casbin.NewSyncedEnforcer(m, a); err != nil {
|
||||
return nil, errors.New(my_errors.ErrorCasbinCreateEnforcerFail)
|
||||
}
|
||||
_ = Enforcer.LoadPolicy()
|
||||
AutoLoadSeconds := variable.ConfigYml.GetDuration("Casbin.AutoLoadPolicySeconds")
|
||||
Enforcer.StartAutoLoadPolicy(time.Second * AutoLoadSeconds)
|
||||
return Enforcer, nil
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package cur_userinfo
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"goskeleton/app/global/variable"
|
||||
"goskeleton/app/http/middleware/my_jwt"
|
||||
)
|
||||
|
||||
// GetCurrentUserId 获取当前用户的id
|
||||
// @context 请求上下文
|
||||
func GetCurrentUserId(context *gin.Context) (int64, bool) {
|
||||
tokenKey := variable.ConfigYml.GetString("Token.BindContextKeyName")
|
||||
currentUser, exist := context.MustGet(tokenKey).(my_jwt.CustomClaims)
|
||||
return currentUser.UserId, exist
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package gorm_v2
|
||||
|
||||
// 数据库参数配置,结构体
|
||||
// 用于解决复杂的业务场景连接到多台服务器部署的 mysql、sqlserver、postgresql 数据库
|
||||
// 具体用法参见常用开发模块:多源数据库的操作
|
||||
|
||||
type ConfigParams struct {
|
||||
Write ConfigParamsDetail
|
||||
Read ConfigParamsDetail
|
||||
}
|
||||
type ConfigParamsDetail struct {
|
||||
Host string
|
||||
DataBase string
|
||||
Port int
|
||||
Prefix string
|
||||
User string
|
||||
Pass string
|
||||
Charset string
|
||||
}
|
@ -0,0 +1,174 @@
|
||||
package gorm_v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
gormLog "gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/utils"
|
||||
"goskeleton/app/global/variable"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 自定义日志格式, 对 gorm 自带日志进行拦截重写
|
||||
func createCustomGormLog(sqlType string, options ...Options) gormLog.Interface {
|
||||
var (
|
||||
infoStr = "%s\n[info] "
|
||||
warnStr = "%s\n[warn] "
|
||||
errStr = "%s\n[error] "
|
||||
traceStr = "%s\n[%.3fms] [rows:%v] %s"
|
||||
traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s"
|
||||
traceErrStr = "%s %s\n[%.3fms] [rows:%v] %s"
|
||||
)
|
||||
logConf := gormLog.Config{
|
||||
SlowThreshold: time.Second * variable.ConfigGormv2Yml.GetDuration("Gormv2."+sqlType+".SlowThreshold"),
|
||||
LogLevel: gormLog.Warn,
|
||||
Colorful: false,
|
||||
}
|
||||
log := &logger{
|
||||
Writer: logOutPut{},
|
||||
Config: logConf,
|
||||
infoStr: infoStr,
|
||||
warnStr: warnStr,
|
||||
errStr: errStr,
|
||||
traceStr: traceStr,
|
||||
traceWarnStr: traceWarnStr,
|
||||
traceErrStr: traceErrStr,
|
||||
}
|
||||
for _, val := range options {
|
||||
val.apply(log)
|
||||
}
|
||||
return log
|
||||
}
|
||||
|
||||
type logOutPut struct{}
|
||||
|
||||
func (l logOutPut) Printf(strFormat string, args ...interface{}) {
|
||||
logRes := fmt.Sprintf(strFormat, args...)
|
||||
logFlag := "gorm_v2 日志:"
|
||||
detailFlag := "详情:"
|
||||
if strings.HasPrefix(strFormat, "[info]") || strings.HasPrefix(strFormat, "[traceStr]") {
|
||||
variable.ZapLog.Info(logFlag, zap.String(detailFlag, logRes))
|
||||
} else if strings.HasPrefix(strFormat, "[error]") || strings.HasPrefix(strFormat, "[traceErr]") {
|
||||
variable.ZapLog.Error(logFlag, zap.String(detailFlag, logRes))
|
||||
} else if strings.HasPrefix(strFormat, "[warn]") || strings.HasPrefix(strFormat, "[traceWarn]") {
|
||||
variable.ZapLog.Warn(logFlag, zap.String(detailFlag, logRes))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 尝试从外部重写内部相关的格式化变量
|
||||
type Options interface {
|
||||
apply(*logger)
|
||||
}
|
||||
type OptionFunc func(log *logger)
|
||||
|
||||
func (f OptionFunc) apply(log *logger) {
|
||||
f(log)
|
||||
}
|
||||
|
||||
// 定义 6 个函数修改内部变量
|
||||
func SetInfoStrFormat(format string) Options {
|
||||
return OptionFunc(func(log *logger) {
|
||||
log.infoStr = format
|
||||
})
|
||||
}
|
||||
|
||||
func SetWarnStrFormat(format string) Options {
|
||||
return OptionFunc(func(log *logger) {
|
||||
log.warnStr = format
|
||||
})
|
||||
}
|
||||
|
||||
func SetErrStrFormat(format string) Options {
|
||||
return OptionFunc(func(log *logger) {
|
||||
log.errStr = format
|
||||
})
|
||||
}
|
||||
|
||||
func SetTraceStrFormat(format string) Options {
|
||||
return OptionFunc(func(log *logger) {
|
||||
log.traceStr = format
|
||||
})
|
||||
}
|
||||
func SetTracWarnStrFormat(format string) Options {
|
||||
return OptionFunc(func(log *logger) {
|
||||
log.traceWarnStr = format
|
||||
})
|
||||
}
|
||||
|
||||
func SetTracErrStrFormat(format string) Options {
|
||||
return OptionFunc(func(log *logger) {
|
||||
log.traceErrStr = format
|
||||
})
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
gormLog.Writer
|
||||
gormLog.Config
|
||||
infoStr, warnStr, errStr string
|
||||
traceStr, traceErrStr, traceWarnStr string
|
||||
}
|
||||
|
||||
// LogMode log mode
|
||||
func (l *logger) LogMode(level gormLog.LogLevel) gormLog.Interface {
|
||||
newlogger := *l
|
||||
newlogger.LogLevel = level
|
||||
return &newlogger
|
||||
}
|
||||
|
||||
// Info print info
|
||||
func (l logger) Info(_ context.Context, msg string, data ...interface{}) {
|
||||
if l.LogLevel >= gormLog.Info {
|
||||
l.Printf(l.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warn print warn messages
|
||||
func (l logger) Warn(_ context.Context, msg string, data ...interface{}) {
|
||||
if l.LogLevel >= gormLog.Warn {
|
||||
l.Printf(l.warnStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
|
||||
}
|
||||
}
|
||||
|
||||
// Error print error messages
|
||||
func (l logger) Error(_ context.Context, msg string, data ...interface{}) {
|
||||
if l.LogLevel >= gormLog.Error {
|
||||
l.Printf(l.errStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace print sql message
|
||||
func (l logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
|
||||
if l.LogLevel <= gormLog.Silent {
|
||||
return
|
||||
}
|
||||
|
||||
elapsed := time.Since(begin)
|
||||
switch {
|
||||
case err != nil && l.LogLevel >= gormLog.Error && (!errors.Is(err, gormLog.ErrRecordNotFound) || !l.IgnoreRecordNotFoundError):
|
||||
sql, rows := fc()
|
||||
if rows == -1 {
|
||||
l.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-1", sql)
|
||||
} else {
|
||||
l.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
|
||||
}
|
||||
case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= gormLog.Warn:
|
||||
sql, rows := fc()
|
||||
slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold)
|
||||
if rows == -1 {
|
||||
l.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-1", sql)
|
||||
} else {
|
||||
l.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
|
||||
}
|
||||
case l.LogLevel == gormLog.Info:
|
||||
sql, rows := fc()
|
||||
if rows == -1 {
|
||||
l.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-1", sql)
|
||||
} else {
|
||||
l.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package error_record
|
||||
|
||||
import "goskeleton/app/global/variable"
|
||||
|
||||
// ErrorDeal 记录错误
|
||||
func ErrorDeal(err error) error {
|
||||
if err != nil {
|
||||
variable.ZapLog.Error(err.Error())
|
||||
}
|
||||
return err
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package publish_subscribe
|
||||
|
||||
import (
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
"goskeleton/app/global/variable"
|
||||
)
|
||||
|
||||
// 等 go 泛型稳定以后,生产者和消费者初始化参数的设置,本段代码就可以继续精简
|
||||
// 目前 apply(*producer) 的参数只能固定为生产者或者消费者其中之一的具体类型
|
||||
|
||||
// 1.生产者初始化参数定义
|
||||
|
||||
// OptionsProd 定义动态设置参数接口
|
||||
type OptionsProd interface {
|
||||
apply(*producer)
|
||||
}
|
||||
|
||||
// OptionFunc 以函数形式实现上面的接口
|
||||
type OptionFunc func(*producer)
|
||||
|
||||
func (f OptionFunc) apply(prod *producer) {
|
||||
f(prod)
|
||||
}
|
||||
|
||||
// SetProdMsgDelayParams 开发者设置生产者初始化时的参数
|
||||
func SetProdMsgDelayParams(enableMsgDelayPlugin bool) OptionsProd {
|
||||
return OptionFunc(func(p *producer) {
|
||||
p.enableDelayMsgPlugin = enableMsgDelayPlugin
|
||||
p.exchangeType = "x-delayed-message"
|
||||
p.args = amqp.Table{
|
||||
"x-delayed-type": "fanout",
|
||||
}
|
||||
p.exchangeName = variable.ConfigYml.GetString("RabbitMq.PublishSubscribe.DelayedExchangeName")
|
||||
// 延迟消息队列,交换机、消息全部设置为持久
|
||||
p.durable = true
|
||||
})
|
||||
}
|
||||
|
||||
// 2.消费者端初始化参数定义
|
||||
|
||||
// OptionsConsumer 定义动态设置参数接口
|
||||
type OptionsConsumer interface {
|
||||
apply(*consumer)
|
||||
}
|
||||
|
||||
// OptionsConsumerFunc 以函数形式实现上面的接口
|
||||
type OptionsConsumerFunc func(*consumer)
|
||||
|
||||
func (f OptionsConsumerFunc) apply(cons *consumer) {
|
||||
f(cons)
|
||||
}
|
||||
|
||||
// SetConsMsgDelayParams 开发者设置消费者端初始化时的参数
|
||||
func SetConsMsgDelayParams(enableDelayMsgPlugin bool) OptionsConsumer {
|
||||
return OptionsConsumerFunc(func(c *consumer) {
|
||||
c.enableDelayMsgPlugin = enableDelayMsgPlugin
|
||||
c.exchangeType = "x-delayed-message"
|
||||
c.exchangeName = variable.ConfigYml.GetString("RabbitMq.PublishSubscribe.DelayedExchangeName")
|
||||
// 延迟消息队列,交换机、消息全部设置为持久
|
||||
c.durable = true
|
||||
})
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
"goskeleton/app/global/variable"
|
||||
)
|
||||
|
||||
// 等 go 泛型稳定以后,生产者和消费者初始化参数的设置,本段代码就可以继续精简
|
||||
// 目前 apply(*producer) 的参数只能固定为生产者或者消费者其中之一的具体类型
|
||||
|
||||
// 1.生产者初始化参数定义
|
||||
|
||||
// OptionsProd 定义动态设置参数接口
|
||||
type OptionsProd interface {
|
||||
apply(*producer)
|
||||
}
|
||||
|
||||
// OptionFunc 以函数形式实现上面的接口
|
||||
type OptionFunc func(*producer)
|
||||
|
||||
func (f OptionFunc) apply(prod *producer) {
|
||||
f(prod)
|
||||
}
|
||||
|
||||
// SetProdMsgDelayParams 开发者设置生产者初始化时的参数
|
||||
func SetProdMsgDelayParams(enableMsgDelayPlugin bool) OptionsProd {
|
||||
return OptionFunc(func(p *producer) {
|
||||
p.enableDelayMsgPlugin = enableMsgDelayPlugin
|
||||
p.exchangeType = "x-delayed-message"
|
||||
p.args = amqp.Table{
|
||||
"x-delayed-type": "direct",
|
||||
}
|
||||
p.exchangeName = variable.ConfigYml.GetString("RabbitMq.Routing.DelayedExchangeName")
|
||||
// 延迟消息队列,交换机、消息全部设置为持久
|
||||
p.durable = true
|
||||
})
|
||||
}
|
||||
|
||||
// 2.消费者端初始化参数定义
|
||||
|
||||
// OptionsConsumer 定义动态设置参数接口
|
||||
type OptionsConsumer interface {
|
||||
apply(*consumer)
|
||||
}
|
||||
|
||||
// OptionsConsumerFunc 以函数形式实现上面的接口
|
||||
type OptionsConsumerFunc func(*consumer)
|
||||
|
||||
func (f OptionsConsumerFunc) apply(cons *consumer) {
|
||||
f(cons)
|
||||
}
|
||||
|
||||
// SetConsMsgDelayParams 开发者设置消费者端初始化时的参数
|
||||
func SetConsMsgDelayParams(enableDelayMsgPlugin bool) OptionsConsumer {
|
||||
return OptionsConsumerFunc(func(c *consumer) {
|
||||
c.enableDelayMsgPlugin = enableDelayMsgPlugin
|
||||
c.exchangeType = "x-delayed-message"
|
||||
c.exchangeName = variable.ConfigYml.GetString("RabbitMq.Routing.DelayedExchangeName")
|
||||
// 延迟消息队列,交换机、消息全部设置为持久
|
||||
c.durable = true
|
||||
})
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package topics
|
||||
|
||||
import (
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
"goskeleton/app/global/variable"
|
||||
)
|
||||
|
||||
// 等 go 泛型稳定以后,生产者和消费者初始化参数的设置,本段代码就可以继续精简
|
||||
// 目前 apply(*producer) 的参数只能固定为生产者或者消费者其中之一的具体类型
|
||||
|
||||
// 1.生产者初始化参数定义
|
||||
|
||||
// OptionsProd 定义动态设置参数接口
|
||||
type OptionsProd interface {
|
||||
apply(*producer)
|
||||
}
|
||||
|
||||
// OptionFunc 以函数形式实现上面的接口
|
||||
type OptionFunc func(*producer)
|
||||
|
||||
func (f OptionFunc) apply(prod *producer) {
|
||||
f(prod)
|
||||
}
|
||||
|
||||
// SetProdMsgDelayParams 开发者设置生产者初始化时的参数
|
||||
func SetProdMsgDelayParams(enableMsgDelayPlugin bool) OptionsProd {
|
||||
return OptionFunc(func(p *producer) {
|
||||
p.enableDelayMsgPlugin = enableMsgDelayPlugin
|
||||
p.exchangeType = "x-delayed-message"
|
||||
p.args = amqp.Table{
|
||||
"x-delayed-type": "topic",
|
||||
}
|
||||
p.exchangeName = variable.ConfigYml.GetString("RabbitMq.Topics.DelayedExchangeName")
|
||||
// 延迟消息队列,交换机、消息全部设置为持久
|
||||
p.durable = true
|
||||
})
|
||||
}
|
||||
|
||||
// 2.消费者端初始化参数定义
|
||||
|
||||
// OptionsConsumer 定义动态设置参数接口
|
||||
type OptionsConsumer interface {
|
||||
apply(*consumer)
|
||||
}
|
||||
|
||||
// OptionsConsumerFunc 以函数形式实现上面的接口
|
||||
type OptionsConsumerFunc func(*consumer)
|
||||
|
||||
func (f OptionsConsumerFunc) apply(cons *consumer) {
|
||||
f(cons)
|
||||
}
|
||||
|
||||
// SetConsMsgDelayParams 开发者设置消费者端初始化时的参数
|
||||
func SetConsMsgDelayParams(enableDelayMsgPlugin bool) OptionsConsumer {
|
||||
return OptionsConsumerFunc(func(c *consumer) {
|
||||
c.enableDelayMsgPlugin = enableDelayMsgPlugin
|
||||
c.exchangeType = "x-delayed-message"
|
||||
c.exchangeName = variable.ConfigYml.GetString("RabbitMq.Topics.DelayedExchangeName")
|
||||
// 延迟消息队列,交换机、消息全部设置为持久
|
||||
c.durable = true
|
||||
})
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package rsa
|
||||
import(
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"encoding/base64"
|
||||
"goskeleton/app/global/variable"
|
||||
"fmt"
|
||||
)
|
||||
func GenerateRSAKeyPair() ([]byte, []byte, error) {
|
||||
priKey, err := rsa.GenerateKey(rand.Reader, variable.ConfigYml.GetInt("RSA.keySize"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pubKey := &priKey.PublicKey
|
||||
|
||||
// 转换为字节切片
|
||||
priASN1 := x509.MarshalPKCS1PrivateKey(priKey)
|
||||
priPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: priASN1,
|
||||
})
|
||||
pubASN1, err := x509.MarshalPKIXPublicKey(pubKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pubPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: pubASN1,
|
||||
})
|
||||
return pubPEM, priPEM, nil
|
||||
}
|
||||
|
||||
func DecryptWithPrivateKey(privateKey *rsa.PrivateKey, encryptedPassword []byte) ([]byte, error) {
|
||||
decryptedBytes, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, encryptedPassword)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt password: %v", err)
|
||||
}
|
||||
return decryptedBytes, nil
|
||||
}
|
||||
|
||||
func DecodeBase64(encodedString string) ([]byte, error) {
|
||||
decodedBytes, err := base64.StdEncoding.DecodeString(encodedString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode base64 string: %v", err)
|
||||
}
|
||||
return decodedBytes, nil
|
||||
}
|
||||
|
||||
|
||||
func parsePKCS1PrivateKey(block *pem.Block) (*rsa.PrivateKey, error) {
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse PKCS1 private key: %v", err)
|
||||
}
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
func parsePKCS8PrivateKey(block *pem.Block) (*rsa.PrivateKey, error) {
|
||||
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse PKCS8 private key: %v", err)
|
||||
}
|
||||
return privateKey.(*rsa.PrivateKey), nil
|
||||
}
|
||||
|
||||
func ParsePrivateKeyFromPEM(pemKey []byte) (*rsa.PrivateKey, error) {
|
||||
block, _ := pem.Decode(pemKey)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to parse PEM block")
|
||||
}
|
||||
|
||||
// 尝试解析 PKCS#1 格式
|
||||
privateKey, err := parsePKCS1PrivateKey(block)
|
||||
if err == nil {
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
// 如果不是 PKCS#1 格式,尝试解析 PKCS#8 格式
|
||||
return parsePKCS8PrivateKey(block)
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package snow_flake
|
||||
|
||||
import (
|
||||
"goskeleton/app/global/consts"
|
||||
"goskeleton/app/global/variable"
|
||||
"goskeleton/app/utils/snow_flake/snowflake_interf"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 创建一个雪花算法生成器(生成工厂)
|
||||
func CreateSnowflakeFactory() snowflake_interf.InterfaceSnowFlake {
|
||||
return &snowflake{
|
||||
timestamp: 0,
|
||||
machineId: variable.ConfigYml.GetInt64("SnowFlake.SnowFlakeMachineId"),
|
||||
sequence: 0,
|
||||
}
|
||||
}
|
||||
|
||||
type snowflake struct {
|
||||
sync.Mutex
|
||||
timestamp int64
|
||||
machineId int64
|
||||
sequence int64
|
||||
}
|
||||
|
||||
// 生成分布式ID
|
||||
func (s *snowflake) GetId() int64 {
|
||||
s.Lock()
|
||||
defer func() {
|
||||
s.Unlock()
|
||||
}()
|
||||
now := time.Now().UnixNano() / 1e6
|
||||
if s.timestamp == now {
|
||||
s.sequence = (s.sequence + 1) & consts.SequenceMask
|
||||
if s.sequence == 0 {
|
||||
for now <= s.timestamp {
|
||||
now = time.Now().UnixNano() / 1e6
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.sequence = 0
|
||||
}
|
||||
s.timestamp = now
|
||||
r := (now-consts.StartTimeStamp)<<consts.TimestampShift | (s.machineId << consts.MachineIdShift) | (s.sequence)
|
||||
return r
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package snowflake_interf
|
||||
|
||||
type InterfaceSnowFlake interface {
|
||||
GetId() int64
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue