H-338: close H-335 release blockers (#59)

* H-16: align takeover with channel AI workflow (#2)

* feat(conversations): complete manual AI takeover

* fix(conversations): align AI takeover flow with channel AI

* fix(conversations): close takeover review gaps

---------

Co-authored-by: Rogee <rogee@ipao.vip>

* feat(shangwutong): sync customer names back to channel (#3)

Co-authored-by: Rogee <rogee@ipao.vip>

* fix(shangwutong): close contact sync review gaps (#4)

Co-authored-by: Rogee <rogee@ipao.vip>

* H-28: harden Shangwutong CID sync (#5)

* fix(shangwutong): close contact sync review gaps

* fix(shangwutong): harden CID sync boundaries

---------

Co-authored-by: Rogee <rogee@ipao.vip>

* fix(conversations): sync AI takeover exit in realtime (#6)

Co-authored-by: Rogee <rogee@ipao.vip>

* test(shangwutong): cover CID rename reliability (#7)

Co-authored-by: Rogee <rogee@ipao.vip>

* H-43: fix WEB Captain takeover E2E flow (#8)

* test(shangwutong): cover CID rename reliability

* H-43: fix WEB Captain takeover flow

* H-48: preserve compatible provider model

* H-49: make Captain takeover atomic

* H-50: prevent duplicate widget initialization

---------

Co-authored-by: Rogee <rogee@ipao.vip>

* H-55: make Captain bindings atomic (#9)

Co-authored-by: Rogee <rogee@ipao.vip>

* H-60: harden Captain migration rollback and concurrency

* chore(agent): baseline — uncommitted work from the local directory

* H-335: add safe Captain skills and user deactivation

* H-338: close auth and Captain review blockers

* H-338: close assignment and session races

* H-338: close assignment and websocket invalidation gaps

* H-338: enforce assignment write invariants

---------

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-20 10:21:19 +08:00
committed by GitHub
co-authored by rogee
parent 1d8145f3b5
commit 6c78820a1f
53 changed files with 1534 additions and 216 deletions
+6 -2
View File
@@ -14,6 +14,7 @@ import (
"github.com/gochat/gochat/internal/config"
"github.com/gochat/gochat/internal/database"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/repository"
"github.com/gochat/gochat/pkg/crypto"
applogger "github.com/gochat/gochat/pkg/logger"
"gorm.io/datatypes"
@@ -237,12 +238,15 @@ func seedSmokeData(ctx context.Context, db *gorm.DB) (*smokeSeedSummary, error)
displayID := uint(1)
lastActivity := time.Now().Unix()
conversation := &model.Conversation{}
if err := db.WithContext(ctx).Where("account_id = ? AND inbox_id = ? AND contact_id = ?", account.ID, inbox.ID, contact.ID).FirstOrCreate(conversation, model.Conversation{AccountID: account.ID, InboxID: inbox.ID, ContactID: contact.ID, ContactInboxID: &contactInbox.ID, DisplayID: &displayID, AssigneeID: &admin.ID, Status: "open", Priority: "medium", ChannelType: "web_widget", Channel: "web_widget", Labels: "vip", LastActivityAt: &lastActivity}).Error; err != nil {
if err := db.WithContext(ctx).Where("account_id = ? AND inbox_id = ? AND contact_id = ?", account.ID, inbox.ID, contact.ID).FirstOrCreate(conversation, model.Conversation{AccountID: account.ID, InboxID: inbox.ID, ContactID: contact.ID, ContactInboxID: &contactInbox.ID, DisplayID: &displayID, Status: "open", Priority: "medium", ChannelType: "web_widget", Channel: "web_widget", Labels: "vip", LastActivityAt: &lastActivity}).Error; err != nil {
return nil, fmt.Errorf("seed conversation: %w", err)
}
if err := db.WithContext(ctx).Model(conversation).Updates(map[string]any{"contact_inbox_id": contactInbox.ID, "display_id": displayID, "assignee_id": admin.ID, "status": "open", "priority": "medium", "channel_type": "web_widget", "channel": "web_widget", "labels": "vip", "last_activity_at": lastActivity}).Error; err != nil {
if err := db.WithContext(ctx).Model(conversation).Updates(map[string]any{"contact_inbox_id": contactInbox.ID, "display_id": displayID, "status": "open", "priority": "medium", "channel_type": "web_widget", "channel": "web_widget", "labels": "vip", "last_activity_at": lastActivity}).Error; err != nil {
return nil, fmt.Errorf("update smoke conversation: %w", err)
}
if err := repository.UpdateConversationAssignee(ctx, db, account.ID, conversation.ID, admin.ID, nil); err != nil {
return nil, fmt.Errorf("assign smoke conversation: %w", err)
}
conversation.ContactInboxID = &contactInbox.ID
conversation.DisplayID = &displayID
conversation.AssigneeID = &admin.ID