feat(publish): 街拍实体键细化为 city+year+title,支持同城同年多专题
This commit is contained in:
@ -50,17 +50,28 @@ func TestRunwayEntityKeyUnique(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestStreetEntityKeyUnique 街拍侧同理(实体键 = city + year)。
|
||||
// TestStreetEntityKeyUnique 街拍实体键 = city + year + title(2026-09-22-04 细化后)。
|
||||
//
|
||||
// 为什么键里要有 title:同城同年可以有多个专题(开发库实例 London 2027 Day 2 / Day 3),
|
||||
// title 是数据里唯一能区分它们的字段。因此:
|
||||
// ① 同城同年**同标题**的第二行必须被拒(实体键仍具备唯一性);
|
||||
// ② 同城同年**不同标题**的第二行必须允许(对应「Day 2 / Day 3 分成两个文章」)。
|
||||
func TestStreetEntityKeyUnique(t *testing.T) {
|
||||
db := testDB(t)
|
||||
applyMigration(t, db, "2026-09-22-01-single-table-publish.sql")
|
||||
applyMigration(t, db, "2026-09-22-03-entity-key-unique.sql")
|
||||
// 04 是替换索引:先 DROP 掉 03 建的 (city, year),再按 (city, year, title) 重建同名索引。
|
||||
// 两个都执行才真实还原「迁移链走完」的库结构,也才能验证旧键确实不再生效。
|
||||
applyMigration(t, db, "2026-09-22-04-street-entity-key-title.sql")
|
||||
repo := NewIngestRepository(db)
|
||||
ctx := context.Background()
|
||||
|
||||
const city = "UniqTestCity"
|
||||
const year = 1994
|
||||
snap := &model.StreetSnap{JobID: 1, Title: "uniq", Year: year, City: city, Status: model.StatusPending}
|
||||
const titleDay2 = "UniqTest Spring 1994 Day 2"
|
||||
const titleDay3 = "UniqTest Spring 1994 Day 3"
|
||||
|
||||
snap := &model.StreetSnap{JobID: 1, Title: titleDay2, Year: year, City: city, Status: model.StatusPending}
|
||||
id, err := repo.CreateStreetSnapWithImages(ctx, snap, []model.StreetSnapImage{{Image: "uniq-a.jpg", SortOrder: 1}})
|
||||
if err != nil {
|
||||
t.Fatalf("首行应能插入: %v", err)
|
||||
@ -70,17 +81,30 @@ func TestStreetEntityKeyUnique(t *testing.T) {
|
||||
db.Exec("DELETE FROM street_snaps WHERE id = ?", id)
|
||||
})
|
||||
|
||||
dup := &model.StreetSnap{JobID: 1, Title: "uniq", Year: year, City: city, Status: model.StatusPending}
|
||||
// ① 同城同年同标题:实体键命中,唯一索引必须拒绝。
|
||||
dup := &model.StreetSnap{JobID: 1, Title: titleDay2, Year: year, City: city, Status: model.StatusPending}
|
||||
if _, err := repo.CreateStreetSnapWithImages(ctx, dup, []model.StreetSnapImage{{Image: "uniq-b.jpg", SortOrder: 1}}); err == nil {
|
||||
t.Fatalf("同一实体键的第二行应被唯一索引拒绝,实际插入成功")
|
||||
t.Fatalf("同城同年同标题的第二行应被唯一索引拒绝,实际插入成功")
|
||||
}
|
||||
|
||||
// ② 同城同年不同标题:不同专题,必须允许共存。
|
||||
// 这一条是本任务的核心:若还原成 (city, year) 的旧键,这里会插入失败。
|
||||
other := &model.StreetSnap{JobID: 1, Title: titleDay3, Year: year, City: city, Status: model.StatusPending}
|
||||
otherID, err := repo.CreateStreetSnapWithImages(ctx, other, []model.StreetSnapImage{{Image: "uniq-c.jpg", SortOrder: 1}})
|
||||
if err != nil {
|
||||
t.Fatalf("同城同年不同标题应可插入,实际失败: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
db.Exec("DELETE FROM street_snap_images WHERE snap_id = ?", otherID)
|
||||
db.Exec("DELETE FROM street_snaps WHERE id = ?", otherID)
|
||||
})
|
||||
|
||||
// 软删首行后,实体键不再占用(与 is_deleted = 0 的部分索引口径一致)。
|
||||
if err := db.Model(&model.StreetSnap{}).Where("id = ?", id).Update("is_deleted", 1).Error; err != nil {
|
||||
t.Fatalf("软删失败: %v", err)
|
||||
}
|
||||
again := &model.StreetSnap{JobID: 1, Title: "uniq", Year: year, City: city, Status: model.StatusPending}
|
||||
newID, err := repo.CreateStreetSnapWithImages(ctx, again, []model.StreetSnapImage{{Image: "uniq-c.jpg", SortOrder: 1}})
|
||||
again := &model.StreetSnap{JobID: 1, Title: titleDay2, Year: year, City: city, Status: model.StatusPending}
|
||||
newID, err := repo.CreateStreetSnapWithImages(ctx, again, []model.StreetSnapImage{{Image: "uniq-d.jpg", SortOrder: 1}})
|
||||
if err != nil {
|
||||
t.Fatalf("软删后应可再插入同实体键: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user