@@ -174,9 +174,13 @@ func GetMediaItemByFolderPath(folderPath string) (*model.MediaItem, error) {
174174
175175// CreateOrUpdateMediaItem 创建或更新媒体条目(按 folder_path + file_name + album_name 组合唯一)
176176// 更新时保留已有的刮削数据,避免重新扫描时把已刮削的字段清空
177+ //
178+ // 注意:使用 Unscoped() 查询是为了把软删除残留记录也包含进来,
179+ // 否则唯一索引 idx_media_folder_file_album 会与软删除行冲突,
180+ // 导致新建条目时报 UNIQUE constraint failed。
177181func CreateOrUpdateMediaItem (item * model.MediaItem ) error {
178182 var existing model.MediaItem
179- result := db .Where ("folder_path = ? AND file_name = ? AND album_name = ?" , item .FolderPath , item .FileName , item .AlbumName ).First (& existing )
183+ result := db .Unscoped (). Where ("folder_path = ? AND file_name = ? AND album_name = ?" , item .FolderPath , item .FileName , item .AlbumName ).First (& existing )
180184 if result .Error == gorm .ErrRecordNotFound {
181185 return db .Create (item ).Error
182186 }
@@ -185,6 +189,8 @@ func CreateOrUpdateMediaItem(item *model.MediaItem) error {
185189 }
186190 item .ID = existing .ID
187191 item .CreatedAt = existing .CreatedAt
192+ // 复用已有记录时清除软删除标记,确保该条目“恢复”为正常记录
193+ item .DeletedAt = gorm.DeletedAt {}
188194 // 如果已有刮削数据,保留刮削字段,防止重新扫描时覆盖刮削结果
189195 if existing .ScrapedAt != nil {
190196 item .ScrapedAt = existing .ScrapedAt
@@ -204,12 +210,13 @@ func CreateOrUpdateMediaItem(item *model.MediaItem) error {
204210 item .ISBN = existing .ISBN
205211 item .ExternalID = existing .ExternalID
206212 }
207- return db .Save (item ).Error
213+ return db .Unscoped (). Save (item ).Error
208214}
209215
210- // UpdateMediaItem 更新媒体条目(仅更新可编辑字段)
216+ // UpdateMediaItem 更新媒体条目
217+ // 使用 Unscoped 确保即使存在软删除标记也能正确更新(避免命中唯一索引但行不可见的诡异情况)。
211218func UpdateMediaItem (item * model.MediaItem ) error {
212- return db .Save (item ).Error
219+ return db .Unscoped (). Save (item ).Error
213220}
214221
215222// DeleteMediaItem 硬删除媒体条目(真正从数据库删除)
0 commit comments