跳到主要内容位置

构建基本的MVC项目

提示

全部的原码放到github上了 🔗链接:koa-template

第一部分:项目结构

1.1树状表示

└── app
└── controllers
├── UserController.js
└── ...
└── models
├── User.js
└── ...
└── services
├── UserService.js
└── ...
└── config
├── database.js
├── schedule.js
└── ...
└── middleware
├── authentication.js
├── logging.js
└── ...
└── routes
├── user.js
└── ...
└── schedule
├── dbBack.js
└── ...
└── utils
├── validation.js
└── ...
├── app.js
└── package.json

1.2具体解释

  • controllers 目录存放控制器文件,负责处理请求和返回响应。
  • models 目录存放模型文件,定义数据结构和与数据库交互的方法。
  • services 目录存放服务层文件,封装业务逻辑,供控制器调用。
  • config 目录存放配置文件,如数据库配置、定时任务配置等。
  • middleware 目录存放中间件文件,用于处理各种请求前或请求后的逻辑。
  • routes 目录存放路由文件,定义API接口及其对应的处理函数。
  • schedule 目录存放定时任务文件,实现定时执行的任务逻辑。
  • utils 目录存放工具函数或类文件,提供项目中需要的公共功能。
  • app.js 是应用的入口文件,启动Koa应用并配置中间件、路由等相关设置。

第二部分:入口文件构建

提示

注释很详细了,应该不用专门解释了

const Koa = require("koa")//引入koa
const {koaBody}=require("koa-body");//解析请求体数据
const cors=require("koa-cors");//路由跨域解决
const helmet=require("koa-helmet");//一般性网络攻击阻截
const router = require('./router/FrontEndRouter')//引入路由管理文件
const config=require("./config/defaultConfig")//引入默认设置文件
const app = new Koa()//创建Koa服务器实例

app.use(koaBody())//注册请求体解析库
app.use(cors());//解决跨域请求的中间件
app.use(helmet());//解决基本的网络攻击,如:xss,sql注入






router(app)//注册路由

app.listen(config.runningPort, () => {
console.log(`服务器启动,运行在:http://localhost:${config.runningPort}`)
})//启动服务器在7002端口


第三部分:路由文件构建

提示

由入口文件,最调用的就是路由文件 所有先弄他,

同时:路由文件会包含多个路由文件,分离接口请求,下面实在Frontend_Route.js 即为专门处理前台的路由接口映射

代码如下

const router = require('koa-router')()

const HomeController = require("../controller/home")
const Login_Controller = require("../controller/FrontEnd_Login")
const FrontEnd_Login_Controller=require("../controller/FrontEnd_Login");
const FrontEnd_Common_Controller=require("../controller/FrontEnd_Common");
const loginBaseURL="/frontend/login";
const commonBaseURL="/frontend/common";


module.exports = (app) => {
//根据不同的url映射,分配到对应控制器上去
router.post( `${loginBaseURL}/register/email`, Login_Controller.registerByEmail )
router.post( `${loginBaseURL}/username`,Login_Controller.loginByUserName)
router.post( `${loginBaseURL}/email`, Login_Controller.loginByEmail)

router.post( `/`, HomeController.index)
router.post( `/home`, HomeController.home)

app.use(router.routes())
.use(router.allowedMethods())
}
//

第四部分:控制器构建

提示

由路由映射直接分配的就是控制器目录,所有然后搞他

同时:控制器文件会包含多个控制器文件,分离逻辑处理,而往往会分的比route文件夹更细,例如前台的请求会再分为登录/注册模块 用户数据请求模块 表格数据请求模块等等,后台又分为登录/注册模块....这样子。

const FrontEnd_Login_Service = require('../service/FrontEnd_Login_Service')

module.exports = {
loginByUserName: async(ctx, next) => {
//用户名登录处理的逻辑函数
console.log(ctx.request.body);
console.log(ctx.request.body);

ctx.response.body = "success"
},
loginByEmail: async(ctx, next) => {
//邮箱登录处理的逻辑函数
console.log(ctx.request.body);

ctx.response.body = "success"
},

registerByEmail: async(ctx, next) => {
//邮箱注册处理的逻辑函数
const {userName,password,email}=ctx.request.body;
let db_result=await FrontEnd_Login_Service.getUserInfoByUserName(userName);
if(db_result.cnt!=0){
//已经有了此用户,不允许注册

}
else{
//没有此用户,允许注册
let store_data={username:userName,password,email,comments:"无信息"};
let store_result=await FrontEnd_Login_Service.addingUserByEmail(store_data);
console.log(store_result);

}
ctx.response.body = "success"
},


}

第五部分:设置部分构建

提示

下面两个分别是关于数据库连接池的配置与统一错误返回的配置

数据库连接池的设置
const pg = require('pg');
const Pool = require('pg-pool');
const databaseConfig={
user: 'opengaussuser',
password: 'openGauss@123',
host: '192.168.163.128',
port: 26000,
database: 'liuhf_db_uni'
}
const pool=new Pool(databaseConfig)
//上面是建立数库连接池,将连接池暴露出去即可供service层使用


module.exports = {
pool:pool,
runningPort:7002,
jwt_session_key:`prod_secret_1685715082`,



}
统一JSON格式错误返回配置

module.exports = {
successFormat:(code="200",msg=`success`,data)=>{
return {
code,
msg,
data,
}

},
commonErrorFormat:(code="500",msg=`service-error`,data)=>{
return {
code,
msg,
data,
}
},
verifyErrorFormat:(code="401",msg=`token-error`,data)=>{
return {
code,
msg,
data,
}
}


}

第六部分:services构建

提示

同理:由控制器在调用的就是服务器关联部分

还是同理,sevvices文件会包含多个services文件,分离数据库关联,根据实际需求分就行了,

还有就是,这个模板项目连接的是OpenGauss(pg)数据库 不是mysql,把这部分换成上面数据库讲的就ok了

const pg = require('pg');
const defaultConfig = require('../config/defaultConfig');
const {pool}=defaultConfig;

module.exports = {


addingUserByEmail: async(storeObj) => {
const client=await pool.connect();//请求连接池连接(连接池已经维持了数据库的连接)
let sql=`INSERT INTO zjutuser.userTab (username,password,email,comments) VALUES ('${storeObj.username}','${storeObj.password}','${storeObj.email}','${storeObj.comments}');`;
console.log(sql);
let result=await client.query(sql);
return {resultArr:result.rows,cnt:result.rowCount}

},

getUserInfoByUserName:async(username)=>{

const client=await pool.connect();//请求连接池连接(连接池已经维持了数据库的连接)

let sql=`select * from zjutuser.userTab WHERE username='${username}'`;
let result=await client.query(sql);
return {resultArr:result.rows,cnt:result.rowCount}


},

getUserInfoByEmail: async(name, pwd) => {

},

}

第七部分:中间件构建

第八部分:定时任务构建

提示

除非有特殊的需求,一般都是入口文件直接调用定时任务 例如网站/数据库日志记录,数据库定时备份

以数据库定时备份为例
//每种数据库的定时备份写的语句不一样,这里以postgres为例
const schedule=require("node-cron");
const defaultConfig=require("../config/defaultConfig");
const { data } = require("cheerio/lib/api/attributes");
const {pool}=defaultConfig;
const client=pool.connect();


schedule.schedule('0 0 */1 * *', async ()=>{
const result=await client.query('BACKUP DATABASE TO \'/root/backup.sql\'');

const TimeStamp=new Date().getTime();
console.log(`数据库备份成功! 记录时间戳:${TimeStamp}`);

await client.release();
})


module.exports={
schedule:{
affair:"backup",
type:"working",
}
}