Files
zn-ai/docs/ChannelRefactorPlan.md
DEV_DSW 79bea4f107 Refactor UUID generation, remove unused logger and encryption utilities, and clean up request handling
- Updated `generateUUID` function for improved readability and performance.
- Deleted `logger.ts`, `other.ts`, `request.ts`, `storage.ts`, `tansParams.ts`, and `validate.ts` as they were no longer needed.
- Simplified TypeScript configuration by removing unnecessary paths and aliases.
- Enhanced Vite configuration for better project structure and maintainability.
2026-04-17 15:38:08 +08:00

11 KiB
Raw Blame History

渠道硬编码移除与动态关联重构计划

上下文与目标

当前 zn-ai 的"一键打开各渠道"功能存在以下问题:

  1. 渠道数据硬编码src/constant/channel.ts 中,仅包含 fliggy/meituan/douyin 三个固定渠道;
  2. AddChannelDialog.vue 是纯 UI 空壳,收集表单后不做任何持久化或事件抛出;
  3. TaskCenter.vue 存在 bug:将普通数组 channels 当作 ref 使用(channels.value),实际传递 undefined 给主进程;
  4. 脚本管理功能中,每个 AutomationScript 已有 channel 字段(存储渠道名称或 URL但未被"一键打开"功能复用。

目标

  • 移除 channels.ts 中的硬编码主数据源,改为从脚本管理中动态聚合可用渠道;
  • 重塑 AddChannelDialog 的交互:快捷搜索选择渠道 → 展示已选列表 → 保存;
  • 修复 TaskCenter.vue 的渠道传递逻辑,使用用户动态配置的列表;
  • 确保 open_all_channel.js 接收的数据格式正确,按需同步调整。

关键文件清单

路径 角色 改动类型
zn-ai/src/constant/channel.ts 硬编码渠道常量 移除数组,保留字典+解析工具
zn-ai/src/stores/channel.ts 新建渠道状态 store 新增
zn-ai/src/stores/script.ts 脚本 Pinia store 只读依赖(聚合 channel
zn-ai/src/pages/home/components/AddChannelDialog.vue 渠道关联弹窗 重写模板与逻辑
zn-ai/src/pages/home/TaskCenter.vue 任务中心卡片 修复+替换数据源
zn-ai/src/pages/home/index.vue 首页容器 可能需补充 store 初始化
zn-ai/src/pages/scripts/components/ScriptEditorDialog.vue 脚本编辑弹窗 替换 channels 引用为字典
zn-ai/electron/scripts/open_all_channel.js 打开渠道的 Playwright 脚本 按需调整字段读取
zn-ai/electron/process/runTaskOperationService.ts IPC 处理OPEN_CHANNEL 按需做数据格式适配

方案设计

1. 数据层:constant/channel.ts + 新建 stores/channel.ts

constant/channel.ts 改造

  • 移除 export const channels: Item[] 硬编码数组。
  • 保留 Item 接口({id, channelName, channelUrl})。
  • 新增 channelDictionary: Record<string, string>,仅作为"已知渠道名称→URL"的参考字典(从原数组迁移)。
  • 新增 resolveChannel(value: string): { name?: string; url: string }
    • value 匹配 channelDictionary 的 key返回 {name: value, url: dictionary[value]}
    • value 本身像 URL以 http 开头),尝试从字典反向查找 name找不到则 name 取 hostname 或原值;
    • 其他情况返回 {name: value, url: value}

新建 stores/channel.tsPinia

使用 Composition API 风格,与 script.ts/cron.ts 保持一致。

export interface ChannelItem {
  id: string;
  channelName: string;
  channelUrl: string;
}

const STORAGE_KEY = 'zn-ai:selected-channels';

export const useChannelStore = defineStore('channel', () => {
  const scriptStore = useScriptStore();

  // 用户选中的"一键打开"渠道(持久化到 localStorage
  const selectedChannels = ref<ChannelItem[]>([]);

  // 从脚本 store 动态聚合可用渠道(去重,按 URL 去重)
  const availableChannels = computed<ChannelItem[]>(() => {
    const map = new Map<string, ChannelItem>();
    for (const script of scriptStore.safeScripts) {
      if (!script.channel) continue;
      const resolved = resolveChannel(script.channel);
      const url = resolved.url;
      if (!map.has(url)) {
        map.set(url, {
          id: `channel-${url}`,
          channelName: resolved.name || url,
          channelUrl: url,
        });
      }
    }
    return Array.from(map.values());
  });

  const loadSelectedChannels = () => { ... };
  const saveSelectedChannels = () => { ... };
  const addSelectedChannel = (item: ChannelItem) => { ... };
  const removeSelectedChannel = (id: string) => { ... };
  const setSelectedChannels = (items: ChannelItem[]) => { ... };

  return {
    selectedChannels,
    availableChannels,
    loadSelectedChannels,
    saveSelectedChannels,
    addSelectedChannel,
    removeSelectedChannel,
    setSelectedChannels,
  };
});

注意AddChannelDialog 打开时应基于 availableChannels 做搜索,确认保存时调用 setSelectedChannels + saveSelectedChannels


2. UI 层:AddChannelDialog.vue 重塑

当前弹窗是两个空白输入框(名称+URL需要改为搜索选择 + 已选列表

新交互流程

  1. 顶部搜索区el-autocomplete(或 el-input + 自定义下拉列表)绑定 availableChannels 过滤结果;
  2. 选择后:将渠道添加到下方的"已选渠道"列表(去重检查);
  3. 已选列表:使用卡片/表格形式展示,每行包含:
    • 渠道名称
    • 渠道 URL截断显示tooltip 展示完整)
    • 删除按钮(el-icon Delete
  4. 底部操作:取消 / 确认。确认时持久化到 store。

状态隔离

弹窗打开时,先 loadSelectedChannels() 到本地临时数组;编辑过程中操作临时数组;确认时写入 store 并持久化;取消时直接关闭,不污染 store。

样式适配

复用项目已有的 custom-script-dialog 圆角/深色模式变量,保持视觉一致性。


3. 修复 TaskCenter.vue

  • 移除 import { channels } from '@constant/channel'
  • 注入 useChannelStore
  • 点击"一键打开各渠道"时:
    const channelStore = useChannelStore();
    window.api.openChannel(channelStore.selectedChannels)
    
  • 如果 selectedChannels 为空,可给出提示(ElMessage.warning)或仍然允许空数组由主进程处理。

4. ScriptEditorDialog.vue 适配

  • import { channels } from '@constant/channel' 改为 import { channelDictionary, resolveChannel } from '@constant/channel'
  • getChannelUrl(channel) 改为在 channelDictionary 中查找。
  • channel URL 替换 watcher 逻辑同步使用 channelDictionary 的 values。

5. open_all_channel.js 与主进程适配

当前 open_all_channel.js 读取环境变量 CHANNELS,期望元素结构为 { channelUrl: string }(第 81 行 channels[i]?.channelUrl)。

channelStore.selectedChannels 的结构保持为 Item{ id, channelName, channelUrl }),因此主进程接收到的数组本身已包含 channelUrl 字段

需要确认 runTaskOperationService.tsIPC_EVENTS.OPEN_CHANNEL handler

ipcMain.handle(IPC_EVENTS.OPEN_CHANNEL, async (_event, channels: any) => { ... })

它直接将 channels 传给 executeScriptServiceInstance.executeScript(scriptPath, { channels }),后者 JSON.stringify 后写入 CHANNELS 环境变量。

结论:只要 TaskCenter.vue 传递的数组元素包含 channelUrlopen_all_channel.js 无需修改即可正常工作。

但用户要求"同步更新 open_all_channel.js",为确保严谨,计划内包含兼容性检查:若发现字段名不一致,统一调整为 channelUrl;若需要增强(如按名称去重、空值过滤),则在 runTaskOperationService.ts 的 handler 中做适配,保持 open_all_channel.js 的输入契约稳定。


Sub-agent 分工(共 3 个)

Agent A — 数据层与字典迁移

负责范围

  • 改造 zn-ai/src/constant/channel.ts(移除数组,保留字典+resolveChannel)。
  • 新建 zn-ai/src/stores/channel.tsPinia storeavailableChannels / selectedChannels / localStorage 持久化)。
  • 修改 ScriptEditorDialog.vue 中的引用,从原 channels 数组切换到 channelDictionary

输入依赖:已确认的 script.ts 结构、channel.ts 原数组。 输出产物:可运行的数据层代码。


Agent B — AddChannelDialog UI 重构

负责范围

  • 重写 zn-ai/src/pages/home/components/AddChannelDialog.vue
  • 实现搜索选择(el-autocomplete 或自定义搜索+下拉)与已选列表展示。
  • 对接 useChannelStore:读取 availableChannels,保存时写入 selectedChannels
  • 保持与项目一致的 dark/light 样式。

输入依赖Agent A 完成的 stores/channel.ts工作方式:可以并行启动,但代码编辑需等待 Agent A 的 store 文件就位(或先在本地 mock 接口)。


Agent C — TaskCenter 修复与端到端验证

负责范围

  • 修改 zn-ai/src/pages/home/TaskCenter.vue:移除硬编码导入,注入 useChannelStore,修复 .value bug。
  • 检查 zn-ai/electron/process/runTaskOperationService.tsopen_all_channel.js 的数据格式兼容性,必要时做适配。
  • zn-ai/src/pages/home/index.vue 中添加 channelStore.loadSelectedChannels() 初始化onMounted 或合适位置)。
  • 运行项目(pnpm run dev 或等效命令),验证:
    1. 脚本管理中有渠道数据时,AddChannelDialog 能搜索到;
    2. 选择并保存后,TaskCenter 能正确传递数组;
    3. open_all_channel.js 能正常解析并打开页面。

输入依赖Agent A 与 Agent B 的产物。 工作方式:必须在 A、B 完成后启动。


执行顺序

Agent A (数据层) ─┬─► Agent B (UI 弹窗)
                  │
                  └─► Agent C (流程修复与验证)
  • A 与 B 可并行或准并行B 可以在 A 提交 store 初版后立即开始对接。
  • C 必须在 A、B 都完成后启动,负责联调与验证。

风险与回退策略

风险 缓解措施
availableChannels 为空(脚本管理无渠道数据) AddChannelDialog 搜索下拉显示"暂无可用渠道",已选列表允许空状态;TaskCenter 点击时给出提示
脚本 channel 字段格式混乱(既有 URL 又有名称) resolveChannel() 统一做归一化处理,以 URL 为唯一键
open_all_channel.js 字段契约被打破 runTaskOperationService.ts 的 handler 中做字段映射/过滤,保证脚本侧输入不变
localStorage 中旧数据格式不兼容 loadSelectedChannels 读取时做 schema 校验,不兼容则清空并回退到空数组

验证清单Agent C 负责)

  • channels.ts 中不再导出硬编码数组。
  • AddChannelDialog 打开后,能从脚本中搜索出渠道(如 fliggy / meituan / douyin
  • 选择多个渠道后列表正确显示,删除单个后状态正确。
  • 点击"确认"关闭弹窗,再次打开能恢复上次保存的选项。
  • TaskCenter 中点击"一键打开各渠道"openChannel 参数为正确的 ChannelItem[](非 undefined
  • open_all_channel.js 成功解析 CHANNELS 并依次打开对应页面。
  • 脚本编辑弹窗中 getChannelUrl 仍能正确将 fliggy 解析为对应 URL。