schedules.ts 7.88 KB
/*
 * @Author: AI Assistant
 * @Date: 2025-11-25
 * @LastEditors: 赵丽婷
 * @LastEditTime: 2026-01-19 17:59:53
 * @FilePath: \LinkMed\linkmed-vue3\src\stores\schedules.ts
 * @Description: 定时任务状态管理
 * Copyright (c) 2025 by 北京连心医疗科技有限公司, All Rights Reserved.
 */
import { defineStore } from "pinia";
import { ref } from "vue";
import { createSchedule, type CreateSchedulePayload } from "@/api/schedules";
import { getUserProfile } from "@/api/user";
import { useDeerFlowStore } from "@/stores/deerflow";
import { useSettingsStore } from "@/stores/settings";

export const useSchedulesStore = defineStore("schedules", () => {
  // State - 存储从组件传递的字段
  const startTask = ref<boolean>(false);
  const startTime = ref<string>("");
  const notifyType = ref<string>("");
  const repeatType = ref<string | undefined>(undefined);
  const repeatExpr = ref<string | undefined>(undefined);
  const repeatEndTime = ref<string | undefined>(undefined);
  const description = ref<string>("");
  const scheduleId = ref<number | undefined>(undefined); // 任务Id

  // 加载状态
  const loading = ref(false);
  const error = ref<string | null>(null);
  const deerflowStore = useDeerFlowStore();
  const settingsStore = useSettingsStore();

  /**
   * 设置定时任务基础信息
   * 从组件中接收的核心字段
   */
  function setScheduleInfo(info: {
    startTime: string;
    notifyType: string;
    repeatType?: string;
    repeatExpr?: string;
    repeatEndTime?: string;
  }) {
    startTime.value = info.startTime;
    notifyType.value = info.notifyType;
    repeatType.value = info.repeatType;
    repeatExpr.value = info.repeatExpr;
    repeatEndTime.value = info.repeatEndTime;
  }

  /**
   * 获取用户邮箱
   */
  async function getUserEmail(): Promise<string> {
    try {
      const userProfile = await getUserProfile();
      if (userProfile && userProfile.email) {
        return userProfile.email;
      }
    } catch (error) {
      console.error("获取用户邮箱失败:", error);
    }
    return "";
  }

  /**
   * 构建深度检索接口的 params 参数
   */
  function buildParams(mode?: "fast" | "standard" | "deep") {
    // 构建 messages 数组
    const messages = [
      {
        role: "user",
        content: description.value || "",
      },
    ];
    // 构建 高级筛选 内容

    const realThreadId = deerflowStore?.currentThreadId || "";

    // 构建 literature_filters 参数
    const filters = settingsStore.getLiteratureFilters() || [];
    const dateRange = settingsStore.getLiteratureDateRange();

    // 判断是否启用了知识库
    const isKnowledgeBaseEnabled = !!settingsStore.selectedKnowledgeBaseDirectory;

    // 将 literatureFilters 转换为只包含 key 和 selected 的格式,只保留 key 为 "dataSource" 的项
    const literatureFiltersFormatted = filters
      .filter((filter) => filter.key === "dataSource")
      .map((filter) => {
        // 如果启用了知识库,数据源应为空;否则如果没有选择,默认使用 pubmed
        let selected = isKnowledgeBaseEnabled
          ? []
          : filter.selected && filter.selected.length > 0
            ? filter.selected
            : ["pubmed"];

        // 强制过滤掉 medRxiv,不影响正常功能
        selected = selected.filter((id: string) => id !== "medRxiv");

        // 如果过滤后为空且未启用知识库,确保至少有 pubmed
        if (selected.length === 0 && !isKnowledgeBaseEnabled) {
          selected = ["pubmed"];
        }

        return {
          key: filter.key || "dataSource",
          selected: selected,
        };
      });

    // 构建 literature_filters 对象
    const literature_filters: any = {
      literatureFilters: literatureFiltersFormatted,
    };

    // 如果有日期范围,添加到结果中
    if (dateRange && Array.isArray(dateRange) && dateRange.length === 2) {
      literature_filters.literatureDateRange = dateRange;
    }

    // 构建完整的 params 对象(深度检索接口参数)
    return {
      messages,
      resources: [],
      debug: false,
      thread_id: realThreadId,
      ...(mode ? { mode } : {}),
      max_plan_iterations: settingsStore.maxPlanIterations,
      max_step_num: settingsStore.maxStepNum,
      max_search_results: settingsStore.maxSearchResults,
      auto_accepted_plan: true,
      interrupt_feedback: null,
      enable_background_investigation: true,
      report_style: "academic",
      enable_deep_thinking: false,
      literature_filters,
      use_surveyx: isKnowledgeBaseEnabled,
      file_ids: isKnowledgeBaseEnabled
        ? settingsStore.knowledgeBaseFileIds.map((id) => "prod" + id)
        : [],
    };
  }

  /**
   * 构建 createSchedule 的 payload 参数
   */
  async function buildPayload(
    mode?: "fast" | "standard" | "deep",
  ): Promise<CreateSchedulePayload> {
    // // 获取用户邮箱
    // const userEmail = await getUserEmail();
    // 构建 params(深度检索接口参数)
    const params = buildParams(mode);

    // 截断 title 字段,最多 200 个字符
    const title =
      description.value.length > 200
        ? description.value.substring(0, 200)
        : description.value;

    // 构建 payload
    const payload: CreateSchedulePayload = {
      title: title,
      question: description.value,
      startTime: startTime.value,
      repeatType: repeatType.value,
      repeatExpr: repeatExpr.value,
      repeatEndTime: repeatEndTime.value,
      notifyType: "all",
      notifyTo: undefined,
      params: params,
    };

    return payload;
  }

  /**
   * 创建定时任务
   * 调用 API 接口
   */
  async function createScheduleTask(mode?: "fast" | "standard" | "deep") {
    try {
      loading.value = true;
      error.value = null;

      // 验证必填字段
      if (!startTime.value) {
        throw new Error("开始时间不能为空");
      }
      if (!description.value || description.value.trim() === "") {
        throw new Error("描述不能为空");
      }

      // // 如果需要邮件通知,检查用户邮箱
      // if (notifyType.value === "email") {
      //   const userEmail = await getUserEmail();
      //   if (!userEmail) {
      //     throw new Error("无法获取用户邮箱,请绑定邮箱");
      //   }
      // }

      // 构建 payload
      const payload = await buildPayload(mode);
      // 调用创建定时任务接口
      // 接口返回形式:HTTP 状态码 200,响应体为可选的 integer <int64>(任务ID)
      const response = await createSchedule(payload);

      // response.data 直接是任务ID(数字),如果存在则创建成功
      const taskId = response.data;
      if (taskId !== undefined && taskId !== null) {
        return {
          success: true,
          data: { id: taskId }, // 为了保持向后兼容,包装成对象
          message: "定时任务创建成功",
        };
      } else {
        throw new Error("创建定时任务失败:未返回任务ID");
      }
    } catch (err: any) {
      error.value = err?.message || "创建定时任务失败";
      console.error("创建定时任务失败:", err);
      return {
        success: false,
        data: null,
        message:
          err?.response?.data?.message || err?.message || "创建定时任务失败",
      };
    } finally {
      loading.value = false;
    }
  }

  /**
   * 重置状态
   */
  function resetScheduleInfo() {
    startTime.value = "";
    notifyType.value = "";
    repeatType.value = undefined;
    repeatExpr.value = undefined;
    repeatEndTime.value = undefined;
    description.value = "";
    error.value = null;
  }

  return {
    // state
    startTask,
    startTime,
    notifyType,
    repeatType,
    repeatExpr,
    repeatEndTime,
    description,
    loading,
    error,
    scheduleId,
    // actions
    setScheduleInfo,
    buildPayload,
    buildParams,
    createScheduleTask,
    resetScheduleInfo,
    getUserEmail,
  };
});