Wekan Lists REST API 实战指南:列表(Lists)的增删改查、复制移动与源码实现解析
【免费下载链接】wekanThe Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . PR source translation to imports/i18n/data/en.i18n.json, other translations at https://app.transifex.com/wekan/wekan项目地址: https://gitcode.com/GitHub_Trending/we/wekan
本指南以 Wekan 官方 API 文档 docs/API/Lists.md 为骨架,结合当前仓库中列表数据模型与 REST 端点实现,完整讲解 Wekan 看板中"列表(List)"这一核心实体的 REST API:包括查询、创建、删除、更新、复制与移动等全部端点,并深入到 models/lists.js 的字段定义、server/models/lists.js 的路由鉴权与软删除机制。读完本文,你将能够直接使用curl或任何 HTTP 客户端,基于 Bearer Token 对 Wekan 列表执行完整的管理操作,并能读懂这些操作背后的源码原理。
一、列表在 Wekan 中的定位与数据模型
列表(List)是 Wekan 看板的纵向列(Column),承载卡片(Card)的归类。每条列表归属且仅归属一个看板(boardId),同时可以归属某个泳道(swimlaneId,可为空以保持向后兼容)。
原文档指出相关代码位于wekan/models/lists.js底部,并给出了基于JsonRoutes的路由注册代码。需要说明的是:在当前仓库中,REST 端点已从models/lists.js迁移至服务端专用文件 server/models/lists.js,并改用 Meteor 的WebApp.handlers注册,同时新增了 PUT(更新)、copy(复制)、move(移动)等端点;models/lists.js现在专职承担集合定义、Schema 校验与业务辅助方法。
列表的字段结构由Lists.attachSchema定义(见 models/lists.js),核心字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
title | String(必填) | 列表标题 |
boardId | String(必填) | 所属看板 ID |
swimlaneId | String,可选,默认'' | 所属泳道 ID,空串表示看板级列表(向后兼容) |
archived | Boolean | 是否归档,插入时默认为false |
archivedAt | Date,可选 | 最近一次归档时间 |
deletedAt | Date,可选 | 软删除标记:null或字段缺失表示存活,有值表示已软删除(配合deletedBy、deleteBatchId实现撤销/回收站,见 docs/Features/Undo/Undo.md) |
sort | Number,可选 | 列表在列中的排序值 |
starred | Boolean,可选,默认false | 是否置顶收藏 |
color | String,可选 | 列表颜色:命名色板或自定义#rrggbb十六进制(models/lib/contrastColor.js 校验) |
type | String,默认'list' | 列表类型,'template-list'为模板列表 |
width | Number,可选,默认220 | 列表宽度(像素,100–1000) |
wipLimit | Object,可选 | 在制品(WIP)限制:value(默认 1)、enabled(默认 false)、soft(默认 false) |
syncSource | Object,可选 | 外部跟踪器同步配置:type('jira'/'github'/'gitlab'/'gitea')、url、projectKey、enabled、lastSyncedAt、lastSyncError |
从 Schema 可以看出:列表的颜色校验(custom()校验器)接受命名色板颜色或#rrggbb十六进制;width超出 100–1000 会被widthOutOfRange拒绝;createdAt、updatedAt、modifiedAt均由autoValue自动维护。
二、鉴权方式:Bearer Token 与看板访问级别
所有列表 REST 端点都要求携带登录凭据。Wekan REST API 采用Authorization: Bearer <token>请求头(Token 来自用户登录后的authToken),以curl调用为例:
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \ -X GET \ http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/lists端点在进入业务逻辑前会调用 server/authentication.js 中的鉴权函数(原文档代码中的Authentication.checkBoardAccess/Authentication.checkUserId目前仍存在,但写操作端点已升级为更严格的checkBoardWriteAccess):
- 查询端点(GET):使用
Authentication.checkBoardAccess(req.userId, paramBoardId),要求当前用户能访问该看板; - 写操作端点(POST / PUT / DELETE / copy / move):使用
Authentication.checkBoardWriteAccess(req.userId, paramBoardId),要求当前用户对该看板拥有写权限(看板成员 + 编辑/管理员角色); - 鉴权失败或抛出的任何异常,均被
try/catch捕获并通过sendJsonResult(res, { code: 200, data: error })返回——这是当前实现的既有行为:HTTP 状态码恒为 200,错误详情放在响应体的data字段中(PUT 端点对"列表不存在"与"颜色非法"会分别返回404/400,是例外)。
三、核心端点详解:查询、创建、删除
原文档给出了四个核心端点的完整源码(旧版JsonRoutes写法),下面逐条给出当前仓库 server/models/lists.js 的实际实现与调用示例。
3.1 获取看板下所有未归档列表:GET/api/boards/:boardId/lists
原文档返回字段为_id与title;当前实现(server/models/lists.js)在此基础上扩展了两个时间字段,便于客户端判断列表或其内部卡片是否有新变化(对应 issue #5251):
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \ http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/lists响应示例:
{ "code": 200, "data": [ { "_id": "PgTuf6sFJsaxto5dC", "title": "To Do", "modifiedAt": "2026-01-01T10:00:00.000Z", "cardsModifiedAt": "2026-01-02T08:30:00.000Z" } ] }实现细节:先通过checkBoardAccess校验看板访问权,再以ReactiveCache.getLists({ boardId, archived: false })查询未归档列表;随后一次查询该看板全部卡片(只取listId、modifiedAt、dateLastActivity字段),借助 models/lib/listActivityDates.js 的newestCardChangeByList在内存中归约出"每个列表内卡片的最新改动时间",避免了逐个列表查询的开销。其中modifiedAt反映列表文档本身(标题、排序、归档)的变化,cardsModifiedAt反映列表内卡片(新增、编辑、归档)的最新变化,且故意包含已归档卡片——归档卡片本身就是客户端关心的变化之一。
3.2 获取单个列表:GET/api/boards/:boardId/lists/:listId
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \ http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/lists/PgTuf6sFJsaxto5dC当前实现(server/models/lists.js)与原文档代码基本一致,通过ReactiveCache.getList({ _id, boardId, archived: false })精确匹配"该看板下、未归档"的列表,返回完整列表文档(含上表全部字段,如sort、color、wipLimit、swimlaneId等);若列表不存在或已归档,data为null。
3.3 创建列表:POST/api/boards/:boardId/lists
原文档的创建逻辑仅写入title与boardId;当前实现(server/models/lists.js)增强了三点:写入权限从checkUserId升级为checkBoardWriteAccess;自动计算sort(取board.lists().length,即追加到列尾);swimlaneId缺省时自动绑定看板默认泳道:
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \ -H "Content-type:application/json" \ -X POST \ http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/lists \ -d '{ "title": "Doing", "swimlaneId": "P7bQz8nKXj3Vw2sL" }'swimlaneId可选;不传时服务端通过board.getDefaultSwimlineAsync()(服务端异步变体)取得默认泳道 ID 并写入。请求成功后返回新建列表的 ID:
{ "code": 200, "data": { "_id": "W9m9YxQKT6zZrKzRW" } }3.4 删除列表:DELETE/api/boards/:boardId/lists/:listId
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \ -X DELETE \ http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/lists/PgTuf6sFJsaxto5dC原文档代码执行的是物理删除Lists.remove(...);当前实现(server/models/lists.js)已改为软删除:先查列表是否存在且未被删除(!list.deletedAt),随后调用softRemoveList({ userId, list })(见 server/models/lists.js),其核心行为是:
- 生成批次 ID(
Random.id())与时间戳,用softDeleteSet将该列表下所有未删除卡片与列表本身批量标记deletedAt/deletedBy/deleteBatchId; - 向
UserPositionHistory记录位置变更(失败不阻断删除); - 向
ChangeHistory写入生命周期记录(changeType: 'removed'),这是"撤销删除/回收站恢复"(issue #1023)得以实现的依据——删除是可恢复的,不会真正销毁数据。
响应:
{ "code": 200, "data": { "_id": "PgTuf6sFJsaxto5dC" } }四、扩展端点:更新(PUT)、复制(copy)、移动(move)
这三个端点是原文档未收录、但当前 server/models/lists.js 已实现的功能,可与 docs/API/REST-API.md 中的通用约定配合使用。
4.1 更新列表:PUT/api/boards/:boardId/lists/:listId
该端点(server/models/lists.js)对应 issue #5396,允许通过 REST 修改列表的title、color、starred、wipLimit。其字段解析与校验逻辑被抽离到纯函数模块 models/lib/listApiUpdate.js 的buildListPutUpdate(body, normalizeColor)中,可在不依赖 Meteor 的 Node 环境下单元测试:
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \ -H "Content-type:application/json" \ -X PUT \ http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/lists/PgTuf6sFJsaxto5dC \ -d '{ "title": "In Progress", "color": "#a1b2c3", "starred": true }'校验规则(与 tests/listApiUpdate.test.cjs 一一对应):
title非空才更新,超过MAX_TITLE_LENGTH = 1000字符时截断到 1000;color必须通过normalizeListColor校验(见 models/lists.js):接受命名色板颜色或#rrggbb十六进制;非法颜色(如'notacolor'、'#zzzzzz')返回400,错误信息为Invalid list color. Use a named palette color or a #rrggbb hex value.;starred只要出现在请求体中即写入(true/false均可);wipLimit作为整体对象透传;- 空请求体不会报错,仅返回未更新(HTTP 404,
data.message: 'Error')。
更新成功返回:
{ "code": 200, "data": { "_id": "PgTuf6sFJsaxto5dC" } }4.2 复制列表:POST/api/boards/:boardId/lists/:listId/copy
将列表连同其全部卡片深拷贝到同看板或另一个看板(server/models/lists.js)。请求体:
| 参数 | 说明 |
|---|---|
toBoardId | 目标看板 ID,缺省为源看板 |
toSwimlaneId | 目标泳道 ID(可选) |
position | 目标位置(从左侧起 0 基索引),通过repositionList在兄弟列表间重算sort实现 |
复制逻辑由列表辅助方法Lists.helpers.copy驱动,其决策过程在 models/lib/listCopyPlan.js 中规划并可单元测试(见 tests/listCopySwimlane.test.cjs):若目标看板存在同名未归档列表则复用,否则新建;随后复制列表内的每一个卡片到规划好的泳道。注意:复制前会对源看板与目标看板分别调用checkBoardWriteAccess,即用户必须对两个看板都有写权限。
4.3 移动列表:POST/api/boards/:boardId/lists/:listId/move
将列表及其卡片移动到同看板或另一个看板(server/models/lists.js),请求体与 copy 相同(toBoardId?、toSwimlaneId?、position?):
- 同看板移动:仅做纯重排(可选改
swimlaneId),不重建列表; - 跨看板移动:委托给
Lists.helpers.move,其决策由 models/lib/listMovePlan.js 规划——目标看板存在同名列表时合并,否则重建;每个卡片跟随列表card.move(boardId, plan.swimlaneId, listId)迁移。跨看板移动前同样校验用户对两个看板的写权限。
五、源码级要点小结
- 路由注册方式:列表 REST 端点当前统一使用 Meteor
WebApp.handlers注册于 server/models/lists.js,替代了原文档所示的JsonRoutes.add旧写法;原文档中的Authentication.checkBoardAccess/checkUserId鉴权模型依然保留,写端点进一步升级为checkBoardWriteAccess。 - 响应结构:除 PUT 的 404/400 分支外,各端点均以 HTTP 200 +
{ code: 200, data: ... }返回;查询端点把异常对象直接放入data字段,客户端应检查data是否为预期的业务结构。 - 删除是可逆的:DELETE 走软删除并级联标记卡片,配合
ChangeHistory生命周期记录支持撤销与回收站恢复,不会物理销毁数据。 - 测试佐证:字段校验行为有 tests/listApiUpdate.test.cjs 覆盖(标题截断、非法颜色拒绝、
starred/wipLimit透传等);跨看板移动/复制与安全边界分别由 tests/listApiUpdate.test.cjs、tests/listCopySwimlane.test.cjs 与 tests/restSecurityAdvisories.test.cjs 等覆盖。
如需以 Python 调用本 API 做自动化集成,可参考仓库根目录 api.py 与 docs/API/New-card-with-Python3-and-REST-API.md 中的完整示例;更多 REST 通用约定(Token 获取、错误处理)见 docs/API/REST-API.md。
【免费下载链接】wekanThe Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . PR source translation to imports/i18n/data/en.i18n.json, other translations at https://app.transifex.com/wekan/wekan项目地址: https://gitcode.com/GitHub_Trending/we/wekan
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考