APITable 附件接口开发指南:BasicModuleAttachmentInterfaceApi 的 upload、cite、urlUpload 与图片审核全解析
2026/9/21 22:45:00 网站建设 项目流程
  • 低代码
  • 后端
  • 前端
  • 协同办公

【免费下载链接】apitable

🚀🎉📚 APITable, an API-oriented low-code platform for building collaborative apps and better than all other Airtable open-source alternatives.

项目地址:https://gitcode.com/apitable/apitable
点击查看免费下载

导读

本指南以 APITable 官方 API 文档 BasicModuleAttachmentInterfaceApi.md 为主体,系统讲解 APITable 基础模块附件接口(服务端路径/base/attach)的五个核心端点:资源上传(upload)、图片 URL 上传(urlUpload)、空间附件引用计数变更(cite)、待人工审核图片分页查询(readReviews)与审核结果提交(submitAuditResult)。读完本文,你将掌握每个接口的请求/响应模型、TypeScript SDK 调用方式、AssetType类型语义,并能对照仓库源码(AssetController.java)理解其底层实现与适用场景。

接口总览

所有接口的相对地址基于http://backend/api/v1,本组接口统一由后端控制器 AssetController.java 中的@ApiResource(path = "/base/attach")声明暴露。客户端 SDK 对应类为BasicModuleAttachmentInterfaceApi,实现位于 BasicModuleAttachmentInterfaceApi.ts。

方法HTTP 请求说明
citePOST/base/attach/cite空间附件资源引用数量变化(同一附件需重复传 token)
readReviewsGET/base/attach/readReviews分页查询需要人工审核的图片
submitAuditResultPOST/base/attach/submitAuditResult提交图片审核结果,提交时需填写审核人姓名
uploadPOST/base/attach/upload上传资源文件,不限制文件类型
urlUploadPOST/base/attach/urlUpload图片 URL 上传接口

所有端点均标注 "No authorization required"(无需额外鉴权),但源码层面readReviewssubmitAuditResult会从会话中读取钉钉用户 ID(SessionContext.getDingtalkUserId())并校验登录态,upload在未登录时则会触发人机校验,详见下文各节。


1. cite —— 空间附件引用计数变更

POST/base/attach/cite,请求体为SpaceAssetOpRo,返回ResponseDataVoid

当同一份附件需要被重复引用时(例如表单匿名填写、多单元格引用同一文件),调用本接口告诉服务端"新增/删除了哪些附件引用",从而保证空间附件表(space_asset)中的引用计数与实际使用一致。

请求体模型:SpaceAssetOpRo

对应模型文件 SpaceAssetOpRo.ts,包含三个字段:

字段类型是否必填说明
addTokenArray<OpAssetRo>写入(新增引用)的 token 集合
removeTokenArray<OpAssetRo>删除(释放引用)的 token 集合
nodeIdstring数据表(Datasheet)节点 Id,示例dst10

其中每个 token 项为 OpAssetRo.ts 模型:token(附件 token)与name(附件名称)。

TypeScript 调用示例(来自官方文档)

import { } from ''; import * as fs from 'fs'; const configuration = .createConfiguration(); const apiInstance = new .BasicModuleAttachmentInterfaceApi(configuration); let body:.BasicModuleAttachmentInterfaceApiCiteRequest = { // SpaceAssetOpRo spaceAssetOpRo: { addToken: [ { token: "token_example", name: "name_example", }, ], removeToken: [ { token: "token_example", name: "name_example", }, ], nodeId: "dst10", }, }; apiInstance.cite(body).then((data:any) => { console.log('API called successfully. Returned data: ' + data); }).catch((error:any) => console.error(error));

请求与响应约定

  • Content-Type:application/json
  • Accept:*/*(SDK 实际发送application/json, */*;q=0.8,见 BasicModuleAttachmentInterfaceApi.ts)
  • 成功:200 OK
  • 失败:500 Internal Server Error

源码实现印证

控制器方法cite的逻辑(AssetController.java):

@PostResource(path = "/cite", requiredLogin = false) public ResponseData<Void> cite(@RequestBody @Valid SpaceAssetOpRo opRo) { // Fill out the form anonymously String spaceId = iNodeService.getSpaceIdByNodeIdIncludeDeleted(opRo.getNodeId()); ExceptionUtil.isNotNull(spaceId, PermissionException.NODE_NOT_EXIST); iSpaceAssetService.datasheetAttachmentCite(spaceId, opRo); return ResponseData.success(); }

从源码可以看出两点关键实现事实:

  1. 匿名表单填写场景:接口注释明确为 "Fill out the form anonymously"(匿名填写表单),因此requiredLogin = false,未登录用户也可调用;
  2. 节点存在性校验:服务端会先通过INodeService.getSpaceIdByNodeIdIncludeDeleted根据nodeId反查所属空间,查不到节点则抛出NODE_NOT_EXIST权限异常,随后才执行datasheetAttachmentCite更新引用计数。

2. readReviews —— 分页查询待人工审核图片

GET/base/attach/readReviews,返回ResponseDataPageInfoAssetsAuditVo

当 OSS 云端图片内容审核结果为"需人工复核"时,审核人员通过本接口分页拉取待审核图片列表。

查询参数

参数类型说明备注
pagePage分页对象defaults to undefined
pageObjectParamsstring分页参数字符串,如{"pageNo":1,"pageSize":20}defaults to undefined,经@PageObjectParam解析

TypeScript 调用示例(来自官方文档)

import { } from ''; import * as fs from 'fs'; const configuration = .createConfiguration(); const apiInstance = new .BasicModuleAttachmentInterfaceApi(configuration); let body:.BasicModuleAttachmentInterfaceApiReadReviewsRequest = { // Page page: { records: [ {}, ], total: 1, size: 1, current: 1, orders: [ { column: "column_example", asc: true, }, ], optimizeCountSql: true, searchCount: true, optimizeJoinOfCountSql: true, countId: "countId_example", maxLimit: 1, pages: 1, }, // string | Page params pageObjectParams: "{"pageNo":1,"pageSize":20}", }; apiInstance.readReviews(body).then((data:any) => { console.log('API called successfully. Returned data: ' + data); }).catch((error:any) => console.error(error));

请求与响应约定

  • Content-Type: Not defined(GET 请求无请求体)
  • Accept:*/*
  • 成功:200 OK,返回ResponseDataPageInfoAssetsAuditVo(分页数据,记录类型为AssetsAuditVo
  • 失败:500 Internal Server Error

源码实现印证

@GetResource(path = "/readReviews", requiredLogin = false) public ResponseData<PageInfo<AssetsAuditVo>> readReviews(@PageObjectParam Page page) { String auditorUserId = SessionContext.getDingtalkUserId(); ExceptionUtil.isNotNull(auditorUserId, AuthException.UNAUTHORIZED); return ResponseData.success(PageHelper.build(iAssetAuditService.readReviews(page))); }

虽然声明requiredLogin = false,但实现中必须从会话中取到钉钉用户 ID(SessionContext.getDingtalkUserId(),取不到即抛出UNAUTHORIZED。也就是说,本接口面向钉钉场景的审核人员开放。分页逻辑由 MyBatis-Plus 的Page承载,并通过PageHelper.build统一转换为前端分页结构。


3. submitAuditResult —— 提交图片审核结果

POST/base/attach/submitAuditResult,请求体为AssetsAuditRo,返回ResponseDataVoid

人工审核完成后,将每张图片的审核结论批量回传。文档特别强调:提交时必须填写审核人姓名

请求体模型:AssetsAuditRo

对应模型文件 AssetsAuditRo.ts:

字段类型是否必填说明
assetlistArray<AssetsAuditOpRo>附件人工审核结果列表
auditorUserIdstring审核用户 Id
auditorNamestring审核用户姓名

列表项 AssetsAuditOpRo.ts 包含两个字段:

字段类型说明
assetFileUrlstring存储路径,如space/2020/03/27/1243592950910349313
auditResultSuggestionstring审核结果建议,如block(拦截)

TypeScript 调用示例(来自官方文档)

import { } from ''; import * as fs from 'fs'; const configuration = .createConfiguration(); const apiInstance = new .BasicModuleAttachmentInterfaceApi(configuration); let body:.BasicModuleAttachmentInterfaceApiSubmitAuditResultRequest = { // AssetsAuditRo assetsAuditRo: { assetlist: [ { assetFileUrl: "space/2020/03/27/1243592950910349313", auditResultSuggestion: "block", }, ], auditorUserId: "0122454826077721", auditorName: "name", }, }; apiInstance.submitAuditResult(body).then((data:any) => { console.log('API called successfully. Returned data: ' + data); }).catch((error:any) => console.error(error));

请求与响应约定

  • Content-Type:application/json
  • Accept:*/*
  • 成功:200 OK
  • 失败:500 Internal Server Error

源码实现印证

@PostResource(path = "/submitAuditResult", requiredLogin = false) public ResponseData<Void> submitAuditResult(@RequestBody @Valid AssetsAuditRo results) { // Query the DingTalk member information in the session String auditorUserId = SessionContext.getDingtalkUserId(); ExceptionUtil.isNotNull(auditorUserId, AuthException.UNAUTHORIZED); iAssetAuditService.submitAuditResult(results); return ResponseData.success(); }

readReviews一致,本接口同样依赖钉钉会话身份。审核落地逻辑在AssetAuditServiceImpl(AssetAuditServiceImpl.java)中实现,该服务类同时处理 OSS 审核结果回调(auditCallback)、非法图片的特殊处置(服务内定义了非法资源占位图ASSETS_PUBLIC_PLACEHOLDER = "/public/placeholder.png")以及审核结果入库。


4. upload —— 上传资源文件

POST/base/attach/upload,请求体为AttachOpRo,返回ResponseDataAssetUploadResult

通用资源上传入口,不限制文件类型。前端通过 multipart 方式携带文件二进制流,服务端根据type决定上传到用户目录还是空间目录。

请求体模型:AttachOpRo

对应模型文件 AttachOpRo.ts:

字段类型是否必填说明
fileHttpFile二进制文件流(type: binary
typenumber附件类型(见下方 AssetType 表)
nodeIdstring节点 Id(数据表附件、封面图、节点描述必须传)
datastring密码登录人机校验值,前端通过 NVC Val 函数获取(未登录时执行人机校验)

AssetType 类型语义(来自 AssetType.java)

枚举含义
0USER_AVATAR用户头像
1SPACE_LOGO空间 Logo
2DATASHEET数据表附件
3COVER封面图
4NODE_DESC节点描述
5DOCUMENT文档

其中AssetType.isSpaceAsset(type)判断逻辑为type.getValue() > SPACE_LOGO.getValue(),即值大于 1 的类型都属于"空间资源"(需绑定nodeId),会走空间上传分支;0/1属于公开/用户级资源。

TypeScript 调用示例(来自官方文档)

import { } from ''; import * as fs from 'fs'; const configuration = .createConfiguration(); const apiInstance = new .BasicModuleAttachmentInterfaceApi(configuration); let body:.BasicModuleAttachmentInterfaceApiUploadRequest = { // AttachOpRo (optional) attachOpRo: { file: { data: Buffer.from(fs.readFileSync('/path/to/file', 'utf-8')), name: '/path/to/file' }, type: 0, nodeId: "dst10", data: "FutureIsComing", }, }; apiInstance.upload(body).then((data:any) => { console.log('API called successfully. Returned data: ' + data); }).catch((error:any) => console.error(error));

请求与响应约定

  • Content-Type:application/json
  • Accept:*/*
  • 成功:200 OK,返回ResponseDataAssetUploadResult(外层为success/code/message/data标准响应包装,见 ResponseDataAssetUploadResult.ts,data为 AssetUploadResult.ts 上传结果)
  • 失败:500 Internal Server Error

源码实现印证

@PostResource(path = "/upload", requiredLogin = false) public ResponseData<AssetUploadResult> upload(@Valid AttachOpRo data) throws IOException { AssetType assetType = AssetType.of(data.getType()); MultipartFile file = data.getFile(); // When not logged in, perform human-machine verification Long userId = SessionContext.getUserIdWithoutException(); if (userId == null) { iAssetService.checkBeforeUpload(data.getNodeId(), data.getData()); } if (AssetType.isSpaceAsset(assetType)) { AssetUploadResult result = iAssetService.uploadFileInSpace(data.getNodeId(), file.getInputStream(), file.getOriginalFilename(), file.getSize(), file.getContentType(), assetType); return ResponseData.success(result); } ExceptionUtil.isNotNull(userId, AuthException.UNAUTHORIZED); AssetUploadResult result = iAssetService.uploadFile(file.getInputStream(), file.getSize(), file.getContentType()); return ResponseData.success(result); }

从源码可提炼以下实现要点:

  1. 未登录人机校验SessionContext.getUserIdWithoutException()取不到用户时,会先调用checkBeforeUpload(nodeId, data)执行人机验证(对应AttachOpRo.data字段的用途);
  2. 空间资源分支type > 1时调用uploadFileInSpace,需要有效的nodeId才能把文件归入对应空间(这也是模型注释要求"数据表附件、封面图、节点描述必须传 nodeId"的原因);
  3. 用户级资源分支type <= 1时走uploadFile,但必须先登录,否则抛UNAUTHORIZED

5. urlUpload —— 图片 URL 上传

POST/base/attach/urlUpload,请求体为AttachUrlOpRo,返回ResponseDataAssetUploadResult

不直接上传文件流,而是传一个图片 URL,由服务端拉取该 URL 对应的图片并转存到对象存储,适合"粘贴外链图片自动转存"的场景。

请求体模型:AttachUrlOpRo

对应模型文件 AttachUrlOpRo.ts:

字段类型是否必填说明
urlstring待上传文件的 URL
typenumber附件类型(0用户头像、1空间 Logo、2数据表附件)
nodeIdstring数据表节点 Id(数据表附件必传)

注意:与AttachOpRo的完整六种类型不同,urlUpload模型注释仅覆盖0/1/2三种类型,实际取值仍以服务端AssetType.of校验为准。

TypeScript 调用示例(来自官方文档)

import { } from ''; import * as fs from 'fs'; const configuration = .createConfiguration(); const apiInstance = new .BasicModuleAttachmentInterfaceApi(configuration); let body:.BasicModuleAttachmentInterfaceApiUrlUploadRequest = { // AttachUrlOpRo (optional) attachUrlOpRo: { url: "url_example", type: 0, nodeId: "dst10", }, }; apiInstance.urlUpload(body).then((data:any) => { console.log('API called successfully. Returned data: ' + data); }).catch((error:any) => console.error(error));

请求与响应约定

  • Content-Type:application/json
  • Accept:*/*
  • 成功:200 OK,返回ResponseDataAssetUploadResult
  • 失败:500 Internal Server Error

源码实现印证

@PostResource(path = "/urlUpload", requiredPermission = false) public ResponseData<AssetUploadResult> urlUpload(@Valid AttachUrlOpRo opRo) { AssetUploadResult result = iAssetService.urlUpload(opRo); return ResponseData.success(result); }

与其余四个端点不同,urlUpload使用的是requiredPermission = false注解(而非requiredLogin = false),说明它面向已登录但无特定权限的常规用户开放;实际转存逻辑封装在IAssetService.urlUpload(opRo)中,由服务端完成 URL 拉取、落盘与 token 生成。


补充:客户端 SDK 的请求构造与响应解析

以上五个方法在 BasicModuleAttachmentInterfaceApi.ts 中都有成对实现:

  • RequestFactory(如cite/readReviews/submitAuditResult/upload/urlUpload):负责拼装路径参数、序列化请求体(ObjectSerializer.stringify(ObjectSerializer.serialize(...)))、设置Accept: application/json, */*;q=0.8Content-Type,并应用默认鉴权(defaultAuth?.applySecurityAuthentication);
  • ResponseProcessor(如citeWithHttpInfo/uploadWithHttpInfo):负责反序列化响应,200返回对应模型(ResponseDataVoidResponseDataAssetUploadResult),500抛出ApiException<ResponseDataVoid>

例如upload成功时,uploadWithHttpInfo会把响应体解析为ResponseDataAssetUploadResult;而citesubmitAuditResult则解析为ResponseDataVoid(即仅需确认success状态)。若需直接以 HTTP 方式调用,可参考上文各接口的路径、请求体与状态码约定拼接请求,例如:

curl -X POST 'http://backend/api/v1/base/attach/cite' \ -H 'Content-Type: application/json' \ -d '{"nodeId":"dst10","addToken":[{"token":"token_example","name":"name_example"}]}'

常见问题与最佳实践

  1. 匿名表单场景务必用 cite 维护引用计数:同一附件被多个单元格/多次提交引用时,通过addToken/removeToken保持space_asset引用数与实际一致,避免附件被误回收;
  2. 空间类附件上传必须携带 nodeIdtype2/3/4/5uploaduploadFileInSpace分支,缺少合法nodeId会导致上传失败;
  3. readReviews / submitAuditResult 依赖钉钉身份:这两个审核接口虽然声明requiredLogin = false,但会话中必须存在钉钉用户信息,否则返回UNAUTHORIZED
  4. 上传类型不可混淆AssetType以枚举值硬编码(AssetType.java),传未知类型会抛出BusinessException("unknown attachment type")
  5. 响应统一包装:所有接口均返回ResponseData包装结构(success/code/message/data),解析时建议先判断success再取data

参考资料

  • API 文档:BasicModuleAttachmentInterfaceApi.md
  • 客户端 SDK 实现:BasicModuleAttachmentInterfaceApi.ts
  • 后端控制器:AssetController.java
  • 附件类型枚举:AssetType.java
  • 审核服务实现:AssetAuditServiceImpl.java
  • 请求/响应模型:SpaceAssetOpRo.ts、OpAssetRo.ts、AssetsAuditRo.ts、AssetsAuditOpRo.ts、AttachOpRo.ts、AttachUrlOpRo.ts、ResponseDataAssetUploadResult.ts
  • 低代码
  • 后端
  • 前端
  • 协同办公

【免费下载链接】apitable

🚀🎉📚 APITable, an API-oriented low-code platform for building collaborative apps and better than all other Airtable open-source alternatives.

项目地址:https://gitcode.com/apitable/apitable
点击查看免费下载
上一篇:karpenter-provider-aws 常见问题深度解析:NodePool、实例选择与中断处理的 FAQ 及源码级解读
下一篇:从 Total Commander 到 Double Commander:双面板文件管理器的日常高效使用上手指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询