admin_backend/model/language.go
2026-02-06 15:31:37 +08:00

343 lines
11 KiB
Go

package model
import (
"backend/common"
"backend/util"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/xuri/excelize/v2"
)
type Language struct {
Id int `json:"Id" db:"Id"`
Key string `json:"key" db:"key"`
EN_US string `json:"en_US" db:"en_US"`
ZH_CN string `json:"zh_CN" db:"zh_CN"`
PT_BR string `json:"pt_BR" db:"pt_BR"`
ES_LATAM string `json:"es_LATAM" db:"es_LATAM"`
Url string `json:"url"`
}
type LanguageOp struct {
Id int `json:"Id" db:"Id"`
LanguageId int `json:"LanguageId" db:"LanguageId"`
Field string `json:"Field" db:"Field"`
OldValue string `json:"OldValue" db:"OldValue"`
NewValue string `json:"NewValue" db:"NewValue"`
Type string `json:"Type" db:"Type"`
Update int `json:"Update" db:"Update"`
}
type LanguageMod struct {
PageSize int `json:"PageSize"`
CurrentPage int `json:"CurrentPage"`
StartTime int `json:"StartTime"`
EndTime int `json:"EndTime"`
SearchField string `json:"SearchField"`
SearchValue string `json:"SearchValue"`
}
func (l *LanguageMod) LanguageList(account string) (map[string]interface{}, error) {
Db := util.MPool.GetGameDB()
column := ""
defer Db.Close()
var languages []*Language
SearchSQL := ""
if l.SearchField != "" {
if l.SearchValue != "" {
escaped := strings.ReplaceAll(l.SearchValue, "'", "''")
SearchSQL = " `" + l.SearchField + "` like '%" + escaped + "%' "
} else {
SearchSQL = " `" + l.SearchField + "` = '' "
}
}
AccountConfig := common.GetTranlaterConfig(account)
if AccountConfig != nil {
column = AccountConfig.Colunm
keysArr := strings.Split(AccountConfig.Keys, ",")
if len(keysArr) > 0 {
keyConditions := make([]string, len(keysArr))
for i, key := range keysArr {
escapedKey := strings.ReplaceAll(strings.TrimSpace(key), "'", "''")
keyConditions[i] = fmt.Sprintf("`key` like '%%%s%%'", escapedKey)
}
if SearchSQL != "" {
SearchSQL += " AND "
}
if AccountConfig.KeysType == "no_include" {
SearchSQL += " NOT (" + strings.Join(keyConditions, " or ") + ") "
} else {
SearchSQL += " (" + strings.Join(keyConditions, " or ") + ") "
}
}
}
if SearchSQL != "" {
SearchSQL = " where " + SearchSQL
}
sql := fmt.Sprintf("SELECT `Id`, `key`, `en_US`, `zh_CN`, `pt_BR`, `es_LATAM` FROM language %s LIMIT ?, ?", SearchSQL)
err := Db.Select(&languages, sql, (l.CurrentPage-1)*l.PageSize, l.PageSize)
if err != nil {
return nil, err
}
var languageOp []*LanguageOp
err = Db.Select(&languageOp, "SELECT `Id`, `LanguageId`, `Field`, `OldValue`, `NewValue`, `Type`, `Update` FROM language_op where `Update` >= ? and `Update` <= ? ", l.StartTime, l.EndTime)
if err != nil {
return nil, err
}
var total int
err = Db.QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM language %s", SearchSQL)).Scan(&total)
if err != nil {
return nil, err
}
for i := range languages {
url := util.GetLanguageImageURL(languages[i].Key)
if url != "" {
//url = "../../../../public/" + url
url = "./" + url
}
languages[i].Url = url
}
return map[string]interface{}{
"data": languages,
"op": languageOp,
"total": total,
"column": column,
}, nil
}
func (l *LanguageMod) LanguageListAll() (map[string]interface{}, error) {
Db := util.MPool.GetGameDB()
defer Db.Close()
var languages []*Language
err := Db.Select(&languages, "SELECT `Id`, `key`, `en_US`, `zh_CN`,`pt_BR`, `es_LATAM` FROM language")
if err != nil {
return nil, err
}
var total int
err = Db.QueryRow("SELECT COUNT(*) FROM language").Scan(&total)
if err != nil {
return nil, err
}
// 导出为 xlsx 文件
f := excelize.NewFile()
mainSheet := "client"
backendSheet := "backend"
// 创建主表
if _, err := f.NewSheet(mainSheet); err != nil {
return nil, err
}
// 创建 backend 子表
if _, err := f.NewSheet(backendSheet); err != nil {
return nil, err
}
SaveTime, _ := util.GetLanguageLastUpdate()
ExportTime, _ := util.GetLanguageExportLastUpdate()
if SaveTime <= ExportTime {
return map[string]interface{}{
"code": 1,
"msg": "No changes since last export",
}, nil
}
// 写表头(在主表和 backend 表都写一份)
headers := []string{"Id", "key", "en_US", "zh_CN", "pt_BR", "es_LATAM"}
cols := []string{"A", "B", "C", "D", "E", "F"}
for i, h := range headers {
if err := f.SetCellValue(mainSheet, cols[i]+"1", h); err != nil {
return nil, err
}
if err := f.SetCellValue(backendSheet, cols[i]+"1", h); err != nil {
return nil, err
}
}
headers2 := []string{"编号", "键", "英文", "简体中文", "葡萄牙语(巴西)", "西班牙语(拉丁美洲)"}
for i, h := range headers2 {
if err := f.SetCellValue(mainSheet, cols[i]+"2", h); err != nil {
return nil, err
}
if err := f.SetCellValue(backendSheet, cols[i]+"2", h); err != nil {
return nil, err
}
}
// 写数据行:分别维护主表和 backend 表的行号
mainRowIndex := 3
backendRowIndex := 3
for _, lang := range languages {
if strings.HasPrefix(lang.Key, "backend") {
row := fmt.Sprintf("%d", backendRowIndex)
if err := f.SetCellValue(backendSheet, "A"+row, lang.Id); err != nil {
return nil, err
}
if err := f.SetCellValue(backendSheet, "B"+row, lang.Key); err != nil {
return nil, err
}
if err := f.SetCellValue(backendSheet, "C"+row, lang.EN_US); err != nil {
return nil, err
}
if err := f.SetCellValue(backendSheet, "D"+row, lang.ZH_CN); err != nil {
return nil, err
}
if err := f.SetCellValue(backendSheet, "E"+row, lang.PT_BR); err != nil {
return nil, err
}
if err := f.SetCellValue(backendSheet, "F"+row, lang.ES_LATAM); err != nil {
return nil, err
}
backendRowIndex++
} else {
row := fmt.Sprintf("%d", mainRowIndex)
if err := f.SetCellValue(mainSheet, "A"+row, lang.Id); err != nil {
return nil, err
}
if err := f.SetCellValue(mainSheet, "B"+row, lang.Key); err != nil {
return nil, err
}
if err := f.SetCellValue(mainSheet, "C"+row, lang.EN_US); err != nil {
return nil, err
}
if err := f.SetCellValue(mainSheet, "D"+row, lang.ZH_CN); err != nil {
return nil, err
}
if err := f.SetCellValue(mainSheet, "E"+row, lang.PT_BR); err != nil {
return nil, err
}
if err := f.SetCellValue(mainSheet, "F"+row, lang.ES_LATAM); err != nil {
return nil, err
}
mainRowIndex++
}
}
// 在 /data/docs 仓库中添加并提交该文件
repoDir := "/data/docs"
cmd := exec.Command("git", "checkout", "main")
cmd.Dir = repoDir
if _, err := cmd.CombinedOutput(); err != nil {
// try to fetch and force-create/reset local main from origin/main
fetch := exec.Command("git", "fetch", "origin", "main")
fetch.Dir = repoDir
if fout, ferr := fetch.CombinedOutput(); ferr != nil {
return nil, fmt.Errorf("git fetch failed: %v: %s", ferr, string(fout))
}
cmd = exec.Command("git", "checkout", "-B", "main", "origin/main")
cmd.Dir = repoDir
if out2, err2 := cmd.CombinedOutput(); err2 != nil {
return nil, fmt.Errorf("git checkout main failed: %v: %s", err2, string(out2))
}
}
cmd = exec.Command("git", "pull")
cmd.Dir = repoDir
if out, err := cmd.CombinedOutput(); err != nil {
return nil, fmt.Errorf("git pull failed: %v: %s", err, string(out))
}
// 保存到临时文件
// 保存到指定目录
pathDir := "/data/docs/config"
if err := os.MkdirAll(pathDir, 0755); err != nil {
return nil, err
}
path := filepath.Join(pathDir, "AllLanguage.xlsx")
if err := f.SaveAs(path); err != nil {
return nil, err
}
relPath, err := filepath.Rel(repoDir, path)
if err != nil {
return nil, err
}
cmd = exec.Command("git", "add", relPath)
cmd.Dir = repoDir
if out, err := cmd.CombinedOutput(); err != nil {
return nil, fmt.Errorf("git add failed: %v: %s", err, string(out))
}
commitMsg := fmt.Sprintf("Export AllLanguage.xlsx at %s", time.Now().Format(time.RFC3339))
cmd = exec.Command("git", "commit", "-m", commitMsg)
cmd.Dir = repoDir
if out, err := cmd.CombinedOutput(); err != nil {
// 忽略 "nothing to commit" 情形
if !strings.Contains(string(out), "nothing to commit") {
return nil, fmt.Errorf("git commit failed: %v: %s", err, string(out))
}
}
cmd = exec.Command("git", "push", "origin", "main")
cmd.Dir = repoDir
if out, err := cmd.CombinedOutput(); err != nil {
return nil, fmt.Errorf("git push failed: %v: %s", err, string(out))
}
util.SaveLanguageExportLastUpdate(util.Now())
// 将文件路径返回到调用方
return map[string]interface{}{
// "data": languages,
"total": total,
}, nil
}
func (l *LanguageMod) LanguageSave(langList []LanguageOp, admin string) error {
Db := util.MPool.GetGameDB()
defer Db.Close()
for _, lang := range langList {
_, err := Db.Exec("UPDATE language SET `"+lang.Field+"` = ? WHERE Id = ?", lang.NewValue, lang.LanguageId)
if err != nil {
return err
}
_, err = Db.Exec("insert into language_op (`LanguageId`, `Field`, `OldValue`, `NewValue`, `Type`, `Update`, `Role`) values (?, ?, ?, ?, ?,?,?)", lang.LanguageId, lang.Field, lang.OldValue, lang.NewValue, lang.Type, util.Now(), admin)
if err != nil {
return err
}
}
// 删除 key 包含 "obsolete" 的记录
if _, err := Db.Exec("DELETE FROM language WHERE `key` LIKE ?", "%obsolete%"); err != nil {
return err
}
util.SaveLanguageLastUpdate(util.Now())
return nil
}
func (l *LanguageMod) LanguageAdd(langList []Language, admin string) error {
Db := util.MPool.GetGameDB()
defer Db.Close()
for _, lang := range langList {
res, err := Db.Exec("INSERT INTO language (`key`, `en_US`, `zh_CN`, `pt_BR`) VALUES (?, ?, ?, ?)", lang.Key, lang.EN_US, lang.ZH_CN, lang.PT_BR)
if err != nil {
return err
}
lastId, err := res.LastInsertId()
if err != nil {
return err
}
lang.Id = int(lastId)
_, err = Db.Exec("insert into language_op (`LanguageId`, `Field`, `OldValue`, `NewValue`, `Type`, `Update`, `Role`) values (?, ?, ?, ?, ?,?,?)", lang.Id, "All", "", lang.EN_US+"|"+lang.ZH_CN+"|"+lang.PT_BR, "Add", util.Now(), admin)
if err != nil {
return err
}
}
util.SaveLanguageLastUpdate(util.Now())
return nil
}
func (l *LanguageMod) LanguageDelete(key string, admin string) error {
Db := util.MPool.GetGameDB()
defer Db.Close()
var lang Language
err := Db.Get(&lang, "SELECT `Id`, `key`, `en_US`, `zh_CN`, `pt_BR` FROM language WHERE `key` = ?", key)
if err != nil {
return err
}
_, err = Db.Exec("DELETE FROM language WHERE `key` = ?", key)
if err != nil {
return err
}
_, err = Db.Exec("insert into language_op (`LanguageId`, `Field`, `OldValue`, `NewValue`, `Type`, `Update`, `Role`) values (?, ?, ?, ?, ?,?,?)", lang.Id, "All", lang.EN_US+"|"+lang.ZH_CN+"|"+lang.PT_BR, "", "Delete", util.Now(), admin)
if err != nil {
return err
}
util.SaveLanguageLastUpdate(util.Now())
return nil
}