2025-06-17 03:10:31 +07:00

237 lines
6.9 KiB
Go

package services
import (
"context"
"encoding/base64"
"encoding/json"
"evening_detective/internal/models"
"evening_detective/internal/modules/password"
"evening_detective/internal/services/pdf_service"
"evening_detective/internal/services/story_service"
"evening_detective/proto"
"fmt"
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
type Services struct {
repository *Repository
storyService *story_service.StoryService
}
func NewServices(
repository *Repository,
storyService *story_service.StoryService,
) *Services {
return &Services{
repository: repository,
storyService: storyService,
}
}
func (s *Services) GiveApplications(ctx context.Context, req *proto.GiveApplicationsReq) (*proto.GiveApplicationsRsp, error) {
applications := mapProtoApplicationsToApplications(req.Applications)
if err := s.repository.GiveApplications(ctx, req.TeamId, applications); err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
return &proto.GiveApplicationsRsp{}, nil
}
func (s *Services) GetGame(ctx context.Context, _ *proto.GetGameReq) (*proto.GetGameRsp, error) {
game, err := s.repository.GetGame(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
return &proto.GetGameRsp{
State: game.State,
StartAt: game.StartTime,
EndAt: game.EndTime,
}, nil
}
func (s *Services) GameStart(ctx context.Context, _ *proto.GameStartReq) (*proto.GameStartRsp, error) {
if err := s.repository.GameUpdateState(ctx, "RUN"); err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
return &proto.GameStartRsp{}, nil
}
func (s *Services) GameStop(ctx context.Context, req *proto.GameStopReq) (*proto.GameStopRsp, error) {
if err := s.repository.GameUpdateState(ctx, "STOP"); err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
return &proto.GameStopRsp{}, nil
}
func (s *Services) AddAction(ctx context.Context, req *proto.AddActionReq) (*proto.AddActionRsp, error) {
team, err := s.getTeam(ctx)
if err != nil {
return nil, err
}
place := s.storyService.GetPlace(req.Place)
actions := []*models.Action{
{
Place: place.Code,
TeamID: team.ID,
Applications: mapStoryApplicationsToApplications(place.Applications),
},
}
if err := s.repository.AddActions(ctx, team.ID, actions); err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
currentApplications, err := s.repository.GetApplications(ctx, team.ID)
if err != nil {
return nil, err
}
newApplications := make([]*models.Application, 0, len(actions))
for _, action := range actions {
for _, actionApplication := range action.Applications {
f := false
for _, currentApplication := range currentApplications {
if currentApplication.Name == actionApplication.Name {
f = true
}
}
if !f {
newApplications = append(newApplications, actionApplication)
}
}
}
if err := s.repository.AddApplications(ctx, team.ID, newApplications); err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
addLog(team, "add action", actions)
return &proto.AddActionRsp{}, nil
}
func (s *Services) GetTeam(ctx context.Context, req *proto.GetTeamReq) (*proto.GetTeamRsp, error) {
team, err := s.getTeam(ctx)
if err != nil {
return nil, err
}
actions, err := s.repository.GetActions(ctx, team.ID)
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
res := make([]*proto.Action, 0, len(actions))
for _, action := range actions {
newAction := mapActionToProtoAction(action)
place := s.storyService.GetPlace(action.Place)
newAction.Text = place.Text
newAction.Name = place.Name
newAction.Applications = make([]*proto.Application, 0, len(place.Applications))
for _, application := range place.Applications {
newAction.Applications = append(newAction.Applications, mapStoryApplicationToProtoApplication(application))
}
res = append(res, newAction)
}
return &proto.GetTeamRsp{
Name: team.Name,
Actions: res,
}, err
}
func (s *Services) GetTeamsCSV(ctx context.Context, req *proto.GetTeamsCSVReq) (*proto.GetTeamsCSVRsp, error) {
panic("unimplemented")
}
func (s *Services) GetTeams(ctx context.Context, _ *proto.GetTeamsReq) (*proto.GetTeamsRsp, error) {
teams, err := s.repository.GetTeams(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
res := make([]*proto.TeamAdvanced, 0, len(teams))
for _, team := range teams {
newTeam := mapTeamsToTeamAdvanced(team)
actions, err := s.repository.GetActions(ctx, team.ID)
if err != nil {
return nil, err
}
newTeam.Url, err = team.GetTeamUrl()
if err != nil {
return nil, err
}
newTeam.SpendTime = int64(len(actions))
currentApplications, err := s.repository.GetApplicationsByState(ctx, team.ID, "NEW")
if err != nil {
return nil, err
}
newTeam.Applications = mapApplicationsToProtoApplications(currentApplications)
res = append(res, newTeam)
}
return &proto.GetTeamsRsp{Teams: res}, err
}
func (s *Services) AddTeams(ctx context.Context, req *proto.AddTeamsReq) (*proto.AddTeamsRsp, error) {
inTeams := make([]*models.Team, 0, len(req.Teams))
for _, team := range req.Teams {
t := mapProtoTeamsToTeam(team)
t.Password = password.GenPass(8)
inTeams = append(inTeams, t)
}
teams, err := s.repository.AddTeams(ctx, inTeams)
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
res := make([]*proto.TeamFull, 0, len(teams))
for _, team := range teams {
res = append(res, mapTeamsToTeamFull(team))
}
return &proto.AddTeamsRsp{Teams: res}, err
}
func (s *Services) DownloadTeamsQrCodesFile(ctx context.Context, req *proto.DownloadTeamsQrCodesFileReq) (*proto.DownloadTeamsQrCodesFileRsp, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
teams, err := s.repository.GetTeams(ctx)
if err != nil {
return nil, err
}
b, err := pdf_service.CreateTeamsPdf(teams)
if err != nil {
return nil, err
}
return &proto.DownloadTeamsQrCodesFileRsp{Result: b}, nil
}
func (s *Services) getTeam(ctx context.Context) (*models.Team, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Errorf(codes.Unauthenticated, "error creds")
}
teamIdArr, ok := md["team-id"]
if !ok {
return nil, status.Errorf(codes.Unauthenticated, "error creds")
}
teamId, err := base64.StdEncoding.DecodeString(teamIdArr[0])
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "error creds")
}
passwordArr, ok := md["password"]
if !ok {
return nil, status.Errorf(codes.Unauthenticated, "error creds")
}
password := passwordArr[0]
team, err := s.repository.GetTeam(ctx, teamId, password)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, err.Error())
}
return team, nil
}
func addLog(team *models.Team, action string, v any) {
vJson, err := json.Marshal(v)
if err == nil {
fmt.Printf("Team %s: %s %s\n", team.Name, action, string(vJson))
}
}