跳到主要内容

云梦镜像开放平台 - OpenAPI

版本: v2.0.0
基础URL: https://api.dreamreflex.com/api
协议: HTTP/HTTPS
数据格式: JSON
目标用户: 客户及二次开发者


目录

  1. 概述
  2. 认证方式
  3. 通用规范
  4. 接口列表
  5. 数据模型
  6. 错误码说明
  7. 完整流程示例
  8. 前端实现建议

概述

这是云梦镜像客户服务系统(Customer Service Application - CSA)的API文档,面向客户和二次开发者。本API提供完整的客户服务功能,包括用户认证、工单管理、业务账号管理、合同查看、项目查看等功能。

功能模块

  • 用户认证: 注册、登录、获取用户信息、更新个人资料、修改密码
  • 工单管理: 创建、查询、更新、删除工单(仅限自己的工单)
  • 业务账号管理: 申请、查看、更新业务账号信息
  • 合同管理: 查看自己的合同列表和详情,更新合同的签署状态
  • 项目管理: 查看自己的项目、文件、链接和任务
  • 联系我们: 提交联系表单

技术特点

  • RESTful API设计
  • JWT Token认证
  • OAuth2.0标准支持
  • 完整的错误处理
  • 分页查询支持

权限说明

  • 普通用户: 可以管理自己的工单、业务账号、查看和管理自己的合同和项目
  • 数据隔离: 用户只能访问自己创建或拥有的资源
  • 管理员功能: 管理员专用接口不在本文档中,如需管理员功能请联系技术支持

认证方式

Token类型

使用 Bearer Token 方式进行身份认证。

如何获取Token

通过登录接口获取Token:

POST /api/auth/login

如何使用Token

在所有需要认证的接口中,添加以下请求头:

Authorization: Bearer <your_access_token>

Token有效期

  • 访问令牌(Access Token): 30分钟
  • Token过期后需要重新登录
  • 建议在Token即将过期前提示用户重新登录

通用规范

HTTP方法

  • GET - 获取资源
  • POST - 创建资源
  • PUT - 更新资源
  • PATCH - 部分更新资源
  • DELETE - 删除资源

请求头

必需的请求头

Content-Type: application/json

需要认证的接口额外添加

Authorization: Bearer <token>

响应格式

成功响应

所有成功的响应都包含实际的数据对象。

示例

{
"id": "507f1f77bcf86cd799439011",
"email": "user@example.com",
"role": "user"
}

错误响应

所有错误响应遵循统一格式:

{
"detail": "错误描述信息"
}

状态码

状态码说明场景
200成功GET、PUT、PATCH请求成功
201已创建POST创建资源成功
204无内容DELETE删除成功
400请求错误参数验证失败或业务逻辑错误
401未授权未提供Token或Token无效
403禁止访问权限不足(如访问他人的资源)
404未找到资源不存在
422参数验证失败请求数据格式错误
500服务器错误服务器内部错误

分页规范

所有列表接口支持分页,使用以下查询参数:

参数类型必填默认值说明
skipinteger0跳过的记录数(用于分页)
limitinteger10返回的记录数(最大100)

分页计算示例

// 获取第2页(每页10条)
const page = 2;
const pageSize = 10;
const skip = (page - 1) * pageSize; // skip = 10
const limit = pageSize; // limit = 10

// 计算总页数
const totalPages = Math.ceil(total / limit);

接口列表

1. 认证模块

1.1 发送邮箱验证码

接口说明: 向指定邮箱发送6位数字验证码,用于注册。

接口地址: POST /api/auth/send-code

是否需要认证: 否

请求参数:

{
"email": "string" // 必填,邮箱地址,需符合邮箱格式
}

请求示例:

POST /api/auth/send-code
Content-Type: application/json

{
"email": "user@example.com"
}

成功响应: 200 OK

{
"message": "验证码已发送",
"email": "user@example.com"
}

错误响应:

状态码说明
422邮箱格式不正确
500邮件发送失败

注意事项:

  • 验证码有效期为10分钟
  • 同一邮箱建议限制发送频率(前端控制,如60秒内只能发送一次)
  • 开发环境下验证码会输出到服务器日志

1.2 用户注册

接口说明: 使用邮箱和验证码注册新用户。

接口地址: POST /api/auth/register

是否需要认证: 否

请求参数:

{
"email": "string", // 必填,邮箱地址
"password": "string", // 必填,密码(8-128位)
"verification_code": "string" // 必填,6位数字验证码
}

参数限制:

  • email: 必须是有效的邮箱格式
  • password: 长度8-128位,支持字母、数字、特殊字符
  • verification_code: 必须是6位数字

请求示例:

POST /api/auth/register
Content-Type: application/json

{
"email": "user@example.com",
"password": "mypassword123",
"verification_code": "123456"
}

成功响应: 201 Created

{
"id": "507f1f77bcf86cd799439011",
"email": "user@example.com",
"role": "user",
"is_active": true,
"created_at": "2024-01-01T00:00:00",
"updated_at": null,
"full_name": null,
"phone_number": null,
"avatar_url": null,
"bio": null
}

错误响应:

状态码说明detail
400验证码无效"验证码无效或已过期"
400邮箱已注册"该邮箱已被注册"
422参数验证失败"密码长度至少为8位" 等

注意事项:

  • 注册后默认角色为 user(普通用户)
  • 账号默认为激活状态
  • 密码会经过安全加密存储(Argon2)

1.3 用户登录(JSON格式)

接口说明: 使用邮箱和密码登录,获取访问令牌。

接口地址: POST /api/auth/login

是否需要认证: 否

请求参数:

{
"email": "string", // 必填,邮箱地址
"password": "string" // 必填,密码
}

请求示例:

POST /api/auth/login
Content-Type: application/json

{
"email": "user@example.com",
"password": "mypassword123"
}

成功响应: 200 OK

{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 1800
}

响应字段说明:

  • access_token: 访问令牌,用于后续API调用
  • token_type: 令牌类型,固定为 "bearer"
  • expires_in: 令牌有效期(秒),默认1800秒(30分钟)

错误响应:

状态码说明detail
401登录失败"邮箱或密码错误"
403账号被禁用"账号已被禁用"

注意事项:

  • Token应该安全存储在前端(localStorage或sessionStorage)
  • Token过期后需要重新登录
  • 建议在Token即将过期前提示用户

1.4 用户登录(OAuth2标准)

接口说明: OAuth2标准登录接口,用于工具集成。

接口地址: POST /api/auth/token

是否需要认证: 否

请求格式: application/x-www-form-urlencoded

请求参数:

username=user@example.com&password=mypassword123

注意: 虽然参数名是 username,但请填写邮箱地址。

成功响应: 同 /api/auth/login

使用场景: 主要用于Swagger UI认证,前端建议使用 /api/auth/login


1.5 获取当前用户信息

接口说明: 获取当前登录用户的详细信息。

接口地址: GET /api/auth/meGET /api/users/me

是否需要认证: 是

请求示例:

GET /api/auth/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

成功响应: 200 OK

{
"id": "507f1f77bcf86cd799439011",
"email": "user@example.com",
"role": "user",
"is_active": true,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-02-01T12:34:56Z",
"full_name": "张三",
"phone_number": "+86-13800000000",
"avatar_url": "https://cdn.example.com/avatar.png",
"bio": "负责北区客户成功支持。"
}

响应字段说明:

  • id: 用户唯一标识
  • email: 用户邮箱
  • role: 用户角色("user" 或 "admin")
  • is_active: 账号是否激活
  • created_at: 账号创建时间(ISO 8601格式,UTC)
  • updated_at: 账号最近更新时间(可能为 null
  • full_name: 用户姓名(可选)
  • phone_number: 联系方式(可选)
  • avatar_url: 头像URL(可选)
  • bio: 个人简介(可选)

错误响应:

状态码说明
401Token无效或已过期
403账号已被禁用

1.6 更新当前用户资料

接口说明: 修改当前登录用户的基础资料(姓名、联系方式、头像、个人简介)。

接口地址: PATCH /api/users/me

是否需要认证: 是

请求示例:

PATCH /api/users/me
Authorization: Bearer <token>
Content-Type: application/json

{
"full_name": "张三",
"phone_number": "+86-13800000000",
"bio": "负责北区客户成功支持。"
}

请求字段说明(全部可选,至少提供一个字段):

字段类型说明
full_namestring用户姓名,最长64字符
phone_numberstring联系方式,最长32字符
avatar_urlstring头像URL
biostring个人简介,最长256字符

成功响应: 200 OK,返回更新后的 User 对象(同 GET /api/auth/me

错误响应:

状态码说明
400未提供任何需要更新的字段
401用户未登录或Token无效

1.7 修改当前用户密码

接口说明: 用户自助修改登录密码。

接口地址: POST /api/users/me/change-password

是否需要认证: 是

请求示例:

POST /api/users/me/change-password
Authorization: Bearer <token>
Content-Type: application/json

{
"old_password": "OldPassw0rd!",
"new_password": "NewPassw0rd!"
}

请求字段说明:

字段类型说明
old_passwordstring当前密码(必填)
new_passwordstring新密码(必填,8-128字符)

成功响应: 200 OK

{
"message": "密码修改成功"
}

错误响应:

状态码说明
400当前密码不正确或与新密码相同
401用户未登录或Token无效

2. 工单模块

2.1 创建工单

接口说明: 创建新的工单。

接口地址: POST /api/tickets

是否需要认证: 是

权限要求: 所有登录用户

请求参数:

{
"title": "string", // 必填,工单标题(1-200字符)
"ticket_type": "string", // 必填,工单类型
"content": "string" // 必填,工单内容(至少1字符)
}

工单类型 (ticket_type):

说明
financial财务问题工单
technical技术问题工单
business业务问题工单

请求示例:

POST /api/tickets
Authorization: Bearer <token>
Content-Type: application/json

{
"title": "无法导出财务报表",
"ticket_type": "financial",
"content": "在尝试导出本月财务报表时,系统提示"导出失败",请帮忙处理。"
}

成功响应: 201 Created

{
"id": "507f1f77bcf86cd799439012",
"title": "无法导出财务报表",
"ticket_type": "financial",
"content": "在尝试导出本月财务报表时,系统提示"导出失败",请帮忙处理。",
"status": "pending",
"feedback": null,
"creator_email": "user@example.com",
"responder_email": null,
"created_at": "2024-01-01T10:00:00",
"updated_at": null,
"replied_at": null
}

错误响应:

状态码说明
401未登录或Token无效
422参数验证失败(如标题过长、类型错误等)

2.2 获取工单列表

接口说明: 获取工单列表,支持分页和筛选。普通用户只能看到自己创建的工单

接口地址: GET /api/tickets

是否需要认证: 是

权限说明:

  • 普通用户:只能看到自己创建的工单
  • 支持分页和状态过滤

查询参数:

参数类型必填默认值说明
skipinteger0跳过的记录数(用于分页)
limitinteger10返回的记录数(最大100)
status_filterstring-按状态筛选

状态筛选 (status_filter):

说明
pending仅显示待回复的工单
replied仅显示已回复的工单
不传显示所有状态

请求示例:

GET /api/tickets?skip=0&limit=10&status_filter=pending
Authorization: Bearer <token>

成功响应: 200 OK

{
"total": 25,
"items": [
{
"id": "507f1f77bcf86cd799439012",
"title": "无法导出财务报表",
"ticket_type": "financial",
"content": "在尝试导出本月财务报表时...",
"status": "pending",
"feedback": null,
"creator_email": "user@example.com",
"responder_email": null,
"created_at": "2024-01-01T10:00:00",
"updated_at": null,
"replied_at": null
},
{
"id": "507f1f77bcf86cd799439013",
"title": "系统登录缓慢",
"ticket_type": "technical",
"content": "最近几天登录系统非常慢...",
"status": "replied",
"feedback": "已优化服务器性能,请重试。",
"creator_email": "user@example.com",
"responder_email": "admin@example.com",
"created_at": "2024-01-01T09:00:00",
"updated_at": "2024-01-01T12:00:00",
"replied_at": "2024-01-01T12:00:00"
}
]
}

响应字段说明:

  • total: 符合条件的工单总数
  • items: 当前页的工单列表

错误响应:

状态码说明
401未登录
422参数验证失败(如limit超过100)

2.3 获取工单详情

接口说明: 获取指定工单的详细信息。普通用户只能查看自己的工单

接口地址: GET /api/tickets/{ticket_id}

是否需要认证: 是

权限说明:

  • 普通用户:只能查看自己的工单

路径参数:

参数类型说明
ticket_idstring工单ID

请求示例:

GET /api/tickets/507f1f77bcf86cd799439012
Authorization: Bearer <token>

成功响应: 200 OK

{
"id": "507f1f77bcf86cd799439012",
"title": "无法导出财务报表",
"ticket_type": "financial",
"content": "在尝试导出本月财务报表时,系统提示"导出失败",请帮忙处理。",
"status": "pending",
"feedback": null,
"creator_email": "user@example.com",
"responder_email": null,
"created_at": "2024-01-01T10:00:00",
"updated_at": null,
"replied_at": null
}

错误响应:

状态码说明
401未登录
403无权访问此工单(不是创建者)
404工单不存在

2.4 更新工单

接口说明: 更新工单的标题和内容。只能更新自己创建的、状态为 pending(待回复)的工单

接口地址: PUT /api/tickets/{ticket_id}

是否需要认证: 是

权限要求:

  • 只能更新自己创建的工单
  • 只能更新状态为 pending(待回复)的工单
  • 已回复的工单不能再更新

路径参数:

参数类型说明
ticket_idstring工单ID

请求参数:

{
"title": "string", // 可选,工单标题(1-200字符)
"content": "string" // 可选,工单内容(至少1字符)
}

注意: 至少提供一个字段,可以只更新标题或只更新内容。

请求示例:

PUT /api/tickets/507f1f77bcf86cd799439012
Authorization: Bearer <token>
Content-Type: application/json

{
"title": "无法导出财务报表(已重试)",
"content": "在尝试导出本月财务报表时,系统提示"导出失败"。我已经重试了多次,问题依然存在。"
}

成功响应: 200 OK

{
"id": "507f1f77bcf86cd799439012",
"title": "无法导出财务报表(已重试)",
"ticket_type": "financial",
"content": "在尝试导出本月财务报表时,系统提示"导出失败"。我已经重试了多次,问题依然存在。",
"status": "pending",
"feedback": null,
"creator_email": "user@example.com",
"responder_email": null,
"created_at": "2024-01-01T10:00:00",
"updated_at": "2024-01-01T10:30:00",
"replied_at": null
}

错误响应:

状态码说明
400工单已被回复,不能再更新
401未登录
403无权更新此工单(不是创建者)
404工单不存在
422参数验证失败

2.5 删除工单

接口说明: 删除指定的工单。只能删除自己创建的工单

接口地址: DELETE /api/tickets/{ticket_id}

是否需要认证: 是

权限说明:

  • 普通用户:可以删除自己创建的工单

路径参数:

参数类型说明
ticket_idstring工单ID

请求示例:

DELETE /api/tickets/507f1f77bcf86cd799439012
Authorization: Bearer <token>

成功响应: 204 No Content

(无响应体)

错误响应:

状态码说明
401未登录
403无权删除此工单
404工单不存在

3. 业务账号模块

3.1 获取当前用户的业务账号

接口说明: 获取当前登录用户的业务账号信息。

接口地址: GET /api/business-accounts/me

是否需要认证: 是

请求示例:

GET /api/business-accounts/me
Authorization: Bearer <token>

成功响应: 200 OK

{
"id": "507f1f77bcf86cd799439020",
"owner_email": "user@example.com",
"status": "active",
"assigned_email": "user@company.com",
"user_code": "USER001",
"company_name": "示例公司",
"tax_number": "91110000MA01234567",
"certification_info": "营业执照等认证信息",
"communication_address": "北京市朝阳区xxx",
"contact_person_name": "张三",
"contact_phone": "+86-13800000000",
"created_at": "2024-01-01T00:00:00",
"updated_at": "2024-01-15T10:30:00"
}

业务账号状态 (status):

说明
pending待审核(已申请,等待管理员审核)
active已激活(已分配专属邮箱,可以正常使用)
disabled已禁用

错误响应:

状态码说明
401未登录
404业务账号不存在(未申请或未创建)

3.2 申请创建业务账号

接口说明: 申请创建业务账号。如果当前用户尚无业务账号,则创建一条 pending 状态的记录。

接口地址: POST /api/business-accounts/me/apply

是否需要认证: 是

请求参数:

{
"company_name": "string", // 可选,公司抬头(最长128字符)
"tax_number": "string", // 可选,税号(最长64字符)
"certification_info": "string", // 可选,认证信息(最长512字符)
"communication_address": "string", // 可选,通信地址(最长256字符)
"contact_person_name": "string", // 可选,联系人姓名(最长64字符)
"contact_phone": "string" // 可选,联系方式(最长32字符)
}

注意: 所有字段都是可选的,但建议尽可能填写完整信息以便审核。

请求示例:

POST /api/business-accounts/me/apply
Authorization: Bearer <token>
Content-Type: application/json

{
"company_name": "示例公司",
"tax_number": "91110000MA01234567",
"certification_info": "营业执照等认证信息",
"communication_address": "北京市朝阳区xxx",
"contact_person_name": "张三",
"contact_phone": "+86-13800000000"
}

成功响应: 201 Created

{
"id": "507f1f77bcf86cd799439020",
"owner_email": "user@example.com",
"status": "pending",
"assigned_email": null,
"user_code": null,
"company_name": "示例公司",
"tax_number": "91110000MA01234567",
"certification_info": "营业执照等认证信息",
"communication_address": "北京市朝阳区xxx",
"contact_person_name": "张三",
"contact_phone": "+86-13800000000",
"created_at": "2024-01-01T00:00:00",
"updated_at": null
}

错误响应:

状态码说明
400已存在申请或已激活账号
401未登录
422参数验证失败

3.3 更新当前用户的业务账号资料

接口说明: 更新当前登录用户业务账号中可编辑字段。

接口地址: PATCH /api/business-accounts/me

是否需要认证: 是

可编辑字段:

  • 公司抬头
  • 税号
  • 认证信息
  • 通信地址
  • 联系人姓名
  • 联系方式

注意: 专属邮箱(assigned_email)和用户识别码(user_code)由管理员分配,用户无法修改。

请求参数:

{
"company_name": "string", // 可选,公司抬头
"tax_number": "string", // 可选,税号
"certification_info": "string", // 可选,认证信息
"communication_address": "string", // 可选,通信地址
"contact_person_name": "string", // 可选,联系人姓名
"contact_phone": "string" // 可选,联系方式
}

请求示例:

PATCH /api/business-accounts/me
Authorization: Bearer <token>
Content-Type: application/json

{
"company_name": "更新后的公司名称",
"contact_phone": "+86-13900000000"
}

成功响应: 200 OK,返回更新后的业务账号对象

错误响应:

状态码说明
401未登录
404业务账号不存在
422参数验证失败

3.4 获取业务账号详情

接口说明: 获取业务账号详情。普通用户只能查看自己的业务账号

接口地址: GET /api/business-accounts/{account_id}

是否需要认证: 是

权限说明:

  • 普通用户只能查看自己的业务账号

路径参数:

参数类型说明
account_idstring业务账号ID

请求示例:

GET /api/business-accounts/507f1f77bcf86cd799439020
Authorization: Bearer <token>

成功响应: 200 OK,返回业务账号对象(同 GET /api/business-accounts/me

错误响应:

状态码说明
401未登录
403无权访问此业务账号
404业务账号不存在

4. 合同模块

4.1 获取我的合同列表

接口说明: 获取当前登录用户自己的合同列表。

接口地址: GET /api/contracts/me

是否需要认证: 是

查询参数:

参数类型必填默认值说明
skipinteger0跳过的记录数(用于分页)
limitinteger10返回的记录数(最大100)

请求示例:

GET /api/contracts/me?skip=0&limit=10
Authorization: Bearer <token>

成功响应: 200 OK

{
"total": 5,
"items": [
{
"id": "507f1f77bcf86cd799439030",
"owner_email": "user@example.com",
"title": "云服务合同(2025版)",
"content_markdown": "# 合同正文\n\n这里是 Markdown 格式的合同内容……",
"is_signed": false,
"created_at": "2024-01-01T00:00:00",
"updated_at": "2024-01-15T10:30:00"
}
]
}

响应字段说明:

  • total: 符合条件的合同总数
  • items: 当前页的合同列表
  • is_signed: 签署状态,true 表示已签署,false 表示待签署
  • content_markdown: 合同内容为 Markdown 格式,前端需要渲染

错误响应:

状态码说明
401未登录
422参数验证失败

4.2 获取合同详情

接口说明: 获取指定合同的详细信息。普通用户只能查看自己的合同

接口地址: GET /api/contracts/{contract_id}

是否需要认证: 是

权限说明:

  • 普通用户只能查看自己的合同

路径参数:

参数类型说明
contract_idstring合同ID

请求示例:

GET /api/contracts/507f1f77bcf86cd799439030
Authorization: Bearer <token>

成功响应: 200 OK

{
"id": "507f1f77bcf86cd799439030",
"owner_email": "user@example.com",
"title": "云服务合同(2025版)",
"content_markdown": "# 合同正文\n\n这里是 Markdown 格式的合同内容……",
"is_signed": false,
"created_at": "2024-01-01T00:00:00",
"updated_at": "2024-01-15T10:30:00"
}

错误响应:

状态码说明
401未登录
403无权访问此合同
404合同不存在

4.3 更新合同签署状态

接口说明: 更新指定合同的签署状态。普通用户只能更新自己的合同

接口地址: PATCH /api/contracts/{contract_id}/sign-status

是否需要认证: 是

权限说明:

  • 普通用户只能更新自己的合同
  • 管理员可以更新任意合同

路径参数:

参数类型说明
contract_idstring合同ID

请求体:

{
"is_signed": true
}

请求体字段说明:

字段类型必填说明
is_signedboolean签署状态,true 表示已签署,false 表示待签署

请求示例:

PATCH /api/contracts/507f1f77bcf86cd799439030/sign-status
Authorization: Bearer <token>
Content-Type: application/json

{
"is_signed": true
}

成功响应: 200 OK

{
"id": "507f1f77bcf86cd799439030",
"owner_email": "user@example.com",
"title": "云服务合同(2025版)",
"content_markdown": "# 合同正文\n\n这里是 Markdown 格式的合同内容……",
"is_signed": true,
"created_at": "2024-01-01T00:00:00",
"updated_at": "2024-01-15T10:30:00"
}

错误响应:

状态码说明
401未登录
403无权更新此合同的签署状态
404合同不存在
422参数验证失败

使用场景:

  • 用户完成合同签署后,更新签署状态为已签署
  • 需要修改签署状态时,可以随时更新

5. 项目模块

5.1 获取我的项目列表

接口说明: 获取当前登录用户自己的项目列表。

接口地址: GET /api/projects/me

是否需要认证: 是

查询参数:

参数类型必填默认值说明
skipinteger0跳过的记录数(用于分页)
limitinteger10返回的记录数(最大100)

请求示例:

GET /api/projects/me?skip=0&limit=10
Authorization: Bearer <token>

成功响应: 200 OK

{
"total": 3,
"items": [
{
"id": "507f1f77bcf86cd799439040",
"owner_email": "user@example.com",
"name": "示例项目",
"description": "这是一个示例项目",
"overview_markdown": "# 项目概述\n\n这里是项目概述内容...",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
]
}

响应字段说明:

  • total: 符合条件的项目总数
  • items: 当前页的项目列表
  • overview_markdown: 项目概述为 Markdown 格式,前端需要渲染

错误响应:

状态码说明
401未登录
422参数验证失败

5.2 获取项目详情

接口说明: 获取指定项目的详细信息。普通用户只能查看自己的项目

接口地址: GET /api/projects/{project_id}

是否需要认证: 是

权限说明:

  • 普通用户只能查看自己的项目

路径参数:

参数类型说明
project_idstring项目ID

请求示例:

GET /api/projects/507f1f77bcf86cd799439040
Authorization: Bearer <token>

成功响应: 200 OK

{
"id": "507f1f77bcf86cd799439040",
"owner_email": "user@example.com",
"name": "示例项目",
"description": "这是一个示例项目",
"overview_markdown": "# 项目概述\n\n这里是项目概述内容...",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}

错误响应:

状态码说明
401未登录
403无权访问此项目
404项目不存在

5.3 获取项目文件列表

接口说明: 获取项目文件列表,支持按文件名和标签筛选。普通用户只能查看自己项目的文件

接口地址: GET /api/projects/{project_id}/files

是否需要认证: 是

权限说明:

  • 普通用户只能查看自己项目的文件

路径参数:

参数类型说明
project_idstring项目ID

查询参数:

参数类型必填默认值说明
skipinteger0跳过的记录数
limitinteger100返回的记录数(最大500)
filenamestring-按文件名筛选(模糊匹配)
tagstring-按标签筛选(精确匹配)

请求示例:

GET /api/projects/507f1f77bcf86cd799439040/files?skip=0&limit=100&tag=文档
Authorization: Bearer <token>

成功响应: 200 OK

{
"total": 10,
"items": [
{
"id": "507f1f77bcf86cd799439050",
"project_id": "507f1f77bcf86cd799439040",
"filename": "项目文档.pdf",
"file_size": 1024000,
"content_type": "application/pdf",
"tag": "文档",
"oss_key": "projects/507f1f77bcf86cd799439040/files/项目文档.pdf",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": null
}
]
}

错误响应:

状态码说明
401未登录
403无权访问此项目
404项目不存在

5.4 获取文件下载预签名URL

接口说明: 获取文件下载预签名URL。客户端使用此URL直接下载文件,避免文件流量经过服务器。普通用户只能下载自己项目的文件

接口地址: POST /api/projects/files/{file_id}/download-url

是否需要认证: 是

权限说明:

  • 普通用户只能下载自己项目的文件

路径参数:

参数类型说明
file_idstring文件ID

请求示例:

POST /api/projects/files/507f1f77bcf86cd799439050/download-url
Authorization: Bearer <token>

成功响应: 200 OK

{
"download_url": "https://oss.example.com/projects/...?signature=...",
"expires_in": 3600
}

响应字段说明:

  • download_url: 预签名下载URL,可直接用于下载文件
  • expires_in: URL有效期(秒),通常为3600秒(1小时)

使用示例:

// 获取下载URL
const response = await fetch('/api/projects/files/507f1f77bcf86cd799439050/download-url', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
});
const { download_url } = await response.json();

// 直接使用URL下载文件
window.open(download_url);
// 或使用 fetch 下载
const fileResponse = await fetch(download_url);
const blob = await fileResponse.blob();
// 处理文件...

错误响应:

状态码说明
401未登录
403无权访问此文件
404文件不存在

5.5 获取项目链接列表

接口说明: 获取项目链接列表。普通用户只能查看自己项目的链接

接口地址: GET /api/projects/{project_id}/links

是否需要认证: 是

权限说明:

  • 普通用户只能查看自己项目的链接

路径参数:

参数类型说明
project_idstring项目ID

查询参数:

参数类型必填默认值说明
skipinteger0跳过的记录数
limitinteger100返回的记录数(最大500)

请求示例:

GET /api/projects/507f1f77bcf86cd799439040/links?skip=0&limit=100
Authorization: Bearer <token>

成功响应: 200 OK

{
"total": 5,
"items": [
{
"id": "507f1f77bcf86cd799439060",
"project_id": "507f1f77bcf86cd799439040",
"title": "项目文档链接",
"url": "https://docs.example.com/project",
"description": "项目相关文档",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": null
}
]
}

错误响应:

状态码说明
401未登录
403无权访问此项目
404项目不存在

5.6 获取项目任务列表

接口说明: 获取项目任务列表,支持按状态筛选。普通用户只能查看自己项目的任务

接口地址: GET /api/projects/{project_id}/tasks

是否需要认证: 是

权限说明:

  • 普通用户只能查看自己项目的任务

路径参数:

参数类型说明
project_idstring项目ID

查询参数:

参数类型必填默认值说明
skipinteger0跳过的记录数
limitinteger100返回的记录数(最大500)
statusstring-按状态筛选(pending=未完成, completed=已完成)

请求示例:

GET /api/projects/507f1f77bcf86cd799439040/tasks?skip=0&limit=100&status=pending
Authorization: Bearer <token>

成功响应: 200 OK

{
"total": 8,
"items": [
{
"id": "507f1f77bcf86cd799439070",
"project_id": "507f1f77bcf86cd799439040",
"title": "完成项目文档",
"description": "编写项目相关文档",
"status": "pending",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": null
}
]
}

任务状态 (status):

说明
pending未完成
completed已完成

错误响应:

状态码说明
401未登录
403无权访问此项目
404项目不存在

6. 联系我们模块

6.1 提交联系表单

接口说明: 提交联系我们表单(公开接口,需要 Cloudflare Turnstile 验证)。

接口地址: POST /api/owa/info

是否需要认证: 否

请求参数:

{
"name": "string", // 必填,称呼
"email": "string", // 必填,邮箱(不进行格式校验,允许任意字符串)
"message": "string", // 必填,消息内容
"turnstileToken": "string" // 必填,Cloudflare Turnstile 验证令牌
}

请求示例:

POST /api/owa/info
Content-Type: application/json

{
"name": "张三",
"email": "user@example.com",
"message": "我想咨询一下产品价格",
"turnstileToken": "0.xxx..."
}

成功响应: 201 Created

{
"id": "507f1f77bcf86cd799439080",
"name": "张三",
"email": "user@example.com",
"message": "我想咨询一下产品价格",
"is_processed": false,
"admin_notes": null,
"created_at": "2024-01-01T10:00:00"
}

错误响应:

状态码说明
400Turnstile验证失败
422参数验证失败

注意事项:

  • 提交后会自动发送通知邮件给管理员
  • 需要在前端集成 Cloudflare Turnstile 进行人机验证
  • 验证令牌有效期为几分钟,请及时提交

数据模型

User(用户)

{
id: string; // 用户唯一标识
email: string; // 邮箱地址
role: "user" | "admin"; // 用户角色
is_active: boolean; // 账号是否激活
created_at: string; // 创建时间(ISO 8601,UTC)
updated_at: string | null; // 最后更新时间,可能为 null
full_name: string | null; // 姓名
phone_number: string | null; // 联系方式
avatar_url: string | null; // 头像链接
bio: string | null; // 个人简介
}

Ticket(工单)

{
id: string; // 工单唯一标识
title: string; // 工单标题
ticket_type: "financial" | "technical" | "business"; // 工单类型
content: string; // 工单内容
status: "pending" | "replied"; // 工单状态
feedback: string | null; // 反馈结果(管理员回复)
creator_email: string; // 创建者邮箱
responder_email: string | null; // 回复者邮箱(管理员)
created_at: string; // 创建时间(ISO 8601)
updated_at: string | null; // 更新时间(ISO 8601)
replied_at: string | null; // 回复时间(ISO 8601)
}

BusinessAccount(业务账号)

{
id: string; // 业务账号唯一标识
owner_email: string; // 所属用户邮箱
status: "pending" | "active" | "disabled"; // 业务账号状态
assigned_email: string | null; // 专属邮箱(管理员分配)
user_code: string | null; // 用户识别码(管理员分配)
company_name: string | null; // 公司抬头
tax_number: string | null; // 税号
certification_info: string | null; // 认证信息
communication_address: string | null; // 通信地址
contact_person_name: string | null; // 联系人姓名
contact_phone: string | null; // 联系方式
created_at: string; // 创建时间(ISO 8601)
updated_at: string | null; // 更新时间(ISO 8601)
}

Contract(合同)

{
id: string; // 合同唯一标识
owner_email: string; // 所属用户邮箱
title: string; // 合同标题
content_markdown: string; // 合同内容(Markdown格式)
is_signed: boolean; // 签署状态,true表示已签署,false表示待签署
created_at: string; // 创建时间(ISO 8601)
updated_at: string | null; // 更新时间(ISO 8601)
}

Project(项目)

{
id: string; // 项目唯一标识
owner_email: string; // 所属用户邮箱
name: string; // 项目名称
description: string | null; // 项目描述
overview_markdown: string; // 项目概述(Markdown格式)
created_at: string; // 创建时间(ISO 8601,UTC)
updated_at: string | null; // 更新时间(ISO 8601,UTC)
}

ProjectFile(项目文件)

{
id: string; // 文件唯一标识
project_id: string; // 所属项目ID
filename: string; // 文件名
file_size: number; // 文件大小(字节)
content_type: string; // 文件MIME类型
tag: string | null; // 文件标签
oss_key: string; // OSS存储键
created_at: string; // 创建时间(ISO 8601,UTC)
updated_at: string | null; // 更新时间(ISO 8601,UTC)
}

ProjectLink(项目链接)

{
id: string; // 链接唯一标识
project_id: string; // 所属项目ID
title: string; // 链接标题
url: string; // 链接URL
description: string | null; // 链接描述
created_at: string; // 创建时间(ISO 8601,UTC)
updated_at: string | null; // 更新时间(ISO 8601,UTC)
}

ProjectTask(项目任务)

{
id: string; // 任务唯一标识
project_id: string; // 所属项目ID
title: string; // 任务标题
description: string | null; // 任务描述
status: "pending" | "completed"; // 任务状态
created_at: string; // 创建时间(ISO 8601,UTC)
updated_at: string | null; // 更新时间(ISO 8601,UTC)
}

Token(令牌)

{
access_token: string; // 访问令牌
token_type: "bearer"; // 令牌类型
expires_in: number; // 有效期(秒)
}

错误码说明

错误响应格式

{
"detail": "错误描述信息"
}

常见错误

状态码场景detail示例前端处理建议
400业务逻辑错误"验证码无效或已过期"提示用户重新操作
401未认证"无效的认证凭据"跳转到登录页
403权限不足"无权访问此资源"提示权限不足
404资源不存在"工单不存在"提示资源不存在
422参数验证失败"密码长度至少为8位"显示验证错误信息
500服务器错误"Internal Server Error"提示稍后重试

错误处理示例

fetch('/api/tickets', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => {
if (!response.ok) {
// 处理错误
if (response.status === 401) {
// Token无效,跳转登录
window.location.href = '/login';
} else if (response.status === 403) {
// 权限不足
alert('权限不足');
}
return response.json().then(err => {
throw new Error(err.detail);
});
}
return response.json();
})
.then(data => {
// 处理成功响应
console.log(data);
})
.catch(error => {
// 处理错误
console.error(error.message);
});

完整流程示例

场景1: 新用户注册并创建工单

// 步骤1: 发送验证码
fetch('http://localhost:8000/api/auth/send-code', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'newuser@example.com'
})
})
.then(res => res.json())
.then(data => console.log('验证码已发送'));

// 步骤2: 用户输入收到的验证码,进行注册
fetch('http://localhost:8000/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'newuser@example.com',
password: 'mypassword123',
verification_code: '123456' // 用户输入的验证码
})
})
.then(res => res.json())
.then(user => console.log('注册成功:', user));

// 步骤3: 登录获取Token
fetch('http://localhost:8000/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'newuser@example.com',
password: 'mypassword123'
})
})
.then(res => res.json())
.then(data => {
const token = data.access_token;
// 存储token
localStorage.setItem('token', token);

// 步骤4: 使用token创建工单
return fetch('http://localhost:8000/api/tickets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
title: '账单查询问题',
ticket_type: 'financial',
content: '无法查看本月账单明细'
})
});
})
.then(res => res.json())
.then(ticket => console.log('工单创建成功:', ticket));

场景2: 用户查看和更新工单

const token = localStorage.getItem('token');

// 步骤1: 获取工单列表
fetch('http://localhost:8000/api/tickets?skip=0&limit=10', {
headers: { 'Authorization': `Bearer ${token}` }
})
.then(res => res.json())
.then(data => {
console.log(`共有 ${data.total} 个工单`);
const firstTicket = data.items[0];

// 步骤2: 查看第一个工单详情
return fetch(`http://localhost:8000/api/tickets/${firstTicket.id}`, {
headers: { 'Authorization': `Bearer ${token}` }
});
})
.then(res => res.json())
.then(ticket => {
console.log('工单详情:', ticket);

// 步骤3: 如果工单状态是pending,可以更新
if (ticket.status === 'pending') {
return fetch(`http://localhost:8000/api/tickets/${ticket.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
content: ticket.content + '\n\n补充:我已经尝试重新登录,问题依然存在。'
})
});
}
})
.then(res => res && res.json())
.then(updated => updated && console.log('工单已更新:', updated));

场景3: 查看业务账号和合同

const token = localStorage.getItem('token');

// 步骤1: 获取业务账号
fetch('http://localhost:8000/api/business-accounts/me', {
headers: { 'Authorization': `Bearer ${token}` }
})
.then(res => res.json())
.then(account => {
console.log('业务账号:', account);

// 步骤2: 获取合同列表
return fetch('http://localhost:8000/api/contracts/me?skip=0&limit=10', {
headers: { 'Authorization': `Bearer ${token}` }
});
})
.then(res => res.json())
.then(data => {
console.log(`共有 ${data.total} 个合同`);
if (data.items.length > 0) {
// 查看第一个合同详情
return fetch(`http://localhost:8000/api/contracts/${data.items[0].id}`, {
headers: { 'Authorization': `Bearer ${token}` }
});
}
})
.then(res => res && res.json())
.then(contract => {
console.log('合同详情:', contract);

// 步骤3: 更新合同签署状态(示例:标记为已签署)
if (contract && !contract.is_signed) {
return fetch(`http://localhost:8000/api/contracts/${contract.id}/sign-status`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ is_signed: true })
});
}
})
.then(res => res && res.json())
.then(updatedContract => updatedContract && console.log('签署状态已更新:', updatedContract));

场景4: 查看项目和下载文件

const token = localStorage.getItem('token');

// 步骤1: 获取项目列表
fetch('http://localhost:8000/api/projects/me?skip=0&limit=10', {
headers: { 'Authorization': `Bearer ${token}` }
})
.then(res => res.json())
.then(data => {
console.log(`共有 ${data.total} 个项目`);
if (data.items.length > 0) {
const project = data.items[0];

// 步骤2: 获取项目文件列表
return fetch(`http://localhost:8000/api/projects/${project.id}/files?skip=0&limit=100`, {
headers: { 'Authorization': `Bearer ${token}` }
});
}
})
.then(res => res && res.json())
.then(fileData => {
if (fileData && fileData.items.length > 0) {
const file = fileData.items[0];

// 步骤3: 获取文件下载URL
return fetch(`http://localhost:8000/api/projects/files/${file.id}/download-url`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` }
});
}
})
.then(res => res && res.json())
.then(downloadData => {
if (downloadData) {
console.log('下载URL:', downloadData.download_url);
// 使用下载URL下载文件
window.open(downloadData.download_url);
}
});

前端实现建议

1. Token管理

// Token存储
class AuthService {
setToken(token) {
localStorage.setItem('access_token', token);
}

getToken() {
return localStorage.getItem('access_token');
}

removeToken() {
localStorage.removeItem('access_token');
}

isLoggedIn() {
return !!this.getToken();
}
}

2. API请求封装

class ApiClient {
constructor(baseURL = 'http://localhost:8000/api') {
this.baseURL = baseURL;
}

async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const token = localStorage.getItem('access_token');

const headers = {
'Content-Type': 'application/json',
...options.headers
};

if (token) {
headers['Authorization'] = `Bearer ${token}`;
}

const response = await fetch(url, {
...options,
headers
});

if (!response.ok) {
if (response.status === 401) {
// Token过期,跳转登录
localStorage.removeItem('access_token');
window.location.href = '/login';
}

const error = await response.json();
throw new Error(error.detail || 'Request failed');
}

// 204 No Content
if (response.status === 204) {
return null;
}

return response.json();
}

get(endpoint, params = {}) {
const queryString = new URLSearchParams(params).toString();
const url = queryString ? `${endpoint}?${queryString}` : endpoint;
return this.request(url, { method: 'GET' });
}

post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}

put(endpoint, data) {
return this.request(endpoint, {
method: 'PUT',
body: JSON.stringify(data)
});
}

patch(endpoint, data) {
return this.request(endpoint, {
method: 'PATCH',
body: JSON.stringify(data)
});
}

delete(endpoint) {
return this.request(endpoint, { method: 'DELETE' });
}
}

// 使用示例
const api = new ApiClient();

// 登录
api.post('/auth/login', {
email: 'user@example.com',
password: 'password123'
}).then(data => {
localStorage.setItem('access_token', data.access_token);
});

// 获取工单列表
api.get('/tickets', { skip: 0, limit: 10 })
.then(data => console.log(data));

// 创建工单
api.post('/tickets', {
title: '问题标题',
ticket_type: 'technical',
content: '问题描述'
}).then(ticket => console.log(ticket));

3. 错误处理

// 统一错误处理
function handleApiError(error) {
// 根据错误类型显示不同提示
if (error.message.includes('验证码')) {
showNotification('验证码错误,请重新获取', 'error');
} else if (error.message.includes('权限')) {
showNotification('权限不足', 'error');
} else {
showNotification('操作失败,请稍后重试', 'error');
}
}

// 使用
api.post('/tickets', data)
.then(ticket => {
showNotification('工单创建成功', 'success');
})
.catch(handleApiError);

4. 分页组件数据

// 分页数据获取
async function fetchTickets(page = 1, pageSize = 10) {
const skip = (page - 1) * pageSize;
const data = await api.get('/tickets', { skip, limit: pageSize });

return {
items: data.items,
total: data.total,
currentPage: page,
totalPages: Math.ceil(data.total / pageSize)
};
}

// 使用
fetchTickets(1, 10).then(paginatedData => {
// 渲染列表
renderTicketList(paginatedData.items);
// 渲染分页器
renderPagination(paginatedData);
});

5. Markdown渲染

合同和项目的概述使用 Markdown 格式,前端需要渲染:

// 使用 marked 库渲染 Markdown
import { marked } from 'marked';

function renderMarkdown(markdown) {
return marked.parse(markdown);
}

// React 示例
function ContractDetail({ contract }) {
const html = renderMarkdown(contract.content_markdown);
return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

6. 文件下载处理

// 获取并下载文件
async function downloadFile(fileId) {
try {
// 获取下载URL
const { download_url } = await api.post(`/projects/files/${fileId}/download-url`);

// 创建临时链接下载
const link = document.createElement('a');
link.href = download_url;
link.download = ''; // 让浏览器自动识别文件名
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
handleApiError(error);
}
}

开发环境说明

验证码获取

开发环境:

  • 如果未配置邮件服务,验证码会输出到服务器控制台
  • 可以从服务器日志中查看验证码

生产环境:

  • 验证码会通过邮件发送到用户邮箱
  • 有效期为10分钟

附录

时间格式

所有时间字段使用 ISO 8601 格式:

2024-01-01T10:30:00
2024-01-01T10:30:00Z // UTC时间

JavaScript解析示例:

const date = new Date('2024-01-01T10:30:00Z');
console.log(date.toLocaleString()); // 本地时间格式

工单类型中英文对照

英文中文说明
financial财务问题账单、付款、发票等
technical技术问题系统bug、功能异常等
business业务问题业务咨询、流程问题等

工单状态中英文对照

英文中文说明
pending待回复工单已创建,等待管理员处理
replied已回复管理员已回复,工单处理完成

业务账号状态中英文对照

英文中文说明
pending待审核已申请,等待管理员审核
active已激活已分配专属邮箱,可以正常使用
disabled已禁用账号已被禁用

联系支持

如有API使用问题,请:

  1. 查看 Swagger文档 - 可在线测试(仅限已登录用户)
  2. 查看服务器日志获取详细错误信息
  3. 联系技术支持团队

文档版本: v2.0.0
最后更新: 2025-11-25
维护者: Dreamreflex DevOps Team
目标用户: 客户及二次开发者