feat(channel): 支持安全删除渠道并保留历史数据
CI / Frontend (typecheck + lint + build) (push) Successful in 5m31s
CI / Backend (go vet + test) (push) Failing after 11m10s

This commit is contained in:
yml2213
2026-11-06 18:36:07 +08:00
parent 6297ea6634
commit de1ffbf4f6
10 changed files with 135 additions and 23 deletions
@@ -637,6 +637,67 @@ func TestChannelListAndToggleRequiresAdmin(t *testing.T) {
if err := model.DB.First(&updated, channel.ID).Error; err != nil || updated.Status != "disabled" {
t.Fatalf("渠道状态未更新: %+v err=%v", updated, err)
}
customer := model.Customer{TenantID: tenant.ID, Name: "历史渠道客户"}
if err := model.DB.Create(&customer).Error; err != nil {
t.Fatal(err)
}
session := model.Session{TenantID: tenant.ID, ChannelID: channel.ID, CustomerID: customer.ID, Status: "waiting"}
if err := model.DB.Create(&session).Error; err != nil {
t.Fatal(err)
}
denyDelete := httptest.NewRecorder()
router.ServeHTTP(denyDelete, bearerRequest(t, http.MethodDelete, fmt.Sprintf("/api/channels/%d", channel.ID), nil, agent))
if denyDelete.Code != http.StatusForbidden {
t.Fatalf("客服删除渠道应 403: %d %s", denyDelete.Code, denyDelete.Body.String())
}
deleteRecorder := httptest.NewRecorder()
router.ServeHTTP(deleteRecorder, bearerRequest(t, http.MethodDelete, fmt.Sprintf("/api/channels/%d", channel.ID), nil, admin))
if deleteRecorder.Code != http.StatusOK {
t.Fatalf("管理员删除渠道失败: %d %s", deleteRecorder.Code, deleteRecorder.Body.String())
}
var deleted model.Channel
if err := model.DB.Unscoped().First(&deleted, channel.ID).Error; err != nil || !deleted.DeletedAt.Valid {
t.Fatalf("渠道应软删除: %+v err=%v", deleted, err)
}
afterDelete := httptest.NewRecorder()
router.ServeHTTP(afterDelete, bearerRequest(t, http.MethodGet, "/api/channels", nil, admin))
var channelList struct {
Data []model.Channel `json:"data"`
}
if err := json.Unmarshal(afterDelete.Body.Bytes(), &channelList); err != nil {
t.Fatal(err)
}
if len(channelList.Data) != 0 {
t.Fatalf("已删除渠道不应出现在列表: %+v", channelList.Data)
}
historyRecorder := httptest.NewRecorder()
router.ServeHTTP(historyRecorder, bearerRequest(t, http.MethodGet, "/api/sessions?page=1&pageSize=10", nil, admin))
var history struct {
List []struct {
ID uint `json:"id"`
ChannelName string `json:"channel_name"`
} `json:"list"`
}
if err := json.Unmarshal(historyRecorder.Body.Bytes(), &history); err != nil {
t.Fatal(err)
}
if len(history.List) != 1 || history.List[0].ChannelName != "网页" {
t.Fatalf("历史会话应保留已删除渠道名称: %+v", history.List)
}
oldWidgetReq := httptest.NewRequest(http.MethodPost, "/api/widget/init",
bytes.NewBufferString(`{"channel_key":"WK_ch_001","page_url":"https://example.com/"}`))
oldWidgetReq.Header.Set("Content-Type", "application/json")
oldWidgetRecorder := httptest.NewRecorder()
router.ServeHTTP(oldWidgetRecorder, oldWidgetReq)
if oldWidgetRecorder.Code != http.StatusNotFound {
t.Fatalf("已删除渠道的旧嵌入代码应失效: %d %s", oldWidgetRecorder.Code, oldWidgetRecorder.Body.String())
}
}
func TestMultipleWebChannelsBindToDomains(t *testing.T) {