diff --git a/api/externals/controller/campus_donation_controller.go b/api/externals/controller/campus_donation_controller.go
new file mode 100644
index 00000000..23c0e7b3
--- /dev/null
+++ b/api/externals/controller/campus_donation_controller.go
@@ -0,0 +1,52 @@
+package controller
+
+import (
+ "net/http"
+
+ "github.com/NUTFes/FinanSu/api/internals/usecase"
+ "github.com/labstack/echo/v4"
+)
+
+type campusDonationController struct {
+ u usecase.CampusDonationUseCase
+}
+
+type CampusDonationController interface {
+ IndexCampusDonationByFloor(echo.Context) error
+ IndexCampusDonationBuildingByPeriod(echo.Context) error
+}
+
+func NewCampusDonationController(u usecase.CampusDonationUseCase) CampusDonationController {
+ return &campusDonationController{u}
+}
+
+func (cdc *campusDonationController) IndexCampusDonationByFloor(c echo.Context) error {
+ ctx := c.Request().Context()
+ buildingId := c.Param("building_id")
+ floorId := c.Param("floor_id")
+
+ if buildingId == "" {
+ return c.String(http.StatusBadRequest, "building_id is required")
+ }
+
+ if floorId == "" {
+ return c.String(http.StatusBadRequest, "floor_id is required")
+ }
+
+ campusDonationByFloors, err := cdc.u.GetCampusDonationByFloors(ctx, buildingId, floorId)
+ if err != nil {
+ return c.String(http.StatusBadRequest, "failed to get campus donation by floor")
+ }
+
+ return c.JSON(http.StatusOK, campusDonationByFloors)
+}
+
+func (f *campusDonationController) IndexCampusDonationBuildingByPeriod(c echo.Context) error {
+ ctx := c.Request().Context()
+ year := c.Param("year")
+ fundInformationBuildingByPeriod, err := f.u.GetCampusDonationBuildingByPeriod(ctx, year)
+ if err != nil {
+ return err
+ }
+ return c.JSON(http.StatusOK, fundInformationBuildingByPeriod)
+}
diff --git a/api/externals/repository/campus_donation_repository.go b/api/externals/repository/campus_donation_repository.go
new file mode 100644
index 00000000..72a32a27
--- /dev/null
+++ b/api/externals/repository/campus_donation_repository.go
@@ -0,0 +1,132 @@
+package repository
+
+import (
+ "context"
+ "database/sql"
+ "log"
+
+ "github.com/NUTFes/FinanSu/api/drivers/db"
+ "github.com/NUTFes/FinanSu/api/externals/repository/abstract"
+ goqu "github.com/doug-martin/goqu/v9"
+)
+
+type campusDonationRepository struct {
+ client db.Client
+ crud abstract.Crud
+}
+
+type CampusDonationRepository interface {
+ AllCampusDonationByFloor(context.Context, string, string) (*sql.Rows, error)
+ AllBuildingsByPeriod(context.Context, string) (*sql.Rows, error)
+}
+
+func NewCampusDonationRepository(c db.Client, ac abstract.Crud) CampusDonationRepository {
+ return &campusDonationRepository{c, ac}
+}
+
+func (cdr *campusDonationRepository) AllCampusDonationByFloor(c context.Context, buildingId string, floorId string) (*sql.Rows, error) {
+
+ query, _, err := dialect.From("buildings").
+ Join(
+ goqu.T("building_units"),
+ goqu.On(goqu.Ex{"building_units.building_id": goqu.I("buildings.id")}),
+ ).
+ Join(
+ goqu.T("floors"),
+ goqu.On(goqu.Ex{"floors.building_unit_id": goqu.I("building_units.id")}),
+ ).
+ Join(
+ goqu.T("rooms"),
+ goqu.On(goqu.Ex{"rooms.floor_id": goqu.I("floors.id")}),
+ ).
+ Join(
+ goqu.T("room_teachers"),
+ goqu.On(goqu.Ex{"room_teachers.room_id": goqu.I("rooms.id")}),
+ ).
+ Join(
+ goqu.T("teachers"),
+ goqu.On(goqu.Ex{"teachers.id": goqu.I("room_teachers.teacher_id")}),
+ ).
+ LeftJoin(
+ goqu.T("campus_donations"),
+ goqu.On(goqu.Ex{"campus_donations.teacher_id": goqu.I("teachers.id")}),
+ ).
+ Select(
+ goqu.I("buildings.id").As("building_id"),
+ goqu.I("buildings.name").As("building_name"),
+ goqu.I("floors.id").As("floor_id"),
+ goqu.I("floors.floor_number").As("floor_number"),
+ goqu.I("teachers.id").As("teacher_id"),
+ goqu.I("teachers.name").As("teacher_name"),
+ goqu.I("rooms.room_name").As("room_name"),
+ goqu.I("campus_donations.price").As("price"),
+ goqu.I("teachers.is_black").As("is_black"),
+ ).
+ Where(
+ goqu.Ex{"buildings.id": buildingId, "floors.id": floorId},
+ ).
+ Order(
+ goqu.I("building_units.unit_number").Asc(),
+ goqu.I("floors.floor_number").Asc(),
+ ).
+ ToSQL()
+
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ return cdr.crud.Read(c, query)
+
+}
+
+func (fir *campusDonationRepository) AllBuildingsByPeriod(c context.Context, year string) (*sql.Rows, error) {
+
+ ds := dialect.From("campus_donations").
+ Select(
+ goqu.I("buildings.id"),
+ goqu.I("buildings.name"),
+ goqu.I("campus_donations.price"),
+ ).
+ Join(goqu.T("teachers"), goqu.On(goqu.Ex{
+ "campus_donations.teacher_id": goqu.I("teachers.id"),
+ })).
+ Join(goqu.T("users"), goqu.On(goqu.Ex{
+ "campus_donations.user_id": goqu.I("users.id"),
+ })).
+ Join(goqu.T("departments"), goqu.On(goqu.Ex{
+ "teachers.department_id": goqu.I("departments.id"),
+ })).
+ Join(goqu.T("room_teachers"), goqu.On(goqu.Ex{
+ "room_teachers.teacher_id": goqu.I("teachers.id"),
+ })).
+ Join(goqu.T("rooms"), goqu.On(goqu.Ex{
+ "room_teachers.room_id": goqu.I("rooms.id"),
+ })).
+ Join(goqu.T("floors"), goqu.On(goqu.Ex{
+ "rooms.floor_id": goqu.I("floors.id"),
+ })).
+ Join(goqu.T("building_units"), goqu.On(goqu.Ex{
+ "floors.building_unit_id": goqu.I("building_units.id"),
+ })).
+ Join(goqu.T("buildings"), goqu.On(goqu.Ex{
+ "building_units.building_id": goqu.I("buildings.id"),
+ })).
+ Join(goqu.T("year_periods"),
+ goqu.On(goqu.And(
+ goqu.I("campus_donations.created_at").Gt(goqu.I("year_periods.started_at")),
+ goqu.I("campus_donations.created_at").Lt(goqu.I("year_periods.ended_at")),
+ )),
+ ).
+ Join(goqu.T("years"), goqu.On(goqu.Ex{
+ "year_periods.year_id": goqu.I("years.id"),
+ })).
+ Where(goqu.Ex{"years.year": year}).
+ Order(goqu.I("campus_donations.updated_at").Desc())
+
+ query, _, err := ds.ToSQL()
+ if err != nil {
+ panic(err)
+ }
+
+ return fir.crud.Read(c, query)
+}
diff --git a/api/generated/openapi_gen.go b/api/generated/openapi_gen.go
index 5742f1d3..381d7cf5 100644
--- a/api/generated/openapi_gen.go
+++ b/api/generated/openapi_gen.go
@@ -67,6 +67,13 @@ type ActivityStyle struct {
SponsorStyleID int `json:"sponsorStyleID"`
}
+// BuildingTotal defines model for buildingTotal.
+type BuildingTotal struct {
+ Id *int `json:"id,omitempty"`
+ Name *string `json:"name,omitempty"`
+ TotalPrice *int `json:"totalPrice,omitempty"`
+}
+
// BuyReport 購入報告の際のパラメータ
type BuyReport struct {
Amount int `json:"amount"`
@@ -112,6 +119,22 @@ type BuyReportWithDivisionId struct {
PaidBy *string `json:"paidBy,omitempty"`
}
+// CampusDonation 教員単体の情報
+type CampusDonation struct {
+ IsBlack *bool `json:"is_black,omitempty"`
+ Price int `json:"price"`
+ RoomName string `json:"room_name"`
+ TeacherId int `json:"teacher_id"`
+ TeacherName string `json:"teacher_name"`
+}
+
+// CampusDonationByFloorAndBuilding 棟ごとの教員情報
+type CampusDonationByFloorAndBuilding struct {
+ BuildingId int `json:"building_id"`
+ BuildingName *string `json:"building_name,omitempty"`
+ Floors []FloorGroup `json:"floors"`
+}
+
// DestroyTeacherIDs defines model for destroyTeacherIDs.
type DestroyTeacherIDs struct {
DeleteIDs []float32 `json:"deleteIDs"`
@@ -222,6 +245,13 @@ type FinancialRecordWithBalance struct {
Year *int `json:"year,omitempty"`
}
+// FloorGroup フロアごとの教員情報
+type FloorGroup struct {
+ Donations []CampusDonation `json:"donations"`
+ FloorId *int `json:"floor_id,omitempty"`
+ FloorNumber string `json:"floor_number"`
+}
+
// Income defines model for income.
type Income struct {
Amount int `json:"amount"`
@@ -828,6 +858,12 @@ type ServerInterface interface {
// (PUT /buy_reports/{id})
PutBuyReportsId(ctx echo.Context, id int) error
+ // (GET /campus_donations/buildings/{year})
+ GetCampusDonationsBuildingsYear(ctx echo.Context, year int) error
+
+ // (GET /campus_donations/year/{year_id}/building/{building_id}/floor/{floor_id})
+ GetCampusDonationsYearYearIdBuildingBuildingIdFloorFloorId(ctx echo.Context, yearId int, buildingId int, floorId int) error
+
// (GET /departments)
GetDepartments(ctx echo.Context) error
@@ -1720,6 +1756,54 @@ func (w *ServerInterfaceWrapper) PutBuyReportsId(ctx echo.Context) error {
return err
}
+// GetCampusDonationsBuildingsYear converts echo context to params.
+func (w *ServerInterfaceWrapper) GetCampusDonationsBuildingsYear(ctx echo.Context) error {
+ var err error
+ // ------------- Path parameter "year" -------------
+ var year int
+
+ err = runtime.BindStyledParameterWithOptions("simple", "year", ctx.Param("year"), &year, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter year: %s", err))
+ }
+
+ // Invoke the callback with all the unmarshaled arguments
+ err = w.Handler.GetCampusDonationsBuildingsYear(ctx, year)
+ return err
+}
+
+// GetCampusDonationsYearYearIdBuildingBuildingIdFloorFloorId converts echo context to params.
+func (w *ServerInterfaceWrapper) GetCampusDonationsYearYearIdBuildingBuildingIdFloorFloorId(ctx echo.Context) error {
+ var err error
+ // ------------- Path parameter "year_id" -------------
+ var yearId int
+
+ err = runtime.BindStyledParameterWithOptions("simple", "year_id", ctx.Param("year_id"), &yearId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter year_id: %s", err))
+ }
+
+ // ------------- Path parameter "building_id" -------------
+ var buildingId int
+
+ err = runtime.BindStyledParameterWithOptions("simple", "building_id", ctx.Param("building_id"), &buildingId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter building_id: %s", err))
+ }
+
+ // ------------- Path parameter "floor_id" -------------
+ var floorId int
+
+ err = runtime.BindStyledParameterWithOptions("simple", "floor_id", ctx.Param("floor_id"), &floorId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter floor_id: %s", err))
+ }
+
+ // Invoke the callback with all the unmarshaled arguments
+ err = w.Handler.GetCampusDonationsYearYearIdBuildingBuildingIdFloorFloorId(ctx, yearId, buildingId, floorId)
+ return err
+}
+
// GetDepartments converts echo context to params.
func (w *ServerInterfaceWrapper) GetDepartments(ctx echo.Context) error {
var err error
@@ -3287,6 +3371,8 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
router.DELETE(baseURL+"/buy_reports/:id", wrapper.DeleteBuyReportsId)
router.GET(baseURL+"/buy_reports/:id", wrapper.GetBuyReportsId)
router.PUT(baseURL+"/buy_reports/:id", wrapper.PutBuyReportsId)
+ router.GET(baseURL+"/campus_donations/buildings/:year", wrapper.GetCampusDonationsBuildingsYear)
+ router.GET(baseURL+"/campus_donations/year/:year_id/building/:building_id/floor/:floor_id", wrapper.GetCampusDonationsYearYearIdBuildingBuildingIdFloorFloorId)
router.GET(baseURL+"/departments", wrapper.GetDepartments)
router.POST(baseURL+"/departments", wrapper.PostDepartments)
router.DELETE(baseURL+"/departments/:id", wrapper.DeleteDepartmentsId)
diff --git a/api/go.mod b/api/go.mod
index f51e9c27..62c68c67 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -14,7 +14,7 @@ require (
github.com/oapi-codegen/runtime v1.1.1
github.com/pkg/errors v0.9.1
github.com/slack-go/slack v0.13.0
- github.com/stretchr/testify v1.9.0
+ github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.31.0
gorm.io/driver/mysql v1.3.3
gorm.io/gorm v1.23.4
@@ -77,6 +77,7 @@ require (
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
+ github.com/samber/lo v1.50.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.29.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
diff --git a/api/go.sum b/api/go.sum
index 65cde78c..c7d706f6 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -981,6 +981,8 @@ github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
+github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY=
+github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
@@ -1009,6 +1011,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
diff --git a/api/internals/di/di.go b/api/internals/di/di.go
index d8f5489f..56a6bf99 100644
--- a/api/internals/di/di.go
+++ b/api/internals/di/di.go
@@ -60,6 +60,7 @@ func InitializeServer() (db.Client, *echo.Echo) {
transactionRepository := repository.NewTransactionRepository(client, crud)
userRepository := repository.NewUserRepository(client, crud)
yearRepository := repository.NewYearRepository(client, crud)
+ campusDonationRepository := repository.NewCampusDonationRepository(client, crud)
// ↓
// UseCase
@@ -99,6 +100,7 @@ func InitializeServer() (db.Client, *echo.Echo) {
teacherUseCase := usecase.NewTeacherUseCase(teacherRepository)
userUseCase := usecase.NewUserUseCase(userRepository, sessionRepository)
yearUseCase := usecase.NewYearUseCase(yearRepository)
+ campusDonationUseCase := usecase.NewCampusDonationUseCase(campusDonationRepository)
// ↓
// Controller
@@ -133,6 +135,7 @@ func InitializeServer() (db.Client, *echo.Echo) {
teacherController := controller.NewTeacherController(teacherUseCase)
userController := controller.NewUserController(userUseCase)
yearController := controller.NewYearController(yearUseCase)
+ campusDonationController := controller.NewCampusDonationController(campusDonationUseCase)
// ↓
// router
@@ -164,6 +167,7 @@ func InitializeServer() (db.Client, *echo.Echo) {
teacherController,
userController,
yearController,
+ campusDonationController,
)
// ↓
diff --git a/api/internals/domain/campus_donation.go b/api/internals/domain/campus_donation.go
new file mode 100644
index 00000000..8de22767
--- /dev/null
+++ b/api/internals/domain/campus_donation.go
@@ -0,0 +1,20 @@
+package domain
+
+type CampusDonationBuilding struct {
+ Id int `json:"id"`
+ Name string `json:"name"`
+ Price int `json:"price"`
+}
+
+// CampusDonationRecord は、1行分のフラットな寄付データを表します
+type CampusDonationRecord struct {
+ BuildingId int `json:"building_id"`
+ BuildingName *string `json:"building_name,omitempty"`
+ FloorId *int `json:"floor_id,omitempty"`
+ FloorNumber string `json:"floor_number"`
+ TeacherId int `json:"teacher_id"`
+ TeacherName string `json:"teacher_name"`
+ RoomName string `json:"room_name"`
+ Price int `json:"price"`
+ IsBlack *bool `json:"is_black,omitempty"`
+}
diff --git a/api/internals/usecase/campus_donation_usecase.go b/api/internals/usecase/campus_donation_usecase.go
new file mode 100644
index 00000000..18b62272
--- /dev/null
+++ b/api/internals/usecase/campus_donation_usecase.go
@@ -0,0 +1,162 @@
+package usecase
+
+import (
+ "context"
+ "log"
+
+ rep "github.com/NUTFes/FinanSu/api/externals/repository"
+ "github.com/NUTFes/FinanSu/api/generated"
+ "github.com/NUTFes/FinanSu/api/internals/domain"
+ "github.com/pkg/errors"
+ "github.com/samber/lo"
+)
+
+type campusDonationUseCase struct {
+ rep rep.CampusDonationRepository
+}
+
+type CampusDonationUseCase interface {
+ GetCampusDonationBuildingByPeriod(context.Context, string) ([]BuildingTotal, error)
+ GetCampusDonationByFloors(context.Context, string, string) ([]CampusDonationByFloorAndBuilding, error)
+}
+
+func NewCampusDonationUseCase(rep rep.CampusDonationRepository) CampusDonationUseCase {
+ return &campusDonationUseCase{rep}
+}
+
+func (cdu *campusDonationUseCase) GetCampusDonationByFloors(c context.Context, buildingId string, floorId string) ([]CampusDonationByFloorAndBuilding, error) {
+ //クエリ実行
+ rows, err := cdu.rep.AllCampusDonationByFloor(c, buildingId, floorId)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if err := rows.Close(); err != nil {
+ log.Println(err)
+ }
+ }()
+
+ var campusDonationRecords []domain.CampusDonationRecord
+ for rows.Next() {
+ var campusDonationRecord domain.CampusDonationRecord
+ if err := rows.Scan(
+ &campusDonationRecord.BuildingId,
+ &campusDonationRecord.BuildingName,
+ &campusDonationRecord.FloorId,
+ &campusDonationRecord.FloorNumber,
+ &campusDonationRecord.TeacherId,
+ &campusDonationRecord.TeacherName,
+ &campusDonationRecord.RoomName,
+ &campusDonationRecord.Price,
+ &campusDonationRecord.IsBlack,
+ ); err != nil {
+ return nil, errors.Wrap(err, "scanning flat campus donation record")
+ }
+ campusDonationRecords = append(campusDonationRecords, campusDonationRecord)
+ }
+
+ return convertCampusDonationRecordsToNestedStructure(campusDonationRecords), nil
+}
+
+func (f *campusDonationUseCase) GetCampusDonationBuildingByPeriod(c context.Context, year string) ([]BuildingTotal, error) {
+ rows, err := f.rep.AllBuildingsByPeriod(c, year)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if err := rows.Close(); err != nil {
+ log.Println(err)
+ }
+ }()
+
+ aggregated := make(map[int]*BuildingTotal)
+ for rows.Next() {
+ var buildingTotal domain.CampusDonationBuilding
+ if err := rows.Scan(&buildingTotal.Id, &buildingTotal.Name, &buildingTotal.Price); err != nil {
+ return nil, err
+ }
+ if b, exists := aggregated[buildingTotal.Id]; exists {
+ if b.TotalPrice == nil {
+ b.TotalPrice = new(int)
+ }
+ *b.TotalPrice += buildingTotal.Price
+ continue
+ }
+ id := buildingTotal.Id
+ name := buildingTotal.Name
+ price := buildingTotal.Price
+ aggregated[buildingTotal.Id] = &BuildingTotal{
+ Id: &id,
+ Name: &name,
+ TotalPrice: &price,
+ }
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+
+ var result []BuildingTotal
+ for _, b := range aggregated {
+ result = append(result, *b)
+ }
+ return result, nil
+}
+
+// convertCampusDonationRecordsToNestedStructure はcampusDonationRecordをネスト構造に変換する。
+func convertCampusDonationRecordsToNestedStructure(records []domain.CampusDonationRecord) []CampusDonationByFloorAndBuilding {
+ // 建物ごとにグループ化するためのマップを作成
+ groupMap := make(map[int]*CampusDonationByFloorAndBuilding)
+
+ for _, record := range records {
+ buildingGroup, ok := groupMap[record.BuildingId]
+ if !ok {
+ buildingGroup = &CampusDonationByFloorAndBuilding{
+ BuildingId: record.BuildingId,
+ BuildingName: record.BuildingName,
+ Floors: []FloorGroup{},
+ }
+ groupMap[record.BuildingId] = buildingGroup
+ }
+
+ // floorGroupの検索
+ var floorGroup *FloorGroup
+ for i := range buildingGroup.Floors {
+ if buildingGroup.Floors[i].FloorId != nil && record.FloorId != nil &&
+ *buildingGroup.Floors[i].FloorId == *record.FloorId {
+ floorGroup = &buildingGroup.Floors[i]
+ break
+ }
+ }
+ // ない場合、floorGroupを作成
+ if floorGroup == nil {
+ newFloorGroup := FloorGroup{
+ FloorId: record.FloorId,
+ FloorNumber: record.FloorNumber,
+ Donations: []CampusDonation{},
+ }
+ buildingGroup.Floors = append(buildingGroup.Floors, newFloorGroup)
+ floorGroup = &buildingGroup.Floors[len(buildingGroup.Floors)-1]
+ }
+
+ // campusDonationの追加
+ floorGroup.Donations = append(floorGroup.Donations, CampusDonation{
+ TeacherId: record.TeacherId,
+ TeacherName: record.TeacherName,
+ RoomName: record.RoomName,
+ Price: record.Price,
+ IsBlack: record.IsBlack,
+ })
+ }
+
+ // lo.MapToSliceを使用してマップをスライスに変換
+ return lo.MapToSlice(groupMap, func(_ int, v *CampusDonationByFloorAndBuilding) CampusDonationByFloorAndBuilding {
+ return *v
+ })
+}
+
+type CampusDonationByFloor = generated.CampusDonationByFloor
+type BuildingTotal generated.BuildingTotal
+type CampusDonationByFloorAndBuilding = generated.CampusDonationByFloorAndBuilding
+type CampusDonationRecord = domain.CampusDonationRecord
+type FloorGroup = generated.FloorGroup
+type CampusDonation = generated.CampusDonation
diff --git a/api/router/router.go b/api/router/router.go
index a85d9ee8..eaac6a2f 100644
--- a/api/router/router.go
+++ b/api/router/router.go
@@ -33,6 +33,7 @@ type router struct {
teacherController controller.TeacherController
userController controller.UserController
yearController controller.YearController
+ campusDonationController controller.CampusDonationController
}
type Router interface {
@@ -67,6 +68,7 @@ func NewRouter(
teacherController controller.TeacherController,
userController controller.UserController,
yearController controller.YearController,
+ campusDonationController controller.CampusDonationController,
) Router {
return router{
activityController,
@@ -96,6 +98,7 @@ func NewRouter(
teacherController,
userController,
yearController,
+ campusDonationController,
}
}
@@ -159,6 +162,10 @@ func (r router) ProvideRouter(e *echo.Echo) {
e.GET("/buy_reports/details", r.buyReportController.IndexBuyReport)
e.PUT("/buy_report/status/:buy_report_id", r.buyReportController.UpdateBuyReportStatus)
+ // campus_donationsのRoute
+ e.GET("/campus_donations/buildings/:year", r.campusDonationController.IndexCampusDonationBuildingByPeriod)
+ e.GET("/campus_donations/year/:year_id/building/:building_id/floor/:floor_id", r.campusDonationController.IndexCampusDonationByFloor)
+
// current_user
e.GET("/current_user", r.userController.GetCurrentUser)
diff --git a/er/columns.html b/er/columns.html
index 4c1d6144..aefec7d3 100644
--- a/er/columns.html
+++ b/er/columns.html
@@ -978,6 +978,160 @@
Columns
"defaultValue": "CURRENT_TIMESTAMP",
"comments": ""
},
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "primaryKey",
+ "keyTitle": "Primary Key",
+ "name": "id",
+ "type": "INT UNSIGNED",
+ "length": 10,
+ "nullable": "",
+ "autoUpdated": "√",
+ "defaultValue": "null",
+ "comments": ""
+ },
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "",
+ "keyTitle": "",
+ "name": "user_id",
+ "type": "INT",
+ "length": 10,
+ "nullable": "",
+ "autoUpdated": "",
+ "defaultValue": "null",
+ "comments": ""
+ },
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "",
+ "keyTitle": "",
+ "name": "teacher_id",
+ "type": "INT",
+ "length": 10,
+ "nullable": "",
+ "autoUpdated": "",
+ "defaultValue": "null",
+ "comments": ""
+ },
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "foreignKey",
+ "keyTitle": "Foreign Key",
+ "name": "year_id",
+ "type": "INT UNSIGNED",
+ "length": 10,
+ "nullable": "",
+ "autoUpdated": "",
+ "defaultValue": "null",
+ "comments": ""
+ },
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "",
+ "keyTitle": "",
+ "name": "price",
+ "type": "INT",
+ "length": 10,
+ "nullable": "",
+ "autoUpdated": "",
+ "defaultValue": "null",
+ "comments": ""
+ },
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "",
+ "keyTitle": "",
+ "name": "remark",
+ "type": "VARCHAR",
+ "length": 255,
+ "nullable": "√",
+ "autoUpdated": "",
+ "defaultValue": "null",
+ "comments": ""
+ },
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "",
+ "keyTitle": "",
+ "name": "is_first_check",
+ "type": "BIT",
+ "length": 1,
+ "nullable": "√",
+ "autoUpdated": "",
+ "defaultValue": "null",
+ "comments": ""
+ },
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "",
+ "keyTitle": "",
+ "name": "is_last_check",
+ "type": "BIT",
+ "length": 1,
+ "nullable": "√",
+ "autoUpdated": "",
+ "defaultValue": "null",
+ "comments": ""
+ },
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "",
+ "keyTitle": "",
+ "name": "received_at",
+ "type": "VARCHAR",
+ "length": 255,
+ "nullable": "",
+ "autoUpdated": "",
+ "defaultValue": "null",
+ "comments": ""
+ },
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "",
+ "keyTitle": "",
+ "name": "created_at",
+ "type": "DATETIME",
+ "length": 19,
+ "nullable": "",
+ "autoUpdated": "",
+ "defaultValue": "CURRENT_TIMESTAMP",
+ "comments": ""
+ },
+ {
+ "tableName": "campus_donations",
+ "tableFileName": "campus_donations",
+ "tableType": "Table",
+ "keyClass": "",
+ "keyTitle": "",
+ "name": "updated_at",
+ "type": "DATETIME",
+ "length": 19,
+ "nullable": "",
+ "autoUpdated": "",
+ "defaultValue": "CURRENT_TIMESTAMP",
+ "comments": ""
+ },
{
"tableName": "departments",
"tableFileName": "departments",
@@ -1328,146 +1482,6 @@ Columns
"defaultValue": "CURRENT_TIMESTAMP",
"comments": ""
},
- {
- "tableName": "fund_informations",
- "tableFileName": "fund_informations",
- "tableType": "Table",
- "keyClass": "primaryKey",
- "keyTitle": "Primary Key",
- "name": "id",
- "type": "INT UNSIGNED",
- "length": 10,
- "nullable": "",
- "autoUpdated": "√",
- "defaultValue": "null",
- "comments": ""
- },
- {
- "tableName": "fund_informations",
- "tableFileName": "fund_informations",
- "tableType": "Table",
- "keyClass": "",
- "keyTitle": "",
- "name": "user_id",
- "type": "INT",
- "length": 10,
- "nullable": "",
- "autoUpdated": "",
- "defaultValue": "null",
- "comments": ""
- },
- {
- "tableName": "fund_informations",
- "tableFileName": "fund_informations",
- "tableType": "Table",
- "keyClass": "",
- "keyTitle": "",
- "name": "teacher_id",
- "type": "INT",
- "length": 10,
- "nullable": "",
- "autoUpdated": "",
- "defaultValue": "null",
- "comments": ""
- },
- {
- "tableName": "fund_informations",
- "tableFileName": "fund_informations",
- "tableType": "Table",
- "keyClass": "",
- "keyTitle": "",
- "name": "price",
- "type": "INT",
- "length": 10,
- "nullable": "",
- "autoUpdated": "",
- "defaultValue": "null",
- "comments": ""
- },
- {
- "tableName": "fund_informations",
- "tableFileName": "fund_informations",
- "tableType": "Table",
- "keyClass": "",
- "keyTitle": "",
- "name": "remark",
- "type": "VARCHAR",
- "length": 255,
- "nullable": "√",
- "autoUpdated": "",
- "defaultValue": "null",
- "comments": ""
- },
- {
- "tableName": "fund_informations",
- "tableFileName": "fund_informations",
- "tableType": "Table",
- "keyClass": "",
- "keyTitle": "",
- "name": "is_first_check",
- "type": "BIT",
- "length": 1,
- "nullable": "√",
- "autoUpdated": "",
- "defaultValue": "null",
- "comments": ""
- },
- {
- "tableName": "fund_informations",
- "tableFileName": "fund_informations",
- "tableType": "Table",
- "keyClass": "",
- "keyTitle": "",
- "name": "is_last_check",
- "type": "BIT",
- "length": 1,
- "nullable": "√",
- "autoUpdated": "",
- "defaultValue": "null",
- "comments": ""
- },
- {
- "tableName": "fund_informations",
- "tableFileName": "fund_informations",
- "tableType": "Table",
- "keyClass": "",
- "keyTitle": "",
- "name": "received_at",
- "type": "VARCHAR",
- "length": 255,
- "nullable": "",
- "autoUpdated": "",
- "defaultValue": "null",
- "comments": ""
- },
- {
- "tableName": "fund_informations",
- "tableFileName": "fund_informations",
- "tableType": "Table",
- "keyClass": "",
- "keyTitle": "",
- "name": "created_at",
- "type": "DATETIME",
- "length": 19,
- "nullable": "",
- "autoUpdated": "",
- "defaultValue": "CURRENT_TIMESTAMP",
- "comments": ""
- },
- {
- "tableName": "fund_informations",
- "tableFileName": "fund_informations",
- "tableType": "Table",
- "keyClass": "",
- "keyTitle": "",
- "name": "updated_at",
- "type": "DATETIME",
- "length": 19,
- "nullable": "",
- "autoUpdated": "",
- "defaultValue": "CURRENT_TIMESTAMP",
- "comments": ""
- },
{
"tableName": "income_expenditure_managements",
"tableFileName": "income_expenditure_managements",
diff --git a/er/constraints.html b/er/constraints.html
index 4aa05faa..f823eeaa 100644
--- a/er/constraints.html
+++ b/er/constraints.html
@@ -80,7 +80,7 @@ Constraints