From 243aac0598d98a83d7ea71514c96c1e181ac1655 Mon Sep 17 00:00:00 2001 From: TT-RR Date: Thu, 21 Sep 2023 11:01:47 +0900 Subject: [PATCH 1/5] create SendEmail func --- util/util.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/util/util.go b/util/util.go index dee6365..b061ab2 100644 --- a/util/util.go +++ b/util/util.go @@ -3,7 +3,10 @@ package util import ( "context" "crypto/rand" + "fmt" + "log" "net/http" + "net/smtp" "strings" "time" @@ -115,3 +118,24 @@ func MakeRandomStr(digit int) (string, error) { } return result, nil } + +func SendMail(id ulid.ULID, password, targetAddrs string) error { + hostname := "smtp.gmail.com" // SMTPサーバーのホスト名 + port := 587 // SMTPサーバーのポート番号 + from := "magische@gmail.com" // 送信元のメールアドレス + recipients := []string{targetAddrs} // 送信先のメールアドレス + title := "magische 予約完了のお知らせ" // メールのタイトル + body := "test" // メールの本文 + + auth := smtp.PlainAuth("", targetAddrs, password, hostname) + msg := []byte(strings.ReplaceAll(fmt.Sprintf( + "To: %s\nSubject: %s\n\n%s", strings.Join(recipients, ","), title, body), + "\n", "\r\n")) + + // メール送信 + err := smtp.SendMail(fmt.Sprintf("%s:%d", hostname, port), auth, from, recipients, msg) + if err != nil { + log.Fatal(err) + } + return err +} From 83b9cd4ba32ec72d2735250557b6d838fe86b3a5 Mon Sep 17 00:00:00 2001 From: TT-RR Date: Thu, 21 Sep 2023 11:19:53 +0900 Subject: [PATCH 2/5] update message --- util/util.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/util/util.go b/util/util.go index b061ab2..d67fa17 100644 --- a/util/util.go +++ b/util/util.go @@ -124,8 +124,9 @@ func SendMail(id ulid.ULID, password, targetAddrs string) error { port := 587 // SMTPサーバーのポート番号 from := "magische@gmail.com" // 送信元のメールアドレス recipients := []string{targetAddrs} // 送信先のメールアドレス - title := "magische 予約完了のお知らせ" // メールのタイトル - body := "test" // メールの本文 + title := "magische 全員回答完了のお知らせ" // メールのタイトル + body := ("全員が回答しました!\n" + + "確認してください!\n") // メールの本文 auth := smtp.PlainAuth("", targetAddrs, password, hostname) msg := []byte(strings.ReplaceAll(fmt.Sprintf( From 7ebc10822972370f6a5639dcfe4599ddcb41c237 Mon Sep 17 00:00:00 2001 From: TT-RR Date: Thu, 21 Sep 2023 11:30:52 +0900 Subject: [PATCH 3/5] update --- util/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/util.go b/util/util.go index d67fa17..f86942a 100644 --- a/util/util.go +++ b/util/util.go @@ -138,5 +138,5 @@ func SendMail(id ulid.ULID, password, targetAddrs string) error { if err != nil { log.Fatal(err) } - return err + return nil } From 72832a8386e635badbda0b0ecf288eb671d5651f Mon Sep 17 00:00:00 2001 From: TT-RR Date: Thu, 21 Sep 2023 11:44:59 +0900 Subject: [PATCH 4/5] create new teble --- entity/event.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/entity/event.go b/entity/event.go index dae81ff..4633acd 100644 --- a/entity/event.go +++ b/entity/event.go @@ -48,3 +48,11 @@ type EventTimeUnitResponse struct { StartsAt time.Time `json:"startsAt"` EndsAt time.Time `json:"endsAt"` } + +type Confirm struct { + ID ulid.ULID `json:"id"` + EventID ulid.ULID `json:"-"` + NotifyByEmail bool `json:"notifyByEmail"` + NumberOfParticipants int `json:"numberOfParticipants"` + ConfirmationEmail string `json:"confirmationEmail"` +} From 5e4be09f9b358b20eaa6f68c634474b26f2a83a0 Mon Sep 17 00:00:00 2001 From: TT-RR Date: Fri, 22 Sep 2023 11:11:29 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=E4=BA=88=E7=B4=84=E7=A2=BA=E5=AE=9A?= =?UTF-8?q?=E6=A9=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/event.go | 8 ++++++++ entity/event.go | 43 ++++++++++++++++++++++++++++++------------- repository/event.go | 21 +++++++++++++++++++-- usecase/event.go | 6 ++++++ 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/controller/event.go b/controller/event.go index 15578e7..b7f7162 100644 --- a/controller/event.go +++ b/controller/event.go @@ -120,7 +120,14 @@ func (ec *eventController) CreateAnswer(c echo.Context) error { if err != nil { return err } + + if event.UserAnswersCount == event.NumberOfParticipants { + if err := util.SendMail(event.ID, event.ConfirmationEmail); err != nil { + return apperror.NewUnknownError(fmt.Errorf("failed to send confirmation email: %w", err), nil) + } + } eventRes := eventToEventResponse(event, user) + i := slices.IndexFunc(eventRes.UserAnswers, func(answer entity.UserEventAnswerResponse) bool { return answer.IsYourAnswer }) @@ -146,6 +153,7 @@ func (ec *eventController) RetrieveUserAnswer(c echo.Context) error { if err != nil { return err } + eventRes := eventToEventResponse(event, user) i := slices.IndexFunc(eventRes.UserAnswers, func(answer entity.UserEventAnswerResponse) bool { return answer.IsYourAnswer diff --git a/entity/event.go b/entity/event.go index 4633acd..12f3c12 100644 --- a/entity/event.go +++ b/entity/event.go @@ -7,22 +7,31 @@ import ( ) type Event struct { - ID ulid.ULID `json:"id"` - OwnerID ulid.ULID `json:"ownerId"` - Name string `json:"name"` - Description string `json:"description"` - DurationAbout string `json:"durationAbout"` - UnitSeconds int `json:"unitSeconds"` - Units []EventTimeUnit `json:"units"` - UserAnswers []UserEventAnswer `json:"userAnswers"` + ID ulid.ULID `json:"id"` + OwnerID ulid.ULID `json:"ownerId"` + Name string `json:"name"` + Description string `json:"description"` + DurationAbout string `json:"durationAbout"` + UnitSeconds int `json:"unitSeconds"` + Units []EventTimeUnit `json:"units"` + UserAnswers []UserEventAnswer `json:"userAnswers"` + UserAnswersCount int `json:"userAnswersCount"` + NotifyByEmail bool `json:"notifyByEmail"` + NumberOfParticipants int `json:"numberOfParticipants"` + ConfirmationEmail string `json:"confirmationEmail"` } + type EventRequest struct { - Name string `json:"name"` - Description string `json:"description"` - DurationAbout string `json:"durationAbout"` - UnitSeconds int `json:"unitDuration"` - Units []EventTimeUnitRequest `json:"units"` + Name string `json:"name"` + Description string `json:"description"` + DurationAbout string `json:"durationAbout"` + UnitSeconds int `json:"unitDuration"` + Units []EventTimeUnitRequest `json:"units"` + NotifyByEmail bool `json:"notifyByEmail"` + NumberOfParticipants int `json:"numberOfParticipants"` + ConfirmationEmail string `json:"confirmationEmail"` } + type EventResponse struct { ID string `json:"id"` OwnerID string `json:"ownerId"` @@ -56,3 +65,11 @@ type Confirm struct { NumberOfParticipants int `json:"numberOfParticipants"` ConfirmationEmail string `json:"confirmationEmail"` } + +type EventNotifyReservation struct { + ID ulid.ULID `json:"id"` + EventID ulid.ULID `json:"-"` + NotifyByEmail bool `json:"notifyByEmail"` + NumberOfParticipants int `json:"numberOfParticipants"` + ConfirmationEmail string `json:"confirmationEmail"` +} diff --git a/repository/event.go b/repository/event.go index e0c718e..901f8b1 100644 --- a/repository/event.go +++ b/repository/event.go @@ -30,12 +30,12 @@ type EventRepository interface { FetchEventAnswersWithUnits(ctx context.Context, tx *sql.Tx, eventId ulid.ULID) ([]entity.UserEventAnswer, error) // イベントの指定ユーザー回答(Unit無し)を取得する FetchEventAnswer(ctx context.Context, tx *sql.Tx, eventId ulid.ULID, userId ulid.ULID) (entity.UserEventAnswer, error) - + // 回答したユーザーの数を取得する + FetchUserAnswerCount(ctx context.Context, tx *sql.Tx, eventId ulid.ULID) (int, error) // イベント参加回答更新 UpdateEventAnswer(ctx context.Context, tx *sql.Tx, answer entity.UserEventAnswer) (entity.UserEventAnswer, error) // イベント参加回答時間単位を登録する RegisterAnswerUnits(ctx context.Context, tx *sql.Tx, answer []entity.UserEventAnswerUnit) ([]entity.UserEventAnswerUnit, error) - // // イベントとイベント単位を取得する // FetchEventAndUnits(ctx context.Context, tx *sql.Tx, eventId ulid.ULID) (entity.Event, error) // // イベントの全情報を取得する @@ -164,6 +164,7 @@ func (er *eventRepository) FetchEventTimeUnits(ctx context.Context, tx *sql.Tx, return etus, nil } +// イベントの全ユーザー回答(Unit付き)を取得する // FetchEventAnswersWithUnits implements EventRepository. func (er *eventRepository) FetchEventAnswersWithUnits(ctx context.Context, tx *sql.Tx, eventId ulid.ULID) ([]entity.UserEventAnswer, error) { // panic("unimplemented") @@ -259,6 +260,22 @@ func (er *eventRepository) FetchEventAnswer(ctx context.Context, tx *sql.Tx, eve }, nil } +// FetchUserAnswerCount implements EventRepository. +func (er *eventRepository) FetchUserAnswerCount(ctx context.Context, tx *sql.Tx, eventId ulid.ULID) (int, error) { + var exc boil.ContextExecutor = tx + if tx == nil { + exc = er.db + } + + count, err := models.UserEventAnswers( + models.UserEventAnswerWhere.EventID.EQ(util.ULIDToString(eventId)), + ).Count(ctx, exc) + if err != nil { + return 0, err + } + return int(count), nil +} + // UpdateEventAnswer implements EventRepository. func (er *eventRepository) UpdateEventAnswer(ctx context.Context, tx *sql.Tx, answer entity.UserEventAnswer) (entity.UserEventAnswer, error) { var exc boil.ContextExecutor = tx diff --git a/usecase/event.go b/usecase/event.go index f539c9a..b54e30b 100644 --- a/usecase/event.go +++ b/usecase/event.go @@ -156,6 +156,12 @@ func (eu *eventUsecase) CreateUserAnswer(ctx context.Context, eventId ulid.ULID, } newAnswer.Units = ansUnits + // ユーザーの回答数を数える + userAnswerCount, err := eu.er.FetchUserAnswerCount(ctx, tx, eventId) + if userAnswerCount != 0 { + return entity.UserEventAnswer{}, err + } + // commit! err = tx.Commit() if err != nil {