diff --git a/internal/server/asset_handlers.go b/internal/server/asset_handlers.go index a66cc5c6..18d68155 100644 --- a/internal/server/asset_handlers.go +++ b/internal/server/asset_handlers.go @@ -7,16 +7,18 @@ import ( "strconv" "strings" + "chat-agent-system/internal/bizdev" "chat-agent-system/internal/upload" ) type projectAssetResponse struct { - AssetID int64 `json:"asset_id"` - Filename string `json:"filename"` - SHA256 string `json:"sha256"` - MimeType string `json:"mime_type"` - SizeBytes int64 `json:"size_bytes"` - ProjectID int64 `json:"project_id"` + AssetID int64 `json:"asset_id"` + Filename string `json:"filename"` + SHA256 string `json:"sha256"` + MimeType string `json:"mime_type"` + SizeBytes int64 `json:"size_bytes"` + ProjectID int64 `json:"project_id"` + ParseState string `json:"parse_state,omitempty"` } func (s *Server) handleUploadProjectAsset(w http.ResponseWriter, r *http.Request) { @@ -77,13 +79,22 @@ func (s *Server) handleUploadProjectAsset(w http.ResponseWriter, r *http.Request return } + parseState := "" + if matIdx := bizdev.Snapshot(s.svc).MaterialIndex; matIdx != nil { + matIdx.EnqueueAsync(ref.FileName, ref.BlobSHA256, data, projectID) + if matIdx.Enabled() { + parseState = "pending" + } + } + writeJSONResponse(w, http.StatusOK, projectAssetResponse{ - AssetID: ref.ID, - Filename: ref.FileName, - SHA256: ref.BlobSHA256, - MimeType: mimeType, - SizeBytes: int64(len(data)), - ProjectID: projectID, + AssetID: ref.ID, + Filename: ref.FileName, + SHA256: ref.BlobSHA256, + MimeType: mimeType, + SizeBytes: int64(len(data)), + ProjectID: projectID, + ParseState: parseState, }) } @@ -106,13 +117,26 @@ func (s *Server) handleListProjectAssets(w http.ResponseWriter, r *http.Request) writeSafeError(w, r, http.StatusInternalServerError, err.Error()) return } + + shas := make([]string, 0, len(refs)) + for _, ref := range refs { + if ref != nil && ref.BlobSHA256 != "" { + shas = append(shas, ref.BlobSHA256) + } + } + states := map[string]string{} + if matIdx := bizdev.Snapshot(s.svc).MaterialIndex; matIdx != nil { + states = matIdx.GetParseStates(r.Context(), shas) + } + out := make([]projectAssetResponse, 0, len(refs)) for _, ref := range refs { out = append(out, projectAssetResponse{ - AssetID: ref.ID, - Filename: ref.FileName, - SHA256: ref.BlobSHA256, - ProjectID: ref.ProjectID, + AssetID: ref.ID, + Filename: ref.FileName, + SHA256: ref.BlobSHA256, + ProjectID: ref.ProjectID, + ParseState: states[ref.BlobSHA256], }) } writeJSONResponse(w, http.StatusOK, out) diff --git a/web/src/api/projectAssets.ts b/web/src/api/projectAssets.ts index d4362db5..9ef42f8d 100644 --- a/web/src/api/projectAssets.ts +++ b/web/src/api/projectAssets.ts @@ -7,6 +7,8 @@ export type ProjectAsset = { mime_type?: string; size_bytes?: number; project_id: number; + /** MinerU 解析状态:pending | running | done | failed */ + parse_state?: string; }; export async function uploadProjectAsset( diff --git a/web/src/components/doc/preview/FilePreviewPanel.tsx b/web/src/components/doc/preview/FilePreviewPanel.tsx index 907e8611..a1141ff1 100644 --- a/web/src/components/doc/preview/FilePreviewPanel.tsx +++ b/web/src/components/doc/preview/FilePreviewPanel.tsx @@ -38,6 +38,7 @@ export const FilePreviewPanel: React.FC = ({ const [isFullscreen, setIsFullscreen] = useState(false); const [recoveredBlob, setRecoveredBlob] = useState(null); const [recoverState, setRecoverState] = useState<'idle' | 'loading' | 'done' | 'miss'>('idle'); + const [parseState, setParseState] = useState(''); const fileMeta = deliverable.fileMeta; const fileName = fileMeta?.originalName || deliverable.title; @@ -71,6 +72,9 @@ export const FilePreviewPanel: React.FC = ({ const match = assets.find((a) => (a.filename || '').trim().toLowerCase() === want) || assets.find((a) => want && (a.filename || '').toLowerCase().includes(want.replace(/\.[^.]+$/, ''))); assetId = match?.asset_id ?? 0; + if (match?.parse_state) { + setParseState(match.parse_state); + } } if (!assetId) { if (!cancelled) setRecoverState('miss'); @@ -503,6 +507,31 @@ export const FilePreviewPanel: React.FC = ({ {badge.label} · {fileSize} + {parseState ? ( + <> + · + + {parseState === 'done' + ? '已解析' + : parseState === 'failed' + ? '解析失败' + : parseState === 'running' + ? '解析中' + : '待解析'} + + + ) : null} ·