feat: migrate POST /api/v1/datasets/<dataset_id>/documents/stop to Go (#15597)

## Summary

Migrate the stop parse documents endpoint from Python to Go.

### Python endpoint
`POST /api/v1/datasets/<dataset_id>/documents/stop` —
`api/apps/restful_apis/document_api.py:1542-1641`

### Changes
| File | Change |
|------|--------|
| `internal/dao/task.go` | Add `GetByDocID` method |
| `internal/dao/task_test.go` | 3 DAO tests (new file) |
| `internal/service/document.go` | Add `StopParseDocuments` + refactor
shared helpers |
| `internal/service/document_test.go` | 8 service tests |
| `internal/handler/document.go` | Add handler + request struct +
interface |
| `internal/handler/document_test.go` | 5 handler tests |
| `internal/router/router.go` | Add `POST /:dataset_id/documents/stop`
route |

### How it works
1. Validates all document IDs belong to the dataset
2. For each document in RUNNING/CANCEL state (or with unfinished tasks):
- Sets Redis cancel signal `{task_id}-cancel` for each associated task
   - Updates `document.run` to CANCEL ("2")
3. Returns `{"success_count": N, "errors": [...]}`

### Test strategy
- **DAO/Service**: SQLite in-memory DB, zero mocks. Redis is nil-safe by
design.
- **Handler**: `fakeDocumentService` implementing `documentServiceIface`
interface.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
Jack
2026-06-04 14:16:13 +08:00
committed by GitHub
parent 5db1b296fb
commit c6eee09ed3
10 changed files with 1027 additions and 80 deletions

View File

@@ -44,6 +44,7 @@ type documentServiceIface interface {
DeleteDocument(id string) error
DeleteDocuments(ids []string, deleteAll bool, datasetID, userID string) (int, error)
ParseDocuments(datasetID, userID string, docIDs []string) ([]*service.ParseDocumentResponse, error)
StopParseDocuments(datasetID string, docIDs []string) (map[string]interface{}, error)
ListDocuments(page, pageSize int) ([]*service.DocumentResponse, int64, error)
ListDocumentsByDatasetID(kbID string, page, pageSize int) ([]*entity.DocumentListItem, int64, error)
GetDocumentsByAuthorID(authorID, page, pageSize int) ([]*service.DocumentResponse, int64, error)
@@ -842,3 +843,46 @@ func (h *DocumentHandler) ParseDocuments(c *gin.Context) {
"data": parseResult,
})
}
type StopParseDocumentRequest struct {
DocumentIDs []string `json:"document_ids" binding:"required"`
}
func (h *DocumentHandler) StopParseDocuments(c *gin.Context) {
datasetID := c.Param("dataset_id")
var req StopParseDocumentRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusOK, gin.H{
"code": common.CodeBadRequest,
"message": err.Error(),
})
return
}
if len(req.DocumentIDs) == 0 {
c.JSON(http.StatusOK, gin.H{
"code": common.CodeBadRequest,
"message": "`document_ids` is required",
})
return
}
userID := c.GetString("user_id")
if !h.datasetService.Accessible(datasetID, userID) {
jsonError(c, common.CodeAuthenticationError, "You don't own the dataset.")
return
}
result, err := h.documentService.StopParseDocuments(datasetID, req.DocumentIDs)
if err != nil {
jsonError(c, common.CodeExceptionError, err.Error())
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "success",
"data": result,
})
}