手把手从零到一打造在线文档之书写服务端技术方案设计

2,354 阅读11分钟

作者:UOrb

声明:文章为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!

前言

首先我 不是 专业的后端,所以服务端的技术方案设计可能不那么 专业,比如 功能设计 那块其实应该更加详细的,会涉及到一些 状态判定、数据来源、 业务逻辑 等一些内容(当然这一块的内容大部分是从需求文档中转化而来的),我这里只是简单的应用接口的排列;数据库设计也是一样,不够专业,只是直白的设计。

不过虽然相对来说粗糙了一些,但是麻雀虽小五脏俱全吧!

其中 接口设计(单独转换之后也可以叫做接口文档)是对前端开发 影响 最大的了,前端的业务逻辑和接口数据 Mock 都需要 依赖 它。

在前后端分离的情况下,前后端同时进行开发,没有预先进行接口设计(接口文档)的话,对前端的影响是非常大的,最常见的就是前端只能把静态页面先写完,等后端接口出来后再配合着进行业务逻辑的开发,然后大部分的工作量都堆积在后期,就导致了加班或项目延期等情况;就算是前端预先根据需求和设计稿自定义了数据结构,等后端接口给出后通过数据模型转换,也是变相的增加了前端的工作量和负担。

项目背景

这里大部分都是从 需求文档 中的引用,所以就不再重写了。

此处省略一万个字...

技术选型

服务端语言毫无疑问是 Node.js

服务端框架将会使用 Nest.js,原因是 Nest.js 是一套 方案、架构 ,同时也会更加贴近真实的后端开发,熟悉 Java 的 Spring 框架的兄弟就会感觉 Nest.js 非常像 Spring,缺点是国内 Nest.js 不太繁荣,也很少有其相关教程,只能翻翻官方文档比较难上手;

数据库使用 mysql5.7 和 redis

功能设计

这里需要根据需求文档、原型图或设计图来设计出功能模块以及相关的一些规则和约定,大多数情况其实就是 产品需求 转换成后端技术需求的一个过程。

登陆

image.png

涉及接口

  1. 用户注册接口
  2. 用户登陆认证接口
  3. 用户信息获取接口

个人中心

image.png

涉及接口

  1. 用户信息获取接口
  2. 文档查询接口
  3. 获取用户空间列表接口
  4. 新增用户空间接口
  5. 编辑指定空间接口
  6. 删除指定空间接口
  7. 获取指定空间的知识库列表接口
  8. 新增指定空间的知识库接口
  9. 编辑指定知识库接口
  10. 删除指定知识库接口

知识库

image.png

涉及接口

  1. 获取指定知识库信息接口
  2. 编辑指定知识库信息接口
  3. 获取指定知识库的文档列表接口
  4. 新增指定空间文档接口
  5. 修改指定文档配置接口
  6. 删除指定文档接口

最近浏览

image.png

涉及接口

  1. 文档查询接口
  2. 获取用户浏览文档记录列表接口

回收站

image.png

涉及接口

  1. 文档查询接口
  2. 获取用户已删除列表接口
  3. 恢复文档接口
  4. 直接删除接口

文档

image.png

涉及接口

  1. 获取指定文档信息数据接口
  2. 编辑/保存指定文档接口
  3. 修改指定文档配置接口

数据库设计

这里需要根据需求文档和转换后的技术需求(功能设计)的内容进行数据库的设计,大体就是看有多少种 载体 ,每个载体基本上就可以设计成一个 数据表 再根据 需求 设计字段,基本上除了业务会用到的数据字段常规的都会带上 创建人、创建时间、更新人、更新时间、私有状态(假删除等逻辑判断) 之类的字段,基本上你业务逻辑梳理(需求 or 功能设计)清楚了,数据库设计基本上不会有太大的问题。

image.png

用户表

CREATE TABLE `online_document`.`user` (
  `id` INT NOT NULL AUTO_INCREMENT COMMENT '唯一 ID',
  `name` VARCHAR(45) NULL COMMENT '昵称',
  `username` VARCHAR(45) NOT NULL COMMENT '帐号',
  `password` VARCHAR(45) NOT NULL COMMENT '密码',
  `create_time` DATETIME NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `id_UNIQUE` (`id` ASC),
  UNIQUE INDEX `username_UNIQUE` (`username` ASC))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT = '用户';

空间表

CREATE TABLE `online_document`.`space` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '唯一ID',
  `user_id` INT UNSIGNED NOT NULL COMMENT '所属用户',
  `name` VARCHAR(45) NOT NULL COMMENT '空间名',
  `describe` VARCHAR(255) NULL COMMENT '简介',
  `create` INT NULL COMMENT '创建人',
  `create_time` DATETIME NULL COMMENT '创建时间',
  `update` INT NULL COMMENT '更新人',
  `update_time` DATETIME NULL COMMENT '更新时间',
  `status` CHAR(1) NOT NULL DEFAULT 1 COMMENT '状态 1: 正常; 0: 删除',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `id_UNIQUE` (`id` ASC))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT = '空间';

知识库表

CREATE TABLE `online_document`.`library` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `user_id` INT UNSIGNED NOT NULL COMMENT '所属用户',
  `space_id` INT UNSIGNED NOT NULL COMMENT '所属空间',
  `name` VARCHAR(45) NOT NULL COMMENT '知识库名称',
  `describe` VARCHAR(255) NULL COMMENT '简介',
  `create` INT NULL COMMENT '创建人',
  `create_time` DATETIME NULL COMMENT '创建时间',
  `update` INT NULL COMMENT '更新人',
  `update_time` DATETIME NULL COMMENT '更新时间',
  `status` CHAR(1) NOT NULL DEFAULT 1 COMMENT '状态 1: 正常; 0: 删除',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `id_UNIQUE` (`id` ASC))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT = '知识库';

文档表

CREATE TABLE `online_document`.`doc` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '唯一 ID',
  `user_id` INT UNSIGNED NOT NULL COMMENT '所属用户',
  `space_id` INT UNSIGNED NOT NULL COMMENT '所属空间',
  `library_id` INT UNSIGNED NOT NULL COMMENT '所属知识库',
  `content_id` INT UNSIGNED NOT NULL COMMENT '文档内容',
  `title` VARCHAR(45) NULL COMMENT '文档标题',
  `create` INT NULL COMMENT '创建人',
  `create_time` DATETIME NULL COMMENT '创建时间',
  `update` INT NULL COMMENT '更新人',
  `update_time` DATETIME NULL COMMENT '更新时间',
  `status` CHAR(1) NOT NULL DEFAULT 1 COMMENT '状态 1: 正常; 0: 删除',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `id_UNIQUE` (`id` ASC))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT = '文档';

文档内容表

CREATE TABLE `online_document`.`content` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '唯一 ID',
  `doc_id` INT NOT NULL COMMENT '所属文档',
  `content` LONGTEXT NULL COMMENT '文档内容',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `id_UNIQUE` (`id` ASC))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT = '文档内容';

浏览记录表

CREATE TABLE `online_document`.`doc_record` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `user_id` INT NOT NULL COMMENT '所属用户',
  `doc_id` INT NOT NULL COMMENT '所属文档',
  `space_id` INT NOT NULL COMMENT '所属空间',
  `library_id` INT NOT NULL COMMENT '所属知识库',
  `doc_title` VARCHAR(45) NULL COMMENT '文档标题',
  `visit_time` DATETIME NOT NULL COMMENT '访问时间',
  `status` CHAR(1) NOT NULL DEFAULT 1 COMMENT '状态 1: 正常; 0: 删除',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `id_UNIQUE` (`id` ASC))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
COMMENT = '浏览记录';

接口设计

接口设计在常规文档中可能更多的使用 表格 的形式,不过写 表格 会花费更多的时间和精力,相对来说就会更精准和详细一些,我这里使用的类 JSON 字符串的形式来书写的。

用户模块

用户注册接口

请求地址:/api/register
请求类型:POST
请求体:

{
  name: string // 昵称
  username: string // 帐号
  password: string // 密码
}

响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: {
    token: string // 认证
  }
}

用户登陆认证接口

请求地址:/api/login
请求类型:POST
请求体:

{
  name: string // 昵称
  username: string // 帐号
  password: string // 密码
}

响应体: { msg: string // 消息 code: number // 状态码 data: { token: string // 认证 } }

用户信息获取接口

请求地址:/api/user
请求类型:GET
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: {
    id: number // 用户 id
    name: string // 用户昵称
  }
}

空间模块

获取用户空间列表接口

请求地址:/api/space
请求类型:GET
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: [
    {
      id: number // 空间 ID
      name: string // 空间名称
      describe: string // 空间简介
    }
  ]
}

新增用户空间接口

请求地址:/api/space
请求类型:POST
请求体:

{
  name: string // 空间名称
  describe: string // 空间简介
}

响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 创建成功;false 创建失败
}

编辑指定空间接口

请求地址:/api/space/:id
请求类型:PATCH
请求体:

{
  name: string // 空间名称
  describe: string // 简介
}

响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 编辑成功;false 编辑失败
}

删除指定空间接口

请求地址:/api/space/:id
请求类型:DELETE
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 删除成功;false 删除失败
}

知识库模块

获取指定空间的知识库列表接口

请求地址:/api/library?spaceId=1
请求类型:GET
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: [
    {
      id: number // 知识库 ID
      name: string // 知识库名称
      describe: string // 知识库简介
    }
  ]
}

新增指定空间的知识库接口

请求地址:/api/library
请求类型:POST
请求体:

{
  spaceId: // 空间 ID
  name: string // 知识库名称
  describe: string // 知识库简介
}

响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 创建成功;false 创建失败
}

编辑指定知识库接口

请求地址:/api/library/:id
请求类型:PATCH
请求体:

{
  name: string // 知识库名称
  describe: string // 知识库简介
}

响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 编辑成功;false 编辑失败
}

删除指定知识库接口

请求地址:/api/library/:id
请求类型:DELETE
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 删除成功;false 删除失败
}

获取指定知识库信息接口

请求地址:/api/library/:id
请求类型:GET
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: {
    id: number // 知识库 ID
  	userId: number // 所属用户
  	spaceId: number // 所属空间 ID
    name: string // 知识库名称
    describe: string // 知识库简介
    create: number // 创建人
    createTime: string // 创建时间
    update: number // 更新人
    updateTime: string // 更新时间
  }
}

获取指定知识库的文档列表接口

请求地址:/api/library/:id/doc
请求类型:GET
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: [
    {
      id: number // 文章 ID
      userId: number // 所属用户
      spaceId: number // 所属空间 ID
      libraryId: number // 所属知识库 ID
      contentId: number // 内容 ID
      title: string // 标题
      create: number // 创建人
      createTime: string // 创建时间
      update: number // 更新人
      updateTime: string // 更新时间
    }
  ]
}

文档模块

获取指定文档内容数据接口

请求地址:/api/doc/:docId/:contentId
请求类型:GET
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: {
    id: number // 内容 ID
    content: string // 文档内容
  }
}

新增指定知识库文档接口

请求地址:/api/doc
请求类型:POST
请求体:

{
  spaceId: string // 所属空间
  libraryId: string // 所属知识库
}

响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 创建成功;false 创建失败
}

更新指定文档接口

请求地址:/api/doc/:id/:contentId
请求类型:PATCH
请求体:

{
  content: string // 文档内容
}

响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 更新成功;false 更新失败
}

修改指定文档配置信息接口

请求地址:/api/doc/:id
请求类型:PATCH
请求体:

{
  title: string // 文档名称
  ... // 其他
}

响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 修改成功;false 修改失败
}

删除指定文档接口

请求地址:/api/doc/:id
请求类型:DELETE
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 删除成功;false 删除失败
}

浏览记录模块

用户注册接口

请求地址:/api/user/recode 请求类型:GET 请求体:无 响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: {
    currPage: number // 当前页码
    pageSize: number // 每页数量
    total: number // 总数
    list: [
      {
        id: number // 记录 ID
        userId: number // 所属用户
        spaceId: number // 所属空间
        libraryId: number // 所属知识库
        docId: number // 所属文档
        docTitle: string // 文档标题
        visitTime: string // 访问时间
      }
    ]
  }
}

回收站模块

获取用户已删除列表接口

请求地址:/api/recycle
请求类型:GET
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: [
    {
      id: number // 文章 ID
  		userId: number // 所属用户
      spaceId: number // 所属空间 ID
      libraryId: number // 所属知识库 ID
      contentId: number // 内容 ID
      title: string // 标题
      create: number // 创建人
      createTime: string // 创建时间
      update: number // 更新人
      updateTime: string // 更新时间
    }
  ]
}

回复指定文档接口

请求地址:/api/recycle/:id
请求类型:PATCH
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 恢复成功;false 恢复失败
}

彻底删除指定文档接口

请求地址:/api/recycle/:id
请求类型:DELETE
请求体:无
响应体:

{
  msg: string // 消息
  code: number // 状态码
  data: boolean // true 删除成功;false 删除失败
}

总结

技术方案是为了研究解决各类技术问题,有针对性、系统性的提出方法和应对措施及相关对策。

技术方案对项目的质量和周期有着紧密的关联,技术方案相当于定下了一个大纲和具体方向,对项目的规范化、标准化、工作量估算以及具体的研发过程有着重要的指导作用。