163 lines
4.1 KiB
Go
163 lines
4.1 KiB
Go
package service
|
||
|
||
import (
|
||
"fmt"
|
||
"ssq-desk/internal/storage/models"
|
||
"ssq-desk/internal/storage/repository"
|
||
)
|
||
|
||
// QueryService 查询服务
|
||
type QueryService struct {
|
||
repo repository.SsqRepository
|
||
}
|
||
|
||
// NewQueryService 创建查询服务
|
||
func NewQueryService(repo repository.SsqRepository) *QueryService {
|
||
return &QueryService{repo: repo}
|
||
}
|
||
|
||
// QueryRequest 查询请求
|
||
type QueryRequest struct {
|
||
RedBalls []int `json:"red_balls"` // 红球列表(最多6个)
|
||
BlueBall int `json:"blue_ball"` // 蓝球(0表示不限制)
|
||
BlueBallRange []int `json:"blue_ball_range"` // 蓝球筛选范围
|
||
}
|
||
|
||
// QueryResult 查询结果
|
||
type QueryResult struct {
|
||
Total int64 `json:"total"` // 总记录数
|
||
Summary []MatchSummary `json:"summary"` // 分类统计
|
||
Details []models.SsqHistory `json:"details"` // 详细记录
|
||
}
|
||
|
||
// MatchSummary 匹配统计
|
||
type MatchSummary struct {
|
||
Type string `json:"type"` // 匹配类型:如 "6红1蓝"
|
||
Count int `json:"count"` // 匹配数量
|
||
Histories []models.SsqHistory `json:"histories"` // 匹配的记录
|
||
}
|
||
|
||
// Query 执行查询
|
||
func (s *QueryService) Query(req QueryRequest) (*QueryResult, error) {
|
||
// 验证输入
|
||
if err := s.validateRequest(req); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 查询数据
|
||
histories, err := s.repo.FindByRedAndBlue(req.RedBalls, req.BlueBall, req.BlueBallRange)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("查询失败: %v", err)
|
||
}
|
||
|
||
// 处理结果:分类统计
|
||
summary := s.calculateSummary(histories, req.RedBalls, req.BlueBall)
|
||
|
||
return &QueryResult{
|
||
Total: int64(len(histories)),
|
||
Summary: summary,
|
||
Details: histories,
|
||
}, nil
|
||
}
|
||
|
||
// validateRequest 验证请求参数
|
||
func (s *QueryService) validateRequest(req QueryRequest) error {
|
||
// 验证红球
|
||
if len(req.RedBalls) > 6 {
|
||
return fmt.Errorf("红球数量不能超过6个")
|
||
}
|
||
for _, ball := range req.RedBalls {
|
||
if ball < 1 || ball > 33 {
|
||
return fmt.Errorf("红球号码必须在1-33之间: %d", ball)
|
||
}
|
||
}
|
||
|
||
// 验证蓝球
|
||
if req.BlueBall > 0 {
|
||
if req.BlueBall < 1 || req.BlueBall > 16 {
|
||
return fmt.Errorf("蓝球号码必须在1-16之间: %d", req.BlueBall)
|
||
}
|
||
}
|
||
|
||
// 验证蓝球范围
|
||
for _, ball := range req.BlueBallRange {
|
||
if ball < 1 || ball > 16 {
|
||
return fmt.Errorf("蓝球筛选范围必须在1-16之间: %d", ball)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// calculateSummary 计算分类统计
|
||
func (s *QueryService) calculateSummary(histories []models.SsqHistory, redBalls []int, blueBall int) []MatchSummary {
|
||
if len(redBalls) == 0 {
|
||
return []MatchSummary{}
|
||
}
|
||
|
||
// 创建红球集合,用于快速查找
|
||
redBallSet := make(map[int]bool)
|
||
for _, ball := range redBalls {
|
||
redBallSet[ball] = true
|
||
}
|
||
|
||
// 初始化统计
|
||
typeCounts := make(map[string][]models.SsqHistory)
|
||
|
||
// 遍历每条记录,计算匹配度
|
||
for _, history := range histories {
|
||
// 统计匹配的红球数量
|
||
matchedRedCount := 0
|
||
if redBallSet[history.RedBall1] {
|
||
matchedRedCount++
|
||
}
|
||
if redBallSet[history.RedBall2] {
|
||
matchedRedCount++
|
||
}
|
||
if redBallSet[history.RedBall3] {
|
||
matchedRedCount++
|
||
}
|
||
if redBallSet[history.RedBall4] {
|
||
matchedRedCount++
|
||
}
|
||
if redBallSet[history.RedBall5] {
|
||
matchedRedCount++
|
||
}
|
||
if redBallSet[history.RedBall6] {
|
||
matchedRedCount++
|
||
}
|
||
|
||
// 判断蓝球是否匹配
|
||
blueMatched := false
|
||
if blueBall > 0 {
|
||
blueMatched = history.BlueBall == blueBall
|
||
} else {
|
||
blueMatched = true // 未指定蓝球时,视为匹配
|
||
}
|
||
|
||
// 生成类型键
|
||
typeKey := fmt.Sprintf("%d红", matchedRedCount)
|
||
if blueMatched {
|
||
typeKey += "1蓝"
|
||
}
|
||
|
||
typeCounts[typeKey] = append(typeCounts[typeKey], history)
|
||
}
|
||
|
||
// 转换为结果格式,按匹配度排序
|
||
summary := make([]MatchSummary, 0)
|
||
types := []string{"6红1蓝", "6红", "5红1蓝", "5红", "4红1蓝", "4红", "3红1蓝", "3红", "2红1蓝", "2红", "1红1蓝", "1红", "0红1蓝", "0红"}
|
||
|
||
for _, t := range types {
|
||
if histories, ok := typeCounts[t]; ok {
|
||
summary = append(summary, MatchSummary{
|
||
Type: t,
|
||
Count: len(histories),
|
||
Histories: histories,
|
||
})
|
||
}
|
||
}
|
||
|
||
return summary
|
||
}
|