refactor: 对象命名标准化落地(RoleKind→ObjectKind、capability/assistant 收口)

后端
- model.SkillDefinition.RoleKind 改名 ObjectKind(列 role_kind → object_kind);
  新增 migrateSkillObjectKindColumn:先拷数据再显式 drop 旧列。
  GORM AutoMigrate 只加不删,字段改名不手写迁移就会新旧两列并存。
- Specialist 结构化列更名走同一套 migrateColumnData:
  role_card_json → interaction_card_json、source_records_json → inputs_records_json
- api/capability_definition.go(413 行)拆为 skill_action_definition.go,
  不再让 capability 充当一级对象名;该文件同时承载技能与动作定义处理器
- 新增 model/app_definition.go + api/app_definition.go:应用目录定义正式建模
- SkillDefinition 移除已废弃的 LegacyObjectEntryRoute 字段,对应迁移分支一并删除
- skill_keys.go 注释更新:权威来源为前端 staticSkillCatalog

前端
- api/assistant.js → chat.js、api/capability.js → skill.js,新增 api/appDefinition.js
- 新增 store/specialistCatalog.js、skillCatalog.js、appCatalog.js,
  取代 config/productizedApps.js(已删)
- config/workbench.js 静态目录去失真:businessApps → staticSpecialistCatalog
  (该变量装的其实是专员目录,与应用 app 语义直接冲突)、
  availableSkills → staticSkillCatalog
- 修复入口 query 协议断裂:写端 appCatalog 用 app_specialist/app_skill/app_prompt,
  读端 SmartAssistantPage 读同名参数并挂响应式监听。
  此前写读两侧命名不一致且无人读旧名,导致从应用目录打开应用时
  预置专员/技能/提示词被静默丢弃、无任何报错
- 页面与组件局部变量去失真:拿到专员对象不再命名为 app

验证:CGO_ENABLED=0 go build ./... 通过;go test ./... 全包通过;npm run build 通过。
旧变量名 businessApps / availableSkills / productizedApps / api/capability / api/assistant
全仓 grep 无残留。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
eaiadmin
2026-09-17 23:38:40 +08:00
co-authored by Claude Code
parent 6194c4733e
commit d0d7b3588c
41 changed files with 2180 additions and 1222 deletions
@@ -353,9 +353,11 @@ import SectionTabs from '@/components/SectionTabs.vue'
import NavSearch from '@/components/NavSearch.vue'
import { useAuthStore } from '@/store/auth'
import { useProjectStore } from '@/store/projectStore'
import { useSkillCatalogStore } from '@/store/skillCatalog'
import { useSpecialistCatalogStore } from '@/store/specialistCatalog'
import { useWorkbenchStore } from '@/store/workbench'
import { useWorkerRuntimeStore } from '@/store/workerRuntime'
import { availableSkills, businessApps, getAppCases, getDefaultCaseId } from '@/config/workbench'
import { getAppCases, getDefaultCaseId } from '@/config/workbench'
import { relativeTime } from '@/config/projectTemplates'
import {
findGroupByPath,
@@ -368,6 +370,8 @@ const route = useRoute()
const router = useRouter()
const auth = useAuthStore()
const projectStore = useProjectStore()
const skillCatalog = useSkillCatalogStore()
const specialistCatalog = useSpecialistCatalogStore()
const workbench = useWorkbenchStore()
const workerRuntime = useWorkerRuntimeStore()
@@ -477,6 +481,8 @@ const selectedCaseId = computed(() => {
})
onMounted(() => {
skillCatalog.hydrate()
specialistCatalog.hydrate()
loadMyTasks()
projectStore.loadProjects().catch((error) => {
console.warn('load projects for shell failed', error)
@@ -539,9 +545,9 @@ watch(
(path) => {
// 顶部胶囊是一级粒度的,所以这里只做两件事:记住你落在哪个专员的工作面
// (右侧工作台要用),以及把路由喂给 store —— 折算成哪个胶囊由 store 一处决定。
const app = businessApps.find((item) => item.legacyObjectEntryRoute === path || item.objectEntryRoute === path)
if (app?.key && !workbench.selectedCases[app.key]) {
workbench.setSelectedCase(app.key, getDefaultCaseId(app.key))
const specialist = specialistCatalog.getByPath(path)
if (specialist?.key && !workbench.selectedCases[specialist.key]) {
workbench.setSelectedCase(specialist.key, getDefaultCaseId(specialist.key))
}
workbench.ensureTab({ pageRoute: path })
},
@@ -602,7 +608,7 @@ async function loadMyTasks() {
// 和「用哪个技能做」,不是完整消息历史。
async function openMyTask(task) {
workerRuntime.openTask(task.id, task.specialist_key)
const currentSkill = availableSkills.find((item) => item.key === workerRuntime.skillKeyOfTask(task.id))
const currentSkill = skillCatalog.getByKey(workerRuntime.skillKeyOfTask(task.id))
const target = currentSkill?.objectEntryRoute || '/home'
if (route.path !== target) await router.push(target)
}