feat: 实现小程序离线巡检与可靠同步
This commit is contained in:
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// CurrentSchemaVersion 是当前后端代码期望的迁移版本。
|
||||
const CurrentSchemaVersion = "6"
|
||||
const CurrentSchemaVersion = "7"
|
||||
|
||||
// RunMigrations 使用嵌入式 SQL 迁移文件将数据库升级到最新版本。
|
||||
func RunMigrations(db *gorm.DB) error {
|
||||
|
||||
@@ -116,4 +116,8 @@ func TestEmbeddedMigrationsIncludeBaseline(t *testing.T) {
|
||||
if err != nil || next != 6 {
|
||||
t.Fatalf("expected biosecurity migration version 6, got %d (err %v)", next, err)
|
||||
}
|
||||
next, err = driver.Next(next)
|
||||
if err != nil || next != 7 {
|
||||
t.Fatalf("expected inspection idempotency migration version 7, got %d (err %v)", next, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,10 +70,11 @@ func createInspection(db *gorm.DB, s3 *service.S3Service, ai *service.AIClient,
|
||||
return func(c *gin.Context) {
|
||||
idemKey := strings.TrimSpace(c.GetHeader("Idempotency-Key"))
|
||||
roomID := strings.TrimSpace(c.PostForm("roomId"))
|
||||
userID := currentUserID(c)
|
||||
|
||||
if idemKey != "" {
|
||||
if idemKey != "" && userID != nil {
|
||||
var exist model.InspectionRecord
|
||||
if db.Where("idempotency_key = ?", idemKey).First(&exist).Error == nil {
|
||||
if db.Where("user_id = ? AND idempotency_key = ?", *userID, idemKey).First(&exist).Error == nil {
|
||||
c.JSON(http.StatusOK, exist)
|
||||
return
|
||||
}
|
||||
@@ -113,7 +114,7 @@ func createInspection(db *gorm.DB, s3 *service.S3Service, ai *service.AIClient,
|
||||
imageURL := s3.Endpoint() + "/" + bucket + "/" + key
|
||||
|
||||
rec := model.InspectionRecord{
|
||||
UserID: currentUserID(c),
|
||||
UserID: userID,
|
||||
ImageURL: &imageURL,
|
||||
AIStatus: "done",
|
||||
}
|
||||
@@ -221,9 +222,9 @@ func createInspection(db *gorm.DB, s3 *service.S3Service, ai *service.AIClient,
|
||||
})
|
||||
if txErr != nil {
|
||||
// 并发幂等:唯一索引冲突时返回已有记录
|
||||
if idemKey != "" {
|
||||
if idemKey != "" && userID != nil {
|
||||
var exist model.InspectionRecord
|
||||
if db.Where("idempotency_key = ?", idemKey).First(&exist).Error == nil {
|
||||
if db.Where("user_id = ? AND idempotency_key = ?", *userID, idemKey).First(&exist).Error == nil {
|
||||
c.JSON(http.StatusOK, exist)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ type InspectionRecord struct {
|
||||
ModelVersion *string `gorm:"column:model_version;size:64" json:"modelVersion,omitempty"`
|
||||
IsMock *bool `gorm:"column:is_mock;default:false" json:"isMock,omitempty"`
|
||||
AIStatus string `gorm:"column:ai_status;size:16;default:done" json:"aiStatus"`
|
||||
IdempotencyKey *string `gorm:"column:idempotency_key;size:128;uniqueIndex" json:"idempotencyKey,omitempty"`
|
||||
IdempotencyKey *string `gorm:"column:idempotency_key;size:128" json:"idempotencyKey,omitempty"`
|
||||
CreatedAt time.Time `gorm:"type:timestamptz" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamptz" json:"updatedAt"`
|
||||
RoomName *string `gorm:"-" json:"roomName,omitempty"`
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
DROP INDEX IF EXISTS idx_inspection_records_user_idempotency;
|
||||
|
||||
-- 恢复全局唯一索引前,先处理跨用户重复,保留所有记录。
|
||||
UPDATE inspection_records AS r
|
||||
SET idempotency_key = r.idempotency_key || '#' || r.id
|
||||
WHERE r.idempotency_key IS NOT NULL
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM inspection_records AS x
|
||||
WHERE x.idempotency_key = r.idempotency_key
|
||||
AND x.id <> r.id
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_inspection_records_idempotency_key
|
||||
ON inspection_records (idempotency_key)
|
||||
WHERE idempotency_key IS NOT NULL;
|
||||
@@ -0,0 +1,17 @@
|
||||
-- 保留全部历史记录,只对同用户重复键追加记录 ID,避免迁移删除或丢失数据。
|
||||
UPDATE inspection_records AS r
|
||||
SET idempotency_key = r.idempotency_key || '#' || r.id
|
||||
WHERE r.idempotency_key IS NOT NULL
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM inspection_records AS x
|
||||
WHERE x.user_id IS NOT DISTINCT FROM r.user_id
|
||||
AND x.idempotency_key = r.idempotency_key
|
||||
AND x.id <> r.id
|
||||
);
|
||||
|
||||
DROP INDEX IF EXISTS idx_inspection_records_idempotency_key;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_inspection_records_user_idempotency
|
||||
ON inspection_records (user_id, idempotency_key)
|
||||
WHERE idempotency_key IS NOT NULL;
|
||||
Reference in New Issue
Block a user