feat: 微信公众号技能包重命名(weixin_public_account)并增强功能
- 将 wechat_official_account 重命名为 weixin_public_account,符合中文命名规范 - 新增 DOCX 文档生成技能、聊天历史、请求 ID 中间件 - 增强工作流、热点服务、文章服务等模块功能 - 前端同步重命名组件和 API - 新增架构文档 AR13/AR14、专员文档更新 - 补充测试用例(seed_specialists_test, db_migration_test) Co-Authored-AI: yes
This commit is contained in:
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"eai_agentplatform/backend/internal/model"
|
||||
skillmodel "eai_agentplatform/backend/internal/skills/model"
|
||||
officialaccountmodel "eai_agentplatform/backend/internal/specialists/packages/wechat_official_account/model"
|
||||
weixinpublicaccountmodel "eai_agentplatform/backend/internal/specialists/packages/weixin_public_account/model"
|
||||
specialistmodel "eai_agentplatform/backend/internal/specialists/model"
|
||||
)
|
||||
|
||||
@@ -45,6 +45,9 @@ func Init(dbPath string) (*gorm.DB, error) {
|
||||
if err := migrateObjectModeColumns(db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := purgeLegacyWeixinPublicAccountData(db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := db.AutoMigrate(
|
||||
&model.User{},
|
||||
@@ -60,6 +63,8 @@ func Init(dbPath string) (*gorm.DB, error) {
|
||||
&model.SystemConfig{},
|
||||
&model.KnowledgeSource{},
|
||||
&model.AiCallLog{},
|
||||
&model.ChatConversation{},
|
||||
&model.ChatMessage{},
|
||||
&model.LearningProgress{},
|
||||
&model.Position{},
|
||||
&model.PositionKnowledge{},
|
||||
@@ -78,8 +83,8 @@ func Init(dbPath string) (*gorm.DB, error) {
|
||||
&model.TaskRun{},
|
||||
&model.UserXAppCenter{},
|
||||
&model.XAppDefinition{},
|
||||
&officialaccountmodel.OfficialAccountArticle{},
|
||||
&officialaccountmodel.OfficialAccountHotspot{},
|
||||
&weixinpublicaccountmodel.WeixinPublicAccountArticle{},
|
||||
&weixinpublicaccountmodel.WeixinPublicAccountHotspot{},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -288,6 +293,74 @@ func normalizeSkillObjectKinds(db *gorm.DB) error {
|
||||
`).Error
|
||||
}
|
||||
|
||||
func purgeLegacyWeixinPublicAccountData(db *gorm.DB) error {
|
||||
const legacySpecialistKey = "wechat-official-account"
|
||||
|
||||
changed := false
|
||||
|
||||
if db.Migrator().HasTable("task_artifact") {
|
||||
if result := db.Exec(`DELETE FROM task_artifact WHERE specialist_key = ?`, legacySpecialistKey); result.Error != nil {
|
||||
return result.Error
|
||||
} else if result.RowsAffected > 0 {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if db.Migrator().HasTable("task_run") {
|
||||
if result := db.Exec(`DELETE FROM task_run WHERE specialist_key = ?`, legacySpecialistKey); result.Error != nil {
|
||||
return result.Error
|
||||
} else if result.RowsAffected > 0 {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if db.Migrator().HasTable("task_record") {
|
||||
if result := db.Exec(`DELETE FROM task_record WHERE specialist_key = ?`, legacySpecialistKey); result.Error != nil {
|
||||
return result.Error
|
||||
} else if result.RowsAffected > 0 {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if db.Migrator().HasTable("weixin_public_account_article") {
|
||||
if result := db.Exec(`DELETE FROM weixin_public_account_article WHERE specialist_key = ?`, legacySpecialistKey); result.Error != nil {
|
||||
return result.Error
|
||||
} else if result.RowsAffected > 0 {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if db.Migrator().HasTable("specialist") {
|
||||
if result := db.Exec(`DELETE FROM specialist WHERE key = ?`, legacySpecialistKey); result.Error != nil {
|
||||
return result.Error
|
||||
} else if result.RowsAffected > 0 {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if db.Migrator().HasTable("official_account_article") {
|
||||
if err := db.Migrator().DropTable("official_account_article"); err != nil {
|
||||
return err
|
||||
}
|
||||
changed = true
|
||||
}
|
||||
|
||||
if db.Migrator().HasTable("official_account_hotspot") {
|
||||
if err := db.Migrator().DropTable("official_account_hotspot"); err != nil {
|
||||
return err
|
||||
}
|
||||
changed = true
|
||||
}
|
||||
|
||||
if changed {
|
||||
if err := db.Exec(`VACUUM`).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateColumnData(db *gorm.DB, tableName, targetColumn, legacyColumn string) error {
|
||||
if !db.Migrator().HasColumn(tableName, legacyColumn) {
|
||||
return nil
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"eai_agentplatform/backend/internal/model"
|
||||
skillmodel "eai_agentplatform/backend/internal/skills/model"
|
||||
weixinpublicaccountmodel "eai_agentplatform/backend/internal/specialists/packages/weixin_public_account/model"
|
||||
specialistmodel "eai_agentplatform/backend/internal/specialists/model"
|
||||
)
|
||||
|
||||
@@ -258,6 +259,198 @@ func TestMigrateAIUsageKindColumnDropsLegacyColumn(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPurgeLegacyWeixinPublicAccountDataRemovesOldRowsAndTables(t *testing.T) {
|
||||
db := openMigrationTestDB(t)
|
||||
if err := db.AutoMigrate(
|
||||
&specialistmodel.Specialist{},
|
||||
&model.TaskRecord{},
|
||||
&model.TaskArtifact{},
|
||||
&model.TaskRun{},
|
||||
&weixinpublicaccountmodel.WeixinPublicAccountArticle{},
|
||||
); err != nil {
|
||||
t.Fatalf("auto migrate: %v", err)
|
||||
}
|
||||
|
||||
if err := db.Exec(`
|
||||
CREATE TABLE official_account_article (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
specialist_key TEXT NOT NULL
|
||||
)
|
||||
`).Error; err != nil {
|
||||
t.Fatalf("create official_account_article: %v", err)
|
||||
}
|
||||
if err := db.Exec(`
|
||||
CREATE TABLE official_account_hotspot (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
source_key TEXT NOT NULL
|
||||
)
|
||||
`).Error; err != nil {
|
||||
t.Fatalf("create official_account_hotspot: %v", err)
|
||||
}
|
||||
|
||||
legacySpecialist := specialistmodel.Specialist{
|
||||
Key: "wechat-official-account",
|
||||
Label: "公众号助手",
|
||||
Tier: "generic",
|
||||
SpecialistMode: "dw",
|
||||
ObjectEntryRoute: "/apps/weixin-public-account",
|
||||
State: "active",
|
||||
MarketTag: "installed",
|
||||
}
|
||||
if err := db.Create(&legacySpecialist).Error; err != nil {
|
||||
t.Fatalf("create legacy specialist: %v", err)
|
||||
}
|
||||
|
||||
currentSpecialist := specialistmodel.Specialist{
|
||||
Key: "weixin-public-account",
|
||||
Label: "公众号创作专员",
|
||||
Tier: "generic",
|
||||
SpecialistMode: "dw",
|
||||
ObjectEntryRoute: "/apps/weixin-public-account",
|
||||
State: "active",
|
||||
MarketTag: "installed",
|
||||
}
|
||||
if err := db.Create(¤tSpecialist).Error; err != nil {
|
||||
t.Fatalf("create current specialist: %v", err)
|
||||
}
|
||||
|
||||
legacyTask := model.TaskRecord{
|
||||
SpecialistKey: "wechat-official-account",
|
||||
Title: "旧公众号任务",
|
||||
Priority: "P2",
|
||||
Status: "待处理",
|
||||
}
|
||||
if err := db.Create(&legacyTask).Error; err != nil {
|
||||
t.Fatalf("create legacy task: %v", err)
|
||||
}
|
||||
|
||||
keepTask := model.TaskRecord{
|
||||
SpecialistKey: "weixin-public-account",
|
||||
Title: "新公众号任务",
|
||||
Priority: "P2",
|
||||
Status: "待处理",
|
||||
}
|
||||
if err := db.Create(&keepTask).Error; err != nil {
|
||||
t.Fatalf("create current task: %v", err)
|
||||
}
|
||||
|
||||
legacyArtifact := model.TaskArtifact{
|
||||
TaskID: legacyTask.ID,
|
||||
SpecialistKey: "wechat-official-account",
|
||||
Title: "旧产物",
|
||||
}
|
||||
if err := db.Create(&legacyArtifact).Error; err != nil {
|
||||
t.Fatalf("create legacy artifact: %v", err)
|
||||
}
|
||||
keepArtifact := model.TaskArtifact{
|
||||
TaskID: keepTask.ID,
|
||||
SpecialistKey: "weixin-public-account",
|
||||
Title: "新产物",
|
||||
}
|
||||
if err := db.Create(&keepArtifact).Error; err != nil {
|
||||
t.Fatalf("create current artifact: %v", err)
|
||||
}
|
||||
|
||||
legacyRun := model.TaskRun{
|
||||
TaskID: legacyTask.ID,
|
||||
SpecialistKey: "wechat-official-account",
|
||||
ActionKey: "topic",
|
||||
ActionTitle: "旧运行",
|
||||
}
|
||||
if err := db.Create(&legacyRun).Error; err != nil {
|
||||
t.Fatalf("create legacy run: %v", err)
|
||||
}
|
||||
keepRun := model.TaskRun{
|
||||
TaskID: keepTask.ID,
|
||||
SpecialistKey: "weixin-public-account",
|
||||
ActionKey: "topic",
|
||||
ActionTitle: "新运行",
|
||||
}
|
||||
if err := db.Create(&keepRun).Error; err != nil {
|
||||
t.Fatalf("create current run: %v", err)
|
||||
}
|
||||
|
||||
legacyArticle := weixinpublicaccountmodel.WeixinPublicAccountArticle{
|
||||
TaskID: legacyTask.ID,
|
||||
SpecialistKey: "wechat-official-account",
|
||||
Keyword: "外骨骼机器人",
|
||||
Audience: "合作方",
|
||||
Goal: "推广",
|
||||
Tone: "专业",
|
||||
}
|
||||
if err := db.Create(&legacyArticle).Error; err != nil {
|
||||
t.Fatalf("create legacy article: %v", err)
|
||||
}
|
||||
keepArticle := weixinpublicaccountmodel.WeixinPublicAccountArticle{
|
||||
TaskID: keepTask.ID,
|
||||
SpecialistKey: "weixin-public-account",
|
||||
Keyword: "外骨骼机器人",
|
||||
Audience: "合作方",
|
||||
Goal: "推广",
|
||||
Tone: "专业",
|
||||
}
|
||||
if err := db.Create(&keepArticle).Error; err != nil {
|
||||
t.Fatalf("create current article: %v", err)
|
||||
}
|
||||
|
||||
if err := purgeLegacyWeixinPublicAccountData(db); err != nil {
|
||||
t.Fatalf("purge legacy weixin public account data: %v", err)
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&specialistmodel.Specialist{}).Where("key = ?", "wechat-official-account").Count(&count).Error; err != nil {
|
||||
t.Fatalf("count legacy specialist: %v", err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatalf("legacy specialist should be removed, count=%d", count)
|
||||
}
|
||||
if err := db.Model(&specialistmodel.Specialist{}).Where("key = ?", "weixin-public-account").Count(&count).Error; err != nil {
|
||||
t.Fatalf("count current specialist: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Fatalf("current specialist should remain, count=%d", count)
|
||||
}
|
||||
|
||||
if err := db.Model(&model.TaskRecord{}).Where("specialist_key = ?", "wechat-official-account").Count(&count).Error; err != nil {
|
||||
t.Fatalf("count legacy tasks: %v", err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatalf("legacy tasks should be removed, count=%d", count)
|
||||
}
|
||||
if err := db.Model(&model.TaskRecord{}).Where("specialist_key = ?", "weixin-public-account").Count(&count).Error; err != nil {
|
||||
t.Fatalf("count current tasks: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Fatalf("current tasks should remain, count=%d", count)
|
||||
}
|
||||
|
||||
if err := db.Model(&model.TaskArtifact{}).Where("specialist_key = ?", "wechat-official-account").Count(&count).Error; err != nil {
|
||||
t.Fatalf("count legacy artifacts: %v", err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatalf("legacy artifacts should be removed, count=%d", count)
|
||||
}
|
||||
if err := db.Model(&model.TaskRun{}).Where("specialist_key = ?", "wechat-official-account").Count(&count).Error; err != nil {
|
||||
t.Fatalf("count legacy runs: %v", err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatalf("legacy runs should be removed, count=%d", count)
|
||||
}
|
||||
if err := db.Model(&weixinpublicaccountmodel.WeixinPublicAccountArticle{}).Where("specialist_key = ?", "wechat-official-account").Count(&count).Error; err != nil {
|
||||
t.Fatalf("count legacy articles: %v", err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatalf("legacy articles should be removed, count=%d", count)
|
||||
}
|
||||
|
||||
if db.Migrator().HasTable("official_account_article") {
|
||||
t.Fatalf("official_account_article should be dropped")
|
||||
}
|
||||
if db.Migrator().HasTable("official_account_hotspot") {
|
||||
t.Fatalf("official_account_hotspot should be dropped")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeSkillObjectKindsMapsAssistantToSkill(t *testing.T) {
|
||||
db := openMigrationTestDB(t)
|
||||
if err := db.AutoMigrate(&skillmodel.SkillDefinition{}); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user