Node.js案例 - 记账本

目录

项目效果

项目的搭建

​编辑

响应静态网页

​编辑

​编辑

结合MongoDB数据库

结合API接口

进行会话控制


项目效果

该案例实现账单的添加删除查看,用户的登录注册。功能比较简单,但是案例主要是使用前段时间学习的知识进行实现的,主要包括express服务的搭建以及使用,并结合MongoDB数据库对数据进行存储以及操作,同时编写相应的API接口,最后进行会话控制,确保数据的安全。如果以下的某一些部分感觉不太理解的话可以看我之前对应的文章。案例中使用到的知识点都是前面文章有涉及到的。

项目的搭建

首先我们直接使用express-generator来快速地搭建express应用骨架,输入命令行:express -e 文件名 然后使用npm i来进行项目依赖的下载。完成之后可以在package.json中对运行的命令 "start": "node./bin/www" 修改为 "start": "nodemon ./bin/www",这样后续的内容修改服务器就会自动地运行了。接下来运行:npm start,进行服务的启动。服务默认是监听3000端口,我们输入http://127.0.0.1:3000进行访问。出现以下页面即服务搭建成功。

接下来,我们需要对路由规则进行配置,我们可以app.js文件中进行查看,app.use('/', indexRouter);我们可以找到对应的路由导入var indexRouter = require('./routes/index'); 因此我们在routes文件夹下面的index.js文件进行路由的配置。

//index.jsvar express = require('express');
var router = express.Router();// 记账本列表
router.get('/account', function(req, res, next) {res.send('账单列表');
});// 记账本列表添加
router.get('/account/create', function(req, res, next) {res.send('添加记录');
});module.exports = router;

输入不同的路径得到不同的结构:

响应静态网页

我们事先准备好了两个页面,一个为账单页面,一个为添加页面。我们借助res.rend()可以对ejs中的内容响应给浏览器的功能来进行操作。在views文件夹下面创建两个ejs文件。将账单页面以及添加页面加入。

//list.ejs<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><linkhref="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css"rel="stylesheet"/><style>label {font-weight: normal;}.panel-body .glyphicon-remove{display: none;}.panel-body:hover .glyphicon-remove{display: inline-block}</style></head><body><div class="container"><div class="row"><div class="col-xs-12 col-lg-8 col-lg-offset-2"><h2>记账本</h2><hr /><div class="accounts"><div class="panel panel-danger"><div class="panel-heading">2023-04-05</div><div class="panel-body"><div class="col-xs-6">抽烟只抽煊赫门,一生只爱一个人</div><div class="col-xs-2 text-center"><span class="label label-warning">支出</span></div><div class="col-xs-2 text-right">25 元</div><div class="col-xs-2 text-right"><spanclass="glyphicon glyphicon-remove"aria-hidden="true"></span></div></div></div><div class="panel panel-success"><div class="panel-heading">2023-04-15</div><div class="panel-body"><div class="col-xs-6">3 月份发工资</div><div class="col-xs-2 text-center"><span class="label label-success">收入</span></div><div class="col-xs-2 text-right">4396 元</div><div class="col-xs-2 text-right"><spanclass="glyphicon glyphicon-remove"aria-hidden="true"></span></div></div></div></div></div></div></div></body>
</html>
//create.ejs
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>添加记录</title><linkhref="/css/bootstrap.css"rel="stylesheet"/><link href="/css/bootstrap-datepicker.css" rel="stylesheet"></head><body><div class="container"><div class="row"><div class="col-xs-12 col-lg-8 col-lg-offset-2"><h2>添加记录</h2><hr /><form method="post" action="/account"><div class="form-group"><label for="item">事项</label><inputname="title"type="text"class="form-control"id="item"/></div><div class="form-group"><label for="time">发生时间</label><inputname="time"type="text"class="form-control"id="time"/></div><div class="form-group"><label for="type">类型</label><select name="type" class="form-control" id="type"><option value="-1">支出</option><option value="1">收入</option></select></div><div class="form-group"><label for="account">金额</label><inputname="account"type="text"class="form-control"id="account"/></div><div class="form-group"><label for="remarks">备注</label><textarea  name="remarks" class="form-control" id="remarks"></textarea></div><hr><button type="submit" class="btn btn-primary btn-block">添加</button></form></div></div></div><script src="/js/jquery.min.js"></script><script src="/js/bootstrap.min.js"></script><script src="/js/bootstrap-datepicker.min.js"></script><script src="/js/bootstrap-datepicker.zh-CN.min.js"></script><script src="/js/main.js"></script></body>
</html>

然后我们修改原本的路由,让其输入/account时为账单页表页面,输入/account/create时为列表添加页面。

// 记账本列表
router.get('/account', function(req, res, next) {res.render('list');
});// 记账本列表添加
router.get('/account/create', function(req, res, next) {res.render('create');
});

结合MongoDB数据库

接下来我们结合前几篇文章我们学习到的MongoDB数据库来对数据的添加,删除以及读取等操作。如果不懂这一步操作的小伙伴看完前面发的文章。

我们在我们项目中创建三个文件夹:config,db以及models。config文件用于对配置进行统一的设置,在里面单独地设置域名端口以及数据库名等信息。db文件夹中创建db.js,主要进行导入mongoose,连接mongoose服务,设置成功以及失败的回调等信息。module用于创建文档结构对象。

//config.js
module.exports={DBHOST:'127.0.0.1',DBPORT:27017,DBNAME:'bilibili',secret:'atguigu'
}
//db.js
module.exports = function (success, error) {//导入配置文件const {DBHOST,DBPORT,DBNAME}=require('../config/config');//导入mongooseconst mongoose = require('mongoose');//连接mongodb服务mongoose.connect(`mongodb://${DBHOST}:${DBPORT}/${DBNAME}`);mongoose.connection.once('open', () => {success();})//设置连接错误的回调mongoose.connection.on('error', () => {error();});//设置连接关闭的回调mongoose.connection.on('close', () => {console.log('连接关闭');});
}

接着再www文件中导入db.js中的函数,在文件中调用函数,传入两个函数,成功的回调以及失败的回调,成功的回调直接写原本启动http服务的代码,确保数据库连接成功之后再启动http服务。失败的回调我们直接输出失败即可。

//www
const db = require('../db/db');
//连接上数据库再来启动http服务
db(()=>{//原本文件中的代码}
},()=>{console.log("连接失败")
})

接着想要操作数据库,我们需要先准备好模型文件:

//models/AccountModel.js
const mongoose = require('mongoose');
//创建文档结构对象
let AccountSchema = new mongoose.Schema({title: {type: String,required: true},time: Date,type: {type: Number,required: true},account: {type: Number,required: true},remarks: {type: String}
});
//创建文档模型对象
let AccountModel = mongoose.model('accounts', AccountSchema);
//暴露模型对象
module.exports = AccountModel;

模型文件准备好之后,我们在对路由文件,routes/index.js文件来进行操作,在原本的文件中添加新增数据的操作:

//新增记录
router.post('/account', function(req, res, next) {AccountModel.create({...req.body,time:moment(req.body.time).toDate()}).then(data=>{res.render('success',{msg:'添加成功~~',url:'/account'});}).catch(err=>{res.status(500).send('插入失败~~');})
});

这里面使用到了一个moment包,主要是用于对日期进行转换为对象形式方便后续的操作。需要在文件中导入const moment=require('moment');

接着我们添加账单,可以使用数据库可视化工具看到自己添加的数据,我使用的是Navicat。新建连接选择Mongo,连接成功之后就可以看到对应的数据库,以及相应的集合。

以上就是我们所添加的数据,但是我们需要在页表页面上也可以看到对应的数据,我们在路由配置文件中进行读取数据的操作。

// 记账本列表
router.get('/account',function(req, res, next) {//获取所有账单信息AccountModel.find().sort({time:-1}).exec().then(data=>{res.render('list',{accounts:data,moment:moment});}).catch(err=>{res.status(500).send('读取失败~~')})});

接着需要修改list.ejs中的代码,方便对数据进行展示:

<!DOCTYPE html>
<html lang="en"><body><div class="container"><div class="row"><div class="col-xs-12 col-lg-8 col-lg-offset-2"><div class="row text-right"><div class="col-xs-12" style="padding-top: 20px;"><form action="/logout" method="post"><button class="btn btn-danger">退出</button></form></div></div><hr><div class="row"><h2 class="col-xs-6">记账本</h2><h2 class="col-xs-6 text-right"><a href="/account/create" class="btn btn-primary">添加账单</a></h2></div><hr /><div class="accounts"><% accounts.forEach(item =>{ %><div class="panel <%= item.type=== -1 ? 'panel-danger':'panel-success' %>"><div class="panel-heading"><%= moment(item.time).format('YYYY-MM-DD') %></div><div class="panel-body"><div class="col-xs-6"><%= item.title %></div><div class="col-xs-2 text-center"><span class="label <%= item.type=== -1 ? 'label-warning':'label-success' %>"><%= item.type=== -1  ? '支出':'收入' %></span></div><div class="col-xs-2 text-right"><%= item.account %> 元</div><div class="col-xs-2 text-right"><a class="delBtn" href="/account/<%= item._id %>"><spanclass="glyphicon glyphicon-remove"aria-hidden="true"></span></a></div></div></div><% }) %>          </div></div></div></div></body>
</html>

接着我们来对数据进行删除操作,我们在list.ejs中对叉号绑定一个事件,当用户确定删除时再进行删除数据,防止数据误删。

 <div class="col-xs-2 text-right"><a class="delBtn" href="/account/<%= item._id %>"><span  class="glyphicon glyphicon-remove" aria-hidden="true"></span></a></div><script>let delBtns=document.querySelectorAll('.delBtn');delBtns.forEach(item =>{item.addEventListener('click',function(e){if(confirm('您确定要删除该文档吗?')){return true;}else{e.preventDefault();}})})</script>

并在路由中设置删除的操作:

//删除记录
router.get('/account/:id',(req,res)=>{//获取params 的 id参数let id=req.params.id;//删除AccountModel.deleteOne({_id:id}).then(data=>{res.render('success',{msg:'删除成功~~',url:'/account'});}).catch(err=>{res.status(500).send('删除失败~~')})});

结合API接口

当我们需要将项目推广到更多的客户端程序时,而不仅仅是局限在我们的浏览器进行访问时,我们就需要为它添加对应的API接口,同样的前几篇文章也有介绍了API接口的详细内容。如果不是太了解的小伙伴可以回头去看看。

为了更好地区分,我们在routes文件夹下创建一个名为web的文件,将原本的路由配置文件放入其中,再创建一个名为api的文件夹,创建一个名为account.js的文件用于存放API路由的配置。文件路径修改之后需要修改引入该文件的路径。并在app.js中导入并使用:

const accountRouter=require('./routes/api/account');app.use('/api',accountRouter);

在api的路由文件中实现创建账单接口、删除账单接口、获取单条数据接口以及更新账单接口。对应代码如下:

// /api/account.js
const express = require('express');
const jwt = require('jsonwebtoken');
//导入moment
const moment = require('moment');
const AccountModel = require('../../models/AccountModel');
//导入中间件
let checkTokenMiddleware = require('../../middlewares/checkTokenMiddleware')
const router = express.Router();// 记账本列表
router.get('/account', checkTokenMiddleware,function (req, res, next) {AccountModel.find().sort({ time: -1 }).exec().then(data => {res.json({//响应码 code: '0000',//响应信息msg: '读取成功',//响应数据data: data})}).catch(err => {res.json({//响应码 code: '1001',//响应信息msg: '读取失败',//响应数据data: null})})});// 记账本列表添加
router.get('/account/create',checkTokenMiddleware, function (req, res, next) {res.render('create');
});//新增记录
router.post('/account', checkTokenMiddleware,function (req, res) {AccountModel.create({...req.body,time: moment(req.body.time).toDate()}).then(data => {res.json({//响应码 code: '0000',//响应信息msg: '创建成功',//响应数据data: data})}).catch(err => {res.json({//响应码 code: '1002',//响应信息msg: '创建失败',//响应数据data: null})})});//删除记录
router.delete('/account/:id', checkTokenMiddleware,(req, res) => {//获取params 的 id参数let id = req.params.id;//删除AccountModel.deleteOne({ _id: id }).then(data => {res.json({//响应码 code: '0000',//响应信息msg: '删除成功',//响应数据data: {}})}).catch(err => {res.json({//响应码 code: '1003',//响应信息msg: '删除失败',//响应数据data: null})})
});
//获取当个账单信息
router.get('/account/:id', checkTokenMiddleware,(req, res) => {//获取params 的 id参数let id = req.params.id;//查询数据库AccountModel.findById(id).then(data => {res.json({//响应码 code: '0000',//响应信息msg: '读取成功',//响应数据data: data})}).catch(err => {res.json({//响应码 code: '1004',//响应信息msg: '读取失败',//响应数据data: null})})
});//更新单个账单信息
router.patch('/account/:id', checkTokenMiddleware,(req, res) => {//获取params 的 id参数let id = req.params.id;AccountModel.updateOne({ _id: id }, req.body).then(data => {//再次查询数据库AccountModel.findById(id).then(data => {res.json({//响应码 code: '0000',//响应信息msg: '更新成功',//响应数据data: data}).catch(err => {res.json({//响应码 code: '1004',//响应信息msg: '读取失败',//响应数据data: null})})}).catch(err => {res.json({//响应码 code: '1005',//响应信息msg: '更新失败',//响应数据data: null})})});})
module.exports = router;

那如何对我们写好的接口做测试呢,在前面的文章中,介绍了Apipost软件来测试接口,我们来尝试一下,发一个GET请求来获取表单的信息数据。成功得到对应的数据。

进行会话控制

接下来我们使用我们学过的session以及token来对数据进行保护。具体的知识点可以看我前几篇发的文章。

接下来,我们为项目添加一个注册的页面,在routes中web文件夹下创建一个auth.js文件,用户配置注册以及登录时的session相关信息。我们同样使用模板引擎来响应注册页面。将该创建好的路由文件在app.js中进行导入以及使用。

创建一个reg.ejs文件:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>注册</title><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet" />
</head><body><div class="container"><div class="row"><div class="col-xs-12 col-md-8 col-md-offset-2 col-lg-4 col-lg-offset-4"><h2>注册</h2><hr /><form method="post" action="/reg"><div class="form-group"><label for="item">用户名</label><input name="username" type="text" class="form-control" id="item" /></div><div class="form-group"><label for="time">密码</label><input name="password" type="password" class="form-control" id="time" /></div><hr><button type="submit" class="btn btn-primary btn-block">注册</button></form></div></div></div>
</body></html>

在响应注册页面

//注册
router.get('/reg',(req,res)=>{res.render('auth/reg');
});

我们需要先创建用户模型,后续才能对其进行插入数据库以及设置session等操作。

const mongoose = require('mongoose');
//创建文档结构对象
let UserSchema = new mongoose.Schema({username:String,password:String});
//创建文档模型对象
let UserModel = mongoose.model('users', UserSchema);
//暴露模型对象
module.exports = UserModel;

将该模型导入到对应的配置文件中,并进行注册等相关操作:

/导入用户模型
const UserModel=require('../../models/UserModel');
const md5=require('md5');//注册用户
router.post('/reg',(req,res)=>{UserModel.create({...req.body,password:md5(req.body.password)}).then(data=>{res.render('success',{msg:'注册成功',url:'/login'});}).catch(err=>{res.status(500).send('注册失败')})
});

接下来实现用户登录功能,我们同样创建一个login.ejs文件放置登录页面模板,复制注册页面的代码,将对应的文字以及路径修改一下即可。这部分不进行代码展示。进行登录的相关操作,当用户登录之后,我们需要对他的session进行写入,并返回sessionid。

//登录
router.get('/login',(req,res)=>{res.render('auth/login');
});
//登录操作
router.post('/login',(req,res)=>{//获取用户名和密码let {username,password}=req.body;UserModel.findOne({username:username,password:md5(password)}).then(data=>{if(!data){return res.send('账号或者密码错误~~')}//写入sessionreq.session.username=data.username;req.session._id=data._id;//登录成功响应res.render('success',{msg:'登录成功',url:'/account'});}).catch(err=>{res.status(500).send('登录失败')})
});

这部分需要先安装express-session以及connect-mongo,并在app.js中进行导入,并设置中间件。

//导入 express-session 
const session = require("express-session");
const MongoStore = require('connect-mongo');//导入配置项
const {DBHOST, DBPORT, DBNAME} = require('./config/config');
//设置 session 的中间件
app.use(session({name: 'sid',   //设置cookie的name,默认值是:connect.sidsecret: 'atguigu', //参与加密的字符串(又称签名)  加盐saveUninitialized: false, //是否为每次请求都设置一个cookie用来存储session的idresave: true,  //是否在每次请求时重新保存session  20 分钟    4:00  4:20store: MongoStore.create({mongoUrl: `mongodb://${DBHOST}:${DBPORT}/${DBNAME}` //数据库的连接配置}),cookie: {httpOnly: true, // 开启后前端无法通过 JS 操作maxAge: 1000 * 60 * 60 * 24 * 7 // 这一条 是控制 sessionID 的过期时间的!!!},
}))

当我们登录成功之后我们可以在数据库中看到我们对应的session信息。

写入之后,我们还需要判断用户是否登录,若用户没有进行登录则拒绝访问,跳转到登录页面。我们在web文件夹下的index.js中编写一个中间件。

//检测登录的中间件
const checkLoginMiddleware = (req, res, next) => {//判断if(!req.session.username){return res.redirect('/login');}next();}

并在下面的路由规则中使用它。这里只对查看记账本列表做演示,其他的一致。

// 记账本列表
router.get('/account',checkLoginMiddleware,function(req, res, next) {AccountModel.find().sort({time:-1}).exec().then(data=>{res.render('list',{accounts:data,moment:moment});}).catch(err=>{res.status(500).send('读取失败~~')})});

接下来继续在auth.js中实现退出登录功能。

//退出登录
router.post('/logout',(req,res)=>{//销毁sessionreq.session.destroy(()=>{res.render('success',{msg:'退出成功',url:'/login'})})
})

在退出登录界面,部分进行修改,防止CSRF跨站请求伪造。它会导致用户的session被获取。大部分的CSRF跨站请求伪造都是使用一个天生具有跨域能力的标签。但是它们发送的请求都是get请求,因此我们将原本的退出修改为post请求。可以防止发生。

<div class="col-xs-12" style="padding-top: 20px;"><form action="/logout" method="post"><button class="btn btn-danger">退出</button></form>
</div>

我们接着在web文件夹下的index.js对首页添加路由规则:

//添加首页路由规则
router.get('/',(req,res)=>{//重定向res.redirect('/account');
});

在app.js中添加404的响应的模板,在views中创建一个404.ejs文件。

// catch 404 and forward to error handler
app.use(function(req, res, next) {res.render('404');
});

以上的操作,我们使用了session对网页端进行了约束,接下来我们使用token来对接口来进行约束。在api文件夹下创建一个auth.js文件对其进行设置。这部分就不再详细介绍了,文件相应的代码如下:

var express = require('express');
var router = express.Router();
//导入jwt
const jwt=require('jsonwebtoken');
//读取配置项
const {secret} =require('../../config/config');
//导入用户模型
const UserModel=require('../../models/UserModel');
const md5=require('md5');//登录操作
router.post('/login',(req,res)=>{//获取用户名和密码let {username,password}=req.body;UserModel.findOne({username:username,password:md5(password)}).then(data=>{if(!data){return res.json({code:'2002',msg:'用户名或者密码错误',data:null})}//创建当前用户tokenlet token=jwt.sign({username:data.username,_id:data._id},secret,{expiresIn:60 * 60 * 24 *7});//响应tokenres.json({code:'0000',msg:'登录成功',data:token})}).catch(err=>{res.json({code:'2001',msg:'数据库读取失败',data:null})})
});//退出登录
router.post('/logout',(req,res)=>{//销毁sessionreq.session.destroy(()=>{res.render('success',{msg:'退出成功',url:'/login'})})
})module.exports = router;

中间件文件:

//checkTokenMiddleware.js
const jwt=require('jsonwebtoken');
//读取配置项
const {secret} =require('../config/config');
module.exports = (req, res, next) => {//获取tokenlet token = req.get('token');//判断if (!token) {return res.json({code: '2003',msg: 'token 缺失',data: null})}//校验tokenjwt.verify(token, secret, (err, data) => {if (err) {return res.json({code: '2004',msg: '校验失败',data: null})}//保存用户的信息req.user=data;//如果执行成功next();});}

通过Apipost来进行校验,当没有携带token时,获取不到数据,当设置请求头token数据时,能够获取到对应的数据。

好啦!本文就到这里了,Node.js系列的文章就告一段落了!如果有不足之处还请见谅~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/182954.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ AVL 树

AVL树的概念 当数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;此时二叉搜索树的搜索效率低下 解决方法&#xff1a;AVL树&#xff08;降低树的高度&#xff0c;从而减少平均搜索长度) 一棵AVL树或者是空树&#xff0c;或者是具有以下性质的二叉搜索树&#xff1…

JavaScript基础—函数、参数、返回值、作用域、变量、匿名函数、综合案例—转换时间,逻辑中断,转换为Boolean型

版本说明 当前版本号[20231129]。 版本修改说明20231126初版20231129完善部分内容 目录 文章目录 版本说明目录JavaScript 基础 - 第4天笔记函数声明和调用声明&#xff08;定义&#xff09;调用细节补充 参数形参和实参函数默认值 返回值作用域全局作用域局部作用域 变量全…

laraval6.0 GatewayWorker 交互通信

laravel 6.0 GatewayWorker 通讯 开发前准备下载 GatewayWorker 及操作方式前端demo测试效果项目中安装GatewayClient 开发前准备 GatewayClient 官网&#xff1a;https://www.workerman.net/ 当前使用的是宝塔操作 下载 GatewayWorker 及操作方式 前端demo 测试效果 项目中安…

纹理烘焙:原理及实现

纹理烘焙是计算机图形学中常见的技术&#xff0c;用于将着色器的细节传输到纹理中。 如果你的着色器计算量很大&#xff0c;但会产生静态结果&#xff0c;例如&#xff0c;这非常有用。 复杂的噪音。 NSDT在线工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器…

Ajax的使用方法

1,什么是Ajax&#xff1f; Ajax&#xff08;异步Javascript和XML&#xff09;&#xff0c;是指一种创建交互式网页应用的网页开发技术。 2&#xff0c;Ajax的作用 Ajax可以使网页实现异步更新----即在不更新整个页面的情况下实现对某一部分进行更新。 简单来说Ajax就是用于连接…

【Python】yaml.safe_load()函数详解和示例

在Python中&#xff0c;PyYAML库提供了对YAML&#xff08;YAML Ain’t Markup Language&#xff09;文件的强大支持。YAML是一种直观的数据序列化标准&#xff0c;可以方便地存储和加载配置文件、数据日志等。 yaml.safe_load和yaml.load是Python的PyYAML库提供的两个函数&…

从零搭建AlibabaCloud微服务项目

1&#xff0c;创建maven项目工程如下 equipment-admin 后台equipment-applet 前台或小程序端或app、h5equipment-common 公共模块equipment-gateway 网关equipment-mapper mapper层操作数据库equipment-model 实体类对应数据库表 2&#xff0c;在父pom文件引入依赖 <proper…

基于Java SSM框架实现美食推荐管理系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现美食推荐管理系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&a…

国内首个农业开源鸿蒙操作系统联合华为正式发布

2023年11月29日&#xff0c;在中国国际供应链促进博览会上&#xff0c;中信农业科技股份有限公司&#xff08;简称“中信农业”&#xff09;与深圳开鸿数字产业发展有限公司&#xff08;简称“深开鸿”&#xff09;以及华为技术有限公司&#xff08;简称“华为”&#xff09;联…

UniWebView 版本3 版本4 版本5介绍

一、介绍 UniWebView是iOS/Android上的web视图组件的包装器&#xff0c;所以运行时拥有与原生web相似性能。是针对Unity所写的插件&#xff0c;节省了项目的开发时间。 官网地址&#xff1a;UniWebView 二、下载&使用 1、下载 &#xff08;1&#xff09;、Unity Asset …

GAN:PacGAN-生成对抗网络中两个样本的威力

论文&#xff1a;https://arxiv.org/pdf/1712.04086.pdf 代码&#xff1a;GitHub - fjxmlzn/PacGAN: [NeurIPS 2018] [JSAIT] PacGAN: The power of two samples in generative adversarial networks 发表&#xff1a;2016 一、摘要 1&#xff1a;GAN最重大的缺陷是&#xf…

自己动手写 chatgpt: Attention 机制的原理与实现

chatgpt等大模型之所以成功都有赖于一种算法突破&#xff0c;那就是 attention 机制。这种机制能让神经网络更有效的从语言中抽取识别其内含的规律&#xff0c;同时它支持多路并行运算&#xff0c;因此相比于原来的自然语言处理算法&#xff0c;它能够通过并发的方式将训练的速…

leetcode 11. 盛最多水的容器(优质解法)

代码&#xff1a; class Solution {public int maxArea(int[] height) {int nheight.length;int left0;int rightn-1;int max0;while (left<right){//计算当前 left 和 right 所在位置的面积int areaMath.min(height[left],height[right])*(right-left);//重置最大值if(are…

进程间通信基础知识【Linux】——上篇

目录 一&#xff0c;理解进程之间的通信 1. 进程间通信目的 2. 进程间通信的技术背景 3&#xff0c;常见的进程间通信 二&#xff0c;管道 1. 尝试建立一个管道 管道的特点&#xff1a; 管道提供的访问控制&#xff1a; 2. 扩展&#xff1a;进程池 阶段一&#xff1a…

sqli-labs靶场详解(less32-less37)

宽字节注入 原理在下方 目录 less-32 less-33 less-34 less-35 less-36 less-37 less-32 正常页面 ?id1 下面有提示 获取到了Hint: The Query String you input is escaped as : 1\ ?id1 看来是把参数中的非法字符就加上了转义 从而在数据库中只能把单引号当成普通的字…

asla四大开源组件应用示例(alsa-lib、alsa-utils、alsa-tools、alsa-plugins)

文章目录 alsa设备文件/dev/snd//sys/class/sound/proc/asoundalsa-lib示例1alsa-utilsalsa-toolsalsa-plugins参考alsa设备文件 /dev/snd/ alsa设备文件目录位于,/dev/snd,如下所示 root@xboard:~#ls /dev/snd -l total 0 drwxr-xr-x 2 root root 60 Nov 6 2023 …

springboot基础配置及maven运行

目录 1、spring快速开始&#xff1a; 2、通过idea工具打开导入包 3、maven打包 1、springboot快速开始&#xff1a; 环境依赖&#xff1a;jdk17 Spring | Quickstart spring初始化包下载&#xff1a; 点击generate&#xff0c;下载包 2、通过idea工具打开导入包 我之前写了…

【Vulnhub靶机】lampiao--DirtyCow

文章目录 漏洞介绍简介原因类型版本危害 信息收集主机扫描端口扫描 漏洞探测漏洞利用权限提升nc文件传输编译 参考 靶机地址&#xff1a;lampiao 下载地址&#xff1a;Lampio: 1 漏洞介绍 简介 脏牛&#xff08;Dirty Cow&#xff09;是Linux内核的一个提权漏洞&#xff0c;…

Redis 通用命令和数据类型

get和set get和set两个命令是最基本也是最常用的命令&#xff0c;主要用于操作字符串类型的数据。 1.SET 命令: SET 命令用于设置指定 key 的值。如果 key 已经持有其他值&#xff0c;SET 就覆写旧值&#xff0c;无视类型。具体的命令格式如下&#xff1a; SET key value例如…

【Linux】tar 命令使用

tar 命令 tar&#xff08;英文全拼&#xff1a;tape archive &#xff09;命令用于备份文件。tar 是用来建立&#xff0c;还原备份文件的工具程序&#xff0c;它可以加入&#xff0c;解开备份文件内的文件。 著者 由约翰吉尔摩和杰伊芬拉森撰写。 语法 tar [选项] [压缩后文…