Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions api/externals/handler/campus_donation_handler.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,52 @@
package handler

import (
"database/sql"
"errors"
"net/http"
"strconv"

"github.com/NUTFes/FinanSu/api/generated"
"github.com/labstack/echo/v4"
)

// router.POST(baseURL+"/campus_donations", wrapper.PostCampusDonations)
func (h *Handler) PostCampusDonations(c echo.Context) error {
var request generated.PostCampusDonationsJSONRequestBody
if err := c.Bind(&request); err != nil {
return c.JSON(http.StatusBadRequest, "failed to bind")
}

campusDonation, err := h.campusDonationUseCase.CreateCampusDonation(c.Request().Context(), request)
if err != nil {
return c.JSON(http.StatusInternalServerError, err.Error())
}

return c.JSON(http.StatusOK, campusDonation)
}

// router.GET(baseURL+"/campus_donations/years/:year/group_keys/:group_key/floors", wrapper.GetCampusDonationsYearsYearGroupKeysGroupKeyFloors)
func (h *Handler) GetCampusDonationsYearsYearGroupKeysGroupKeyFloors(
c echo.Context,
year int,
groupKey generated.CampusDonationBuildingGroupKey,
params generated.GetCampusDonationsYearsYearGroupKeysGroupKeyFloorsParams,
) error {
groupKeyValue := string(groupKey)

buildingFloors, err := h.campusDonationUseCase.GetBuildingFloorDonationsByYear(
c.Request().Context(),
year,
&groupKeyValue,
params.FloorNumber,
)
if err != nil {
return err
}

return c.JSON(http.StatusOK, buildingFloors)
}

// router.GET(baseURL+"/campus_donations/buildings/:year", wrapper.GetCampusDonationsBuildingsYear)
func (h *Handler) GetCampusDonationsBuildingsYear(c echo.Context, year int) error {
yearStr := strconv.Itoa(year)
Expand All @@ -17,3 +57,21 @@ func (h *Handler) GetCampusDonationsBuildingsYear(c echo.Context, year int) erro

return c.JSON(http.StatusOK, buildingTotals)
}

// router.PUT(baseURL+"/campus_donations/:id", wrapper.PutCampusDonationsId)
func (h *Handler) PutCampusDonationsId(c echo.Context, id int) error {
var request generated.PutCampusDonationsIdJSONRequestBody
if err := c.Bind(&request); err != nil {
return c.JSON(http.StatusBadRequest, "failed to bind")
}

campusDonation, err := h.campusDonationUseCase.UpdateCampusDonation(c.Request().Context(), id, request)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return c.JSON(http.StatusNotFound, "campus donation not found")
}
return c.JSON(http.StatusInternalServerError, err.Error())
}

return c.JSON(http.StatusOK, campusDonation)
}
174 changes: 168 additions & 6 deletions api/externals/repository/campus_donation_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,174 @@ import (
"database/sql"

"github.com/NUTFes/FinanSu/api/drivers/db"
"github.com/NUTFes/FinanSu/api/externals/repository/abstract"
"github.com/NUTFes/FinanSu/api/internals/domain"
goqu "github.com/doug-martin/goqu/v9"
)

type campusDonationRepository struct {
client db.Client
crud abstract.Crud
}

type CampusDonationRepository interface {
Create(context.Context, domain.CampusDonation) (int, error)
Update(context.Context, int, domain.CampusDonation) error
FindByID(context.Context, int) (domain.CampusDonation, error)
GetBuildingFloorDonationsByYear(context.Context, int, *string, *string) (*sql.Rows, error)
AllBuildingTotalsByYear(context.Context, string) (*sql.Rows, error)
}

func NewCampusDonationRepository(c db.Client, ac abstract.Crud) CampusDonationRepository {
return &campusDonationRepository{c, ac}
func NewCampusDonationRepository(c db.Client) CampusDonationRepository {
return &campusDonationRepository{client: c}
}

func (cdr *campusDonationRepository) Create(ctx context.Context, donation domain.CampusDonation) (int, error) {
ds := dialect.
Insert(goqu.T("campus_donations")).
Rows(goqu.Record{
"user_id": donation.UserID,
"teacher_id": donation.TeacherID,
"year_id": donation.YearID,
"price": donation.Price,
"received_at": donation.ReceivedAt,
})

query, args, err := ds.ToSQL()
if err != nil {
return 0, err
}

result, err := cdr.client.DB().ExecContext(ctx, query, args...)
if err != nil {
return 0, err
}

id, err := result.LastInsertId()
if err != nil {
return 0, err
}

return int(id), nil
}

func (cdr *campusDonationRepository) Update(ctx context.Context, id int, donation domain.CampusDonation) error {
ds := dialect.
Update(goqu.T("campus_donations")).
Set(goqu.Record{
"user_id": donation.UserID,
"teacher_id": donation.TeacherID,
"year_id": donation.YearID,
"price": donation.Price,
"received_at": donation.ReceivedAt,
}).
Where(goqu.I("id").Eq(id))

query, args, err := ds.ToSQL()
if err != nil {
return err
}

_, err = cdr.client.DB().ExecContext(ctx, query, args...)
return err
}

func (cdr *campusDonationRepository) FindByID(ctx context.Context, id int) (domain.CampusDonation, error) {
ds := selectCampusDonationQuery().Where(goqu.I("campus_donations.id").Eq(id)).Limit(1)

query, args, err := ds.ToSQL()
if err != nil {
return domain.CampusDonation{}, err
}

var donation domain.CampusDonation
err = cdr.client.DB().QueryRowContext(ctx, query, args...).Scan(
&donation.ID,
&donation.UserID,
&donation.TeacherID,
&donation.YearID,
&donation.Price,
&donation.ReceivedAt,
)
if err != nil {
return domain.CampusDonation{}, err
}

return donation, nil
}

func (cdr *campusDonationRepository) GetBuildingFloorDonationsByYear(
ctx context.Context,
year int,
groupKey *string,
floorNumber *string,
) (*sql.Rows, error) {
donationTotalsByTeacher := dialect.
Select(
goqu.I("campus_donations.teacher_id").As("teacher_id"),
goqu.SUM(goqu.I("campus_donations.price")).As("total_price"),
).
From(goqu.T("campus_donations")).
InnerJoin(
goqu.T("years"),
goqu.On(goqu.I("campus_donations.year_id").Eq(goqu.I("years.id"))),
).
Where(goqu.I("years.year").Eq(year)).
GroupBy(goqu.I("campus_donations.teacher_id"))
Comment on lines +108 to +119
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The subquery donationTotalsByTeacher filters by year but does not filter by building or floor. While this is logically correct because it's joined later on teacher_id, it might be more efficient to include building/floor filters in the subquery if the campus_donations table is very large, to reduce the number of rows processed in the aggregation.


queryDataset := dialect.
From(goqu.T("buildings")).
InnerJoin(
goqu.T("rooms"),
goqu.On(goqu.I("rooms.building_id").Eq(goqu.I("buildings.id"))),
).
InnerJoin(
goqu.T("room_teachers"),
goqu.On(goqu.I("room_teachers.room_id").Eq(goqu.I("rooms.id"))),
).
InnerJoin(
goqu.T("teachers"),
goqu.On(goqu.I("teachers.id").Eq(goqu.I("room_teachers.teacher_id"))),
).
LeftJoin(
donationTotalsByTeacher.As("donation_totals"),
goqu.On(goqu.I("donation_totals.teacher_id").Eq(goqu.I("teachers.id"))),
).
Select(
goqu.I("buildings.id").As("building_id"),
goqu.I("buildings.name").As("building_name"),
goqu.I("buildings.unit_number").As("unit_number"),
goqu.I("rooms.floor_number").As("floor_number"),
goqu.I("rooms.room_name").As("room_name"),
goqu.I("teachers.id").As("teacher_id"),
goqu.I("teachers.name").As("teacher_name"),
goqu.I("donation_totals.total_price").As("total_price"),
goqu.COALESCE(goqu.I("teachers.is_black"), false).As("is_black"),
).
Order(
goqu.I("buildings.unit_number").Asc(),
goqu.I("buildings.id").Asc(),
goqu.I("rooms.floor_number").Asc(),
goqu.I("rooms.room_name").Asc(),
goqu.I("teachers.name").Asc(),
)

if groupKey != nil && *groupKey != "" {
queryDataset = queryDataset.Where(goqu.I("buildings.group_key").Eq(*groupKey))
}

if floorNumber != nil && *floorNumber != "" {
queryDataset = queryDataset.Where(goqu.I("rooms.floor_number").Eq(*floorNumber))
}

query, args, err := queryDataset.ToSQL()
if err != nil {
return nil, err
}

return cdr.client.DB().QueryContext(ctx, query, args...)
}

func (cdr *campusDonationRepository) AllBuildingTotalsByYear(
c context.Context,
ctx context.Context,
year string,
) (*sql.Rows, error) {
donationBuildingsDataset := dialect.
Expand Down Expand Up @@ -74,5 +223,18 @@ func (cdr *campusDonationRepository) AllBuildingTotalsByYear(
return nil, err
}

return cdr.client.DB().QueryContext(c, query, args...)
return cdr.client.DB().QueryContext(ctx, query, args...)
}

func selectCampusDonationQuery() *goqu.SelectDataset {
return dialect.
From(goqu.T("campus_donations")).
Select(
goqu.I("campus_donations.id").As("id"),
goqu.I("campus_donations.user_id").As("user_id"),
goqu.I("campus_donations.teacher_id").As("teacher_id"),
goqu.I("campus_donations.year_id").As("year_id"),
goqu.I("campus_donations.price").As("price"),
goqu.L("DATE_FORMAT(campus_donations.received_at, '%Y-%m-%d')").As("received_at"),
)
}
Loading