或就爱你功能
This commit is contained in:
parent
f5842f8702
commit
7b2b3a3e2f
@ -85,7 +85,7 @@ go run ./cmd/server -config configs/config.yaml -bootstrap
|
||||
5. 新增任务类型时,需要补齐创建接口、runner handler、重试/失败结果和产物策略。
|
||||
6. 区域创建和区域启用/禁用是后台管理能力,实现在 `hyapp-admin-server`,不要为此新增 user-service RPC 或把后台开关逻辑下沉到 app 后端服务。
|
||||
7. 国家创建和国家启用/禁用是后台管理后端能力,实现在 `hyapp-admin-server`,不要为这些后台动作新增 user-service RPC 或 user-service 业务逻辑。
|
||||
8. 后台创建 BD Leader 必须传后台选择的 `regionId`;admin-server 负责权限和审计,user-service 在关系创建事务里更新目标用户 `users.region_id`,不要在 admin-server 里用 GetUser 做“用户原区域必须一致”的拒绝。
|
||||
8. 后台创建 BD Leader 按目标用户当前 `users.region_id` 创建;admin-server 只负责权限和审计,不传 `regionId`,user-service 在关系创建事务里读取目标用户区域并写入 `bd_leader_profiles.region_id`,不要在创建 BD Leader 时顺手修改用户区域。
|
||||
9. `hyapp.local/api` 是唯一允许直接依赖的 app 后端契约 module;不要 import `hyapp/services/*`、`hyapp/internal/*` 或 `hyapp/pkg/*`。
|
||||
|
||||
## 验证
|
||||
|
||||
@ -143,7 +143,12 @@ func (h *Handler) SetBDStatus(c *gin.Context) {
|
||||
}
|
||||
|
||||
func bdRoleFromStatusRoute(c *gin.Context) (string, string) {
|
||||
if strings.HasPrefix(c.FullPath(), "/admin/bd-leaders/") {
|
||||
return bdRoleFromStatusPath(c.FullPath())
|
||||
}
|
||||
|
||||
func bdRoleFromStatusPath(fullPath string) (string, string) {
|
||||
// FullPath 带有外层 /api/v1 分组;只要命中 BD Leader 状态路由片段,就必须走独立的 bd_leader_profiles,避免误查普通 bd_profiles。
|
||||
if strings.Contains(fullPath, "/admin/bd-leaders/") {
|
||||
return "bd_leader", "bd_leader_profiles"
|
||||
}
|
||||
return "bd", "bd_profiles"
|
||||
|
||||
@ -12,6 +12,19 @@ func TestNormalizeListQueryAllowsCoinSellerComputedSorts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestBDRoleFromStatusPathUsesLeaderTableWithAPIPrefix 锁定线上真实 FullPath 带 /api/v1 前缀时,BD Leader 状态仍然写独立负责人表。
|
||||
func TestBDRoleFromStatusPathUsesLeaderTableWithAPIPrefix(t *testing.T) {
|
||||
role, target := bdRoleFromStatusPath("/api/v1/admin/bd-leaders/:user_id/status")
|
||||
if role != "bd_leader" || target != "bd_leader_profiles" {
|
||||
t.Fatalf("leader status route must use bd_leader_profiles, got role=%q target=%q", role, target)
|
||||
}
|
||||
|
||||
role, target = bdRoleFromStatusPath("/api/v1/admin/bds/:user_id/status")
|
||||
if role != "bd" || target != "bd_profiles" {
|
||||
t.Fatalf("bd status route must use bd_profiles, got role=%q target=%q", role, target)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSortCoinSellerListItemsComputedFields 验证 wallet 聚合字段排序只改变展示顺序,不改动币商身份数据。
|
||||
func TestSortCoinSellerListItemsComputedFields(t *testing.T) {
|
||||
items := []*CoinSellerListItem{
|
||||
|
||||
@ -496,6 +496,7 @@ func rocketStateForNow(input *state.RocketState, cfg RoomRocketConfig, now time.
|
||||
rocket.CurrentFuel = rocket.FuelThreshold
|
||||
}
|
||||
rocket.PendingLaunches = validRocketPendingLaunches(rocket.PendingLaunches, rocket.ResetAtMS)
|
||||
syncRocketLaunchSnapshotFromPending(rocket)
|
||||
if rocket.Status == state.RocketStatusCompleted {
|
||||
rocket.CurrentLevel = roomRocketLevelCount
|
||||
rocket.FuelThreshold = roomRocketLevelByNumber(cfg, roomRocketLevelCount).FuelThreshold
|
||||
@ -510,6 +511,31 @@ func rocketStateForNow(input *state.RocketState, cfg RoomRocketConfig, now time.
|
||||
return rocket
|
||||
}
|
||||
|
||||
func syncRocketLaunchSnapshotFromPending(rocket *state.RocketState) {
|
||||
if rocket == nil {
|
||||
return
|
||||
}
|
||||
if len(rocket.PendingLaunches) == 0 {
|
||||
// 当前充能档没有待发射火箭时,不能继续暴露上一枚火箭的发射时间,否则客户端会把过期 launch_at_ms 渲染成 00:00:00。
|
||||
rocket.IgnitedAtMS = 0
|
||||
rocket.LaunchAtMS = 0
|
||||
rocket.Top1UserID = 0
|
||||
rocket.IgniterUserID = 0
|
||||
return
|
||||
}
|
||||
next := rocket.PendingLaunches[0]
|
||||
for _, launch := range rocket.PendingLaunches[1:] {
|
||||
if launch.LaunchAtMS > 0 && (next.LaunchAtMS <= 0 || launch.LaunchAtMS < next.LaunchAtMS) {
|
||||
next = launch
|
||||
}
|
||||
}
|
||||
// RoomRocketState 上的倒计时字段兼容旧客户端;真实多枚队列仍以 PendingLaunches 为准。
|
||||
rocket.IgnitedAtMS = next.IgnitedAtMS
|
||||
rocket.LaunchAtMS = next.LaunchAtMS
|
||||
rocket.Top1UserID = next.Top1UserID
|
||||
rocket.IgniterUserID = next.IgniterUserID
|
||||
}
|
||||
|
||||
func validRocketPendingLaunches(input []state.RocketPendingLaunch, resetAtMS int64) []state.RocketPendingLaunch {
|
||||
out := make([]state.RocketPendingLaunch, 0, len(input))
|
||||
seen := make(map[string]bool, len(input))
|
||||
@ -821,6 +847,7 @@ func (s *Service) launchRoomRocket(ctx context.Context, cmd command.LaunchRoomRo
|
||||
|
||||
pendingAfter := removeRocketPendingLaunch(current.Rocket.PendingLaunches, pendingIndex)
|
||||
current.Rocket.PendingLaunches = pendingAfter
|
||||
syncRocketLaunchSnapshotFromPending(current.Rocket)
|
||||
current.Rocket.LaunchedAtMS = nowMS
|
||||
current.Rocket.LastRewards = rewards
|
||||
|
||||
|
||||
@ -425,6 +425,9 @@ func TestRoomRocketLaunchGrantsLockedTop1AndIgniterAfterLeave(t *testing.T) {
|
||||
if stateView.GetCurrentLevel() != 2 || stateView.GetStatus() != state.RocketStatusCharging || len(stateView.GetPendingLaunches()) != 0 {
|
||||
t.Fatalf("launch rocket must keep current next-level progress and clear pending launch: %+v", stateView)
|
||||
}
|
||||
if stateView.GetLaunchAtMs() != 0 || stateView.GetIgnitedAtMs() != 0 || stateView.GetTop1UserId() != 0 || stateView.GetIgniterUserId() != 0 {
|
||||
t.Fatalf("launched rocket must not leak expired launch countdown into next charging level: %+v", stateView)
|
||||
}
|
||||
if len(stateView.GetLastRewards()) != 3 {
|
||||
t.Fatalf("launch rocket must retain in-room, top1 and igniter grants for client display: %+v", stateView.GetLastRewards())
|
||||
}
|
||||
|
||||
@ -181,13 +181,13 @@ type RocketState struct {
|
||||
FuelThreshold int64
|
||||
// Status 区分 charging/completed;已点火火箭在 PendingLaunches 中独立排队,不阻塞当前进度。
|
||||
Status string
|
||||
// IgnitedAtMS/LaunchAtMS 保留最近一次点火快照,客户端可用 PendingLaunches 获得完整队列。
|
||||
// IgnitedAtMS/LaunchAtMS 投影下一枚待发射火箭,完整多枚队列仍以 PendingLaunches 为准。
|
||||
IgnitedAtMS int64
|
||||
LaunchAtMS int64
|
||||
LaunchedAtMS int64
|
||||
// ResetAtMS 是下一次 UTC 自然日重置毫秒时间。
|
||||
ResetAtMS int64
|
||||
// Top1UserID 和 IgniterUserID 在燃料满瞬间锁定,用户离房或下线仍按这里发奖。
|
||||
// Top1UserID 和 IgniterUserID 投影下一枚待发射火箭的锁定用户;发奖以 PendingLaunches 内快照为准。
|
||||
Top1UserID int64
|
||||
IgniterUserID int64
|
||||
// RocketID 是单次火箭从积攒到发射的幂等标识,奖励抽取和 wallet command_id 都依赖它。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user