利用apidoc自動生成model文檔

公司在之前進行存管對接后悄蕾,對內(nèi)部架構(gòu)進行了細(xì)分,業(yè)務(wù)邏輯也比之前復(fù)雜了不少奠骄,由此數(shù)據(jù)庫文檔的必要性顯得十分重要含鳞;組長參照了mysql在定義字段時添加的comment字段生成文檔蝉绷,在mongoose也使用了這一方式熔吗,然后通過讀取model.js生成符合apidoc的注釋,通過apidoc生成文檔宵晚。

  • 先看效果:


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

說來慚愧,這任務(wù)本來是落在我身上吱型,當(dāng)時我根據(jù)apidoc的樣式模擬寫出html津滞,但是顯示效果不好铝侵,組長就想到利用apidoc來生成文檔,但是我拖拉了兩個周末后組長自己寫了demo咪鲜,而后我只是做了樣式優(yōu)化的部分

// 這是之前我寫得第一版
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: '發(fā)起終端' },
    status: { type: Number, required: true, comment: '狀態(tài)' },
    time: { type: Number, required: true, comment: '訂單創(chuàng)建時間' },
    doneTime: { type: Number, comment: '訂單完成時間' },
    expireTime: { type: Number, comment: '訂單失效時間' },
    // 充值提現(xiàn)訂單有
    bank: { type: String, comment: '銀行編碼' },
    bankcard: { type: String, comment: '銀行卡號' },
    // 購買還款訂單有
    product: { type: Object, comment: '產(chǎn)品' },
    asset_id: { type: String, comment: '資產(chǎn)id' },
    extend: {
      type: Object
      // 統(tǒng)計所有用到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函數(shù)生成相應(yīng)的注釋享郊,最后將生成的注釋寫入指定的文件內(nèi)
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 = ''
  // 這里調(diào)用lodash的isFunction览祖,兼容type: String這種寫法
  if (_.isFunction(value)) {
    return `${prefix} {${value.name}} ${key}\n`
  }
  if (_.isObject(value) && value.type) {
    let description = value.comment || ''
    // apidoc沒有索引標(biāo)志,所以只能寫在description
    if (value.index) {
      description += ' (加索引)'
    }
    // 只能規(guī)定的值
    if (value.enum) {
      // 區(qū)分number和string
      if (value.type === Number){
        enumStr += `=${value.enum.join(',')}`
      } else if (value.type === String){
        enumStr += `="${value.enum.join('\",\"')}"`
      }
    }
    // 是否有默認(rèn)值
    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`
  // 若是數(shù)組展蒂,將遞歸執(zhí)行g(shù)enerateFieldDoc
  for (let attr in value) {
    ret += generateFieldDoc(`${key}.${attr}`, value[attr])
  }
  return ret
}

總得來說,借助apidoc生成model文檔,是可以滿足查看的需求悼泌,而且顯示上可以與接口文檔存放在同一位置統(tǒng)一查看隘世,不過在顯示效果上可以缺失了索引等屬性鸠踪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末营密,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子评汰,更是在濱河造成了極大的恐慌纷捞,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件被去,死亡現(xiàn)場離奇詭異主儡,居然都是意外死亡,警方通過查閱死者的電腦和手機惨缆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門糜值,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坯墨,你說我怎么就攤上這事寂汇。” “怎么了捣染?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵健无,是天一觀的道長。 經(jīng)常有香客問我液斜,道長累贤,這世上最難降的妖魔是什么叠穆? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮臼膏,結(jié)果婚禮上硼被,老公的妹妹穿的比我還像新娘。我一直安慰自己渗磅,他們只是感情好嚷硫,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著始鱼,像睡著了一般仔掸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上医清,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天起暮,我揣著相機與錄音,去河邊找鬼会烙。 笑死负懦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柏腻。 我是一名探鬼主播纸厉,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼五嫂!你這毒婦竟也來了颗品?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤沃缘,失蹤者是張志新(化名)和其女友劉穎躯枢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孩灯,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡闺金,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年逾滥,在試婚紗的時候發(fā)現(xiàn)自己被綠了峰档。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡寨昙,死狀恐怖讥巡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情舔哪,我是刑警寧澤欢顷,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站捉蚤,受9級特大地震影響抬驴,放射性物質(zhì)發(fā)生泄漏炼七。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一布持、第九天 我趴在偏房一處隱蔽的房頂上張望豌拙。 院中可真熱鬧,春花似錦题暖、人聲如沸按傅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唯绍。三九已至,卻和暖如春枝誊,著一層夾襖步出監(jiān)牢的瞬間况芒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工侧啼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牛柒,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓痊乾,卻偏偏與公主長得像皮壁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子哪审,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理蛾魄,服務(wù)發(fā)現(xiàn),斷路器湿滓,智...
    卡卡羅2017閱讀 134,633評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,782評論 25 707
  • 夕陽西下滴须,溫雪兒一直很喜歡夕陽,它很漂亮叽奥。但今天她不怎么喜歡扔水。溫雪兒緩慢地跟在父母身后,離弄堂的出口越來越近朝氓,卻離...
    落曇閱讀 198評論 0 4
  • 五月的海濱小城赵哲,仍是乍暖還寒的天氣待德,別處槐花已香滿枝頭,此間槐花依舊不可多得枫夺。早在三月份将宪,午飯后閑步時,同事們就計...
    被封號了莫名其妙閱讀 128評論 0 0
  • Java Concurrency 在多線程環(huán)境下,為了保證共享數(shù)據(jù)的原子和內(nèi)存可見性较坛,需要進行鎖操作印蔗。在JAVA中...
    落日無風(fēng)閱讀 225評論 0 1