diff --git a/cmd/api/main.go b/cmd/api/main.go index af1d906..5b8ea8c 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -42,7 +42,7 @@ func main() { animalRepo := repository.NewAnimalRepository(db) animalUsecase := usecase.NewAnimalUsecase(userRepo, animalRepo) rest.RegisterAnimalHandler(animalUsecase, validator, api) - campaignUsecase := usecase.NewCampaignUsecase(campaignRepo, log, supabaseClient) + campaignUsecase := usecase.NewCampaignUsecase(campaignRepo, userRepo, log, supabaseClient) rest.RegisterCampaignHandler(campaignUsecase, api, validator) reportUsecase := usecase.NewReportUsecase(reportRepo, userRepo, supabaseClient) rest.RegisterReportHandler(reportUsecase, validator, api) diff --git a/db/migrations/20240427032748_campaign_submissions.up.sql b/db/migrations/20240427032748_campaign_submissions.up.sql index be57abc..ba66516 100644 --- a/db/migrations/20240427032748_campaign_submissions.up.sql +++ b/db/migrations/20240427032748_campaign_submissions.up.sql @@ -2,6 +2,7 @@ CREATE TABLE CampaignSubmissions ( UserID BIGINT UNSIGNED NOT NULL, CampaignID BIGINT UNSIGNED NOT NULL, Submission TEXT NOT NULL, + Status ENUM('PENDING', 'APPROVED', 'REJECTED') NOT NULL DEFAULT 'PENDING', CreatedAt DATETIME NOT NULL DEFAULT NOW(), FOREIGN KEY FKCampaignSubmissionsUsers (UserID) REFERENCES Users (ID) ON UPDATE CASCADE ON DELETE CASCADE, FOREIGN KEY FKCampaignSubmissionsCampaigns (CampaignID) REFERENCES Campaigns (ID) ON UPDATE CASCADE ON DELETE CASCADE diff --git a/internal/delivery/rest/campaign.go b/internal/delivery/rest/campaign.go index 09b636d..1aef775 100644 --- a/internal/delivery/rest/campaign.go +++ b/internal/delivery/rest/campaign.go @@ -31,6 +31,8 @@ func RegisterCampaignHandler( router.Delete("/:id", middleware.BearerAuth("true"), campaignHandler.Delete) router.Get("/_admin", middleware.BearerAuth("true"), campaignHandler.GetAllForAdmin) router.Get("/_admin/:id", middleware.BearerAuth("true"), campaignHandler.GetByIDForAdmin) + router.Get("/_submissions", middleware.BearerAuth("true"), campaignHandler.GetAllCampaignSubmission) + router.Patch("/_submissions/:id/:userID", middleware.BearerAuth("true"), campaignHandler.UpdateStatusCampaignSubmission) } func (h *campaignHandler) FetchAll(c *fiber.Ctx) error { @@ -178,3 +180,30 @@ func (h *campaignHandler) GetByIDForAdmin(c *fiber.Ctx) error { } return c.Status(fiber.StatusOK).JSON(campaign) } + +func (h *campaignHandler) GetAllCampaignSubmission(c *fiber.Ctx) error { + campaigns, err := h.usecase.GetAllCampaignSubmission(c.Context()) + if err != nil { + return err + } + return c.Status(fiber.StatusOK).JSON(campaigns) +} + +func (h *campaignHandler) UpdateStatusCampaignSubmission(c *fiber.Ctx) error { + id, err := strconv.ParseInt(c.Params("id"), 10, 64) + if err != nil { + return err + } + + userID, err := strconv.ParseInt(c.Params("userID"), 10, 64) + if err != nil { + return err + } + + value := c.Query("status") + err = h.usecase.UpdateStatusCampaignSubmission(c.Context(), id, userID, value) + if err != nil { + return err + } + return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Submission updated"}) +} diff --git a/internal/model/campaign.go b/internal/model/campaign.go index 21c3d23..989dfd0 100644 --- a/internal/model/campaign.go +++ b/internal/model/campaign.go @@ -27,4 +27,5 @@ type CampaignSubmission struct { UserID int64 `db:"UserID" json:"userId"` CampaignID int64 `db:"CampaignID" json:"campaignId"` Submission string `db:"Submission" json:"submission"` + Status string `db:"Status" json:"status"` } diff --git a/internal/repository/campaign.go b/internal/repository/campaign.go index a02a081..13e6614 100644 --- a/internal/repository/campaign.go +++ b/internal/repository/campaign.go @@ -30,6 +30,9 @@ type CampaignQueryerItf interface { GetByID(ctx context.Context, id int64) (model.Campaign, error) GetAll(ctx context.Context) ([]model.Campaign, error) CreateSubmission(ctx context.Context, campaignSubmission model.CampaignSubmission) error + GetAllCampaignSubmission(ctx context.Context) ([]model.CampaignSubmission, error) + UpdateStatusCampaignSubmission(ctx context.Context, submission model.CampaignSubmission) error + GetCampaignSubmissionByID(ctx context.Context, id int64, userID int64) (model.CampaignSubmission, error) } type campaignQueryer struct { @@ -155,3 +158,32 @@ func (q *campaignQueryer) CreateSubmission(ctx context.Context, campaignSubmissi _, err = q.ext.ExecContext(ctx, query, args...) return err } + +func (q *campaignQueryer) GetAllCampaignSubmission(ctx context.Context) ([]model.CampaignSubmission, error) { + var submissions []model.CampaignSubmission + if err := sqlx.SelectContext(ctx, q.ext, &submissions, qGetAllCampaignSubmission); err != nil { + return nil, err + } + return submissions, nil +} + +func (q *campaignQueryer) UpdateStatusCampaignSubmission(ctx context.Context, submission model.CampaignSubmission) error { + query, args, err := sqlx.Named(qUpdateStatusCampaignSubmission, fiber.Map{ + "UserID": submission.UserID, + "CampaignID": submission.CampaignID, + "Status": submission.Status, + }) + if err != nil { + return err + } + _, err = q.ext.ExecContext(ctx, query, args...) + return err +} + +func (q *campaignQueryer) GetCampaignSubmissionByID(ctx context.Context, id int64, userID int64) (model.CampaignSubmission, error) { + var submission model.CampaignSubmission + if err := sqlx.GetContext(ctx, q.ext, &submission, qGetCampaignSubmissionByID, id, userID); err != nil { + return model.CampaignSubmission{}, err + } + return submission, nil +} diff --git a/internal/repository/query.go b/internal/repository/query.go index 53ed1d1..34b0c85 100644 --- a/internal/repository/query.go +++ b/internal/repository/query.go @@ -152,6 +152,28 @@ const ( (CampaignID, UserID, Submission) VALUE (:CampaignID, :UserID, :Submission);` + qGetAllCampaignSubmission = ` + SELECT + CampaignID, + UserID, + Submission, + Status + FROM CampaignSubmissions + ` + qUpdateStatusCampaignSubmission = ` + UPDATE CampaignSubmissions + SET Status = :Status + WHERE CampaignID = :CampaignID AND UserID = :UserID; + ` + qGetCampaignSubmissionByID = ` + SELECT + CampaignID, + UserID, + Submission + FROM CampaignSubmissions + WHERE CampaignID = ? AND UserID = ? + LIMIT 1; + ` // Exchanges qCreateExchange = ` diff --git a/internal/usecase/campaign.go b/internal/usecase/campaign.go index ebca1ac..5d9edb4 100644 --- a/internal/usecase/campaign.go +++ b/internal/usecase/campaign.go @@ -21,20 +21,24 @@ type CampaignUsecaseItf interface { GetByID(ctx context.Context, id int64) (model.Campaign, error) GetAll(ctx context.Context) ([]model.Campaign, error) SubmitSubmission(ctx context.Context, id int64, req model.CampaignSubmissionRequest) error + GetAllCampaignSubmission(ctx context.Context) ([]model.CampaignSubmission, error) + UpdateStatusCampaignSubmission(ctx context.Context, id int64, userID int64, value string) error } type campaignUsecase struct { campaignRepo repository.CampaignRepositoryItf + userRepo repository.UserRepositoryItf log *logrus.Logger supabase *storage_go.Client } func NewCampaignUsecase( campaignRepo repository.CampaignRepositoryItf, + userRepo repository.UserRepositoryItf, log *logrus.Logger, supabase *storage_go.Client, ) CampaignUsecaseItf { - return &campaignUsecase{campaignRepo, log, supabase} + return &campaignUsecase{campaignRepo, userRepo, log, supabase} } func (u *campaignUsecase) FetchAll(ctx context.Context) ([]model.Campaign, error) { @@ -198,3 +202,50 @@ func (u *campaignUsecase) SubmitSubmission(ctx context.Context, id int64, req mo } return client.Commit() } + +func (u *campaignUsecase) GetAllCampaignSubmission(ctx context.Context) ([]model.CampaignSubmission, error) { + client, err := u.campaignRepo.NewClient(false, nil) + if err != nil { + return nil, err + } + submissions, err := client.GetAllCampaignSubmission(ctx) + if err != nil { + return nil, err + } + return submissions, nil +} + +func (u *campaignUsecase) UpdateStatusCampaignSubmission(ctx context.Context, id int64, userID int64, value string) error { + campaignClient, err := u.campaignRepo.NewClient(true, nil) + if err != nil { + return err + } + defer campaignClient.Rollback() + + submission, err := campaignClient.GetCampaignSubmissionByID(ctx, id, userID) + if err != nil { + return err + } + + userClient, err := u.userRepo.NewClient(true, nil) + if err != nil { + return err + } + defer userClient.Rollback() + + if value == "APPROVED" { + if err := userClient.UpdateBalance(ctx, userID, 100); err != nil { + return err + } + } + + submission.Status = value + err = campaignClient.UpdateStatusCampaignSubmission(ctx, submission) + if err != nil { + return err + } + if err := campaignClient.Commit(); err != nil { + return err + } + return userClient.Commit() +} diff --git a/internal/usecase/user.go b/internal/usecase/user.go index a7450d1..693f787 100644 --- a/internal/usecase/user.go +++ b/internal/usecase/user.go @@ -172,8 +172,7 @@ func (h *userUsecase) Exchange(ctx context.Context, exchange model.ExchangeReque return ErrInsufficientBalance } - user.Balance -= int(exchange.Amount) - if err := userClient.UpdateBalance(ctx, ctx.Value(ClientID).(int64), user.Balance); err != nil { + if err := userClient.UpdateBalance(ctx, ctx.Value(ClientID).(int64), -int(exchange.Amount)); err != nil { return err } @@ -194,7 +193,7 @@ func (h *userUsecase) Exchange(ctx context.Context, exchange model.ExchangeReque return err } - return nil + return userClient.Commit() } func (e *userUsecase) GetExchanges(ctx context.Context) ([]model.ExchangeCleanResource, error) {