尝试全栈(上)

公司近期一直在力推使用文档平台。大家都有认识,文档很重要,但似乎又都有共识,实在是不想写它。除了组织内容需要耗费时间以外,使用一个书写简洁、交流方便、支持协作的工具也很重要。经过考察,决定采用 MinDoc(https://github.com/lifei6671/mindoc)来当此重任。

它的主要优点:高效、小巧,对 Markdown 支持较好,支持文档导出。

提出问题。MinDoc 对文档的组织有两个层级,顶层你可以随意认知,称作项目(界面中的官方称谓)、库、版块、书等皆可,而在代码实现中其采用的术语是 book;另一层则是各自成篇的文档,实现中采用术语为 doc。打开一个 book,其中的所有 doc 会用树状结构组织并呈现出来。经过一段时间的使用,发现其内置的 PDF 导出特性似乎值得做一些改进。现在的行为是直接导出整个 book,而根据经验,导出某一篇 doc 似乎是更常见的需求。在小兄弟们都各自忙碌的情况下,决定自己上手。

分析问题。从目前已经可以正常导出 PDF 来看,很显然 PDF 生成的本身不是重点,重点在于如何将其生成的数据源可以切割成以 doc 为粒度。如果后端此功能就绪的话,则前端仅需将指定的 doc 的 ID 发给后端服务即可完成整个功能。简单总结问题点:1、后端要实现以 doc 为单位生成 PDF;2、前端执行导出功能时把指定 doc 的(应该就是正在浏览着的)ID 作为参数传递给后端。

解决问题。优先解决问题 1。首先要搞清楚的,是功能流转的走向。从界面上看,鼠标移动到“项目导出 PDF”上时,其对应的链接为 /export/bookid?output=pdf 这种形式,其中 bookid 为实际的 book 的 ID。据此,到源代码中寻找对应的处理逻辑,凭着一点点感觉,在 routers/router.go 中发现了如下代码:

beego 是 Go 语言实现的一个框架,不熟,但此处显然是在注册 URL 的处理路由。已经判定 export 的常见功能是导出 doc 而非 book,因此在这里做了一个调整,/export 这个路径保留给 doc,而把 book 的导出重新定义为 /exportbook,根据查询到的知识,后两个参数为 URL 实际对应的处理对象以及方法名称(Export 是方法名,前面的 * 表示 HTTP 的请求方法,可以指定 POST 或者 GET,都支持就是 *)。因此修改后的路由注册代码为:

显然,需要去 DocumentController 的代码里,把原来的 Export 方法改名为 ExportBook,并新增一个名为 ExportDoc 的方法。至于 URL 里的 :id 字样,一开始完全是根据上下文的代码照猫画虎,并不知晓其值的来龙去脉,更不知道为它会付出更多的时间。

打开 controllers/document.go,就是 DocumentController 的实现。经过粗略审视,发现新增的 ExportDoc 功能,势必将会有不少代码与 ExportBook(也即原始的 Export 方法)雷同,因此又做出选择,Export 方法保留,增加一个参数用以区别是导出 book 还是 doc,然后把新增的 ExportDoc 和 ExportBook 实现为对 Export 方法得浅封装。见下:

在 Export 的实现中,真正实现 PDF 生成的代码只有一行,因此可以根据 single_doc 参数将之保留在正确分支中,如下:

现在,重点转移到了 RecursiveFun 这个方法上。

经过查看分析,发现这个方法在一个双重循环中同时完成了 PDF 的输出和把处于同一 book 中的 doc 页面组织到一起的逻辑,并不足够优雅。好在分离性较强,因而很容易就把单篇 doc 输出到 PDF 的部分剥离到了一个独立方法中,方法名很无趣,唤作 EachFun,原型见下:

查看比较 EachFun 和 RecursiveFun 的原型,就会发现二者极为相像。只不过由于对于单个 doc 来说,其 parent_id 可以直接获取而不用传入,因此EachFun 去掉了该参数,同时对于倒数第二个参数,也从 RecursiveFun 的表示一组 doc 的指针数组改为了单个 doc 指针。在它们的基础上,如果拿到了前端传入的 doc ID,再实现一个通过 ID 得到 doc 指针的方法(实际中被抽象实现为 GetDocumentById,其中对用户自定义的 ID 和系统自动生成的 ID 做了兼容处理,当然也是参考自源代码中的别处),则后端的 PDF 生成部分应该就算基本就绪了。那么如何获得前端传来的 ID 呢,不妨照猫画虎一下,于是,上面的那段不完整代码补充为了如下样子:

是时候让修改后的功能接受一下检验了。打开 views/document/default_read.tpl,查找“项目导出”字样,将该行修改为:

之后编译运行。最先验证的是 book 导出功能是否被破坏,很欣喜地发现并没有。那么,接下来就要解决上面那行里面那个小小的 :id 了。胜利就在眼前,不是么?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注