diff --git a/Makefile b/Makefile deleted file mode 100644 index 2a6e6e9..0000000 --- a/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -ROOT_DIR = $(shell pwd) -NAMESPACE = "default" -DEPLOY_NAME = "template-single" -DOCKER_NAME = "template-single" - -include ./hack/hack-cli.mk -include ./hack/hack.mk \ No newline at end of file diff --git a/api/hero/v1/hero.go b/api/hero/v1/hero.go index 031384d..bcffb94 100644 --- a/api/hero/v1/hero.go +++ b/api/hero/v1/hero.go @@ -26,5 +26,5 @@ type GetDetailReq struct { Code string `v:"required" dc:"角色code"` } type GetDetailRes struct { - Data *HeroDetailVO `json:"data"` + *HeroDetailVO } diff --git a/internal/controller/hero/hero_v1.go b/internal/controller/hero/hero_v1.go index ae4a1a4..2a4d7cc 100644 --- a/internal/controller/hero/hero_v1.go +++ b/internal/controller/hero/hero_v1.go @@ -39,7 +39,7 @@ func (c *ControllerV1) GetDetail(ctx context.Context, req *v1.GetDetailReq) (res } res = &v1.GetDetailRes{ - Data: detail, + HeroDetailVO: detail, } return res, nil } diff --git a/internal/logic/hero/hero.go b/internal/logic/hero/hero.go index 32013e8..d161c5b 100644 --- a/internal/logic/hero/hero.go +++ b/internal/logic/hero/hero.go @@ -4,11 +4,17 @@ import ( "context" v1 "epic/api/hero/v1" "epic/internal/dao" + "epic/internal/model/dto" "epic/internal/model/entity" "epic/internal/service" "epic/utility" - "fmt" + "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/gconv" + "math" + "math/rand" + "sort" + "strings" "time" ) @@ -40,9 +46,9 @@ func (l *Logic) GetHeroByCode(ctx context.Context, code string) (*entity.EpicHer // GetHeroList 查询所有英雄信息,并按创建时间倒序排列 func (l *Logic) GetHeroList(ctx context.Context) ([]*v1.EpicHeroVO, error) { - utility.RedisCache.Set(ctx, "epic_artifact_map_key111", "122", 1000*time.Second) - utility.RedisCache.Set(ctx, "epic_artifact_map_key222", "6565", 0) - fmt.Println(utility.RedisCache.Get(ctx, "NAME")) + //utility.RedisCache.Set(ctx, "epic_artifact_map_key111", "122", 1000*time.Second) + //utility.RedisCache.Set(ctx, "epic_artifact_map_key222", "6565", 0) + //fmt.Println(utility.RedisCache.Get(ctx, "NAME")) var ( doList []*entity.EpicHeroInfo // 数据库原始结构 @@ -78,8 +84,370 @@ func (l *Logic) GetHeroList(ctx context.Context) ([]*v1.EpicHeroVO, error) { } func (l *Logic) GetHeroDetailByCode(ctx context.Context, code string) (*v1.HeroDetailVO, error) { - //TODO implement me - panic("implement me") + var ( + heroDetailVO *v1.HeroDetailVO + heroRespSimpleVO *v1.HeroRespSimpleVO + hero60AttributeVO *v1.Hero60AttributeVO + heroSetAvgVO *v1.HeroSetAvgVO + heroSetPercentVOS []*v1.HeroSetPercentVO + heroArtifactPercentVOS []*v1.HeroArtifactPercentVO + heroSetShowS []*v1.HeroSetShowVO + + epicHeroInfo *entity.EpicHeroInfo + fribbleHeroSet *entity.FribbleHeroSet + + heroSetData dto.HeroSetData + err error + ) + + err = dao.EpicHeroInfo.Ctx(ctx). + Where(dao.EpicHeroInfo.Columns().HeroCode, code). + Scan(&epicHeroInfo) + + //设置基基本属性 + if err := gconv.Struct(epicHeroInfo, &heroRespSimpleVO); err != nil { + return nil, err + } + + //设置60级属性 + err = gjson.DecodeTo(epicHeroInfo.HeroAttrLv60, &hero60AttributeVO) + if err != nil { + return nil, err + } + + // 优化:先查 RedisCache,再查数据库 + cacheKey := "epic_hero_set:" + code + jsonContent, err := utility.RedisCache.Get(ctx, cacheKey) + if err == nil && !jsonContent.IsEmpty() { + fribbleHeroSet = &entity.FribbleHeroSet{ + HeroCode: code, + JsonContent: jsonContent.String(), + } + } else { + err = dao.FribbleHeroSet.Ctx(ctx). + Where(dao.FribbleHeroSet.Columns().HeroCode, code). + Scan(&fribbleHeroSet) + if err != nil { + return nil, err + } + // 写入 Redis 缓存,1小时 + utility.RedisCache.Set(ctx, cacheKey, fribbleHeroSet.JsonContent, 0) + } + + // 解析 JsonContent 字段 + if err := gjson.DecodeTo(fribbleHeroSet.JsonContent, &heroSetData); err != nil { + return nil, err + } + + // 优化:合并多次遍历 heroSetData.Data + var ( + totalCp, totalAtk, totalHp, totalSpd, totalDef int + totalChc, totalChd, totalEff, totalEfr float64 + setsNameCount = make(map[string]int) + artifactCount = make(map[string]int) + allItems []dto.HeroSetItem + ) + //now := time.Now() + setTypeNameMap := map[string]string{ + "set_speed": "速度", + "set_cri": "暴击", + "set_revenge": "复仇", + "set_counter": "反击", + "set_vampire": "吸血", + "set_immune": "免疫", + "set_cri_dmg": "破灭", + "set_torrent": "激流", + "set_max_hp": "生命", + "set_penetrate": "穿透", + "set_scar": "伤口", + "set_shield": "护盾", + "set_def": "防御", + "set_acc": "命中", + "set_res": "抵抗", + } + isTwoSet := func(setType string) bool { + twoSet := map[string]struct{}{ + "set_acc": {}, "set_cri": {}, "set_immune": {}, "set_torrent": {}, + "set_max_hp": {}, "set_penetrate": {}, "set_def": {}, "set_res": {}, + } + _, ok := twoSet[setType] + return ok + } + isFourSet := func(setType string) bool { + fourSet := map[string]struct{}{ + "set_speed": {}, "set_cri_dmg": {}, "set_revenge": {}, "set_counter": {}, + "set_vampire": {}, "set_scar": {}, "set_shield": {}, + } + _, ok := fourSet[setType] + return ok + } + for i := range heroSetData.Data { + item := &heroSetData.Data[i] + // 1. 解析 SetsName + var fourNames, twoNames []string + for setType, count := range item.Sets { + if isFourSet(setType) && count >= 4 { + if cn, ok := setTypeNameMap[setType]; ok { + fourNames = append(fourNames, cn) + } else { + fourNames = append(fourNames, setType) + } + } + } + for setType, count := range item.Sets { + if isTwoSet(setType) && count >= 2 { + if cn, ok := setTypeNameMap[setType]; ok { + twoNames = append(twoNames, cn) + } else { + twoNames = append(twoNames, setType) + } + } + } + allNames := append(fourNames, twoNames...) + item.SetsName = strings.Join(allNames, ",") + // 2. 累加属性 + totalCp += item.Gs + totalAtk += item.Atk + totalHp += item.Hp + totalSpd += item.Speed + totalDef += item.Def + totalChc += float64(item.Chc) + totalChd += float64(item.Chd) + totalEff += float64(item.Eff) + totalEfr += float64(item.Efr) + // 3. 统计套装组合 + if item.SetsName != "" { + setsNameCount[item.SetsName]++ + } + // 4. 统计神器 + if item.ArtifactCode != "" { + artifactCount[item.ArtifactCode]++ + } + // 5. 收集 heroSetShowS 原始数据 + allItems = append(allItems, *item) + } + count := float64(len(heroSetData.Data)) + if count > 0 { + heroSetAvgVO = &v1.HeroSetAvgVO{ + Cp: int(float64(totalCp) / count), + Atk: int(float64(totalAtk) / count), + Hp: int(float64(totalHp) / count), + Spd: int(float64(totalSpd) / count), + Def: int(float64(totalDef) / count), + Chc: totalChc / count, + Chd: totalChd / count, + Dac: 0, + Eff: totalEff / count, + Efr: totalEfr / count, + } + } + + // 统计所有套装组合(SetsName)出现次数 + setsNameCount = make(map[string]int) + total := len(heroSetData.Data) + for _, item := range heroSetData.Data { + setsName := item.SetsName + if setsName != "" { + setsNameCount[setsName]++ + } + } + + // 生成百分比VO,并按百分比从大到小排序 + type percentVO struct { + SetName string + Percent float64 + } + var percentVOList []percentVO + for setsName, cnt := range setsNameCount { + percent := 0.0 + if total > 0 { + percent = float64(cnt) * 100.0 / float64(total) + } + percent = math.Round(percent*10) / 10 // 保留一位小数 + percentVOList = append(percentVOList, percentVO{ + SetName: setsName, + Percent: percent, + }) + } + // 排序 + sort.Slice(percentVOList, func(i, j int) bool { + return percentVOList[i].Percent > percentVOList[j].Percent + }) + + heroSetPercentVOS = make([]*v1.HeroSetPercentVO, 0, len(percentVOList)) + for _, vo := range percentVOList { + heroSetPercentVOS = append(heroSetPercentVOS, &v1.HeroSetPercentVO{ + SetName: vo.SetName, + Percent: vo.Percent, + }) + } + + // 只保留占比最多的前三种套装类型 + if len(heroSetPercentVOS) > 3 { + heroSetPercentVOS = heroSetPercentVOS[:3] + } + + // 计算百分比并排序 + type artifactPercentVO struct { + ArtifactCode string + Percent float64 + } + var artifactPercentVOList []artifactPercentVO + totalArtifact := len(heroSetData.Data) + for code, cnt := range artifactCount { + percent := 0.0 + if totalArtifact > 0 { + percent = float64(cnt) * 100.0 / float64(totalArtifact) + } + percent = math.Round(percent*10) / 10 + artifactPercentVOList = append(artifactPercentVOList, artifactPercentVO{ + ArtifactCode: code, + Percent: percent, + }) + } + sort.Slice(artifactPercentVOList, func(i, j int) bool { + return artifactPercentVOList[i].Percent > artifactPercentVOList[j].Percent + }) + // 查询神器详细信息 + artifactInfoMap := make(map[string]*entity.EpicArtifactInfo) + if len(artifactPercentVOList) > 0 { + codes := make([]string, 0, len(artifactPercentVOList)) + for _, vo := range artifactPercentVOList { + codes = append(codes, vo.ArtifactCode) + } + var artifactInfos []*entity.EpicArtifactInfo + err := dao.EpicArtifactInfo.Ctx(ctx).WhereIn(dao.EpicArtifactInfo.Columns().ArtifactCode, codes).Scan(&artifactInfos) + if err == nil { + for _, info := range artifactInfos { + artifactInfoMap[info.ArtifactCode] = info + } + } + } + heroArtifactPercentVOS = make([]*v1.HeroArtifactPercentVO, 0, 3) + for i, vo := range artifactPercentVOList { + if i >= 3 { + break + } + info := artifactInfoMap[vo.ArtifactCode] + heroArtifactPercentVOS = append(heroArtifactPercentVOS, &v1.HeroArtifactPercentVO{ + ArtifactCode: vo.ArtifactCode, + ArtifactName: func() string { + if info != nil { + return info.ArtifactName + } else { + return "" + } + }(), + Rarity: func() string { + if info != nil { + return info.Rarity + } else { + return "" + } + }(), + Role: func() string { + if info != nil { + return info.Role + } else { + return "" + } + }(), + ImageUrl: func() string { + if info != nil { + return info.ImageUrl + } else { + return "" + } + }(), + Percent: vo.Percent, + }) + } + + // 生成 heroSetShowS:取最新500条,评分前30%,随机取6个 + allItems = heroSetData.Data + // 按 CreateDate 降序排序 + sort.Slice(allItems, func(i, j int) bool { + t1, err1 := time.Parse("2006-01-02 15:04:05", allItems[i].CreateDate) + t2, err2 := time.Parse("2006-01-02 15:04:05", allItems[j].CreateDate) + if err1 != nil && err2 != nil { + return false + } + if err1 != nil { + return false + } + if err2 != nil { + return true + } + return t1.After(t2) + }) + // 取最新600条 + maxN := 600 + if len(allItems) > maxN { + allItems = allItems[:maxN] + } + // 按 Gs 降序排序 + sort.Slice(allItems, func(i, j int) bool { + return allItems[i].Gs > allItems[j].Gs + }) + // 取前30% + topN := int(float64(len(allItems)) * 0.3) + if topN < 1 { + topN = len(allItems) + } + if topN > len(allItems) { + topN = len(allItems) + } + topItems := allItems[:topN] + // 随机取6个 + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(topItems), func(i, j int) { topItems[i], topItems[j] = topItems[j], topItems[i] }) + pickN := 6 + if len(topItems) < pickN { + pickN = len(topItems) + } + picked := topItems[:pickN] + // 组装 HeroSetShowVO + heroSetShowS = make([]*v1.HeroSetShowVO, 0, pickN) + for _, item := range picked { + heroSetShowS = append(heroSetShowS, &v1.HeroSetShowVO{ + Cp: item.Gs, + Atk: item.Atk, + Hp: item.Hp, + Spd: item.Speed, + Def: item.Def, + Chc: float64(item.Chc), + Chd: float64(item.Chd), + Dac: 0, + Eff: float64(item.Eff), + Efr: float64(item.Efr), + Hds: "", + Ctr: "", + ArfName: func() string { + info := artifactInfoMap[item.ArtifactCode] + if info != nil { + return info.ArtifactName + } + return "" + }(), + ArfPic: func() string { + info := artifactInfoMap[item.ArtifactCode] + if info != nil { + return info.ImageUrl + } + return "" + }(), + }) + } + + heroDetailVO = &v1.HeroDetailVO{ + HeroRespSimpleVO: heroRespSimpleVO, + Hero60AttributeVO: hero60AttributeVO, + HeroSetAvgVO: heroSetAvgVO, + HeroSetPercentVOS: heroSetPercentVOS, + HeroArtifactPercentVOS: heroArtifactPercentVOS, + HeroSetShows: heroSetShowS, + } + return heroDetailVO, nil } // ClearHeroCache 清理英雄相关缓存 diff --git a/internal/model/dto/hero_set_dto.go b/internal/model/dto/hero_set_dto.go new file mode 100644 index 0000000..f02330a --- /dev/null +++ b/internal/model/dto/hero_set_dto.go @@ -0,0 +1,28 @@ +package dto + +type HeroSetData struct { + Data []HeroSetItem `json:"data"` +} + +type HeroSetItem struct { + ArtifactCode string `json:"artifactCode"` + Atk int `json:"atk"` + Chc int `json:"chc"` + Chd int `json:"chd"` + CreateDate string `json:"createDate"` + Def int `json:"def"` + Eff int `json:"eff"` + Efr int `json:"efr"` + Gs int `json:"gs"` + Hp int `json:"hp"` + Speed int `json:"spd"` // 注意字段名 spd 在 json 中是 speed + UnitCode string `json:"unitCode"` + UnitName string `json:"unitName"` + Sets map[string]int `json:"sets"` // 动态套装类型 + SetsName string `json:"setsName"` // 解析后触发的套装效果名称,例如 "速度,生命" +} + +type HeroSetGroup struct { + SetImmune int `json:"set_immune"` + SetVampire int `json:"set_vampire"` +}