美文网首页大前端刷级之路
利用apidoc自动生成model文档

利用apidoc自动生成model文档

作者: 天驱丶 | 来源:发表于2017-09-18 00:24 被阅读9次

    公司在之前进行存管对接后,对内部架构进行了细分,业务逻辑也比之前复杂了不少,由此数据库文档的必要性显得十分重要;组长参照了mysql在定义字段时添加的comment字段生成文档,在mongoose也使用了这一方式,然后通过读取model.js生成符合apidoc的注释,通过apidoc生成文档。

    • 先看效果:


      Screenshot from 2017-09-17 23-43-58.png

    说来惭愧,这任务本来是落在我身上,当时我根据apidoc的样式模拟写出html,但是显示效果不好,组长就想到利用apidoc来生成文档,但是我拖拉了两个周末后组长自己写了demo,而后我只是做了样式优化的部分

    // 这是之前我写得第一版
    const TYPES = [String, Date, Boolean, Number]
    const field = User.schema.obj
    // console.log(field.type.name, field.comment)
    let tbody = ''
    // 定义空格符
    const space = ' '
    function renderTbody(data, index) {
      for (const attr in data) {
        index = index || 0
        // 如果是子属性,则添加空格符
        let gap = ''
        for (let i = 0; i < index; i++) {
          gap += space
        }
        if (_.includes(TYPES, field[attr])) {
          tbody += `
          <tr>
            <td class="code">${gap}${attr}</td>
            <td>${typeof field[attr]()}</td>
            <td><p>${attr}</p></td>
          </tr>
        `
        } else if (attr.comment) {
          tbody += `
          <tr>
            <td class="code">${gap}${attr}</td>
            <td>${typeof field[attr].type()}</td>
            <td><p>${field[attr].comment}</p></td>
          </tr>
        `
        } else {
          renderTbody(field[attr], index + 1)
        }
      }
    }
    renderTbody(field)
    
    const body = `
    <table class="table">
        <thead>
            <tr>
                <th style="width: 30%">Field</th>
                <th style="width: 10%">Type</th>
                <th style="width: 40%">Description</th>
            </tr>
        </thead>
        <tbody>
            ${tbody}
        </tbody>
    </table>
    `
    let html = `<!DOCTYPE html>
    <html>
      <head id="head">
        <meta charset="UTF-8">
        <meta name="format-detection" content="telephone=no">
        <meta name="Description" content="modelDoc">
        <title>modelDoc</title>
        <link rel="stylesheet" type="text/css" href="./modelDoc.css">
      </head>
      <body>
        <h3>${User.schema.name}</h3>
        ${body}
      </body>
    </html>`
    console.log('生成页面...')
    fs.writeFileSync(path.resolve(__dirname, '../../assets/modelDoc/index.html'), html)
    process.exit(0)
    

    以下是apidoc的版本

    • 先来看看model的定义
    attributes: {
        uid: { type: String, required: true, index: true, comment: '用户id' },
        amount: { type: Number, required: true, comment: '金额' },
        oType: {
          type: Number,
          required: true,
          enum: Object.values(Constant.ORDER_TYPE),
          comment: '订单类型'
        },
        channel: { type: String, required: true, default: Constant.CHANNEL.SYSTEM, comment: '订单来源渠道' },
        os: { type: String, required: true, default: Constant.CLIENT.SYSTEM, comment: '发起终端' },
        status: { type: Number, required: true, comment: '状态' },
        time: { type: Number, required: true, comment: '订单创建时间' },
        doneTime: { type: Number, comment: '订单完成时间' },
        expireTime: { type: Number, comment: '订单失效时间' },
        // 充值提现订单有
        bank: { type: String, comment: '银行编码' },
        bankcard: { type: String, comment: '银行卡号' },
        // 购买还款订单有
        product: { type: Object, comment: '产品' },
        asset_id: { type: String, comment: '资产id' },
        extend: {
          type: Object
          // 统计所有用到extend的地方,都加上
          // isLazy: {type: Boolean, comment: '是否自动投资'}
        },
        message: { type: String, comment: '失败原因' }
      }
    
    • model层使用了脚本读取文件
    const fs = require('fs');
    const path = require('path');
    
    const models = fs.readdirSync(path.resolve(__dirname, './'));
    let ret = {};
    for (const model of models) {
      ret[model.slice(0, model.indexOf('.js'))] = require(`./${model}`)
    }
    module.exports = ret;
    

    因此在读取model时只需要const models = require('../../app/model/')

    • 先定义apidoc的name和group,然后通过generateFieldDoc函数生成相应的注释,最后将生成的注释写入指定的文件内
    for (let i in models) {
      let model = models[i]
      const schema = model.schema.obj
      doc += `
    /**
     * @api {POST} /${model.modelName} ${model.modelName}
     * @apiName ${model.modelName}
     * @apiGroup model
    `
      for (let attr in schema) {
        doc += generateFieldDoc(attr, schema[attr])
      }
      doc += ' */\n'
    }
    fs.writeFileSync(path.resolve(__dirname, '../../app/model/modelDoc.js'), doc)
    process.exit(0)
    
    function generateFieldDoc (key, value) {
      let prefix = ' * @apiParam'
      let defaultStr = ''
      let enumStr = ''
      // 这里调用lodash的isFunction,兼容type: String这种写法
      if (_.isFunction(value)) {
        return `${prefix} {${value.name}} ${key}\n`
      }
      if (_.isObject(value) && value.type) {
        let description = value.comment || ''
        // apidoc没有索引标志,所以只能写在description
        if (value.index) {
          description += ' (加索引)'
        }
        // 只能规定的值
        if (value.enum) {
          // 区分number和string
          if (value.type === Number){
            enumStr += `=${value.enum.join(',')}`
          } else if (value.type === String){
            enumStr += `="${value.enum.join('\",\"')}"`
          }
        }
        // 是否有默认值
        if (value.default) {
          defaultStr += value.type === String ? `="${value.default}"` : `=${value.default}`
        }
        // 是否必填
        if (!value.required) {
          if (value.enum) {
            key = `[${key}`
            enumStr = `${enumStr}]`
          } else {
            key = `[${key}]`
          }
        }
        return `${prefix} {${value.type.name}${enumStr}} ${key}${defaultStr} ${description}\n`
      }
      const type = _.isArray(value) ? 'Array' : 'Object'
      let ret = `${prefix} {${type}} ${key}\n`
      // 若是数组,将递归执行generateFieldDoc
      for (let attr in value) {
        ret += generateFieldDoc(`${key}.${attr}`, value[attr])
      }
      return ret
    }
    

    总得来说,借助apidoc生成model文档,是可以满足查看的需求,而且显示上可以与接口文档存放在同一位置统一查看,不过在显示效果上可以缺失了索引等属性。

    相关文章

      网友评论

        本文标题:利用apidoc自动生成model文档

        本文链接:https://www.haomeiwen.com/subject/xyqvsxtx.html