- 低代码
- 后端
- 前端
- 协同办公
【免费下载链接】apitable
🚀🎉📚 APITable, an API-oriented low-code platform for building collaborative apps and better than all other Airtable open-source alternatives.
导读
本指南以 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 请求 | 说明 |
|---|---|---|
| cite | POST/base/attach/cite | 空间附件资源引用数量变化(同一附件需重复传 token) |
| readReviews | GET/base/attach/readReviews | 分页查询需要人工审核的图片 |
| submitAuditResult | POST/base/attach/submitAuditResult | 提交图片审核结果,提交时需填写审核人姓名 |
| upload | POST/base/attach/upload | 上传资源文件,不限制文件类型 |
| urlUpload | POST/base/attach/urlUpload | 图片 URL 上传接口 |
所有端点均标注 "No authorization required"(无需额外鉴权),但源码层面readReviews与submitAuditResult会从会话中读取钉钉用户 ID(SessionContext.getDingtalkUserId())并校验登录态,upload在未登录时则会触发人机校验,详见下文各节。
1. cite —— 空间附件引用计数变更
POST
/base/attach/cite,请求体为SpaceAssetOpRo,返回ResponseDataVoid。
当同一份附件需要被重复引用时(例如表单匿名填写、多单元格引用同一文件),调用本接口告诉服务端"新增/删除了哪些附件引用",从而保证空间附件表(space_asset)中的引用计数与实际使用一致。
请求体模型:SpaceAssetOpRo
对应模型文件 SpaceAssetOpRo.ts,包含三个字段:
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
addToken | Array<OpAssetRo> | 否 | 写入(新增引用)的 token 集合 |
removeToken | Array<OpAssetRo> | 否 | 删除(释放引用)的 token 集合 |
nodeId | string | 是 | 数据表(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(); }从源码可以看出两点关键实现事实:
- 匿名表单填写场景:接口注释明确为 "Fill out the form anonymously"(匿名填写表单),因此
requiredLogin = false,未登录用户也可调用; - 节点存在性校验:服务端会先通过
INodeService.getSpaceIdByNodeIdIncludeDeleted根据nodeId反查所属空间,查不到节点则抛出NODE_NOT_EXIST权限异常,随后才执行datasheetAttachmentCite更新引用计数。
2. readReviews —— 分页查询待人工审核图片
GET
/base/attach/readReviews,返回ResponseDataPageInfoAssetsAuditVo。
当 OSS 云端图片内容审核结果为"需人工复核"时,审核人员通过本接口分页拉取待审核图片列表。
查询参数
| 参数 | 类型 | 说明 | 备注 |
|---|---|---|---|
page | Page | 分页对象 | defaults to undefined |
pageObjectParams | string | 分页参数字符串,如{"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:
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
assetlist | Array<AssetsAuditOpRo> | 否 | 附件人工审核结果列表 |
auditorUserId | string | 是 | 审核用户 Id |
auditorName | string | 是 | 审核用户姓名 |
列表项 AssetsAuditOpRo.ts 包含两个字段:
| 字段 | 类型 | 说明 |
|---|---|---|
assetFileUrl | string | 存储路径,如space/2020/03/27/1243592950910349313 |
auditResultSuggestion | string | 审核结果建议,如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:
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
file | HttpFile | 是 | 二进制文件流(type: binary) |
type | number | 是 | 附件类型(见下方 AssetType 表) |
nodeId | string | 否 | 节点 Id(数据表附件、封面图、节点描述必须传) |
data | string | 否 | 密码登录人机校验值,前端通过 NVC Val 函数获取(未登录时执行人机校验) |
AssetType 类型语义(来自 AssetType.java)
| 值 | 枚举 | 含义 |
|---|---|---|
0 | USER_AVATAR | 用户头像 |
1 | SPACE_LOGO | 空间 Logo |
2 | DATASHEET | 数据表附件 |
3 | COVER | 封面图 |
4 | NODE_DESC | 节点描述 |
5 | DOCUMENT | 文档 |
其中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); }从源码可提炼以下实现要点:
- 未登录人机校验:
SessionContext.getUserIdWithoutException()取不到用户时,会先调用checkBeforeUpload(nodeId, data)执行人机验证(对应AttachOpRo.data字段的用途); - 空间资源分支:
type > 1时调用uploadFileInSpace,需要有效的nodeId才能把文件归入对应空间(这也是模型注释要求"数据表附件、封面图、节点描述必须传 nodeId"的原因); - 用户级资源分支:
type <= 1时走uploadFile,但必须先登录,否则抛UNAUTHORIZED。
5. urlUpload —— 图片 URL 上传
POST
/base/attach/urlUpload,请求体为AttachUrlOpRo,返回ResponseDataAssetUploadResult。
不直接上传文件流,而是传一个图片 URL,由服务端拉取该 URL 对应的图片并转存到对象存储,适合"粘贴外链图片自动转存"的场景。
请求体模型:AttachUrlOpRo
对应模型文件 AttachUrlOpRo.ts:
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
url | string | 是 | 待上传文件的 URL |
type | number | 是 | 附件类型(0用户头像、1空间 Logo、2数据表附件) |
nodeId | string | 否 | 数据表节点 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.8与Content-Type,并应用默认鉴权(defaultAuth?.applySecurityAuthentication); - ResponseProcessor(如
citeWithHttpInfo/uploadWithHttpInfo):负责反序列化响应,200返回对应模型(ResponseDataVoid或ResponseDataAssetUploadResult),500抛出ApiException<ResponseDataVoid>。
例如upload成功时,uploadWithHttpInfo会把响应体解析为ResponseDataAssetUploadResult;而cite、submitAuditResult则解析为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"}]}'常见问题与最佳实践
- 匿名表单场景务必用 cite 维护引用计数:同一附件被多个单元格/多次提交引用时,通过
addToken/removeToken保持space_asset引用数与实际一致,避免附件被误回收; - 空间类附件上传必须携带 nodeId:
type为2/3/4/5时upload走uploadFileInSpace分支,缺少合法nodeId会导致上传失败; - readReviews / submitAuditResult 依赖钉钉身份:这两个审核接口虽然声明
requiredLogin = false,但会话中必须存在钉钉用户信息,否则返回UNAUTHORIZED; - 上传类型不可混淆:
AssetType以枚举值硬编码(AssetType.java),传未知类型会抛出BusinessException("unknown attachment type"); - 响应统一包装:所有接口均返回
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.
相关推荐
APITable 附件上传凭证与签名接口实战:BasicsAttachmentUploadTokenInterfaceApi 完全解析
APITable 附件上传凭证与签名接口实战:BasicsAttachmentUploadTokenInterfaceApi 完全解析 导读 本文围绕 APIT
低代码后端前端协同办公APITable 附件上传回调接口(BasicModuleAccessoryCallbackInterfaceApi)实战指南
APITable 附件上传回调接口(BasicModuleAccessoryCallbackInterfaceApi)实战指南 导读 本文围绕 APITable
低代码后端前端协同办公Headlamp 插件开发指南:深入解析 PodAttachEvent 接口与 Pod 附加(Attach)事件机制
Headlamp 插件开发指南:深入解析 PodAttachEvent 接口与 Pod 附加(Attach)事件机制 PodAttachEvent 是 Head
云原生开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考