前言

关于个人博客这个项目,从我大学刚毕业的第一份工作开始开始就在筹划了,因为很酷,现在工作也有一段时间了,虽然还是个小菜鸟,但是可以尝试通过博客项目去进行学习,本来是打算从数据库到编辑器后端再到前端都一并完成,但是评估了一下觉得自己实力暂时不够,打算积累一下在制作,恰好某一天突发奇想想到可以借助某个平台的编辑器,然后通过api调用,这样就可以快速搭建,于是找到了语雀。

目标简单分解

1、创建数据库-blog
表user: 存放用户数据-本人数据
表article:存放文章数据
表articlelist: 存放各知识库文章列表数据
表knowledgecatalog: 存放知识库
表comment:存放文章评论数据
2、创建定时任务与手动更新:读取语雀的文章数据,读取blog数据,判断有的更新,没有的新增
3、创建api,前端页面制作
4、上线http://www.xfhhjy.top/#/

起步

通过npm安装egg.js
npm init egg --type=simple
npm i

egg.js框架目录约定规范

  • app/router.js 用于配置URL路由规则
  • app/controller/** 用于解析用户输入,处理后返回相应结果
  • app/service/** 用于编写业务逻辑层
  • app/middleware/** 用于编写中间件
  • app/public/** 用于放置静态资源
  • app/extend/** 用于框架的扩展
  • config/config.{env}.js 用于编写配置文件
  • config/plugin.js 用于配置需要加载的插件
  • test/ **用于单元测试
  • app.js和agent.js用于自定义启动时的初始化工作
  • app/schedule/ **用于定时任务
  • app/view/ **用于放置模板文件
  • app/model/** 用于放置领域模型
  • 官方说明>>

    创建完成egg.js以后,引入egg-mysql

    npm i egg-mysql --save
    config/plugin.js 安装插件
    1. const plugin = {
    2. mysql : {
    3. enable: true,
    4. package: 'egg-mysql'
    5. }
    6. }
    7. module.exports = plugin
    config/config.default.js 设置参数
    1. exports.mysql = {
    2. // database configuration
    3. client: {
    4. // host
    5. host: '地址',
    6. // port
    7. port: '端口',
    8. // username
    9. user: '',
    10. // password
    11. password: '',
    12. // database
    13. database: '数据库名称',
    14. },
    15. // load into app, default is open
    16. app: true,
    17. // load into agent, default is close
    18. agent: false,
    19. };
    egg-mysql使用说明-点击跳转>>

    app/service 创建yuque.js

    ```javascript const Service = require(‘egg’).Service;

//存放所有语雀api返回 let knowledgeArr = [] //存放语雀返回的知识库原始数据 let articleListArr = [] //存放经过特殊处理的语雀返回的所有知识库下的次级文章数据 class Yuque extends Service {

  1. async user() {
  2. const { app,yqApi } = this
  3. const data = await yqApi(`/user`)
  4. const result = await app.mysql.update('user',{
  5. id : 1,
  6. name : data.name,
  7. login : data.login,
  8. avatar_url : data.avatar_url
  9. })
  10. return `user数据更新成功!`
  11. }
  12. async knowledgeCatalog(){
  13. //更新知识库目录
  14. const { app,yqApi } = this
  15. /*
  16. * 1、获取语雀api数据
  17. * 2、根据知识库id查询blog数据库表knowledgecatalog数据
  18. * 3、如果查询到了,则直接更新,否则执行添加
  19. */
  20. const data = await yqApi( `/users/${app.config.yuque.namespaceId}/repos`)
  21. for(let item of data){
  22. if(item.name.indexOf('博客') !== -1 ){
  23. knowledgeArr.push(item)
  24. let row = {
  25. yuque_library_id : item.id,
  26. type : item.type,
  27. slug : item.slug,
  28. name : item.name,
  29. namespace : item.namespace,
  30. description : item.description
  31. }
  32. const query = await app.mysql.get('knowledgecatalog',{ yuque_library_id:item.id })
  33. if(!query){
  34. //数据新增
  35. const add = await app.mysql.insert('knowledgecatalog',row)
  36. }else{
  37. //数据覆盖更新
  38. row.id = query.id
  39. const up = await app.mysql.update('knowledgecatalog',row)
  40. }
  41. }
  42. }
  43. return `knowledgeCatalog知识库数据更新成功!`
  44. }
  45. async articleList(){
  46. //先读取知识库,然后根据知识库id去读取知识库下面的文章列表页,并存入数据库
  47. const { app,yqApi } = this
  48. const Arr = []
  49. for(let list of knowledgeArr){
  50. const data = await yqApi(`/repos/${list.id}/docs`)
  51. data.forEach((item) => {
  52. Arr.push(item)
  53. })
  54. }
  55. articleListArr = Arr
  56. for(let list of Arr){
  57. //循环所有次级文章列表,查询数据库是否有对应数据,如果无,则增加,有则覆盖最新数据
  58. let { id,slug,title,description,book_id,custom_description,created_at,updated_at } = list
  59. let row = {
  60. yuque_id:id,
  61. slug,
  62. title,
  63. description,
  64. book_id,
  65. custom_description,
  66. created_at,
  67. updated_at
  68. }
  69. const query = await app.mysql.get('articlelist',{ yuque_id:id })
  70. if(!query){
  71. const add = await app.mysql.insert('articlelist',row)
  72. }else{
  73. row.id = query.id
  74. const up = await app.mysql.update('articlelist',row)
  75. }
  76. }
  77. return `articleList文章列表数据更新成功!`
  78. }
  79. async article(){
  80. //通过知识库knowledgeArr的namespace与articleListArr的slug获取所有文档
  81. const { app,yqApi } = this
  82. const Arr = []
  83. let namespace = (book_id)=>{
  84. for(let item of knowledgeArr){
  85. if(item.id === book_id){
  86. return item.namespace
  87. }
  88. }
  89. }
  90. for(let list of articleListArr){
  91. const data = await yqApi(`/repos/${namespace(list.book_id)}/docs/${list.slug}`)
  92. Arr.push(data)
  93. }
  94. for(let list of Arr){
  95. //循环所有次级文章列表,查询数据库是否有对应数据,如果无,则增加,有则覆盖最新数据
  96. let { id,slug,title,book_id,format,body,body_draft,body_html,body_lake,created_at,updated_at } = list
  97. let row = {
  98. yuque_id:id,
  99. slug,
  100. title,
  101. book_id,
  102. format,
  103. body,
  104. body_draft,
  105. body_html,
  106. body_lake,
  107. created_at,
  108. updated_at
  109. }
  110. const query = await app.mysql.get('article',{ yuque_id:id })
  111. if(!query){
  112. const add = await app.mysql.insert('article',row)
  113. }else{
  114. row.id = query.id
  115. const up = await app.mysql.update('article',row)
  116. }
  117. }
  118. return `文章数据更新成功!`
  119. }
  120. yqApi = async (url) =>{
  121. const { ctx,app,jsonConvert } = this
  122. const c = await ctx.curl(`${app.config.yuque.baseUrl}${url}`,{
  123. headers:app.config.yuque.headers
  124. })
  125. const data = await jsonConvert(c.data)
  126. return data
  127. }
  128. async jsonConvert(data){
  129. //转换buffer数据为json并返回
  130. const result = JSON.parse(data.toString())
  131. return result.data
  132. }

}

module.exports = Yuque

  1. <a name="JP8R4"></a>
  2. ### app/controller 创建yuqueUpData.js
  3. ```javascript
  4. const Controller = require('egg').Controller;
  5. class upData extends Controller {
  6. async index() {
  7. const { ctx } = this;
  8. const user = await ctx.service.yuque.user() //更新用户数据
  9. const knowledgeCatalog = await ctx.service.yuque.knowledgeCatalog() //更新知识库数据
  10. const articleList = await ctx.service.yuque.articleList() //更新文章列表数据
  11. const article = await ctx.service.yuque.article() //更新文章列表数据
  12. ctx.body = [user,knowledgeCatalog,articleList,article].join(' ');
  13. }
  14. }
  15. module.exports = upData;

app/schedule/UpBlogs.js 创建定时任务

  1. //定时更新语雀数据到blog数据库
  2. const Subscription = require('egg').Subscription
  3. class UpBlogs extends Subscription {
  4. static get schedule(){
  5. return {
  6. interval : '60m', //60分钟执行一次
  7. type: 'all' //指定所有的worker都需要执行
  8. }
  9. }
  10. async subscribe(){
  11. const { ctx } = this;
  12. const user = await ctx.service.yuque.user() //更新用户数据
  13. const knowledgeCatalog = await ctx.service.yuque.knowledgeCatalog() //更新知识库数据
  14. const articleList = await ctx.service.yuque.articleList() //更新文章列表数据
  15. const article = await ctx.service.yuque.article() //更新文章列表数据
  16. console.log([user,knowledgeCatalog,articleList,article].join(' '));
  17. }
  18. }
  19. module.exports = UpBlogs

app/router.js 创建路由手动更新链接

  1. module.exports = app => {
  2. const { router, controller } = app;
  3. router.get('/yuque_up_data',controller.yuqueUpData.index)
  4. };

至此,目标2已完成