Open Call 三态招募与画师退出企划 (2026-08-11)

Open Call(后端模型名为 Project)不再使用独立的开启/关闭开关和 is_private 控制招募。Project.status 统一为三个状态:

status产品名称公开列表展示通过 ID 链接访问接受新应征处理已有应征
public公开招募是是是是
link_only链接访问否是是是
closed停止招募否是否是

Project.status、ProjectRequest.status 和 WorkTask.status 是相互独立的状态:

  • Project 状态决定 Open Call 在哪里可见,以及是否允许产生新应征。
  • ProjectRequest 状态决定画师是否仍参加 Open Call。
  • WorkTask 状态决定已经创建的 Commission 如何履约。
  • 停止招募或画师退出均不会取消、删除或修改已有 Commission。

接口变更

content

user

artist_center

接口示例

content

POST /api/content/projects/list

  • 功能说明:获取首页、Open Call 列表等公开区域展示的 Open Call。
  • 变更说明:只返回 status=public 且 is_archived=false 的数据。link_only 和 closed 均不进入公开列表、搜索和排序结果。

请求参数

沿用原有分页、分类、画风、发布者、关键词、价格和排序参数,无新增请求字段。

响应字段

每个 Project 新增或调整以下字段:

字段类型说明
statusstring固定为 public
can_applyboolean当前是否允许画师应征,公开列表固定为 true
is_publicly_listedboolean是否出现在公开列表,公开列表固定为 true

错误响应

无新增,沿用原有参数校验语义。

GET /api/content/projects/info

  • 功能说明:通过数字 ID 获取 Open Call 详情。
  • 变更说明:未归档的 public、link_only、closed 均可通过该接口访问。link_only 只是不进入公开列表,不使用额外访问凭证;系统接受用户枚举或猜测 Project ID 的风险。

请求参数

字段类型必填说明
idnumber是Open Call ID

响应示例

{
  "data": {
    "id": 123,
    "status": "link_only",
    "can_apply": true,
    "is_publicly_listed": false,
    "project_requests": []
  }
}

错误响应

  • 404:Open Call 不存在或已归档

user

POST /api/projects/create

  • 功能说明:创建 Open Call。
  • 变更说明:使用三态 status 控制公开范围和招募能力;链接访问直接使用响应中的 Project ID。

新增请求参数

字段类型必填说明
statusstring否public / link_only / closed,默认 public

请求示例

{
  "name": { "zh": "角色插画招募", "_lang": "zh" },
  "content": { "zh": "招募说明", "_lang": "zh" },
  "currency_id": 1,
  "price_start": 10000,
  "price_end": 20000,
  "deadline": "2026-09-30 23:59:59",
  "payment_channels": ["stripe"],
  "status": "link_only"
}

响应示例

{
  "data": {
    "id": 123,
    "status": "link_only",
    "can_apply": true,
    "is_publicly_listed": false
  }
}

错误响应

  • 422:status 不是三个合法值之一,或其他字段校验失败

POST /api/projects/update

  • 功能说明:更新 Open Call 内容。
  • 变更说明:该接口不接受也不处理 status;招募状态只能通过 update_status 修改。

状态更新接口区分

操作接口说明
修改标题、预算等内容POST /api/projects/update不读取 status,不会改变招募状态
修改招募状态POST /api/projects/update_status必须提交 id 和三态 status

前端若把 status 提交给 /api/projects/update,接口会成功更新其他内容,但招募状态不会变化。

错误响应

无新增,沿用原有错误语义。

POST /api/projects/update_status

  • 功能说明:修改 Open Call 的招募状态。
  • 变更说明:替代原独立 Open Calls 开关,同时控制公开范围和是否接受新应征。

请求参数

字段类型必填说明
idnumber是当前用户拥有的 Open Call ID
statusstring是public / link_only / closed

请求示例

{
  "id": 123,
  "status": "closed"
}

响应示例

{
  "ok": true,
  "data": {
    "id": 123,
    "status": "closed",
    "can_apply": false,
    "is_publicly_listed": false
  }
}

状态切换不会修改已有 ProjectRequest、WorkTask、Order、Group 或 Stage。重新切换到 public / link_only 后恢复接受新应征。

错误响应

  • 403:Open Call 不属于当前用户
  • 422:状态值不合法

POST /api/projects/list

  • 功能说明:获取当前用户的 Open Call 管理列表。
  • 变更说明:响应返回三态能力字段;应征预览保留“已产生 Commission 后退出”的记录。

新增/调整响应字段

字段类型说明
data[].statusstringOpen Call 三态
data[].can_applyboolean是否接受新的画师应征
data[].is_publicly_listedboolean是否出现在公开区域
data[].project_requests[].is_exitedboolean画师是否已退出
data[].project_requests[].participation_statusstringjoined / exited
data[].project_requests[].has_created_commissionboolean是否曾创建 Commission
data[].project_requests[].can_chooseboolean是否还能选中并创建新 Commission

project_requests_count 只统计当前仍加入的应征;chosen_project_requests_count 包含已退出但曾产生 Commission 的历史应征。

错误响应

无新增,沿用原有错误语义。

GET /api/projects/info

  • 功能说明:获取当前用户拥有的 Open Call 管理详情。
  • 变更说明:新增 can_apply、is_publicly_listed;三种状态和已归档数据均可由 owner 管理接口访问。

请求参数

字段类型必填说明
idnumber是Open Call ID

错误响应

无新增,沿用原有错误语义。

POST /api/project_requests/choose

  • 功能说明:选中画师应征并创建一个新的 Commission WorkTask。
  • 变更说明:closed 只禁止新的画师应征,不影响从已有有效应征创建 Commission。应征画师已退出或 Open Call 已归档时禁止创建;已有 Commission 不受影响。

请求参数

沿用原有 project_request_id、deadline、remuneration 和多语言 name。

新增错误响应

400:Open Call 已归档,不能继续创建 Commission,错误码 82001。

{
  "code": 82001,
  "message": "This Open Call cannot create new commissions"
}

400:画师已退出当前应征,错误码 82003。

{
  "code": 82003,
  "message": "Artist has exited this Open Call"
}

POST /api/project_requests/requested_list

  • 功能说明:获取 owner 的 Open Call 应征列表。
  • 变更说明:未被选中便退出的应征不再返回;已产生 Commission 后退出的应征继续返回,但 can_choose=false。

请求参数

字段类型必填说明
project_idnumber是当前 owner 拥有的 Open Call ID
filterstring否all / public / hidden / selected / joined / exited
pagenumber否页码,默认 1
sizenumber否每页数量,默认 15,最大 50
order_bystring否latest / lower_price / artist_commission

filter=selected 会同时包含:

  • 当前 status=user_chosen 的应征;
  • 已退出但至少存在一条关联 WorkTask 的应征。

filter=joined 返回所有未退出的应征,包括 status=pending 和 status=user_chosen。owner 应征列表页面只展示“应征中”和“已退出”两个筛选项,分别对应 joined 和 exited。

filter=exited 只返回已退出且至少存在一条关联 WorkTask 的应征;未产生 Commission 就退出的应征不会出现在 owner 列表。

新增响应字段

{
  "data": [
    {
      "id": 456,
      "status": "exited",
      "is_exited": true,
      "participation_status": "exited",
      "has_created_commission": true,
      "can_choose": false,
      "work_tasks": []
    }
  ],
  "total": 1,
  "counts": {
    "all": 1,
    "public": 1,
    "hidden": 0,
    "selected": 1,
    "joined": 0,
    "exited": 1
  }
}

ProjectRequest 原始 status 直接返回 exited;is_exited 和 participation_status 可用于简化前端判断。

错误响应

无新增,沿用原有错误语义。

POST /api/project_requests/choosed_list

  • 功能说明:获取已经选中过并产生 Commission 的应征。
  • 变更说明:包含已退出但存在关联 WorkTask 的历史应征,已有 WorkTask 结构和状态保持不变。

新增响应字段

与 requested_list 相同,新增 is_exited、participation_status、has_created_commission、can_choose。

错误响应

无新增,沿用原有错误语义。

artist_center

POST /api/artist_center/project_request/create

  • 功能说明:画师应征 Open Call。
  • 变更说明:公开招募和链接访问都通过 project_id 应征,不需要额外访问凭证;停止招募不允许新应征。

请求示例:链接访问

{
  "project_id": 123,
  "currency_id": 1,
  "detail": { "zh": "我希望参加", "_lang": "zh" },
  "artworks": [1001],
  "budget": 12000,
  "days_need": 5
}

新增错误响应

  • 400 + 82001:Open Call 已停止招募
  • 400 + 82002:当前画师已经存在 pending 或 user_chosen 应征
  • 400 + 82005:当前画师存在已退出应征,必须通过 update 恢复原记录
{
  "code": 82002,
  "message": "Already joined this Open Call"
}

退出后不能通过 create 创建新的 ProjectRequest;必须通过 update 恢复原记录。

POST /api/artist_center/project_request/exit

  • 功能说明:画师退出 Open Call 的应征列表。
  • 变更说明:新增接口;pending 和 user_chosen 均可退出。退出只更新 ProjectRequest,不修改已有 Commission。

请求参数

字段类型必填说明
idnumber是当前画师拥有的 ProjectRequest ID

请求示例

{
  "id": 456
}

响应示例

{
  "ok": true,
  "data": {
    "id": 456,
    "status": "exited",
    "is_exited": true,
    "participation_status": "exited",
    "has_created_commission": true,
    "can_choose": false
  }
}

接口约束

  • 重复退出幂等成功,不重复发送退出通知。
  • 退出后普通编辑返回 82004;传 is_exited=false 时允许编辑并恢复应征。
  • 退出后 owner 不能基于当前应征创建新 Commission。
  • 已有关联 WorkTask、Order、Group、Stage 的数据和状态完全不变。
  • Open Call 已停止招募或已归档时,画师仍可退出。

退出后的变化

项目退出前退出后:未产生 Commission退出后:已产生 Commission
ProjectRequest statuspending / user_chosenexitedexited
is_exitedfalsetruetrue
participation_statusjoinedexitedexited
画师端 list返回返回,可用 status=exited返回,可用 status=exited
Owner requested_list返回不返回返回,可用 filter=exited
Owner choosed_list选中后且存在 Commission 时返回不返回返回
公开 content 详情按应征 visibility 规则返回不返回不返回
can_exittruefalsefalse
can_choose按 Open Call 状态决定falsefalse
编辑当前应征允许普通编辑返回 82004普通编辑返回 82004
基于当前应征新建 Commission允许禁止,错误码 82003禁止,错误码 82003
已有 Commission正常无完整保留,不改变状态
再次应征有活跃应征时禁止update 恢复原 Request IDupdate 恢复原 Request ID

首次退出会向 owner 发送 project_request.artist_exited 站内通知,但不发送邮件;重复退出幂等成功且不重复通知。

错误响应

  • 404:ProjectRequest 不存在或不属于当前画师
  • 422:请求参数校验失败

POST /api/artist_center/project_request/update

  • 功能说明:编辑当前画师的 Open Call 应征。
  • 变更说明:退出后的应征复用此接口恢复;更新字段并将原记录状态改回 pending,已有 Commission 关系保持不变。

新增请求参数

字段类型必填说明
is_exitedboolean否恢复已退出应征时必须传 false;不传时仅执行普通编辑,不接受 true

恢复必须同时满足:

  • ProjectRequest 属于当前画师且当前状态为 exited。
  • Open Call 状态为 public 或 link_only。
  • Open Call 尚未归档。

恢复成功后返回同一个 ProjectRequest ID,status=pending、is_exited=false。若该记录已有 Commission,list_by_chosen 和 owner choosed_list 仍保留历史记录。

新增错误响应

  • 400 + 82001:Open Call 已停止招募或已归档,不能恢复。
  • 400 + 82004:应征已经退出,但请求没有传 is_exited=false。
  • 422:is_exited=true 或其他不合法值。
{
  "code": 82004,
  "message": "Exited Open Call request cannot be updated"
}

POST /api/artist_center/project_request/list

  • 功能说明:分页获取当前画师的 Open Call 应征。
  • 变更说明:新增服务端状态筛选和计数,分页后不需要前端自行过滤。

新增请求参数

字段类型必填说明
statusstring否all / joined / exited,默认 all

joined 包含 pending 和 user_chosen;exited 对应数据库状态 exited。 旧参数 filter 已删除,传入时返回 422。

响应示例

{
  "data": [
    {
      "id": 456,
      "is_exited": true,
      "participation_status": "exited",
      "has_created_commission": true,
      "can_exit": false,
      "can_choose": false
    }
  ],
  "total": 1,
  "counts": {
    "all": 3,
    "joined": 2,
    "exited": 1
  }
}

错误响应

  • 422:status 不是合法值,或仍传入旧参数 filter

POST /api/artist_center/project_request/list_by_chosen

  • 功能说明:获取当前画师已经选中过并存在 Commission 的应征。
  • 变更说明:退出后仍返回历史应征和关联 WorkTask,确保已有 Commission 入口不会消失。

新增响应字段

与 list 相同,新增 is_exited、participation_status、has_created_commission、can_exit、can_choose。

错误响应

无新增,沿用原有错误语义。

错误码汇总

错误码枚举名称HTTP场景
82001OpenCallNotAcceptingApplications400Open Call 已停止招募或已归档时禁止新应征与恢复;已归档时也禁止创建 Commission
82002OpenCallRequestAlreadyJoined400当前画师已有活跃应征
82003OpenCallRequestCannotChooseExited400owner 尝试选中已退出应征
82004OpenCallRequestCannotUpdateExited400画师尝试编辑已退出应征
82005OpenCallRequestMustRestoreExited400画师存在已退出应征,不能通过 create 新建,必须恢复原记录

前端必须根据 code 处理业务失败,不依赖 message 文案。

数据迁移说明

历史状态迁移

原 status原 is_private新 status
openfalsepublic
opentruelink_only
close / closed任意closed
  • 不生成或保存 share_token。兼容清理迁移会删除曾执行早期实现时创建的 projects.share_token 字段。
  • is_private 数据库字段暂时保留兼容,新代码不再用它查询公开内容。
  • is_archived 继续独立存在;归档后的 Open Call 不允许通过 ID 访问。
  • 旧状态字符串 open / close 不再是 API 合法入参。

ProjectRequest 退出状态

  • 数据库和 API 状态值统一使用 exited,不保留 artist_canceled。
  • 旧 /api/artist_center/project_request/cancel 路由已删除,只使用 /exit。
  • 部署时必须执行数据库升级迁移:先扩展 ENUM、将已有 artist_canceled 记录一次性转换为 exited,再从 ENUM 中删除旧值。该迁移不保留旧 API、旧路由或旧状态入参兼容。
  • 未选中便退出的应征从 owner 和公开接口隐藏。
  • 已产生 Commission 后退出的应征在 owner 管理接口中保留,但 can_choose=false。
  • 公开 content 接口始终隐藏所有已退出应征。

前端对接清单

  • Open Call 创建、编辑状态和状态筛选统一使用 public / link_only / closed,删除原独立 Open Calls 开关。
  • Owner 直接使用 Project ID 生成链接访问 URL。
  • 链接页面通过 GET /api/content/projects/info?id={project_id} 获取详情;link_only 和 closed 不进入公开列表,但知道 ID 即可访问。
  • can_apply=false 时隐藏或禁用应征入口,并展示停止招募状态。
  • link_only 应征与公开招募相同,只提交 project_id 和原有应征字段。
  • 应征卡片可直接使用 status=exited,也可使用 is_exited / participation_status。
  • has_created_commission=true && is_exited=true 时保留历史卡片、置灰并设置不可选中。
  • 画师端使用 /exit,操作前展示二次确认;说明已有 Commission 不受影响。
  • 画师应征页使用 status=joined|exited 和服务端 counts,不要再传旧参数 filter,也不要在当前分页结果上自行过滤。
  • 重新应征复用 update 表单与接口,并额外传 is_exited=false;成功后继续使用原 ProjectRequest ID。
  • Owner 应征页可使用 filter=exited 展示已退出且存在历史 Commission 的应征。
  • 所有新业务失败按 82001 ~ 82004 错误码处理。