This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,153 +1,52 @@
|
||||
# 巅峰之战天选池当前逻辑说明
|
||||
|
||||
更新时间:2026-04-25
|
||||
更新时间:2026-04-26
|
||||
|
||||
## 1. 入口职责
|
||||
|
||||
当前巅峰相关入口拆成 3 个点:
|
||||
- `point_1`:巅峰之战主页面,展示当前玩家已配置的天选精灵。
|
||||
- `point_2`:天选池投票页面,只保存玩家本周投票。
|
||||
- `point_3`:天选精灵配置页面,配置玩家自己的天选精灵属性。
|
||||
|
||||
- `point_1`:打开巅峰之战主页面
|
||||
- `point_2`:打开天选池投票页面
|
||||
- `point_3`:打开本周正式天选配置页面
|
||||
## 2. 投票数据
|
||||
|
||||
三者职责如下:
|
||||
投票表是 `config_peak_tianxuan_vote`。
|
||||
|
||||
- `point_1` 只负责展示当前赛季、段位、匹配入口,以及“本周正式天选”摘要
|
||||
- `point_2` 只负责玩家投票,不负责配置本周正式天选属性
|
||||
- `point_3` 只负责配置“本周正式天选”和其预设属性
|
||||
字段口径:
|
||||
|
||||
## 2. 两套数据的区别
|
||||
- `week_index`:日期周,格式为 ISO 年周,例如 `202617`。
|
||||
- `player_id`:投票玩家。
|
||||
- `pet_id`:投票精灵。
|
||||
|
||||
当前天选系统明确分成两套数据:
|
||||
约束口径:
|
||||
|
||||
### 2.1 投票候选池
|
||||
- 每个玩家每个日期周只能投 1 只精灵。
|
||||
- 同周重复投票时更新原记录。
|
||||
- 票数统计按 `week_index + pet_id` 聚合。
|
||||
|
||||
这是玩家在 `point_2` 页面看到并进行投票的候选列表。
|
||||
## 3. 投票池
|
||||
|
||||
来源规则:
|
||||
`point_2` 的候选池来自乱斗精灵池 `config_boss_melee`,按 `mon_id` 去重。
|
||||
|
||||
1. 先读取乱斗精灵池 `config_boss_melee`
|
||||
2. 从乱斗池中提取精灵 ID,按 `mon_id` 去重
|
||||
3. 再减掉“上一周已经作为正式天选出现过的精灵”
|
||||
4. 剩余结果作为本周投票候选池
|
||||
当前不再因为上周出现过就从候选池移除。上周投票结果只作为展示和后续配置参考。
|
||||
|
||||
也就是说:
|
||||
页面会展示:
|
||||
|
||||
- `point_2` 不是手工配置候选池
|
||||
- `point_2` 候选池是“乱斗池 - 上周正式天选”
|
||||
- 当前日期周投票池。
|
||||
- 上一日期周投票统计。
|
||||
- 当前玩家本周已投的精灵。
|
||||
|
||||
### 2.2 本周正式天选
|
||||
## 4. 天选配置
|
||||
|
||||
这是本周真正会进入巅峰/BP 的正式天选列表。
|
||||
天选配置表是 `config_peak_tianxuan`。
|
||||
|
||||
来源规则:
|
||||
这张表现在表示“玩家额外拥有的天选精灵配置”,参考 `player_pet` 的归属思路,不再按周保存。
|
||||
|
||||
1. 在 `point_3` 页面手工配置
|
||||
2. 写入 `config_peak_tianxuan`
|
||||
3. 每条记录保存精灵 ID、周序号、展示顺序、属性、技能、皮肤等信息
|
||||
核心字段:
|
||||
|
||||
也就是说:
|
||||
|
||||
- 玩家投票结果不直接等于本周正式天选
|
||||
- 真正进入 BP 的是 `point_3` 配置出来的本周正式天选
|
||||
|
||||
## 3. 上周去重规则
|
||||
|
||||
当前已经实现的去重口径如下:
|
||||
|
||||
### 3.1 投票候选去重
|
||||
|
||||
`point_2` 候选池生成时,会自动排除上一周正式天选出现过的精灵。
|
||||
|
||||
表现为:
|
||||
|
||||
- 后端会返回 `tianxuan_prev_week_pool`
|
||||
- 前端投票页会显示“上周周免 / 本周排除项”
|
||||
- 这些精灵不会再进入当前投票候选列表
|
||||
|
||||
### 3.2 正式天选去重
|
||||
|
||||
`point_3` 配置本周正式天选时,后端会校验:
|
||||
|
||||
- 同一周内不能重复配置同一只精灵
|
||||
- 本周正式天选不能和上一周正式天选重复
|
||||
|
||||
## 4. 玩家投票规则
|
||||
|
||||
玩家在 `point_2` 投票页的规则如下:
|
||||
|
||||
- 每个玩家每周只能投 1 只
|
||||
|
||||
当前投票不会写入 `player_pvp`,而是单独写入“周投票记录表”。
|
||||
|
||||
当前投票记录字段最小化为:
|
||||
|
||||
- `week_index`
|
||||
- `player_id`
|
||||
- `pet_id`
|
||||
|
||||
表结构:
|
||||
|
||||
- `config_peak_tianxuan_vote`
|
||||
|
||||
约束规则:
|
||||
|
||||
- 同一玩家同一周只能有 1 条投票记录
|
||||
- 如果本周重复投票,不新增记录,直接更新该玩家本周那条记录的 `pet_id`
|
||||
- `updateTime` 就是该玩家本周最近一次修改投票的时间
|
||||
|
||||
这份数据当前用途:
|
||||
|
||||
- 作为“本周玩家投票明细”保留
|
||||
- 用于统计本周每只候选精灵的总票数
|
||||
|
||||
这份数据当前不直接参与:
|
||||
|
||||
- 巅峰匹配入队
|
||||
- BP 本周正式天选生成
|
||||
|
||||
## 5. point_1 当前展示逻辑
|
||||
|
||||
巅峰主页面当前展示的是“本周正式天选”,不是玩家投票候选池。
|
||||
|
||||
页面会从 `/admin/game/pvp/get` 读取:
|
||||
|
||||
- `current_tianxuan_pool`:本周正式天选
|
||||
- `tianxuan_vote_pool`:本周投票候选池
|
||||
- `tianxuan_prev_week_pool`:上周正式天选,用于排除展示
|
||||
|
||||
主页面当前重点展示:
|
||||
|
||||
- 段位和匹配模式
|
||||
- 本周正式天选数量
|
||||
- 本周正式天选列表
|
||||
|
||||
## 6. point_2 当前展示逻辑
|
||||
|
||||
天选池投票页当前会:
|
||||
|
||||
1. 请求 `/admin/game/pvp/get`
|
||||
2. 读取 `tianxuan_vote_pool`
|
||||
3. 读取 `tianxuan_prev_week_pool`
|
||||
4. 读取 `selected_tianxuan_pet_ids`
|
||||
5. 按当前段位限制可投数量
|
||||
|
||||
页面表现:
|
||||
|
||||
- 顶部显示当前周和投票数量限制
|
||||
- 单独显示“上周周免 / 本周排除项”
|
||||
- 候选列表支持搜索
|
||||
- 候选列表支持无限滚动懒加载
|
||||
|
||||
## 7. point_3 当前配置逻辑
|
||||
|
||||
`point_3` 对应的是本周正式天选配置面板。
|
||||
|
||||
当前可配置字段包括:
|
||||
|
||||
- `week_index`
|
||||
- `display_order`
|
||||
- `pet_id`
|
||||
- `preset_name`
|
||||
- `level`
|
||||
- `nature`
|
||||
@@ -164,66 +63,19 @@
|
||||
- `remark`
|
||||
- `is_enable`
|
||||
|
||||
存储表:
|
||||
约束口径:
|
||||
|
||||
- `config_peak_tianxuan`
|
||||
- 同一玩家不能重复配置同一只天选精灵。
|
||||
- 不再校验“本周不能和上周重复”。
|
||||
- 不再要求 `week_index`。
|
||||
|
||||
## 8. BP 当前使用逻辑
|
||||
## 5. BP 使用
|
||||
|
||||
BP 当前不再读取玩家投票结果。
|
||||
BP 不直接读取投票记录。
|
||||
|
||||
当前口径:
|
||||
进入 BP 时读取双方各自的 `config_peak_tianxuan` 配置:
|
||||
|
||||
- BP 使用的是“本周正式天选”
|
||||
- 后端在进入 BP 时读取 `config_peak_tianxuan` 当前周配置
|
||||
- 同一份正式天选会同时下发给双方展示
|
||||
- 我方看到自己的天选配置。
|
||||
- 对方看到对方自己的天选配置。
|
||||
|
||||
也就是说:
|
||||
|
||||
- 玩家投票结果不直接进 BP
|
||||
- `point_3` 配置才是真正进入 BP 的天选列表
|
||||
|
||||
## 9. 接口口径
|
||||
|
||||
### 9.1 `/admin/game/pvp/get`
|
||||
|
||||
当前主要返回:
|
||||
|
||||
- `rank_info`
|
||||
- `required_tianxuan_vote_count`
|
||||
- `tianxuan_week_index`
|
||||
- `tianxuan_prev_week_index`
|
||||
- `tianxuan_vote_pool`
|
||||
- `tianxuan_prev_week_pool`
|
||||
- `current_tianxuan_pool`
|
||||
|
||||
### 9.2 `/admin/game/pvp/save_tianxuan_vote`
|
||||
|
||||
当前用途:
|
||||
|
||||
- 保存玩家本周唯一一条投票记录
|
||||
|
||||
保存前会校验:
|
||||
|
||||
- 只能投候选池中的精灵
|
||||
- 每次只允许提交 1 只精灵
|
||||
- 同一玩家同一周重复提交时,覆盖原投票记录
|
||||
|
||||
## 10. 票数统计方式
|
||||
|
||||
候选列表中的票数不再存死值。
|
||||
|
||||
当前实现为:
|
||||
|
||||
1. 每周投票明细写入 `config_peak_tianxuan_vote`
|
||||
2. 候选列表展示时,按 `week_index + pet_id` 聚合统计票数
|
||||
3. 下一周如果要看“上周投票结果”,直接统计上周的投票记录表即可
|
||||
|
||||
## 11. 当前已知口径总结
|
||||
|
||||
一句话总结当前逻辑:
|
||||
|
||||
- `point_2`:玩家从“乱斗池 - 上周正式天选”里投票
|
||||
- `point_3`:运营/配置侧手工配置本周正式天选和属性
|
||||
- `point_1` / BP:都使用 `point_3` 配好的本周正式天选
|
||||
- 玩家投票明细单独存 `config_peak_tianxuan_vote`,不进 `player_pvp`
|
||||
投票结果后续可用于决定哪些精灵开放给玩家配置,但配置本身是玩家维度的数据。
|
||||
|
||||
@@ -31,6 +31,13 @@ func (e *NewSel0) IsOwner() bool {
|
||||
return e.ID().GetCatchTime() == source.CurPet[0].Info.CatchTime
|
||||
}
|
||||
|
||||
func (e *NewSel0) CurrentSkillHit() bool {
|
||||
if e.Ctx().SkillEntity == nil {
|
||||
return false
|
||||
}
|
||||
return e.Ctx().SkillEntity.AttackTime != 0
|
||||
}
|
||||
|
||||
// 免疫"能力(battle_lv)下降"
|
||||
type NewSel1 struct {
|
||||
NewSel0
|
||||
|
||||
@@ -28,6 +28,9 @@ func (e *NewSel49) Action_end_ex() bool {
|
||||
if e.Ctx().SkillEntity == nil {
|
||||
return true
|
||||
}
|
||||
if !e.CurrentSkillHit() {
|
||||
return true
|
||||
}
|
||||
|
||||
if e.Ctx().SkillEntity.Category() == info.Category.PHYSICAL {
|
||||
e.attackType = 1
|
||||
|
||||
@@ -22,6 +22,9 @@ func (e *NewSel6) Action_end_ex() bool {
|
||||
if e.Ctx().SkillEntity.Category() != info.Category.PHYSICAL {
|
||||
return true
|
||||
}
|
||||
if !e.CurrentSkillHit() {
|
||||
return true
|
||||
}
|
||||
|
||||
// 3. 概率判定(Args()[1]为触发概率)
|
||||
success, _, _ := e.Input.Player.Roll(int(e.Args()[1].IntPart()), 100)
|
||||
|
||||
@@ -23,6 +23,9 @@ func (e *NewSel74) Action_end_ex() bool {
|
||||
if e.Ctx().SkillEntity.Category() == info.Category.STATUS {
|
||||
return true
|
||||
}
|
||||
if !e.CurrentSkillHit() {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查概率是否触发
|
||||
success, _, _ := e.Input.Player.Roll(int(e.Args()[0].IntPart()), 100)
|
||||
|
||||
@@ -23,6 +23,9 @@ func (e *NewSel78) Action_end_ex() bool {
|
||||
if e.Ctx().SkillEntity.Category() != info.Category.SPECIAL {
|
||||
return true
|
||||
}
|
||||
if !e.CurrentSkillHit() {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查概率是否触发
|
||||
success, _, _ := e.Input.Player.Roll(int(e.Args()[1].IntPart()), 100)
|
||||
|
||||
@@ -19,9 +19,9 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
configservice "blazing/modules/config/service"
|
||||
"blazing/modules/player/model"
|
||||
blservice "blazing/modules/player/service"
|
||||
configservice "blazing/modules/config/service"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -583,7 +583,8 @@ func newQueueKey(player pvpwire.QueuePlayerSnapshot) queueKey {
|
||||
func newBanPickStartInfo(match pvpwire.MatchFoundPayload, self, opponent pvpwire.QueuePlayerSnapshot) *fight.CrossServerBanPickStartOutboundInfo {
|
||||
myPets := buildBanPickPetInfos(self.CatchTimes)
|
||||
opponentPets := buildBanPickPetInfos(opponent.CatchTimes)
|
||||
currentTianxuanPets := buildCurrentWeekTianxuanPetInfos()
|
||||
myTianxuanPets := buildCurrentWeekTianxuanPetInfos(self.UserID)
|
||||
opponentTianxuanPets := buildCurrentWeekTianxuanPetInfos(opponent.UserID)
|
||||
return &fight.CrossServerBanPickStartOutboundInfo{
|
||||
SessionID: match.SessionID,
|
||||
OpponentUserID: opponent.UserID,
|
||||
@@ -592,11 +593,11 @@ func newBanPickStartInfo(match pvpwire.MatchFoundPayload, self, opponent pvpwire
|
||||
Status: self.Status,
|
||||
TimeoutSeconds: match.BanPickTimeout,
|
||||
SelectableCount: uint32(minInt(battlePetLimit(self.FightMode), len(myPets))),
|
||||
TianxuanSelectableCount: uint32(len(currentTianxuanPets)),
|
||||
TianxuanSelectableCount: uint32(len(myTianxuanPets)),
|
||||
MyPets: myPets,
|
||||
MyTianxuanPets: currentTianxuanPets,
|
||||
MyTianxuanPets: myTianxuanPets,
|
||||
OpponentPets: opponentPets,
|
||||
OpponentTianxuanPets: currentTianxuanPets,
|
||||
OpponentTianxuanPets: opponentTianxuanPets,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -752,8 +753,8 @@ func buildTianxuanPetInfos(petIDs []uint32) []fight.CrossServerBanPickTianxuanPe
|
||||
return result
|
||||
}
|
||||
|
||||
func buildCurrentWeekTianxuanPetInfos() []fight.CrossServerBanPickTianxuanPetInfo {
|
||||
petIDs, err := configservice.NewPeakTianxuanService().CurrentTianxuanPetIDs()
|
||||
func buildCurrentWeekTianxuanPetInfos(playerID uint32) []fight.CrossServerBanPickTianxuanPetInfo {
|
||||
petIDs, err := configservice.NewPeakTianxuanService().CurrentTianxuanPetIDs(playerID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,23 +11,23 @@ const (
|
||||
type PeakTianxuan struct {
|
||||
*BaseConfig
|
||||
|
||||
WeekIndex uint32 `gorm:"not null;index:idx_peak_tianxuan_week;uniqueIndex:idx_peak_tianxuan_week_pet;comment:'周序号'" json:"week_index"`
|
||||
DisplayOrder uint32 `gorm:"not null;default:0;comment:'展示顺序'" json:"display_order"`
|
||||
PetID uint32 `gorm:"not null;uniqueIndex:idx_peak_tianxuan_week_pet;comment:'天选精灵ID'" json:"pet_id"`
|
||||
PresetName string `gorm:"type:varchar(64);not null;default:'';comment:'预设显示名'" json:"preset_name"`
|
||||
Level uint32 `gorm:"not null;default:100;comment:'预设等级'" json:"level"`
|
||||
Nature uint32 `gorm:"not null;default:0;comment:'预设性格'" json:"nature"`
|
||||
Hp uint32 `gorm:"not null;default:0;comment:'预设当前生命'" json:"hp"`
|
||||
MaxHp uint32 `gorm:"not null;default:0;comment:'预设最大生命'" json:"max_hp"`
|
||||
Attack uint32 `gorm:"not null;default:0;comment:'预设攻击'" json:"attack"`
|
||||
Defence uint32 `gorm:"not null;default:0;comment:'预设防御'" json:"defence"`
|
||||
SpAttack uint32 `gorm:"not null;default:0;comment:'预设特攻'" json:"sp_attack"`
|
||||
SpDefence uint32 `gorm:"not null;default:0;comment:'预设特防'" json:"sp_defence"`
|
||||
Speed uint32 `gorm:"not null;default:0;comment:'预设速度'" json:"speed"`
|
||||
SkinID uint32 `gorm:"not null;default:0;comment:'预设皮肤ID'" json:"skin_id"`
|
||||
EffectIDs []uint32 `gorm:"type:jsonb;not null;default:'[]';comment:'预设特性/效果ID列表'" json:"effect_ids"`
|
||||
SkillIDs []uint32 `gorm:"type:jsonb;not null;default:'[]';comment:'预设技能ID列表'" json:"skill_ids"`
|
||||
VoteCount uint32 `gorm:"not null;default:0;comment:'票数统计'" json:"vote_count"`
|
||||
PlayerID uint32 `gorm:"not null;index:idx_peak_tianxuan_player;uniqueIndex:idx_peak_tianxuan_player_pet;comment:'所属玩家ID'" json:"player_id"`
|
||||
DisplayOrder uint32 `gorm:"not null;default:0;comment:'展示顺序'" json:"display_order"`
|
||||
PetID uint32 `gorm:"not null;uniqueIndex:idx_peak_tianxuan_player_pet;comment:'天选精灵ID'" json:"pet_id"`
|
||||
PresetName string `gorm:"type:varchar(64);not null;default:'';comment:'预设显示名'" json:"preset_name"`
|
||||
Level uint32 `gorm:"not null;default:100;comment:'预设等级'" json:"level"`
|
||||
Nature uint32 `gorm:"not null;default:0;comment:'预设性格'" json:"nature"`
|
||||
Hp uint32 `gorm:"not null;default:0;comment:'预设当前生命'" json:"hp"`
|
||||
MaxHp uint32 `gorm:"not null;default:0;comment:'预设最大生命'" json:"max_hp"`
|
||||
Attack uint32 `gorm:"not null;default:0;comment:'预设攻击'" json:"attack"`
|
||||
Defence uint32 `gorm:"not null;default:0;comment:'预设防御'" json:"defence"`
|
||||
SpAttack uint32 `gorm:"not null;default:0;comment:'预设特攻'" json:"sp_attack"`
|
||||
SpDefence uint32 `gorm:"not null;default:0;comment:'预设特防'" json:"sp_defence"`
|
||||
Speed uint32 `gorm:"not null;default:0;comment:'预设速度'" json:"speed"`
|
||||
SkinID uint32 `gorm:"not null;default:0;comment:'预设皮肤ID'" json:"skin_id"`
|
||||
EffectIDs []uint32 `gorm:"type:jsonb;not null;default:'[]';comment:'预设特性/效果ID列表'" json:"effect_ids"`
|
||||
SkillIDs []uint32 `gorm:"type:jsonb;not null;default:'[]';comment:'预设技能ID列表'" json:"skill_ids"`
|
||||
VoteCount uint32 `gorm:"not null;default:0;comment:'票数统计'" json:"vote_count"`
|
||||
}
|
||||
|
||||
func (*PeakTianxuan) TableName() string {
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"blazing/common/data/xmlres"
|
||||
"blazing/cool"
|
||||
@@ -22,20 +23,36 @@ func NewPeakTianxuanService() *PeakTianxuanService {
|
||||
return &PeakTianxuanService{
|
||||
&cool.Service{
|
||||
Model: model.NewPeakTianxuan(),
|
||||
Where: func(ctx context.Context) []g.Array {
|
||||
admin := cool.GetAdmin(ctx)
|
||||
return []g.Array{{"player_id", uint32(admin.UserId)}}
|
||||
},
|
||||
InsertParam: func(ctx context.Context) g.MapStrAny {
|
||||
admin := cool.GetAdmin(ctx)
|
||||
return g.MapStrAny{
|
||||
"player_id": uint32(admin.UserId),
|
||||
}
|
||||
},
|
||||
PageQueryOp: &cool.QueryOp{
|
||||
KeyWordField: []string{"preset_name", "remark"},
|
||||
FieldEQ: []string{"week_index", "pet_id", "is_enable"},
|
||||
FieldEQ: []string{"player_id", "pet_id", "is_enable"},
|
||||
Where: func(ctx context.Context) []g.Array {
|
||||
admin := cool.GetAdmin(ctx)
|
||||
return []g.Array{{"player_id", uint32(admin.UserId)}}
|
||||
},
|
||||
AddOrderby: g.MapStrStr{
|
||||
"week_index": "desc",
|
||||
"display_order": "asc",
|
||||
"id": "desc",
|
||||
},
|
||||
},
|
||||
ListQueryOp: &cool.QueryOp{
|
||||
KeyWordField: []string{"preset_name", "remark"},
|
||||
FieldEQ: []string{"week_index", "pet_id", "is_enable"},
|
||||
FieldEQ: []string{"player_id", "pet_id", "is_enable"},
|
||||
Where: func(ctx context.Context) []g.Array {
|
||||
admin := cool.GetAdmin(ctx)
|
||||
return []g.Array{{"player_id", uint32(admin.UserId)}}
|
||||
},
|
||||
AddOrderby: g.MapStrStr{
|
||||
"week_index": "desc",
|
||||
"display_order": "asc",
|
||||
"id": "desc",
|
||||
},
|
||||
@@ -49,17 +66,20 @@ func (s *PeakTianxuanService) ModifyBefore(ctx context.Context, method string, p
|
||||
return nil
|
||||
}
|
||||
|
||||
admin := cool.GetAdmin(ctx)
|
||||
param["player_id"] = uint32(admin.UserId)
|
||||
|
||||
var (
|
||||
weekIndex = gconv.Uint32(param["week_index"])
|
||||
petID = gconv.Uint32(param["pet_id"])
|
||||
level = gconv.Uint32(param["level"])
|
||||
hp = gconv.Uint32(param["hp"])
|
||||
maxHp = gconv.Uint32(param["max_hp"])
|
||||
recordID = gconv.Uint32(param["id"])
|
||||
playerID = gconv.Uint32(param["player_id"])
|
||||
petID = gconv.Uint32(param["pet_id"])
|
||||
level = gconv.Uint32(param["level"])
|
||||
hp = gconv.Uint32(param["hp"])
|
||||
maxHp = gconv.Uint32(param["max_hp"])
|
||||
recordID = gconv.Uint32(param["id"])
|
||||
)
|
||||
|
||||
if weekIndex == 0 {
|
||||
return gerror.New("周序号不能为空")
|
||||
if playerID == 0 {
|
||||
return gerror.New("配置玩家不能为空")
|
||||
}
|
||||
if petID == 0 {
|
||||
return gerror.New("天选精灵ID不能为空")
|
||||
@@ -72,7 +92,9 @@ func (s *PeakTianxuanService) ModifyBefore(ctx context.Context, method string, p
|
||||
}
|
||||
|
||||
var currentCount int
|
||||
query := cool.DBM(s.Model).Where("week_index", weekIndex).Where("pet_id", petID)
|
||||
query := cool.DBM(s.Model).
|
||||
Where("player_id", playerID).
|
||||
Where("pet_id", petID)
|
||||
if recordID > 0 {
|
||||
query = query.WhereNot("id", recordID)
|
||||
}
|
||||
@@ -80,72 +102,67 @@ func (s *PeakTianxuanService) ModifyBefore(ctx context.Context, method string, p
|
||||
return err
|
||||
}
|
||||
if currentCount > 0 {
|
||||
return gerror.New("同一周不能重复配置同一只天选精灵")
|
||||
}
|
||||
|
||||
prevPetIDs, err := s.ListWeekPetIDs(weekIndex - 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := prevPetIDs[petID]; ok {
|
||||
return gerror.New("本周天选精灵不能和上一周重复")
|
||||
return gerror.New("同一玩家不能重复配置同一只天选精灵")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) LatestWeekIndex() (uint32, error) {
|
||||
var result struct {
|
||||
WeekIndex uint32 `json:"week_index"`
|
||||
}
|
||||
|
||||
if err := dbm_nocache_noenable(s.Model).
|
||||
Fields("COALESCE(MAX(week_index), 0) AS week_index").
|
||||
Scan(&result); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result.WeekIndex, nil
|
||||
func dateWeekIndex(t time.Time) uint32 {
|
||||
year, week := t.ISOWeek()
|
||||
return uint32(year*100 + week)
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) ListWeekPetIDs(weekIndex uint32) (map[uint32]struct{}, error) {
|
||||
func currentDateWeekIndex() uint32 {
|
||||
return dateWeekIndex(time.Now())
|
||||
}
|
||||
|
||||
func prevDateWeekIndex() uint32 {
|
||||
return dateWeekIndex(time.Now().AddDate(0, 0, -7))
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) CurrentVoteWeekIndex() (uint32, uint32, error) {
|
||||
return currentDateWeekIndex(), prevDateWeekIndex(), nil
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) ListWeekPetIDs(weekIndex uint32, playerIDs ...uint32) (map[uint32]struct{}, error) {
|
||||
ret := make(map[uint32]struct{})
|
||||
if weekIndex == 0 {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
var list []model.PeakTianxuan
|
||||
if err := dbm_enable(s.Model).Where("week_index", weekIndex).Scan(&list); err != nil {
|
||||
var voteRows []struct {
|
||||
PetID uint32 `json:"pet_id"`
|
||||
}
|
||||
if err := cool.DBM(model.NewPeakTianxuanVote()).
|
||||
Fields("pet_id").
|
||||
Where("week_index", weekIndex).
|
||||
Group("pet_id").
|
||||
Scan(&voteRows); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
for _, item := range voteRows {
|
||||
ret[item.PetID] = struct{}{}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) GetWeekList(weekIndex uint32) ([]model.PeakTianxuan, error) {
|
||||
func (s *PeakTianxuanService) GetWeekList(weekIndex uint32, playerIDs ...uint32) ([]model.PeakTianxuan, error) {
|
||||
var list []model.PeakTianxuan
|
||||
if weekIndex == 0 {
|
||||
return list, nil
|
||||
}
|
||||
|
||||
err := dbm_enable(s.Model).
|
||||
Where("week_index", weekIndex).
|
||||
Order("display_order asc,id asc").
|
||||
Scan(&list)
|
||||
query := dbm_enable(s.Model)
|
||||
if len(playerIDs) > 0 && playerIDs[0] > 0 {
|
||||
query = query.Where("player_id", playerIDs[0])
|
||||
}
|
||||
err := query.Order("display_order asc,id asc").Scan(&list)
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) GetCurrentWeekList() ([]model.PeakTianxuan, uint32, error) {
|
||||
weekIndex, err := s.LatestWeekIndex()
|
||||
if err != nil || weekIndex == 0 {
|
||||
return nil, weekIndex, err
|
||||
}
|
||||
|
||||
list, err := s.GetWeekList(weekIndex)
|
||||
func (s *PeakTianxuanService) GetCurrentWeekList(playerIDs ...uint32) ([]model.PeakTianxuan, uint32, error) {
|
||||
weekIndex := currentDateWeekIndex()
|
||||
list, err := s.GetWeekList(0, playerIDs...)
|
||||
return list, weekIndex, err
|
||||
}
|
||||
|
||||
@@ -186,24 +203,21 @@ func (s *PeakTianxuanService) SaveVote(playerID uint32, petID uint32) (uint32, e
|
||||
return 0, gerror.New("投票精灵不在候选池中")
|
||||
}
|
||||
|
||||
currentWeekIndex, err := s.LatestWeekIndex()
|
||||
voteWeekIndex, _, err := s.CurrentVoteWeekIndex()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if currentWeekIndex == 0 {
|
||||
currentWeekIndex = 1
|
||||
}
|
||||
|
||||
voteModel := model.NewPeakTianxuanVote()
|
||||
data := model.PeakTianxuanVote{
|
||||
WeekIndex: currentWeekIndex,
|
||||
WeekIndex: voteWeekIndex,
|
||||
PlayerID: playerID,
|
||||
PetID: normalize[0],
|
||||
}
|
||||
|
||||
var count int
|
||||
count, err = cool.DBM(voteModel).
|
||||
Where("week_index", currentWeekIndex).
|
||||
Where("week_index", voteWeekIndex).
|
||||
Where("player_id", playerID).
|
||||
Count()
|
||||
if err != nil {
|
||||
@@ -211,7 +225,7 @@ func (s *PeakTianxuanService) SaveVote(playerID uint32, petID uint32) (uint32, e
|
||||
}
|
||||
if count > 0 {
|
||||
_, err = cool.DBM(voteModel).
|
||||
Where("week_index", currentWeekIndex).
|
||||
Where("week_index", voteWeekIndex).
|
||||
Where("player_id", playerID).
|
||||
Data(data).
|
||||
Update()
|
||||
@@ -225,8 +239,35 @@ func (s *PeakTianxuanService) SaveVote(playerID uint32, petID uint32) (uint32, e
|
||||
return normalize[0], nil
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) BuildCurrentTianxuanPayload() ([]g.Map, uint32, error) {
|
||||
list, weekIndex, err := s.GetCurrentWeekList()
|
||||
func (s *PeakTianxuanService) GetPlayerVote(playerID uint32) (uint32, error) {
|
||||
voteWeekIndex, prevWeekIndex, err := s.CurrentVoteWeekIndex()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var vote model.PeakTianxuanVote
|
||||
if err = cool.DBM(model.NewPeakTianxuanVote()).
|
||||
Where("week_index", voteWeekIndex).
|
||||
Where("player_id", playerID).
|
||||
Scan(&vote); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if vote.PetID > 0 || prevWeekIndex == 0 {
|
||||
return vote.PetID, nil
|
||||
}
|
||||
|
||||
// 兼容旧数据:之前投票可能写在“当前正式周”里,打开页面仍需标出玩家已选。
|
||||
if err = cool.DBM(model.NewPeakTianxuanVote()).
|
||||
Where("week_index", prevWeekIndex).
|
||||
Where("player_id", playerID).
|
||||
Scan(&vote); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return vote.PetID, nil
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) BuildCurrentTianxuanPayload(playerIDs ...uint32) ([]g.Map, uint32, error) {
|
||||
list, weekIndex, err := s.GetCurrentWeekList(playerIDs...)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -241,7 +282,7 @@ func (s *PeakTianxuanService) BuildCurrentTianxuanPayload() ([]g.Map, uint32, er
|
||||
}
|
||||
ret = append(ret, g.Map{
|
||||
"id": item.ID,
|
||||
"week_index": item.WeekIndex,
|
||||
"player_id": item.PlayerID,
|
||||
"display_order": item.DisplayOrder,
|
||||
"pet_id": item.PetID,
|
||||
"petId": item.PetID,
|
||||
@@ -269,37 +310,25 @@ func (s *PeakTianxuanService) BuildCurrentTianxuanPayload() ([]g.Map, uint32, er
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) BuildVoteCandidatePoolPayload() ([]g.Map, uint32, uint32, error) {
|
||||
currentWeekIndex, err := s.LatestWeekIndex()
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
prevWeekIndex := uint32(0)
|
||||
if currentWeekIndex > 1 {
|
||||
prevWeekIndex = currentWeekIndex - 1
|
||||
}
|
||||
|
||||
prevWeekPetIDs, err := s.ListWeekPetIDs(prevWeekIndex)
|
||||
voteWeekIndex, prevWeekIndex, err := s.CurrentVoteWeekIndex()
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
currentWeekVoteMap := make(map[uint32]uint32)
|
||||
if currentWeekIndex > 0 {
|
||||
var voteRows []struct {
|
||||
PetID uint32 `json:"pet_id"`
|
||||
VoteCount uint32 `json:"vote_count"`
|
||||
}
|
||||
if err = cool.DBM(model.NewPeakTianxuanVote()).
|
||||
Fields("pet_id, COUNT(1) AS vote_count").
|
||||
Where("week_index", currentWeekIndex).
|
||||
Group("pet_id").
|
||||
Scan(&voteRows); err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
for _, item := range voteRows {
|
||||
currentWeekVoteMap[item.PetID] = item.VoteCount
|
||||
}
|
||||
var voteRows []struct {
|
||||
PetID uint32 `json:"pet_id"`
|
||||
VoteCount uint32 `json:"vote_count"`
|
||||
}
|
||||
if err = cool.DBM(model.NewPeakTianxuanVote()).
|
||||
Fields("pet_id, COUNT(1) AS vote_count").
|
||||
Where("week_index", voteWeekIndex).
|
||||
Group("pet_id").
|
||||
Scan(&voteRows); err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
for _, item := range voteRows {
|
||||
currentWeekVoteMap[item.PetID] = item.VoteCount
|
||||
}
|
||||
|
||||
var meleeList []model.PetBaseConfig
|
||||
@@ -318,9 +347,6 @@ func (s *PeakTianxuanService) BuildVoteCandidatePoolPayload() ([]g.Map, uint32,
|
||||
if petID == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := prevWeekPetIDs[petID]; ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := unique[petID]; ok {
|
||||
continue
|
||||
}
|
||||
@@ -354,11 +380,11 @@ func (s *PeakTianxuanService) BuildVoteCandidatePoolPayload() ([]g.Map, uint32,
|
||||
})
|
||||
}
|
||||
|
||||
return ret, currentWeekIndex, prevWeekIndex, nil
|
||||
return ret, voteWeekIndex, prevWeekIndex, nil
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) CurrentTianxuanPetIDs() ([]uint32, error) {
|
||||
list, _, err := s.GetCurrentWeekList()
|
||||
func (s *PeakTianxuanService) CurrentTianxuanPetIDs(playerIDs ...uint32) ([]uint32, error) {
|
||||
list, _, err := s.GetCurrentWeekList(playerIDs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -374,42 +400,30 @@ func (s *PeakTianxuanService) CurrentTianxuanPetIDs() ([]uint32, error) {
|
||||
}
|
||||
|
||||
func (s *PeakTianxuanService) BuildWeekTianxuanPayload(weekIndex uint32) ([]g.Map, error) {
|
||||
list, err := s.GetWeekList(weekIndex)
|
||||
if err != nil {
|
||||
var voteRows []struct {
|
||||
PetID uint32 `json:"pet_id"`
|
||||
VoteCount uint32 `json:"vote_count"`
|
||||
}
|
||||
if err := cool.DBM(model.NewPeakTianxuanVote()).
|
||||
Fields("pet_id, COUNT(1) AS vote_count").
|
||||
Where("week_index", weekIndex).
|
||||
Group("pet_id").
|
||||
Order("vote_count desc, pet_id asc").
|
||||
Scan(&voteRows); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]g.Map, 0, len(list))
|
||||
for _, item := range list {
|
||||
name := item.PresetName
|
||||
if name == "" {
|
||||
if pet, ok := xmlres.PetMAP[int(item.PetID)]; ok {
|
||||
name = pet.DefName
|
||||
}
|
||||
ret := make([]g.Map, 0, len(voteRows))
|
||||
for _, item := range voteRows {
|
||||
name := ""
|
||||
if pet, ok := xmlres.PetMAP[int(item.PetID)]; ok {
|
||||
name = pet.DefName
|
||||
}
|
||||
ret = append(ret, g.Map{
|
||||
"id": item.ID,
|
||||
"week_index": item.WeekIndex,
|
||||
"display_order": item.DisplayOrder,
|
||||
"pet_id": item.PetID,
|
||||
"petId": item.PetID,
|
||||
"name": name,
|
||||
"preset_name": item.PresetName,
|
||||
"level": item.Level,
|
||||
"nature": item.Nature,
|
||||
"hp": item.Hp,
|
||||
"max_hp": item.MaxHp,
|
||||
"attack": item.Attack,
|
||||
"defence": item.Defence,
|
||||
"sp_attack": item.SpAttack,
|
||||
"sp_defence": item.SpDefence,
|
||||
"speed": item.Speed,
|
||||
"skin_id": item.SkinID,
|
||||
"effect_ids": item.EffectIDs,
|
||||
"skill_ids": item.SkillIDs,
|
||||
"vote_count": item.VoteCount,
|
||||
"is_enable": item.IsEnable,
|
||||
"remark": item.Remark,
|
||||
"pet_id": item.PetID,
|
||||
"petId": item.PetID,
|
||||
"name": name,
|
||||
"vote_count": item.VoteCount,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -33,24 +33,29 @@ type GetPVPReq struct {
|
||||
func (c *PVPController) Get(ctx context.Context, req *GetPVPReq) (res *cool.BaseRes, err error) {
|
||||
res = &cool.BaseRes{}
|
||||
var (
|
||||
admin = cool.GetAdmin(ctx)
|
||||
pvpInfo = service.NewPVPService(uint32(admin.UserId)).Get(uint32(admin.UserId))
|
||||
tianxuanSv = configService.NewPeakTianxuanService()
|
||||
votePool []g.Map
|
||||
currentPool []g.Map
|
||||
prevWeekPool []g.Map
|
||||
weekIndex uint32
|
||||
prevWeekIndex uint32
|
||||
admin = cool.GetAdmin(ctx)
|
||||
pvpInfo = service.NewPVPService(uint32(admin.UserId)).Get(uint32(admin.UserId))
|
||||
tianxuanSv = configService.NewPeakTianxuanService()
|
||||
votePool []g.Map
|
||||
currentPool []g.Map
|
||||
prevWeekPool []g.Map
|
||||
playerVotePetID uint32
|
||||
weekIndex uint32
|
||||
prevWeekIndex uint32
|
||||
)
|
||||
if votePool, weekIndex, prevWeekIndex, err = tianxuanSv.BuildVoteCandidatePoolPayload(); err != nil {
|
||||
return
|
||||
}
|
||||
if currentPool, _, err = tianxuanSv.BuildCurrentTianxuanPayload(); err != nil {
|
||||
if currentPool, _, err = tianxuanSv.BuildCurrentTianxuanPayload(uint32(admin.UserId)); err != nil {
|
||||
return
|
||||
}
|
||||
if prevWeekPool, err = tianxuanSv.BuildWeekTianxuanPayload(prevWeekIndex); err != nil {
|
||||
return
|
||||
}
|
||||
if playerVotePetID, err = tianxuanSv.GetPlayerVote(uint32(admin.UserId)); err != nil {
|
||||
return
|
||||
}
|
||||
markMyTianxuanVote(votePool, playerVotePetID)
|
||||
|
||||
res.Data = g.Map{
|
||||
"id": pvpInfo.ID,
|
||||
@@ -63,6 +68,7 @@ func (c *PVPController) Get(ctx context.Context, req *GetPVPReq) (res *cool.Base
|
||||
"tianxuan_vote_pool": votePool,
|
||||
"tianxuan_prev_week_pool": prevWeekPool,
|
||||
"current_tianxuan_pool": currentPool,
|
||||
"my_tianxuan_vote_pet_id": playerVotePetID,
|
||||
}
|
||||
return
|
||||
|
||||
@@ -77,9 +83,13 @@ func (c *PVPController) SaveTianxuanVote(ctx context.Context, req *SaveTianxuanV
|
||||
res = &cool.BaseRes{}
|
||||
|
||||
var (
|
||||
admin = cool.GetAdmin(ctx)
|
||||
petID uint32
|
||||
saved uint32
|
||||
admin = cool.GetAdmin(ctx)
|
||||
tianxuanSv = configService.NewPeakTianxuanService()
|
||||
petID uint32
|
||||
saved uint32
|
||||
votePool []g.Map
|
||||
weekIndex uint32
|
||||
prevWeekIndex uint32
|
||||
)
|
||||
|
||||
for _, item := range req.PetIDs {
|
||||
@@ -90,13 +100,34 @@ func (c *PVPController) SaveTianxuanVote(ctx context.Context, req *SaveTianxuanV
|
||||
break
|
||||
}
|
||||
|
||||
if saved, err = configService.NewPeakTianxuanService().SaveVote(uint32(admin.UserId), petID); err != nil {
|
||||
if saved, err = tianxuanSv.SaveVote(uint32(admin.UserId), petID); err != nil {
|
||||
return
|
||||
}
|
||||
if votePool, weekIndex, prevWeekIndex, err = tianxuanSv.BuildVoteCandidatePoolPayload(); err != nil {
|
||||
return
|
||||
}
|
||||
markMyTianxuanVote(votePool, saved)
|
||||
|
||||
res.Data = g.Map{
|
||||
"saved_pet_id": saved,
|
||||
"my_tianxuan_vote_pet_id": saved,
|
||||
"required_tianxuan_vote_count": 1,
|
||||
"tianxuan_week_index": weekIndex,
|
||||
"tianxuan_prev_week_index": prevWeekIndex,
|
||||
"tianxuan_vote_pool": votePool,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func markMyTianxuanVote(votePool []g.Map, petID uint32) {
|
||||
if petID == 0 {
|
||||
return
|
||||
}
|
||||
for _, item := range votePool {
|
||||
if gconv.Uint32(item["pet_id"]) == petID || gconv.Uint32(item["petId"]) == petID {
|
||||
item["is_voted"] = true
|
||||
item["isVoted"] = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"current_season": 5
|
||||
"current_season": 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user