Compare commits
5 Commits
main
...
feat-cjy-t
| Author | SHA1 | Date | |
|---|---|---|---|
| c8d7c55f57 | |||
| b736d2d505 | |||
| 24ac1abeab | |||
| a707b58e5a | |||
| a95fdcb2d2 |
1
go.mod
1
go.mod
@ -176,6 +176,7 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/mozillazg/go-pinyin v0.21.0 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/nxadm/tail v1.4.11 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@ -678,6 +678,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/mozillazg/go-pinyin v0.21.0 h1:Wo8/NT45z7P3er/9YSLHA3/kjZzbLz5hR7i+jGeIGao=
|
||||
github.com/mozillazg/go-pinyin v0.21.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
|
||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
|
||||
@ -457,6 +457,9 @@ func GetWorkAnalysis(ctx *gin.Context) {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
if locale := utils.ParseLocaleHeader(ctx.GetHeader("Accept-Language")); locale != "" {
|
||||
resp.Title = utils.ConvertTitleByLocale(resp.Title, locale)
|
||||
}
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
}
|
||||
@ -546,6 +549,11 @@ func ListWorkAnalysisForApp(ctx *gin.Context) {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
if locale := utils.ParseLocaleHeader(ctx.GetHeader("Accept-Language")); locale != "" {
|
||||
for _, item := range resp.Data {
|
||||
item.Title = utils.ConvertTitleByLocale(item.Title, locale)
|
||||
}
|
||||
}
|
||||
// RefreshWorkAnalysisApproval(ctx, resp.Data)
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
@ -1105,6 +1113,11 @@ func TobeConfirmedList(ctx *gin.Context) {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
if locale := utils.ParseLocaleHeader(ctx.GetHeader("Accept-Language")); locale != "" {
|
||||
for _, item := range resp.Data {
|
||||
item.Title = utils.ConvertTitleByLocale(item.Title, locale)
|
||||
}
|
||||
}
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
}
|
||||
|
||||
@ -784,6 +784,9 @@ func GetCompetitiveReportForApp(ctx *gin.Context) {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
if locale := utils.ParseLocaleHeader(ctx.GetHeader("Accept-Language")); locale != "" {
|
||||
resp.Title = utils.ConvertTitleByLocale(resp.Title, locale)
|
||||
}
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
}
|
||||
@ -884,6 +887,11 @@ func ListReportByArtistUuidForApp(ctx *gin.Context) {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
if locale := utils.ParseLocaleHeader(ctx.GetHeader("Accept-Language")); locale != "" {
|
||||
for _, item := range resp.Data {
|
||||
item.Title = utils.ConvertTitleByLocale(item.Title, locale)
|
||||
}
|
||||
}
|
||||
// RefreshCompetitiveReportApproval(ctx, resp.Data)
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
|
||||
181
pkg/utils/reportname.go
Normal file
181
pkg/utils/reportname.go
Normal file
@ -0,0 +1,181 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mozillazg/go-pinyin"
|
||||
)
|
||||
|
||||
// reportTypeTranslations locale 到 数据报告/竞品报告 翻译的映射
|
||||
var reportTypeTranslations = map[string]map[string]string{
|
||||
"zh-CN": {
|
||||
"数据报告": "数据报告",
|
||||
"竞品报告": "竞品报告",
|
||||
},
|
||||
"zh-TW": {
|
||||
"数据报告": "數據報告",
|
||||
"竞品报告": "競合報告",
|
||||
},
|
||||
"EN": {
|
||||
"数据报告": "Data Report",
|
||||
"竞品报告": "Competitor Report",
|
||||
},
|
||||
"de-DE": {
|
||||
"数据报告": "Datenbericht",
|
||||
"竞品报告": "Wettbewerbsanalyse",
|
||||
},
|
||||
"ja-JP": {
|
||||
"数据报告": "データレポート",
|
||||
"竞品报告": "競合分析レポート",
|
||||
},
|
||||
}
|
||||
|
||||
// pinyinTitles 中文称谓"老师"对应的拼音形式(用于检测)
|
||||
var pinyinTitles = []string{"lao shi", "laoshi"}
|
||||
|
||||
// ConvertNameToPinyin 将中文姓名转换为拼音,单词首字母大写,单词间用空格分隔。
|
||||
// 对于非中文 locale,会去除"老师"称谓。
|
||||
// 示例:"王旗老师" -> "Wang Qi"
|
||||
func ConvertNameToPinyin(name string) string {
|
||||
args := pinyin.NewArgs()
|
||||
slices := pinyin.Pinyin(name, args)
|
||||
|
||||
var result []string
|
||||
for _, s := range slices {
|
||||
if len(s) > 0 {
|
||||
// 首字母大写
|
||||
runes := []rune(s[0])
|
||||
if len(runes) > 0 {
|
||||
uppered := string([]rune{runes[0] - 32}) + string(runes[1:])
|
||||
result = append(result, uppered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pinyinName := joinWithSpace(result)
|
||||
|
||||
// 对于非中文 locale,去除拼音 "Lao Shi"
|
||||
lowerName := strings.ToLower(pinyinName)
|
||||
for _, pt := range pinyinTitles {
|
||||
if strings.HasSuffix(lowerName, pt) {
|
||||
// 提取去掉称谓后的姓名部分
|
||||
nameWithoutTitle := strings.TrimSuffix(pinyinName, " Lao Shi")
|
||||
nameWithoutTitle = strings.TrimSuffix(nameWithoutTitle, " Laoshi")
|
||||
return nameWithoutTitle
|
||||
}
|
||||
}
|
||||
|
||||
return pinyinName
|
||||
}
|
||||
|
||||
// joinWithSpace 用空格连接字符串,过滤空字符串
|
||||
func joinWithSpace(parts []string) string {
|
||||
var nonEmpty []string
|
||||
for _, p := range parts {
|
||||
if p != "" {
|
||||
nonEmpty = append(nonEmpty, p)
|
||||
}
|
||||
}
|
||||
if len(nonEmpty) == 0 {
|
||||
return ""
|
||||
}
|
||||
result := nonEmpty[0]
|
||||
for i := 1; i < len(nonEmpty); i++ {
|
||||
result += " " + nonEmpty[i]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ConvertReportName 将报告名转换为指定语言版本。
|
||||
// prefix: YYYY-MM 格式,如 "2026-03"
|
||||
// personName: 中文姓名,如 "王旗老师"
|
||||
// reportType: "数据报告" 或 "竞品报告"
|
||||
// locale: "zh-CN", "zh-TW", "EN", "de-DE", 或 "ja-JP"
|
||||
func ConvertReportName(prefix, personName, reportType, locale string) string {
|
||||
reportTypeTrans, ok := reportTypeTranslations[locale]
|
||||
if !ok {
|
||||
reportTypeTrans = reportTypeTranslations["zh-CN"]
|
||||
}
|
||||
|
||||
translatedReportType, ok := reportTypeTrans[reportType]
|
||||
if !ok {
|
||||
translatedReportType = reportType
|
||||
}
|
||||
|
||||
// 对于中文 locale(zh-CN, zh-TW),保持原样
|
||||
if locale == "zh-CN" || locale == "zh-TW" {
|
||||
displayName := personName
|
||||
if locale == "zh-TW" {
|
||||
// zh-TW 去掉"老师",繁体报告类型已通过 translatedReportType 获取
|
||||
displayName = strings.ReplaceAll(personName, "老师", "")
|
||||
}
|
||||
return fmt.Sprintf("%s%s%s", prefix, displayName, translatedReportType)
|
||||
}
|
||||
|
||||
// 对于其他 locale,转换为拼音
|
||||
pinyinName := ConvertNameToPinyin(personName)
|
||||
return fmt.Sprintf("%s %s %s", prefix, pinyinName, translatedReportType)
|
||||
}
|
||||
|
||||
// isValidPrefix 校验 prefix 是否为 YYYY-MM 格式(年份和月份都是数字)
|
||||
func isValidPrefix(prefix string) bool {
|
||||
if len(prefix) != 7 || prefix[4] != '-' {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < 4; i++ {
|
||||
if prefix[i] < '0' || prefix[i] > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if prefix[5] < '0' || prefix[5] > '9' || prefix[6] < '0' || prefix[6] > '9' {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ConvertTitleByLocale 将报告标题根据 locale 进行翻译转换。
|
||||
// title: 原始中文标题,格式为 "YYYY-MM" + 姓名 + "数据报告"/"竞品报告",如 "2026-03王旗老师数据报告"
|
||||
// locale: "zh-CN", "zh-TW", "EN", "de-DE", "ja-JP" 或其他 Accept-Language 值
|
||||
// 返回翻译后的标题,如果格式不符则返回原标题。
|
||||
func ConvertTitleByLocale(title, locale string) string {
|
||||
if len(title) < 7 {
|
||||
return title
|
||||
}
|
||||
|
||||
prefix := title[:7] // YYYY-MM
|
||||
if !isValidPrefix(prefix) {
|
||||
return title
|
||||
}
|
||||
|
||||
var reportType string
|
||||
if strings.HasSuffix(title, "数据报告") {
|
||||
reportType = "数据报告"
|
||||
} else if strings.HasSuffix(title, "竞品报告") {
|
||||
reportType = "竞品报告"
|
||||
} else {
|
||||
return title
|
||||
}
|
||||
|
||||
personName := title[7 : len(title)-len(reportType)]
|
||||
|
||||
return ConvertReportName(prefix, personName, reportType, locale)
|
||||
}
|
||||
|
||||
// ParseLocaleHeader 解析 Accept-Language 请求头,返回规范化的 locale 字符串。
|
||||
// zh-CN / zh / ZhCN 无需翻译,返回 "";其余值规范化后返回,不支持的语言同样返回 ""。
|
||||
func ParseLocaleHeader(header string) string {
|
||||
if header == "zh-CN" || header == "zh" || header == "ZhCN" {
|
||||
return ""
|
||||
}
|
||||
switch header {
|
||||
case "zh-TW", "ZhTW":
|
||||
return "zh-TW"
|
||||
case "en", "EN":
|
||||
return "EN"
|
||||
case "de-DE", "ja-JP":
|
||||
return header
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
274
pkg/utils/reportname_test.go
Normal file
274
pkg/utils/reportname_test.go
Normal file
@ -0,0 +1,274 @@
|
||||
package utils
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestIsValidPrefix(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
prefix string
|
||||
expected bool
|
||||
}{
|
||||
{name: "valid 2026-03", prefix: "2026-03", expected: true},
|
||||
{name: "valid 2026-01", prefix: "2026-01", expected: true},
|
||||
{name: "valid 1999-12", prefix: "1999-12", expected: true},
|
||||
{name: "invalid too short", prefix: "2026-3", expected: false},
|
||||
{name: "invalid too long", prefix: "2026-003", expected: false},
|
||||
{name: "invalid no dash", prefix: "202603", expected: false},
|
||||
{name: "invalid letters in year", prefix: "ab-03", expected: false},
|
||||
{name: "invalid letters in month", prefix: "2026-ab", expected: false},
|
||||
{name: "invalid month > 12", prefix: "2026-13", expected: true}, // 只校验格式,不校验月份合理性
|
||||
{name: "empty string", prefix: "", expected: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := isValidPrefix(tt.prefix)
|
||||
if result != tt.expected {
|
||||
t.Errorf("isValidPrefix(%q) = %v, want %v", tt.prefix, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertTitleByLocale(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
title string
|
||||
locale string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "EN Data Report",
|
||||
title: "2026-03王旗老师数据报告",
|
||||
locale: "EN",
|
||||
expected: "2026-03 Wang Qi Data Report",
|
||||
},
|
||||
{
|
||||
name: "EN Competitor Report",
|
||||
title: "2026-03王旗老师竞品报告",
|
||||
locale: "EN",
|
||||
expected: "2026-03 Wang Qi Competitor Report",
|
||||
},
|
||||
{
|
||||
name: "zh-TW Data Report",
|
||||
title: "2026-03王旗老师数据报告",
|
||||
locale: "zh-TW",
|
||||
expected: "2026-03王旗數據報告",
|
||||
},
|
||||
{
|
||||
name: "zh-TW Competitor Report",
|
||||
title: "2026-03王旗老师竞品报告",
|
||||
locale: "zh-TW",
|
||||
expected: "2026-03王旗競合報告",
|
||||
},
|
||||
{
|
||||
name: "de-DE Data Report",
|
||||
title: "2026-03王旗老师数据报告",
|
||||
locale: "de-DE",
|
||||
expected: "2026-03 Wang Qi Datenbericht",
|
||||
},
|
||||
{
|
||||
name: "de-DE Competitor Report",
|
||||
title: "2026-03王旗老师竞品报告",
|
||||
locale: "de-DE",
|
||||
expected: "2026-03 Wang Qi Wettbewerbsanalyse",
|
||||
},
|
||||
{
|
||||
name: "ja-JP Data Report",
|
||||
title: "2026-03王旗老师数据报告",
|
||||
locale: "ja-JP",
|
||||
expected: "2026-03 Wang Qi データレポート",
|
||||
},
|
||||
{
|
||||
name: "ja-JP Competitor Report",
|
||||
title: "2026-03王旗老师竞品报告",
|
||||
locale: "ja-JP",
|
||||
expected: "2026-03 Wang Qi 競合分析レポート",
|
||||
},
|
||||
{
|
||||
name: "zh-CN returns original",
|
||||
title: "2026-03王旗老师数据报告",
|
||||
locale: "zh-CN",
|
||||
expected: "2026-03王旗老师数据报告",
|
||||
},
|
||||
{
|
||||
name: "invalid prefix returns original",
|
||||
title: "202603王旗老师数据报告",
|
||||
locale: "EN",
|
||||
expected: "202603王旗老师数据报告",
|
||||
},
|
||||
{
|
||||
name: "personName with digit translates Chinese chars only",
|
||||
title: "2026-03王旗1老师数据报告",
|
||||
locale: "EN",
|
||||
expected: "2026-03 Wang Qi Data Report",
|
||||
},
|
||||
{
|
||||
name: "personName with leading English drops non-Chinese chars",
|
||||
title: "2026-03wang老师数据报告",
|
||||
locale: "EN",
|
||||
expected: "2026-03 Lao Shi Data Report",
|
||||
},
|
||||
{
|
||||
name: "title too short returns original",
|
||||
title: "2026-03",
|
||||
locale: "EN",
|
||||
expected: "2026-03",
|
||||
},
|
||||
{
|
||||
name: "unknown reportType returns original",
|
||||
title: "2026-03王旗老师其他报告",
|
||||
locale: "EN",
|
||||
expected: "2026-03王旗老师其他报告",
|
||||
},
|
||||
{
|
||||
name: "empty title returns original",
|
||||
title: "",
|
||||
locale: "EN",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := ConvertTitleByLocale(tt.title, tt.locale)
|
||||
if result != tt.expected {
|
||||
t.Errorf("ConvertTitleByLocale(%q, %q) = %q, want %q", tt.title, tt.locale, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertReportName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
prefix string // YYYY-MM
|
||||
personName string // 中文姓名
|
||||
reportType string // 数据报告 或 竞品报告
|
||||
locale string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "EN Data Report",
|
||||
prefix: "2026-03",
|
||||
personName: "王旗老师",
|
||||
reportType: "数据报告",
|
||||
locale: "EN",
|
||||
expected: "2026-03 Wang Qi Data Report",
|
||||
},
|
||||
{
|
||||
name: "EN Competitor Report",
|
||||
prefix: "2026-03",
|
||||
personName: "王旗老师",
|
||||
reportType: "竞品报告",
|
||||
locale: "EN",
|
||||
expected: "2026-03 Wang Qi Competitor Report",
|
||||
},
|
||||
{
|
||||
name: "zh-TW Data Report",
|
||||
prefix: "2026-03",
|
||||
personName: "王旗老师",
|
||||
reportType: "数据报告",
|
||||
locale: "zh-TW",
|
||||
expected: "2026-03王旗數據報告",
|
||||
},
|
||||
{
|
||||
name: "zh-TW Competitor Report",
|
||||
prefix: "2026-03",
|
||||
personName: "王旗老师",
|
||||
reportType: "竞品报告",
|
||||
locale: "zh-TW",
|
||||
expected: "2026-03王旗競合報告",
|
||||
},
|
||||
{
|
||||
name: "de-DE Data Report",
|
||||
prefix: "2026-03",
|
||||
personName: "王旗老师",
|
||||
reportType: "数据报告",
|
||||
locale: "de-DE",
|
||||
expected: "2026-03 Wang Qi Datenbericht",
|
||||
},
|
||||
{
|
||||
name: "de-DE Competitor Report",
|
||||
prefix: "2026-03",
|
||||
personName: "王旗老师",
|
||||
reportType: "竞品报告",
|
||||
locale: "de-DE",
|
||||
expected: "2026-03 Wang Qi Wettbewerbsanalyse",
|
||||
},
|
||||
{
|
||||
name: "ja-JP Data Report",
|
||||
prefix: "2026-03",
|
||||
personName: "王旗老师",
|
||||
reportType: "数据报告",
|
||||
locale: "ja-JP",
|
||||
expected: "2026-03 Wang Qi データレポート",
|
||||
},
|
||||
{
|
||||
name: "ja-JP Competitor Report",
|
||||
prefix: "2026-03",
|
||||
personName: "王旗老师",
|
||||
reportType: "竞品报告",
|
||||
locale: "ja-JP",
|
||||
expected: "2026-03 Wang Qi 競合分析レポート",
|
||||
},
|
||||
{
|
||||
name: "zh-CN Data Report (no conversion)",
|
||||
prefix: "2026-03",
|
||||
personName: "王旗老师",
|
||||
reportType: "数据报告",
|
||||
locale: "zh-CN",
|
||||
expected: "2026-03王旗老师数据报告",
|
||||
},
|
||||
{
|
||||
name: "zh-CN Competitor Report (no conversion)",
|
||||
prefix: "2026-03",
|
||||
personName: "王旗老师",
|
||||
reportType: "竞品报告",
|
||||
locale: "zh-CN",
|
||||
expected: "2026-03王旗老师竞品报告",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := ConvertReportName(tt.prefix, tt.personName, tt.reportType, tt.locale)
|
||||
if result != tt.expected {
|
||||
t.Errorf("ConvertReportName() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertNameToPinyin(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "simple name",
|
||||
input: "王旗",
|
||||
expected: "Wang Qi",
|
||||
},
|
||||
{
|
||||
name: "name with title",
|
||||
input: "王旗老师",
|
||||
expected: "Wang Qi",
|
||||
},
|
||||
{
|
||||
name: "two word name with title",
|
||||
input: "王小明老师",
|
||||
expected: "Wang Xiao Ming",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := ConvertNameToPinyin(tt.input)
|
||||
if result != tt.expected {
|
||||
t.Errorf("ConvertNameToPinyin() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user