{"record":{"id":"fa009266d00cd095","repo":"flipped-aurora/gin-vue-admin","slug":"d-d-fa0092","errorCode":null,"errorMessage":"分片不全: %d/%d","messagePattern":"分片不全: (.+?)/(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/media/media_upload.go","lineNumber":130,"sourceCode":"\t\tWhere(\"id = ? AND status = ?\", uploadID, media.UploadStatusUploading).\n\t\tUpdate(\"status\", media.UploadStatusMerging)\n\tif res.Error != nil {\n\t\treturn m, res.Error\n\t}\n\tif res.RowsAffected == 0 {\n\t\treturn m, errors.New(\"上传不在可合并状态(可能已在合并或已完成)\")\n\t}\n\n\tfail := func(e error) (media.FileUploadAndDownload, error) {\n\t\tglobal.GVA_DB.WithContext(ctx).Model(&media.MediaUpload{}).Where(\"id = ?\", uploadID).Update(\"status\", media.UploadStatusFailed)\n\t\treturn m, e\n\t}\n\n\t// 校验分片齐全\n\tvar cnt int64\n\tglobal.GVA_DB.WithContext(ctx).Model(&media.MediaUploadChunk{}).Where(\"upload_id = ?\", uploadID).Count(&cnt)\n\tif int(cnt) != up.ChunkTotal {\n\t\treturn fail(fmt.Errorf(\"分片不全: %d/%d\", cnt, up.ChunkTotal))\n\t}\n\n\t// 合并到临时成品\n\tmerged := filepath.Join(upload.ChunkDir(uploadID), \"merged.bin\")\n\tgotMd5, err := upload.MergeChunks(uploadID, up.ChunkTotal, merged)\n\tif err != nil {\n\t\treturn fail(err)\n\t}\n\tif gotMd5 != up.FileHash {\n\t\treturn fail(errors.New(\"整文件校验失败\"))\n\t}\n\n\t// 经 OSS 接口推到配置存储\n\tfh, cleanup, err := upload.BuildFileHeader(merged, \"file\", up.FileName)\n\tif err != nil {\n\t\treturn fail(err)\n\t}\n\tdefer cleanup()","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/media/media_upload.go#L112-L148","documentation":"Complete finalizes a chunked upload: it counts recorded MediaUploadChunk rows for the upload session and compares to the session's ChunkTotal. If fewer chunks are recorded than expected it returns \"分片不全: %d/%d\" (chunks incomplete: got/total) via the fail() path without merging. Note the Count error is ignored, so this message also fires when the count query itself fails.","triggerScenarios":"Calling Complete before all chunks have been uploaded (some indexes never sent), a chunk rejected by MD5 validation (error 438) leaving a gap, chunks written to a different uploadID, or a DB error making Count return 0 — all raise this from media_upload.go:130.","commonSituations":"Frontend firing Complete optimistically before the last parallel chunk request resolves; a failed/corrupt chunk silently skipped by the client; retries creating a new uploadID while Complete uses the old one; DB connectivity issues causing a zero count (error swallowed).","solutions":["List uploaded chunk indexes and upload only the missing ones, then call Complete again","Fix client logic to await all chunk PUTs (Promise.all) before calling Complete","Handle chunk-level failures: any chunk rejected (e.g. hash mismatch) must be retried before Complete","Confirm the client uses the same uploadID returned at session creation for every chunk and the Complete call","Investigate DB errors if all chunks were actually uploaded — the Count error is ignored, so a DB outage surfaces as this message"],"exampleFix":"// before\nchunks.forEach(c => upload(c))\nawait complete(uploadId)\n// after\nawait Promise.all(chunks.map(c => upload(c)))\nawait complete(uploadId)","handlingStrategy":"validation","validationCode":"// client: verify all chunks acknowledged before completing\nconst acked = await listUploadedChunks(uploadId)\nif (acked.length !== chunkTotal) {\n    const missing = diff(range(chunkTotal), acked)\n    await Promise.all(missing.map(i => uploadChunk(uploadId, i)))\n}","typeGuard":null,"tryCatchPattern":"if err := svc.Complete(ctx, uploadID, meta); err != nil {\n    if strings.Contains(err.Error(), \"分片不全\") {\n        // parse got/total, re-upload missing chunk indexes, then retry Complete\n        return reconcileAndComplete(uploadID)\n    }\n    return err\n}","preventionTips":["Await all parallel chunk uploads before calling Complete","Track per-chunk ACKs and retry failures (including hash rejections) before completing","Use a single uploadID consistently for all chunks and Complete","On the server, check the Count() error instead of ignoring it so DB outages aren't masked as missing chunks"],"tags":["upload","chunked-upload","state-machine","integrity"],"backgroundTag":"chunks-incomplete","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}