接上篇,科科,好,我們繼續
我們在這裡先把json資料入庫吧~
首先,database/scheme裡定義好資料型別。
const mongoose = require('mongoose')
const detailHouseSchema = new mongoose.Schema({ //定義資料模式
link:String,
text:String,
_id:String,
areaDetail:[
{
link: String,
text: String,
_id: String,
house:[
{
name: String,
huxing: String,
favorPos: String,
aroundPrice: Number,
adress: String,
area: String
}
]
}
]
})
mongoose.model('detailHouse',detailHouseSchema)
複製程式碼
然後我們需要到中介軟體裡去建立連線資料庫和執行插入的動作。
middleWares/database.js
import mongoose from 'mongoose'
import config from '../config'
import fs from 'fs'
import { resolve } from 'path'
const r = path => resolve(__dirname,path) //將路徑片段轉成一個絕對路徑
const models = r('../database/schema')
/**
* 依次引入本地爬去好的json檔案,插入資料庫
*/
var areaJson = require('database/json/AreaDetail.json')
var areaHouseJson = require('database/json/AreaHouse.json')
var detailHouseJson = require('database/json/detailHouse.json')
/**
* 依次引入schema
*/
fs.readdirSync(models) //讀取檔案
.filter(file => ~file.search(/^[^\.].*js$/)) //篩選出字尾是js的檔案
.forEach(file => require(resolve(models,file)))
export const database = app =>{
mongoose.set('debug',true)
mongoose.connect(config.db)
mongoose.connection.on('disconnected', ()=>{
mongoose.connect(config.db)
})
mongoose.connection.on('error', err =>{
console.log(err)
})
mongoose.connection.on('open', async () =>{
console.log('connected to MongoDb',config.db)
/**
* 杭州主城區資料入庫
*/
let area = mongoose.model('area')
let areaDataBase = await area.find({}).exec()
if (!areaDataBase.length) area.insertMany(areaJson)
/**
* 杭州主城區的房價資料入庫
*/
let areaHouse = mongoose.model('areaHouse')
let areaHouseDataBase = await areaHouse.find({}).exec()
if(!areaHouseDataBase.length) areaHouse.insertMany(areaHouseJson)
/**
* 杭州主城區裡包括了分割槽的房價資料入庫
*/
let detailHouse = mongoose.model('detailHouse')
let detailHouseDataBase = await detailHouse.find({}).exec()
if(!detailHouseDataBase.length) detailHouse.insertMany(detailHouseJson)
})
}
複製程式碼
成功的話,如下圖~ bling~~~
走到這裡,我們要停下來對後端的路由做一個提取個封裝。首先,我這專案頁面量不大,如果單純的用koa-router去原生去寫是沒有問題的,但是如果你是實際的專案,路由很多,這個時候再去那麼寫,程式碼的可讀性就很差了。
Decorator可以動態地給一個物件新增額外的職責。雖然,利用子類繼承也可以實現這樣的功能,但是Decorator提供了一個更靈活的方式。因為繼承會為型別引入的靜態特質,使得這種擴充套件方式缺乏靈活性;並且隨著子類的增多(擴充套件功能的增多),各種子類的組合(擴充套件功能的組合)會導致更多子類的膨脹。
那麼我們要在decorator/router.js裡要定義一些公用的方法,其中還新增了列印日誌功能,在除錯的時候也是美滋滋的一匹。
先去middlewares/routers/router.js裡去呼叫我們用修飾起封裝好的方法和Route。
import Route from '../decorator/router'
import { resolve } from 'path'
const r = path => resolve(__dirname, path)
export const router = app => {
const apiPath = r('../routes')
/**
* 路由分離
*/
const router = new Route(app, apiPath)
router.init()
}
複製程式碼
現在去封裝Route
decorator/router.js
import Router from 'koa-router'
import { resolve } from 'path'
import _ from 'lodash'
import { glob } from 'glob' //用正則去匹配檔案
export let routesMap = new Map()
export const symbolPrefix = Symbol('prefix')
export const normalizePath = path => path.startsWith('/') ? path : `/${path}`
export const isArray = c => _.isArray(c) ? c : [c]
export default class Route{
constructor(app,apipath){
this.app = app
this.router = new Router()
this.apipath = apipath
}
init(){
/**
* 這裡利用傳進來的apipath去引入字尾為js的檔案
*/
glob
.sync(resolve(this.apipath,'./*.js'))
.forEach(require);
for(let [ conf , controller ] of routesMap){
/*
*思路就是把每一個路由檔案的controller拎出來
* 然後跟它的路由做一個一一匹配
* */
const controllers = isArray(controller)
let prefixPath = conf.target[symbolPrefix]
if(prefixPath) prefixPath = normalizePath(prefixPath)
const routerPath = prefixPath + conf.path
this.router[conf.method](routerPath,...controllers) //function (name, path, middlewares)
}
this.app.use(this.router.routes()) // 新增路由中介軟體
this.app.use(this.router.allowedMethods()) // 對請求進行一些限制處理
}
}
/**
*
* @param {path,target}
* 保證每一個controller都是獨一無二的
*/
export const controller = path => target => target.prototype[symbolPrefix] = path
/**
*
* @param {conf}
* 定義簡單的route
*/
export const route = conf => (target, key, desc) =>{
conf.path = normalizePath(conf.path)
routesMap.set({
target:target,
...conf,
},target[key])
}
/**
*
* @param {path}
* 定義get方法
*/
export const get = path => route({
method:'get',
path:path
})
/**
*
* @param {path}
* 定義post方法
*/
export const post = path => route({
method:'post',
path:path
})
/**
*
* 列印日誌
*/
let reqID = 0
const decorate = (args, middleware) => {
let [ target, key, descriptor ] = args
target[key] = isArray(target[key])
target[key].unshift(middleware)
return descriptor
}
export const convert = middleware => (...args) => decorate(args, middleware)
export const log = convert(async (ctx, next) => {
let currentReqID = reqID++
console.time(`${currentReqID} ${ctx.method} ${ctx.url}`)
await next()
console.timeEnd(`${currentReqID} ${ctx.method} ${ctx.url}`)
})
複製程式碼
然後再來看看我們介面定義的檔案,程式碼趕緊簡潔的一匹.
routes/crawler.js
import { controller, get , log} from '../decorator/router'
import mongoose from 'mongoose'
const areaDataBase = mongoose.model('area')
const areaHouseDataBase = mongoose.model('areaHouse')
const detailHouse = mongoose.model('detailHouse')
@controller('')
export class Crawler{
/**
* 獲取杭州城區下的房子資訊
*/
@get('/getDetail')
@log
async detailHouse (ctx,next){
let query = ctx.query
let { _id } = query;
if (!_id) return (ctx.body = '_id is required')
let area = await detailHouse
.findById(_id)
.exec()
ctx.body = {
code:0,
area
}
}
/**
* 獲取杭州城區下的房子資訊
*/
@get('/getAreaHouse')
@log
async areaHouse (ctx,next){
let areaHouse = await areaHouseDataBase
.find({})
.exec()
ctx.body = {
code:0,
areaHouse
}
}
/**
* 獲取杭州城區單條的名稱
*/
@get('/getArea/:_id')
@log
async getArea (ctx,next){
const { params } = ctx
const { _id } = params
if (!_id) return (ctx.body = '_id is required')
let area = await areaDataBase
.findById(_id)
.exec()
ctx.body = area
}
/**
* 獲取杭州城區的名稱
*/
@get('/getArea')
@log
async Area (ctx,next){
let area = await areaDataBase
.find({})
.exec()
ctx.body = {
code:0,
area
}
}
}
複製程式碼
走到這裡,後端的程式碼基本上全部完成了,我們從資料的爬取-->入資料庫-->-->介面的定義。
剩下的就是簡單的前端的介面呼叫啦~ 我這裡就不具體展示出程式碼啦~介面呼叫完成,基本上就能完成我們的目標樣子啦~
真心的話要放在最後,這是小弟第一次從後到前擼的專案,對於node,mongo,資料庫如何建表研究的還很膚淺,小弟在這裡班門弄斧啦~真心希望和我一樣喜歡倒騰的小夥伴可以自己也上手玩玩~真的能學到不少知識~
本來還想放上原始碼的,礙於註釋都沒有新增的很全,容老夫慢慢把註釋不全了在貼出來~
2018/07/31