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

重複回答の排除 #1155

Closed
wants to merge 3 commits into from
Closed
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
2 changes: 2 additions & 0 deletions model/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ var (
ErrInvalidTx = errors.New("invalid tx")
// ErrDeadlineExceeded deadline exceeded
ErrDeadlineExceeded = errors.New("deadline exceeded")
// ErrDuplicatedAnswered
ErrDuplicatedAnswered = errors.New("duplicated answered is not allowed")
)
4 changes: 2 additions & 2 deletions model/questionnaires.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (

// IQuestionnaire QuestionnaireのRepository
type IQuestionnaire interface {
InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string) (int, error)
UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, questionnaireID int) error
InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, IsDuplicateAnswerAllowed bool) (int, error)
UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, IsDuplicateAnswerAllowed bool, questionnaireID int) error
DeleteQuestionnaire(ctx context.Context, questionnaireID int) error
GetQuestionnaires(ctx context.Context, userID string, sort string, search string, pageNum int, nontargeted bool) ([]QuestionnaireInfo, int, error)
GetAdminQuestionnaires(ctx context.Context, userID string) ([]Questionnaires, error)
Expand Down
63 changes: 34 additions & 29 deletions model/questionnaires_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,19 @@ func NewQuestionnaire() *Questionnaire {

// Questionnaires questionnairesテーブルの構造体
type Questionnaires struct {
ID int `json:"questionnaireID" gorm:"type:int(11) AUTO_INCREMENT;not null;primaryKey"`
Title string `json:"title" gorm:"type:char(50);size:50;not null"`
Description string `json:"description" gorm:"type:text;not null"`
ResTimeLimit null.Time `json:"res_time_limit,omitempty" gorm:"type:TIMESTAMP NULL;default:NULL;"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"type:TIMESTAMP NULL;default:NULL;"`
ResSharedTo string `json:"res_shared_to" gorm:"type:char(30);size:30;not null;default:administrators"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"`
ModifiedAt time.Time `json:"modified_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"`
Administrators []Administrators `json:"-" gorm:"foreignKey:QuestionnaireID"`
Targets []Targets `json:"-" gorm:"foreignKey:QuestionnaireID"`
Questions []Questions `json:"-" gorm:"foreignKey:QuestionnaireID"`
Respondents []Respondents `json:"-" gorm:"foreignKey:QuestionnaireID"`
ID int `json:"questionnaireID" gorm:"type:int(11) AUTO_INCREMENT;not null;primaryKey"`
Title string `json:"title" gorm:"type:char(50);size:50;not null"`
Description string `json:"description" gorm:"type:text;not null"`
ResTimeLimit null.Time `json:"res_time_limit,omitempty" gorm:"type:TIMESTAMP NULL;default:NULL;"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"type:TIMESTAMP NULL;default:NULL;"`
ResSharedTo string `json:"res_shared_to" gorm:"type:char(30);size:30;not null;default:administrators"`
IsDuplicateAnswerAllowed bool `json:"is_duplicate_answer_allowed" gorm:"type:tinyint(4);size:4;not null;default:0"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"`
ModifiedAt time.Time `json:"modified_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"`
Administrators []Administrators `json:"-" gorm:"foreignKey:QuestionnaireID"`
Targets []Targets `json:"-" gorm:"foreignKey:QuestionnaireID"`
Questions []Questions `json:"-" gorm:"foreignKey:QuestionnaireID"`
Respondents []Respondents `json:"-" gorm:"foreignKey:QuestionnaireID"`
}

// BeforeCreate Update時に自動でmodified_atを現在時刻に
Expand Down Expand Up @@ -79,7 +80,7 @@ type ResponseReadPrivilegeInfo struct {
}

// InsertQuestionnaire アンケートの追加
func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string) (int, error) {
func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, isDuplicateAnswerAllowed bool) (int, error) {
db, err := getTx(ctx)
if err != nil {
return 0, fmt.Errorf("failed to get tx: %w", err)
Expand All @@ -88,16 +89,18 @@ func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, des
var questionnaire Questionnaires
if !resTimeLimit.Valid {
questionnaire = Questionnaires{
Title: title,
Description: description,
ResSharedTo: resSharedTo,
Title: title,
Description: description,
ResSharedTo: resSharedTo,
IsDuplicateAnswerAllowed: isDuplicateAnswerAllowed,
}
} else {
questionnaire = Questionnaires{
Title: title,
Description: description,
ResTimeLimit: resTimeLimit,
ResSharedTo: resSharedTo,
Title: title,
Description: description,
ResTimeLimit: resTimeLimit,
ResSharedTo: resSharedTo,
IsDuplicateAnswerAllowed: isDuplicateAnswerAllowed,
}
}

Expand All @@ -110,7 +113,7 @@ func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, des
}

// UpdateQuestionnaire アンケートの更新
func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, questionnaireID int) error {
func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, isDuplicateAnswerAllowed bool, questionnaireID int) error {
db, err := getTx(ctx)
if err != nil {
return fmt.Errorf("failed to get tx: %w", err)
Expand All @@ -119,17 +122,19 @@ func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, des
var questionnaire interface{}
if resTimeLimit.Valid {
questionnaire = Questionnaires{
Title: title,
Description: description,
ResTimeLimit: resTimeLimit,
ResSharedTo: resSharedTo,
Title: title,
Description: description,
ResTimeLimit: resTimeLimit,
ResSharedTo: resSharedTo,
IsDuplicateAnswerAllowed: isDuplicateAnswerAllowed,
}
} else {
questionnaire = map[string]interface{}{
"title": title,
"description": description,
"res_time_limit": gorm.Expr("NULL"),
"res_shared_to": resSharedTo,
"title": title,
"description": description,
"res_time_limit": gorm.Expr("NULL"),
"res_shared_to": resSharedTo,
"is_duplicate_answer_allowed": isDuplicateAnswerAllowed,
}
}

Expand Down
54 changes: 31 additions & 23 deletions model/questionnaires_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,11 @@ func insertQuestionnaireTest(t *testing.T) {
assertion := assert.New(t)

type args struct {
title string
description string
resTimeLimit null.Time
resSharedTo string
title string
description string
resTimeLimit null.Time
resSharedTo string
isDuplicateAnswerAllowed bool
}
type expect struct {
isErr bool
Expand Down Expand Up @@ -451,7 +452,7 @@ func insertQuestionnaireTest(t *testing.T) {
for _, testCase := range testCases {
ctx := context.Background()

questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, testCase.args.title, testCase.args.description, testCase.args.resTimeLimit, testCase.args.resSharedTo)
questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, testCase.args.title, testCase.args.description, testCase.args.resTimeLimit, testCase.args.resSharedTo, testCase.args.isDuplicateAnswerAllowed)

if !testCase.expect.isErr {
assertion.NoError(err, testCase.description, "no error")
Expand All @@ -475,6 +476,7 @@ func insertQuestionnaireTest(t *testing.T) {
assertion.Equal(testCase.args.description, questionnaire.Description, testCase.description, "description")
assertion.WithinDuration(testCase.args.resTimeLimit.ValueOrZero(), questionnaire.ResTimeLimit.ValueOrZero(), 2*time.Second, testCase.description, "res_time_limit")
assertion.Equal(testCase.args.resSharedTo, questionnaire.ResSharedTo, testCase.description, "res_shared_to")
assertion.Equal(testCase.args.isDuplicateAnswerAllowed, questionnaire.IsDuplicateAnswerAllowed, testCase.description, "is_duplicate_answer_allowed")

assertion.WithinDuration(time.Now(), questionnaire.CreatedAt, 2*time.Second, testCase.description, "created_at")
assertion.WithinDuration(time.Now(), questionnaire.ModifiedAt, 2*time.Second, testCase.description, "modified_at")
Expand All @@ -487,10 +489,11 @@ func updateQuestionnaireTest(t *testing.T) {
assertion := assert.New(t)

type args struct {
title string
description string
resTimeLimit null.Time
resSharedTo string
title string
description string
resTimeLimit null.Time
resSharedTo string
isDuplicateAnswerAllowed bool
}
type expect struct {
isErr bool
Expand Down Expand Up @@ -647,10 +650,11 @@ func updateQuestionnaireTest(t *testing.T) {

before := &testCase.before
questionnaire := Questionnaires{
Title: before.title,
Description: before.description,
ResTimeLimit: before.resTimeLimit,
ResSharedTo: before.resSharedTo,
Title: before.title,
Description: before.description,
ResTimeLimit: before.resTimeLimit,
ResSharedTo: before.resSharedTo,
IsDuplicateAnswerAllowed: before.isDuplicateAnswerAllowed,
}
err := db.
Session(&gorm.Session{NewDB: true}).
Expand All @@ -662,7 +666,7 @@ func updateQuestionnaireTest(t *testing.T) {
createdAt := questionnaire.CreatedAt
questionnaireID := questionnaire.ID
after := &testCase.after
err = questionnaireImpl.UpdateQuestionnaire(ctx, after.title, after.description, after.resTimeLimit, after.resSharedTo, questionnaireID)
err = questionnaireImpl.UpdateQuestionnaire(ctx, after.title, after.description, after.resTimeLimit, after.resSharedTo, after.isDuplicateAnswerAllowed, questionnaireID)

if !testCase.expect.isErr {
assertion.NoError(err, testCase.description, "no error")
Expand All @@ -686,6 +690,7 @@ func updateQuestionnaireTest(t *testing.T) {
assertion.Equal(after.description, questionnaire.Description, testCase.description, "description")
assertion.WithinDuration(after.resTimeLimit.ValueOrZero(), questionnaire.ResTimeLimit.ValueOrZero(), 2*time.Second, testCase.description, "res_time_limit")
assertion.Equal(after.resSharedTo, questionnaire.ResSharedTo, testCase.description, "res_shared_to")
assertion.Equal(after.isDuplicateAnswerAllowed, questionnaire.IsDuplicateAnswerAllowed, testCase.description, "is_duplicate_answer_allowed")

assertion.WithinDuration(createdAt, questionnaire.CreatedAt, 2*time.Second, testCase.description, "created_at")
assertion.WithinDuration(time.Now(), questionnaire.ModifiedAt, 2*time.Second, testCase.description, "modified_at")
Expand Down Expand Up @@ -726,7 +731,7 @@ func updateQuestionnaireTest(t *testing.T) {
for _, arg := range invalidTestCases {
ctx := context.Background()

err := questionnaireImpl.UpdateQuestionnaire(ctx, arg.title, arg.description, arg.resTimeLimit, arg.resSharedTo, invalidQuestionnaireID)
err := questionnaireImpl.UpdateQuestionnaire(ctx, arg.title, arg.description, arg.resTimeLimit, arg.resSharedTo, arg.isDuplicateAnswerAllowed, invalidQuestionnaireID)
if !errors.Is(err, ErrNoRecordUpdated) {
if err == nil {
t.Errorf("Succeeded with invalid questionnaireID")
Expand All @@ -743,10 +748,11 @@ func deleteQuestionnaireTest(t *testing.T) {
assertion := assert.New(t)

type args struct {
title string
description string
resTimeLimit null.Time
resSharedTo string
title string
description string
resTimeLimit null.Time
resSharedTo string
isDuplicateAnswerAllowed bool
}
type expect struct {
isErr bool
Expand All @@ -772,10 +778,11 @@ func deleteQuestionnaireTest(t *testing.T) {
ctx := context.Background()

questionnaire := Questionnaires{
Title: testCase.args.title,
Description: testCase.args.description,
ResTimeLimit: testCase.args.resTimeLimit,
ResSharedTo: testCase.args.resSharedTo,
Title: testCase.args.title,
Description: testCase.args.description,
ResTimeLimit: testCase.args.resTimeLimit,
ResSharedTo: testCase.args.resSharedTo,
IsDuplicateAnswerAllowed: testCase.args.isDuplicateAnswerAllowed,
}
err := db.
Session(&gorm.Session{NewDB: true}).
Expand Down Expand Up @@ -1341,6 +1348,7 @@ func getQuestionnaireInfoTest(t *testing.T) {
assertion.Equal(testCase.expect.questionnaire.Title, actualQuestionnaire.Title, testCase.description, "questionnaire(Title)")
assertion.Equal(testCase.expect.questionnaire.Description, actualQuestionnaire.Description, testCase.description, "questionnaire(Description)")
assertion.Equal(testCase.expect.questionnaire.ResSharedTo, actualQuestionnaire.ResSharedTo, testCase.description, "questionnaire(ResSharedTo)")
assertion.Equal(testCase.expect.questionnaire.IsDuplicateAnswerAllowed, actualQuestionnaire.IsDuplicateAnswerAllowed, testCase.description, "questionnaire(IsDuplicateAnswerAllowed)")
assertion.WithinDuration(testCase.expect.questionnaire.ResTimeLimit.ValueOrZero(), actualQuestionnaire.ResTimeLimit.ValueOrZero(), 2*time.Second, testCase.description, "questionnaire(ResTimeLimit)")
assertion.WithinDuration(testCase.expect.questionnaire.CreatedAt, actualQuestionnaire.CreatedAt, 2*time.Second, testCase.description, "questionnaire(CreatedAt)")
assertion.WithinDuration(testCase.expect.questionnaire.ModifiedAt, actualQuestionnaire.ModifiedAt, 2*time.Second, testCase.description, "questionnaire(ModifiedAt)")
Expand Down
26 changes: 26 additions & 0 deletions model/respondents_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,19 @@ func (*Respondent) InsertRespondent(ctx context.Context, userID string, question
return 0, fmt.Errorf("failed to get tx: %w", err)
}

var questionnaire Questionnaires
var respondent Respondents

err = db.
Where("id = ?", questionnaireID).
First(&questionnaire).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return 0, ErrRecordNotFound
}
if err != nil {
return 0, fmt.Errorf("failed to get questionnaire: %w", err)
}

if submittedAt.Valid {
respondent = Respondents{
QuestionnaireID: questionnaireID,
Expand All @@ -86,12 +98,26 @@ func (*Respondent) InsertRespondent(ctx context.Context, userID string, question
}
}

if !questionnaire.IsDuplicateAnswerAllowed {
err = db.
Where("questionnaire_id = ? AND user_traqid = ?", questionnaireID, userID).
First(&Respondents{}).Error
if err == nil {
return 0, ErrDuplicatedAnswered
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
return 0, fmt.Errorf("failed to check duplicate answer: %w", err)
}

}

err = db.Create(&respondent).Error
if err != nil {
return 0, fmt.Errorf("failed to insert a respondent record: %w", err)
}

return respondent.ResponseID, nil

}

// UpdateSubmittedAt 投稿日時更新
Expand Down
Loading
Loading