mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
feat: i18n
This commit is contained in:
@@ -3,7 +3,6 @@ package base
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/model"
|
||||
|
||||
@@ -162,13 +161,13 @@ func (ai *AppImpl[T, R]) CountByCond(cond any) int64 {
|
||||
|
||||
// Tx 执行事务操作
|
||||
func (ai *AppImpl[T, R]) Tx(ctx context.Context, funcs ...func(context.Context) error) (err error) {
|
||||
tx := contextx.GetTx(ctx)
|
||||
tx := GetTxFromCtx(ctx)
|
||||
dbCtx := ctx
|
||||
var txDb *gorm.DB
|
||||
|
||||
if tx == nil {
|
||||
txDb = global.Db.Begin()
|
||||
dbCtx, tx = contextx.WithTxDb(ctx, txDb)
|
||||
dbCtx, tx = NewCtxWithTxDb(ctx, txDb)
|
||||
} else {
|
||||
txDb = tx.DB
|
||||
tx.Count++
|
||||
|
||||
45
server/pkg/base/ctx.go
Normal file
45
server/pkg/base/ctx.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CtxKey string
|
||||
|
||||
const (
|
||||
DbKey CtxKey = "db"
|
||||
)
|
||||
|
||||
// Tx 事务上下文信息
|
||||
type Tx struct {
|
||||
Count int
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
// NewCtxWithTxDb 将事务db放置context中
|
||||
func NewCtxWithTxDb(ctx context.Context, db *gorm.DB) (context.Context, *Tx) {
|
||||
if tx := GetTxFromCtx(ctx); tx != nil {
|
||||
return ctx, tx
|
||||
}
|
||||
|
||||
tx := &Tx{Count: 1, DB: db}
|
||||
return context.WithValue(ctx, DbKey, tx), tx
|
||||
}
|
||||
|
||||
// GetDbFromCtx 获取ctx中的事务db
|
||||
func GetDbFromCtx(ctx context.Context) *gorm.DB {
|
||||
if tx := GetTxFromCtx(ctx); tx != nil {
|
||||
return tx.DB
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTxFromCtx 获取当前ctx事务
|
||||
func GetTxFromCtx(ctx context.Context) *Tx {
|
||||
if tx, ok := ctx.Value(DbKey).(*Tx); ok {
|
||||
return tx
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -100,7 +100,7 @@ type RepoImpl[T model.ModelI] struct {
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) Insert(ctx context.Context, e T) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
if db := GetDbFromCtx(ctx); db != nil {
|
||||
return br.InsertWithDb(ctx, db, e)
|
||||
}
|
||||
return gormx.Insert(br.fillBaseInfo(ctx, e))
|
||||
@@ -111,7 +111,7 @@ func (br *RepoImpl[T]) InsertWithDb(ctx context.Context, db *gorm.DB, e T) error
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) BatchInsert(ctx context.Context, es []T) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
if db := GetDbFromCtx(ctx); db != nil {
|
||||
return br.BatchInsertWithDb(ctx, db, es)
|
||||
}
|
||||
for _, e := range es {
|
||||
@@ -129,7 +129,7 @@ func (br *RepoImpl[T]) BatchInsertWithDb(ctx context.Context, db *gorm.DB, es []
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) UpdateById(ctx context.Context, e T, columns ...string) error {
|
||||
return br.UpdateByIdWithDb(ctx, contextx.GetDb(ctx), e, columns...)
|
||||
return br.UpdateByIdWithDb(ctx, GetDbFromCtx(ctx), e, columns...)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T, columns ...string) error {
|
||||
@@ -140,7 +140,7 @@ func (br *RepoImpl[T]) UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T, c
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) UpdateByCond(ctx context.Context, values any, cond any) error {
|
||||
return br.UpdateByCondWithDb(ctx, contextx.GetDb(ctx), values, cond)
|
||||
return br.UpdateByCondWithDb(ctx, GetDbFromCtx(ctx), values, cond)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) UpdateByCondWithDb(ctx context.Context, db *gorm.DB, values any, cond any) error {
|
||||
@@ -190,7 +190,7 @@ func (br *RepoImpl[T]) SaveWithDb(ctx context.Context, db *gorm.DB, e T) error {
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) DeleteById(ctx context.Context, id ...uint64) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
if db := GetDbFromCtx(ctx); db != nil {
|
||||
return br.DeleteByIdWithDb(ctx, db, id...)
|
||||
}
|
||||
return gormx.DeleteById(br.getModel(), id...)
|
||||
@@ -201,7 +201,7 @@ func (br *RepoImpl[T]) DeleteByIdWithDb(ctx context.Context, db *gorm.DB, id ...
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) DeleteByCond(ctx context.Context, cond any) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
if db := GetDbFromCtx(ctx); db != nil {
|
||||
return br.DeleteByCondWithDb(ctx, db, cond)
|
||||
}
|
||||
return gormx.DeleteByCond(br.getModel(), toQueryCond(cond))
|
||||
|
||||
111
server/pkg/base/sql.go
Normal file
111
server/pkg/base/sql.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SQLStatement 结构体用于存储解析后的 SQL 语句及其注释
|
||||
type SQLStatement struct {
|
||||
Comment string
|
||||
SQL string
|
||||
}
|
||||
|
||||
var sqlMap = make(map[string]string)
|
||||
|
||||
func RegisterSql(fs embed.FS) error {
|
||||
return walkDir(fs, ".", func(fp string, data []byte) error {
|
||||
if filepath.Ext(fp) != ".sql" {
|
||||
return nil
|
||||
}
|
||||
|
||||
fileNameWithExt := path.Base(fp)
|
||||
sqls, err := parseSQL(string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := strings.TrimSuffix(fileNameWithExt, path.Ext(fileNameWithExt))
|
||||
for _, sql := range sqls {
|
||||
sqlMap[filename+"."+strings.TrimSpace(sql.Comment)] = strings.TrimSpace(sql.SQL)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetSQL(filename, stmt string) string {
|
||||
return sqlMap[filename+"."+stmt]
|
||||
}
|
||||
|
||||
// walkDir 递归遍历目录
|
||||
func walkDir(fsys fs.FS, path string, callback func(filePath string, data []byte) error) error {
|
||||
entries, err := fs.ReadDir(fsys, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
entryPath := filepath.Join(path, entry.Name())
|
||||
if entry.IsDir() {
|
||||
// 递归遍历子目录
|
||||
if err := walkDir(fsys, entryPath, callback); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// 读取文件内容
|
||||
data, err := fs.ReadFile(fsys, entryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := callback(entryPath, data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseSQL 解析带有注释的 SQL 语句
|
||||
func parseSQL(sql string) ([]SQLStatement, error) {
|
||||
var statements []SQLStatement
|
||||
lines := strings.Split(sql, "\n")
|
||||
|
||||
var currentComment string
|
||||
var currentSQL string
|
||||
|
||||
for _, line := range lines {
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmedLine, "--") {
|
||||
// 处理单行注释
|
||||
if currentSQL != "" {
|
||||
statements = append(statements, SQLStatement{Comment: currentComment, SQL: strings.TrimRight(currentSQL, " ")})
|
||||
currentComment = ""
|
||||
currentSQL = ""
|
||||
}
|
||||
currentComment += strings.TrimPrefix(trimmedLine, "--") + "\n"
|
||||
continue
|
||||
}
|
||||
|
||||
if trimmedLine == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
currentSQL += line + " "
|
||||
if strings.HasSuffix(trimmedLine, ";") {
|
||||
statements = append(statements, SQLStatement{Comment: currentComment, SQL: strings.TrimRight(currentSQL, " ")})
|
||||
currentComment = ""
|
||||
currentSQL = ""
|
||||
}
|
||||
}
|
||||
|
||||
// 处理最后一段未结束的 SQL 语句
|
||||
if currentSQL != "" {
|
||||
statements = append(statements, SQLStatement{Comment: currentComment, SQL: strings.TrimRight(currentSQL, " ")})
|
||||
}
|
||||
|
||||
return statements, nil
|
||||
}
|
||||
28
server/pkg/base/sql_test.go
Normal file
28
server/pkg/base/sql_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParserSql(t *testing.T) {
|
||||
sql := `-- selectByCond
|
||||
Select * from tdb where id > 10;
|
||||
-- another comment
|
||||
Select * from another_table where name = 'test'
|
||||
and age = ?;
|
||||
-- multi-line comment
|
||||
-- continues here
|
||||
Select * from yet_another_table
|
||||
Where id = ?;`
|
||||
|
||||
statements, err := parseSQL(sql)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, stmt := range statements {
|
||||
fmt.Printf("Comment: %s\nSQL: %s\n\n", stmt.Comment, stmt.SQL)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
|
||||
"reflect"
|
||||
@@ -11,9 +13,9 @@ import (
|
||||
// 断言错误为ni
|
||||
// @param msgAndParams 消息与参数占位符,第一位为错误消息可包含%s等格式化标识。其余为Sprintf格式化值内容
|
||||
//
|
||||
// ErrIsNil(err)
|
||||
// ErrIsNil(err, "xxxx")
|
||||
// ErrIsNil(err, "xxxx: %s", "yyyy")
|
||||
// ErrIsNil(err)
|
||||
// ErrIsNil(err, "xxxx")
|
||||
// ErrIsNil(err, "xxxx: %s", "yyyy")
|
||||
func ErrIsNil(err error, msgAndParams ...any) {
|
||||
if err != nil {
|
||||
if len(msgAndParams) == 0 {
|
||||
@@ -24,24 +26,60 @@ func ErrIsNil(err error, msgAndParams ...any) {
|
||||
}
|
||||
}
|
||||
|
||||
// 断言错误为ni
|
||||
// @param msgId i18n消息id
|
||||
//
|
||||
// ErrIsNil(err)
|
||||
// ErrIsNil(err, "xxxx")
|
||||
// ErrIsNil(err, "xxxx: %s", "yyyy")
|
||||
func ErrIsNilI(ctx context.Context, err error, msgId i18n.MsgId, attrs ...any) {
|
||||
if err != nil {
|
||||
if len(attrs) == 0 {
|
||||
panic(errorx.NewBiz(err.Error()))
|
||||
}
|
||||
|
||||
panic(errorx.NewBiz(i18n.TC(ctx, msgId, attrs...)))
|
||||
}
|
||||
}
|
||||
|
||||
func ErrNotNil(err error, msg string, params ...any) {
|
||||
if err == nil {
|
||||
panic(errorx.NewBiz(fmt.Sprintf(msg, params...)))
|
||||
}
|
||||
}
|
||||
|
||||
// ErrIsNilAppendErr 断言错误为nil,否则抛出业务异常,异常消息可包含‘%s’进行错误信息提示
|
||||
//
|
||||
// // -> xxxx: err.Error()
|
||||
// biz.ErrIsNilAppendErr(err, "xxxx: %s")
|
||||
func ErrIsNilAppendErr(err error, msg string) {
|
||||
if err != nil {
|
||||
panic(errorx.NewBiz(fmt.Sprintf(msg, err.Error())))
|
||||
}
|
||||
}
|
||||
|
||||
// ErrIsNilAppendErr 断言错误为nil,否则抛出业务异常,异常消息可包含‘%s’进行错误信息提示
|
||||
//
|
||||
// // -> xxxx: err.Error()
|
||||
// biz.ErrIsNilAppendErr(err, "xxxx: %s")
|
||||
func ErrIsNilAppendErrI(ctx context.Context, err error, msgId i18n.MsgId) {
|
||||
if err != nil {
|
||||
panic(errorx.NewBiz(fmt.Sprintf(i18n.TC(ctx, msgId), err.Error())))
|
||||
}
|
||||
}
|
||||
|
||||
func IsTrue(exp bool, msg string, params ...any) {
|
||||
if !exp {
|
||||
panic(errorx.NewBiz(fmt.Sprintf(msg, params...)))
|
||||
}
|
||||
}
|
||||
|
||||
func IsTrueI(ctx context.Context, exp bool, msgId i18n.MsgId, attrs ...any) {
|
||||
if !exp {
|
||||
panic(errorx.NewBiz(i18n.TC(ctx, msgId, attrs...)))
|
||||
}
|
||||
}
|
||||
|
||||
func IsTrueBy(exp bool, err *errorx.BizError) {
|
||||
if !exp {
|
||||
panic(err)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Lang string `yaml:"lang"`
|
||||
Port int `yaml:"port"`
|
||||
Model string `yaml:"model"`
|
||||
ContextPath string `yaml:"context-path"` // 请求路径上下文
|
||||
@@ -15,6 +16,9 @@ type Server struct {
|
||||
}
|
||||
|
||||
func (s *Server) Default() {
|
||||
if s.Lang == "" {
|
||||
s.Lang = "zh_CN"
|
||||
}
|
||||
if s.Model == "" {
|
||||
s.Model = "release"
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"context"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CtxKey string
|
||||
@@ -13,7 +11,6 @@ type CtxKey string
|
||||
const (
|
||||
LoginAccountKey CtxKey = "loginAccount"
|
||||
TraceIdKey CtxKey = "traceId"
|
||||
DbKey CtxKey = "db"
|
||||
)
|
||||
|
||||
func NewLoginAccount(la *model.LoginAccount) context.Context {
|
||||
@@ -24,7 +21,7 @@ func WithLoginAccount(ctx context.Context, la *model.LoginAccount) context.Conte
|
||||
return context.WithValue(ctx, LoginAccountKey, la)
|
||||
}
|
||||
|
||||
// 从context中获取登录账号信息,不存在返回nil
|
||||
// GetLoginAccount 从context中获取登录账号信息,不存在返回nil
|
||||
func GetLoginAccount(ctx context.Context) *model.LoginAccount {
|
||||
if la, ok := ctx.Value(LoginAccountKey).(*model.LoginAccount); ok {
|
||||
return la
|
||||
@@ -36,6 +33,7 @@ func NewTraceId() context.Context {
|
||||
return WithTraceId(context.Background())
|
||||
}
|
||||
|
||||
// WithTraceId 将traceId放置context中
|
||||
func WithTraceId(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, TraceIdKey, stringx.RandByChars(16, stringx.Nums+stringx.LowerChars))
|
||||
}
|
||||
@@ -47,35 +45,3 @@ func GetTraceId(ctx context.Context) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Tx 事务上下文信息
|
||||
type Tx struct {
|
||||
Count int
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
// WithTxDb 将事务db放置context中
|
||||
func WithTxDb(ctx context.Context, db *gorm.DB) (context.Context, *Tx) {
|
||||
if tx := GetTx(ctx); tx != nil {
|
||||
return ctx, tx
|
||||
}
|
||||
|
||||
tx := &Tx{Count: 1, DB: db}
|
||||
return context.WithValue(ctx, DbKey, tx), tx
|
||||
}
|
||||
|
||||
// GetDb 获取ctx中的事务db
|
||||
func GetDb(ctx context.Context) *gorm.DB {
|
||||
if tx := GetTx(ctx); tx != nil {
|
||||
return tx.DB
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTx 获取当前ctx事务
|
||||
func GetTx(ctx context.Context) *Tx {
|
||||
if tx, ok := ctx.Value(DbKey).(*Tx); ok {
|
||||
return tx
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package errorx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/pkg/i18n"
|
||||
)
|
||||
|
||||
// 业务错误
|
||||
@@ -32,9 +34,17 @@ func (e BizError) String() string {
|
||||
return fmt.Sprintf("errCode: %d, errMsg: %s", e.Code(), e.Error())
|
||||
}
|
||||
|
||||
// 创建业务逻辑错误结构体,默认为业务逻辑错误
|
||||
func NewBiz(msg string, formats ...any) *BizError {
|
||||
return &BizError{code: BizErr.code, err: fmt.Sprintf(msg, formats...)}
|
||||
// NewBiz 创建业务逻辑错误结构体,默认为业务逻辑错误
|
||||
func NewBiz(msg string, formatValues ...any) *BizError {
|
||||
return &BizError{code: BizErr.code, err: fmt.Sprintf(msg, formatValues...)}
|
||||
}
|
||||
|
||||
// NewBizI 使用i18n的msgId创建业务逻辑错误结构体,默认为业务逻辑错误 (使用ctx中的国际化语言)
|
||||
//
|
||||
// // NameErr = {{.name}} is invalid => xxx is invalid
|
||||
// NewBizI(ctx, imsg.NameErr, "name", "xxxx")
|
||||
func NewBizI(ctx context.Context, msgId i18n.MsgId, attrs ...any) *BizError {
|
||||
return &BizError{code: BizErr.code, err: i18n.TC(ctx, msgId, attrs...)}
|
||||
}
|
||||
|
||||
// 创建业务逻辑错误结构体,可设置指定错误code
|
||||
|
||||
@@ -113,7 +113,7 @@ func (bus *EventBus) Unsubscribe(topic string, subId string) error {
|
||||
defer bus.lock.Unlock()
|
||||
subManager := bus.subscriberManager[topic]
|
||||
if subManager == nil {
|
||||
return errors.New("该主题不存在订阅者")
|
||||
return errors.New("there is no subscriber for this topic")
|
||||
}
|
||||
subManager.delSubscriber(subId)
|
||||
return nil
|
||||
@@ -122,7 +122,7 @@ func (bus *EventBus) Unsubscribe(topic string, subId string) error {
|
||||
func (bus *EventBus) Publish(ctx context.Context, topic string, val any) {
|
||||
bus.lock.Lock() // will unlock if handler is not found or always after setUpPublish
|
||||
defer bus.lock.Unlock()
|
||||
logx.Debugf("主题-[%s]-发布了事件", topic)
|
||||
logx.Debugf("topic - [%s] - published the event", topic)
|
||||
event := &Event{
|
||||
Topic: topic,
|
||||
Val: val,
|
||||
@@ -138,7 +138,7 @@ func (bus *EventBus) Publish(ctx context.Context, topic string, val any) {
|
||||
}
|
||||
|
||||
for subId, handler := range handlers {
|
||||
logx.Debugf("订阅者-[%s]-开始执行主题-[%s]-发布的事件", subId, topic)
|
||||
logx.Debugf("subscriber - [%s] - starts executing events published by topic - [%s]", subId, topic)
|
||||
if handler.once {
|
||||
subscriberManager.delSubscriber(subId)
|
||||
}
|
||||
@@ -164,7 +164,7 @@ func (bus *EventBus) WaitAsync() {
|
||||
func (bus *EventBus) doSubscribe(topic string, subId string, handler *eventHandler) error {
|
||||
bus.lock.Lock()
|
||||
defer bus.lock.Unlock()
|
||||
logx.Debugf("订阅者-[%s]-订阅了主题-[%s]", subId, topic)
|
||||
logx.Debugf("subscribers - [%s] - subscribed to topic - [%s]", subId, topic)
|
||||
subManager := bus.subscriberManager[topic]
|
||||
if subManager == nil {
|
||||
subManager = NewSubscriberManager()
|
||||
@@ -177,7 +177,7 @@ func (bus *EventBus) doSubscribe(topic string, subId string, handler *eventHandl
|
||||
func (bus *EventBus) doPublish(ctx context.Context, handler *eventHandler, event *Event) error {
|
||||
err := handler.handlerFunc(ctx, event)
|
||||
if err != nil {
|
||||
logx.Errorf("订阅者执行主题[%s]失败: %s", event.Topic, err.Error())
|
||||
logx.Errorf("subscriber failed to execute topic [%s]: %s", event.Topic, err.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
22
server/pkg/i18n/ctx.go
Normal file
22
server/pkg/i18n/ctx.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package i18n
|
||||
|
||||
import "context"
|
||||
|
||||
type CtxKey string
|
||||
|
||||
const (
|
||||
LangKey CtxKey = "lang"
|
||||
)
|
||||
|
||||
// NewCtxWithLang 将lang放置context中
|
||||
func NewCtxWithLang(ctx context.Context, lang string) context.Context {
|
||||
return context.WithValue(ctx, LangKey, lang)
|
||||
}
|
||||
|
||||
// GetLangFromCtx 从context中获取lang
|
||||
func GetLangFromCtx(ctx context.Context) string {
|
||||
if val, ok := ctx.Value(LangKey).(string); ok {
|
||||
return val
|
||||
}
|
||||
return ""
|
||||
}
|
||||
68
server/pkg/i18n/i18n.go
Normal file
68
server/pkg/i18n/i18n.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
)
|
||||
|
||||
type MsgId int
|
||||
|
||||
const (
|
||||
Zh_CN = "zh-cn"
|
||||
En = "en"
|
||||
)
|
||||
|
||||
// langMsgs key=lang, value=msgs
|
||||
var langMsgs = make(map[string]map[MsgId]string)
|
||||
|
||||
var defaultLang = Zh_CN
|
||||
|
||||
// AppendLangMsg append lang msg
|
||||
func AppendLangMsg(lang string, msgs map[MsgId]string) {
|
||||
langMsgs[lang] = collx.MapMerge(langMsgs[lang], msgs)
|
||||
}
|
||||
|
||||
// SetLang set default lang
|
||||
func SetLang(lang string) {
|
||||
defaultLang = lang
|
||||
}
|
||||
|
||||
// T load msg by key, and use default lang
|
||||
//
|
||||
// // NameErr = {{.name}} is invalid => xxx is invalid
|
||||
// T(imsg.NameErr, "name", "xxxx")
|
||||
func T(msgId MsgId, attrs ...any) string {
|
||||
return TL(defaultLang, msgId, attrs...)
|
||||
}
|
||||
|
||||
// TC load msg by key, and use context lang
|
||||
func TC(ctx context.Context, msgId MsgId, attrs ...any) string {
|
||||
return TL(GetLangFromCtx(ctx), msgId, attrs...)
|
||||
}
|
||||
|
||||
// TL load msg by lang
|
||||
//
|
||||
// // NameErr = {{.name}} is invalid => xxx is invalid
|
||||
// T(imsg.NameErr, "name", "xxxx")
|
||||
func TL(lang string, msgId MsgId, attrs ...any) string {
|
||||
if lang == "" {
|
||||
lang = defaultLang
|
||||
}
|
||||
|
||||
msgs := langMsgs[lang]
|
||||
if msgs == nil {
|
||||
msgs = langMsgs[defaultLang]
|
||||
}
|
||||
|
||||
msg := msgs[msgId]
|
||||
if len(attrs) == 0 {
|
||||
return msg
|
||||
}
|
||||
|
||||
if parseMsg, err := stringx.TemplateParse(msg, collx.Kvs(attrs...)); err != nil {
|
||||
return msg
|
||||
} else {
|
||||
return parseMsg
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func (c *Container) Inject(obj any) error {
|
||||
|
||||
// 对所有组件实例执行Inject。即为实例字段注入依赖的组件实例
|
||||
func (c *Container) InjectComponents() error {
|
||||
componentsGroups := collx.ArraySplit[*Component](collx.MapValues(c.components), 3)
|
||||
componentsGroups := collx.ArraySplit[*Component](collx.MapValues(c.components), 5)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/runtimex"
|
||||
@@ -25,16 +26,26 @@ type LogInfo struct {
|
||||
save bool // 是否保存日志
|
||||
}
|
||||
|
||||
// 新建日志信息,默认不保存该日志
|
||||
// NewLog 新建日志信息,默认不保存该日志
|
||||
func NewLog(description string) *LogInfo {
|
||||
return &LogInfo{Description: description, LogResp: false, save: false}
|
||||
}
|
||||
|
||||
// 新建日志信息,并且需要保存该日志信息
|
||||
// NewLogI 使用msgId新建日志信息,默认不保存该日志
|
||||
func NewLogI(msgId i18n.MsgId) *LogInfo {
|
||||
return &LogInfo{Description: i18n.T(msgId), LogResp: false, save: false}
|
||||
}
|
||||
|
||||
// NewLogSave 新建日志信息,并且需要保存该日志信息
|
||||
func NewLogSave(description string) *LogInfo {
|
||||
return &LogInfo{Description: description, LogResp: false, save: true}
|
||||
}
|
||||
|
||||
// NewLogSaveI 使用msgId新建日志信息,并且需要保存该日志信息
|
||||
func NewLogSaveI(msgId i18n.MsgId) *LogInfo {
|
||||
return &LogInfo{Description: i18n.T(msgId), LogResp: false, save: true}
|
||||
}
|
||||
|
||||
// 记录返回结果
|
||||
func (i *LogInfo) WithLogResp() *LogInfo {
|
||||
i.LogResp = true
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/assert"
|
||||
@@ -28,7 +29,10 @@ type Ctx struct {
|
||||
}
|
||||
|
||||
func NewCtx(f F) *Ctx {
|
||||
ctx := &Ctx{MetaCtx: contextx.WithTraceId(f.GetRequest().Context())}
|
||||
metaCtx := contextx.WithTraceId(f.GetRequest().Context())
|
||||
metaCtx = i18n.NewCtxWithLang(metaCtx, f.GetRequest().Header.Get("Accept-Language"))
|
||||
|
||||
ctx := &Ctx{MetaCtx: metaCtx}
|
||||
ctx.wrapperF = NewWrapperF(f)
|
||||
return ctx
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/pkg/config"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/req"
|
||||
"net/http"
|
||||
@@ -28,6 +29,8 @@ func runWebServer(ctx context.Context) {
|
||||
// 设置日志保存函数
|
||||
req.SetSaveLogFunc(sysapp.GetSyslogApp().SaveFromReq)
|
||||
|
||||
i18n.SetLang(config.Conf.Server.Lang)
|
||||
|
||||
srv := http.Server{
|
||||
Addr: config.Conf.Server.GetPort(),
|
||||
// 注册路由
|
||||
@@ -41,7 +44,7 @@ func runWebServer(ctx context.Context) {
|
||||
defer cancel()
|
||||
err := srv.Shutdown(timeout)
|
||||
if err != nil {
|
||||
logx.Errorf("Failed to Shutdown HTTP Server: %v", err)
|
||||
logx.Errorf("failed to Shutdown HTTP Server: %v", err)
|
||||
}
|
||||
|
||||
initialize.Terminate()
|
||||
|
||||
Reference in New Issue
Block a user