mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-09-19 02:18:25 +08:00
refactor(knowledge-base): address review feedback
Backend
- BatchDeleteKnowledge handler now enqueues an asynq
TypeKnowledgeListDelete task instead of calling DeleteKnowledgeList
synchronously, matching the pattern used by ClearKnowledgeBaseContents.
Avoids long HTTP timeouts on large batches and shares the existing
async cleanup pipeline.
- Extract a small enqueueKnowledgeListDelete helper on KnowledgeHandler
so BatchDeleteKnowledge and ClearKnowledgeBaseContents share the
payload/marshal/enqueue boilerplate.
Frontend (DocumentListView)
- softer outer border and lighter row separators
- header background switched to the page tone with placeholder-color
text, reducing institutional gray
- selected row uses an inset left accent bar and a quieter brand tint
instead of a saturated background; hover stays neutral
- consolidate the .row-more-btn { opacity: 1 } rule that was duplicated
across the hover and selected blocks
This commit is contained in:
@@ -265,8 +265,8 @@ const handleAction = (action: 'edit' | 'reparse' | 'move' | 'delete', item: Know
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
background: var(--td-bg-color-container, #fff);
|
||||
border: 1px solid var(--td-component-border, #e7e7e7);
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--td-component-stroke, #f0f0f0);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -291,12 +291,13 @@ const handleAction = (action: 'edit' | 'reparse' | 'move' | 'delete', item: Know
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
height: 40px;
|
||||
height: 36px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--td-text-color-secondary, #888);
|
||||
background: var(--td-bg-color-secondarycontainer, #fafafa);
|
||||
border-bottom: 1px solid var(--td-component-border, #e7e7e7);
|
||||
letter-spacing: 0.02em;
|
||||
color: var(--td-text-color-placeholder, #a6a6a6);
|
||||
background: var(--td-bg-color-page, #fafbfc);
|
||||
border-bottom: 1px solid var(--td-component-stroke, #f0f0f0);
|
||||
}
|
||||
|
||||
.doc-list-body {
|
||||
@@ -305,25 +306,31 @@ const handleAction = (action: 'edit' | 'reparse' | 'move' | 'delete', item: Know
|
||||
}
|
||||
|
||||
.doc-list-row {
|
||||
position: relative;
|
||||
height: 48px;
|
||||
font-size: 13px;
|
||||
color: var(--td-text-color-primary, #232323);
|
||||
border-bottom: 1px solid var(--td-component-stroke, #f0f0f0);
|
||||
border-bottom: 1px solid var(--td-component-stroke, #f3f3f3);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.12s ease;
|
||||
transition: background-color 0.12s ease, box-shadow 0.12s ease;
|
||||
|
||||
&:last-child { border-bottom: 0; }
|
||||
|
||||
&:hover,
|
||||
&.menu-open {
|
||||
background: var(--td-bg-color-container-hover, #f6f8fa);
|
||||
.row-more-btn { opacity: 1; }
|
||||
&:hover:not(.selected),
|
||||
&.menu-open:not(.selected) {
|
||||
background: var(--td-bg-color-page, #f7f8fa);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: var(--td-brand-color-1, #f0f6ff);
|
||||
&:hover { background: var(--td-brand-color-2, #e6f0ff); }
|
||||
background: var(--td-brand-color-1, #f2f5fc);
|
||||
box-shadow: inset 3px 0 0 var(--td-brand-color, #0052d9);
|
||||
|
||||
&:hover { background: var(--td-brand-color-light, #e8eefc); }
|
||||
}
|
||||
|
||||
&:hover .row-more-btn,
|
||||
&.menu-open .row-more-btn,
|
||||
&.selected .row-more-btn { opacity: 1; }
|
||||
}
|
||||
|
||||
.cell {
|
||||
|
||||
@@ -187,6 +187,29 @@ func (h *KnowledgeHandler) handleDuplicateKnowledgeError(c *gin.Context,
|
||||
return false
|
||||
}
|
||||
|
||||
// enqueueKnowledgeListDelete enqueues an async batch-delete task for the
|
||||
// given knowledge IDs and returns the asynq task ID.
|
||||
func (h *KnowledgeHandler) enqueueKnowledgeListDelete(
|
||||
ctx context.Context, tenantID uint64, ids []string,
|
||||
) (string, error) {
|
||||
payload := types.KnowledgeListDeletePayload{
|
||||
TenantID: tenantID,
|
||||
KnowledgeIDs: ids,
|
||||
}
|
||||
langfuse.InjectTracing(ctx, &payload)
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal payload: %w", err)
|
||||
}
|
||||
task := asynq.NewTask(types.TypeKnowledgeListDelete, payloadBytes,
|
||||
asynq.Queue("low"), asynq.MaxRetry(3))
|
||||
info, err := h.asynqClient.Enqueue(task)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("enqueue task: %w", err)
|
||||
}
|
||||
return info.ID, nil
|
||||
}
|
||||
|
||||
// CreateKnowledgeFromFile godoc
|
||||
// @Summary 从文件创建知识
|
||||
// @Description 上传文件并创建知识条目
|
||||
@@ -715,16 +738,23 @@ func (h *KnowledgeHandler) BatchDeleteKnowledge(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
logger.Infof(ctx, "Batch deleting %d knowledge entries from KB %s", len(ids), secutils.SanitizeForLog(kbID))
|
||||
if err := h.kgService.DeleteKnowledgeList(ctx, ids); err != nil {
|
||||
logger.ErrorWithFields(ctx, err, nil)
|
||||
c.Error(errors.NewInternalServerError(err.Error()))
|
||||
taskID, err := h.enqueueKnowledgeListDelete(ctx, effectiveTenantID, ids)
|
||||
if err != nil {
|
||||
logger.Errorf(ctx, "Failed to enqueue batch knowledge delete task: %v", err)
|
||||
c.Error(errors.NewInternalServerError("Failed to enqueue batch delete task"))
|
||||
return
|
||||
}
|
||||
|
||||
logger.Infof(ctx, "Batch knowledge delete task enqueued: %s, kb_id: %s, count: %d",
|
||||
taskID, secutils.SanitizeForLog(kbID), len(ids))
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"deleted": len(ids),
|
||||
"message": "Batch delete task submitted",
|
||||
"data": gin.H{
|
||||
"task_id": taskID,
|
||||
"deleted_count": len(ids),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -782,21 +812,7 @@ func (h *KnowledgeHandler) ClearKnowledgeBaseContents(c *gin.Context) {
|
||||
knowledgeIDs = append(knowledgeIDs, knowledge.ID)
|
||||
}
|
||||
|
||||
payload := types.KnowledgeListDeletePayload{
|
||||
TenantID: effectiveTenantID,
|
||||
KnowledgeIDs: knowledgeIDs,
|
||||
}
|
||||
langfuse.InjectTracing(ctx, &payload)
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
logger.Errorf(ctx, "Failed to marshal knowledge list delete payload: %v", err)
|
||||
c.Error(errors.NewInternalServerError("Failed to create cleanup task"))
|
||||
return
|
||||
}
|
||||
|
||||
task := asynq.NewTask(types.TypeKnowledgeListDelete, payloadBytes,
|
||||
asynq.Queue("low"), asynq.MaxRetry(3))
|
||||
info, err := h.asynqClient.Enqueue(task)
|
||||
taskID, err := h.enqueueKnowledgeListDelete(ctx, effectiveTenantID, knowledgeIDs)
|
||||
if err != nil {
|
||||
logger.Errorf(ctx, "Failed to enqueue knowledge list delete task: %v", err)
|
||||
c.Error(errors.NewInternalServerError("Failed to enqueue cleanup task"))
|
||||
@@ -804,7 +820,7 @@ func (h *KnowledgeHandler) ClearKnowledgeBaseContents(c *gin.Context) {
|
||||
}
|
||||
|
||||
logger.Infof(ctx, "Knowledge base contents clear task enqueued: %s, kb_id: %s, count: %d",
|
||||
info.ID, secutils.SanitizeForLog(kbID), len(knowledgeIDs))
|
||||
taskID, secutils.SanitizeForLog(kbID), len(knowledgeIDs))
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
|
||||
Reference in New Issue
Block a user