Skip to content

campのdisplayIdに一意性制約をもたせた#301

Merged
akimon658 merged 13 commits intomainfrom
fix/prevent-duplicate-camp-display-id
Mar 12, 2026
Merged

campのdisplayIdに一意性制約をもたせた#301
akimon658 merged 13 commits intomainfrom
fix/prevent-duplicate-camp-display-id

Conversation

@ynta-3
Copy link
Copy Markdown
Contributor

@ynta-3 ynta-3 commented Jan 23, 2026

  • camps.display_idカラムにユニークインデックスを追加
  • 既存DBに対応するためマイグレーションv5を作成
  • CreateCampでGORM v2のエラーハンドリングに対応
  • 重複検証のテストケースを追加

修正前: 同じdisplayIdで複数のキャンプが作成できてしまう
修正後: 重複時に409 Conflictエラーを返す

close #268

Summary by CodeRabbit

  • 新機能

    • キャンプの表示IDに対するデータベース上の一意制約を追加しました(重複登録を防止)。
  • バグ修正

    • 表示IDの重複を検出した際、作成・更新で適切にHTTP 409を返し、明確なエラーメッセージを表示するよう改善しました。
  • テスト

    • 表示ID重複時の振る舞いを検証するリポジトリ/APIのテストを追加しました。

- camps.display_idカラムにユニークインデックスを追加
- 既存DBに対応するためマイグレーションv5を作成
- CreateCampでGORM v2のエラーハンドリングに対応
- 重複検証のテストケースを追加

修正前: 同じdisplayIdで複数のキャンプが作成できてしまう
修正後: 重複時に409 Conflictエラーを返す
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes issue #268 by enforcing uniqueness of camps.display_id so duplicate camps cannot be created with the same displayId, and ensures the repository returns an “already exists” error that the API maps to 409 Conflict.

Changes:

  • Add a unique index on camps.display_id (model tag + DB migration v5).
  • Update CreateCamp to map duplicated-key errors to model.ErrAlreadyExists (GORM v2 error handling).
  • Add a repository test case verifying duplicate DisplayID creation fails.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
repository/gormrepository/camp_test.go Adds a test ensuring duplicate DisplayID creation returns ErrAlreadyExists.
repository/gormrepository/camp.go Maps GORM duplicated-key errors to model.ErrAlreadyExists in CreateCamp.
model/camp.go Adds gorm:"uniqueIndex" on Camp.DisplayID.
migration/v5.go Introduces migration to add/drop a unique index on camps.display_id.
migration/migrate.go Registers migration v5.
Comments suppressed due to low confidence (1)

repository/gormrepository/camp.go:24

  • Now that camps.display_id is unique, UpdateCamp can also hit duplicate-key errors when changing display_id. Currently only CreateCamp maps duplicated-key errors to model.ErrAlreadyExists, so an update collision would bubble up as an unhandled DB error (likely turning into a 500). Consider applying the same duplicated-key mapping in UpdateCamp (and then mapping that to 409 in the HTTP layer, similar to create).
func (r *Repository) CreateCamp(camp *model.Camp) error {
	if err := r.db.Create(camp).Error; err != nil {
		if errors.Is(err, gorm.ErrDuplicatedKey) {
			return model.ErrAlreadyExists
		}

		var mysqlErr *mysql.MySQLError

		if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
			return model.ErrAlreadyExists
		}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread migration/v7.go Outdated
Comment thread migration/v7.go Outdated
Comment thread migration/migrate.go Outdated
Comment thread repository/gormrepository/camp_test.go
Copy link
Copy Markdown
Member

@akimon658 akimon658 left a comment

Choose a reason for hiding this comment

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

ありがとう!
方針は良い感じだと思います。追加で以下の点に対応してもらえると助かります 🙏

  • repositoryで今回追加したエラーが起こったとき、routerでecho.ErrConflictを返すようにしてほしいです
    • テストも追加
  • Copilotがコメントしている内容に対応してもらいたいです
  • マジで申し訳ないんですが、僕が先にv5のマイグレーションを追加してしまったため、v6にしてほしいです 🙇
  • mainブランチをマージしてコンフリクトを解消してほしいです(↑を先にやったほうが良いと思う)

大変だと思いますがお願いします 🙏

Comment thread migration/v7.go Outdated
Comment thread repository/gormrepository/camp.go Outdated
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

データベースで camps.display_id に一意インデックスを追加し、モデル・マイグレーション・リポジトリ・ルーター・テストを更新して、DisplayID 重複時に専用エラー ErrCampAlreadyExists を返し HTTP 409 を返すようにした変更。

Changes

Cohort / File(s) Summary
マイグレーション層
migration/migrate.go, migration/v7.go
マイグレーション v7 を追加し、camps.display_id に対する一意インデックス idx_camps_display_id を作成/削除するロジックを追加。
モデル定義
model/camp.go
Camp.DisplayID に GORM の uniqueIndex タグを追加。
リポジトリ定義
repository/camp.go
新しい公開エラー ErrCampAlreadyExists を追加。
GORM リポジトリ実装
repository/gormrepository/camp.go
Create/Update のエラー判定で gorm.ErrDuplicatedKey を検出して ErrCampAlreadyExists を返すよう変更(MySQL 固有ハンドリングを削除)。
リポジトリテスト
repository/gormrepository/camp_test.go
DisplayID 重複時に ErrCampAlreadyExists が返るユニットテストを追加。
ルーター / ハンドラー
router/camps.go
AdminPostCamp / AdminPutCamp のエラーハンドリングで ErrCampAlreadyExists を検出し HTTP 409 を返す分岐を追加。
ルーターテスト
router/camps_test.go
DisplayID 衝突を想定した統合(ハンドラー)テストを複数追加。

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Router
    participant Repo
    participant DB
    Client->>Router: POST /admin/camps (displayId)
    Router->>Repo: CreateCamp(req)
    Repo->>DB: INSERT INTO camps (display_id, ...)
    DB-->>Repo: duplicate key error
    Repo-->>Router: ErrCampAlreadyExists
    Router-->>Client: 409 Conflict ("Camp already exists")

    Client->>Router: PUT /admin/camps/:id (displayId -> existing)
    Router->>Repo: UpdateCamp(id, req)
    Repo->>DB: UPDATE camps SET display_id=...
    DB-->>Repo: duplicate key error
    Repo-->>Router: ErrCampAlreadyExists
    Router-->>Client: 409 Conflict ("Camp with this display ID already exists")
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PRのタイトルは「campのdisplayIdに一意性制約をもたせた」で、リンク済みissue #268の要件「campのdisplayIdが重複していても作成できてしまう」に対して、一意性制約を追加することで直接的に対応している。変更内容とタイトルが一致しており、何が変更されたかが明確である。
Linked Issues check ✅ Passed Issue #268で「campのdisplayIdが重複していても作成できてしまう」という問題が報告されており、このPRではdisplay_idカラムにユニークインデックスを追加し、重複時に409エラーを返すように対応している。マイグレーション、GORM v2エラーハンドリング、テストケース追加など、すべてこの要件に対応している。
Out of Scope Changes check ✅ Passed すべての変更がissue #268「campのdisplayIdの重複防止」に関連している。マイグレーション、struct tags、エラーハンドリング、APIレスポンス、テストケースは全て一意性制約の実装に直結しており、スコープ外の変更は見当たらない。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/prevent-duplicate-camp-display-id

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@migration/v6.go`:
- Around line 12-17: マイグレーション v6 のインデックス操作が無条件で実行され重複作成/削除で失敗するので、ALTER TABLE
を直接実行する代わりに GORM のマイグレータ存在チェックを使って冪等化してください:migrations in migration/v6.go
のインデックス名 idx_camps_display_id を対象に、アップ時は db.Migrator().HasIndex(&models.Camp,
"display_id")(または同等の HasIndex 呼び出し)で存在確認してなければ作成、ロールバック時は存在すれば DROP(または
db.Migrator().DropIndex(&models.Camp, "display_id"))するように置き換えてください。Ensure the
code uses the same model/field (DisplayID / models.Camp) to maintain consistency
with model/camp.go.

In `@repository/gormrepository/camp.go`:
- Around line 16-18: CreateCamp 正規化と同様に UpdateCamp でも DB の重複キーエラーを
model.ErrAlreadyExists に変換してください: UpdateCamp のクエリ実行後に返る err
をチェックし、errors.Is(err, gorm.ErrDuplicatedKey) の場合は生の err を返すのではなく
model.ErrAlreadyExists を返すように処理を追加して、display_id
の更新時の重複制約違反も一貫したエラー契約になるようにしてください。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 19d9a7cb-46ae-4606-b160-aba03318a994

📥 Commits

Reviewing files that changed from the base of the PR and between 7c5463e and 9daacfd.

📒 Files selected for processing (5)
  • migration/migrate.go
  • migration/v6.go
  • model/camp.go
  • repository/gormrepository/camp.go
  • repository/gormrepository/camp_test.go

Comment thread migration/v7.go Outdated
Comment on lines +12 to +17
return db.Exec(
"ALTER TABLE camps ADD UNIQUE INDEX idx_camps_display_id (display_id)",
).Error
},
Rollback: func(db *gorm.DB) error {
return db.Exec("ALTER TABLE camps DROP INDEX idx_camps_display_id").Error
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check migration v6 implementation and surrounding context
fd -e go migration | head -20

Repository: traPtitech/rucQ

Length of output: 41


🏁 Script executed:

#!/bin/bash
# Search for the specific migration file and examine its contents
find . -name "v6.go" -o -name "*migration*" -type f | grep -E "\.(go|sql)$"

Repository: traPtitech/rucQ

Length of output: 76


🏁 Script executed:

#!/bin/bash
# Look for the migration file content
rg -l "v6\(\)" --type=go

Repository: traPtitech/rucQ

Length of output: 95


🏁 Script executed:

#!/bin/bash
# Search for DisplayID and UNIQUE INDEX references
rg -A 5 -B 5 "DisplayID|idx_camps_display_id" --type=go

Repository: traPtitech/rucQ

Length of output: 9301


🏁 Script executed:

#!/bin/bash
# Check full content of migration v6
cat migration/v6.go

Repository: traPtitech/rucQ

Length of output: 496


🏁 Script executed:

#!/bin/bash
# Check migrate.go for context and other migration patterns
cat migration/migrate.go

Repository: traPtitech/rucQ

Length of output: 697


🏁 Script executed:

#!/bin/bash
# Check if HasIndex is used anywhere else in migrations
rg "HasIndex|Migrator" --type=go migration/

Repository: traPtitech/rucQ

Length of output: 719


マイグレーション v6 を冪等化してください。

Line 13-17 のインデックス作成・削除が条件なく実行されるため、既に idx_camps_display_id がある環境で失敗リスクがあります。特に、model/camp.go の DisplayID フィールドに gorm:"uniqueIndex" が定義されているため、InitSchema の AutoMigrate によってインデックスが先に作成された場合、v6 の ALTER TABLE は重複キーエラーで失敗します。存在確認を追加するか、model 定義との一貫性を整理してください。

💡 提案:HasIndex でのガード
 func v6() *gormigrate.Migration {
 	return &gormigrate.Migration{
 		ID: "6",
 		Migrate: func(db *gorm.DB) error {
+			if db.Migrator().HasIndex(&model.Camp{}, "idx_camps_display_id") {
+				return nil
+			}
 			return db.Exec(
 				"ALTER TABLE camps ADD UNIQUE INDEX idx_camps_display_id (display_id)",
 			).Error
 		},
 		Rollback: func(db *gorm.DB) error {
+			if !db.Migrator().HasIndex(&model.Camp{}, "idx_camps_display_id") {
+				return nil
+			}
 			return db.Exec("ALTER TABLE camps DROP INDEX idx_camps_display_id").Error
 		},
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@migration/v6.go` around lines 12 - 17, マイグレーション v6
のインデックス操作が無条件で実行され重複作成/削除で失敗するので、ALTER TABLE を直接実行する代わりに GORM
のマイグレータ存在チェックを使って冪等化してください:migrations in migration/v6.go のインデックス名
idx_camps_display_id を対象に、アップ時は db.Migrator().HasIndex(&models.Camp,
"display_id")(または同等の HasIndex 呼び出し)で存在確認してなければ作成、ロールバック時は存在すれば DROP(または
db.Migrator().DropIndex(&models.Camp, "display_id"))するように置き換えてください。Ensure the
code uses the same model/field (DisplayID / models.Camp) to maintain consistency
with model/camp.go.

Comment thread repository/gormrepository/camp.go
@ynta-3 ynta-3 marked this pull request as draft March 5, 2026 06:16
@ynta-3 ynta-3 self-assigned this Mar 5, 2026
@ynta-3 ynta-3 marked this pull request as ready for review March 5, 2026 16:58
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
migration/v7.go (1)

19-24: ⚠️ Potential issue | 🟠 Major

マイグレーションのインデックス操作を冪等化してください。

CreateIndex / DropIndex を無条件実行しているため、既存環境のスキーマ状態次第で migrate/rollback が失敗する可能性があります。DisplayID はモデル側でも uniqueIndex 指定があるため、先に作成済みのケースを吸収した方が安全です。

💡 修正案
 func v7() *gormigrate.Migration {
 	return &gormigrate.Migration{
 		ID: "7",
 		Migrate: func(db *gorm.DB) error {
+			if db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") {
+				return nil
+			}
 			return db.Migrator().CreateIndex(&v7Camp{}, "DisplayID")
 		},
 		Rollback: func(db *gorm.DB) error {
+			if !db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") {
+				return nil
+			}
 			return db.Migrator().DropIndex(&v7Camp{}, "idx_camps_display_id")
 		},
 	}
 }
#!/bin/bash
# Verify whether schema initialization or other migrations can already create/drop the same index.
# Expected: You can confirm if idx_camps_display_id might exist before v7 runs.

rg -nP --type=go -C4 '\bAutoMigrate\b|\bInitSchema\b|\bMigrate\(' migration
rg -nP --type=go -C4 'func v7\(|CreateIndex\(|DropIndex\(' migration
rg -nP --type=go -C3 'DisplayID\s+string\s+`gorm:"uniqueIndex' model
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@migration/v7.go` around lines 19 - 24, The migration currently
unconditionally calls CreateIndex/DropIndex which can fail if the index already
exists or was created under a different name; update the v7 migration's Migrate
and Rollback to first check existence via db.Migrator().HasIndex before acting.
In Migrate (function Migrate in the v7 migration) call
db.Migrator().HasIndex(&v7Camp{}, "DisplayID") and only call
CreateIndex(&v7Camp{}, "DisplayID") when HasIndex returns false; in Rollback
(function Rollback) check for the index using db.Migrator().HasIndex(&v7Camp{},
"idx_camps_display_id") and/or db.Migrator().HasIndex(&v7Camp{}, "DisplayID")
and only call DropIndex if an index is present, otherwise return nil so rollback
is idempotent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@migration/v7.go`:
- Around line 19-24: The migration currently unconditionally calls
CreateIndex/DropIndex which can fail if the index already exists or was created
under a different name; update the v7 migration's Migrate and Rollback to first
check existence via db.Migrator().HasIndex before acting. In Migrate (function
Migrate in the v7 migration) call db.Migrator().HasIndex(&v7Camp{}, "DisplayID")
and only call CreateIndex(&v7Camp{}, "DisplayID") when HasIndex returns false;
in Rollback (function Rollback) check for the index using
db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") and/or
db.Migrator().HasIndex(&v7Camp{}, "DisplayID") and only call DropIndex if an
index is present, otherwise return nil so rollback is idempotent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e542bc5c-b503-4480-9e63-f97198b313d9

📥 Commits

Reviewing files that changed from the base of the PR and between 9daacfd and 1b3f04b.

📒 Files selected for processing (7)
  • migration/migrate.go
  • migration/v7.go
  • repository/camp.go
  • repository/gormrepository/camp.go
  • repository/gormrepository/camp_test.go
  • router/camps.go
  • router/camps_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • migration/migrate.go

Copy link
Copy Markdown
Member

@akimon658 akimon658 left a comment

Choose a reason for hiding this comment

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

ほぼ良さそうです!
あと少しだけ対応をお願いします 🙏

Comment thread repository/gormrepository/camp.go Outdated
Comment on lines 20 to 24
var mysqlErr *mysql.MySQLError

if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
return model.ErrAlreadyExists
return repository.ErrCampAlreadyExists
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ここは不要なので、消しちゃってください 🙏
errors.Asでチェックするやり方は今の設定だと使えないんだけど、僕が書き換えずに放置しちゃってただけです

Comment thread repository/gormrepository/camp.go Outdated
Comment on lines +76 to +81
var mysqlErr *mysql.MySQLError

if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
return repository.ErrCampAlreadyExists
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ここも不要

Comment thread migration/v7.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
migration/v7.go (1)

24-25: ⚠️ Potential issue | 🟠 Major

RollbackHasIndex でガードした方が安全です。

DropIndex を無条件実行すると、インデックス未作成の環境や途中状態でロールバックが失敗します。Migrate と同様に存在確認を入れて冪等にしておきたいです。

💡 修正案
 		Rollback: func(db *gorm.DB) error {
+			if !db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") {
+				return nil
+			}
 			return db.Migrator().DropIndex(&v7Camp{}, "idx_camps_display_id")
 		},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@migration/v7.go` around lines 24 - 25, The Rollback currently calls
db.Migrator().DropIndex(&v7Camp{}, "idx_camps_display_id") unconditionally;
change Rollback (the Rollback func) to first check
db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") and only call
DropIndex if HasIndex returns true so the rollback is idempotent and safe in
environments where the index may not exist (mirror the Migrate behavior).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@migration/v7.go`:
- Around line 19-21: The migration's Migrate function attempts to create a
unique index idx_camps_display_id on v7Camp.DisplayID but has no pre-check for
existing duplicate display_id values; add a duplicate-detection step before
calling db.Migrator().CreateIndex(&v7Camp{}, "DisplayID") that queries the
v7Camp table (e.g., GROUP BY display_id HAVING COUNT(*)>1) and if duplicates
exist either (a) return a clear error listing sample duplicate display_ids and
counts or (b) run a deterministic deduplication/resolution routine, then only
call CreateIndex when no duplicates remain; reference the Migrate function and
v7Camp type and ensure the returned error includes the duplicate details and
index name (idx_camps_display_id).
- Around line 19-23: The Migrate function in migration/v7.go lacks a return when
the index already exists, causing a compile error; in the Migrate method
(function name: Migrate) check the existing branch that calls
db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") and add a return nil
when true, so that the function returns an error value in all code paths instead
of falling through; keep the existing CreateIndex call
(db.Migrator().CreateIndex(&v7Camp{}, "DisplayID")) for the false branch.

---

Duplicate comments:
In `@migration/v7.go`:
- Around line 24-25: The Rollback currently calls
db.Migrator().DropIndex(&v7Camp{}, "idx_camps_display_id") unconditionally;
change Rollback (the Rollback func) to first check
db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") and only call
DropIndex if HasIndex returns true so the rollback is idempotent and safe in
environments where the index may not exist (mirror the Migrate behavior).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8186fe0e-ea7c-4ff4-bf56-c3df79a59fb9

📥 Commits

Reviewing files that changed from the base of the PR and between 1b3f04b and be34304.

📒 Files selected for processing (1)
  • migration/v7.go

Comment thread migration/v7.go Outdated
Comment on lines +19 to +21
Migrate: func(db *gorm.DB) error {
if !db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") {
return db.Migrator().CreateIndex(&v7Camp{}, "DisplayID")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

既存データに重複 display_id があると、このマイグレーションは失敗します。

ユニークインデックス追加前の重複解消や、少なくとも重複検出時に原因が分かるエラーを返す前処理がないため、旧バージョン運用中に重複が入っている環境では本番マイグレーションが止まります。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@migration/v7.go` around lines 19 - 21, The migration's Migrate function
attempts to create a unique index idx_camps_display_id on v7Camp.DisplayID but
has no pre-check for existing duplicate display_id values; add a
duplicate-detection step before calling db.Migrator().CreateIndex(&v7Camp{},
"DisplayID") that queries the v7Camp table (e.g., GROUP BY display_id HAVING
COUNT(*)>1) and if duplicates exist either (a) return a clear error listing
sample duplicate display_ids and counts or (b) run a deterministic
deduplication/resolution routine, then only call CreateIndex when no duplicates
remain; reference the Migrate function and v7Camp type and ensure the returned
error includes the duplicate details and index name (idx_camps_display_id).

Comment thread migration/v7.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
migration/v7.go (1)

25-27: Rollback も冪等化を検討してください。

Migrate 関数は HasIndex で存在チェックを行っていますが、Rollback はインデックスが存在しない場合にエラーになる可能性があります。

💡 冪等な Rollback の提案
 		Rollback: func(db *gorm.DB) error {
+			if !db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") {
+				return nil
+			}
 			return db.Migrator().DropIndex(&v7Camp{}, "idx_camps_display_id")
 		},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@migration/v7.go` around lines 25 - 27, Rollback currently calls
db.Migrator().DropIndex(&v7Camp{}, "idx_camps_display_id") unconditionally which
can error if the index is already absent; modify the Rollback function to first
check db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") and only call
DropIndex when it returns true, returning nil when the index does not exist so
the rollback becomes idempotent.
repository/gormrepository/camp.go (1)

72-74: エラー型の一貫性について確認してください。

UpdateCamp では rowsAffected == 0 の場合に model.ErrNotFound を返していますが、GetCampByID(Line 40)では repository.ErrCampNotFound を返しています。意図的な使い分けであれば問題ありませんが、一貫性のために統一することを検討してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@repository/gormrepository/camp.go` around lines 72 - 74, The UpdateCamp
function returns model.ErrNotFound when rowsAffected == 0 but GetCampByID
returns repository.ErrCampNotFound; make these consistent by choosing one
canonical error and using it everywhere (either change UpdateCamp to return
repository.ErrCampNotFound or change GetCampByID to return model.ErrNotFound).
Update all references in the repository (functions UpdateCamp and GetCampByID)
to the selected error symbol and adjust any callers/tests if needed so the
repository surface exposes a single, consistent NotFound error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@migration/v7.go`:
- Around line 25-27: Rollback currently calls db.Migrator().DropIndex(&v7Camp{},
"idx_camps_display_id") unconditionally which can error if the index is already
absent; modify the Rollback function to first check
db.Migrator().HasIndex(&v7Camp{}, "idx_camps_display_id") and only call
DropIndex when it returns true, returning nil when the index does not exist so
the rollback becomes idempotent.

In `@repository/gormrepository/camp.go`:
- Around line 72-74: The UpdateCamp function returns model.ErrNotFound when
rowsAffected == 0 but GetCampByID returns repository.ErrCampNotFound; make these
consistent by choosing one canonical error and using it everywhere (either
change UpdateCamp to return repository.ErrCampNotFound or change GetCampByID to
return model.ErrNotFound). Update all references in the repository (functions
UpdateCamp and GetCampByID) to the selected error symbol and adjust any
callers/tests if needed so the repository surface exposes a single, consistent
NotFound error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0be23593-9ce6-4739-af67-fc7a7846e571

📥 Commits

Reviewing files that changed from the base of the PR and between be34304 and 53b1c75.

📒 Files selected for processing (2)
  • migration/v7.go
  • repository/gormrepository/camp.go

Copy link
Copy Markdown
Member

@akimon658 akimon658 left a comment

Choose a reason for hiding this comment

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

👍

@akimon658 akimon658 merged commit 3863d11 into main Mar 12, 2026
6 checks passed
@akimon658 akimon658 deleted the fix/prevent-duplicate-camp-display-id branch March 12, 2026 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

campのdisplayIdが重複していても作成できてしまう

3 participants