package roomadmin import ( "context" "database/sql" "encoding/json" "fmt" "sort" "time" "hyapp-admin-server/internal/appctx" ) var roomSeatCountCandidates = []int32{10, 15, 20, 25, 30} type RoomConfig struct { CandidateSeatCounts []int32 `json:"candidateSeatCounts"` AllowedSeatCounts []int32 `json:"allowedSeatCounts"` DefaultSeatCount int32 `json:"defaultSeatCount"` UpdatedAtMS int64 `json:"updatedAtMs"` } // GetRoomConfig 读取房间座位数配置;缺行时返回产品默认值,不阻塞首次打开配置页。 func (s *Service) GetRoomConfig(ctx context.Context) (RoomConfig, error) { if s.db == nil { return RoomConfig{}, fmt.Errorf("room mysql is not configured") } row := s.db.QueryRowContext(ctx, ` SELECT allowed_seat_counts, default_seat_count, updated_at_ms FROM room_seat_configs WHERE app_code = ? `, appctx.FromContext(ctx)) var rawAllowed string config := defaultRoomConfig() if err := row.Scan(&rawAllowed, &config.DefaultSeatCount, &config.UpdatedAtMS); err != nil { if err == sql.ErrNoRows { return config, nil } return RoomConfig{}, err } if err := json.Unmarshal([]byte(rawAllowed), &config.AllowedSeatCounts); err != nil { return RoomConfig{}, err } return normalizeRoomConfig(config) } // UpdateRoomConfig 保存后台房间配置;候选集合固定,后台只控制启用项和默认值。 func (s *Service) UpdateRoomConfig(ctx context.Context, req updateRoomConfigRequest) (RoomConfig, error) { if s.db == nil { return RoomConfig{}, fmt.Errorf("room mysql is not configured") } config, err := normalizeRoomConfig(RoomConfig{ AllowedSeatCounts: req.AllowedSeatCounts, DefaultSeatCount: req.DefaultSeatCount, UpdatedAtMS: time.Now().UTC().UnixMilli(), }) if err != nil { return RoomConfig{}, err } rawAllowed, err := json.Marshal(config.AllowedSeatCounts) if err != nil { return RoomConfig{}, err } now := config.UpdatedAtMS if _, err := s.db.ExecContext(ctx, ` INSERT INTO room_seat_configs (app_code, allowed_seat_counts, default_seat_count, created_at_ms, updated_at_ms) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE allowed_seat_counts = VALUES(allowed_seat_counts), default_seat_count = VALUES(default_seat_count), updated_at_ms = VALUES(updated_at_ms) `, appctx.FromContext(ctx), string(rawAllowed), config.DefaultSeatCount, now, now); err != nil { return RoomConfig{}, err } return config, nil } func defaultRoomConfig() RoomConfig { return RoomConfig{ CandidateSeatCounts: append([]int32(nil), roomSeatCountCandidates...), AllowedSeatCounts: append([]int32(nil), roomSeatCountCandidates...), DefaultSeatCount: 15, } } func normalizeRoomConfig(config RoomConfig) (RoomConfig, error) { candidateSet := make(map[int32]bool, len(roomSeatCountCandidates)) for _, candidate := range roomSeatCountCandidates { candidateSet[candidate] = true } seen := make(map[int32]bool, len(config.AllowedSeatCounts)) allowed := make([]int32, 0, len(config.AllowedSeatCounts)) for _, value := range config.AllowedSeatCounts { if !candidateSet[value] { return RoomConfig{}, fmt.Errorf("%w: 座位数只能选择 10、15、20、25、30", ErrInvalidArgument) } if seen[value] { return RoomConfig{}, fmt.Errorf("%w: 座位数不能重复", ErrInvalidArgument) } seen[value] = true allowed = append(allowed, value) } if len(allowed) == 0 { return RoomConfig{}, fmt.Errorf("%w: 至少启用一个座位数", ErrInvalidArgument) } sort.Slice(allowed, func(i int, j int) bool { return allowed[i] < allowed[j] }) if !seen[config.DefaultSeatCount] { return RoomConfig{}, fmt.Errorf("%w: 默认座位数必须在已启用座位数中", ErrInvalidArgument) } config.CandidateSeatCounts = append([]int32(nil), roomSeatCountCandidates...) config.AllowedSeatCounts = allowed return config, nil }