Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

csvで協賛企業のアップロードを行うAPIの作成&返り値取得のためrowAffeted件数分の取得を作成 #881

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
76 changes: 59 additions & 17 deletions api/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2087,26 +2087,69 @@ const docTemplate = `{
},
},
},
"/sponsors/periods/{year}": {
"get": {
tags: ["sponsor"],
"description": "年度で指定されたsponsorを取得",
"parameters": [
"/sponsors/periods/{year}": {
"get": {
tags: ["sponsor"],
"description": "年度で指定されたsponsorを取得",
"parameters": [
{
"name": "year",
"in": "path",
"description": "year",
"required": true,
"type": "integer"
}
],
"responses": {
"200": {
"description": "sponsorの取得完了",
}
}
}
},
"/sponsors/csv": {
"post": {
"tags": ["sponsor"],
"description": "CSVファイルを使ってsponsorを作成する",
"consumes": [
"multipart/form-data"
],
"parameters": [
{
"name": "year",
"in": "path",
"description": "year",
"required": true,
"type": "integer"
"name": "file",
"in": "formData",
"description": "CSVファイル",
"required": true,
"type": "file"
}
],
"responses": {
],
"responses": {
"200": {
"description": "sponsorの取得完了",
"description": "作成されたsponsorsの情報が返ってくる",
}
}
}
},
},
},
"/sponsors/rowAffected/{rowAffected}": {
"get": {
tags: ["sponsor"],
"description": "createdAt以降のsponsorの取得",
"parameters": [
{
"name": "rowAffected",
"in": "path",
"description": "rowAffected",
"required": true,
"type": "integer",
}
],
"responses": {
"200": {
"description": "sponsorの取得",
}
}
},
},
"/sponsorstyles": {
"get": {
tags: ["sponsorstyle"],
Expand Down Expand Up @@ -3012,7 +3055,6 @@ const docTemplate = `{
"year":{
"type": "int",
"example": 2024,

},
"startedAt":{
"type": "string",
Expand All @@ -3026,7 +3068,7 @@ const docTemplate = `{
"required":{
"year",
"startedAt",
"endedAt"
"endedAt",
},
},
},
Expand Down
31 changes: 30 additions & 1 deletion api/externals/controller/sponsor_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type SponsorController interface {
UpdateSponsor(echo.Context) error
DestroySponsor(echo.Context) error
IndexSponsorByPeriod(echo.Context) error
CreateSponsorsByCsv(echo.Context) error
IndexSponsorsByRowAffected(echo.Context) error
}

func NewSponsorController(u usecase.SponsorUseCase) SponsorController {
Expand Down Expand Up @@ -92,7 +94,7 @@ func (s *sponsorController) DestroySponsor(c echo.Context) error {
return c.String(http.StatusOK, "Destroy Sponsor")
}

//年度別に取得
// 年度別に取得
func (s *sponsorController) IndexSponsorByPeriod(c echo.Context) error {
year := c.Param("year")
sponsors, err := s.u.GetSponsorByPeriod(c.Request().Context(), year)
Expand All @@ -101,3 +103,30 @@ func (s *sponsorController) IndexSponsorByPeriod(c echo.Context) error {
}
return c.JSON(http.StatusOK, sponsors)
}

// cavで一括登録
func (s *sponsorController) CreateSponsorsByCsv(c echo.Context) error {
file, err := c.FormFile("file")
if err != nil {
return err
}
csv, err := file.Open()
if err != nil {
return err
}
defer csv.Close()
csvSponsor, err := s.u.CreateSponsorsByCsv(c.Request().Context(), csv)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"message": err.Error()})
}
return c.JSON(http.StatusOK, csvSponsor)
}

func (s *sponsorController) IndexSponsorsByRowAffected(c echo.Context) error {
row := c.Param("row")
sponsors, err := s.u.GetSponsorByRowAffected(c.Request().Context(), row)
if err != nil {
return err
}
return c.JSON(http.StatusOK, sponsors)
}
15 changes: 15 additions & 0 deletions api/externals/repository/abstract/abstract_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"strconv"

"github.com/NUTFes/FinanSu/api/drivers/db"
"github.com/pkg/errors"
Expand All @@ -17,6 +18,7 @@ type Crud interface {
Read(context.Context, string) (*sql.Rows, error)
ReadByID(context.Context, string) (*sql.Row, error)
UpdateDB(context.Context, string) error
UpdateAndReturnRows(context.Context, string) (string, error)
}

func NewCrud(client db.Client) Crud {
Expand Down Expand Up @@ -46,3 +48,16 @@ func (a abstractRepository) UpdateDB(ctx context.Context, query string) error {
fmt.Printf("\x1b[36m%s\n", query)
return err
}

func (a abstractRepository) UpdateAndReturnRows(ctx context.Context, query string) (string, error) {
rows, err := a.client.DB().ExecContext(ctx, query)
if err != nil {
return "", err
}
count, err := rows.RowsAffected()
if err != nil {
return "", err
}
countStr := strconv.FormatInt(count, 10)
return countStr, err
}
35 changes: 35 additions & 0 deletions api/externals/repository/sponsor_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package repository
import (
"context"
"database/sql"
"fmt"
"strings"

"github.com/NUTFes/FinanSu/api/drivers/db"
"github.com/NUTFes/FinanSu/api/externals/repository/abstract"
"github.com/NUTFes/FinanSu/api/internals/domain"
)

type sponsorRepository struct {
Expand All @@ -21,6 +24,8 @@ type SponsorRepository interface {
Delete(context.Context, string) error
FindLatestRecord(context.Context) (*sql.Row, error)
AllByPeriod(context.Context, string) (*sql.Rows, error)
CreateByCsv(context.Context, []domain.Sponsor) (string, error)
FindByRowsAffected(context.Context, string) (*sql.Rows, error)
}

func NewSponsorRepository(c db.Client, ac abstract.Crud) SponsorRepository {
Expand Down Expand Up @@ -116,3 +121,33 @@ func (sr *sponsorRepository) AllByPeriod(c context.Context, year string) (*sql.R
" ORDER BY sponsors.id;"
return sr.crud.Read(c, query)
}

// csvで一括登録
func (sr *sponsorRepository) CreateByCsv(c context.Context, csvRecords []domain.Sponsor) (string, error) {
query := `
INSERT INTO
sponsors (name, tel, email, address, representative)
VALUES`
values := []string{}
for _, record := range csvRecords {
values = append(values, fmt.Sprintf("('%s', '%s', '%s', '%s', '%s')",
record.Name,
record.Tel,
record.Email,
record.Address,
record.Representative,
))
}
query += strings.Join(values, ", ")
rowAffected, err := sr.crud.UpdateAndReturnRows(c, query)
if err != nil {
return "", err
}
return rowAffected, err
}

// rowの件数分取得
func (sr *sponsorRepository) FindByRowsAffected(c context.Context, row string) (*sql.Rows, error) {
query := fmt.Sprintf("SELECT * FROM sponsors ORDER BY id DESC LIMIT %s", row)
return sr.crud.Read(c, query)
}
126 changes: 126 additions & 0 deletions api/internals/usecase/sponsor_usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ package usecase

import (
"context"
"encoding/csv"
"fmt"
"io"
"strings"
"unicode/utf8"

rep "github.com/NUTFes/FinanSu/api/externals/repository"
"github.com/NUTFes/FinanSu/api/internals/domain"
Expand All @@ -18,6 +23,8 @@ type SponsorUseCase interface {
UpdateSponsor(context.Context, string, string, string, string, string, string) (domain.Sponsor, error)
DestroySponsor(context.Context, string) error
GetSponsorByPeriod(context.Context, string) ([]domain.Sponsor, error)
CreateSponsorsByCsv(context.Context, io.Reader) ([]domain.Sponsor, error)
GetSponsorByRowAffected(context.Context, string) ([]domain.Sponsor, error)
}

func NewSponsorUseCase(rep rep.SponsorRepository) SponsorUseCase {
Expand Down Expand Up @@ -160,3 +167,122 @@ func (s *sponsorUseCase) GetSponsorByPeriod(c context.Context, year string) ([]d
}
return sponsors, nil
}

func (s *sponsorUseCase) CreateSponsorsByCsv(c context.Context, csvFile io.Reader) ([]domain.Sponsor, error) {
sponsor := domain.Sponsor{}
var sponsors []domain.Sponsor

r := csv.NewReader(csvFile)
r.TrimLeadingSpace = true
records, err := r.ReadAll()
if err != nil {
return nil, err
}

if len(records) == 0 {
return nil, fmt.Errorf("csvの中身が空です。")
}

header := []string{"Name", "Tel", "Email", "Address", "Representative"}
records = removeBOM(records)

for i, record := range records {
if i == 0 {
if !isHeaderMatch(header, record) {
return nil, fmt.Errorf("異なるヘッダーがあります。")
}
Kubosaka marked this conversation as resolved.
Show resolved Hide resolved
continue
}

for j := range record {
if isEmpty(record[j]) {
return nil, fmt.Errorf("空のレコードがあります。")
}
}

sponsor := domain.Sponsor{
Name: record[0],
Tel: record[1],
Email: record[2],
Address: record[3],
Representative: record[4],
}
sponsors = append(sponsors, sponsor)
}
rowAffected, err := s.rep.CreateByCsv(c, sponsors)
if err != nil {
return nil, err
}

sponsors = []domain.Sponsor{}
rows, err := s.rep.FindByRowsAffected(c, string(rowAffected))
if err != nil {
return nil, err
}
for rows.Next() {
err := rows.Scan(
&sponsor.ID,
&sponsor.Name,
&sponsor.Tel,
&sponsor.Email,
&sponsor.Address,
&sponsor.Representative,
&sponsor.CreatedAt,
&sponsor.UpdatedAt,
)
if err != nil {
return nil, err
}
sponsors = append(sponsors, sponsor)
}
return sponsors, nil
}

func (s *sponsorUseCase) GetSponsorByRowAffected(c context.Context, row string) ([]domain.Sponsor, error) {
sponsor := domain.Sponsor{}
var sponsors []domain.Sponsor
rows, err := s.rep.FindByRowsAffected(c, row)
if err != nil {
return nil, err
}
for rows.Next() {
err := rows.Scan(
&sponsor.ID,
&sponsor.Name,
&sponsor.Tel,
&sponsor.Email,
&sponsor.Address,
&sponsor.Representative,
&sponsor.CreatedAt,
&sponsor.UpdatedAt,
)
if err != nil {
return nil, err
}
sponsors = append(sponsors, sponsor)
}
return sponsors, nil
}

func isEmpty(s string) bool {
return s == ""
}

func removeBOM(header [][]string) [][]string {
for i, row := range header {
if len(row) > 0 && strings.HasPrefix(row[0], "\uFEFF") {
_, size := utf8.DecodeRuneInString(row[0])
header[i][0] = row[0][size:]
}
}
return header
}

func isHeaderMatch(headers []string, records []string) bool {
for i := range headers {
if headers[i] != records[i] {
return false
}
}
return true
}
2 changes: 2 additions & 0 deletions api/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ func (r router) ProvideRouter(e *echo.Echo) {
e.PUT("/sponsors/:id", r.sponsorController.UpdateSponsor)
e.DELETE("/sponsors/:id", r.sponsorController.DestroySponsor)
e.GET("/sponsors/periods/:year", r.sponsorController.IndexSponsorByPeriod)
e.POST("/sponsors/csv", r.sponsorController.CreateSponsorsByCsv)
e.GET("/sponsors/rowAffected/:row", r.sponsorController.IndexSponsorsByRowAffected)

// sponsorstylesのRoute
e.GET("/sponsorstyles", r.sponsorStyleController.IndexSponsorStyle)
Expand Down
Loading