feat(ai-kb): 落地 RAG 知识库与灰度链路(48 feature / 546 chunk)

This commit is contained in:
kuaifan 2026-06-10 04:05:18 +00:00
parent f6067d1bd5
commit af206480fb
588 changed files with 33431 additions and 2 deletions

97
.github/workflows/ai-kb-reindex.yml vendored Normal file
View File

@ -0,0 +1,97 @@
name: "AI-KB Reindex"
# 主仓库 ai-kb 内容变更后,远程触发 AI 插件 `POST /kb/reindex` 增量入库。
# 失败不阻塞 PR/发布AI 容器下次启动会自动 ingest_all 兜底plan §三 / R9
on:
push:
branches:
- "pro"
paths:
- "resources/ai-kb/**"
workflow_dispatch:
inputs:
mode:
description: "incremental | full"
required: false
default: "incremental"
paths:
description: "JSON 数组,相对 resources/ai-kb/ 的路径mode=incremental 时使用)"
required: false
default: "[]"
jobs:
reindex:
permissions:
contents: read
runs-on: ubuntu-latest
timeout-minutes: 5
# 不被其他 job 依赖;失败不阻塞合并。
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # 用于 git diff HEAD~1 HEAD
- name: Resolve changed paths
id: paths
run: |
set -euo pipefail
MODE="${{ github.event.inputs.mode || 'incremental' }}"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
INPUT_PATHS='${{ github.event.inputs.paths }}'
if [ -z "${INPUT_PATHS}" ] || [ "${INPUT_PATHS}" = "[]" ]; then
MODE="full"
PATHS_JSON="[]"
else
PATHS_JSON="${INPUT_PATHS}"
fi
else
CHANGED=$(git diff --name-only HEAD~1 HEAD -- 'resources/ai-kb/**/*.md' 'resources/ai-kb/**/*.yaml' 'resources/ai-kb/**/*.yml' || true)
if [ -z "${CHANGED}" ]; then
echo "No ai-kb file changes detected; skip reindex call."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
PATHS_JSON=$(printf '%s\n' "${CHANGED}" \
| sed 's|^resources/ai-kb/||' \
| jq -R . | jq -cs .)
fi
echo "mode=${MODE}" >> "$GITHUB_OUTPUT"
echo "paths_json=${PATHS_JSON}" >> "$GITHUB_OUTPUT"
echo "skip=false" >> "$GITHUB_OUTPUT"
- name: Trigger /kb/reindex
if: steps.paths.outputs.skip != 'true'
env:
AI_SERVICE_URL: ${{ secrets.AI_SERVICE_URL }}
KB_INGEST_TOKEN: ${{ secrets.KB_INGEST_TOKEN }}
MODE: ${{ steps.paths.outputs.mode }}
PATHS_JSON: ${{ steps.paths.outputs.paths_json }}
run: |
set -euo pipefail
if [ -z "${AI_SERVICE_URL:-}" ] || [ -z "${KB_INGEST_TOKEN:-}" ]; then
echo "::warning ::AI_SERVICE_URL or KB_INGEST_TOKEN secret not set; skip remote reindex."
exit 0
fi
BODY=$(jq -cn --arg mode "$MODE" --argjson paths "$PATHS_JSON" '{mode:$mode, paths:$paths}')
echo "Posting body: $BODY"
HTTP_CODE=$(curl -sS -o /tmp/kb-reindex.json -w '%{http_code}' \
--max-time 90 \
--retry 2 --retry-delay 5 --retry-connrefused \
-X POST "${AI_SERVICE_URL%/}/kb/reindex" \
-H "X-Ingest-Token: ${KB_INGEST_TOKEN}" \
-H 'Content-Type: application/json' \
-d "$BODY" || true)
echo "HTTP $HTTP_CODE"
cat /tmp/kb-reindex.json || true
if [ "$HTTP_CODE" != "200" ]; then
echo "::warning ::reindex returned HTTP $HTTP_CODE — AI 容器下次启动会 ingest_all 兜底(参见 plan §三/R9"
exit 0 # 不阻塞 push / 发布
fi

View File

@ -49,6 +49,32 @@ Laravel 8 (LaravelS/Swoole) + Vue 2 (Vite) + Electron。开源任务/项目管
- 新增用户可见文本须追加原文(简体中文)到:前端 `language/original-web.txt`,后端 `language/original-api.txt`(去重)
- 前端翻译用 `$L("文本")`,动态值用 `(*)` 占位:`$L('共(*)条', n)`——禁止拼接翻译
## DooTask AI 知识库 (ai-kb) 同步规则
`dootask/resources/ai-kb/` 是**专给 AI 助手 RAG 检索的功能知识库**,与人类文档(`dootask-website`)独立。它直接影响产品内 AI 助手能否正确回答用户的「X 怎么用」。
**何时必须同步更新 ai-kb**
- 新增、修改、删除任何用户可见的功能、菜单、按钮、流程、字段
- 调整 API 行为(错误码、参数含义、返回结构)
- 引入新插件 / 微应用,或修改权限 / 角色定义
**操作步骤**
1. 在 `resources/ai-kb/_meta/feature-map.yaml` 找到对应 `feature` 的 chunk 清单
2. 按 `resources/ai-kb/_schema/chunk-style.md` 风格修改对应 markdown
3. 更新 frontmatter 的 `last_verified` 字段为当前主程序版本号
4. 该功能没有对应 chunk 时,按 `resources/ai-kb/_schema/frontmatter.md` 规范新建
**禁止**
- 跨章节指代("如上图所示"、"在前面一节"——RAG 切块后会丢失上下文
- 把 dootask-website 的人类教程或截图直接复制过来
- 单独提交「只改 ai-kb」的 PR——应与触发它的主代码改动同一个 PR
- 改产品代码但不改 ai-kbPR review 应拦截)
**自动化**:合入 main 后 CI 自动调用 AI 插件的 `POST /kb/reindex` 增量入库;失败由 AI 容器重启自动 `ingest_all` 兜底。
## Playwright 测试
- Playwright 测试结果放在 `tests/playwright-results/`,包含测试环境、测试用例、结果截图等信息

View File

@ -46,8 +46,17 @@ class AssistantController extends AbstractController
$modelType = trim(Request::input('model_type', ''));
$modelName = trim(Request::input('model_name', ''));
$contextInput = Request::input('context', []);
// ai-kb 检索语种;缺省 zh前端传 'zh' / 'en'
$supportedLocales = config('ai.rag_supported_locales', ['zh', 'en']);
$locale = trim(Request::input('locale', 'zh'));
if (!in_array($locale, $supportedLocales, true)) {
$locale = 'zh';
}
return AI::createStreamKey($modelType, $modelName, $contextInput);
// 灰度判定(参考 config/ai.php总开关 + canary 白名单
$ragEnabled = AI::ragEnabledFor((int) $user->userid);
return AI::createStreamKey($modelType, $modelName, $contextInput, $locale, $ragEnabled);
}
/**

View File

@ -140,7 +140,31 @@ class AI
* @param mixed $contextInput
* @return array
*/
public static function createStreamKey($modelType, $modelName, $contextInput = [])
/**
* 判定当前用户是否启用 ai-kb RAG灰度判定
*
* 规则(参考 config/ai.php
* - 总开关 rag_enabled=false 关闭所有kill switch
* - rag_canary_userids 为空 全员启用
* - 否则仅白名单 userid 启用
*/
public static function ragEnabledFor(int $userid): bool
{
if (!config('ai.rag_enabled', true)) {
return false;
}
$raw = trim((string) config('ai.rag_canary_userids', ''));
if ($raw === '') {
return true;
}
$allow = array_filter(array_map(
fn($v) => (int) trim($v),
explode(',', $raw)
), fn($v) => $v > 0);
return in_array($userid, $allow, true);
}
public static function createStreamKey($modelType, $modelName, $contextInput = [], $locale = 'zh', $ragEnabled = true)
{
$modelType = trim((string)$modelType);
$modelName = trim((string)$modelName);
@ -221,6 +245,9 @@ class AI
'model_type' => $remoteModelType,
'model_name' => $modelName,
'context' => $contextJson,
'locale' => $locale,
// ai-kb 灰度透传1 启用 RAGhint + search_help_docs tool0 关闭
'rag_enabled' => $ragEnabled ? '1' : '0',
];
$baseUrl = trim((string)($setting[$modelType . '_base_url'] ?? ''));

57
config/ai.php Normal file
View File

@ -0,0 +1,57 @@
<?php
/*
|--------------------------------------------------------------------------
| DooTask AI 助手灰度配置
|--------------------------------------------------------------------------
|
| RAG帮助知识库检索功能上线时按以下顺序灰度参考 plan §八):
| Stage 1 stagingRAG_ENABLED=true staging 环境,全体可用
| Stage 2 canaryRAG_ENABLED=true + RAG_CANARY_USERIDS="1,2,3,4,5"
| 仅白名单 user 命中 RAG
| Stage 3 broad清空 RAG_CANARY_USERIDS全局启用
|
| 紧急关停kill switch5 分钟生效):
| 1) 改容器 env RAG_ENABLED=false
| 2) ./cmd php restart swoole 重读 config
| 3) AI 容器收到 rag_enabled=0 时跳过 RAG hint 注入与 search_help_docs 工具挂载
|
| 灰度判定语义:
| rag_enabled (env total switch)
| ├─ false 所有人都不走 RAGkill switch
| └─ true 进一步看 canary
| ├─ rag_canary_userids 为空(默认)→ 全员启用
| └─ rag_canary_userids 有值 仅白名单 userid 启用
|
*/
return [
/*
|--------------------------------------------------------------------------
| RAG 总开关
|--------------------------------------------------------------------------
| true - 默认开启,按 canary 白名单进一步过滤
| false - 紧急 kill switch,所有用户都不走 RAG
*/
'rag_enabled' => filter_var(env('RAG_ENABLED', true), FILTER_VALIDATE_BOOLEAN),
/*
|--------------------------------------------------------------------------
| RAG canary 白名单
|--------------------------------------------------------------------------
| 逗号分隔的 userid 列表。
| 留空表示 全员启用Stage 3 broad rollout
| 有值表示 仅白名单 userid 命中 RAGStage 2 canary
*/
'rag_canary_userids' => env('RAG_CANARY_USERIDS', ''),
/*
|--------------------------------------------------------------------------
| RAG 检索语种受控集合
|--------------------------------------------------------------------------
| 前端可传的 locale 值;不在集合内默认回退到 zh。
| P0 仅启用 zhP1 起开放 en。
*/
'rag_supported_locales' => ['zh', 'en'],
];

80
resources/ai-kb/README.md Normal file
View File

@ -0,0 +1,80 @@
# ai-kb — DooTask AI 助手知识库
这是**专为大语言模型LLM检索使用**的 DooTask 功能知识库,不是给人类阅读的产品文档(那个在 [dootask-website](https://github.com/dootask/dootask-website) 仓库的 `help/docs`)。
它的唯一消费者是 AI 助手:用户问"看板列怎么改名 / 审批可以分支吗 / 5.4 有什么新功能"时,助手通过 RAG 检索这里的内容来作答。
## 目录结构
```
ai-kb/
├── _schema/ 写作规范(必读)
│ ├── frontmatter.md frontmatter 字段规范 + 受控词表
│ └── chunk-style.md chunk 写作风格 + 正反例
├── _meta/ 元数据CI 与脚本读取)
│ ├── feature-map.yaml feature 全集 + 每个 feature 的 chunk 清单
│ └── tool-binding.yaml chunk ↔ MCP 工具映射
├── _eval/ 回归测试
│ └── golden-50q.yaml 50 题评估集
├── zh/ 中文知识库P0 主战场)
│ ├── concept/ 「是什么」
│ ├── howto/ 「怎么做」(含 apps/ 子目录覆盖应用中心)
│ ├── faq/ 「为什么 / 出错怎么办」
│ ├── menu-map/ 「X 入口在哪」
│ ├── glossary/ 术语 + 别名
│ └── shortcut/ 快捷键、移动端手势
└── en/ 英文知识库P1 起草P0 保留空目录)
```
## 为什么不复用 dootask-website 的人类文档
| 维度 | 人类文档 | ai-kb |
|---|---|---|
| 阅读单位 | 一篇文章 | 一个 chunk128-512 token |
| 自包含性 | 假设从头读 | 每个 chunk 独立可懂 |
| 跨章节指代 | 「如上图所示」可以 | 禁止 |
| 截图 | 必要 | 禁止依赖(用文字描述) |
| 同义词 | 一处定义 | 显式列别名 |
| 否定信息 | 少 | 必备 |
| 元数据 | 标题即可 | 严格 frontmatter |
直接对人类文档做 RAG 召回率低、易编造,所以这里独立维护。
## 怎么开始写一个 chunk
1. 通读 [`_schema/frontmatter.md`](./_schema/frontmatter.md) — 字段规范与受控词表
2. 通读 [`_schema/chunk-style.md`](./_schema/chunk-style.md) — 写作风格与正反例
3. 在 [`_meta/feature-map.yaml`](./_meta/feature-map.yaml) 找到对应 feature 的 chunk 清单和归属批次
4. 在对应 `zh/<type>/<feature>/<id>.md` 路径下新建文件
5. 提交 PRCI 会自动跑 lint通过且 review 完毕后合入 mainCI 自动触发 AI 插件的 `POST /kb/reindex` 入库
## 改 DooTask 主程序后必须同步更新这里
**这是硬性约束** —— 详见主仓库根目录 `CLAUDE.md` 中「DooTask AI 知识库 (ai-kb) 同步规则」章节。新增/修改/删除任何用户可见的功能、菜单、按钮、流程、字段、API 行为、权限角色,都必须在**同一 PR**里更新对应 chunk 并把 frontmatter 的 `last_verified` 改成当前版本号。
不更新的代价是 AI 助手给用户讲错路径,比 PR 多写两行成本高得多。
## 工程接口(代码在 AI 插件那一侧)
ingest、检索、lint、eval 的实现在 `dootask-plugins/system-plugins/ai/src/helper/kb/`。本目录纯内容,不放 Python 代码。
AI 插件容器通过只读 volume mount 看到本目录:
```yaml
volumes:
- ../../../dootask/resources/ai-kb:/app/kb-content:ro
```
触发入库CI 或运维手动):
```bash
curl -X POST 'http://ai-service/kb/reindex' \
-H "X-Ingest-Token: $KB_INGEST_TOKEN" \
-d '{"paths":["zh/howto/task-create.md"], "mode":"incremental"}'
```
容器启动时 lifespan 会自动跑一次 `ingest_all` 作为兜底。
## 维护责任
- **内容**:产品功能负责人 / PM / 技术写作者按 `_meta/feature-map.yaml` 中的 `owner` 列认领
- **schema 与受控词表**:架构组维护,修改需走 PR
- **lint / ingest / retriever 代码**AI 插件维护组

View File

@ -0,0 +1,575 @@
# 50 题回归测试集S5 完整验收)
#
# 字段:
# id 测试题 id
# q 用户提问
# locale zh / en
# expected_chunk_ids 期望被 retriever 召回的 chunk id≥1 条命中即 recall=1
# expected_answer 期望答案要点LLM-judge 用,自然语言描述关键事实)
# must_say 正例:答案必须包含的关键词中至少 1 条 / 拒答桶:拒答关键词
# must_not_say 答案禁止出现的内容(编造点)
# bucket easy / medium / hard / honest-negative
# feature 对应 feature追溯定位
#
# 分布plan §4.1
# 一级导航 + 项目/任务核心 12 (easy 6 / med 6)
# 视图/会议/报告/审批/签到/OKR 10
# 应用中心 6 (easy 4 / med 2)
# 用户/组织/系统管理 6 (med 4 / hard 2)
# AI/搜索/终端 4 (medium)
# 快捷键/术语 4 (easy)
# 跨模块组合 3 (hard)
# 中英混合 2 (medium)
# 诚实性 negative 3
# ---合计--- 50
version: 3
last_updated: 2026-06-10
tests:
# ----- 桶 1: 一级导航 + 项目/任务核心 12 -----
- id: q1
q: "看板列怎么改名?"
locale: zh
expected_chunk_ids: [project.column.howto.edit, view.kanban.howto.column-color]
expected_answer: "在项目看板视图,点击列标题右侧「···」菜单选「修改」,在弹窗中输入新列表名保存;同一菜单还可设置列颜色、归档、删除列,列顺序通过拖拽调整。"
must_say: [列, 改名, 重命名]
must_not_say: []
bucket: easy
feature: project
- id: q2
q: "怎么快速创建一个任务?"
locale: zh
expected_chunk_ids: [task.create.howto.quick]
expected_answer: "在项目看板任意列底部点击添加区域,输入任务描述回车即可创建;或左上角「新建任务」按钮(快捷键 Ctrl/Cmd+K 或 Ctrl/Cmd+N。"
must_say: [快速, 创建, 任务]
must_not_say: []
bucket: easy
feature: task
- id: q3
q: "DooTask 的默认工作流是什么样的?"
locale: zh
expected_chunk_ids: [project.flow.concept.default]
expected_answer: "默认 5 个流程节点:待处理 / 进行中 / 待测试 / 已完成 / 已取消,可自定义。"
must_say: [待处理, 进行中, 已完成]
must_not_say: []
bucket: easy
feature: project
- id: q4
q: "仪表盘上有哪些卡片?"
locale: zh
expected_chunk_ids: [dashboard.concept]
expected_answer: "仪表盘顶部有「今日到期 / 超期任务 / 待完成任务」3 个统计卡片,下方按今日到期、超期、待完成、协助的任务分组列出任务,是个人工作台。"
must_say: [仪表盘, 卡片]
must_not_say: []
bucket: easy
feature: dashboard
- id: q5
q: "日历入口在哪?"
locale: zh
expected_chunk_ids: [calendar.entry.menu-map, menu-navigation.calendar.menu-map]
expected_answer: "左侧主导航点击「日历」即可进入(与仪表盘、消息、文件、应用并列)。"
must_say: [日历]
must_not_say: []
bucket: easy
feature: calendar
- id: q6
q: "子任务可以再嵌套子任务吗?"
locale: zh
expected_chunk_ids: [task.subtask.limits.concept]
expected_answer: "DooTask 子任务只支持单层,不能继续嵌套子子任务。"
must_say: [子任务, 不支持嵌套, 不能嵌套, 单层, 只支持]
must_not_say: [支持多层嵌套, 可以无限嵌套]
bucket: easy
feature: task
- id: q7
q: "想改任务负责人怎么改?"
locale: zh
expected_chunk_ids: [task.edit.howto.basic, role-permission.task-role.concept]
expected_answer: "打开任务详情,在「负责人」字段下拉选择新成员;只有项目成员可被指派。"
must_say: [负责人]
must_not_say: []
bucket: medium
feature: task
- id: q8
q: "我要离职了,能不能把项目移交给同事?"
locale: zh
expected_chunk_ids: [project.transfer.howto]
expected_answer: "项目页右上角「···」菜单选「移交项目」,选择新负责人确认即可;仅项目主负责人可操作,移交后项目群归属同步变更。"
must_say: [移交, 转让, 项目]
must_not_say: []
bucket: medium
feature: project
- id: q9
q: "任务快到截止时间会怎么提醒我?"
locale: zh
expected_chunk_ids: [task.notify.concept, mobile-client.notify.concept]
expected_answer: "到期前约 1 小时和超期约 1 小时后「task-alert」任务提醒机器人会私聊发送「任务即将超时 / 已超时」提醒(需管理员开启 APP 推送设置);消息经站内/桌面通知和移动端推送触达。"
must_say: [提醒]
must_not_say: []
bucket: hard
feature: task
- id: q10
q: "项目里的任务能导出 Excel 吗?"
locale: zh
expected_chunk_ids: [project.export.howto]
expected_answer: "限管理员:左上角菜单「团队管理」子菜单(或应用中心「数据导出」)选「导出任务统计」,选成员(最多 100 人)和时间范围(最长 90 天)导出 Excel另有「导出超期任务」。"
must_say: [导出, Excel]
must_not_say: []
bucket: medium
feature: project
- id: q11
q: "删除看板列时,列里已有的任务怎么办?"
locale: zh
expected_chunk_ids: [project.column.howto.remove]
expected_answer: "删除列会级联删除(软删除)该列下未归档的任务;被删任务可在项目菜单「已删除任务」中查看并「还原」。删除前也可先把任务拖到其他列。"
must_say: [删除, 任务]
must_not_say: [自动迁移到其它列]
bucket: medium
feature: project
- id: q12
q: "把任务从一个项目搬到另一个项目"
locale: zh
expected_chunk_ids: [task.move.howto.cross-project]
expected_answer: "任务操作菜单选「移动」,在「移动任务」弹窗选择目标项目与列表,可同时重设任务状态、负责人、协助人后确认;任务主要字段保留。"
must_say: [跨项目, 移动]
must_not_say: []
bucket: medium
feature: task
# ----- 桶 2: 视图/会议/报告/审批/签到/OKR 10 -----
- id: q13
q: "甘特图视图怎么用?"
locale: zh
expected_chunk_ids: [view.gantt.howto]
expected_answer: "在项目视图切换器选「甘特图」,可拖动任务条调整开始/结束时间。"
must_say: [甘特图]
must_not_say: []
bucket: easy
feature: view
- id: q14
q: "怎么创建一个会议?"
locale: zh
expected_chunk_ids: [meeting.create.howto]
expected_answer: "左上角「+」下拉选「新会议」Ctrl/Cmd+J或应用中心「在线会议」填会议主题、邀请成员后点「开始会议」他人可凭会议频道 ID 加入。"
must_say: [会议, 创建, 新建]
must_not_say: []
bucket: easy
feature: meeting
- id: q15
q: "怎么写一份工作报告?"
locale: zh
expected_chunk_ids: [report.create.howto]
expected_answer: "应用中心打开「工作报告」新建汇报,选「周报 / 日报」,系统按任务数据自动生成「已完成工作 / 未完成的工作」内容模板,编辑后选择汇报对象提交。"
must_say: [报告]
must_not_say: []
bucket: easy
feature: report
- id: q16
q: "审批流可以分支吗?"
locale: zh
expected_chunk_ids: [approve.node.concept, approve.template.howto]
expected_answer: "DooTask 审批支持单人 / 会签(多人同意/否决)/ 抄送等节点P0 版本暂不支持按表单条件路由的「条件分支」,需拆为多个流程模板。"
must_say: [分支, 不支持, 节点]
must_not_say: [完全支持条件分支, 可以任意配置条件路由]
verify_note: "审批为 AppStore 插件docker/appstore/apps/approve流程设计器以 iframe 加载approve/setting.vue:24源码不在主仓库主仓库仅可证实存在「抄送」节点approve/details.vue:117-124会签/条件分支能力无法用代码裁决"
bucket: medium
feature: approve
- id: q17
q: "签到打卡数据怎么导出?"
locale: zh
expected_chunk_ids: [checkin.export.howto, data-export.checkin.howto]
expected_answer: "限管理员:应用中心「数据导出」(或左上角菜单 → 团队管理)选「导出签到数据」,选成员(最多 100 人)、日期范围(最长 35 天)和时间段导出 Excel普通员工没有此权限。"
must_say: [签到, 导出]
must_not_say: []
bucket: easy
feature: checkin
- id: q18
q: "OKR 怎么和上级对齐?"
locale: zh
expected_chunk_ids: [okr.align.howto]
expected_answer: "在 OKR 详情勾选「对齐到」选择上级 O个人 OKR 拆解自团队 / 部门 / 公司 OKR。"
must_say: [OKR, 对齐]
must_not_say: []
verify_note: "OKR 为 AppStore 插件docker/appstore/apps/okr源码不在主仓库「对齐到」具体交互无法用主仓库代码裁决"
bucket: medium
feature: okr
- id: q19
q: "让 AI 帮我写周报"
locale: zh
expected_chunk_ids: [report.ai-generate.howto, ai-assistant.report-draft.howto]
expected_answer: "在工作报告编辑页点「AI 整理汇报」需先填写汇报内容新建时系统已按任务自动生成内容AI 助手会在此基础上整理润色,结果可应用回编辑器继续编辑。"
must_say: [AI, 周报, 报告]
must_not_say: []
bucket: medium
feature: report
- id: q20
q: "WiFi 自动签到怎么用?"
locale: zh
expected_chunk_ids: [checkin.wifi.howto, checkin.mac-not-match.faq]
expected_answer: "前提:管理员开启签到功能并在公司 OpenWrt 路由器执行系统生成的安装脚本(路由器每分钟上报在线设备 MAC。个人在「签到打卡」→ 签到设置 → WiFi 签到中填写自己设备的 MAC 地址,设备连上该 WiFi 即自动打卡;不在该网络时不会自动签到。"
must_say: [WiFi, MAC, 自动]
must_not_say: []
bucket: medium
feature: checkin
- id: q21
q: "我加入会议失败怎么办?"
locale: zh
expected_chunk_ids: [meeting.cannot-join.faq]
expected_answer: "常见原因:会议号已结束 / 无效;分享链接过期;网络受限(声网 Agora 域名被拦截);管理员未配置 Agora。"
must_say: [会议, 失败]
must_not_say: []
bucket: hard
feature: meeting
- id: q22
q: "在群聊里能直接开会议吗?"
locale: zh
expected_chunk_ids: [meeting.from-dialog.howto]
expected_answer: "聊天输入框右侧「展开(+)」工具菜单选「新会议」,会自动把当前会话成员(不含机器人)填入邀请名单;会话列表右键联系人也有「发起会议」。"
must_say: [会议, 对话, 发起]
must_not_say: []
bucket: medium
feature: meeting
# ----- 桶 3: 应用中心 6 -----
- id: q23
q: "微应用是什么?"
locale: zh
expected_chunk_ids: [micro-app.concept]
expected_answer: "微应用是 DooTask 中以独立卡片/页面形式嵌入的插件式应用,从「应用商店」安装,登录态(用户 token自动继承。"
must_say: [微应用, 插件]
must_not_say: []
bucket: easy
feature: micro-app
- id: q24
q: "怎么装一个新的应用?"
locale: zh
expected_chunk_ids: [micro-app.install.howto]
expected_answer: "管理员在应用页的「应用商店」(仅管理员可见)找到目标应用点击安装,安装后应用出现在所有用户的应用面板。"
must_say: [安装, 应用]
must_not_say: []
bucket: easy
feature: micro-app
- id: q25
q: "系统应用和微应用有什么区别?"
locale: zh
expected_chunk_ids: [app-system.relation.concept]
expected_answer: "系统应用是内置卡片(工作报告/签到打卡/在线会议/我的收藏等无需安装微应用来自应用商店安装如审批中心、OKR、AI 助手管理员应用LDAP/邮件通知/APP 推送/数据导出/团队管理等)仅管理员可见。"
must_say: [系统应用, 微应用, 区别]
must_not_say: []
bucket: medium
feature: app-system
- id: q26
q: "我的收藏应用入口在哪?"
locale: zh
expected_chunk_ids: [app-system.favorite.howto, menu-navigation.favorite.menu-map]
expected_answer: "应用中心「我的收藏」卡片,或左上角主菜单「我的收藏」;里面是你收藏的任务、项目、文件、消息。"
must_say: [收藏]
must_not_say: []
bucket: easy
feature: app-system
- id: q27
q: "应用顺序能调整吗?"
locale: zh
expected_chunk_ids: [micro-app.sort.howto]
expected_answer: "应用页右上角「···」菜单选「调整排序」进入排序模式,拖动卡片调整顺序后点「保存」;排序仅自己可见,可「恢复默认」。"
must_say: [应用, 排序, 拖]
must_not_say: []
bucket: easy
feature: micro-app
- id: q28
q: "为什么我的 DooTask 看不到 OKR"
locale: zh
expected_chunk_ids: [okr.cannot-install.faq]
expected_answer: "OKR 是应用商店插件,需管理员先在「应用商店」安装后才会显示;安装失败可查看应用商店安装日志、检查网络能否拉取镜像。"
must_say: [OKR, 安装, 应用市场]
must_not_say: []
bucket: medium
feature: okr
# ----- 桶 4: 用户/组织/系统管理 6 -----
- id: q29
q: "注销账号以后还能恢复吗?"
locale: zh
expected_chunk_ids: [user-account.delete-restore.faq]
expected_answer: "注销(删除帐号)不可恢复,系统仅保留昵称等基础信息用于历史消息展示;管理员能恢复的是「已离职」状态的帐号(且离职时移交的数据无法恢复),与注销不同。"
must_say: [注销, 不可恢复, 无法恢复]
must_not_say: []
bucket: medium
feature: user-account
- id: q30
q: "AI 模型在哪里配置?"
locale: zh
expected_chunk_ids: [menu-navigation.ai-config.menu-map, system-setting.ai-model.howto]
expected_answer: "管理员打开应用中的「AI 助手」,在助手设置面板(仅管理员可见)配置各模型提供商的 API Key / Base URL / 模型列表与默认模型,可一键获取默认模型列表。"
must_say: [AI, 模型, 配置]
must_not_say: []
bucket: medium
feature: system-setting
- id: q31
q: "操作时提示「权限不足」,是哪里没设?"
locale: zh
expected_chunk_ids: [role-permission.permission-denied.faq]
expected_answer: "通常因为操作超出当前角色权限。可能:非项目负责人无法修改项目设置;非管理员无法访问系统设置;任务可见用户名单未包含你。建议联系管理员或项目负责人调整。"
must_say: [权限, 角色]
must_not_say: []
bucket: medium
feature: role-permission
- id: q32
q: "怎么把超级管理员转给别人?"
locale: zh
expected_chunk_ids: [role-permission.transfer-owner.howto]
expected_answer: "DooTask 没有单独的「转让超级管理员」功能,采用多管理员模型:管理员在「团队管理」成员操作菜单中对目标成员「设为管理员」,如需移交可再取消自己的管理员身份。"
must_say: [管理员, 团队管理]
must_not_say: []
bucket: medium
feature: role-permission
- id: q33
q: "LDAP 用户同步不到 DooTask 怎么办?"
locale: zh
expected_chunk_ids: [ldap.troubleshoot.faq, ldap.sync.howto]
expected_answer: "检查 LDAP 配置Host / Port / Base DN / 绑定 DN 及密码)是否正确,可用「测试连接」验证;确认网络/防火墙放通;注意 LDAP 用户是在登录 DooTask 时才同步创建,不是批量预同步。"
must_say: [LDAP]
must_not_say: []
bucket: hard
feature: ldap
- id: q34
q: "管理员怎么一次性批量导入员工?"
locale: zh
expected_chunk_ids: [user-account.import.howto]
expected_answer: "管理员在「团队管理」点「批量导入」,上传 xls/xlsx/csv 文件(列顺序:邮箱、昵称、初始密码、职位(选填)),先预览校验再确认导入;可下载官方模板。"
must_say: [批量, 导入, 用户, Excel]
must_not_say: []
bucket: hard
feature: user-account
# ----- 桶 5: AI / 搜索 / 终端 4 -----
- id: q35
q: "AI 助手怎么换一个模型?"
locale: zh
expected_chunk_ids: [ai-assistant.model-switch.howto, ai-assistant.model.concept]
expected_answer: "在 AI 助手浮窗底部输入区的模型下拉切换可选模型由管理员在「AI 助手」应用的设置面板配置。"
must_say: [AI, 模型, 切换]
must_not_say: []
bucket: medium
feature: ai-assistant
- id: q36
q: "智能搜索是什么?"
locale: zh
expected_chunk_ids: [search.intelligent.concept]
expected_answer: "智能搜索基于 Manticore Search 插件,支持关键词 / 语义(向量)/ 混合搜索,可搜任务、项目、文件内容、消息、联系人;未安装插件时回退普通 MySQL 关键词搜索。"
must_say: [智能搜索, 语义]
must_not_say: []
bucket: medium
feature: search
- id: q37
q: "桌面端怎么设置开机自启?"
locale: zh
expected_chunk_ids: [electron-client.auto-start.howto]
expected_answer: "桌面端没有内置「开机自启」开关,需在操作系统的启动项中手动添加(如 Windows 启动文件夹 / 任务计划、macOS 系统设置的登录项)。"
must_say: [开机自启, 桌面端]
must_not_say: []
bucket: medium
feature: electron-client
- id: q38
q: "手机端收不到推送通知"
locale: zh
expected_chunk_ids: [mobile-client.push-fail.faq]
expected_answer: "检查App 通知权限是否开启、系统省电模式是否限制后台、UMENG 推送是否配置完成、网络是否受限。"
must_say: [推送, 移动端, App]
must_not_say: []
bucket: medium
feature: mobile-client
# ----- 桶 6: 快捷键 / 术语 4 -----
- id: q39
q: "任务编辑时有什么快捷键?"
locale: zh
expected_chunk_ids: [shortcut.task.shortcut]
expected_answer: "任务编辑快捷键Ctrl/Cmd + S 保存任务修改任务名输入框回车直接保存Shift+回车换行);详细描述为 TinyMCE 富文本编辑器,支持 Ctrl/Cmd + B 加粗等通用富文本快捷键。"
must_say: [快捷键, 任务]
must_not_say: []
bucket: easy
feature: shortcut
- id: q40
q: "DooTask 全局快捷键有哪些?"
locale: zh
expected_chunk_ids: [shortcut.global.shortcut]
expected_answer: "全局快捷键Ctrl/Cmd 组合B 新建项目、F 或 / 搜索、K 或 N 新建任务、U 创建群组、J 新会议、I 呼出 AI 助手(需安装 AI 插件、S 保存任务、, 打开设置Ctrl/Cmd+Alt+L 下载内容(桌面端)。"
must_say: [快捷键]
must_not_say: []
bucket: easy
feature: shortcut
- id: q41
q: "怎么关掉当前弹窗?"
locale: zh
expected_chunk_ids: [shortcut.dialog.shortcut]
expected_answer: "按 Esc 关闭弹窗或抽屉;某些表单弹窗需要先取消编辑态再 Esc。"
must_say: [Esc, 关闭]
must_not_say: []
bucket: easy
feature: shortcut
- id: q42
q: "手机上有什么手势操作?"
locale: zh
expected_chunk_ids: [shortcut.mobile-gesture.shortcut, mobile-client.gesture.concept]
expected_answer: "移动端主要手势:长按列表项(会话、项目、文件等)弹出操作菜单;列表滚动到底自动加载更多。"
must_say: [手势, 移动, 长按]
must_not_say: [左滑显示任务操作菜单]
verify_note: "代码仅证实长按手势v-longpress 指令manage.vue/messenger.vue/file.vue与滚动加载未找到左滑操作菜单、下拉刷新手势的实现移动端为 WebView 壳加载同一 SPA"
bucket: easy
feature: shortcut
# ----- 桶 7: 跨模块组合 3 -----
- id: q43
q: "任务到期了 DooTask 通过哪些方式通知我?涉及桌面端、手机端、邮件?"
locale: zh
expected_chunk_ids:
- task.notify.concept
- mobile-client.notify.concept
- electron-client.notify.concept
expected_answer: "到期前/超期后约 1 小时task-alert 机器人私聊发送提醒依赖管理员开启「APP 推送」设置):桌面端收到站内消息并弹系统通知(依赖系统通知权限)、移动端走 UMENG 推送;任务到期提醒不发邮件——邮件通知仅覆盖未读聊天消息(文本/文件/语音/会议类),不含此类模板提醒。"
must_say: [桌面, 移动, 推送, 提醒]
must_not_say: []
bucket: hard
feature: task
- id: q44
q: "项目里我改不了任务,提示权限不足,怎么排查?"
locale: zh
expected_chunk_ids:
- project.permission-denied-task.faq
- role-permission.permission-denied.faq
- role-permission.project-role.concept
expected_answer: "首先确认你在该项目里的角色(负责人/管理员/成员)和任务权限点;其次检查该项目自定义权限规则;若仍受限请联系项目负责人或管理员调整。"
must_say: [权限, 项目, 角色]
must_not_say: []
bucket: hard
feature: role-permission
- id: q45
q: "在群聊里讨论时直接派一个任务出去要怎么操作?"
locale: zh
expected_chunk_ids:
- task.create.howto.via-mention
- messenger.task-mention.concept
expected_answer: "右键(移动端长按)群里某条文本消息选「新任务」,消息内容自动填入任务描述,可设项目、负责人、截止时间后添加;输入框输入 # 只能引用/关联已有任务,不能新建。"
must_say: [任务, 对话]
must_not_say: []
bucket: hard
feature: task
# ----- 桶 8: 中英混合提问 2locale=zh测中英查询鲁棒性-----
- id: q46
q: "How to create a quick task in DooTask?"
locale: zh
expected_chunk_ids: [task.create.howto.quick]
expected_answer: "On the Kanban board, click the add area at the bottom of any column, type the task name and press Enter; or use the top-left New Task button (Ctrl/Cmd+K or N)."
must_say: [quick, task, create]
must_not_say: []
bucket: medium
feature: task
- id: q47
q: "checkin export 怎么用?"
locale: zh
expected_chunk_ids: [checkin.export.howto, data-export.checkin.howto]
expected_answer: "Admin opens the Data Export app (or top-left menu → Team Management) → Export Checkin Data, picks members and a date range (max 35 days) and downloads Excel; regular users have no permission."
must_say: [签到, 导出, export]
must_not_say: []
bucket: medium
feature: checkin
# ----- 桶 9: 诚实性 negative 3 -----
- id: q48
q: "DooTask 怎么训练一个自己的大语言模型?"
locale: zh
expected_chunk_ids: []
expected_answer: "拒答类DooTask 不提供训练大语言模型的能力,仅支持配置 / 接入外部 LLM 提供商的 API。必须明确说明帮助文档中没有相关内容。"
must_say: [未找到, 没找到, 不提供, 无相关]
must_not_say: [训练步骤, 训练流程, 训练数据集准备]
bucket: honest-negative
feature: ai-assistant
- id: q49
q: "怎么把全公司员工的工资数据导出来?"
locale: zh
expected_chunk_ids: []
expected_answer: "拒答类DooTask 是项目协作工具,不存储工资数据,没有此导出能力。必须明确说明帮助文档中没有相关内容,建议用户去 HR / 薪资系统查询。"
must_say: [未找到, 没找到, 不存储, 没有此功能]
must_not_say: [工资导出步骤, 工资导出按钮]
bucket: honest-negative
feature: data-export
- id: q50
q: "DooTask 怎么接入 SAP ERP"
locale: zh
expected_chunk_ids: []
expected_answer: "拒答类DooTask 目前没有内置 SAP / ERP 集成能力。必须明确说明帮助文档中没有相关内容,可考虑自建桥接服务或等待 P1 接入。"
must_say: [未找到, 没找到, 不支持, 暂无]
must_not_say: [SAP 集成步骤, ERP 接入步骤]
bucket: honest-negative
feature: ai-assistant
# 运行说明:
# # 仅 recall@5无需 LLM
# docker exec dootask-3bed84-app-ai-ai-1 \
# python -m helper.kb.eval --suite /app/kb-content/_eval/golden-50q.yaml
#
# # 端到端recall + answer + honest需要 LLM-judge见 §下方)
# docker exec dootask-3bed84-app-ai-ai-1 \
# python -m helper.kb.eval --suite /app/kb-content/_eval/golden-50q.yaml \
# --judge claude-3-5-sonnet --judge-base-url <openai-compatible-url> --judge-api-key <key>
#
# DoD§六
# recall@5 ≥ 85% (40/47 题, 3 题为 honest-negative 不参评)
# answer_correct (LLM judge) ≥ 80% (40/50)
# honest_refuse 100% (3/3)
# tool_call_rate ≥ 90%main.py 集成度,需要灰度上线后看真实流量)

View File

@ -0,0 +1,129 @@
# 10 题初版回归测试集S1 联调早期 smoke test 用)
# P0 S5 阶段扩展到 50 题golden-50q.yaml
#
# 字段:
# id 测试题 id
# q 用户提问
# locale zh / en
# expected_chunk_ids 期望被 retriever 召回的 chunk id≥1 条命中即 recall=1
# expected_answer 期望答案要点LLM-judge 用,自然语言描述关键事实)
# must_say 答案必须包含的关键词(正例桶)/ 拒答关键词negative 桶)
# must_not_say 答案禁止出现的内容
# bucket easy / medium / hard / honest-negative
# feature 对应 feature追溯定位
version: 1
last_updated: 2026-06-09
tests:
- id: q1
q: "看板列怎么改名?"
locale: zh
expected_chunk_ids: [view.kanban.column.howto.rename]
expected_answer: "在看板视图,鼠标悬停在列标题上,会出现编辑图标或下拉菜单,点击「重命名」/双击列标题进入编辑态,输入新名字回车保存。"
must_say: [列, 重命名]
must_not_say: []
bucket: easy
feature: view
- id: q2
q: "怎么快速创建一个任务?"
locale: zh
expected_chunk_ids: [task.create.howto.quick]
expected_answer: "项目详情页右上角「+」按钮选「快速添加任务」,输入任务名回车即可创建。"
must_say: [快速, 创建]
must_not_say: []
bucket: easy
feature: task
- id: q3
q: "签到应用怎么导出数据?"
locale: zh
expected_chunk_ids: [app-admin.data-export.howto.checkin, checkin.export.howto]
expected_answer: "管理员视角通过应用中心的「导出管理」选择「导出签到数据」,按日期范围导出 Excel。"
must_say: [导出, 签到]
must_not_say: []
bucket: easy
feature: data-export
- id: q4
q: "我提示「权限不足」是为什么?"
locale: zh
expected_chunk_ids: [role-permission.permission-denied.faq]
expected_answer: "通常因为操作超出当前角色范围。常见原因:非项目负责人无法修改项目设置;非管理员无法访问系统设置;任务可见用户名单未包含当前用户。建议联系管理员或项目负责人调整角色/权限。"
must_say: [权限, 角色]
must_not_say: []
bucket: medium
feature: role-permission
- id: q5
q: "审批流可以分支吗?"
locale: zh
expected_chunk_ids: [approve.process.concept, approve.faq.branching]
expected_answer: "当前 DooTask 审批中心暂不支持条件分支(按条件路由到不同审批人);只支持顺序串行审批与并行抄送。如需分支建议拆成多个审批模板。"
must_say: [分支]
must_not_say: [支持条件路由, 可以配置分支]
bucket: medium
feature: approve
- id: q6
q: "AI 助手怎么换模型?"
locale: zh
expected_chunk_ids: [ai-assistant.model.howto.switch]
expected_answer: "在 AI 助手浮窗顶部的模型下拉选择器切换前提是管理员已在系统设置「AI 模型」中配置了相应模型的 API key/base_url。"
must_say: [模型, 切换]
must_not_say: []
bucket: medium
feature: ai-assistant
- id: q7
q: "任务到截止时间会怎么提醒我?"
locale: zh
expected_chunk_ids: [task.deadline.concept, notification.task-deadline.howto, mobile-notify.task.howto]
expected_answer: "DooTask 在任务接近截止时通过多渠道提醒App 推送(依赖 UMENG 配置)、桌面端 Electron 通知、邮件(依赖管理员开通)、机器人在群里 @你、任务列表的红色标记。可在个人设置关闭部分通道。"
must_say: [提醒]
must_not_say: []
bucket: hard
feature: task
- id: q8
q: "How do I create a quick task in DooTask?"
locale: en
expected_chunk_ids: [task.create.howto.quick]
expected_answer: "On the project detail page top-right click the + button, choose Quick add task, type the task name and press Enter."
must_say: [quick, create]
must_not_say: []
bucket: medium
feature: task
- id: q9
q: "DooTask 怎么训练一个自己的大语言模型?"
locale: zh
expected_chunk_ids: []
expected_answer: "拒答类DooTask 不提供训练大语言模型的能力,仅支持配置 / 接入外部 LLM 提供商的 API。请明确说明帮助文档中没有相关内容。"
must_say: [未找到, 没找到] # 命中任一即可(运行时取并集)
must_not_say: [训练步骤, 训练流程]
bucket: honest-negative
feature: ai-assistant
- id: q10
q: "怎么把所有员工的工资数据导出?"
locale: zh
expected_chunk_ids: []
expected_answer: "拒答类DooTask 是项目协作工具,不存储工资数据,没有此导出能力。要明确说明帮助文档中没有相关内容,建议用户检查薪资 HR 系统。"
must_say: [未找到, 没找到]
must_not_say: [工资导出, 步骤]
bucket: honest-negative
feature: data-export
# 运行说明:
# docker exec ai python -m helper.kb.eval --suite /app/kb-content/_eval/golden-v0.yaml
#
# 评分:
# recall@5 expected_chunk_ids 中任一 ∈ retriever 返回的 top-5 → 1否则 0
# (空集表示这是 honest-negative 题,不参与 recall 评分)
# answer_correct Claude 3.5 Sonnet judge对比 expected_answer0/1
# honest_refuse bucket == honest-negative 时must_say 至少命中 1 → 1
#
# v0 用于 S1 早期 smoke test (10 题); 完整 50 题在 S5 写入 golden-50q.yaml

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,198 @@
# MCP 工具 ↔ chunk 关联
#
# 用途:
# 1. chunk frontmatter 的 related_tools 字段取值受控lint 校验)
# 2. ingest 期把 related_tools 写入 chunk metadataretriever 可联动工具
# 3. 让 AI 在调 search_help_docs 时同时知道"还能调哪个工具直接操作"
#
# 工具清单来自 dootask-plugins/mcp/server/src/dootaskMcpServer.ts33 个 MCP 工具)
# 加 helper/tools.py 中的内置工具GetSessionImageTool 等)
version: 1
last_updated: 2026-06-09
tools:
# ===== 用户 =====
get_users_basic:
description: 批量获取用户基本信息(昵称/邮箱/头像)
related_features: [user-account, org-department]
typical_chunk_types: [concept]
search_users:
description: 按关键词搜索用户,支持按项目/对话范围筛选
related_features: [user-account, search]
typical_chunk_types: [howto]
# ===== 任务 =====
list_tasks:
description: 列表获取用户相关任务,支持状态/项目/时间范围筛选
related_features: [task]
typical_chunk_types: [howto]
get_task:
description: 获取任务完整详情
related_features: [task]
typical_chunk_types: [concept, howto]
create_task:
description: 在指定项目创建新任务
related_features: [task]
typical_chunk_types: [howto]
update_task:
description: 更新任务属性
related_features: [task]
typical_chunk_types: [howto]
complete_task:
description: 标记任务完成
related_features: [task]
typical_chunk_types: [howto]
delete_task:
description: 删除或恢复任务
related_features: [task]
typical_chunk_types: [howto]
create_sub_task:
description: 为父任务添加子任务
related_features: [task]
typical_chunk_types: [howto]
get_task_files:
description: 获取任务附件列表
related_features: [task, file]
typical_chunk_types: [howto]
# ===== 项目 =====
list_projects:
description: 列表获取用户可访问项目
related_features: [project]
typical_chunk_types: [howto]
get_project:
description: 获取项目完整详情
related_features: [project]
typical_chunk_types: [concept, howto]
create_project:
description: 创建新项目
related_features: [project]
typical_chunk_types: [howto]
update_project:
description: 更新项目信息
related_features: [project]
typical_chunk_types: [howto]
# ===== 消息 / 对话 =====
search_dialogs:
description: 搜索群聊或个人对话
related_features: [messenger, search]
typical_chunk_types: [howto]
send_message:
description: 向对话发送消息
related_features: [messenger]
typical_chunk_types: [howto]
send_task_ai_message:
description: 作为 AI 助手向任务对话发送消息
related_features: [messenger, task, ai-assistant]
typical_chunk_types: [howto]
get_message_list:
description: 获取对话消息历史
related_features: [messenger]
typical_chunk_types: [howto]
# ===== 文件 =====
list_files:
description: 列表获取用户文件
related_features: [file]
typical_chunk_types: [howto]
search_files:
description: 搜索文件
related_features: [file, search]
typical_chunk_types: [howto]
get_file_detail:
description: 获取文件详情
related_features: [file]
typical_chunk_types: [howto]
fetch_file_content:
description: 按文件路径获取文本内容
related_features: [file]
typical_chunk_types: [howto]
# ===== 工作报告 =====
list_received_reports:
description: 列表获取收到的工作报告
related_features: [report]
typical_chunk_types: [howto]
get_report_detail:
description: 获取报告详情
related_features: [report]
typical_chunk_types: [howto]
generate_report_template:
description: 基于任务完成情况自动生成报告模板
related_features: [report, ai-assistant]
typical_chunk_types: [howto]
create_report:
description: 创建并提交工作报告
related_features: [report]
typical_chunk_types: [howto]
list_my_reports:
description: 列表获取发送的报告
related_features: [report]
typical_chunk_types: [howto]
mark_reports_read:
description: 批量标记报告为已读/未读
related_features: [report]
typical_chunk_types: [howto]
# ===== 搜索 / 内容提取 =====
intelligent_search:
description: 统一搜索(任务/项目/文件/联系人/消息),支持语义搜索
related_features: [search]
typical_chunk_types: [howto, concept]
extract_image_text:
description: 图片文字提取OCR支持中英文
related_features: [file]
typical_chunk_types: [howto]
# ===== 页面操作 (UI 自动化, WebSocket) =====
get_page_context:
description: 获取当前页面上下文(页面类型/可交互元素/可用操作)
related_features: [ai-assistant]
typical_chunk_types: [concept]
execute_action:
description: 在用户页面执行操作(打开任务/对话/切换项目/页面跳转)
related_features: [ai-assistant]
typical_chunk_types: [howto]
execute_element_action:
description: 操作页面元素(点击/输入/选择/聚焦/滚动/悬停)
related_features: [ai-assistant]
typical_chunk_types: [howto]
# ===== 内置工具AI 插件 helper/tools.py=====
get_session_image:
description: 获取用户上传的会话图片(多模态用)
related_features: [ai-assistant, file]
typical_chunk_types: [howto]
search_help_docs:
description: 本知识库自身的检索工具;当用户询问 DooTask 功能用法/概念/操作步骤时调用
related_features: [ai-assistant]
typical_chunk_types: [howto]

View File

@ -0,0 +1,243 @@
# Chunk 写作风格指南
这里的内容**只给 LLM 读**,不是给人。写作风格和习惯与人类文档完全不同。这份指南强约束所有 chunk 的形态。
## 核心原则(按重要程度排)
1. **每个 chunk 单独可读**。RAG 切块后会丢失上下文。读者只能看到这一段,前后段都不存在。所以禁止「如上所述」「在前一节」「参见 task.md 第二段」这类指代。
2. **关键名词在第一段出现一次**。提升 dense embedding 召回。
3. **显式同义词**。「子任务」和「subtask」、「列」和「看板列」、「待办」和「todo」要在 `aliases` 字段里并列。
4. **显式否定信息**。"X 不支持,因为 Y" 是高价值信息,能让 LLM 拒绝编造。
5. **结构化优于叙述化**。能用编号列表就别写段落;能用表格就别写散文。
6. **不依赖截图**。所有信息用文字描述,必要时描述按钮位置("页面右上角")。
## 字数指引
- 正文(不含 frontmatter**200-1500 字符**lint 强制)
- 黄金区间 **250-700 字符**(最贴近切块器目标 512 token
- 短于 200 字符的内容合并到相关 chunk长于 1500 字符的必须拆成多篇用 `[[id]]` 互链
## 标题结构
只用 `H1``H2`。**不要用 H3**。
```markdown
# {title} ← 与 frontmatter title 一致H1 只能一个
## 入口 / 是什么 / 操作步骤 ← H2按 type 选择模板(见下)
...
## 字段说明 / 不支持项 / 相关
...
```
切块器按标题切;层级浅检索更稳。
## 各 `type` 推荐小节模板
### `concept`(是什么)
```
## 定义
(一句话)
## 关键属性
- 列表化字段
## 与其他概念的关系
- 父子 / 包含 / 依赖
```
### `howto`(怎么做)
```
## 入口
- 桌面端 / 移动端的具体路径
## 操作步骤
1. 编号步骤
## 字段默认值
(可选,表格更清晰)
## 不支持
- negative 中已列的项也在正文重复一次
```
### `faq`(为什么 / 出错怎么办)
```
## 问题
(一句话描述现象)
## 原因
(短解释)
## 解决
1. 步骤
```
### `menu-map`(在哪里)
```
## 路径
桌面端:左侧栏 → 应用 → 审批
移动端:底部 Tabbar → 应用 → 审批
快捷键:无
## 权限要求
- end-user 可见
```
### `glossary`(术语表)
通常一个 feature 一篇,结构化列表:
```
## task任务
**别名**todo, 待办, 卡片
**定义**:一个待完成的工作项,归属于某个项目。
## subtask子任务
...
```
### `shortcut`(快捷键)
表格优先:
```
| 操作 | 桌面端 | 移动端 |
|---|---|---|
| AI 助手 | Cmd+I | (无) |
```
## 同义词处理
`aliases` 字段里列 ≥ 1 条用户**可能怎么提问**的口语化说法。不是堆关键词,是模拟真实提问:
✅ 好的:
```yaml
aliases:
- 怎么建任务
- 新建待办
- 加一条 todo
- 我要建任务
```
❌ 不好的(关键词堆砌):
```yaml
aliases:
- 任务
- 创建
- 添加
- 新建
- 任务管理
```
## 否定信息negative的写法
业界实测:在 RAG chunk 里**显式写"不支持 X 因为 Y"**,能显著降低 LLM 编造率。
```yaml
negative:
- 快速创建不支持设置截止时间,需用完整创建
- 审批中心不支持嵌套子审批
- 任务删除后 30 天内可恢复30 天后无法找回
```
正文也要重复一次(让 chunk 自包含):
```markdown
## 不支持
- 快速创建不支持设置截止时间,需用完整创建
```
## 跨引用 `[[id]]` 语法
引用其他 chunk 用 `[[id]]`lint 会校验目标存在:
```markdown
想设置更多字段,切到完整创建模式:[[task.create.howto.full]]
任务的可见性规则见:[[task.visibility.concept]]
```
这种引用比"参见 task.md 第二段"鲁棒得多。
## 正例 vs 反例
### 反例 1依赖截图
```markdown
点击下图红框中的按钮即可创建任务。
截图xxx.png
```
问题LLM 看不到截图。
改:
```markdown
点击项目详情页右上角的「+」按钮,选「快速添加任务」。
```
### 反例 2跨章节指代
```markdown
如上一节所述,权限分三级。这里只讨论第二级。
```
问题:切块后看不到「上一节」。
改:
```markdown
项目角色有三级:成员、负责人、管理员。本文只讲负责人。
(或用 [[project.role.concept]] 指向定义文)
```
### 反例 3故事化叙述
```markdown
小王是公司的项目经理,他每天早上都要看一下今天的任务。他打开 DooTask
点击仪表盘,看到今日待办...
```
问题:信息密度低。
改:
```markdown
## 入口
左侧栏「仪表盘」→「今日待办」卡片
## 显示内容
- 今天截止的任务
- 今日新分配的任务
- 已超期的任务(带红色标记)
```
### 反例 4信息不完整 / 缺否定信息
```markdown
# 任务标签
任务可以打标签便于分类。
```
问题:信息量太少,且没说限制。
改:
```markdown
# 任务标签
## 是什么
任务可以打多个标签,用于分类和筛选。标签按项目隔离,不跨项目共享。
## 操作
- 添加:任务详情页 →「标签」字段 → 选择或新建
- 删除:标签上的 × 按钮
- 项目内管理:项目设置 →「标签管理」
## 不支持
- 标签不跨项目共享(每个项目独立维护)
- 单个任务最多 10 个标签
- 标签名 ≤ 20 字符
```
## 写完检查清单
提交 PR 前,每条 chunk 自检:
- [ ] 不读其他 chunk 也能懂这一篇
- [ ] 关键名词在第一段出现
- [ ] `aliases` 至少 1 条且像真实提问
- [ ] 有 `## 不支持` 小节(或在 frontmatter `negative` 写明"无已知限制"
- [ ] 没有截图、没有跨章节指代
- [ ] 正文长度在 200-1500 字符
- [ ] `last_verified` 是当前主程序版本号
- [ ] 跨引用 `[[id]]` 目标存在

View File

@ -0,0 +1,129 @@
# Frontmatter 规范
每个 chunk 文件必须以 YAML frontmatter 开头,被 `---` 包围,紧接正文。
## 完整字段表
```yaml
---
id: app.approve # 必填全局唯一kebab.dot 格式
title: 审批中心 # 必填,单行
type: howto # 必填枚举concept | howto | faq | menu-map | glossary | shortcut
feature: approve # 必填,受控词表(见下)
scope: end-user # 必填枚举end-user | admin | super-admin
locale: zh # 必填枚举zh | en
aliases: # 必填≥1 条;用户可能怎么问
- 审批
- 走流程
- 报销审批
related_tools: [] # 可空;关联的 MCP 工具名(来自 dootask-mcp
related_pages: [application] # 可空;与页面弱提示词联动的 page id
prerequisites: # 可空;前置条件
- 应用市场已安装 approve 插件
negative: [] # 可空但鼓励 ≥1 条;显式列出不支持项
last_verified: v1.7.90 # 必填,当前主程序版本号
---
```
## 字段详解
| 字段 | 类型 | 必填 | 约束 |
|---|---|---|---|
| `id` | string | 是 | 全局唯一;小写 + `.` 分隔;与 `_meta/feature-map.yaml` 中条目对应;推荐格式 `<feature>.<sub>.<type>``task.create.howto` |
| `title` | string | 是 | 单行,不超过 60 字符;用作 chunk 检索时的展示标题 |
| `type` | enum | 是 | `concept`(是什么)/ `howto`(怎么做)/ `faq`(为什么/出错怎么办)/ `menu-map`(在哪里)/ `glossary`(术语)/ `shortcut`(快捷键) |
| `feature` | enum | 是 | 受控词表见下;新增 feature 需先改 feature-map.yaml |
| `scope` | enum | 是 | `end-user`(普通成员)/ `admin`(管理员)/ `super-admin`(超管) |
| `locale` | enum | 是 | `zh` / `en`;与文件所在子目录一致 |
| `aliases` | list[string] | 是≥1 | 用户可能怎么提问;用于 dense 检索 + 同义词桥接;用自然口语,不要堆关键词 |
| `related_tools` | list[string] | 否 | 关联的 MCP 工具名(来自 `_meta/tool-binding.yaml`),便于检索时联动工具调用 |
| `related_pages` | list[string] | 否 | 当用户在哪个页面提问最相关;与前端 page-context 系统联动 |
| `prerequisites` | list[string] | 否 | 前置条件,如"需要管理员权限"、"插件已安装" |
| `negative` | list[string] | 否(鼓励) | 显式列出不支持的能力,如"暂不支持嵌套子审批" |
| `last_verified` | string | 是 | 当前主程序版本号,如 `v1.7.90`;改了 chunk 必须同步刷新 |
## 受控词表
### `type` 全集
| type | 用途 | 示例 id |
|---|---|---|
| `concept` | 解释「是什么」 | `task.subtask.concept` |
| `howto` | 教「怎么做」 | `task.create.howto` |
| `faq` | 「为什么 / 出错怎么办」 | `auth.permission-denied.faq` |
| `menu-map` | 「X 入口在哪」 | `approve.entry.menu-map` |
| `glossary` | 术语 + 别名表(通常一个 feature 一篇) | `task.glossary` |
| `shortcut` | 快捷键、移动端手势 | `global.shortcut` |
### `scope` 全集
- `end-user` — 所有登录用户都能用
- `admin` — 系统管理员(`userIsAdmin`
- `super-admin` — 超级管理员(只有第一个用户)
### `feature` 全集
`_meta/feature-map.yaml` 的顶层 `features:` 列表定义。**新增 feature 必须先改 feature-map.yaml 再写 chunk**,否则 lint 失败。
### `related_tools` 取值
必须是 `_meta/tool-binding.yaml` 已定义的工具名,否则 lint 失败。
## lint 强制规则
`dootask-plugins/system-plugins/ai/src/helper/kb/lint.py` 强制执行:
1. frontmatter 必填字段非空
2. `id` 全局唯一
3. `id` 与文件路径匹配规则:`zh/<type>/<feature-path>/<id-tail>.md`
4. `feature``_meta/feature-map.yaml` 的受控词表内
5. `type` / `scope` / `locale` 在各自枚举内
6. `aliases` 至少 1 条且每条 ≥ 2 字符
7. `related_tools` 中每条都在 `_meta/tool-binding.yaml` 已声明
8. `last_verified` 匹配 `v\d+\.\d+\.\d+` 模式
9. 正文(去掉 frontmatter 后)长度 200-1500 字符
10. 正文不含跨 chunk 指代词:`如上图``如上所述``如前文``在前面的``参见上节`
11. 引用其他 chunk 用 `[[id]]` 语法id 必须存在于本仓库
12. frontmatter 与正文之间必须有一个空行
## 示例(最简骨架)
```markdown
---
id: task.create.howto.quick
title: 快速创建任务
type: howto
feature: task
scope: end-user
locale: zh
aliases:
- 怎么建任务
- 新建待办
- 加一条 todo
related_tools: [create_task]
related_pages: [project_detail, task_list]
prerequisites: []
negative:
- 快速创建不支持设置截止时间,需用完整创建
last_verified: v1.7.90
---
# 快速创建任务
## 入口
- 桌面端:项目详情页右上角「+」→「快速添加任务」
- 移动端:底部 Tabbar「+」按钮
## 操作步骤
1. 在输入框输入任务名(必填,≤ 200 字)
2. 回车提交
## 字段默认值
- 负责人:当前用户
- 状态:列表第一列(未开始)
- 优先级:普通
- 截止时间:无
## 想设置更多字段
切到完整创建模式:[[task.create.howto.full]]
```

View File

View File

View File

View File

View File

View File

View File

View File

@ -0,0 +1,60 @@
---
id: abuse-report.concept
title: 举报管理是什么
type: concept
feature: abuse-report
scope: admin
locale: zh
aliases:
- 举报管理
- 投诉管理
- 举报后台
- 用户举报
- complaint
related_tools: []
related_pages: [application]
prerequisites:
- 需要系统管理员权限
negative:
- 普通成员只能提交举报,看不到「举报管理」后台
- 举报后不会自动封号或屏蔽,需要管理员手动决定
- 不支持自动 AI 内容审核(仅人工处理)
last_verified: v1.7.90
---
# 举报管理是什么
## 定义
举报管理complaint是 DooTask 内置的违规内容处理后台。任何成员在群聊 / 个人对话遇到违规消息时可提交举报,系统管理员在后台审核并标记「已处理」或删除。模型对应 `App\Models\Complaint`,控制器 `ComplaintController`
## 关键属性
- **举报对象**:对话(`dialog_id`),不能对单条消息单独举报
- **举报人**`userid`,提交后系统机器人会通知前 10 个在线管理员
- **举报类型**:固定 7 种id 10/20/30/40/50/60/70见下
- **状态**0 待处理 / 1 已处理 / 2 已删除
- **附件**:最多 N 张图片(前端 `imgs[]` 数组,含 path
- **原因**:必填文本
## 举报类型受控词表
| id | 含义 |
|---|---|
| 10 | 诈骗诱导转账 |
| 20 | 引流下载其他 APP 付费 |
| 30 | 敲诈勒索 |
| 40 | 照片与本人不一致 |
| 50 | 色情低俗 |
| 60 | 频繁广告骚扰 |
| 70 | 其他问题 |
## 通知机制
举报提交后,后端取 `identity LIKE '%,admin,%'` 且按 `line_at` 倒序的前 10 位管理员,由 `system-msg` 系统机器人 template 消息推送「收到新的举报信息:{原因}」。
## 不支持
- 不支持对单条消息举报(只能对整个 dialog
- 不支持举报者匿名(后端会存 `userid`
- 不支持自定义举报类型
- 不支持自动黑名单 / 封禁
## 相关
- 处理流程:[[abuse-report.handle.howto]]
- 入口:[[abuse-report.entry.menu-map]]

View File

@ -0,0 +1,64 @@
---
id: ai-assistant.auth.concept
title: AI 助手鉴权stream_key
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 鉴权
- AI 授权
- stream_key
- AI token
- AI 流凭证
related_tools: []
related_pages: []
prerequisites:
- 用户已登录
- 当前用户允许聊天
negative:
- stream_key 是一次性凭证,发完一条消息就失效
- 流式接口 /ai/invoke/stream/* 不在主仓库,由 dootask-ai 容器提供
- 普通用户不需要关心 stream_key前端自动获取
last_verified: v1.7.90
---
# AI 助手鉴权stream_key
## 定义
AI 助手发送提问前必须先调用 `POST api/assistant/auth` 生成一次性 `stream_key`,再用它去开 SSE 流(`/ai/invoke/stream/{stream_key}`),既复用登录态又避免把用户 token 暴露给 ai 容器。
## 数据流
1. 用户点发送
2. 前端 `POST api/assistant/auth`,参数:`model_type``model_name``context`JSON`locale`zh/en
3. 后端 `AssistantController::auth` 校验登录 + 聊天权限,写入临时凭证
4. 返回 `{stream_key: "xxx"}`
5. 前端开 SSE`ai/invoke/stream/{stream_key}`
6. ai 容器消费凭证、转发上游模型,推回 `append/replace/done`
## 关键约束
- **一次性**:一个 stream_key 仅能开一次 SSE 流
- **绑定用户**:内嵌 `userid`,越权使用被拒
- **携带 context**:对话历史在 auth 阶段写入ai 容器不反向查 DooTask 库
- **插件校验**`AssistantController` 构造函数先做 `Apps::isInstalledThrow('ai')`;未装直接抛错
## 权限校验
`User::auth()->checkChatInformation()`:未登录 / 被禁止聊天 / 激活异常都会拒绝。
## 相关接口
| 接口 | 用途 |
|---|---|
| `POST api/assistant/auth` | 生成 stream_key |
| `GET api/assistant/models` | 拉可用模型列表 |
| `POST api/assistant/session/list` | 历史会话 |
| `POST api/assistant/session/save` | 保存当前会话 |
| `POST api/assistant/session/delete` | 删除 / 清空 |
## 不支持
- 不支持复用 stream_key
- 不支持匿名调用
- 不支持终端用户直接管理 token
## 相关
- [[ai-assistant.privacy.concept]]
- [[ai-assistant.streaming.concept]]

View File

@ -0,0 +1,61 @@
---
id: ai-assistant.embed-entry.concept
title: 业务嵌入式 AI 入口
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 嵌入业务
- 任务里 AI
- 项目 AI 生成
- 工作报告 AI
- 消息里 AI
- AI 业务入口
related_tools: []
related_pages: [task_detail, project_create, report_edit, dialog]
prerequisites:
- 应用市场已安装 ai 插件
- 管理员已配置至少一个 AI 模型
negative:
- 嵌入式入口与浮窗对话不共用同一会话池(按 sessionKey 隔离)
- 嵌入入口大多有自定义 system 提示词,输出格式可能强约束(如 JSON
- 不是所有页面都有 AI 按钮,只有这里列出的业务场景才内嵌
last_verified: v1.7.90
---
# 业务嵌入式 AI 入口
## 定义
除浮按钮和快捷键外DooTask 在多个业务表单内嵌入了「上下文相关」的 AI 入口。它们与全局浮窗调的是同一个 AI 助手组件,但通过 `sessionKey` + `onBeforeSend` + `onApply` 等参数定制行为,让 AI 直接产出可应用到表单的结构化内容。
## 已知嵌入入口
| 入口 | 触发位置 | sessionKey | 典型用途 |
|---|---|---|---|
| 项目创建 | 新建项目弹窗 AI 按钮 | `project-create` | 生成项目名 + 看板列 |
| 任务创建 | 添加任务弹窗 AI 按钮 | `task-add` | 生成任务标题 / 描述 / 子任务 |
| 工作汇报 | 汇报编辑器 AI 工具 | `report-edit` | 基于近期任务生成草稿 |
| 消息输入 | 群 / 私聊输入栏 AI 图标 | `chat-message` | 帮你写消息草稿 |
| 任务讨论 | 任务详情 @AI | — | 机器人方式触发 |
## 与浮窗的区别
- **会话池隔离**:不同 `sessionKey` 走不同桶;项目创建会话不会和任务创建混在一起
- **上下文重建**:嵌入入口通过 `onBeforeSend` 重写 context不走默认助手 system 提示)
- **应用按钮**:回答下方有「应用此内容」按钮,自动回填原表单
- **加载提示**:可自定义「正在生成项目结构…」之类提示
## 应用此内容
回答完成后弹出 `Button`
1. 点击触发 `onApply` 回调
2. 解析 AI 输出(通常 JSON并填入原表单
3. 应用成功后嵌入弹窗自动关闭
## 不支持
- 嵌入会话不出现在浮窗历史下拉里
- 不能在嵌入入口切回浮窗系统提示词
- 没有 AI 按钮的页面(日历 / 文件 / 个人设置)不能内嵌唤起
## 相关
- [[ai-assistant.entry.howto]]
- [[ai-assistant.session.concept]]

View File

@ -0,0 +1,51 @@
---
id: ai-assistant.float-button.concept
title: AI 助手浮按钮
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 浮窗按钮
- AI 浮窗
- AI 浮窗怎么用
- 屏幕角落的 AI 球
- AI 小球
- 那个小气泡
- 浮动按钮
related_tools: []
related_pages: []
prerequisites:
- 应用市场已安装 ai 插件
- 管理员已在系统设置「AI 模型」中配置至少一个模型
negative:
- 浮按钮位置只保留水平/垂直两个距离,不存绝对坐标,换屏分辨率不会跑出屏外
- 收起状态下点击直接打开 AI 助手,不会再触发拖动
- 拖动 ≥ 5px 或按下 ≥ 200ms 算拖动;小于该阈值才视为点击
last_verified: v1.7.90
---
# AI 助手浮按钮
## 定义
浮按钮是 DooTask 桌面端 / 移动端常驻在屏幕角落的圆形 AI 入口44px 直径,点击呼出 AI 助手浮窗或弹窗。它独立挂在 `<body>` 上,与当前路由解耦,**任何登录后页面都能看到**(登录页除外)。
## 关键属性
- **位置存储**:只保存两个距离(距左 / 距右 + 距上 / 距下)和「贴边收起」状态,写入 IndexedDB key `aiAssistant.floatButtonPosition`
- **默认位置**:桌面端右下角(距右 24px、距底 100px移动端竖屏靠右、距底约 1/4 屏高
- **可拖动**:桌面端鼠标按住拖动;移动端单指触摸拖动
- **贴边收起**:靠近左 / 右屏幕边缘 ≤ 12px 时延迟自动收起为竖条(桌面端 1 秒、移动端 5 秒)
- **收起态再点开**:收起状态下点按钮直接打开 AI 助手,不会再拖动
## 显示条件
- 已安装 ai 插件(`microAppsIds` 包含 `ai`
- 已登录(`userId > 0`
- 当前不在登录页
- AI 助手弹窗未打开时
## 与浮窗的关系
浮按钮只是「入口」,点击触发 `openAIAssistantGlobal` 事件。真正的对话窗口由 [[ai-assistant.modal.concept]] 渲染。
## 不支持
- 不支持双击 / 长按弹出菜单(除了移动端长按用于拖动)
- 不支持把浮按钮完全隐藏后再用快捷键恢复——隐藏需在「个人设置」关闭(详见 [[ai-assistant.float-button.howto]]

View File

@ -0,0 +1,56 @@
---
id: ai-assistant.match-elements.concept
title: AI 怎么找到页面元素
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- match_elements
- AI 找按钮
- 元素匹配
- 向量匹配元素
- AI 怎么知道点哪
- AI 元素识别
related_tools: []
related_pages: []
prerequisites:
- 应用市场已安装 ai 插件
negative:
- 匹配纯依赖元素可见文本/aria-label/title无文本的图标按钮命中率低
- 单次匹配上限 50 个候选元素
- 不持久化匹配结果,每轮交互需重新取上下文
last_verified: v1.7.90
---
# AI 怎么找到页面元素
## 定义
当 AI 想操作页面元素(点按钮、填表)时,先调 `get_page_context` 拿到候选元素列表(每条含 ref / name / role再通过后端 API `POST api/assistant/match-elements` 把"用户意图描述"和"候选列表"提交给 embedding 服务,返回按余弦相似度排序的命中元素。
## 工作流
1. **采集**:前端按 ARIA 角色扫描,给每个可交互元素分配 refe1, e2...)和 name
2. **关键词过滤**:先用 query 做子串匹配
3. **向量匹配**:关键词没命中时对 query 和元素 name 求 embedding取相似度 top-K默认 10最多 50
4. **执行**:模型拿匹配元素的 ref 调 `execute_element_action`
## 元素信息字段
- `ref`本轮唯一标识e1, e2...
- `name`:可见文本 / aria-label / title
- `role`ARIA 角色button / textbox / link 等)
- `selector` + `nth`:兜底选择器
## 命中率
- 有清晰文本的按钮:高
- 仅 icon 无 aria-label建议加 title
- 隐藏元素:默认不采集
## 不支持
- 不支持图像 OCR 识图(仅基于文本)
- 不支持「按位置」找元素("左上角第三个"
- 不能跨 iframe 匹配
## 相关
- 取页面上下文:[[ai-assistant.page-context-tool.concept]]
- 操作元素:[[ai-assistant.element-action.howto]]
- 页面操作机制:[[ai-assistant.page-action.concept]]

View File

@ -0,0 +1,63 @@
---
id: ai-assistant.mobile-entry.concept
title: 移动端 AI 助手入口
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- 手机上怎么用 AI
- 移动端 AI 入口
- 手机 AI 助手
- 移动端 AI 浮球
- 手机 AI 弹窗
related_tools: []
related_pages: []
prerequisites:
- 应用市场已安装 ai 插件
- 管理员已配置至少一个 AI 模型
negative:
- 移动端没有键盘快捷键
- 浮按钮位置不在桌面 / 移动间同步
- AI 弹窗在移动端走全屏,不能调整大小
last_verified: v1.7.90
---
# 移动端 AI 助手入口
## 定义
移动端指通过 iOS / Android App 或手机浏览器访问 DooTask`isMobile = true`含竖横屏窄屏。AI 助手在移动端有两类入口:**浮按钮小球** 和 **业务页面内嵌按钮**
## 浮按钮(主入口)
- 屏幕右侧默认距底约 1/4 屏高悬浮
- 加载后自动收起为屏幕边缘 48px 高竖条
- 单指按住可拖动到任何位置;松开后靠近左右边缘会再次自动收起
- 单击展开 AI 助手;展开后**全屏**弹窗,覆盖整个视口
## 全屏对话
- 移动端 AI 弹窗强制全屏(`is-mobile-fullscreen`
- 顶部有标题栏 + 关闭按钮 + 历史会话下拉
- 不能调整大小、不能拖动窗口
## 业务嵌入入口
- 任务详情底部输入区 AI 图标
- 工作汇报编辑器工具栏「AI 生成」
- 消息会话群里 @AI
- 创建项目名右侧 AI 按钮
## 与桌面端的差异
| 维度 | 桌面端 | 移动端 |
|---|---|---|
| 弹窗形态 | 浮窗或 modal | 全屏 modal |
| 浮按钮自动收起延迟 | 1 秒 | 5 秒(加载即收起) |
| 快捷键 | Cmd/Ctrl + I | 无 |
| 默认浮按钮位置 | 右下距底 100px | 右侧距底 1/4 屏高 |
## 不支持
- 不支持移动端快捷收起 / 展开浮按钮(必须靠拖动)
- 不支持移动端关闭浮按钮本身
- 不支持移动端浮窗与桌面浮窗位置同步
## 相关
- [[ai-assistant.float-button.concept]]
- [[ai-assistant.modal.concept]]

View File

@ -0,0 +1,68 @@
---
id: ai-assistant.modal.concept
title: AI 助手弹窗形态
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 浮窗
- AI 弹窗
- AI 全屏
- AI 窗口
- AI 模态框
related_tools: []
related_pages: []
prerequisites: []
negative:
- 移动端浮窗一律全屏,不能调整大小
- 桌面端浮窗最小 380×400最大 800×900
- 弹窗 z-index 每 5/20 秒自动调高确保覆盖其他模态
last_verified: v1.7.90
---
# AI 助手弹窗形态
## 定义
AI 助手有两种渲染形态,由打开时的 `displayMode` 决定:
- `chat`**浮窗**(默认,可拖动 / 调整大小 / 桌面右下角)
- `modal`**居中模态**(默认 600px 宽,移动端自动全屏)
## chat 浮窗(桌面端默认)
- 通过 `v-transfer-dom` 挂到 `<body>`,避免被父容器裁切
- 自带 8 个 resize 控制点(四边 + 四角),可拖拽改大小
- 标题栏可拖动整窗,双击切换全屏
- 位置 / 尺寸记忆到 IndexedDB`aiAssistant.chatPosition` / `aiAssistant.chatSize`
## 尺寸约束chat 浮窗)
| 维度 | 最小 | 最大 | 默认 |
|---|---|---|---|
| 宽度 | 380 | 800 | 460 |
| 高度 | 400 | 900 | 600 |
## modal 模态(业务嵌入入口默认)
- iView `Modal` 组件渲染,居中遮罩
- 宽度:新建会话 440已有对话 600
- 桌面端遮罩点击**不**关闭(`mask-closable: false`
- 移动端强制全屏
## 移动端
- 不论 displayMode 是什么都全屏
- 顶部有标题栏、关闭、历史下拉
- 关闭按钮 `Icon type="ios-close"` 位于右上
- 输入区固定在底部
## z-index 自适应
- 初始 `topZIndex = max(modalTransferIndex, 1000) + 1000`
- 弹窗打开期间每 5 秒重新评估(高于新模态)
- 关闭后每 20 秒评估
## 不支持
- 不支持最小化为小球(要小球用浮按钮)
- 不支持多窗口同时打开多个对话
- 不支持自定义 z-index
## 相关
- [[ai-assistant.float-button.concept]]
- [[ai-assistant.close.howto]]
- [[ai-assistant.mobile-entry.concept]]

View File

@ -0,0 +1,68 @@
---
id: ai-assistant.model.concept
title: AI 助手可用模型
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 模型
- 可以选哪些 AI
- DooTask AI 模型
- GPT Claude DeepSeek
- AI 服务商
related_tools: []
related_pages: []
prerequisites:
- 应用市场已安装 ai 插件
- 管理员已在系统设置「AI 模型」中配置至少一个模型的 API Key
negative:
- 终端用户不能自己加模型 / 改 API Key必须由管理员配置
- 没有「免费内置模型」DooTask 不自带 API Key
- 模型下拉为空 = 管理员未配 / 未启用任何模型
last_verified: v1.7.90
---
# AI 助手可用模型
## 定义
AI 助手在浮窗底部下拉框展示的「可选模型」,全部来自管理员在系统设置 →「AI 模型」中配置并启用的服务商。前端通过 `GET api/assistant/models` 拉当前生效配置,按服务商分组渲染。
## 支持的服务商分组
`AIBotMap` 顺序展示(每组最多前 5 个模型 + 默认模型):
| key | 显示名 |
|---|---|
| `openai` | ChatGPT |
| `claude` | Claude |
| `deepseek` | DeepSeek |
| `gemini` | Gemini |
| `grok` | Grok |
| `ollama` | Ollama本地 |
| `zhipu` | 智谱清言 |
| `qianwen` | 通义千问 |
| `wenxin` | 文心一言 |
## 模型 ID
前端模型选项内部 id 是 `{type}:{value}`,如 `openai:gpt-4o``type` 路由到对应服务商 SDK`value` 是模型名(透传上游)。
## 默认模型
每个分组带 `defaultModel`(管理员设置)。首次打开按此顺序选定:
1. 用户上次选过的模型IndexedDB `aiAssistant.model`
2. 第一个有 `defaultModel` 的分组的默认模型
3. 第一个分组的第一个模型
4. 否则空
## 模型能力差异
- **多模态**:仅部分模型支持,如 `gpt-4o``claude-3-5-sonnet``gemini-1.5-pro`
- **长文本**:默认 context 取最近 10 轮,超出截断;与模型最大 token 无关
## 不支持
- 不支持同一问题同时发多个模型对比
- 不支持终端用户自定义模型 / 改 API Key / base_url
- 切换模型不会清空历史会话,会用新模型续接
## 相关
- [[ai-assistant.model-switch.howto]]
- [[ai-assistant.model-empty.faq]]
- [[ai-assistant.multimodal.concept]]

View File

@ -0,0 +1,59 @@
---
id: ai-assistant.multimodal.concept
title: AI 助手多模态(图片输入)
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 看图
- 上传图片给 AI
- 视觉模型
- AI 多模态
- AI 识图
- AI 看截图
related_tools: []
related_pages: []
prerequisites:
- 当前模型支持视觉输入(如 gpt-4o / claude-3.5-sonnet / gemini-1.5-pro
negative:
- 仅图片,不支持视频 / 音频 / 文档
- 一次最多 5 张图片
- 图片会被压缩到长边 1568px JPEG原图不保留
- 切到不支持视觉的模型后发图会上游报错
last_verified: v1.7.90
---
# AI 助手多模态(图片输入)
## 定义
多模态指 AI 助手允许用户在对话中**同时附带图片**。图片以 base64 拼到 `content` 数组(`{type: 'image_url', image_url: {url: dataUrl}}`),与文本一起发给视觉模型解析。
## 三种添加方式
1. **点击图片按钮**:浮窗底部图片图标,弹系统文件选择器
2. **拖放**:把图片拖到浮窗,松手上传
3. **粘贴**:在输入框 Ctrl/Cmd + V 粘贴剪贴板里的图片
## 数量与大小约束
- 单次最多 5 张(`maxImages`),超出 toast「最多上传 5 张图片」
- 自动压缩:长边 ≤ 1568px、统一转 JPEG
- 原文件不上传,只上传压缩后的 dataUrl
## 视觉模型要求
- 必须选视觉模型才能正确解析(`openai:gpt-4o``claude:claude-3-5-sonnet``gemini:gemini-1.5-pro` 等)
- 非视觉模型(如 `deepseek:deepseek-chat``grok:grok-2`)通常忽略图片或上游报错
## 持久化
- 图片随会话一起保存
- 服务端转存到 `public/uploads/assistant/YYYYMM/{userid}/xxx.jpg`
- 再次打开会话通过 `serverImageMap` 拿回 URL 显示
## 不支持
- 不支持视频 / PDF / 音频;只接受 `image/*` MIME
- 不支持单张超大图保持原始分辨率
- 不支持拖到收起的浮按钮上(先展开)
- 不支持「不压缩直发」开关
## 相关
- [[ai-assistant.image-upload.howto]]
- [[ai-assistant.model-switch.howto]]

View File

@ -0,0 +1,53 @@
---
id: ai-assistant.page-action.concept
title: AI 操作页面的机制
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 控制页面
- AI 自动操作
- execute_action 是什么
- AI 跳转页面
- 页面自动化
- AI 点按钮
related_tools: []
related_pages: []
prerequisites:
- 应用市场已安装 ai 插件
- 应用市场已安装 mcp_server 插件
negative:
- AI 的页面操作仅在浏览器/桌面端会话窗口内生效,无法控制其他用户的页面
- 一次只能操作当前会话所在的页面,不能开新标签页
- 关闭浏览器或切到别的标签页时,页面操作会断连失败
last_verified: v1.7.90
---
# AI 操作页面的机制
## 定义
AI 助手通过 `execute_action`(高层导航)和 `execute_element_action`(低层元素操作)两个 MCP 工具操作用户当前页面。后端通过 WebSocket 把指令推给前端,前端的 `action-executor.js` 执行真实 DOM 行为或路由跳转,结果回传给 AI 让对话继续。
## 两层接口
- **高层 execute_action**:语义化命名(如 `open_task``navigate_to_dashboard`),参数明确(任务 ID由前端封装好 router 调用;优先用这层,稳定不易错
- **低层 execute_element_action**:基于元素 ref 的通用动作click/type/select/focus/scroll/hover用于没封装好的细节操作
## 受支持的高层动作
- `open_task``open_dialog``open_project``open_file``open_folder`
- `navigate_to_dashboard / messenger / calendar / files`
## 受支持的低层元素动作
- `click``type``select``focus``scroll``hover`
## 不支持
- 不能模拟键盘组合键、不能拖拽
- 不能操作 iframe 内的内容
- 不能跳转外部 URLgoForward 只走应用内路由)
- 不能伪造非用户主动触发的事件(如自动提交表单审批通过)
## 相关
- 让 AI 跳页面:[[ai-assistant.page-action.howto]]
- AI 操作元素:[[ai-assistant.element-action.howto]]
- 元素查找接口:[[ai-assistant.match-elements.concept]]
- 取页面上下文:[[ai-assistant.page-context-tool.concept]]

View File

@ -0,0 +1,56 @@
---
id: ai-assistant.page-context-tool.concept
title: AI 怎么知道你在哪个页面
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- get_page_context
- AI 看页面
- 页面上下文
- AI 怎么知道当前页
- AI 读取页面
- AI 上下文采集
related_tools: [get_page_context]
related_pages: []
prerequisites:
- 应用市场已安装 ai 插件
- 应用市场已安装 mcp_server 插件
negative:
- 仅当用户在 AI 浮窗当前会话所在的浏览器/桌面端窗口时才能采集
- 不能跨标签 / 跨设备同步采集,每个 socket 只对应一个页面
- 不读取密码框/被遮挡元素/隐藏元素
last_verified: v1.7.90
---
# AI 怎么知道你在哪个页面
## 定义
AI 通过 MCP 工具 `get_page_context` 向当前用户的浏览器请求页面上下文,包括:当前路由名(如 `manage-project`、URL、标题、可交互元素清单带 ref / name / role、该页可用的高层动作。结果由前端 `page-context-collector.js` 实时收集后回传。
## 返回字段
- `page_type`:路由名(如 `manage-task`
- `page_url` / `page_title`
- `elements`可交互元素ref / name / role
- `total_count` / `has_more` / `offset`:分页
- `available_actions`:该页可用的高层动作(如项目页可 `open_task`
- `ref_map`ref → 定位信息
## 调用模式
- **轻量**`interactive_only=true` + `max_elements=20`
- **完整**:默认前 100 个(含内容)
- **搜索**:传 `query` 先关键词后向量匹配
## 隐式触发
用户在浮窗里问"这个页面有什么操作"、"帮我点这页的某按钮"、"切到下一项目"时AI 都会先调 `get_page_context` 再决定下一步。
## 不支持
- 不返回每个元素的位置坐标(仅 selector
- 不会返回 input 框的当前值(不读取用户私有输入)
- 不能在弹窗/抽屉之外拿到全屏快照(仅 DOM 节点)
## 相关
- 元素匹配:[[ai-assistant.match-elements.concept]]
- 操作元素:[[ai-assistant.element-action.howto]]
- 页面操作机制:[[ai-assistant.page-action.concept]]

View File

@ -0,0 +1,78 @@
---
id: ai-assistant.page-context.concept
title: AI 助手页面上下文(弱提示词)
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 知道当前页面
- 页面感知
- AI 上下文
- 弱提示词
- 当前页面注入
- AI 现在在哪
related_tools: []
related_pages: []
prerequisites: []
negative:
- 弱提示词只传「页面类型 / 实体 id / 名称 / 对话类型」四类,不传业务详细数据
- 详细数据需要 AI 通过 MCP 工具自取
- 嵌入入口(专用 onBeforeSend不走弱提示词机制
last_verified: v1.7.90
---
# AI 助手页面上下文(弱提示词)
## 定义
**弱提示词**是 AI 助手浮窗对话时自动注入的极简「我现在在哪」描述,让 AI 知道用户提问发生在哪个项目 / 任务 / 对话页,能正确解析「这个项目 / 这条任务」之类指代。
## 注入格式
渲染为一行 `system` 消息穿插到 context 历史:
```
[当前页面] 项目详情页(project_id=123,名称:DooTask)
[页面切换] 任务详情页(task_id=4567,名称:修复 SSE 断流)
```
前缀:`[当前页面]` 首次注入,`[页面切换]` 后续在不同页面再提问时。
## 注入的字段
仅四类,绝不带其他业务数据:
| 字段 | 含义 |
|---|---|
| `type` | 实体类型task / dialog / project / file / report |
| `id` | 实体 id |
| `name` | 实体名称 |
| `dialogType` | 群聊 / 私聊,仅 dialog 有 |
## 为什么穿插历史
用户可能在不同项目 / 任务页连续提问。只放当前页会让历史里两个「这个项目」失去锚点。穿插能给历史每条用户消息打上「问题发生时所在的页面」AI 才能正确分辨。
## 可识别的页面
- 任务详情弹窗(最高优先)→ `task:{id}`
- 对话详情弹窗 → `dialog:{id}`
- 仪表盘 → `dashboard`
- 项目列表 / 详情 → `project-list` / `project:{id}`
- 消息列表 / 会话 → `messenger` / `dialog:{id}`
- 日历 → `calendar`
- 文件列表 / 详情 → `file-list` / `file:{id}`
- 工作汇报编辑 / 详情 → `report:{id}`
## 何时不注入
- 不可识别路由(登录页、个人设置等)
- 同 `contextKey` 连续提问
- 嵌入入口(带 `onBeforeSend`
## 详细数据怎么拿
弱提示词只告「在哪」不告「内容」。AI 需要看任务描述、项目统计时通过 MCP 工具自取(如 `get_task``get_project_data`)。
## 不支持
- 不支持把整页内容塞给 AI
- 不支持用户手动关闭弱提示词
- 不支持自定义注入字段
## 相关
- [[ai-assistant.embed-entry.concept]]
- [[ai-assistant.welcome-prompts.concept]]

View File

@ -0,0 +1,80 @@
---
id: ai-assistant.privacy.concept
title: AI 助手数据隐私
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 数据安全
- AI 对话保存在哪
- AI 会泄露吗
- AI 上传图片去哪
- DooTask AI 隐私
related_tools: []
related_pages: []
prerequisites: []
negative:
- 对话会通过 ai 插件转发给管理员配置的上游 LLM 服务商,受其隐私条款约束
- 不存在「AI 完全本地化」开关,除非管理员只配 Ollama 本地模型
- 用户无法自助导出会话(只能在浮窗手动清空历史)
last_verified: v1.7.90
---
# AI 助手数据隐私
## 数据存到哪里
### 对话内容
- 数据库表:`ai_assistant_sessions`,按 `userid` 行级隔离
- 其他普通用户绝对看不到别人会话
- 仅当前用户、系统管理员可通过后台数据库访问
### 图片
- 路径:`public/uploads/assistant/YYYYMM/{userid}/xxx.jpg`
- 按用户 id 分目录,删除会话时物理删除
### 配置 / 模型选择
- 存 IndexedDB `aiAssistant.model`(仅当前浏览器)
- 浮按钮位置 / 输入历史也存 IndexedDB不上传
## 数据传给谁
```
浏览器
→ DooTask 后端 (POST api/assistant/auth)
→ ai 插件容器 (dootask-ai)
→ 上游 LLM 服务商 (OpenAI / Claude / DeepSeek 等)
```
- 上游服务商由**系统管理员**决定
- 选 Ollama 本地模型则对话不出 DooTask 部署网络
- 选公网服务则按服务商隐私条款处理
## 发送上去的内容
- 本次提问的文本 + 图片(多模态时)
- 当前会话最近 10 轮 context
- 当前页面弱提示词(仅页面类型 + 实体 id + 名称,**不含**业务详细数据)
- DooTask 默认 system prompt
## 不会发送的
- 用户密码、token、密钥
- 不在 context 窗口内的历史会话
- 其他用户对话
- 私聊 / 群聊具体消息(除非用户主动复制到提问)
- 完整页面业务数据(只发弱提示词)
## 清理与删除
- 单条 / 清空当前桶:[[ai-assistant.session-delete.howto]]
- 清浏览器 IndexedDB清掉模型选择、浮按钮位置、输入历史
- 上游侧保留多久取决于服务商(如 OpenAI 默认 30 天)
## 不支持
- 不支持端到端加密让管理员都看不到
- 不支持把某条对话标记「不发送」
- 不支持自助导出全部历史
## 相关
- [[ai-assistant.auth.concept]]
- [[ai-assistant.session.concept]]
- [[ai-assistant.page-context.concept]]

View File

@ -0,0 +1,70 @@
---
id: ai-assistant.session.concept
title: AI 助手会话
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 会话
- AI 历史
- AI 对话记录
- AI session
- session_key
- 会话桶
related_tools: []
related_pages: []
prerequisites: []
negative:
- 单个桶最多 20 条会话,超过自动淘汰最早
- 嵌入式入口的会话不出现在浮窗历史里
- 删除会话不可恢复,对应图片也会从 public/uploads 物理删除
last_verified: v1.7.90
---
# AI 助手会话
## 定义
会话session是 AI 助手中一组连续的用户提问 + AI 回答集合,按「场景桶」隔离持久化到 `ai_assistant_sessions` 表,归当前用户所有。
## 关键属性
| 字段 | 含义 |
|---|---|
| `session_key` | **场景桶**,浮窗用 `global`,嵌入入口用各自 key |
| `session_id` | 唯一 id格式 `session-{时间戳}-{随机串}` |
| `title` | 自动取首条用户消息前 20 字 |
| `data` | JSON 数组,存所有 user/assistant/system 消息 |
| `images` | JSON 对象 `{imageId: 服务端路径}` |
| `updated_at` | 历史列表按其倒序 |
## 持久化时机
- 流式回答完成后立即保存
- 编辑历史问题、删除消息后保存
- 普通编辑期间有 2 秒防抖
## 桶session_key的作用
不同业务入口走不同桶:
- `global`:浮窗 + 快捷键 + 顶部「+」菜单
- `project-create`:新建项目 AI 助手
- `task-add`:新建任务 AI 助手
- `report-edit`:工作汇报 AI
- `chat-message`:消息输入 AI
切桶时先保存当前会话再载入目标桶历史列表,避免互相串扰。
## 数量限制
- 每个桶最多保留 20 条(`maxSessionsPerKey`
- 超过从最旧的开始淘汰
- 单会话内最多 50 条 message`maxResponses`
## 不支持
- 不支持跨用户 / 跨桶搜索;只查当前用户当前桶
- 不支持把某条会话从一个桶导到另一个桶
- 不支持手动改会话标题
## 相关
- [[ai-assistant.session-list.howto]]
- [[ai-assistant.new-chat.howto]]
- [[ai-assistant.session-delete.howto]]
- [[ai-assistant.embed-entry.concept]]

View File

@ -0,0 +1,74 @@
---
id: ai-assistant.streaming.concept
title: AI 助手流式输出
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 流式回答
- 边写边出
- AI 一边打字一边显示
- SSE 流
- AI 实时输出
related_tools: []
related_pages: []
prerequisites: []
negative:
- 不支持 WebSocket 推送,固定走 SSE
- 不支持手动重试单条;流断后该 message 标记 error需重新发送
- 同会话并发流会被前一个的发送动作清掉
last_verified: v1.7.90
---
# AI 助手流式输出
## 定义
AI 助手通过 **SSEServer-Sent Events** 接收上游 LLM 的逐 token 输出,每收到一个 chunk 就追加到当前 message 的 `rawOutput`,呈现 ChatGPT 般「边写边出」的效果。
## 数据流
1. `POST api/assistant/auth` → 拿 stream_key
2. `EventSource ai/invoke/stream/{stream_key}`
3. 监听三类事件:
- `append` → 追加(`entry.rawOutput += chunk`
- `replace` → 整段替换(`entry.rawOutput = chunk`
- `done` → 流结束(`entry.status = 'completed'`
4. 断流 / 异常 → `handleStreamFailed`
## 事件类型
| 事件 | 用途 |
|---|---|
| `append` | 普通增量 token最常见 |
| `replace` | 整段替换(工具结果回填、思考链重写) |
| `done` | 流结束payload 含 `error` 字段表示上游报错 |
## 状态机
每条 AI message 4 个状态:
- `waiting` → 已发出但首个 chunk 未到
- `streaming` → 正在接收 chunk
- `completed``done` 正常收到
- `error` → 上游错误、连接失败、用户中断
## 并发与归属
- 每个流绑定 `owner = {sessionKey, sessionId, localId}`
- 用户切到其他会话后,原流仍后台跑,结束时写回原会话存储
- 发新问题前会清掉同会话的所有活跃流,防止两段对话错位
## 中断恢复保护
- 关浮窗 / 页面崩溃后,下次加载会话发现 message 仍是 `streaming/waiting` 则归一为 `error`
- 避免「永远转圈」(`sanitizeResponsesForPersist`
## 滚动行为
- 视图在最底部时新 chunk 自动 `scrollToBottom`
- 用户向上翻历史时(距底 > 20px不强制下拉
## 不支持
- 不支持 WebSocket
- 不支持 chunk 级别撤销
- 不支持自动重试断流
## 相关
- [[ai-assistant.stop.howto]]
- [[ai-assistant.modal.concept]]
- [[ai-assistant.session-save.howto]]

View File

@ -0,0 +1,53 @@
---
id: ai-assistant.tool-call.concept
title: 工具调用的流式事件结构
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- tool_call 是什么
- AI 工具调用的过程
- 工具调用气泡
- AI 调用了工具显示什么
- tool_call 事件
related_tools: []
related_pages: []
prerequisites: []
negative:
- 单次回复可包含多次工具调用(并行或串行),不限于一次
- 工具调用结果不计入用户上下文 token但会消耗会话 token
- 用户不能在前端取消已发出的工具调用,只能整体中断本轮回复
last_verified: v1.7.90
---
# 工具调用的流式事件结构
## 定义
AI 助手回复以流式 SSE 推送给前端,事件中除了文本增量,还会出现工具调用片段。每次工具调用在浮窗里渲染为一个独立气泡,展示"工具名 + 参数 + 状态",让用户能看到 AI 在背后做什么。
## 一次完整工具调用的事件序列
1. `tool_call_start`:模型决定调工具,前端插入气泡,状态置「执行中」
2. `tool_call_arguments`(可多次):参数 JSON 增量流式拼接
3. `tool_call_result`:后端/前端返回结果,状态变「完成」或「失败」
4. 模型基于结果继续生成 `message` 文本
## 气泡可见信息
- 工具名(如 `list_tasks`
- 入参 JSON折叠/展开)
- 出参摘要(成功)或错误码(失败)
- 执行耗时
## 与普通文本的关系
- 工具结果不直接回给用户,而是回灌给模型
- 模型读结果后再生成下一段自然语言回答("我找到 3 条任务……"
- 用户体感是"AI 一边查一边说"
## 不支持
- 工具调用进行中无法手动改参数;要重来需点重试或重新提问
- 失败的工具调用不会自动二次重试(模型可能换工具或道歉)
## 相关
- 工具机制总览:[[ai-assistant.tools.concept]]
- 工具清单:[[ai-assistant.tools-list.concept]]
- 失败处理:[[ai-assistant.tool-failed.faq]]

View File

@ -0,0 +1,66 @@
---
id: ai-assistant.tools-list.concept
title: AI 助手可用工具清单
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 有哪些工具
- AI 能调用什么
- AI 工具列表
- MCP 工具有哪些
- AI 都能做什么
related_tools: []
related_pages: []
prerequisites:
- 应用市场已安装 ai 插件
- 应用市场已安装 mcp_server 插件
negative:
- 工具集合由系统维护,普通用户无法自定义增减
- 列表项名是后端工具名snake_case用户不需要记
- 不在清单中的能力 AI 无法直接执行(例如改系统设置、装插件)
last_verified: v1.7.90
---
# AI 助手可用工具清单
## 用户与组织
- `get_users_basic`:批量取用户昵称/邮箱/头像
- `search_users`:按关键词找人,支持按项目/对话筛选
## 任务
- `list_tasks`:列任务(状态/项目/时间筛选)
- `get_task`:取任务完整详情
- `create_task``update_task``complete_task``delete_task`
- `create_sub_task`:建子任务
- `get_task_files`:取任务附件
## 项目
- `list_projects``get_project``create_project``update_project`
## 消息与对话
- `search_dialogs`:搜对话
- `send_message`:发消息
- `send_task_ai_message`:作为 AI 向任务讨论区发消息
- `get_message_list`:取历史消息
## 文件
- `list_files``search_files``get_file_detail``fetch_file_content`
- `extract_image_text`:图片 OCR
## 工作报告
- `list_received_reports``list_my_reports``get_report_detail``create_report``mark_reports_read`
- `generate_report_template`:基于已完成任务生成报告草稿
## 搜索
- `intelligent_search`:跨任务/项目/文件/联系人/消息统一语义搜索
## 页面自动化
- `get_page_context`:取当前页面结构与可交互元素
- `execute_action`:打开任务/项目/对话或跳功能页
- `execute_element_action`:点击/输入/选择/聚焦/滚动/悬停
## 知识库
- `search_help_docs`检索本知识库DooTask 功能说明)
- `get_session_image`:取用户上传到会话的图片

View File

@ -0,0 +1,51 @@
---
id: ai-assistant.tools.concept
title: AI 助手的工具调用机制
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 工具
- AI 调工具
- 怎么让 AI 调工具
- 让 AI 调用工具
- MCP 工具
- AI 怎么操作系统
- function calling
- AI 能做什么
related_tools: []
related_pages: []
prerequisites:
- 应用市场已安装 ai 插件
- 应用市场已安装 mcp_server 插件
negative:
- 工具是否可用取决于所选模型是否支持 tool/function calling部分模型仅能纯文本回答
- 工具调用结果会展示给用户,不会静默执行
- 用户没权限的操作(如读不到的任务)即使 AI 调工具也会被后端拒绝
last_verified: v1.7.90
---
# AI 助手的工具调用机制
## 定义
DooTask AI 助手通过 MCPModel Context Protocol协议调用工具把"问 AI"扩展为"让 AI 帮我做事"。AI 收到用户请求后判断是否需要调工具,例如查任务、发消息、跳页面,由插件 `mcp_server` 把请求路由到对应的后端 API 或前端动作执行器,再把结果回灌给模型,模型基于结果继续回答。
## 工具来源
- **dootask-mcp 内置工具**33 个,覆盖任务/项目/消息/文件/报告/搜索/页面操作
- **AI 助手内置工具**`search_help_docs`(检索本知识库)、`get_session_image`(取多模态图片)
- 工具清单维护在仓库 `resources/ai-kb/_meta/tool-binding.yaml`
## 工具分两类
- **数据工具**:直接调后端 API`create_task``send_message`),不依赖前端 UI
- **页面工具**:通过 WebSocket 命令前端浏览器(如 `execute_action` 打开任务、`execute_element_action` 点按钮)
## 触发条件
- AI 模型本身必须支持 tool callingOpenAI / Claude / DeepSeek / Qwen 等主流模型均支持)
- 管理员在系统设置「AI 模型」中开启该模型
- `mcp_server` 插件已安装并运行
## 相关
- 工具完整清单:[[ai-assistant.tools-list.concept]]
- 工具调用流式事件结构:[[ai-assistant.tool-call.concept]]
- 工具调用失败处理:[[ai-assistant.tool-failed.faq]]

View File

@ -0,0 +1,75 @@
---
id: ai-assistant.welcome-prompts.concept
title: AI 助手欢迎语与快捷提示
type: concept
feature: ai-assistant
scope: end-user
locale: zh
aliases:
- AI 欢迎页
- AI 快捷提示
- AI 推荐问法
- AI 起手
- AI 建议
- AI 提示卡片
related_tools: []
related_pages: []
prerequisites: []
negative:
- 用户不能自定义快捷提示卡片
- 卡片在每次场景切换时会重抽,并非固定不变
- 卡片点击只是把文本填入输入框,不会自动发送
last_verified: v1.7.90
---
# AI 助手欢迎语与快捷提示
## 定义
打开 AI 助手浮窗、**还没有任何对话**时显示的引导界面,包含 AI 图标 + 「欢迎使用 AI 助手」标题 + 若干个**快捷提示卡片**。卡片内容根据当前页面场景动态切换。
## 卡片分类
按四类设计确保多样性:
| 类型 | 含义 | 示例 |
|---|---|---|
| `query` | 查询 / 概览 | 「我今天有哪些任务?」 |
| `action` | 推进 / 操作 | 「帮我把这条任务标记完成」 |
| `sync` | 同步 / 协作 | 「给项目组发一条进度更新」 |
| `review` | 复盘 / 总结 | 「总结一下这周的工作」 |
## 与页面场景联动
卡片基于以下数据动态生成:
- 当前路由(仪表盘 / 项目 / 任务 / 消息 / 文件 / 日历)
- 当前打开的弹窗(任务 / 对话)
- 当前项目 / 任务 / 对话 id
- 当前语言(中 / 英)
切换页面时通过 `welcomePromptsKey` watcher 触发100ms 防抖避免闪屏。
## 点击行为
1. 点击某张卡片
2. 卡片文本被填入输入框
3. 输入框自动获焦
4. **不会**自动发送,需手动按发送或回车
## 显示条件
- AI 助手浮窗已打开
- `visibleResponses.length === 0`(当前会话无消息)
- `displayMode === 'chat'`modal 模式由业务方控制)
## 何时刷新
- 路由变更
- 打开 / 关闭任务弹窗
- 打开 / 关闭对话弹窗
- 切换项目
均走 `welcomePromptsKey` watcher + 100ms 防抖。
## 不支持
- 不支持自定义卡片文案
- 不支持「关闭欢迎提示卡片」开关
- 不支持把某条卡片置顶或常驻
## 相关
- [[ai-assistant.modal.concept]]
- [[ai-assistant.page-context.concept]]

View File

@ -0,0 +1,42 @@
---
id: application.center.concept
title: 应用中心是什么
type: concept
feature: application
scope: end-user
locale: zh
aliases:
- 应用中心
- 应用入口
- 微应用在哪
- 怎么打开应用
- 应用是什么
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 应用中心不是「应用市场」AppStore后者是管理员安装/卸载插件的独立入口
- 普通成员看不到管理员应用区(如 LDAP、邮件通知、数据导出等
last_verified: v1.7.90
---
# 应用中心是什么
## 定义
应用中心是 DooTask 左侧栏的一级导航之一,聚合了「系统应用」「管理员应用」「微应用」三类卡片,是用户开启大部分非核心模块的统一入口。
## 入口
- 桌面端:左侧栏「应用」图标(位列仪表盘 / 日历 / 消息 / 文件之后)
- 移动端:底部 Tabbar 「应用」
## 三类应用
- **系统应用**:内置固定功能,所有用户可见(审批、签到、工作报告、我的收藏、最近打开、机器人、创建群组、在线会议、创建项目、添加任务、导出管理)
- **管理员应用**仅系统管理员可见LDAP、邮件通知、APP 推送、举报管理、数据导出、团队管理)
- **微应用**由已安装插件提供OKR、思维导图、流程图、在线文档、Memos、KPI 等),列表随安装情况变化
## 个性化
- 拖拽:长按卡片可调整顺序(仅影响个人视图)
- 管理员可在「自定义应用菜单」里调整全员可见的应用项
## 与「应用市场」的区别
应用中心是**使用**应用的入口应用市场AppStore是**安装/卸载/配置**插件的入口,仅管理员可见。

View File

@ -0,0 +1,48 @@
---
id: application.classify.concept
title: 应用中心的三类应用
type: concept
feature: application
scope: end-user
locale: zh
aliases:
- 应用分类
- 系统应用是什么
- 管理员应用是什么
- 微应用是什么
- 应用怎么区分
- 应用类型
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 普通成员看不到「管理员应用」分区
- 微应用不固定,随插件安装/卸载动态变化
last_verified: v1.7.90
---
# 应用中心的三类应用
## 定义
应用中心把所有可点击的入口卡片分成三类:系统应用、管理员应用、微应用。三者按数据源、可见范围、是否依赖插件来区分。
## 三类对比
| 类别 | 数据来源 | 可见范围 | 是否依赖插件 |
|---|---|---|---|
| 系统应用 | 主程序硬编码11 个固定项) | 所有登录用户 | 否,主程序内置 |
| 管理员应用 | 主程序硬编码LDAP / 邮件 / 推送 / 举报 / 数据导出 / 团队管理) | 仅 `userIsAdmin` | 否 |
| 微应用 | 已安装插件提供的 menu 注册项 | 由插件 `visible_to` 决定admin / all | 是 |
## 关键差异
- **是否能被卸载**:系统应用和管理员应用无法卸载;微应用随插件卸载消失
- **能否自定义**:管理员可在「自定义应用菜单」给微应用追加 / 覆盖入口配置,系统应用不能动
- **排序**:三类卡片混合后由用户拖拽个性化(仅本人可见)
## 与「应用市场」的关系
- 应用中心 = **使用**入口
- 应用市场AppStore= 安装 / 卸载 / 配置插件,仅管理员可见,会改变可用的微应用清单
## 相关
- 系统应用完整列表见:[[app-system.list.concept]]
- 拖拽排序:[[application.sort.howto]]

View File

@ -0,0 +1,59 @@
---
id: approve.form.concept
title: 审批表单字段
type: concept
feature: approve
scope: admin
locale: zh
aliases:
- 审批表单
- 表单字段
- 表单设计
- 自定义字段
- 申请单字段
- 表单组件有哪些
- 申请类型字段
related_tools: []
related_pages: [application]
prerequisites:
- 当前用户是系统管理员
- 在流程模板编辑器中
negative:
- 当前版本不支持公式字段(如「时长 = 结束时间 - 开始时间」自动算)
- 不支持联动字段A 字段值变化时显示/隐藏 B 字段)
- 不支持把表单字段值用作下一节点审批人路由依据(无条件分支)
- 表单字段一旦发布后改动,已提交的旧实例展示按当时快照不回填
last_verified: v1.7.90
---
# 审批表单字段
## 定义
审批表单是发起人填写的申请单结构,由若干**字段组件**拼成。管理员在流程模板编辑器iframe 内)拖拽这些组件即可设计表单。表单值随流程实例一起存到插件库的 `var` 字段JSON并随状态变化贯穿始终。
## 内置字段组件
- **单行文本**:限长字符串,如「申请人姓名」「合同编号」
- **多行文本**:长描述,如「请假事由」「报销说明」
- **数字**:整数 / 小数,如「报销金额」
- **日期 / 日期时间**:年-月-日 或精确到分钟,常用于开始/结束时间
- **单选 / 多选 / 下拉**:固定选项集,如「假期类型」可枚举「年假/事假/病假/调休/产假/陪产假/婚假/丧假/哺乳假/产检假/其他」
- **附件 / 图片**:上传文件,前端控件复用主程序 `ImgUpload`;图片限制宽高与张数(默认上限 3 张、2048×2048
- **明细 / 子表**:一对多结构,如报销单的多行费用明细
- **金额 / 时长**:业务语义字段(基于数字字段封装)
## 必填、提示、默认值
- 每个字段可配置 `required`(必填)、占位提示、默认值
- 必填项校验在发起页前端拦截;提交到主程序 `process__start` 再做二次校验
## 与流程节点的关系
- 表单字段是"装数据的",节点是"管流转的",两者解耦
- 节点拿不到 form 字段做路由决策(不支持条件分支)
## 数据存储
- 表单值在主程序前端组成 `var` JSON 后随 `proc_name + department_id` 一起调 `approve/process/start`
- 插件把 `var` 整体存进 proc inst 行;后续审批人查看时反序列化展示
## 不支持
- 不支持字段联动 / 公式 / 条件分支
- 不支持表单级权限(不能按角色显示不同字段)
- 不支持跨流程模板字段复用

View File

@ -0,0 +1,49 @@
---
id: approve.history.concept
title: 审批流程历史 History
type: concept
feature: approve
scope: end-user
locale: zh
aliases:
- 审批历史
- 审批操作记录
- 流程留痕
- 谁审过
- 流程节点历史
- ProcInstHistory
related_tools: []
related_pages: [application]
prerequisites:
- 应用市场已安装 approve 插件
negative:
- 历史记录不可手动编辑,只由系统在节点流转时写入
- 删除审批process/delById会连带删除该流程的全部历史
- 历史不会单独导出,导出走 admin 的 approve/export 数据导出
last_verified: v1.7.90
---
# 审批流程历史 History
## 定义
流程历史(`ApproveProcInstHistory`,表 `approve_proc_inst_history`)是每个流程实例([[approve.process-inst.concept]])在节点流转时留下的快照,记录谁发起、当前节点、最近意见、整体状态,用于已结束审批的留档查询和驱动「已办」「抄送我(已结束)」两类列表。
## 关键字段
- **proc_def_id / proc_def_name**:所属模板 ID 和名称
- **title**:审批标题
- **start_user_id / start_user_name**:发起人
- **department_id / department / company**:发起部门和公司
- **node_id / candidate / task_id**:当前节点、候选人 userid 串、task ID
- **start_time / end_time / duration**:开始/结束/持续时长
- **state**0 待审批 / 1 审批中 / 2 通过 / 3 拒绝 / 4 撤回
- **is_finished / var**:是否结束 / 表单数据 JSON
- **latest_comment / global_comment**:最近一次意见 / 全文评论汇总
## 谁会读取
- 「已办」:`approve/procHistory/findTask`
- 「抄送我」已结束:`approve/procHistory/findProcNotify`
- 「已发起」已结束:`approve/procHistory/startByMyself`
- 详情页流程图:合并 `node_infos` 与历史渲染
## 副作用:用户请假/外出状态
静态方法 `getUserApprovalStatus(userid)` 按当前时间是否落在某条已通过的「请假/外出」表单时段内决定用户在系统其他地方的「请假中」标签。1 分钟缓存。

View File

@ -0,0 +1,58 @@
---
id: approve.node.concept
title: 审批节点类型
type: concept
feature: approve
scope: admin
locale: zh
aliases:
- 审批节点
- 单人审批
- 会签是什么
- 顺序会签
- 并行会签
- 或签
- 多人审批怎么配
- 审批人怎么设
related_tools: []
related_pages: [application]
prerequisites:
- 当前用户是系统管理员
- 在流程模板编辑器中
negative:
- 当前版本不支持条件分支节点(按表单字段值自动路由到不同审批人),节点链只能串行/并行
- 不支持嵌套子流程节点
- 不支持回退到任意节点:拒绝直接结束流程,不能让发起人改了再走原路
last_verified: v1.7.90
---
# 审批节点类型
## 定义
流程模板由若干**节点**串成一条链。一个节点 = 一步审批/抄送。节点决定"这一步由谁参与、用什么方式参与、参与后流程怎么走"。审批人来源支持指定具体用户、按角色/部门负责人动态解析。每条流程发起后会生成对应的"审批任务task"分发到各节点候选人手上。
## 主要节点类型
- **发起节点**:流程起点,自动以申请人身份填充;不需要配置审批人
- **单人审批**1 个候选人,处理后流程进入下一节点
- **顺序会签**:多个审批人按顺序处理,前一人通过才轮到下一人;任一人拒绝整单拒绝
- **并行会签**:多个审批人同时收到任务,**全部**通过才进入下一节点;任一人拒绝整单拒绝
- **或签**:多个候选人同时收到任务,**任一人**处理即代表全节点完成(典型用于"任一主管审批"
- **抄送节点**通知性节点候选人只收到通知在「抄送我的」Tab 看到),无须操作,流程不等待
## 审批人来源
- 指定具体用户:在编辑器里勾选成员列表
- 部门负责人:动态解析为发起人当前部门的 `owner_userid`(在 `UserDepartment` 表里维护)
- 角色:按主程序角色匹配
- 发起人自选:留空让发起人发起时手动指定
## 节点完成后的通知
节点产生新任务后,主程序通过 `approval-alert` 机器人在群聊推送"待你审批"模板消息给候选人;状态变更(通过/拒绝/撤回)也走同一机器人推送。
## 与状态的关系
- 整单状态审批中1/ 通过2/ 拒绝3/ 撤回4
- 当前所在节点 + 候选人由插件维护,主程序通过 `process__getProcessById` 拿到
## 不支持
- 不支持条件分支(不能按表单字段值 IF X 自动路由到分支 A需要按条件走不同审批人时把流程拆成多个流程模板如「请假 3 天以内」「请假 3 天以上」各建一个),由发起人按情况选择
- 不支持任意节点回退
- 不支持节点级 SLA 超时自动转交

View File

@ -0,0 +1,47 @@
---
id: approve.notify.concept
title: 审批通知与「审批助手」机器人
type: concept
feature: approve
scope: end-user
locale: zh
aliases:
- 审批机器人
- 审批通知
- 审批助手
- approval-alert
- 怎么收到审批提醒
- 审批卡片
related_tools: []
related_pages: [application, messenger]
prerequisites:
- 应用市场已安装 approve 插件
- 当前账号未屏蔽「审批助手」机器人会话
negative:
- 不支持改成第三方 webhook 推送
- 不支持邮件 / 短信 / APP 推送渠道(只发应用内聊天)
- 不支持自定义模板文案
- 机器人不能被「@」也不能直接对话指令
last_verified: v1.7.90
---
# 审批通知与「审批助手」机器人
## 定义
审批的所有通知都通过系统机器人「审批助手」userid `approval-alert`)的 1 对 1 私聊推送,消息类型 `template`(卡片)。卡片含标题、关键字段、缩略图(请假类带图片时),底部「查看详情」跳进审批详情。
## 何时发卡片
后端 `approveMsg()``type` 投递:
| type | 触发 | 收件人 | 标题样例 |
|---|---|---|---|
| `approve_reviewer` | 到我作审批人节点 | 待审批人 | {发起人} 提交的「{模板}」待你审批 |
| `approve_notifier` | 到我所在 notifier 节点 | 抄送人 | 抄送 {发起人} 提交的「{模板}」记录 |
| `approve_comment_notifier` | 我参与的审批被加全文评论 | 其他相关方 | {评论人} 评论了 {发起人} 的「{模板}」审批 |
| `approve_submitter` | 我发起的被通过/拒绝 | 发起人 | 您发起的「{模板}」已通过 / 被 {审批人} 拒绝 |
## 卡片更新机制
审批人同意/拒绝、发起人撤销时不发新卡,而用 `msg_id` 反查原卡片调 `change-{msg_id}` 原地更新(绿-通过/红-拒绝/灰-撤回),避免聊天里堆几十条。
## 未读徽标
`approve_reviewer` 推送时附带 WebSocket 事件 `approve/unread`,「审批」入口和 Tab「待办」名后的数字立即 +1处理后 -1。数量接口 `approve/process/doto`

View File

@ -0,0 +1,46 @@
---
id: approve.concept
title: 审批中心是什么
type: concept
feature: approve
scope: end-user
locale: zh
aliases:
- 审批是什么
- 审批流程是什么
- approve 插件
- 什么是流程审批
- 工作流
related_tools: []
related_pages: [application]
prerequisites:
- 应用市场已安装 approve 插件
negative:
- 审批是独立插件,未安装时整个功能不可用
- 不支持条件分支(按表单值路由到不同审批人)
- 不支持嵌套子审批
- 表单不支持公式计算字段
last_verified: v1.7.90
---
# 审批中心是什么
## 定义
审批中心approve是 DooTask 的内置流程审批应用,用于在企业内提交、流转、处理表单类申请。典型场景:请假、出差、报销、用印、采购、合同会签。它是独立插件(独立 docker 服务 + 独立数据库),主程序通过 `ApproveController` 代理调用并把消息回写到聊天会话。
## 三个核心对象
- **流程模板 ProcDef**:管理员预先定义的审批样板,含表单字段、审批人节点、抄送人节点。普通用户只能选用,不能修改
- **流程实例 ProcInst**:一次具体的审批运行;用户每提交一次就生成一条,详见 [[approve.process-inst.concept]]
- **节点 Node**:模板里的步骤;常见类型 `starter`(发起)、`approver`(审批人)、`notifier`(抄送)、`end`(结束)
## 三类角色
- **发起人**:提交审批的人,能撤回未结束的审批、能给已结束的审批补评论
- **审批人**:在「待办」看到任务,可同意或拒绝
- **抄送人**:在「抄送我」看到知会,无须操作
## 状态机
流程实例的 `state`1 审批中、2 已通过、3 已拒绝、4 已撤回。详情页右上角用 Tag 显示,列表也按这个色区分。
## 与项目/任务/聊天的关系
- 审批不属于任何项目,是独立工作流
- 所有通知通过「审批助手」机器人发到 1 对 1 聊天([[approve.notify.concept]]

View File

@ -0,0 +1,43 @@
---
id: approve.plugin.concept
title: 审批插件架构
type: concept
feature: approve
scope: end-user
locale: zh
aliases:
- 审批是插件吗
- approve 插件是什么
- 审批中心怎么部署的
- 为什么审批要单独装
- 审批不在主程序里吗
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- approve 不是主程序内置功能,必须先在应用市场安装插件才能用
- 审批数据存在独立的 `<前缀>approve_*` 数据表里,不在主程序业务表
- 卸载插件时若勾选「删除数据」会清空全部审批数据,无法恢复
last_verified: v1.7.90
---
# 审批插件架构
## 定义
审批approve是 DooTask 的独立插件,由 `kuaifan/dooapprove` Docker 镜像提供一套独立的工作流引擎服务。主程序通过反向代理与之通信,所有审批的流程定义、实例、任务、历史都由插件维护,不在主程序业务表里。
## 关键属性
- **独立容器**:插件作为 docker-compose 服务名 `approve` 启动,端口仅在内网暴露
- **独立数据库表**:复用主库实例但表前缀为 `<DB_PREFIX>approve_`(如 `pre_approve_*`),与主程序业务表逻辑隔离
- **HTTP 反代**:主程序 nginx 把 `/approve/` 转给插件容器;`/approve/api/` 先经 `/approveAuth` 校验主程序 token 再放行
- **业务桥接**:主程序 `ApproveController` 通过 `http://approve` 调用插件 REST 接口(路径前缀 `/api/v1/workflow/...`),把结果包成 `Base::retSuccess` 返回前端
- **通知桥接**:审批状态变化通过 `approval-alert` 机器人在 DooTask 群聊中下发模板消息
## 与主程序的关系
- 主程序登录 token = 审批插件身份凭据(通过 `verifyToken` 接口校验)
- 主程序用户/部门/机器人是审批的"人员主数据",审批不复制用户表
- 用户感知不到容器分离:在 [[app-system.approve.howto]] 描述的「审批中心」页面内完成所有操作
## 不支持
- 主程序无法直接 SQL 查询审批数据,必须经插件 API
- 关闭/卸载插件后无法发起新审批:「应用 - 审批」入口在新发起处会报错(`Apps::isInstalledThrow`

View File

@ -0,0 +1,51 @@
---
id: approve.process-inst.concept
title: 流程实例 ProcInst 是什么
type: concept
feature: approve
scope: end-user
locale: zh
aliases:
- 流程实例
- 审批单
- ProcInst
- 审批运行
- 一次审批
- 流程 ID
related_tools: []
related_pages: [application]
prerequisites:
- 应用市场已安装 approve 插件
negative:
- 流程实例不可改字段,提交后表单只读
- 一旦后续节点已被任何审批人处理,发起人不能再撤销
- 已结束的实例只有发起人或管理员能删([[approve.doto.howto]]
last_verified: v1.7.90
---
# 流程实例 ProcInst 是什么
## 定义
流程实例ProcInst是「一次具体的审批运行」的对象由发起审批时创建。模板ProcDef是图纸实例是按图纸跑出来的一条具体审批单。每提交一次就生成一个新实例有唯一数字 ID。后端表 `approve_proc_inst`,主程序通过 `approve/process/findById` 取数据。
## 关键属性
- **id**:实例唯一 ID
- **proc_def_name**:模板名(如「请假申请」)
- **start_user_id / start_user_name**:发起人 ID 和昵称
- **start_time**:提交时间
- **state**1 审批中 / 2 已通过 / 3 已拒绝 / 4 已撤回
- **is_finished**是否结束state ≠ 1 即结束)
- **task_id**:当前 task ID处理动作要带
- **candidate**:当前候选审批人 userid 逗号串
- **node_id / node_infos**:当前节点 ID + 所有节点状态数组,用于渲染流程图
- **var**表单字段对象type / start_time / end_time / description / other 等)
- **global_comments**:全文评论数组([[approve.comment.howto]]
## 生命周期
1. **创建**`approve/process/start`state=1
2. **流转**:每次 `approve/task/complete` 推进节点
3. **结束**state 变 2/3/4
4. **清理**:仅结束态可调 `approve/process/delById` 物理删除
## 与历史记录的关系
每次节点流转写一条 [[approve.history.concept]]`approve_proc_inst_history`),实例被删后历史也消失。

View File

@ -0,0 +1,48 @@
---
id: approve.template.concept
title: 流程模板(流程定义)
type: concept
feature: approve
scope: admin
locale: zh
aliases:
- 流程模板
- 审批模板
- 流程定义
- procdef 是什么
- 流程名称
- 审批流程是什么
related_tools: []
related_pages: [application]
prerequisites:
- 应用市场已安装 approve 插件
negative:
- 流程模板只能由系统管理员创建、编辑、删除;普通成员只能基于已发布模板发起申请
- 删除模板会同时清空该模板下的所有审批数据,不可恢复
- 模板间不可复制:每个模板都要在编辑器里独立配置节点和表单
last_verified: v1.7.90
---
# 流程模板(流程定义)
## 定义
流程模板Process Definition简称 procdef是审批插件里"审批种类"的元数据,定义了一类审批的全部行为:表单字段、节点顺序、每个节点的审批人/抄送人。普通成员发起的每一单审批(流程实例)都必须基于某个已发布的流程模板。常见模板:请假申请、报销申请、出差申请、用印申请、采购申请。
## 关键属性
- **唯一性**:以 `name` 区分(如「请假申请」),同名模板不允许重复创建
- **版本**:每次发布累计 `version`;旧版本会被新版本顶替,进行中的审批仍按当时的版本走完
- **状态**:草稿(编辑中)/ 已发布(前台可选)
- **存储位置**:插件独立数据库 `<前缀>approve_*` 表,不在主程序业务表
- **生效范围**:全租户共享,不按部门隔离;要按部门分模板请改用不同 `name`
## 与流程实例的关系
- 一个模板procdef= 一份蓝图
- 用户每发起一单 = 创建一个流程实例proc inst状态独立流转
- 删除模板:插件会同时清空所有以该模板发起的实例,**不可恢复**
## 与节点 / 表单的关系
- 流程模板包含两部分配置:**节点链**(决定走哪几步、由谁审批)见 [[approve.node.concept]]、**表单字段**(决定申请人填什么)见 [[approve.form.concept]]
- 节点和表单在同一个流程编辑器iframe 内)一起设计、一起发布
## 创建入口
管理员在审批中心顶部「流程设置」按钮 → 「+」新建,详细步骤见 [[approve.template.howto]]。

View File

@ -0,0 +1,42 @@
---
id: app-admin.list.concept
title: 管理员应用全集
type: concept
feature: app-admin
scope: admin
locale: zh
aliases:
- 管理员应用有哪些
- 管理员能用哪些应用
- 后台管理应用
- 管理员入口
- 管理员卡片
related_tools: []
related_pages: [application]
prerequisites:
- 需要系统管理员权限userIsAdmin
negative:
- 普通成员看不到管理员应用区,整块都不会渲染
- 管理员应用不通过应用市场安装,是主程序内置
- 超级管理员专属功能(如 License Key、合规设置不属于这里需走系统设置
last_verified: v1.7.90
---
# 管理员应用全集
## 定义
管理员应用是 DooTask 应用中心「管理员」分区里的卡片,仅系统管理员(`userIsAdmin`)可见,用于打开管理类抽屉/弹窗,无需进入「系统设置」深层菜单。
## 当前内置 6 个
- **LDAP**:第三方目录服务接入与同步
- **邮件通知**SMTP 服务器与通知开关配置
- **APP 推送**:移动端 UMENG 推送配置
- **举报管理**:处理用户举报记录
- **数据导出**:任务/超期/审批/签到等 4 种数据导出
- **团队管理**:成员列表 / 部门管理 / 邀请加入
## 与签到/会议管理的关系
签到管理、会议管理在「常用」分区,所有用户都能看到。但它们的「设置」入口(签到规则、会议参数)由抽屉右上角「设置」链接打开,仅管理员可见,详见 [[app-admin.checkin-setting.howto]]、[[app-admin.meeting-mgmt.howto]]。
## 可见性细则
见 [[app-admin.visibility.concept]]。

View File

@ -0,0 +1,45 @@
---
id: app-admin.visibility.concept
title: 管理员应用对谁可见
type: concept
feature: app-admin
scope: admin
locale: zh
aliases:
- 谁能看到管理员应用
- 普通成员能看到 LDAP 吗
- 管理员应用权限
- 为什么我看不到管理员分区
- 管理员区不显示
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 部门负责人 / 部门管理员不属于系统管理员,看不到管理员应用
- 没有「按应用单独授权管理员卡片」的细粒度开关
- 关掉账号管理员身份后,应用中心管理员区在下次刷新前可能仍有缓存
last_verified: v1.7.90
---
# 管理员应用对谁可见
## 定义
应用中心「管理员」分区Admin row只对系统管理员渲染判断字段是前端 Vuex 的 `userIsAdmin`,对应后端 `User::isAdmin()`。普通成员、临时帐号、部门负责人均看不到这块。
## 判定逻辑
- 后端:用户 `identity` 字段包含 `admin``isAdmin() = true`
- 前端:`store.state.userIsAdmin``true` 才挂载管理员卡片列表
- 任一管理员应用项都带 `show: this.userIsAdmin` 过滤
- 整个「管理员」标题区也用 `adminAppItems.length > 0` 判定0 项则连标题都不显示
## 普通成员看到什么
- 只看「常用」分区(系统应用 + 微应用,不含管理员卡片)
- 微应用如果设了 `visible_to: admin`,普通成员也看不到
## 升级身份后
- 让某成员变成管理员:系统管理员在「团队管理」勾选其「管理员」身份
- 该用户刷新页面后才能看到管理员应用区,应用中心不会自动热更新
## 相关
- 管理员应用清单:[[app-admin.list.concept]]
- 入口在哪:[[app-admin.entry.menu-map]]

View File

@ -0,0 +1,53 @@
---
id: app-system.list.concept
title: 系统应用全集列表
type: concept
feature: app-system
scope: end-user
locale: zh
aliases:
- 系统应用有哪些
- 内置应用清单
- DooTask 自带应用
- 都有什么应用
- 应用中心默认有什么
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 列表是主程序硬编码,不能通过插件追加或卸载
- 不同终端因屏幕方向会再追加日历 / 文件 / 设置三个入口
last_verified: v1.7.90
---
# 系统应用全集列表
## 定义
系统应用是 DooTask 主程序在应用中心「常用」区硬编码的内置入口,所有登录用户可见。共 11 个固定项,外加少量按终端形态动态出现的入口。
## 11 个核心系统应用
| value | 名称 | 作用 |
|---|---|---|
| approve | 审批中心 | 发起 / 处理审批流(依赖 approve 插件) |
| signin | 签到打卡 | 上下班打卡(依赖 signin 设置face 插件可选) |
| report | 工作报告 | 日报 / 周报 / 月报 |
| favorite | 我的收藏 | 查看收藏的任务 / 文件 / 消息 |
| recent | 最近打开 | 最近访问过的对象索引 |
| mybot | 我的机器人 | 自建消息机器人管理 |
| createGroup | 创建群组 | 直接打开创建群对话框 |
| meeting | 在线会议 | 进入会议入口 |
| addProject | 创建项目 | 直接打开创建项目对话框 |
| addTask | 添加任务 | 跨项目快速建任务 |
| exportManage | 导出管理 | 导出任务统计 / 超期 / 审批 / 签到数据 |
## 仅竖屏额外出现
移动端 / 桌面竖屏布局时应用中心还会追加日历calendar、文件file、设置setting三个入口以替代横屏被收起的左侧导航。
## 角标
- approve 卡片右上角显示「审批未读数」
- report 卡片右上角显示「工作报告未读数」
## 相关
- 三类应用怎么区分:[[application.classify.concept]]
- 哪些卡片普通成员/管理员能看见:[[app-system.visibility.concept]]

View File

@ -0,0 +1,50 @@
---
id: app-system.relation.concept
title: 系统应用 vs 微应用 vs 管理员应用
type: concept
feature: app-system
scope: end-user
locale: zh
aliases:
- 系统应用和微应用的区别
- 微应用是什么
- 管理员应用和系统应用的区别
- 三种应用怎么区分
- 哪些是内置哪些是插件
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 系统应用无法卸载或禁用
- 微应用列表会随插件安装 / 卸载变化
- 三类之间无法相互转换(如不能把系统应用改成微应用)
last_verified: v1.7.90
---
# 系统应用 vs 微应用 vs 管理员应用
## 定义
DooTask 应用中心同时呈现三种来源不同的卡片:系统应用(内置)、管理员应用(内置管理工具)、微应用(插件提供)。三者按数据来源、可见范围、能否卸载、是否依赖插件来区分。
## 对比表
| 维度 | 系统应用 | 管理员应用 | 微应用 |
|---|---|---|---|
| 数量是否固定 | 是(内置约 11 个) | 是6 个LDAP / 邮件通知 / APP 推送 / 举报管理 / 数据导出 / 团队管理) | 否,随插件变化 |
| 数据来源 | 主程序硬编码 | 主程序硬编码 | 已安装插件的 menu 注册 |
| 是否需要安装 | 不需要 | 不需要 | 需要管理员从「应用商店」安装 |
| 默认可见 | 所有登录用户 | 仅 `userIsAdmin` | 由插件 `visible_to` 决定 |
| 能否卸载 | 否 | 否 | 是,通过应用商店 |
| 升级方式 | 跟随主程序版本 | 跟随主程序版本 | 应用商店单独升级 |
## 典型例子
- 系统应用:工作报告、签到打卡、在线会议、我的收藏、最近打开、我的机器人、创建群组、群接龙、群投票、创建项目、添加任务
- 管理员应用LDAP、邮件通知、APP 推送、举报管理、数据导出、团队管理;管理员分区还有「应用商店」入口
- 微应用装插件后出现审批中心approve 插件、OKR、AI 助手ai 插件、思维导图minder、流程图drawio、在线文档OnlyOffice
## 共同点
- 三类都展示在应用中心,混合后受拖拽排序(见 [[application.sort.howto]])影响
- 都不在左侧主导航一级栏出现
## 想看系统应用完整列表
全集见:[[app-system.list.concept]]

View File

@ -0,0 +1,50 @@
---
id: app-system.visibility.concept
title: 系统应用对谁可见
type: concept
feature: app-system
scope: end-user
locale: zh
aliases:
- 系统应用权限
- 谁能看到这些应用
- 普通员工能用哪些应用
- 管理员独享的应用
- 应用可见性
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 没有按部门 / 按角色单独控制系统应用可见性的开关
- 普通成员无法通过设置「解锁」管理员应用区
last_verified: v1.7.90
---
# 系统应用对谁可见
## 定义
系统应用按主程序写死的两条规则展示:常用区对所有登录用户开放,管理员区仅 `userIsAdmin` 用户可见。无法精细到部门或角色级别。
## 普通成员end-user能看到
所有 11 个常用系统应用都对普通成员开放:审批、签到、工作报告、我的收藏、最近打开、机器人、创建群组、在线会议、创建项目、添加任务、导出管理。
但是否能正常用,还要看附加条件:
- approve需安装 approve 插件,未安装时点开会提示
- signin 的人脸打卡:需安装 face 插件
- meeting需主程序的会议模块启用
- 在权限不足时(如非管理员开导出),后端仍会拒绝
## 管理员admin额外能看到
管理员区独立显示,仅 `userIsAdmin` 用户能看见:
- LDAP 设置
- 邮件通知
- APP 推送
- 举报管理
- 数据导出(与常用区的「导出管理」是不同入口,前者偏配置后者偏一键导出)
- 团队管理
## 超级管理员super-admin
应用中心层面没有超管专属卡片;超管的差异点在「设置」内部页面,不在应用中心。
## 想详细看每个卡片做什么
全集列表见:[[app-system.list.concept]]

View File

@ -0,0 +1,54 @@
---
id: appstore.concept
title: 应用市场是什么
type: concept
feature: appstore
scope: admin
locale: zh
aliases:
- 应用商店
- 应用市场
- AppStore
- 插件市场
- 装插件在哪
- 插件管理
related_tools: []
related_pages: [application]
prerequisites:
- 需要系统管理员权限
negative:
- 不是 iOS / Android 那种第三方应用商店,仅装 DooTask 内部插件
- 普通成员看不到「应用商店」入口
- 不支持单用户安装,所有插件对全员生效
last_verified: v1.7.90
---
# 应用市场是什么
## 定义
应用市场AppStore是 DooTask 的插件管理后台,让系统管理员一键安装 / 卸载 / 更新各种功能插件,例如 AI 助手、审批、签到、OnlyOffice 等。其本体是一个名为 `appstore` 的微应用,注册在 `application/admin` 位置(见 `store/mutations.js` 第 396 行)。
## 关键属性
- **微应用形态**:通过 `MicroApps` 加载 iframeURL 为 `appstore/internal`
- **后端校验**`App\Module\Apps::isInstalled($appId)` 读取 `docker/appstore/config/{appId}/config.yml``status: installed` 判断
- **未安装会抛 ApiException**`Apps::isInstalledThrow()` 提示「应用「X」未安装」
- **跨容器调度**:内部调 `http://appstore` 服务 API 完成安装动作
- **生命周期 Hook**:用户创建 / 离职会调 `dispatchUserHook` 通知各插件user_onboard / offboard / update
## 插件类型
- 官方内置ai、approve、checkin/face、office、drawio、minder、okr、searchmanticore、fileview
- 社区插件:以 `community_` 前缀命名,如 `community_kuaifan_memos``community_kuaifan_kpi``community_Learntotolearn_roomly`
## 与「微应用菜单」的区别
- **应用市场**:管理插件「装/卸/更新」的容器
- **微应用菜单**:插件装好后注册到「应用」页的菜单项,普通成员可见的入口
## 不支持
- 不支持卸载 `appstore` 自身(`isInstalled('appstore')` 强制返回 true
- 不支持普通成员浏览未装插件列表
- 不支持安装非 DooTask 兼容的任意 Docker 镜像
## 相关
- 安装:[[appstore.install.howto]]
- 卸载:[[appstore.uninstall.howto]]
- 入口:[[appstore.entry.menu-map]]

View File

@ -0,0 +1,46 @@
---
id: bot.concept
title: 机器人是什么
type: concept
feature: bot
scope: end-user
locale: zh
aliases:
- 机器人
- bot
- robot
- DooTask 机器人
- 机器人有什么用
- 机器人分几种
related_tools: []
related_pages: [messenger]
prerequisites: []
negative:
- 机器人本质是特殊 Userusers.bot = 1不能用普通账号登录网页
- 机器人不能登录前端 UI只能通过 token 调用 API 或被 @ 触发
- 普通用户最多创建 50 个自建机器人
last_verified: v1.7.90
---
# 机器人是什么
## 定义
DooTask 的机器人bot / robot是一种特殊用户账号数据库 `users.bot = 1`),可以加入会话、收发消息、被 @ 触发,但不能登录 UI。它通常用于自动通知、外部系统接入、AI 对话。
## 三类机器人
DooTask 把机器人分三类,能力依次递增:
- **内置系统机器人**system bot邮箱以 `@bot.system` 结尾由系统创建并维护如「任务提醒」「审批」「签到打卡」「AI 助手」「会议通知」。普通用户不能新建或删除,仅管理员可改设置。详见 [[bot.system-list.concept]]。
- **用户自建机器人**UserBot任何登录用户都可在「应用 → 我的机器人」里创建,用于把外部系统消息推进 DooTask或拿到 token 后由外部代码代发消息。详见 [[bot.create.howto]]。
- **Webhook 接入**:自建机器人配置 `webhook_url` 后,收到的群消息 / @触发 / 成员变更等事件会被 POST 到该地址,外部服务可据此回复。详见 [[bot.webhook.concept]]。
## 关键属性
- 机器人有独立 `userid``token`、头像、昵称
- 像普通用户一样被加入群([[bot.invite.howto]])或被 @
- 群聊里必须 @ 机器人才会触发回复,单聊则任意消息都触发([[bot.mention.howto]]
- 自建机器人可设 `clear_day`消息保留天数1-999默认 90 天)
## 不支持
- 机器人之间不会互相触发(避免死循环),收到对方消息直接忽略
- 系统机器人不能由普通用户删除(删除会报「系统机器人不能删除」)
- 单个用户不能创建超过 50 个自建机器人(超出报「超过最大创建数量」)

View File

@ -0,0 +1,58 @@
---
id: bot.system-list.concept
title: 内置系统机器人有哪些
type: concept
feature: bot
scope: end-user
locale: zh
aliases:
- 系统机器人有哪些
- 内置机器人清单
- 自带的机器人
- 任务提醒机器人
- 审批机器人
- 签到机器人
- 会议机器人
related_tools: []
related_pages: [messenger]
prerequisites: []
negative:
- 系统机器人名单写死在 `UserBot::systemBotName`,不支持自助新增类型
- 系统机器人不可删除;仅管理员能改昵称 / 头像
- 部分机器人依赖对应插件已安装AI、审批等否则不出现
last_verified: v1.7.90
---
# 内置系统机器人有哪些
DooTask 自带一组系统机器人,邮箱统一以 `@bot.system` 结尾,由后端 `UserBot::systemBotName` 维护。普通用户能用,但不能创建或删除(详见 [[bot.permission.faq]])。
## 通知/提醒类
- `system-msg@bot.system`「系统消息」:系统公告、登录提醒、版本通知
- `task-alert@bot.system`「任务提醒」:任务被分配、截止时间临近、状态变更
- `todo-alert@bot.system`「待办提醒」:个人待办到期提醒
- `meeting-alert@bot.system`「会议通知」:会议邀请、开始/结束提醒(需会议插件)
- `okr-alert@bot.system`「OKR 提醒」OKR 周期推进、KR 更新(需 OKR 插件)
- `approval-alert@bot.system`「审批」:审批待办、结果通知(需审批插件)
## 互动/办公类
- `check-in@bot.system`「签到打卡」:单聊里发指令打卡,支持手动 / 定位 / 路由器 MAC / 人脸
- `anon-msg@bot.system`「匿名消息」:在他人单聊里以匿名身份发消息
- `bot-manager@bot.system`「机器人管理」:用 `/list` `/newbot` `/setname` 等斜杠指令管理自建机器人
## AI 对话类
- `ai-openai@bot.system`「ChatGPT」
- `ai-claude@bot.system`「Claude」
- `ai-deepseek@bot.system`「DeepSeek」
- `ai-gemini@bot.system`「Gemini」
- `ai-grok@bot.system`「Grok」
- `ai-ollama@bot.system`「Ollama」
- `ai-zhipu@bot.system`「智谱清言」
- `ai-qianwen@bot.system`「通义千问」
- `ai-wenxin@bot.system`「文心一言」
AI 机器人都需要在「系统设置 → AI 设置」里填 API Key 才能启用,未配置则报「机器人未启用」。
## 不支持
- 系统机器人的指令、行为由后端写死,无法自定义回复逻辑
- AI 机器人在群聊里必须 @ 才回复;私聊不需要 @

View File

@ -0,0 +1,64 @@
---
id: bot.webhook.concept
title: 机器人 Webhook 接入
type: concept
feature: bot
scope: end-user
locale: zh
aliases:
- webhook
- 机器人回调
- 外部系统接入机器人
- 机器人事件订阅
- 怎么让机器人自动回复
- bot 推送地址
related_tools: []
related_pages: [application]
prerequisites:
- 已创建用户机器人([[bot.create.howto]]
negative:
- webhook_url 必须以 `http://``https://` 开头,否则不发送
- URL 长度最大 255 字符
- 调用超时 30 秒,超时不重试
- 用户自建机器人收到斜杠开头(`/...`)的消息直接忽略,不会触发 webhook
last_verified: v1.7.90
---
# 机器人 Webhook 接入
## 定义
Webhook 是把「机器人收到的事件」用 HTTP POST 推到外部服务的地址。外部服务回 `{"code":200,"message":"..."}`DooTask 会把 `message` 作为机器人的文本回复发回会话。
## 可订阅事件
后端常量见 `App\Models\UserBot`
| 事件 key | 触发时机 |
|---|---|
| `message` | 机器人收到消息(单聊任意消息;群聊需 @ 机器人) |
| `dialog_open` | 用户首次打开和机器人的会话 |
| `member_join` | 群聊里机器人或他人加入 |
| `member_leave` | 群聊里成员离开 |
不勾任何事件时默认按 `[message]` 处理(参考 `normalizeWebhookEvents`)。
## 请求体字段
`event = message` 时主要字段:
- `event`: `message`
- `text`: 用户的纯文本指令
- `reply_text`: 若是引用回复,被引用消息的文本
- `token`: 机器人当次有效的 API token可调 DooTask API 代发消息
- `dialog_id` / `dialog_type` / `session_id`
- `msg_id` / `msg_uid` / `mention`(是否被 @
- `bot_uid`: 机器人 userid
- `msg_user`: 发送方信息userid / email / nickname / 临时 token
- `extras`: JSON 字符串,含 `timestamp`
- `version`: 当前 DooTask 版本
## 设置
在「我的机器人」编辑面板填 `webhook_url` 并勾事件;或单聊「机器人管理」里 `/webhook <bot_id> <url>`
## 不支持
- 不支持鉴权签名(如 HMAC请用 HTTPS + 服务端校验 `token`
- 不支持调用失败重试 / 死信队列;失败仅在后端 info 日志记录
- 不能区分 webhook 收到的消息是来自哪个具体群成员之外的额外字段

View File

@ -0,0 +1,54 @@
---
id: calendar.allday.concept
title: 日历的全天事件 vs 定时事件
type: concept
feature: calendar
scope: end-user
locale: zh
aliases:
- 全天事件
- 定时事件
- 跨天任务
- 日历事件类型
- 任务跨多天日历
related_tools: [list_tasks]
related_pages: [calendar]
prerequisites: []
negative:
- 没有任务 type 字段区分,全天 / 定时是按时间范围**自动判定**
- 任务的 end_at 含具体时分时被识别为定时事件
- 跨多天的任务在月视图渲染为横条,在周 / 日视图同样横跨
last_verified: v1.7.90
---
# 日历的全天事件 vs 定时事件
## 定义
DooTask 日历把每个任务自动分类成「全天」或「定时」,规则只看任务的 start_at / end_at 时间范围,没有独立的 `is_allday` 标签:
- **全天事件**`start_at <= 今天 00:01``end_at >= 今天 23:59`(或更长)
- **定时事件**start_at / end_at 都在同一天内的具体时段
## 渲染差异
| 类型 | 月视图 | 周视图 | 日视图 |
|---|---|---|---|
| 全天 | 单元格顶部横条 | 顶部全天条带 | 顶部全天条 |
| 定时 | 显示时段标记 | 纵向时段块 | 纵向时段块 |
## 怎么把任务变成全天
- 创建 / 编辑任务时只填日期(不填时分)→ 默认 end_at 设为当天 23:59:59
- 跨多天的任务自然变全天(横跨多个单元格)
## 怎么把任务变成定时
- 编辑 end_at 时填具体时分(如 14:00-15:30
- 周 / 日视图能看到准确的时段块
## 跨多天任务
- 一个任务 start_at 在周一、end_at 在周三 → 在月视图横跨 3 格
- 在周视图顶部连续横条
- 在日视图每一天都看到这条事件
## 不支持
- 任务无独立 `is_allday` 字段,全靠时间范围推断
- 编辑日历事件时不能切换"按全天 / 定时"
- 不能给单个任务设"每天某时段重复"

View File

@ -0,0 +1,52 @@
---
id: calendar.edit.concept
title: 在日历事件上能做什么操作
type: concept
feature: calendar
scope: end-user
locale: zh
aliases:
- 日历事件点击
- 点日历任务怎么办
- 日历任务详情
- 日历改任务
- 日历事件编辑
related_tools: [update_task, complete_task, delete_task]
related_pages: [calendar, task_detail]
prerequisites: []
negative:
- 日历上无独立的"事件编辑"窗口,所有改动走任务详情页
- 无法在日历直接添加协作者、改可见性等非时间字段
- 移动端日历仅查看([[calendar.mobile.faq]]
last_verified: v1.7.90
---
# 在日历事件上能做什么操作
## 是什么
日历显示的"事件"本质是 ProjectTask 任务的时间投影([[calendar.concept]])。点击事件 / 拖动 / 右键的所有操作最终都改 ProjectTask 字段。
## 桌面端操作矩阵
| 操作 | 做法 | 改的字段 |
|---|---|---|
| 看任务详情 | 单击事件块 | 无(跳转任务详情页) |
| 改 end_at 时间 | 拖动事件块([[calendar.drag.howto]] | end_at / start_at |
| 改 duration | 拖事件边缘 | start_at 或 end_at |
| 完成任务 | 进任务详情勾选完成([[task.complete.howto]] | complete_at |
| 删除任务 | 进任务详情删除([[task.delete-restore.howto]] | deleted_at |
| 改其他字段(描述 / 优先级 / 标签) | 进任务详情编辑([[task.edit.howto.basic]] | 各字段 |
## 移动端操作矩阵
- 单击事件 → 跳转任务详情
- 所有编辑都在任务详情里做
- 拖动 / 右键 / 改 duration 都不支持
## 创建新事件
- 在日历空白处单击 / 拖框 → 详见 [[calendar.create.howto]]
- 创建的是带时间的任务,不是独立事件
## 不支持
- 日历事件无独立的"右键菜单"提供任务字段一键编辑
- 不能在日历"复制事件"为下一周(用 [[task.recurring.howto]] 循环代替)
- 不能给日历事件单独打 calendar 标签(任务标签是 [[task.field.tag.concept]]

View File

@ -0,0 +1,53 @@
---
id: calendar.filter.concept
title: 日历的过滤维度(按项目分色)
type: concept
feature: calendar
scope: end-user
locale: zh
aliases:
- 日历筛选
- 日历按项目
- 日历分组
- 项目颜色日历
- 怎么筛日历事件
related_tools: [list_tasks, list_projects]
related_pages: [calendar]
prerequisites: []
negative:
- 日历无显式"项目筛选"按钮,仅按 calendarId项目 id分组着色
- 不能"只看 X 项目,隐藏其他"
- 不能按负责人、优先级、标签过滤事件
last_verified: v1.7.90
---
# 日历的过滤维度(按项目分色)
## 定义
DooTask 日历前端把每个任务的 `project_id` 作为 `calendarId`,按项目分组并赋不同颜色,让用户视觉上区分不同项目的任务。这是日历能做的"过滤 / 分组"上限。
## 颜色规则
- 每个项目自动分配一个颜色(按 project_id 散列)
- 同项目所有任务共用一个颜色
- 颜色不直接用 [[task.field.priority.concept]] 的 p_color 或 [[task.field.color.concept]] 的 color
- 仅在日历视图独立设色,不影响列表 / 看板
## 显式筛选(不支持)
| 维度 | 是否支持 | 替代 |
|---|---|---|
| 按项目 | 部分(仅着色) | 进项目详情看 |
| 按负责人 | ✗ | 仪表盘 / 项目页 |
| 按优先级 | ✗ | 项目列表 |
| 按标签 | ✗ | 项目列表 |
| 按是否完成 | ✗(默认隐藏完成) | 任务列表 |
| 按工作流状态 | ✗ | 项目工作流视图 |
## 想做"只看 X 项目的日历"
- 进项目详情 → 切到日历视图(每个项目有自己的日历视图)
- 比全局日历的筛选粒度更细
- 但项目级日历也仅显示该项目的任务
## 不支持
- 日历无显式筛选 UI
- 不能保存"筛选预设"(如"只看紧急任务"
- 不能按用户标签 / 部门做日历视图

View File

@ -0,0 +1,53 @@
---
id: calendar.concept
title: 日历是什么 / 数据源
type: concept
feature: calendar
scope: end-user
locale: zh
aliases:
- 日历是什么
- 日历显示什么
- 日历数据从哪
- 日历来自任务吗
- 任务怎么变日历事件
related_tools: [list_tasks]
related_pages: [calendar]
prerequisites: []
negative:
- 日历**没有独立的事件表**,全部从 ProjectTask 派生
- 不显示会议Meeting、签到、报告仅显示任务
- 仅显示当前用户作为负责人owner=1的任务不显示协作任务
last_verified: v1.7.90
---
# 日历是什么 / 数据源
## 定义
DooTask 日历是把 [[task.field.deadline.concept]] 有 end_at 的任务按时间轴铺到月 / 周 / 日格子里的展示视图。它**没有独立的 schedule / event 表**,所有事件完全来自 `ProjectTask` 表,按 `start_at``end_at` 字段绘制。
## 数据规则
- 数据源ProjectTask 表
- 必备字段:`end_at` 非空(没 end_at 的任务不会出现在日历)
- 关系:`ProjectTaskUser.owner = 1` 命中当前用户
- 状态:`archived_at IS NULL`(已归档不显示)
- 范围:按当前可视时间范围 `rangeTime` 查询,切换月 / 周 / 日时自动调整
## 事件分类
| 类型 | 判断规则 | 显示样式 |
|---|---|---|
| 全天 | start_at <= 今天 00:01 且 end_at >= 今天 23:59 | 顶部横条 |
| 定时 | start_at / end_at 在同一天内的具体时段 | 时间段块 |
详见 [[calendar.allday.concept]]。
## 与其他模块的关系
- **仪表盘**[[dashboard.concept]]):列表形式,按今日 / 超期 / 待完成分类,不显示位置
- **任务列表**:按列表展示,可看已完成 / 已归档
- **日历**:按时间格展示,仅未归档 + 有 end_at + 我是负责人
## 不支持
- 没有独立日历事件(必须先建任务,详见 [[calendar.create.howto]]
- 不能订阅他人 / 部门日历
- 不能 iCal 导出 / 外部日历同步
- 详见 [[calendar.ical.faq]]

View File

@ -0,0 +1,55 @@
---
id: calendar.task.concept
title: 日历只显示「我作为负责人的任务」
type: concept
feature: calendar
scope: end-user
locale: zh
aliases:
- 日历看不到协作任务
- 日历只有自己负责的
- 日历不显示别人安排
- 协作任务在哪
- 日历过滤范围
related_tools: [list_tasks]
related_pages: [calendar]
prerequisites: []
negative:
- 日历不显示 ProjectTaskUser.owner=0协作 / assist的任务
- 想看协作任务请到 [[dashboard.assist.howto]]
- 不能切换"显示所有项目任务包括别人的"
last_verified: v1.7.90
---
# 日历只显示「我作为负责人的任务」
## 定义
DooTask 日历的过滤规则相当窄:仅展示 `ProjectTaskUser.owner = 1` 命中当前用户的、有 end_at 的、未归档的任务。所以**协作的任务不会出现在日历上**。这是设计:日历是"我自己要安排时间做什么"的视图,避免被他人的任务挤满。
## 不显示的内容
| 类型 | 为什么不显示 |
|---|---|
| 我是协作者assist的任务 | owner=1 过滤掉 |
| 我没设 end_at 的任务 | end_at 是日历定位依据 |
| 已归档的任务 | archived_at 过滤 |
| 已完成的任务 | 大部分场景仅显示未完成(视前端配置) |
| 别人的任务 | 没在 ProjectTaskUser 命中我 |
| 会议Meeting | 不是 ProjectTask |
| 签到 / 报告 | 不是 ProjectTask |
详见 [[calendar.meeting-not-shown.faq]]。
## 怎么看协作任务
- 仪表盘「[[dashboard.assist.howto]]」分组
- 任务列表设过滤器「协作的任务」
- 项目详情页所有视图都能看到(不分负责人 / 协作)
## 怎么让任务出现在日历
1. 你必须是该任务负责人([[task.field.owner-assist.concept]] owner=1
2. 任务必须有 end_at 字段([[task.field.deadline.concept]]
3. 任务未归档、未完成(默认前端配置)
## 不支持
- 不能切换"也显示协作任务"
- 不能给团队成员的日历做合并视图
- 不能看别人日历

View File

@ -0,0 +1,51 @@
---
id: calendar.timezone.concept
title: 日历的时区处理
type: concept
feature: calendar
scope: end-user
locale: zh
aliases:
- 日历时区
- 跨时区任务
- 不同地区看任务时间
- 时区不一样任务时间
- 国际团队日历
related_tools: [list_tasks]
related_pages: [calendar, user_settings]
prerequisites: []
negative:
- 任务数据库无时区字段,时间按 UTC 存储
- 日历显示按用户当前浏览器的本地时区
- 同一任务在不同时区用户看到的时段会差几小时
last_verified: v1.7.90
---
# 日历的时区处理
## 定义
DooTask 任务的 `start_at` / `end_at` 在数据库**统一按 UTC 存储**,但前端展示时按浏览器的本地时区呈现。日历组件也按这个规则渲染。所以**跨时区的团队成员看到的同一任务时段可能不一样**,但 UTC 真值一致。
## 显示规则
- 浏览器自动识别本地时区
- 日历事件块按 `(start_at UTC) → 本地时区` 的转换显示
- 日 / 周视图的时段标尺也按本地时区
- 用户切换电脑时区 / 出差 → 日历事件位置相应平移
## 时区选择器
- 日历组件支持显示时区切换器(`timezonesCollapsed=false`
- 在桌面端日历视图右上角可展开切换显示时区
- 切换显示时区只影响**前端展示**,不修改任务的 UTC 字段
## 跨时区团队的注意事项
1. 北京同事建一个"今天 09:00"的任务UTC 01:00
2. 巴黎同事UTC+1看到"今天 02:00"
3. 旧金山同事UTC-7看到"今天前一天 18:00"
如果团队都用同一约定时区(如 UTC+8任务时间会一致。
## 不支持
- 任务无 timezone 字段(不能为单个任务指定时区)
- 不能给"全公司"设固定时区
- 不能给日历事件标注"按某时区显示"
- 不支持时区随项目 / 用户偏好设置自动切换

View File

@ -0,0 +1,39 @@
---
id: checkin.face.concept
title: 人脸签到原理与依赖
type: concept
feature: checkin
scope: end-user
locale: zh
aliases:
- 人脸识别原理
- face 插件
- 人脸签到依赖
- 刷脸打卡怎么工作的
related_tools: []
related_pages: []
prerequisites:
- 应用市场已安装 face 插件
last_verified: v1.7.90
---
# 人脸签到原理与依赖
## 定义
人脸签到是 DooTask 签到的一种方式,依赖独立的 **face 插件**提供人脸识别后端服务(容器名 `face`,内部端口 7788。主程序只负责存储人脸图片地址 (`UserCheckinFace.faceimg`) 和触发签到记录写入,**所有特征提取、比对都由 face 插件完成**
## 工作流程
1. 成员在签到设置上传人脸图片
2. 主程序把图片 base64 后 POST 到 `http://face:7788/user`,参数含 `enrollid`(成员 ID`name`(昵称)、`backupnum=50`
3. face 容器把特征存入设备库
4. 现场人脸识别一体机扫到该成员 → 调主程序 API 写一条 `UserCheckinRecord`
## 关键依赖
- **face 插件**:人脸识别引擎容器,未安装会抛 `Apps::isInstalledThrow('face')` 异常
- **人脸识别一体机硬件**:插件 README 注明「需配合指定硬件设备使用」
- **管理员开关**:「签到方式」勾选「人脸签到」+「允许修改」开启「允许成员上传人脸图片」
## 不支持
- 主程序自带的浏览器 / WebRTC 摄像头不能直接当人脸打卡器
- face 插件镜像较大(约 15MB+),首次安装下载较慢
- 删除成员或卸载插件后face 容器内的特征不会自动清空,需要管理员手动删除

View File

@ -0,0 +1,46 @@
---
id: checkin.concept
title: 签到打卡是什么
type: concept
feature: checkin
scope: end-user
locale: zh
aliases:
- 签到打卡
- 上下班打卡
- 考勤
- DooTask 签到
- 打卡功能
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 签到不支持节假日 / 调休的精细配置,只识别基础节假日(自动跳过提醒)
- 不是 KPI / 工资系统,仅记录打卡时间,不算迟到 / 早退分数
- 数据无法跨企业 / 跨实例同步
last_verified: v1.7.90
---
# 签到打卡是什么
## 定义
签到打卡是 DooTask 内置的考勤记录功能,用于成员每天上下班打卡。打卡数据存到 `UserCheckinRecord` 表,按天聚合多次打卡时间,并自动切分为「上班 / 下班」时段。功能由系统管理员在「签到设置」全局开关,开启后所有成员都可参与。
## 支持的签到方式
DooTask 提供 4 种签到方式,管理员可在「签到设置 → 签到方式」勾选启用:
- **人脸签到face**:通过外接人脸识别设备扫脸,需安装 [[checkin.plugin.concept]] 中的 face 插件
- **WiFi 签到auto**办公网路由器OpenWrt定时上报 MAC落到办公网即自动签到详见 [[checkin.wifi.howto]]
- **定位签到locat**:移动 App 在「签到机器人」对话里发送位置,落在允许半径内打卡
- **手动签到manual**:在「签到机器人」对话框输入指令打卡
## 涉及的数据
- `UserCheckinRecord`:每天每次打卡记录(含时间数组)
- `UserCheckinMac`:成员的 MAC 地址(最多 3 个)
- `UserCheckinFace`:成员的人脸图片(仅 1 张)
- `checkinSetting`:系统签到全局参数
## 相关概念
- 签到规则与配置:[[checkin.rule.concept]]
- 签到提醒机制:[[checkin.remind.concept]]
- 是否是插件:[[checkin.plugin.concept]]

View File

@ -0,0 +1,41 @@
---
id: checkin.plugin.concept
title: 签到是插件还是内置功能
type: concept
feature: checkin
scope: end-user
locale: zh
aliases:
- 签到是插件吗
- 签到要装吗
- checkin 插件
- 人脸签到是插件吗
- 签到要不要安装
related_tools: []
related_pages: []
prerequisites: []
negative:
- 应用市场没有名为「checkin」或「签到」的独立插件
- 卸载 face 插件不会影响 WiFi / 定位 / 手动签到
last_verified: v1.7.90
---
# 签到是插件还是内置功能
## 结论
签到打卡的**主体功能是 DooTask 内置的**,不需要单独安装插件。模型 (`UserCheckinRecord` / `UserCheckinMac` / `UserCheckinFace`)、签到机器人 (`check-in@bot.system`)、提醒任务 (`CheckinRemindTask`)、设置接口都打包在主程序里,开箱即用。
## 各签到方式的依赖
- **手动签到manual**:无依赖,主程序自带
- **WiFi 签到auto**:无插件依赖,只需要管理员在 OpenWrt 路由器执行一键安装脚本
- **定位签到locat**:无插件依赖,但需要管理员在「签到设置」配置百度 / 高德 / 腾讯地图 Key且只支持移动端 App
- **人脸签到face****需要安装 face 插件**应用市场搜「Face check-in」并配套人脸识别硬件设备
## 关联应用市场
- `face` 插件:人脸识别后端服务,未装时人脸上传 / 现场刷脸都会失败,详见 [[checkin.face.concept]]
- `approve` 插件:影响提醒筛选——已请假 / 外出审批的成员不会收到缺卡提醒
## 不支持
- 没有第三方「考勤」插件取代内置签到
- 没法只装签到不装签到机器人(机器人是系统自动创建的)
- 主程序版本升级后签到能力随版本走,无独立版本号

View File

@ -0,0 +1,46 @@
---
id: checkin.remind.concept
title: 签到提醒怎么发的
type: concept
feature: checkin
scope: end-user
locale: zh
aliases:
- 签到提醒
- 打卡提醒
- 上班提醒
- 缺卡提醒
- 为什么收不到打卡提醒
- 谁会收到签到提醒
related_tools: []
related_pages: []
prerequisites: []
negative:
- 不是所有人都会收到提醒,新成员入职 3 天内无打卡记录不会被提醒
- 法定节假日不会发提醒(主程序内置节假日表自动跳过)
- 已请假 / 外出审批通过的成员不会被提醒
last_verified: v1.7.90
---
# 签到提醒怎么发的
## 定义
签到提醒是由 `CheckinRemindTask` 异步任务通过签到机器人 (`check-in@bot.system`) 在群外私聊推送的两类消息,用于催员工按时打卡。任务每分钟跑一次,只在触发窗口内的当日推一次。
## 两种提醒
- **打卡提醒in**:上班时间前的「快到上班时间了,别忘了打卡哦」,提前分钟数由「签到打卡提醒」字段控制(默认 5 分钟)
- **缺卡提醒exceed**:上班时间过去后还没打卡发的「上班时间到了,你还没有打卡哦」,延后分钟数由「签到缺卡提醒」字段控制(默认 10 分钟)
提前 / 延后值都可在管理员「签到设置」里改,设为 0 则关闭对应提醒。
## 提醒对象筛选
任务按下面规则逐个判断每位在职非机器人成员,全部命中才推送:
- 当天还没有 `UserCheckinRecord`(已打卡的不推)
- 过去 3 天内有过签到记录(排除新人 / 长期不打卡者)
- 没有请假 / 外出的审批正在生效(依赖 approve 插件的 `ApproveProcInstHistory::userIsLeave`
## 关联设置
- 「签到设置 → 功能开启」必须为「开启」
- 「签到时间」第一段被视为上班时间,提醒以它为基准
- 节假日识别由 `Extranet::isHoliday()` 查询,命中节假日全员不推

View File

@ -0,0 +1,49 @@
---
id: checkin.rule.concept
title: 签到规则与全局配置
type: concept
feature: checkin
scope: admin
locale: zh
aliases:
- 签到规则
- 签到全局设置
- 上下班时间设置
- 签到方式配置
- 签到时间窗口
- 管理员怎么开签到
related_tools: []
related_pages: []
prerequisites:
- 需要系统管理员身份
last_verified: v1.7.90
---
# 签到规则与全局配置
## 入口
桌面端 / 移动端:「应用」→「签到打卡」→ 抽屉右上角「签到设置」(仅管理员可见)
也可走:管理后台 → 系统设置 → 签到
## 主要字段
配置存储在 `checkinSetting` 系统设置项,关键字段:
- **功能开启open**`open` / `close`,关闭后整套签到能力对所有人停用
- **签到时间time**`[上班时间, 下班时间]`,如 `["09:00", "18:00"]`,提醒任务基于这两个时间点
- **最早可提前advance/ 最晚可延后delay**:上下班时间前后允许签到的分钟数,超过会被拒
- **签到打卡提醒remindin**:上班前 N 分钟推「打卡提醒」
- **签到缺卡提醒remindexceed**:上班后 N 分钟推「缺卡提醒」
- **签到方式modes**:勾选启用的方式列表,从 `face` / `auto`WiFi/ `locat`(定位)/ `manual`(手动)中多选
- **允许修改edit / face_upload**:是否允许成员自行改 MAC / 上传人脸
## 子方式额外配置
- **人脸签到**:签到备注 + 重复打卡提醒开关
- **WiFi 签到**:路由器一键安装命令
- **定位签到**:百度 / 高德 / 腾讯三选一的地图 Key + 允许签到坐标 + 半径50-5000 米)
- **手动签到**:签到备注
## 不支持
- 不支持按部门 / 角色配置不同上下班时间,全员一套
- 不支持配置自定义节假日;节假日由内置接口 `Extranet::isHoliday()` 判断
- 不支持多班次 / 排班
- 改完设置即时生效,但已生成的签到记录不会回溯调整

View File

@ -0,0 +1,54 @@
---
id: compliance.concept
title: DooTask 合规能力概览
type: concept
feature: compliance
scope: admin
locale: zh
aliases:
- 合规
- 合规能力
- GDPR 支持
- 数据合规
- 隐私合规
- 数据保留
related_tools: []
related_pages: []
prerequisites:
- 多数动作需要系统管理员权限
negative:
- DooTask 主程序没有专门的「合规设置」集中页面
- 不内置 GDPR DSR 工单系统,需要管理员人工响应
- 不支持自动数据保留周期清理(需手动或脚本)
- 没有自动获取用户同意cookie banner 等)的开关
last_verified: v1.7.90
---
# DooTask 合规能力概览
## 定义
DooTask 主程序没有把「合规」做成单独菜单,但通过多个分散功能覆盖了数据合规、用户隐私、内容审核等场景。这里把和合规相关的现有能力索引到一起,方便管理员对照内部合规要求逐项检查。
## 涉及的现有能力
| 维度 | 现有手段 | 主程序入口 |
|---|---|---|
| 内容审核 | 用户举报 + 管理员处理 | [[abuse-report.concept]] |
| 数据导出(数据可携性) | 任务 / 审批 / 签到 Excel 导出 | [[data-export.concept]] |
| 账号下线 / 数据删除 | 团队管理 → 删除成员Apps 触发 `user_offboard` hook | 团队管理 |
| 审计 | 操作日志(部分模块)+ 审批历史 | 模块自带 |
| 访问控制 | 项目 / 任务 / 系统三级权限 + LDAP | [[role-permission.permission-denied.faq]] |
| 数据本地化 | 全私有部署 + Docker所有数据库在自有服务器 | 部署阶段 |
| 加密传输 | HTTPS需自配 Nginx 证书) | 部署阶段 |
## 删除请求GDPR 第 17 条)
用户被删除时,主程序会调用 `Apps::dispatchUserHook($user, 'user_offboard')`,把删除事件以 `event_type=delete` 推送到 appstore 微服务,由各插件自行清理用户数据。具体执行情况依赖每个插件实现。
## 不支持
- 没有内置的合规配置面板
- 没有自动数据保留策略(你需要外部脚本定期清理)
- 没有内置数据出口审计(数据导出动作未单独留审计日志)
- 不内置 cookie 同意弹窗、隐私政策签署、DSR 工单等模块
## 相关
- 合规配置项细节:[[compliance.howto]]
- 入口与责任人:[[compliance.entry.menu-map]]

View File

@ -0,0 +1,56 @@
---
id: dashboard.concept
title: 仪表盘是什么 / 显示哪些卡片
type: concept
feature: dashboard
scope: end-user
locale: zh
aliases:
- 仪表盘是什么
- 仪表盘有什么
- 仪表盘卡片
- 仪表盘有哪些卡片
- 仪表盘显示什么
- 仪表盘显示哪些卡片
- 个人工作台
related_tools: [list_tasks]
related_pages: [dashboard]
prerequisites: []
negative:
- 仪表盘只显示「我作为负责人 owner=1」的任务协作任务在专门的分组
- 仪表盘不显示项目进度 / 团队整体数据,仅个人视角
- 仪表盘不显示已完成任务(默认)
last_verified: v1.7.90
---
# 仪表盘是什么 / 显示哪些卡片
## 定义
DooTask 仪表盘Dashboard是当前用户的个人工作台回答「今天我要做什么」。仪表盘由顶部 3 张数字卡片 + 下方 4 个分组列表卡片组成,从前端 store 的 `dashboardTask``assistTask` getter 实时计算,按时间分类显示当前用户名下的未完成任务。
## 顶部 3 张数字卡片
| 卡片 | 含义 |
|---|---|
| 今日到期 | end_at 在今天范围内、未完成、未归档的任务数 |
| 超期任务 | end_at 已过今天 23:59:59 但未完成的任务数 |
| 待完成 | 没设 end_at 或 end_at 还未到的未完成任务数 |
## 下方 4 张分组卡片
1. **今日到期** 卡片 — 今天必须完成的任务
2. **超期任务** 卡片 — 需要立刻处理或调整截止时间的任务
3. **待完成** 卡片 — 后续要做的任务(含无截止时间)
4. **协助的任务** 卡片 — 我作为 [[task.field.owner-assist.concept]] 中协作者assist的任务独立于上面三组
## 数据来源
- 全部从 ProjectTask 表派生([[task.field.deadline.concept]] 的 end_at 字段)
- 仅算 owner=1 命中当前用户的(前 3 组assist 命中的(第 4 组)
- 已归档([[task.archive.howto]]/ 已完成([[task.complete.howto]])的任务不显示
## 与「项目统计」的区别
- 仪表盘:个人视角,全部项目
- [[project.statistics.concept]]:单项目视角,整项目
## 不支持
- 不显示团队 / 部门维度的汇总
- 不能切换看别人的仪表盘
- 不能增加自定义卡片

View File

@ -0,0 +1,52 @@
---
id: data-export.concept
title: 数据导出概览
type: concept
feature: data-export
scope: admin
locale: zh
aliases:
- 数据导出是什么
- 导出 Excel
- 导出报表
- 后台能导出哪些数据
- 管理员导出
related_tools: []
related_pages: [application]
prerequisites:
- 需要系统管理员权限userIsAdmin
negative:
- 导出文件不直接下载到浏览器,而是由系统机器人发到管理员的私聊
- 不支持普通成员自助导出,全部入口仅管理员可见
- 不支持导出原始 JSON / 数据库表,只导出预设字段的 Excel
last_verified: v1.7.90
---
# 数据导出概览
## 定义
数据导出是 DooTask 内置的管理员功能,把系统数据按预设字段整理成 Excel 文件,通过系统机器人(`system-msg`)异步发到管理员的私聊。需要系统管理员(`userIsAdmin`)才能触发。
## 支持的导出类型
后台「数据导出」菜单实际提供 4 类导出,分别对应不同的 API
| 名称 | API | 说明 |
|---|---|---|
| 任务统计 | `api/project/task/export` | 按成员 + 时间段导出任务,含负责人/工时/状态 |
| 超期任务 | `api/project/task/exportoverdue` | 全系统未完成且已超期的任务 |
| 审批数据 | `api/approve/export` | 按流程分类 + 状态 + 时间段导出审批单 |
| 签到数据 | `api/system/checkin/export` | 按成员 + 日期段 + 时间段导出签到记录 |
## 关键属性
- **异步生成**:触发后立即返回,文件由 Swoole 协程后台生成(`go(...)`),完成后才发消息
- **下载链接限时**:消息中的下载链接(`api/.../down`)使用 `Down::cache_decode()` 解码,链接过期文件失效
- **机器人通知**:所有导出完成消息发送方都是 `system-msg` 系统机器人
- **范围限制**:每种导出都有自己的成员数 / 日期范围上限(通常单次 ≤ 35 天、≤ 50 个成员),具体在每个子类型的提示文案里说明
## 不支持
- 不支持导出工作报告、用户列表、项目列表(产品里未开放对应入口)
- 不支持自定义字段选择,导出列固定
- 不支持定时 / 周期性自动导出
## 入口
管理员侧入口和操作步骤见 [[data-export.entry.menu-map]]。

View File

@ -0,0 +1,53 @@
---
id: desktop-notify.concept
title: 桌面通知是什么
type: concept
feature: desktop-notify
scope: end-user
locale: zh
aliases:
- 桌面通知
- 桌面端通知
- 系统通知栏
- 弹窗通知
- 电脑右下角通知
- 任务栏闪烁
related_tools: []
related_pages: []
prerequisites:
- 使用 DooTask 桌面端Electron 客户端)或允许了浏览器通知的 Web 版
negative:
- Web 版需用户在浏览器对话框中点「允许」才能弹出通知,否则只能在 APP 内提示
- 桌面通知不走友盟,与 APP 推送是完全独立的通道
- 关闭 DooTask 进程后不会有通知(与移动端不同,没有后台守护服务)
last_verified: v1.7.90
---
# 桌面通知是什么
## 定义
桌面通知是 DooTask 桌面端Electron 客户端)和 Web 版在新消息到达时,通过操作系统原生通知 API 弹出的提示框。桌面端调用 `new Notification()`Node 端Web 版用浏览器 Notification API。
## 关键属性
- **触发**:新消息到达时由前端 `pages/manage.vue` 调用 `openNotification` 走 IPC 给主进程
- **内容**:标题(单聊=昵称 / 群聊=群名)、正文(消息预览)、图标(发送者头像)
- **快捷回复**:桌面端通知支持 hasReply=true 直接在通知框输入回复,回填到 DooTask
- **点击行为**:点通知会把主窗口拉前并打开对应会话
- **Dock 角标 / 任务栏**macOS 显示 Dock badge 数字Windows 任务栏闪烁,托盘可显示未读数
## 平台差异
| 系统 | 通知风格 | Dock/Tray |
|---|---|---|
| macOS | 通知中心 | Dock badge + 托盘 Title 文字 |
| Windows | 操作中心 | 任务栏闪烁(窗口失焦时) |
| Linux | libnotify | 仅通知 |
## 与其他通知的关系
- [[push-notice.concept]] 友盟推送只走移动 APP桌面端不参与
- [[email-notice.concept]] 邮件只用于汇总未读或系统验证,与桌面通知并行
- 浏览器 Web 版桌面通知由浏览器实现,关闭浏览器即失效
## 不支持
- 不支持自定义通知音效(用系统默认)
- 不支持自定义通知时长(受系统通知中心控制)
- 不能按会话单独配置桌面通知开关(要总开关或会话级免打扰 [[push-notice.silent.howto]]

View File

@ -0,0 +1,61 @@
---
id: desktop-notify.tray.concept
title: Dock 角标与任务栏
type: concept
feature: desktop-notify
scope: end-user
locale: zh
aliases:
- Dock 角标
- macOS 红点
- 任务栏未读
- 托盘红点
- 任务栏闪烁
- 未读数字
- Tray
- badge
related_tools: []
related_pages: []
prerequisites:
- 使用 DooTask 桌面端Electron
negative:
- 任务栏闪烁只在 Windows 平台 + 窗口失焦时生效macOS 不闪
- Dock badge 只在 macOS 显示Windows 不显示数字角标
- Linux 通常没有 Dock badge 与任务栏闪烁(依桌面环境而异)
last_verified: v1.7.90
---
# Dock 角标与任务栏
## 定义
DooTask 桌面端通过 Electron 主进程的 IPC 通道 `setDockBadge` 在 macOS Dock、Windows 任务栏、系统托盘上同步展示未读消息数,让用户在不切回 DooTask 窗口时也能感知到「有未读」。
## 平台行为
| 平台 | 表现 | 数据来源 |
|---|---|---|
| macOS | Dock 图标右上角红色数字 | `app.dock.setBadge(text)` |
| macOS | 托盘图标右侧显示未读数文字 | `mainTray.setTitle(text)` |
| Windows | 窗口失焦时任务栏图标闪烁(黄色) | `mainWindow.flashFrame(true)` |
| Windows | 系统托盘有图标,右键菜单含显示/退出 | Tray + contextMenu |
| Linux | 无 Dock badge / 闪烁;仅托盘 | 通常依赖桌面环境 |
## 数字含义
未读数 = 当前用户所有「未读 + 非静默」的消息条数;按下面规则计算:
- 已读会话不计
- 标记为免打扰silence=1的会话不计
- 系统静默消息silence=1不计
- 多设备共享同一个未读数(后端聚合)
## 清零时机
- 打开一个会话并阅读到底 → 该会话未读清零Dock badge 减
- 全部会话已读后 Dock badge 完全消失
- macOS Dock 上的红点消失 = 0Windows 任务栏闪烁会在窗口被点击聚焦时自动停止
## 托盘点击行为
- macOS / Windows单击托盘图标 → 显示主窗口(被最小化或隐藏时拉前)
- Windows 托盘还可右键 → 「显示」/「退出」
## 跟桌面通知的区别
- 桌面通知是**一次性**事件,弹出后消失;详见 [[desktop-notify.concept]]
- Dock badge / 任务栏是**持续状态**,反映当前未读总数
- 两者数据源相同但展示方式独立:通知被系统抑制不影响角标更新

View File

@ -0,0 +1,55 @@
---
id: drawio.concept
title: 流程图drawio是什么
type: concept
feature: drawio
scope: end-user
locale: zh
aliases:
- drawio
- 流程图
- 画流程图
- draw.io
- DooTask 流程图
- UML 图
related_tools: []
related_pages: [file]
prerequisites:
- 应用市场已安装 drawio 插件
negative:
- 流程图编辑能力不是主程序内置,未装 drawio 插件无法新建 `drawio` 类型文件
- 不支持把 drawio 图形直接嵌入到任务详情/讨论消息中(只能作为文件链接发送)
- 大图(千级节点)渲染会变慢,建议拆图
last_verified: v1.7.90
---
# 流程图drawio是什么
## 定义
流程图是 DooTask 文件系统中的一种文件类型(`type=drawio`用于绘制流程图、UML、ER 图、网络拓扑、思维导图等通用图形。它由独立的 drawio 插件提供编辑器(基于开源 jgraph/drawio主程序通过 iframe 嵌入 `drawio/webapp/index.html`,文件内容存到 DooTask 自己的文件库里。
## 关键属性
- **文件类型**`drawio`(与 `mind` 思维导图、`document` 文本、`word/excel/ppt` 平行)
- **存储位置**DooTask 文件系统(个人文件 / 项目文件 / 共享)
- **编辑器**:嵌入式 drawio 完整编辑器,含图形库、画布、属性面板
- **导出**:支持导出 PNG / PDF依赖插件内置的 export-server缺它则导出失效
- **历史版本**:随 DooTask 文件历史一同保存
## 支持的图形类型
- 流程图、泳道图
- UML 类图、时序图、用例图
- 网络拓扑、机柜图
- 实体关系ER
- 思维导图(更建议用专用的 minder 插件)
- 各类业务建模、原型草图
## 与其他文件类型的关系
- **vs minder 思维导图**思维导图专注放射结构节点编辑更轻drawio 是通用图形,自由度高
- **vs OnlyOffice Word/Excel**OnlyOffice 支持多人实时drawio 单人编辑
- **vs DooTask 内置 document**document 是富文本/Markdown没有矢量图形能力
## 相关
- 插件元信息:[[drawio.plugin.concept]]
- 创建流程图:[[drawio.create.howto]]
- 入口在哪:[[drawio.entry.menu-map]]
- 内置模板:[[drawio.template.concept]]

View File

@ -0,0 +1,53 @@
---
id: drawio.plugin.concept
title: drawio 插件元信息
type: concept
feature: drawio
scope: admin
locale: zh
aliases:
- drawio 插件
- 流程图插件
- 安装 drawio
- drawio 插件版本
- draw.io 集成
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 主程序不内置流程图编辑器,未装插件时新建文件菜单不会显示「图表」
- 插件体积大约 700MB下载较慢需要稳定网络
- 缺少同包的 export-server 时PNG/PDF 导出会失败
last_verified: v1.7.90
---
# drawio 插件元信息
## 定义
流程图能力由 `drawio` 插件提供(应用市场 app id 为 `drawio`,当前主版本 30.0.4,基于开源 jgraph/drawio。插件包内除了 drawio 前端,还包含一个 export-server镜像 `kuaifan/export-server`)负责把图形导出为 PNG/PDF。主程序仅以 iframe 加载 `drawio/webapp/index.html`,编辑器交互与文件保存全部跑在前端 + 主程序文件 API。
## 关键属性
- **作者**社区维护Community上游 https://www.drawio.com/
- **分类**:微应用(不在管理员应用区)
- **包大小**:约 700MB含 drawio 资源 + export-server 镜像)
- **运行形态**:静态资源 + 一个导出服务容器
- **关键依赖**`webapp/index.html``EXPORT_URL` 指向插件内的 `/drawio/export/`,删 export-server 会导致导出 PNG/PDF 失效
- **数据存储**:图形内容存到主程序文件表(`type=drawio`),不在插件容器单独存
## 安装与启用
1. 在应用市场管理员入口搜索「Drawio」或「流程图」
2. 点击安装,等待资源下载(约 700MB请通过「安装日志」查看进度
3. 安装完成后插件自动启用,文件新建菜单立即出现「图表」选项
## 卸载影响
- 卸载后菜单消失,原有 `drawio` 文件保留在文件库中但无法继续编辑/预览
- 重新安装后旧文件继续可用
## 已知限制
- 网络不畅时首次加载图形库较慢
- 离线环境需提前推到内网镜像源
## 相关
- 是什么:[[drawio.concept]]
- 入口在哪:[[drawio.entry.menu-map]]
- 内置模板:[[drawio.template.concept]]

View File

@ -0,0 +1,60 @@
---
id: drawio.template.concept
title: drawio 内置图形与模板
type: concept
feature: drawio
scope: end-user
locale: zh
aliases:
- drawio 模板
- drawio 图形库
- drawio 能画什么
- 流程图都有哪些类型
- drawio 支持的图形
related_tools: []
related_pages: [file]
prerequisites:
- 应用市场已安装 drawio 插件
negative:
- DooTask 不提供「业务模板市场」,新建图永远是空白画布
- 不能直接将 drawio 图形粘贴到 DooTask 任务详情或讨论消息
- 模板/形状无法跨公司账号共享(每个部署独立)
last_verified: v1.7.90
---
# drawio 内置图形与模板
## 定义
drawio 编辑器自带丰富的图形库Shape Libraries和绘图分类但 DooTask 集成时**不预置业务模板**。每次新建图都从空白画布开始,需要绘图者从图形库手动拖出元素或在 drawio 内的「File → New From Template」选择官方模板。
## 主要图形分类
drawio 左侧图形库可勾选启用以下分类(部分):
- 通用形状General
- 流程图Flowchart
- 泳道图Swimlanes
- UML类图、时序图、用例图、状态图、活动图
- 实体关系ER
- 网络与基础设施Cisco / AWS / Azure / GCP / Kubernetes 图标)
- 软件架构C4 模型)
- 业务流程BPMN
- 思维导图mockup
- 电气、布线、机柜
- 安卓/iOS 线框图
## 选用方式
- 编辑器左下角「More Shapes」勾选要启用的图形库
- 直接从左侧图形库拖元素到画布
- 通过菜单 `File → New From Template` 选择官方预设模板(流程示例、组织结构等)
## 与思维导图minder对比
- drawio 也能画思维导图,但灵活但偏「画」
- 专注思维导图推荐用专门的 [[minder.concept]] 插件,节点操作更顺手
## 不支持
- DooTask 主程序不预置业务模板(如审批流程模板等)
- 模板不能跨部署共享,只能依赖 drawio 上游内置的模板
- 不能上传自定义图形库到所有用户
## 相关
- 是什么:[[drawio.concept]]
- 创建:[[drawio.create.howto]]

View File

@ -0,0 +1,60 @@
---
id: electron-client.notify.concept
title: 桌面端原生通知
type: concept
feature: electron-client
scope: end-user
locale: zh
aliases:
- 桌面通知
- 桌面端推送
- 收不到通知
- 弹出通知
- 通知中心
- 桌面端消息提醒
related_tools: []
related_pages: []
prerequisites: []
negative:
- 桌面端通知不依赖浏览器权限,但需要系统级允许 DooTask 发通知
- 系统勿扰模式 / 专注模式开启时,不会弹出但会进通知中心
- 桌面端通知与移动端 UMENG 推送是两套独立通道,见 [[mobile-client.notify.concept]]
last_verified: v1.7.90
---
# 桌面端原生通知
## 定义
桌面端的「原生通知」是通过操作系统通知中心弹出的提示(macOS 通知中心 / Windows 操作中心),区别于网页端的浏览器通知。新消息、任务变更、AI 响应等事件会触发,即使 App 处于后台 / 最小化也能收到。
## 触发场景
- 新消息(私聊 / 群聊)
- 任务被分配 / @ 提醒
- 项目变更(状态、负责人)
- 截止提醒
- 系统通知(管理员推送)
## 系统授权
首次启动会向操作系统申请通知权限:
- **macOS**:系统设置 → 「通知」→ 找到「DooTask」→ 允许「允许通知」、选择「横幅」或「提醒」样式
- **Windows**:设置 → 「系统」→「通知」→ 找到「DooTask」→ 打开
未授权时 App 仍能收到消息,但不会弹系统通知,只会在 App 内显示红点 / 角标。
## 应用内开关
DooTask 内部还有一层免打扰开关:
- 个人设置 → 「通知」→ 开关「桌面通知」/「声音」
- 单个会话 / 项目 → 「免打扰」可单独静音
通知触发顺序:**系统权限 → 应用免打扰 → 单会话免打扰**,任一环节关闭都收不到。
## 通知样式
- 标题:消息来源(如群名、发送人)
- 正文:消息预览(开启「消息预览」时显示;关闭则只显示「您有一条新消息」)
- 点击通知:激活窗口并跳到对应会话 / 任务
## 不支持
- 不支持自定义通知音效(用系统默认)
- 不支持通知中折叠 / 分组(每条独立)
- 系统勿扰模式开启时不弹横幅,但仍写入通知中心
- 与浏览器 Notifications API 不通用,网页端单独管理

View File

@ -0,0 +1,53 @@
---
id: electron-client.concept
title: 桌面端是什么
type: concept
feature: electron-client
scope: end-user
locale: zh
aliases:
- 桌面端
- 桌面版
- 桌面客户端
- 客户端
- 装到电脑上
- Electron 端
- Mac 版
- Windows 版
related_tools: []
related_pages: []
prerequisites: []
negative:
- 桌面端不能改服务器地址,首次启动时录入,后续切换需在登录页注销重设
- 桌面端没有「独立内核更新」,功能更新依赖服务器后端 + 客户端版本同步
- 桌面端无法离线使用,所有数据走与网页端相同的 HTTP / WebSocket
last_verified: v1.7.90
---
# 桌面端是什么
## 定义
桌面端是 DooTask 的本地客户端,基于 Electron 38 打包,套壳一份 Chromium 内核 + 复用网页端代码,再补足系统集成能力(托盘、原生通知、全局快捷键、截图、下载管理、本地 MCP)。当前应用版本独立于服务器主程序版本,自带更新通道。
## 关键属性
- **跨平台**:macOS / Windows / Linux 三大桌面系统都有官方包
- **数据互通**:登录任意服务器后,数据与网页端、移动端完全一致
- **系统集成**:原生通知、托盘、全局快捷键、屏幕截图、下载管理器
- **多窗口 / 多 Tab**:支持把任务、文件、应用拖出独立窗口
- **本地 MCP**:内置 fastmcp 服务,供 AI 助手调本机能力
## 与网页端的差异
- 桌面端默认拦截外链,在内置浏览器打开;网页端走系统浏览器
- 桌面端有「关闭即托盘 / 退出」选项,网页端关掉就是关掉
- 桌面端能注册 `Cmd/Ctrl + I`(AI 助手)等系统级快捷键
- 桌面端有内置截图工具([[electron-client.shortcut.concept]])
## 何时选桌面端
- 长时间办公(挂在后台 / 收推送)
- 需要原生通知不丢消息
- 频繁用 AI 助手 / 快捷键
- 经常处理文件(下载管理更直观)
## 不支持
- 不支持移动端的触屏手势
- 桌面端窗口最小化默认不退出后端连接(`window-all-closed` 在 macOS 不退出)

View File

@ -0,0 +1,59 @@
---
id: electron-client.platforms.concept
title: 桌面端支持平台
type: concept
feature: electron-client
scope: end-user
locale: zh
aliases:
- 支持哪些系统
- 支持的操作系统
- macOS 能用吗
- Windows 能用吗
- Linux 能用吗
- M1 M2 能用吗
- ARM 版
- 苹果芯片
related_tools: []
related_pages: []
prerequisites: []
negative:
- 不提供 32 位 Windows 安装包(只有 x64 / arm64)
- macOS 13 之前的旧版可能因 Electron 38 不兼容无法启动
- Linux 版只打 deb / rpm,不打 snap / flatpak
last_verified: v1.7.90
---
# 桌面端支持平台
## 系统支持矩阵
| 系统 | 架构 | 包格式 | 说明 |
|---|---|---|---|
| macOS | x64(Intel) | dmg / zip / pkg | 推荐 macOS 13+ |
| macOS | arm64(Apple Silicon) | dmg / zip / pkg | M1 / M2 / M3 / M4 |
| macOS | universal | dmg / pkg | 通用包(同时兼容 Intel 和 Apple Silicon) |
| Windows | x64 | NSIS 安装包(.exe) | 推荐 Windows 10+ |
| Windows | arm64 | NSIS 安装包(.exe) | Surface Pro X 等 ARM 笔记本 |
| Linux | x64 | deb(Ubuntu/Debian) / rpm(RHEL/CentOS/Fedora) | 通过 Electron Forge 打包 |
## 安装包命名约定
官方发布的安装包名格式:`DooTask-v{version}-{os}-{arch}.{ext}`,例如:
- `DooTask-v1.7.90-mac-arm64.dmg`(Apple Silicon)
- `DooTask-v1.7.90-mac-x64.dmg`(Intel Mac)
- `DooTask-v1.7.90-mac-universal.pkg`(macOS 通用包)
- `DooTask-v1.7.90-win-x64.exe`(Windows 64 位)
- `DooTask-v1.7.90-win-arm64.exe`(Windows ARM)
## 如何选择对应包
- **Apple Silicon Mac**(M1/M2/M3/M4):选 `mac-arm64``mac-universal`
- **Intel Mac**:选 `mac-x64``mac-universal`
- **不确定 Mac 芯片**:点苹果菜单 → 「关于本机」查看「芯片」字段
- **Windows**:绝大多数选 `win-x64`;Surface Pro X 等 ARM 笔记本选 `win-arm64`
- **Linux**:Ubuntu / Debian 用 deb,CentOS / Fedora 用 rpm
## 不支持
- Windows 7 / 8 / 8.1(Electron 38 不再支持)
- 不支持 32 位 Windows(`ia32`)
- 不支持 macOS 12 及更早版本(部分系统 API 调用失败)
- 国产 ARM Linux(如龙芯、华为鲲鹏)无官方包,可自行 build

View File

@ -0,0 +1,61 @@
---
id: electron-client.proxy.concept
title: 桌面端代理设置
type: concept
feature: electron-client
scope: end-user
locale: zh
aliases:
- 桌面端代理
- 客户端代理
- 桌面端走代理
- HTTP 代理
- SOCKS 代理
- 公司代理
- VPN 设置
related_tools: []
related_pages: []
prerequisites: []
negative:
- 桌面端**没有内置代理设置面板**,默认跟随操作系统代理配置
- 不支持在 App 内直接填写代理服务器地址 / 鉴权
- 全代理 / 分流策略需要在系统或网络层面解决
last_verified: v1.7.90
---
# 桌面端代理设置
## 代理来源
DooTask 桌面端基于 Electron,网络请求默认遵循**操作系统的代理设置**(从 Chromium 继承)。客户端内**没有**独立的代理配置入口。代理生效路径:
- macOS:系统设置 → 「网络」→ 当前网络 → 「详细信息」→「代理」
- Windows:设置 → 「网络和 Internet」→「代理」
- Linux:多数发行版在「设置 → 网络 → 代理」
## 适用场景
- 内网部署需走公司 HTTP 代理出公网
- 通过 VPN 客户端访问私有 DooTask 实例
- 测试环境需经过 mitm / Charles 抓包(配 PAC 或全局代理)
## 启动参数(进阶)
Electron 支持启动时通过命令行参数注入代理。例如 macOS 终端启动:
```bash
open -a DooTask --args \
--proxy-server="http://192.168.1.10:8080" \
--proxy-bypass-list="*.internal.com"
```
Windows 在快捷方式属性里给 `dootask.exe` 加同样参数。这种方式属于绕过系统代理,生效优先级最高。
## 鉴权代理
需要用户名 / 密码的代理:
- 操作系统代理面板里录入凭证(macOS 钥匙串、Windows 凭据管理器)
- Chromium 弹出鉴权对话框时输入
## 故障排查
- 客户端打不开 / 登录失败,先确认浏览器(系统默认浏览器)能否打开 DooTask 网址
- 抓包检查请求是否经过预期代理
- 走代理后 WebSocket 连接可能被部分企业代理阻断,需让运维放通 `wss://`
## 不支持
- 客户端 UI 无代理配置项,无法在 App 内切换代理
- 不支持代理协议自动切换(SOCKS5 / HTTP / HTTPS)
- 不支持单 App 独立代理(全系统级)

View File

@ -0,0 +1,66 @@
---
id: electron-client.shortcut.concept
title: 桌面端全局快捷键
type: concept
feature: electron-client
scope: end-user
locale: zh
aliases:
- 桌面端快捷键
- 全局快捷键
- 桌面热键
- 截图快捷键
- AI 助手快捷键
- 怎么截图
- 新建任务快捷键
related_tools: []
related_pages: []
prerequisites: []
negative:
- 全局快捷键仅在 DooTask 进程运行中(含后台 / 托盘)生效,App 退出后不可用
- 快捷键冲突时(被其他软件占用)DooTask 注册会失败,无错误提示
- 部分快捷键(如截图)用户可自定义按键,默认未绑定
last_verified: v1.7.90
---
# 桌面端全局快捷键
## 快捷键总览
桌面端注册了一组系统级全局快捷键,在 App 处于后台 / 最小化 / 托盘时也能触发。下表 `Mod` 在 macOS 是 `Command`、Windows / Linux 是 `Ctrl`
| 操作 | macOS | Windows / Linux | 备注 |
|---|---|---|---|
| AI 助手 | Cmd + I | Ctrl + I | 需安装 AI 微应用 |
| 新建任务 | Cmd + N | Ctrl + N | 弹出快速创建 |
| 新建项目 | Cmd + B | Ctrl + B | |
| 新会议 | Cmd + J | Ctrl + J | |
| 打开设置 | Cmd + , | Ctrl + , | |
| 下载内容 | Cmd + Option + L | Ctrl + Alt + L | 打开下载管理器 |
| 截图 | Cmd + Shift + (自定义字母) | Ctrl + Shift + (自定义字母) | 用户在设置里指定字母 |
## 应用内导航
| 操作 | macOS | Windows |
|---|---|---|
| 后退 | Cmd + ← | Alt + ← |
| 前进 | Cmd + → | Alt + → |
| 刷新 | Cmd + R | Ctrl + R / F5 |
| 强制刷新 | Cmd + Shift + R | Ctrl + Shift + R |
| 关闭窗口 | Cmd + W | Ctrl + W |
| 退出 App | Cmd + Q | Alt + F4 |
## 自定义截图快捷键
1. 个人设置 → 「键盘」→「截图快捷键」
2. 输入框只接受一个字母 / 数字(自动大写)
3. 实际组合键为:`Mod + Shift + 你填的字母`
4. 留空表示不绑定
## 注册原理
桌面端通过 Electron `globalShortcut.register` 在 App 启动时注册,App 退出时 `unregisterAll`。冲突时(被其他 App 抢先注册)会静默失败。
## 与网页端的对比
网页端只有应用内键盘(必须 DooTask 标签页激活才生效),没有系统级全局快捷键。详见 [[web-client.shortcut.concept]]。
## 不支持
- 不支持把全部快捷键改成自定义(仅截图键可自定义)
- 不支持「按键组合录制」(只能输入字母)
- 不支持移动端手势(那是 [[mobile-client.gesture.concept]] 的能力)

View File

@ -0,0 +1,61 @@
---
id: electron-client.tray.concept
title: 桌面端系统托盘
type: concept
feature: electron-client
scope: end-user
locale: zh
aliases:
- 系统托盘
- 最小化到托盘
- 托盘图标
- 后台运行
- 关闭后还在
- 任务栏图标
- 状态栏图标
related_tools: []
related_pages: []
prerequisites: []
negative:
- Linux 端没有托盘(仅 macOS / Windows 实现)
- 托盘菜单只在 Windows 上提供「显示 / 退出」右键菜单;macOS 点击直接呼出窗口
- 托盘红点 / 角标数量来自服务器未读计数,需在线才会更新
last_verified: v1.7.90
---
# 桌面端系统托盘
## 定义
系统托盘是 DooTask 桌面端在 macOS 菜单栏 / Windows 任务栏右下角驻留的小图标,用于让 App 在窗口关闭后仍在后台运行,持续接收消息、显示未读数。
## 平台支持
- **macOS**:在屏幕顶部菜单栏出现 DooTask 图标(模板图,跟随系统深浅色)
- **Windows**:在右下角任务栏托盘区出现 DooTask 图标
- **Linux**:不创建托盘
## 行为
### 点击托盘图标
- macOS / Windows 单击:呼出 / 隐藏主窗口
### Windows 右键托盘
- 「显示」:把主窗口拉到前台
- 「退出」:完全退出 App(不再后台保留)
### macOS 关闭主窗口
- 直接点窗口左上角红色 × 仅隐藏窗口,App 继续在 Dock 和菜单栏运行
- 真正退出要用菜单 → 「DooTask」→「退出 DooTask」(Cmd + Q)
### Windows 关闭主窗口
- 关闭按钮的具体行为可由用户在设置里选(隐藏到托盘 / 直接退出),取决于客户端版本
- 完全退出请用托盘右键「退出」或菜单「文件」→「退出」
## 托盘提示与角标
- 鼠标悬停托盘图标:显示「DooTask」名称
- macOS:有未读消息时托盘文字会显示数字(如「3」)
- Dock 角标(macOS)/ 任务栏角标(Windows):由系统决定显示方式
## 不支持
- Linux 桌面环境(GNOME / KDE)无托盘集成
- 托盘图标不可自定义颜色 / 形状
- 不支持自定义右键菜单项(目前仅「显示 / 退出」)

View File

@ -0,0 +1,46 @@
---
id: email-notice.concept
title: 邮件通知是什么
type: concept
feature: email-notice
scope: admin
locale: zh
aliases:
- 邮件通知
- 邮件提醒
- SMTP 通知
- 系统发邮件
- 邮箱通知
- 收不到邮件
related_tools: []
related_pages: []
prerequisites:
- 管理员已在系统设置「邮箱设置」配好 SMTP 服务器
negative:
- 未配置 SMTP 时所有邮件功能都不发邮件,但不会报错给用户
- 邮件通道只用于系统通知(注册验证 / 改邮箱 / 未读消息 / 删除账号验证),不能用作客户营销邮件
- 邮件发送依赖第三方 SMTPDooTask 自身不内置邮件服务器
last_verified: v1.7.90
---
# 邮件通知是什么
## 定义
邮件通知是 DooTask 通过管理员配置的 SMTP 服务器,向用户邮箱发送系统通知的能力。系统级配置存放在 `emailSetting`,包含 SMTP 服务器、端口、账号、密码、忽略地址、未读消息提醒规则等字段。
## 关键属性
- **全局开关**:未配 SMTP 时邮件链路不工作(不报错,也不发出)
- **注册验证**`reg_verify` = open 时新注册账号需邮箱验证才能登录,修改邮箱/删除账号也走验证码
- **未读消息提醒**`notice_msg` = open 时按时间范围 `msg_unread_time_ranges` 把未读消息汇总成邮件发送
- **忽略地址**`ignore_addr` 列表中的邮箱永远不收邮件(如内部测试号、机器人号)
- **发件人**系统别名System Alias+ SMTP 账号,如 `Task <noreply@example.com>`
## 触发邮件的场景
具体场景见 [[email-notice.scenarios.concept]]。
## 与其他通知的关系
- **APP 推送**(友盟):见 [[push-notice.concept]],独立通道
- **桌面通知**Electron本地系统通知不走邮件
- **移动端通知**iOS/Android 推送,不走邮件
邮件、APP 推送、桌面通知三个通道**并行触发**,互不影响。

View File

@ -0,0 +1,53 @@
---
id: email-notice.scenarios.concept
title: 邮件通知场景
type: concept
feature: email-notice
scope: admin
locale: zh
aliases:
- 什么时候发邮件
- 邮件场景
- 系统会发哪些邮件
- 邮件触发
- 邮件类型
related_tools: []
related_pages: []
prerequisites:
- 管理员已配置 SMTP见 [[email-notice.config.howto]]
negative:
- 任务分配、审批通知、@提及不会单独发邮件,只通过站内消息和 APP 推送
- 未读消息邮件只汇总指定时间窗口内的未读,不是每条消息都发
- 邮件发送失败不会重试,也不会通知发起方
last_verified: v1.7.90
---
# 邮件通知场景
## 定义
DooTask 仅在少数系统级事件中通过邮件触达用户。所有事件都走管理员配置的 SMTP 通道。
## 全部触发场景
| 场景 | 收件人 | 触发条件 |
|---|---|---|
| 注册邮箱验证 | 新注册用户 | `reg_verify` 开启时,注册即发验证链接 |
| 修改邮箱验证码 | 用户原邮箱 | 用户提交「修改邮箱」时发 6 位验证码30 分钟有效)|
| 注销账号验证码 | 用户当前邮箱 | 用户提交「删除账号」时发验证码 |
| 未读消息汇总 | 启用通知的用户 | `notice_msg` 开启且在 `msg_unread_time_ranges` 时间范围内,未读消息超过指定分钟数时按用户汇总发送 |
| 测试邮件 | 管理员指定地址 | 管理员点「邮件发送测试」时发一封测试 |
## 未读消息邮件的精细规则
- 单聊未读 ≥ `msg_unread_user_minute` 分钟才汇入下次邮件
- 群聊未读 ≥ `msg_unread_group_minute` 分钟才汇入下次邮件
- 任一值 = -1 → 该类型完全不发
- 仅 `text / file / record / meeting` 这 4 类消息会被汇总
- 标记为静默silence的消息不进邮件
- 忽略地址列表里的用户永远不收
## 不在邮件范围内
- 任务创建 / 状态变更 / 截止提醒
- 审批通过 / 驳回 / 抄送
- 项目邀请
- @提及 / 引用 / 评论
以上靠 [[push-notice.concept]] 和站内消息WebSocket通知。

View File

@ -0,0 +1,48 @@
---
id: favorite.concept
title: 收藏与最近
type: concept
feature: favorite
scope: end-user
locale: zh
aliases:
- 收藏是什么
- 最近打开是什么
- 我的收藏和最近浏览
- 收藏夹
- 收藏功能介绍
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 收藏只对自己可见,不能分享给其他成员
- 收藏不会跟随对象状态变化(收藏一个任务后,任务被删除则收藏项失效)
- 不支持给收藏分组 / 打标签 / 排序,列表按收藏时间倒序
last_verified: v1.7.90
---
# 收藏与最近
## 定义
DooTask 用两套独立机制保存「常用入口」与「访问历史」:
- **收藏UserFavorite**:用户主动「加星」保存的对象,列表稳定,仅本人可见
- **最近打开UserRecentItem**:系统自动记录最近访问的任务和文件,按浏览时间倒序,不依赖手动操作
- **任务浏览历史UserTaskBrowse**:任务详情页底层的访问记录,给「最近浏览」与统计使用
## 收藏的关键属性
- 支持 4 种对象类型:**任务task、项目project、文件file、消息message**
- 每个收藏可加 ≤ 255 字符的备注
- 同一对象只能收藏一次再次「加星」即取消toggle 语义)
- 删除被收藏对象,收藏项保留但实际数据为空
## 最近打开的关键属性
- 自动记录:进入任务详情、打开文件、查看任务附件、查看消息附件
- 来源标记:保留进入路径(来自项目、文件柜、任务、对话),便于反向回跳
- 仅展示最近一段时间内的访问,不做长期归档
## 与收藏的区别
- **收藏**:用户决定保留,长期稳定;适合「常用项目」「重要任务」
- **最近打开**:系统自动维护,会被新访问覆盖;适合「我刚才看的那个文件」
详见 [[favorite.recent.concept]]。

View File

@ -0,0 +1,62 @@
---
id: favorite.recent.concept
title: 最近打开是什么
type: concept
feature: favorite
scope: end-user
locale: zh
aliases:
- 最近打开
- 最近访问
- 最近浏览
- 最近看过的任务
- 最近用过的文件
- 历史记录
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 最近打开不可手动添加,全部由系统自动写入
- 不能合并多个设备的访问记录(按账号聚合,但同账号不同端共用)
- 不会无限保留,旧记录会被新访问覆盖 / 清理
last_verified: v1.7.90
---
# 最近打开是什么
## 定义
最近打开由 `UserRecentItem` 模型维护,是系统自动记录的「我最近访问过的对象」。每次打开任务、文件、任务附件、消息附件时自动写入,按 `browsed_at` 倒序展示。它和「我的收藏」(见 [[favorite.concept]])是两套独立机制。
## 支持的对象类型
| target_type | 含义 |
|---|---|
| `task` | 任务(打开任务详情时记录) |
| `file` | 文件柜中的文件 |
| `task_file` | 任务下的附件 |
| `message_file` | 聊天消息中的附件 |
## 来源标记
每条记录还会保存「从哪里进入的」(`source_type` + `source_id`
- `project` — 从项目内进入的任务
- `project_task` — 从任务页打开的任务附件
- `filesystem` — 从文件柜打开
- `dialog` — 从对话打开的消息附件
通过来源可以反向回跳到原入口。
## 与收藏的区别
- **收藏**:用户主动「加星」,长期稳定,本人可见,最多 4 种类型(任务 / 项目 / 文件 / 消息)。详见 [[favorite.concept]]
- **最近打开**:系统自动写入,按浏览时间排序,会被新访问覆盖;不含「项目」和「消息」(消息附件除外)
## 与「任务浏览历史」的关系
任务详情页另有一份 `UserTaskBrowse` 表,专门记录任务粒度的浏览历史,用于「最近浏览过的任务」推荐。「最近打开」是更上层的聚合视图,覆盖任务和文件两类。
## 入口
- 桌面端:左侧栏「应用」→「最近打开」
- 移动端:底部 Tabbar「应用」→「最近打开」
## 操作能力
- 查看:分页浏览、按类型过滤
- 删除单条:列表中点「删除」即可(不会影响原对象)
- 不支持手动添加;不支持永久保留某条

View File

@ -0,0 +1,50 @@
---
id: file.preview.concept
title: 文件预览类型
type: concept
feature: file
scope: end-user
locale: zh
aliases:
- 文件能不能预览
- 在线看文件
- 文档预览
- 支持哪些文件预览
- excel 能直接看吗
related_tools: [get_file_detail, fetch_file_content]
related_pages: [file]
prerequisites: []
negative:
- office 类doc/xls/ppt 等)预览需要管理员在应用市场安装 office 插件OnlyOffice
- 其他格式PDF / CAD / OFD 等)预览需要 fileview 插件
- 未安装对应插件时打开会提示「未安装」,但仍可下载
- 视频 / 音频不在文件预览里直接播放,会触发下载
last_verified: v1.7.90
---
# 文件预览类型
## 定义
文件预览指在浏览器内不下载即可查看的能力。DooTask 不同文件类型走不同预览管线,依赖不同插件。
## 浏览器直接预览(无需插件)
- **document**内置在线文档Markdown 渲染器)
- **txt / code**:纯文本与代码高亮
- **picture**jpg / png / webp / gif / bmp 直接渲染
- **mind**:思维导图(需 minder 插件,但产品默认随包提供)
- **drawio**:流程图(需 drawio 插件)
## 需安装插件预览
- **word / excel / ppt**:内置类型,但实际渲染由 OnlyOffice 完成,需 office 插件
- **pdf / cad / ofd / tif** 等:由 fileview 插件提供
- **wps**fileview 插件
## 不支持预览的类型
- **archive**zip / rar / 7z 等):只能下载,无在线解压
- **media**mp3 / mp4 / mov 等):从文件入口直接下载,预览能力依赖浏览器原生
- **axurerp**:仅记录类型,需用 Axure 客户端查看
## 在线编辑 vs 仅预览
- 在线编辑document、word/excel/pptoffice 插件、mind、drawio
- 仅预览pdf、其他 fileview 支持的类型
- 编辑权限受 FileUser 权限位(读写 / 只读)控制

View File

@ -0,0 +1,47 @@
---
id: file.tree.concept
title: 文件树结构
type: concept
feature: file
scope: end-user
locale: zh
aliases:
- 文件夹结构
- 文件父子关系
- 文件嵌套
- 文件树
- 文件层级
related_tools: [list_files, get_file_detail]
related_pages: [file]
prerequisites: []
negative:
- 单个文件夹下最多 300 个直接子文件 / 子文件夹
- 不支持软链接 / 快捷方式(一个文件只能在一个位置)
- 不支持跨用户的全局共享根目录
last_verified: v1.7.90
---
# 文件树结构
## 定义
DooTask 的文件以树形结构组织每个文件或文件夹type=folder通过 `pid` 字段指向父级,根目录 `pid=0`。系统额外维护 `pids` 字段(递归祖先 ID 串)用于快速查询路径与权限。
## 关键属性
- **pid**:直接父文件夹 ID0 表示根
- **pids**:祖先链字符串如 `,5,12,28,`,用于 LIKE 查询子树
- **type=folder**:文件夹本身也是 File 表的一条记录
- **userid**:拥有者(与共享文件夹一致时表示共享根)
- **pshare**:所属共享根 ID0 表示不在任何共享内
## 容量限制
- 单文件夹直接子项数量上限 300含子文件夹
- 整树深度无强制上限但超过 10 层不推荐
- 共享文件夹最多 100 个共享成员
## 与「项目任务文件」的区别
- 文件树:用户的网盘,独立功能
- 项目任务文件:附在任务下的附件(`project_task_files` 表),不进入文件树
- 两者完全隔离,互不可见
## 文件类型枚举
folder文件夹/ document在线文档/ mind思维导图/ drawio流程图/ word / excel / ppt / picture / archive / pdf / txt / code / media 等。

View File

@ -0,0 +1,54 @@
---
id: file.version.concept
title: 文件版本历史
type: concept
feature: file
scope: end-user
locale: zh
aliases:
- 文件历史版本
- 恢复旧版本
- 文件回滚
- 谁改过这个文件
- 文档历史
related_tools: [get_file_detail]
related_pages: [file]
prerequisites: []
negative:
- 版本历史仅对在线编辑类document / word / excel / ppt / mind / drawio有效
- 普通上传文件覆盖后只会替换最新版,无完整历史
- 没有版本数量上限,但定期清理由系统设置决定
- 不支持「版本备注」字段(只看保存人 + 时间 + 大小)
last_verified: v1.7.90
---
# 文件版本历史
## 定义
对支持在线编辑的文件,每次保存都会在 `file_contents` 表插入新记录FileContent形成版本历史。文件本身File 表)只指向最新版,但通过 `whereFid + orderByDesc('id')` 可查到所有历史版本。
## 关键属性
- **fid**:所属文件 ID
- **content**JSON 元数据,包含真实存储路径 url、类型 type、扩展 ext
- **text**:提取出的纯文本(用于全文检索)
- **size**:本版本大小(字节)
- **userid**:本次保存人
- **created_at**:保存时间
## 哪些文件类型有版本
- document在线文档
- word / excel / ppt通过 OnlyOffice 在线编辑)
- mind思维导图
- drawio流程图
## 哪些文件类型没有版本
- 上传二进制pdf / archive / picture / media 等)覆盖上传只保留最新一版
- folder 本身无内容,无版本
## 查看与恢复
- 入口:文件预览页 / 编辑页 → 顶部菜单「历史记录」
- 列表显示每一版的保存人头像 + 时间 + 大小
- 选某一版可「预览」或「恢复」(恢复 = 把该历史版复制为新的最新版,保留中间版本)
## 与"文件协作锁"
DooTask 在线 Office 编辑通过 OnlyOffice 提供多人协作,多人编辑同一文档时由 OnlyOffice 自身的协同逻辑保证一致性,并在最后一个编辑者关闭后回写一版到 FileContent。

View File

@ -0,0 +1,48 @@
---
id: fileview.concept
title: 文件预览fileview是什么
type: concept
feature: fileview
scope: end-user
locale: zh
aliases:
- fileview
- 文件预览
- 在线预览
- 预览 PDF
- 预览 Word
- kkfileview
- 在线看文件
related_tools: []
related_pages: [file]
prerequisites:
- 应用市场已安装 fileview 插件
negative:
- fileview 只提供「只读预览」,不能在线编辑(编辑要装 OnlyOffice见 [[office.concept]]
- 主程序本身能直接预览图片jpg/png/gif 等)和文本/代码,这些不需要 fileview
- 视频/音频在线播放走主程序自带能力,也不属于 fileview 范畴
last_verified: v1.7.90
---
# 文件预览fileview是什么
## 定义
fileview 是 DooTask 的**通用文件在线预览**能力,基于开源 kkfileview 引擎由独立插件提供。所有不能被主程序直接渲染的文件类型PDF、Word 系、Excel 系、PPT 系、Visio、CAD 等)在点击文件名时会跳转到 fileview 的预览页(路径 `fileview/onlinePreview?url=<base64 文件地址>`),返回浏览器内可滚动、可缩放、可翻页的页面。
## 关键属性
- **只读**:仅查看,不可编辑(编辑能力需 OnlyOffice 插件)
- **入口**:点击文件即可,主程序对超出本地预览范围的文件自动 301 到 fileview 路径
- **格式覆盖**:远超主程序自带能力,包括 Office 全家桶、PDF、各类压缩包、3D 模型、CAD 等(详见 [[fileview.supported.concept]]
- **加载机制**fileview 后端拉取文件 → 转码或解析 → 推送渲染结果到浏览器
- **依赖**:依赖独立 fileview 容器持续运行
## 与其他预览能力的关系
- **图片**jpg/jpeg/png/gif/bmp 等):主程序内置直接渲染,不走 fileview
- **代码/文本**2MB 内、扩展名属于 `codeExt` 列表):主程序内嵌 AceEditor 渲染,不走 fileview
- **Word/Excel/PPT**:装了 OnlyOffice 时走 OnlyOffice 编辑器(可编辑);没装但装了 fileview 则走 fileview 只读预览
- **思维导图mind/ 流程图drawio**DooTask 自己的图形文件,由 minder/drawio 插件渲染,不走 fileview
## 相关
- 插件元信息:[[fileview.plugin.concept]]
- 支持的文件类型:[[fileview.supported.concept]]
- 在线编辑 Office 文档:[[office.concept]]

View File

@ -0,0 +1,53 @@
---
id: fileview.plugin.concept
title: fileview 插件元信息
type: concept
feature: fileview
scope: admin
locale: zh
aliases:
- fileview 插件
- kkfileview 插件
- 安装文件预览
- 文件预览插件版本
- 预览插件多大
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- 未安装 fileview 插件时PDF/Office 等文件点击后会报错或下载,无法在线预览
- 主程序不内置 kkfileview本插件是唯一来源
- 不能离线安装到不联网的环境(需访问应用市场镜像源)
last_verified: v1.7.90
---
# fileview 插件元信息
## 定义
文件在线预览由 `fileview` 插件提供(应用市场 app id 为 `fileview`,当前主版本 4.4.0,基于开源 kkfileview 引擎)。主程序在用户点击文件时判断扩展名,若属于 fileview 范畴则 301 跳转到 `fileview/onlinePreview?url=<base64>`,由 fileview 容器渲染。
## 关键属性
- **作者**社区维护Community上游 https://kkview.cn/
- **分类**:微应用(不在管理员应用区,但行为更像「基础设施」——不直接出现在「应用中心」用户菜单,由文件页自动触发)
- **包大小**:约 630MB含 LibreOffice + 各类转码工具)
- **运行形态**:独立 Docker 容器,对接主程序 nginx 上的 `/fileview/` 反代
- **渲染方式**:服务器侧用 LibreOffice 把 docx/pptx 转 PDF 再推到浏览器;纯 PDF 直接 PDF.js 渲染;图片格式化预览
- **跨设备**:桌面端、移动端均能预览
## 安装与启用
1. 在应用市场管理员入口搜索「FileView」或「文件预览」
2. 点击安装,等待镜像下载(约 630MB按「安装日志」判断进度
3. 安装完成后插件自动启用,所有未被主程序原生预览覆盖的文件点击后会自动用 fileview 渲染
## 卸载影响
- 卸载后点击 PDF/Office 等文件会失败(路径仍指向 fileview 但容器不存在)
- 重装后自动恢复
## 与 OnlyOffice 共存
- 同时装了 office 和 fileviewWord/Excel/PPT 优先走 OnlyOffice 编辑器(可编辑)
- 只装 fileviewWord/Excel/PPT 只能用 fileview 只读预览
- 只装 officefileview 才能预览的格式(如 PDF、Visio、CAD 等)无法在线预览
## 相关
- 是什么:[[fileview.concept]]
- 支持的文件类型:[[fileview.supported.concept]]

View File

@ -0,0 +1,61 @@
---
id: fileview.supported.concept
title: fileview 支持的文件类型
type: concept
feature: fileview
scope: end-user
locale: zh
aliases:
- fileview 支持什么格式
- 哪些文件能在线预览
- DooTask 预览的格式
- 能不能预览 PDF
- 能不能预览 CAD
- 预览支持的扩展名
related_tools: []
related_pages: [file]
prerequisites:
- 应用市场已安装 fileview 插件
negative:
- 加密/密码保护的 Office 文档预览失败fileview 解不开)
- 超大文件(>100MB预览很慢甚至超时
- 不能在预览页里编辑/批注
last_verified: v1.7.90
---
# fileview 支持的文件类型
## 定义
fileview 插件基于开源 kkfileview 引擎,可在线预览 30+ 种文件格式。主程序对扩展名做粗分流:图片、代码文本、视频音频走主程序自带能力;其余几乎全部交给 fileview。
## 主要支持的格式分类
- **PDF**pdf
- **Word 系**doc, docx, dot, dotx, odt, ott, rtf
- **Excel 系**xls, xlsx, xlsm, xlt, xltx, ods, ots, csv, tsv
- **PowerPoint 系**ppt, pptx, pps, ppsx, pot, potx, odp, otp
- **WPS 系**wps, et, dps与 Office 同一套转码路径)
- **OpenDocument 全系**odt/ods/odp/ott/ots/otp 等
- **流程图 / 矢量**vsd, vsdxVisiodrawio 文件由 drawio 插件直接打开不走 fileview
- **CAD**dwg
- **3D**obj, stl
- **电子书**epub, mobi
- **压缩包**zip, rar, 7z, tar, gz展开目录树预览
- **邮件/日历**eml, ics
- **代码(大文件兜底)**:当代码文本 > 2MB 主程序不再内嵌渲染,可由 fileview 兜底
## 主程序不走 fileview 的格式
- 图片jpg, jpeg, webp, png, gif, bmp主程序内置渲染
- 视频/音频mp3, wav, mp4, flv 等(内置播放器)
- 代码/文本 ≤ 2MB内嵌 AceEditor
- 思维导图mind→ minder 插件
- 流程图drawio→ drawio 插件
- Word/Excel/PPT 若装了 OnlyOffice → 走 OnlyOffice 编辑器(可编辑)
## 不支持
- 加密 / 密码保护的 Office 文档无法预览
- 不保证体积过大(数百 MB 起)的文件能正常预览(可能超时或加载缓慢)
- 不能在预览页里编辑fileview 只读)
## 相关
- 插件元信息:[[fileview.plugin.concept]]
- 是什么:[[fileview.concept]]

View File

@ -0,0 +1,62 @@
---
id: kpi.concept
title: KPI 绩效考核是什么
type: concept
feature: kpi
scope: end-user
locale: zh
aliases:
- KPI
- 绩效
- 绩效考核
- 绩效管理
- 员工评估
- Key Performance Indicator
related_tools: []
related_pages: [application]
prerequisites:
- 应用市场已安装 kpi 插件
negative:
- KPI 不是 OKR与 OKR 在 DooTask 中是两个独立插件
- KPI 不是主程序内置功能,未装插件时不可用
- KPI 用户角色与 DooTask 系统角色不完全等价(详见正文)
last_verified: v1.7.90
---
# KPI 绩效考核是什么
## 定义
KPIKey Performance Indicator关键绩效指标在 DooTask 中由独立插件 `community_kuaifan_kpi` 提供是面向企业和组织的现代化绩效考核管理系统。它支持创建考核任务、按模板录入指标评分、邀请同事评分、HR 审核与异议处理,让多角色协作完成员工绩效评估。
## 在 DooTask 中的形态
- 通过应用市场安装的社区插件(基于 Next.js + Go
- 与 DooTask 用户体系打通:用户信息、部门信息自动同步
- 与 DooTask 主程序作为应用插件集成,菜单挂在「应用」下
## 用户角色
KPI 内部有自己独立的三级角色(与 DooTask 系统角色不完全等价):
- **employee普通员工**:查看 / 填写自己的考核、提交异议、参与邀请评分
- **manager部门主管**:员工的全部能力 + 评估下属、导出数据
- **hrHR 管理员)**:完整系统权限,管理部门 / 员工 / 模板 / 规则 / 异议
## 关键能力
- **考核管理**:创建、填写、管理绩效考核
- **邀请评分**:多角度 360 度评价(详见 [[kpi.create.howto]]
- **异议处理**员工可申诉HR 审核调整得分
- **统计分析**:数据图表与报表,支持 Excel 导出
- **KPI 模板**HR 维护通用模板供考核复用
## KPI 与 OKR 的区别
- **KPI**:直接考核指标,与绩效 / 薪酬绑定,偏重「不能丢分」
- **OKR**:目标管理工具,鼓励挑战性目标,与考核解耦
## 不支持
- 不直接对接 DooTask 任务完成情况自动算绩效
- 不支持脱离 DooTask 单独登录使用
## 相关
- 插件元信息:[[kpi.plugin.concept]]
- 入口在哪:[[kpi.entry.menu-map]]
- 创建考核:[[kpi.create.howto]]
- 评分机制:[[kpi.scoring.concept]]

View File

@ -0,0 +1,60 @@
---
id: kpi.plugin.concept
title: KPI 插件元信息
type: concept
feature: kpi
scope: end-user
locale: zh
aliases:
- KPI 插件
- kpi 怎么装
- 绩效插件
- 绩效考核插件
- community_kuaifan_kpi
related_tools: []
related_pages: [application]
prerequisites: []
negative:
- KPI 不是主程序内置功能,未装插件时入口不会出现
- 插件升级不通过 git pull需要在应用市场更新
- 主程序版本必须高于 1.4.67,否则插件无法安装
last_verified: v1.7.90
---
# KPI 插件元信息
## 定义
KPI 绩效考核在 DooTask 中由社区插件提供,应用市场 app id 为 `community_kuaifan_kpi`(当前版本 0.1.9feature 短名 `kpi`。主程序不内置任何 KPI 代码,所有绩效逻辑都跑在独立 Docker 容器中,作为应用插件挂载到 DooTask 界面。
## 关键属性
- **作者**DooTask 官方
- **要求**:主程序版本 > 1.4.67(依赖新 API 能力)
- **运行形态**:单个 Docker 容器(镜像 `dootask/kpi:<version>`
- **数据存储**:独立 SQLite 数据库,本地卷 `kpi_data` 挂载到 `/web/db`,不入主库
- **菜单注入**:安装后在「应用中心」注册「绩效考核」入口
- **重启策略**`unless-stopped`,主机重启容器自动恢复
## 用户生命周期钩子
插件订阅了 DooTask 的用户事件,自动维护 KPI 内部用户:
- `user_onboard`DooTask 创建用户时,自动在 KPI 内建号
- 部门不存在则自动创建该部门
- DooTask 管理员自动设为 HR 角色
- 部门负责人自动设为 manager 角色
- 其余设为 employee 角色
- `user_offboard`DooTask 删除用户时KPI 同步清理
## 信息同步规则
用户登录 KPI 时:
- 自动更新姓名、职位
- **角色保持不变**(不会因为 DooTask 角色变化而重新分配 KPI 角色)
## 不支持
- 不能离线安装到不联网的环境(需访问应用市场镜像源)
- 不能在主程序 < 1.4.67 的环境上安装
## 相关
- 是什么:[[kpi.concept]]
- 入口在哪:[[kpi.entry.menu-map]]
- 评分机制:[[kpi.scoring.concept]]

View File

@ -0,0 +1,73 @@
---
id: kpi.scoring.concept
title: KPI 评分机制与权重
type: concept
feature: kpi
scope: end-user
locale: zh
aliases:
- KPI 怎么算分
- 绩效权重
- 自评权重
- 上级评分
- 邀请评分权重
- 绩效规则
- 最终得分怎么来的
related_tools: []
related_pages: [application]
prerequisites:
- 应用市场已安装 kpi 插件
negative:
- 仅 HR 管理员可以配置绩效规则
- 权重百分比合计必须为 100%,否则保存不通过
- 关闭绩效规则开关后,系统回退到默认评分方式
- 配置修改后立即生效,仅影响后续新发起的考核
last_verified: v1.7.90
---
# KPI 评分机制与权重
## 定义
KPI 插件的「绩效规则」用于配置不同评分来源在最终得分中的权重比例。系统会根据规则把自评、上级评分、邀请评分按比例加权,自动算出员工的最终绩效得分。
## 评分来源
一次考核可能涉及最多三种评分来源:
- **自评**:员工对照模板给自己打分
- **上级评分**:直属主管打分
- **邀请评分**HR 邀请的第三方人员打分(可选环节,仅在 HR 发起邀请后生效)
## 配置场景
绩效规则按是否有邀请评分分为两套权重配置:
- **无邀请评分场景**
- 自评权重 + 上级评分权重 = 100%
- **有邀请评分场景**
- 自评权重 + 邀请评分权重 + 上级评分权重 = 100%
## 配置流程
1. HR 进入 KPI 的「绩效规则」配置页
2. 打开绩效规则开关,规则生效
3. 按公司制度分别设置两种场景下各来源的权重百分比
4. 保存后立即生效,影响后续新发起的考核
5. 若关闭开关,系统使用默认评分方式
## HR 审核环节
- 主管评估、邀请评分完成后HR 在「审核」环节查看汇总结果
- 系统按绩效规则自动算出最终得分HR 可手动调整
- 员工在「待确认」状态查看最终分有异议可申诉HR 审核后再调分
## 异议处理对得分的影响
- 员工提交异议后考核状态变为「异议处理中」
- HR 可填写处理原因并调整最终得分
- 处理后员工需重新确认;如仍有异议可再次提交(每轮异议都需 HR 重新处理)
## 不支持
- 不支持非 HR 角色配置绩效规则
- 不支持单条考核临时改权重(只能改全局规则)
- 不支持权重为负值或合计不等于 100%
## 相关
- KPI 是什么:[[kpi.concept]]
- 创建考核:[[kpi.create.howto]]
- 入口在哪:[[kpi.entry.menu-map]]

View File

@ -0,0 +1,60 @@
---
id: ldap.concept
title: LDAP 集成是什么
type: concept
feature: ldap
scope: admin
locale: zh
aliases:
- LDAP
- AD 登录
- Active Directory
- 域账号登录
- 企业目录
- LDAP 集成
- LDAP 是什么
- 单点登录 LDAP
related_tools: []
related_pages: []
prerequisites:
- 已部署可达的 LDAP / AD 服务
negative:
- 不支持 OAuth / SAML / OIDC这页只讲 LDAP
- 不支持多 LDAP 域,只能配置 1 个 default connection
- LDAP 用户没邮箱属性就无法首次登录会抛「LDAP 用户缺少邮箱属性」)
- LDAP 用户密码不存到本地,本地密码用随机串占位
last_verified: v1.7.90
---
# LDAP 集成是什么
## 定义
LDAPLightweight Directory Access Protocol集成让 DooTask 用企业已有的 LDAP / Active Directory 账号体系做认证。开启后用户在登录页输入企业域账号 + 密码DooTask 通过 LDAP 协议向目录服务器认证,认证成功后在本地自动创建或合并账号。
实现位于 `app/Ldap/LdapUser.php`,依赖 `directorytree/ldaprecord` 库。设置存在 `setting` 表的 `thirdAccessSetting` 分组。
## 关键属性
- **ldap_open** — 总开关;非 `open` 时所有 LDAP 调用直接 short-circuit 返回
- **ldap_host / ldap_port** — 目录服务器地址(端口默认 389
- **ldap_user_dn / ldap_password** — 管理员 Bind DN 与密码,用于搜索用户
- **ldap_base_dn** — 搜索基准 DN限定查找范围
- **ldap_login_attr** — 登录属性,可选 `cn` / `uid` / `mail` / `sAMAccountName` / `userPrincipalName`,默认 `cn`
- **ldap_sync_local** — 本地账号反向写入 LDAP 的开关
## 工作流程
1. 用户在 DooTask 登录页输入企业账号 + 密码
2. 后端用管理员 Bind 搜索 `loginAttr=用户名` 的 entry
3. 拿到该 entry 的真实 DN用「DN + 用户输入的密码」二次 Bind
4. Bind 成功 → 从 entry 中提取邮箱(按 `mail / cn / uid / userPrincipalName` 顺序)
5. 本地按 email 查找用户:找不到则注册(本地密码随机),找到则合并
6. 同步昵称、头像(`jpegPhoto` 字段)到本地账号
## 与其他概念的关系
- **本地账号**:本地账号若没 `ldap` identity被 LDAP 用户合并时会打上 `ldap`
- **同步本地**`ldap_sync_local=open`):本地用户登录或注册时反向把账号写到 LDAP便于统一管控
- **Swoole 协程**:连接在容器中共享,认证成功后会立刻还原成管理员绑定,避免污染下一个请求
配置入口见 [[system-setting.third-access.howto]] 或 [[ldap.config.howto]]。

View File

@ -0,0 +1,59 @@
---
id: license.concept
title: License Key 是什么
type: concept
feature: license
scope: super-admin
locale: zh
aliases:
- License
- 授权码
- 许可证
- License Key 是什么
- 多少人能用
- 用户上限
- 终端授权
- 怎么算授权
- SN 是什么
- 绑定 MAC
related_tools: []
related_pages: []
prerequisites:
- 需要系统管理员权限才能查看
- 仅超级管理员能保存 License
negative:
- 3 人以下的部署不强制 License不绑 SN / MAC但仍受人数限制
- 一份 License 不能拆给多个 DooTask 终端共用
- 过期或人数超限不会立刻锁死功能,但会在管理端持续报错提示
last_verified: v1.7.90
---
# License Key 是什么
## 定义
License Key 是 DooTask 终端的授权凭证,决定一个部署允许多少注册用户、绑定哪台机器、有效期到何时。它是一段加密字符串,由官方根据「终端 SN + MAC + 人数 + 过期时间」签发。
后端通过 `api/system/license` 接口读写,存储在主程序根目录的 License 文件中(由 `Doo::licenseSave/licenseContent` 管理)。
## 关键属性
- **licensecontent** — License 原文字符串
- **info.people** — 允许的最大用户数0 表示无限制
- **info.sn** — 授权绑定的终端 SN
- **info.mac** — 授权允许的 MAC 列表(数组)
- **info.expired_at** — 过期时间(字符串,空字符串/0 表示永久)
- **doo_sn / doo_version** — 当前终端的 SN 与主程序版本
- **macs** — 当前服务器实际网卡 MAC 列表
- **user_count** — 当前非机器人、未禁用的活跃用户数
## 与其他概念的关系
- **小团队豁免**`info.people <= 3` 时不校验 SN / MAC相当于「3 人内永久免费」
- **超额提示**`user_count > info.people` 时返回 `error: 终端用户数超过License限制`
- **绑定校验**SN 不匹配 → `终端SN与License不匹配`MAC 不在白名单 → `终端MAC与License不匹配`
- **过期校验**:当前时间 > `expired_at``终端License已过期`
## 使用场景
- 申请新的 License见 [[license.howto]]
- 处理过期或失效:见 [[license.expire.faq]]
- 管理后台「License」页会汇总 `error` 数组,展示所有不满足的规则

View File

@ -0,0 +1,44 @@
---
id: meeting.concept
title: 在线会议是什么
type: concept
feature: meeting
scope: end-user
locale: zh
aliases:
- 会议是什么
- 视频会议
- 在线会议
- DooTask 会议
- 音视频会议
related_tools: []
related_pages: []
prerequisites:
- 管理员已在系统设置开启会议并填入服务密钥
negative:
- 不支持会议录制(当前版本无录制按钮)
- 不支持屏幕共享(仅音视频通话)
- 不支持会议预约(无法预定将来某个时间的会议,只能即时发起)
- 不支持等候室 / 主持人审批入会
last_verified: v1.7.90
---
# 在线会议是什么
## 定义
在线会议是 DooTask 内置的音视频通话能力,基于 Agora声网RTC 实现。任何登录用户可即时发起一个会议频道,邀请同事或通过分享链接让访客加入,会议中可开关麦克风/摄像头、互相邀请。
## 关键属性
- **频道标识**:每个会议有一个 `meetingid`11 位大写字母数字串),是用户可见的会议号
- **底层频道**:服务端按 `meetingid` 派生一个内部 `channel`,配合 Agora token 鉴权
- **创建人**:发起人记为 `userid`;访客加入时 `userid` 为空
- **生命周期**:会议在所有人离开 10 分钟后由 [[meeting.close.howto]] 自动关闭并写 `end_at`
- **邀请方式**:直接拉成员 + 分享链接6 小时有效)两种,详见 [[meeting.invite.howto]] / [[meeting.share.howto]]
- **会议消息**:发起 / 邀请会在对方对话生成一张「会议卡片」消息,结束后卡片自动更新为「已结束」
## 与其他能力的关系
- 与「即时通讯」:会议入口集成在对话窗口;会议事件以消息形式回写到对话
- 与「日历」:发起的会议不会自动写入日历(需要手动建日历事件)
## 不支持
- 录制、屏幕共享、会议预约、等候室、主持人踢人等高级能力当前版本均不提供

View File

@ -0,0 +1,62 @@
---
id: meeting.tourist.concept
title: 会议访客(免登录加入)
type: concept
feature: meeting
scope: end-user
locale: zh
aliases:
- 访客加入会议
- 不登录开会
- 外部人员加入
- 临时身份开会
- 没账号能开会吗
related_tools: []
related_pages: []
prerequisites:
- 发起人已生成会议分享链接
negative:
- 访客无法主动邀请其他人
- 访客身份仅在当前会议有效,不会保留到系统
- 访客退出后再次进入需要重新填写姓名
last_verified: v1.7.90
---
# 会议访客(免登录加入)
## 定义
访客是指未注册 / 未登录 DooTask、通过会议分享链接以临时身份加入会议的用户。访客信息不写入用户表仅缓存 6 小时。
## 如何成为访客
1. 拿到形如 `/meeting/<meetingid>/<sharekey>` 的会议分享链接
2. 在浏览器打开(不需要登录)
3. 加入对话框里填写「你的姓名」作为临时昵称
4. 选择是否开启麦克风 / 摄像头,点「加入会议」
## 关键属性
| 属性 | 值 |
|---|---|
| 用户身份 | 临时 uid无系统 userid |
| 昵称来源 | 入会时手动填写 |
| 头像 | 自动按昵称生成默认头像 |
| 有效期 | 6 小时(与 sharekey TTL 一致) |
| 数据存储 | Redis 缓存,键 `meeting_share_link_code_<uid>` |
## 访客在会议中能做什么
- 收听 / 发言(麦克风)
- 开关自己的摄像头
- 看到其他参会人的画面和昵称
## 访客在会议中不能做什么
- 不能邀请其他成员(无「邀请」按钮,仅能「复制链接」转发)
- 不能查看会议外的对话或卡片
- 不能在系统其他模块出现(个人主页、@提及等都搜不到
## 不支持
- 不支持设置访客头像(自动按昵称生成)
- 不支持把访客转为正式用户(需另行注册)
- 不支持禁止某访客重新加入(只能等链接过期)
## 相关
- 生成分享链接:[[meeting.share.howto]]
- 加入流程:[[meeting.join.howto]]

View File

@ -0,0 +1,55 @@
---
id: memos.concept
title: Memos 是什么
type: concept
feature: memos
scope: end-user
locale: zh
aliases:
- Memos
- 速记
- 笔记应用
- 个人笔记
- 想法记录
- 碎片笔记
related_tools: []
related_pages: [application]
prerequisites:
- 应用市场已安装 memos 插件
negative:
- Memos 不是主程序内置功能,未装插件时不可用
- Memos 默认走 SQLite 本地存储,不接 DooTask 主库
- 不支持游客 / 未登录访问
last_verified: v1.7.90
---
# Memos 是什么
## 定义
Memos笔记 / 速记)是一款隐私优先、轻量级的开源笔记服务(开源项目 [usememos.com](https://www.usememos.com)),在 DooTask 中以独立插件形式集成主要用来快速记录想法、待办、链接、代码片段等碎片化内容。每条记录就是一条「memo」时间倒序排列在时间线上。
## 在 DooTask 中的形态
- 通过应用市场安装的社区插件,与主程序同源、共用 TLS
- 走主程序 nginx 子路径 `/apps/memos/` 反向代理,用户无需额外登录
- 数据自托管,使用 SQLite存放于应用目录 `data/memos`
## 关键特性
- **轻量速记**:以时间线方式记录碎片想法,类似 Twitter/微博风格
- **隐私优先**:数据完全自托管,不上传第三方
- **单点登录**DooTask 登录态自动同步,免输密码
- **标签 / 分类**:支持 `#标签` 语法分类整理(详见 [[memos.tag.concept]]
## 适用场景
- 工作日志、灵感速记
- 临时收藏链接 / 代码片段
- 个人学习笔记 / 读书摘录
- 团队不强协作场景下的轻量笔记
## 不支持
- 不替代正式文档(如需协作文档请用项目内文档)
- Memos 不直接与任务 / 项目数据互通
## 相关
- 插件元信息:[[memos.plugin.concept]]
- 入口在哪:[[memos.entry.menu-map]]
- 怎么写一条:[[memos.create.howto]]

Some files were not shown because too many files have changed in this diff Show More