291 lines
8.2 KiB
Go
Raw Normal View History

2025-07-17 17:09:09 +08:00
package smdl
import (
"admin/apps/game/domain/entity"
dto2 "admin/internal/model/dto"
"admin/lib/xlog"
"encoding/base64"
2025-07-22 09:37:37 +08:00
"encoding/json"
"fmt"
2025-07-17 17:09:09 +08:00
"strconv"
2025-07-22 09:37:37 +08:00
"strings"
"time"
2025-07-17 17:09:09 +08:00
)
type GameLogHook struct {
}
var removeFields = map[string]struct{}{
"pub_mediaid": {},
"pub_role_name": {},
"pub_udid": {},
}
2025-07-22 09:37:37 +08:00
var fieldsAlias = map[string]string{
2025-07-17 17:09:09 +08:00
"pub_viplev": "vip等级",
"pub_userid": "账号",
"pub_totalcash": "总充值金额",
"pub_serverid": "服务器id",
"pub_rolename": "角色名",
"pub_roleid": "角色id",
"pub_lev": "等级",
"pub_language": "语言",
"pub_ip": "IP",
}
var base64decodeFields = map[string]struct{}{
"pub_rolename": {},
"chatlog_msg": {},
}
func init() {
for i := 0; i < 15; i++ {
removeFields["chatlog_score"] = struct{}{}
removeFields["chatlog_score"+strconv.Itoa(i)] = struct{}{}
removeFields["chatlog_label"] = struct{}{}
removeFields["chatlog_label"+strconv.Itoa(i)] = struct{}{}
removeFields["chatlog_checklevel"] = struct{}{}
removeFields["chatlog_checklevel"+strconv.Itoa(i)] = struct{}{}
}
removeFields["chatlog_code"] = struct{}{}
removeFields["chatlog_checkresult"] = struct{}{}
}
func (hook *GameLogHook) Trim(projectInfo *entity.Project, eventName []string, totalCount int, fieldsDescInfo []*dto2.GameLogFieldInfo, rows [][]any) (
int, []*dto2.GameLogFieldInfo, [][]any) {
// 删除不需要的字段
i := 0
for {
if i >= len(fieldsDescInfo) {
break
}
field := fieldsDescInfo[i]
if _, find := removeFields[field.Name]; find {
// 找到这个字段在去掉的列表里
fieldsDescInfo = append(fieldsDescInfo[:i], fieldsDescInfo[i+1:]...)
// 对应所有数据行里也删除这个值
2025-07-22 09:37:37 +08:00
for rowI, row := range rows {
2025-07-17 17:09:09 +08:00
row = append(row[:i], row[i+1:]...)
2025-07-22 09:37:37 +08:00
rows[rowI] = row
2025-07-17 17:09:09 +08:00
}
} else {
// 查找要不要把value做base64解码
if _, find := base64decodeFields[field.Name]; find {
for _, row := range rows {
2025-07-22 09:37:37 +08:00
newValue, err := base64.StdEncoding.DecodeString(row[i].(string))
2025-07-17 17:09:09 +08:00
if err != nil {
xlog.Warnf("base64 decode field:%v value:%v error:%v", field.Name, row[i], err)
} else {
row[i] = string(newValue)
}
}
}
i++
}
}
2025-07-22 09:37:37 +08:00
//fBin, _ := json.Marshal(&fieldsDescInfo)
//rBin, _ := json.Marshal(&rows)
//xlog.Tracef("gamelog1 query result:%v, rows:%v", string(fBin), string(rBin))
fieldsDescInfo, rows = (&queryResultInfo{fields: fieldsDescInfo, rows: rows}).tidyByEventDescInfo(eventName)
//fBin, _ = json.Marshal(&fieldsDescInfo)
//rBin, _ = json.Marshal(&rows)
//xlog.Tracef("gamelog1 query result:%v, rows:%v", string(fBin), string(rBin))
for i, f := range fieldsDescInfo {
// 修改每一行日志别名
if f.Name == "xwl_part_event" {
for j := range rows {
opEventName := rows[j][i].(string)
descInfo, find := globEventList[opEventName]
if find {
rows[j][i] = descInfo.Alias
}
}
} else if f.Name == "xwl_part_date" {
for j := range rows {
eventDate := rows[j][i].(time.Time)
//eventDate, err := time.Parse("2006-01-02T15:04:05+07:00", eventDateString)
rows[j][i] = eventDate.Format(time.DateTime)
}
}
// 修改每一行公共属性别名
alias, find := fieldsAlias[f.Name]
if find && f.Name == f.Alias {
f.Alias = alias
}
}
fBin, _ := json.Marshal(&fieldsDescInfo)
rBin, _ := json.Marshal(&rows)
xlog.Tracef("gamelog2 query result:%v, rows:%v", string(fBin), string(rBin))
i = 0
for {
if i >= len(fieldsDescInfo) {
break
}
f := fieldsDescInfo[i]
swapI := 2
if f.Name == "pub_serverid" || f.Name == "pub_roleid" || f.Name == "pub_rolename" {
pubF := fieldsDescInfo[i]
for moveI := i; moveI > swapI; moveI-- {
fieldsDescInfo[moveI] = fieldsDescInfo[moveI-1]
}
fieldsDescInfo[swapI] = pubF
for _, row := range rows {
pubValue := row[i]
for moveI := i; moveI > swapI; moveI-- {
row[moveI] = row[moveI-1]
}
row[swapI] = pubValue
}
swapI++
}
i++
}
return totalCount, fieldsDescInfo, rows
}
type queryResultInfo struct {
fields []*dto2.GameLogFieldInfo
rows [][]any
}
func (info *queryResultInfo) tidyByEventDescInfo(eventNameList []string) ([]*dto2.GameLogFieldInfo, [][]any) {
if len(eventNameList) == 0 {
return info.fields, info.rows
}
if len(eventNameList) == 1 {
en := eventNameList[0]
enDescInfo, find := globEventList[en]
if !find {
panic(en)
}
for _, f := range info.fields {
for _, df := range enDescInfo.fields {
fName := removeFieldEventName(en, f.Name)
if fName == df.Name && f.Name == f.Alias {
f.Alias = df.Alias
break
}
}
}
return info.fields, info.rows
}
// 神魔的埋点里字段名是日志名_字段名所以如果一次游戏日志查询包含多个事件名会查出稀疏数据
// 例如gainitem,loseitem两个日志都会存在gainitem_itemid,loseitem_itemid需要把对应
// 为0的稀疏字段去掉
// 具体方案:
// 1.创建新的行数据,将自己日志的字段值先追加进来,然后前面补上日志名,后面补上公共字段,
// 2.字段描述信息直接全部去重
newRows := make([][]any, len(info.rows))
for rowI, row := range info.rows {
curRowEventName := row[0].(string)
enDescInfo, find := globEventList[curRowEventName]
if !find {
// 不存在描述信息,先崩溃下
panic(curRowEventName)
}
for _, fieldDesc := range enDescInfo.fields {
for i, field := range info.fields {
fName := removeFieldEventName(curRowEventName, field.Name)
if fName == fieldDesc.Name {
newRows[rowI] = append(newRows[rowI], row[i])
break
}
}
}
}
// 上面步骤走完newRows每一行都是各自日志的字段值了但是每一行长度可能不一样例如gainitem就比loseitem多一个字段
// 下面步骤会把字段描述信息通过事件名具有的所有字段去重追加例如gainitem就是全量的字段loseitem就是子集
newFieldsDescInfo := make([]*dto2.GameLogFieldInfo, 0, len(info.fields))
for _, en := range eventNameList {
enDescInfo, find := globEventList[en]
if !find {
panic(en)
}
find = false
for _, fd := range enDescInfo.fields {
for _, newFd := range newFieldsDescInfo {
if fd.Name == newFd.Name {
find = true
break
}
}
if !find {
// 追加
for _, oldFd := range info.fields {
fName := removeFieldEventName(en, oldFd.Name)
if fName == fd.Name {
newFieldInfo := &dto2.GameLogFieldInfo{
Name: fName,
Alias: oldFd.Alias,
IsPublicField: oldFd.IsPublicField,
FieldType: oldFd.FieldType,
2025-07-17 17:09:09 +08:00
}
2025-07-22 09:37:37 +08:00
if oldFd.Name == oldFd.Alias {
newFieldInfo.Alias = fd.Alias
}
newFieldsDescInfo = append(newFieldsDescInfo, newFieldInfo)
2025-07-17 17:09:09 +08:00
break
}
}
}
}
}
2025-07-22 09:37:37 +08:00
// 走完上面这一步newFieldsDescInfo就是事件列表具有的所有去重字段了
allEventFieldsLen := len(newFieldsDescInfo)
// 给newRows和newFieldsDescInfo前追加公共字段了
// 追加前面的日志名、时间
tmpFields := make([]*dto2.GameLogFieldInfo, 2)
copy(tmpFields, info.fields[:2])
newFieldsDescInfo = append(tmpFields, newFieldsDescInfo...)
for i, newRow := range newRows {
if len(newRow) < allEventFieldsLen {
rowCols := len(newRow)
for j := 0; j < allEventFieldsLen-rowCols; j++ {
// 补充这一行日志没有的字段
newRow = append(newRow, "\\")
}
} else if len(newRow) > allEventFieldsLen {
panic(fmt.Sprintf("%v,%v", len(newRow), allEventFieldsLen))
}
tmpCols := make([]any, 2)
copy(tmpCols, info.rows[i][:2])
newRow = append(tmpCols, newRow...)
newRows[i] = newRow
}
// 给newRows和newFieldsDescInfo后追加公共字段了
for i, f := range info.fields {
if len(f.Name) >= 4 && f.Name[:4] == "pub_" {
newFieldsDescInfo = append(newFieldsDescInfo, info.fields[i:]...)
for rowI, newRow := range newRows {
newRow = append(newRow, info.rows[rowI][i:]...)
newRows[rowI] = newRow
}
break
}
}
return newFieldsDescInfo, newRows
}
// 因为推送到clickhouse的字段名都是日志名_字段名的格式放置不同日志出现同名字段冲突这里直接去掉一下日志名
func removeFieldEventName(eventName, fieldName string) string {
return strings.ReplaceAll(fieldName, eventName+"_", "")
2025-07-17 17:09:09 +08:00
}