From cd04467b9bef897356d0cdce70267d7c724aad0f Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Fri, 10 Apr 2026 09:42:37 +0800 Subject: [PATCH] Go: add delete search (#14014) ### What problem does this PR solve? As title. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai --- internal/dao/search.go | 15 ++++++++++ internal/handler/search.go | 58 ++++++++++++++++++++++++++++++++++++++ internal/router/router.go | 1 + internal/service/search.go | 17 +++++++++++ 4 files changed, 91 insertions(+) diff --git a/internal/dao/search.go b/internal/dao/search.go index b229c40fff..ea5c0ca49b 100644 --- a/internal/dao/search.go +++ b/internal/dao/search.go @@ -145,3 +145,18 @@ func (dao *SearchDAO) QueryByTenantIDAndID(tenantID string, searchID string) ([] err := DB.Where("tenant_id = ? AND id = ? AND status = ?", tenantID, searchID, "1").Find(&searches).Error return searches, err } + +// DeleteByID deletes a search by ID (soft delete by setting status to "0") +// Reference: Python common_service.py::delete_by_id +func (dao *SearchDAO) DeleteByID(id string) error { + return DB.Model(&entity.Search{}).Where("id = ?", id).Update("status", "0").Error +} + +// Accessible4Deletion checks if a search can be deleted by a specific user +// Reference: Python search_service.py::accessible4deletion +// Returns true if the search exists, is valid, and was created by the user +func (dao *SearchDAO) Accessible4Deletion(searchID string, userID string) bool { + var search entity.Search + err := DB.Where("id = ? AND created_by = ? AND status = ?", searchID, userID, "1").First(&search).Error + return err == nil +} diff --git a/internal/handler/search.go b/internal/handler/search.go index 942d813cf4..a1d31031eb 100644 --- a/internal/handler/search.go +++ b/internal/handler/search.go @@ -251,3 +251,61 @@ func (h *SearchHandler) GetSearch(c *gin.Context) { "message": "success", }) } + +// DeleteSearch delete a search app +// @Summary Delete Search App +// @Description Delete a search app by ID +// @Tags search +// @Accept json +// @Produce json +// @Param search_id path string true "search app ID" +// @Success 200 {object} map[string]interface{} +// @Router /api/v1/searches/{search_id} [delete] +func (h *SearchHandler) DeleteSearch(c *gin.Context) { + // Get current user from context (same as Python current_user) + user, errorCode, errorMessage := GetUser(c) + if errorCode != common.CodeSuccess { + jsonError(c, errorCode, errorMessage) + return + } + userID := user.ID + + // Get search_id from path parameter (same as Python ) + searchID := c.Param("search_id") + if searchID == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": common.CodeBadRequest, + "data": nil, + "message": "search_id is required", + }) + return + } + + // Delete search with permission check + err := h.searchService.DeleteSearch(userID, searchID) + if err != nil { + // Check if it's an authorization error + if err.Error() == "no authorization" { + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeAuthenticationError, + "data": false, + "message": "No authorization.", + }) + return + } + // Delete failed error + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeDataError, + "data": nil, + "message": err.Error(), + }) + return + } + + // Return success response (same as Python get_json_result(data=True)) + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeSuccess, + "data": true, + "message": "success", + }) +} diff --git a/internal/router/router.go b/internal/router/router.go index 3c70680c84..b80acf88cc 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -197,6 +197,7 @@ func (r *Router) Setup(engine *gin.Engine) { searches.GET("", r.searchHandler.ListSearches) searches.POST("", r.searchHandler.CreateSearch) searches.GET("/:search_id", r.searchHandler.GetSearch) + searches.DELETE("/:search_id", r.searchHandler.DeleteSearch) } file := v1.Group("/files") diff --git a/internal/service/search.go b/internal/service/search.go index 1264191b1e..5d72e5a1ee 100644 --- a/internal/service/search.go +++ b/internal/service/search.go @@ -233,3 +233,20 @@ func (s *SearchService) GetSearchDetail(userID string, searchID string) (*entity return search, nil } + +// DeleteSearch deletes a search app by ID +func (s *SearchService) DeleteSearch(userID string, searchID string) error { + // Step 1: Check deletion permission (same as Python SearchService.accessible4deletion) + // Python: cls.model.select().where(cls.model.id == search_id, cls.model.created_by == user_id, cls.model.status == StatusEnum.VALID.value).first() + if !s.searchDAO.Accessible4Deletion(searchID, userID) { + return fmt.Errorf("no authorization") + } + + // Step 2: Execute delete (same as Python SearchService.delete_by_id) + // Python: cls.model.delete().where(cls.model.id == pid).execute() + if err := s.searchDAO.DeleteByID(searchID); err != nil { + return fmt.Errorf("failed to delete search App %s: %w", searchID, err) + } + + return nil +}