Node + Express 后台开发 —— 起步_环球今日报

2023-04-21 16:33:03 来源: 博客园
Node + Express 后台开发 —— 起步

前面陆续学习了一下 node、npm、模块,也稍尝试 Express,感觉得换一个思路加快进行。

比如笔者对前端的开发已较熟悉,如果领导给一个内部小网站的需求,难道说你得给我配置一个后端


【资料图】

又不是做一个复杂的后端,只是简单的数据存储(增删改查)、上传文件、下载csv或excel,无需考虑性能、稳定性、负载均衡等等,怎么就不能做

目标

实现简单后台的开发和部署

Express 项目生成器生成项目

可通过应用生成器工具 express-generator 可以快速创建一个应用的骨架

创建项目文件夹 spug-back-end,进入项目后执行 npx express-generator:

Administrator@ /e/spug-back-end$ npx express-generatornpm WARN exec The following package was not found and will be installed: express-generator@4.16.1npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)  warning: the default view engine will not be jade in future releases  warning: use `--view=jade" or `--help" for additional options   create : public\   create : public\javascripts\   create : public\images\   create : public\stylesheets\   create : public\stylesheets\style.css   create : routes\   create : routes\index.js   create : routes\users.js   create : views\   create : views\error.jade   create : views\index.jade   create : views\layout.jade   create : app.js   create : package.json   create : bin\   create : bin\www   install dependencies:     $ npm install   run the app:     $ DEBUG=spug-back-end:* npm start

生成如下内容:

Administrator@ /e/spug-back-end$ lltotal 5-rw-r--r-- 1 Administrator 197121 1075 Apr 14 15:06 app.jsdrwxr-xr-x 1 Administrator 197121    0 Apr 14 15:06 bin/-rw-r--r-- 1 Administrator 197121  301 Apr 14 15:06 package.jsondrwxr-xr-x 1 Administrator 197121    0 Apr 14 15:06 public/drwxr-xr-x 1 Administrator 197121    0 Apr 14 15:06 routes/drwxr-xr-x 1 Administrator 197121    0 Apr 14 15:06 views/

Tip: 对于较老的 Node 版本(8.2.0 以下),请通过 npm 将 Express 应用程序生成器安装到全局环境中并使用

$ npm install -g express-generator$ express

根据上文提示安装依赖 npm install

Administrator@ /e/spug-back-end$ npm installnpm WARN deprecated constantinople@3.0.2: Please update to at least constantinople 3.1.1npm WARN deprecated transformers@2.1.0: Deprecated, use jstransformernpm WARN deprecated jade@1.11.0: Jade has been renamed to pug, please install the latest version of pug instead of jadeadded 99 packages, and audited 100 packages in 22s1 package is looking for funding  run `npm fund` for details8 vulnerabilities (1 low, 4 high, 3 critical)To address all issues (including breaking changes), run:  npm audit fix --forceRun `npm audit` for details.

全局安装 nodemon(在Node.js应用程序开发过程中使用的简单监视器脚本),编码过程中无需重启 node 服务即可生效:

PS E:\spug-back-end> npm i -g nodemonchanged 32 packages, and audited 33 packages in 1s3 packages are looking for funding  run `npm fund` for detailsfound 0 vulnerabilities

修改启动脚本:

"version": "0.0.0",   "private": true,   "scripts": {-    "start": "node ./bin/www"+    "start": "nodemon ./bin/www"

通过 npm run start本地启动服务:

PS E:\spug-back-end> npm run start> spug-back-end@0.0.0 start> nodemon ./bin/www[nodemon] 2.0.22[nodemon] to restart at any time, enter `rs`[nodemon] watching path(s): *.*[nodemon] watching extensions: js,mjs,json[nodemon] starting `node ./bin/www`

入口文件 /bin/www.js默认使用的是 3000 端口(var port = normalizePort(process.env.PORT || "3000");

浏览器访问 http://localhost:3000/,页面显示:

ExpressWelcome to Express
项目解读模板

views目录中存放的是 jade 模板文件:

Administrator@ /e/spug-back-end/views (master)$ lserror.jade  index.jade  layout.jade

首页对应的模板是 index.jade:

Administrator@ /e/spug-back-end/views (master)$ cat index.jadeextends layoutblock content  h1= title  p Welcome to #{title}

Express 生成器默认使用 jade 模板,对前端不是很友好,笔者通过 npx express-generator --view=ejs选用 ejs模板(对前端更友好)重新创建。

首页对应模板 index.ejs:

      <%= title %>            

<%= title %>

Welcome to <%= title %>

入口文件

bin/www.js是应用的入口文件。

核心代码如下:

// 加载 app.jsvar app = require("../app");var http = require("http");var port = normalizePort(process.env.PORT || "3000");app.set("port", port);// 创建 http servervar server = http.createServer(app);server.listen(port);

完整代码如下:

#!/usr/bin/env node/** * Module dependencies. */var app = require("../app");var debug = require("debug")("spug-back-end:server");var http = require("http");/** * Get port from environment and store in Express. */var port = normalizePort(process.env.PORT || "3000");app.set("port", port);/** * Create HTTP server. */var server = http.createServer(app);/** * Listen on provided port, on all network interfaces. */server.listen(port);server.on("error", onError);server.on("listening", onListening);/** * Normalize a port into a number, string, or false. */function normalizePort(val) {  var port = parseInt(val, 10);  if (isNaN(port)) {    // named pipe    return val;  }  if (port >= 0) {    // port number    return port;  }  return false;}/** * Event listener for HTTP server "error" event. */function onError(error) {  if (error.syscall !== "listen") {    throw error;  }  var bind = typeof port === "string"    ? "Pipe " + port    : "Port " + port;  // handle specific listen errors with friendly messages  switch (error.code) {    case "EACCES":      console.error(bind + " requires elevated privileges");      process.exit(1);      break;    case "EADDRINUSE":      console.error(bind + " is already in use");      process.exit(1);      break;    default:      throw error;  }}/** * Event listener for HTTP server "listening" event. */function onListening() {  var addr = server.address();  var bind = typeof addr === "string"    ? "pipe " + addr    : "port " + addr.port;  debug("Listening on " + bind);}

Tip: 和我们之前创建的最简单的服务器类似

app.js

入口文件中引入 app.js,完整代码如下:

var createError = require("http-errors");var express = require("express");var path = require("path");var cookieParser = require("cookie-parser");var logger = require("morgan");var indexRouter = require("./routes/index");var usersRouter = require("./routes/users");// Creates an Express applicationvar app = express();// view engine setup// 模板引擎相关代码app.set("views", path.join(__dirname, "views"));app.set("view engine", "ejs");app.use(logger("dev"));// for parsing application/jsonapp.use(express.json());// for parsing application/x-www-form-urlencodedapp.use(express.urlencoded({ extended: false }));app.use(cookieParser());// 放开静态资源app.use(express.static(path.join(__dirname, "public")));// 定义两个路由。一个返回页面一个返回后端数据// app.use([path,] callback [, callback...]) - 在指定路径挂载指定的一个或多个中间件函数:当请求路径的基匹配路径时,执行中间件函数。app.use("/", indexRouter);app.use("/users", usersRouter);// catch 404 and forward to error handlerapp.use(function(req, res, next) {  next(createError(404));});// error handlerapp.use(function(err, req, res, next) {  // set locals, only providing error in development  res.locals.message = err.message;  res.locals.error = req.app.get("env") === "development" ? err : {};  // render the error page  res.status(err.status || 500);  res.render("error");});module.exports = app;

和我们之前用 Express 实现的报名系统 非常类似。这里创建了一个 Express 应用,并定义了两个示例路由:

var indexRouter = require("./routes/index");var usersRouter = require("./routes/users");// 返回页面app.use("/", indexRouter);// 返回后端数据app.use("/users", usersRouter);
路由

app.js 使用了两个路由。内容如下:

// routes/index.jsvar express = require("express");var router = express.Router();/* GET home page. */router.get("/", function(req, res, next) {  res.render("index", { title: "Express" });});module.exports = router;
// routes/users.jsvar express = require("express");var router = express.Router();/* GET users listing. */router.get("/", function(req, res, next) {  res.send("respond with a resource");});module.exports = router;

浏览器访问 http://localhost:3000/users,页面显示 respond with a resource

mongodb

数据库通常会安装到 linux 中,这里以 ubuntu 为例,通过 apt-get install mongodb即可安装,非常方便:

// 笔者刚已执行root@linux:/home/pjl# apt-get install mongodbReading package lists... DoneBuilding dependency treeReading state information... Donemongodb is already the newest version (1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu5.3).0 upgraded, 0 newly installed, 0 to remove and 150 not upgraded.

数据库现在已经启动,我们通过mongo -version验证安装成功:

root@linux:/home/pjl# mongo -versionMongoDB shell version v3.6.8git version: 8e540c0b6db93ce994cc548f000900bdc740f80aOpenSSL version: OpenSSL 1.1.1f  31 Mar 2020allocator: tcmallocmodules: nonebuild environment:    distarch: x86_64    target_arch: x86_64

mongodb 配置文件是 /etc/mongodb.conf。请注意下面两项配置:

// 远程连接#bind_ip = 127.0.0.1bind_ip = 0.0.0.0// 开机自启动#auth = trueauth = true
MongoDB shell

输入 mongo 即可进入 MongoDB shell(操作mongo):

root@linux:/home/pjl# mongoMongoDB shell version v3.6.8connecting to: mongodb://127.0.0.1:27017Implicit session: session { "id" : UUID("aedc6541-4a67-4e60-8eb4-d1325c82d061") }MongoDB server version: 3.6.8Welcome to the MongoDB shell.For interactive help, type "help".For more comprehensive documentation, see        http://docs.mongodb.org/Questions? Try the support group        http://groups.google.com/group/mongodb-userServer has startup warnings:2023-04-15T14:34:32.327+0800 I STORAGE  [initandlisten]2023-04-15T14:34:32.327+0800 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine2023-04-15T14:34:32.327+0800 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem2023-04-15T14:34:34.861+0800 I CONTROL  [initandlisten]2023-04-15T14:34:34.861+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.2023-04-15T14:34:34.861+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.2023-04-15T14:34:34.861+0800 I CONTROL  [initandlisten]> help        db.help()                    help on db methods        db.mycoll.help()             help on collection methods        sh.help()                    sharding helpers        rs.help()                    replica set helpers        help admin                   administrative help        help connect                 connecting to a db help        help keys                    key shortcuts        help misc                    misc things to know        help mr                      mapreduce        show dbs                     show database names        show collections             show collections in current database        show users                   show users in current database        show profile                 show most recent system.profile entries with time >= 1ms        show logs                    show the accessible logger names        show log [name]              prints out the last segment of log in memory, "global" is default        use                 set current database        db.foo.find()                list objects in collection foo        db.foo.find( { a : 1 } )     list objects in foo where a == 1        it                           result of the last line evaluated; use to further iterate        DBQuery.shellBatchSize = x   set default number of items to display on shell        exit                         quit the mongo shell> show dbsadmin   0.000GBconfig  0.000GBlocal   0.000GB

通过 help可以查看帮助,通过show dbs发现现在有三个数据库。

新建数据库

通过 use 可以创建或切换数据库。比如:

// use - 创建数据库 pjl_db。如果存在则直接切换> use pjl_dbswitched to db pjl_db// db - 目前操作的是 pjl_db 数据库> dbpjl_db

由于新建的数据库 pjl_db 什么都没有,所以通过 show dbs显示不出来,可通过 createCollection 创建表,再次查询即可显示该数据库。比如

// 新建数据库未能展示> show dbsadmin   0.000GBconfig  0.000GBlocal   0.000GB// 通过 tab 建有提示> db.createdb.createCollection(  db.createRole(        db.createUser(        db.createView(// 创建表 users。这里叫集合> db.createCollection("users"){ "ok" : 1 }// 再次查询则可显示新建数据库 pjl_db> show dbsadmin   0.000GBconfig  0.000GBlocal   0.000GBpjl_db  0.000GB

Tipdb.createCollection("pjl_db", {size: 1024*1024, capped: true, max: 1000})- 创建 pjl_db 数据库,同时这个数据库最多 1M,记录数只能有1000条,在多一条则把第一条给替代。db.createCollection("users")则不作限制。

删除数据库

通过 db.dropDatabase() 即可删除数据库。比如:

> db.dropDatabase(){ "dropped" : "pjl_db", "ok" : 1 }> show dbsadmin   0.000GBconfig  0.000GBlocal   0.000GB
新增、查看和删除表

通过 db.createCollection("users")新建 users 表。比如:

// 创建表 users。这里叫集合> db.createCollection("users"){ "ok" : 1 }// 再次查询则可显示新建数据库 pjl_db> show dbsadmin   0.000GBconfig  0.000GBlocal   0.000GBpjl_db  0.000GB
db.getCollectionNames - 查看有哪些表db.tableName.drop - 删除某张表

示例:

// 新建两张表: table-b、table-c> db.createCollection("table-b"){ "ok" : 1 }> db.createCollection("table-c"){ "ok" : 1 }// tab 提示> db.getCollectiondb.getCollection(       db.getCollectionInfos(  db.getCollectionNames(// 有哪些表> db.getCollectionNames()[ "table-b", "table-c", "users" ]// 删除table-b表失败> db.table-b.drop()2023-04-15T15:17:10.232+0800 E QUERY    [thread1] ReferenceError: b is not defined :@(shell):1:1// 删除table-b表成功> db["table-b"].drop()true> db.getCollectionNames()[ "table-c", "users" ]
表格-新增数据

通过 db.users.save可以向 users 表中单条、批量插入数据。甚至字段名不同,字段数量不同也能插入。请看示例:

// 给 users 表插入一条数据 {"name": "pjl", age: 18}> db.users.save({"name": "pjl", age: 18})WriteResult({ "nInserted" : 1 })// 查询users表> db.users.find(){ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }// 批量插入数据> db.users.save([{"name": "pjl2", age: 19}, {"name": "pjl3", age: 20}])BulkWriteResult({        "writeErrors" : [ ],        "writeConcernErrors" : [ ],        "nInserted" : 2,        "nUpserted" : 0,        "nMatched" : 0,        "nModified" : 0,        "nRemoved" : 0,        "upserted" : [ ]})// 批量插入成功> db.users.find(){ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }// 换字段名和字段长度,也能插入成功> db.users.save({"name2": "pjl", age2: 18, tel: 131111111})WriteResult({ "nInserted" : 1 })> db.users.find(){ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643a529a73f16a13ae248f46"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }

Tip:向 mongo 表中插入数据非常自由,什么字段、什么类型都可以。

表格-删除数据

通过 db.users.remove({age2:18})可以删除 age2=18 的数据,通过 db.users.remove({})删除 users 表中所有数据。请看示例:

// 目前有4条数据> db.users.find(){ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643a529a73f16a13ae248f46"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 可以删除 age2=18 的数据> db.users.remove({age2:18})WriteResult({ "nRemoved" : 1 })> db.users.find(){ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }// 删除 users 表中所有数据是 `db.users.remove({})`。`db.users.remove()` 会报错> db.users.remove()2023-04-16T09:10:55.859+0800 E QUERY    [thread1] Error: remove needs a query :DBCollection.prototype._parseRemove@src/mongo/shell/collection.js:357:1DBCollection.prototype.remove@src/mongo/shell/collection.js:382:18@(shell):1:1// 删除 users 表中所有数据> db.users.remove({})WriteResult({ "nRemoved" : 3 })> db.users.find()>
表格-修改数据

通过 db.users.update({name:"pjl2"}, {$set: {age: 20}})修改 name=pjl2 的数据,将 age 改为 20。

Tip:直接 db.users.update({name:"pjl"}, {age: 19})会替换整条数据。

\$inc指增加,如果需要减去,则将数字改成负数。

示例如下:

> db.users.find(){ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "name" : "pjl", "age" : 18 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 修改 name=pjl 的数据,将 age改为19> db.users.update({name:"pjl"}, {age: 19})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })// 替换了条数据> db.users.find(){ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 通过 $set 成功更改 age 而不影响其他字段> db.users.update({name:"pjl2"}, {$set: {age: 20}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.users.find(){ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 20 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 给 age 增加1> db.users.update({name:"pjl2"}, {$inc: {age: 1}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.users.find(){ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 21 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 给 age 减去1> db.users.update({name:"pjl2"}, {$inc: {age: -1}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.users.find(){ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 20 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }
表格-查询数据

首先清空 users 表,并插入6条数据。

find

通过 db.users.find()查询所有数据:

> db.users.find(){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }>
大于小于$gt - 大于$gte - 大于等于$lt - 小于$lte - 小于等于
// 查询 age 大于 50 的数据> db.users.find({age:{$gt: 50}}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }// 查询 age 大于 69 的数据> db.users.find({age:{$gt: 69}})// 查询 age 大于等于 69 的数据> db.users.find({age:{$gte: 69}}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }// age 小于 20 > db.users.find({age:{$lt: 20}}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }// 查询 age 大于 10,小于 40 的数据> db.users.find({age:{$gt:10, $lt:40}}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }>
单条件db.users.find({name: "jia"})- 查询 name 等于jia 的数据db.users.find({name: /jia/})- 查询 name 中包含jia 的数据db.users.find({name: /jia$/})- 查询 name 中以 jia 结尾的数据
// 查询 name 等于 jia 的数据> db.users.find({name: "jia"}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }// 查询 name 中包含 jia 的数据> db.users.find({name: /jia/}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }// 查询 name 中以 jia 结尾的数据> db.users.find({name: /jia$/}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }
多条件

db.users.find({$or: [{age: 19}, {name: "jia"}]})查询 age=19 或 name=jia

// 查询 age=19 或 name=jia> db.users.find({$or: [{age: 19}, {name: "jia"}]}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
列过滤db.users.find({}, {name: 1})- 只显示 name 这个字段。默认主键回返回。1 是显示,0指不显示db.users.find({}, {name: 1, age: 1, _id: 0})- 只要 name 和 age 字段,主键不要
// 只显示 name 这个字段。默认主键回返回> db.users.find({}, {name: 1}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali" }// 只显示 name 这个字段,主键也不要> db.users.find({}, {name: 1, _id: 0}){ "name" : "peng" }{ "name" : "jia" }{ "name" : "li" }{ "name" : "pengjia" }{ "name" : "pengjiali" }{ "name" : "jiali" }// 只要 name 和 age 字段,主键不要> db.users.find({}, {name: 1, age: 1, _id: 0}){ "name" : "peng", "age" : 19 }{ "name" : "jia", "age" : 29 }{ "name" : "li", "age" : 39 }{ "name" : "pengjia", "age" : 49 }{ "name" : "pengjiali", "age" : 59 }{ "name" : "jiali", "age" : 69 }
排序

通过 db.users.find().sort({age: -1})执行 age 逆序,正序则为1。

// age 逆序> db.users.find({}, {age:1}).sort({age: -1}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "age" : 69 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "age" : 59 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "age" : 49 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "age" : 39 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "age" : 29 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "age" : 19 }// age 正序> db.users.find({}, {age:1}).sort({age: 1}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "age" : 19 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "age" : 29 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "age" : 39 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "age" : 49 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "age" : 59 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "age" : 69 }
分页

分页可以通过 skip 和 limit实现。例如 db.users.find().skip(2).limit(2)查询第二页。跳过2条,查询2条。

> db.users.find(){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }// 查询前3条> db.users.find().limit(3){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }// 第一页。跳过0条,查询2条> db.users.find().skip(0).limit(2){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }// 第二页。跳过2条,查询2条> db.users.find().skip(2).limit(2){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }// 第三页。跳过4条,查询2条> db.users.find().skip(4).limit(2){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }// 先 age 逆序,在取2条> db.users.find().sort({age: -1}).skip(0).limit(2){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }

通过 count() 查询记录数,例如 db.users.find({name: "jia"}).count()

// 总共 6 条> db.users.find().count()6// 符合条件的共 1 条> db.users.find({name: "jia"}).count()1> db.users.find({name: /jia/}).count()4

db.users.findOne()- 查询第一条

// 查询第一条> db.users.findOne(){        "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"),        "name" : "peng",        "age" : 19,        "tel" : "0730-1231"}
项目目录划分

程序那么复杂,不可能全写在一起,笔者做如下分层:

路由层- 匹配路由,调用控制层控制层- 取得前端请求数据,加工处理,比如调用数据库(services层),在返回数据给前端服务层- 引用创建的数据库模型,对数据进行增删改查模型层- 创建数据库模型(这里是创建表)

用户模块为例,在这4层中分别创建如下 js:

- services            // 服务层  - UserService.js    // User 模块的服务层- routes              // 路由  - UserRouter.js     // User 模块的路由。比如登录、登录- models              // 模型层  - UserModel.js      // User Model- controllers         // 控制层  - UserController.js 
连接数据库

前面我们操作数据库是直接进入 mongo shell 操作:

// 创建并切换到数据库 pjl_dbuse pjl_db// 创建 users 表(这里叫集合)db.createCollection("users")

现在我们需要通过 node 来操作数据库。

启动mongo服务

通过 mongod --dbpath=/var/lib/mongodb --bind_ip=0.0.0.0 --port=27017启动mongo服务(未设置数据库密码)。其中 0.0.0.0用户远程连接,端口是 27017

如果需要后台启动(关闭终端服务也不会停止),需要指定日志路径,就像这样:

root@linux:/home/pjl# mongod --dbpath=/var/lib/mongodb --fork --logpath=/var/log/mongodb/mongodb.log --bind_ip=0.0.0.0 --port=27017about to fork child process, waiting until server is ready for connections.forked process: 133354child process started successfully, parent exiting

查看 mongod 进程:

root@linux:/home/pjl# ps aux |grep mongodroot      133354  7.5  1.5 976348 62496 ?        Sl   09:46   0:00 mongod --dbpath=/var/lib/mongodb --fork --logpath=/var/log/mongodb/mongodb.log --bind_ip=0.0.0.0 --port=27017root      133383  0.0  0.0  12120   716 pts/0    S+   09:47   0:00 grep --color=auto mongodroot@linux:/home/pjl#

Tip:还可以通过指定配置文件启动:

root@linux:/home/pjl# mongod -f /etc/mongodb.conf// 没反应,通过 `tail -10 日志` 能看到输出

另起窗口进入 mongo shell,运行 show dbs报错如下:

// 笔者将配置文件中端口改为 27027root@linux:/var/log/mongodb# mongo --port=27027MongoDB shell version v3.6.8connecting to: mongodb://127.0.0.1:27027/Implicit session: session { "id" : UUID("dc184887-824d-474a-a942-3d42ff1a21bf") }MongoDB server version: 3.6.8> show dbs2023-04-21T09:52:04.824+0800 E QUERY    [thread1] Error: listDatabases failed:{        "ok" : 0,        "errmsg" : "there are no users authenticated",        "code" : 13,        "codeName" : "Unauthorized"} :_getErrorWithCode@src/mongo/shell/utils.js:25:13Mongo.prototype.getDBs@src/mongo/shell/mongo.js:67:1shellHelper.show@src/mongo/shell/utils.js:860:19shellHelper@src/mongo/shell/utils.js:750:15@(shellhelp2):1:1>

网友说是 安装mongo数据库时,配置文件中加了安全权限的设置,解决请看这里

安装 mongoose

通过 npm i mongoose -D安装 mongoose —— Mongoose is a MongoDB object modeling tool)。

通过 mongoose 操作数据库非常方便

连接数据库

连接数据库很简单,只需要先启动数据库服务,然后在启动应用前连接数据库即可。代码如下:

// bin/www.jsvar http = require("http");+ // 引入数据库+ require("../config/db.config")var port = normalizePort(process.env.PORT || "3000");
// config/db.config.jsconst mongoose = require("mongoose")// mongo服务 ipmongoose.connect("mongodb://192.168.1.223:27017/pjl_db")mongoose.connection.once("open", () => {    console.log("数据库连接成功")})

通过 mongo shell 查到目前只有3个数据库:

> show dbsadmin   0.000GBconfig  0.000GBlocal   0.000GB

启动应用,控制台输出 数据库连接成功:

PS E:\spug-back-end> npm run start> spug-back-end@0.0.0 start> nodemon ./bin/www[nodemon] 2.0.22[nodemon] to restart at any time, enter `rs`[nodemon] watching path(s): *.*[nodemon] watching extensions: js,mjs,json[nodemon] starting `node ./bin/www`数据库连接成功

发现 pjl_db 数据库自动被创建。

> show dbsadmin   0.000GBconfig  0.000GBlocal   0.000GBpjl_db  0.000GB

Tip:有了数据库,还得需要表才能对数据进行增、删、改、查。在 mongoose 中,需要创建一个 model(模型),可以把他当做一张表(或一条记录),比如下文登录模块的 UserModel.js就是一个 model,向 model 插入一条数据时,mongoose 会自动创建一张名为 users 的表(或集合)。

登录

这里我们完成系统登录模块的开发。

app.js

app.js 引入用户路由。

// app.jsvar indexRouter = require("./routes/index");var usersRouter = require("./routes/users");+ const UserRouter = require("./routes/UserRouter")app.use("/users", usersRouter);+ app.use(UserRouter);
UserRouter.js

定义登录路由(/user/login),路由匹配成功,进入控制层处理。

// routes/UserRouter.js// 用户路由var express = require("express");var router = express.Router();const UserController = require("../controllers/UserController.js")/* POST users listing. */router.post("/user/login", UserController.login);module.exports = router;
UserController.js

控制层调用服务层,如果数据库能通过用户名和密码查询到该用户则表明登录成功,否则返回用户名密码不匹配

// controllers/UserController.jsconst UserService = require("../services/UserService")const UserModel = require("../models/UserModel")const UserController = {    login: async (req, res) => {        // req.body - 例如 {"username":"pjl","password":"123456"}        console.log("res.body", JSON.stringify(res.body))        var result = await UserService.login(req.body)        if(result.length === 0){            res.send({                code: "-1",                error: "用户名密码不匹配"            })        }else{            res.send({                code: "0",                error: ""            })        }    }}module.exports = UserController
UserService.js

模型层通过 model 查找数据库

// services/UserService.jsconst UserModel = require("../models/UserModel.js")const UserService = {    login: async ({username, password}) => {        return UserModel.find({            username,            password        })    }}module.exports = UserService
UserModel.js

mongoose 通过 model 创建表。

// models/UserModel.js// model 与表一一对应const mongoose = require("mongoose")const Schema = mongoose.Schema// 通过 schema 限制一下集合(表),否则什么都能传入,太自由了不好const Usertype = {    username: String,    password: String,    // 性别    gender: Number,    // 头像    avatar: String,    // 角色    role: Number, // 管理员1,编辑2}const UserModel = mongoose.model("user", new Schema(Usertype))module.exports = UserModel
测试

这里笔者在 git bash 中使用 curl(客户端的url) 模拟登录(post 用户名+密码):

Administrator@ ~/Desktop$ curl -X POST -d "username=pjl" -d "password=123456" http://localhost:3000/user/login  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                 Dload  Upload   Total   Spent    Left  Speed100    76  100    48  100    28   1425    831 --:--:-- --:--:-- --:--:--  2533{"code":"-1","error":"用户名密码不匹配"}

由于 users 表中没有数据,当然也就查询不到(返回 {"code":"-1","error":"用户名密码不匹配"})。

笔者手动插入该条用户信息:

> use pjl_dbswitched to db pjl_db> db.users.save({username: "pjl", password: "123456"})WriteResult({ "nInserted" : 1 })

再次登录就能查询到 {"code":"0","error":""}

Administrator@ ~/Desktop$ curl -X POST -d "username=pjl" -d "password=123456" http://localhost:3000/user/login  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                 Dload  Upload   Total   Spent    Left  Speed100    51  100    23  100    28   1730   2106 --:--:-- --:--:-- --:--:--  5100{"code":"0","error":""}

标签:

上一篇 :

下一篇 :

Node + Express 后台开发 —— 起步_环球今日报

Node+Express后台开发——起步前面陆续学习了一下node、npm、模块,也稍尝试Express,感觉得换一个思路...

04-21 16:33:03

一个洗衣粉袋子的循环之路:如何突破塑料回收瓶颈

一个废弃的矿泉水瓶,比一个洗衣粉、薯片的塑料袋显然更容易进入回收的循环体系。在陶氏公司包装与特种...

04-21 15:25:18

产业观察|算力板块抢眼 年内涨超50%

从业绩看,同花顺算力板块86只个股中,36家已经发布2022年年报或业绩快报,42家公布业绩预告。

04-21 15:26:23

开好局 起好步——青海各行各业这样干|青海省卫健委

开好局起好步——青海各行各业这样干|青海省卫健委

04-21 15:25:22

森源电气(002358)4月21日主力资金净买入404.07万元

截至2023年4月21日收盘,森源电气(002358)报收于4 74元,上涨0 85%,换手率1 42%,成交量13 14万手,成交额6325 98万元。

04-21 15:24:19

绥化工务段加强检查力度提高设备整修质量

中新网黑龙江新闻4月21日电(吴健)19日,绥化工务段绥化线路车间检查验收组班长王洪文,组织8名验收组成...

04-21 14:06:34

天天时讯:行走黄渤海丨传统民居变“网红”民宿,“五一”假期东楮岛村海草房一房难求

行走黄渤海丨传统民居变“网红”民宿,“五一”假期东楮岛村海草房一房难求

04-21 13:41:15

世界实时:外汇局:外商在中国的投资能够共享中国经济高质量发展的投资红利、转型红利以及市场红利

外汇局:外商在中国的投资能够共享中国经济高质量发展的投资红利、转型红利以及市场红利,中国,外资,外商...

04-21 13:35:25

天天观察:农行四川分行:邂逅春日里的“天府粮仓”

文 李彦赤何东周皓图片由农银报业提供阳春三月,万物复苏。四川成都彭州市天府粮仓敖平和濛阳片区,放...

04-21 12:57:40

梦见大火是啥情况_梦见大火是什么原因

1、 "火 "相关的周公解梦:·梦见登高遥望远处的火焰——由于疏忽而犯错误的可能性很大。2、把约会的...

04-21 12:26:51

尼克斯VS骑士NBA篮球预测今日推荐:两队关键之战

北京时间4月22日上午8点30分,NBA季后赛将继续进行,克利夫兰骑士将要在客场挑战纽约尼克斯。尼克斯VS克...

04-21 12:19:58

华福证券:给予宁德时代买入评级,目标价位702.0元 全球看点

华福证券有限责任公司邓伟,游宝来近期对宁德时代进行研究并发布了研究报告《Q1业绩超预期,新技术落地巩...

04-21 11:24:02

当期是什么意思通俗一点_当期是什么意思

1、当期就是当前所在的会计分期。2、比如会计分期为月,现在说当期,当期就是2月份,会计分期为季,说当...

04-21 11:22:52

辽宁省人大常委会原副主任孙国相受贿案一审开庭 天天时快讯

2023年4月20日,河北省廊坊市中级人民法院一审公开开庭审理了辽宁省人大常委会原党组副书记、副主任孙国...

04-21 10:51:14

聚焦茶叶过度包装:为让茶叶变高档散茶也要高端装

聚焦茶叶过度包装:为让茶叶变高档散茶也要高端装

04-21 10:43:51

续约曼城!瓜帅神了,50万周薪,取消解约,名记揭内幕,皇马失望 每日视讯

听到这个消息,给人的第一感觉就是不可思议,哈兰德刚加盟俱乐部一年不到,合同还有4年到期,为什么就续...

04-21 09:39:18

武陵源区:开展监督检查 保护群众舌尖上的安全

红网时刻张家界4月20日讯(通讯员邓恒)随着景区游客的增加,游客在景区用餐日益增多。为进一步规范景区...

04-21 09:45:30

热资讯!仙鹤股份:4月20日融资买入544.39万元,融资融券余额9452.85万元

4月20日,仙鹤股份(603733)融资买入544 39万元,融资偿还385 35万元,融资净买入159 04万元,融资...

04-21 09:12:09

讯息:青岛机场到青岛北站地铁时刻表_青岛机场到青岛北站

1、公交线路:613路→118路。2、全程约14 1公里从青岛流亭国际机场步行约370米,到达流亭国际机场站2、乘坐

04-21 08:46:49

不再盲目穿衣,50岁女性必备穿搭技巧,教你如何展现优雅与自信

不再盲目穿衣,50岁女性必备穿搭技巧,教你如何展现优雅与自信,驼色,时尚,连衣裙,穿搭技巧,身材比例

04-21 08:19:19

京东发布《2023线上图书消费观察》:女性比男性更爱买书|世界热头条

新京报贝壳财经讯(记者程子姣)在4月23日“世界读书日”到来之际,京东消费及产业发展研究院发布《2023...

04-21 08:13:47

海正药业:4月20日融资净买入1751.8万元,连续3日累计净买入2273.71万元|环球关注

4月20日,海正药业(600267)融资买入3914 12万元,融资偿还2162 32万元,融资净买入1751 8万元,融...

04-21 08:11:45

两项新国标公布 “快递单”个人信息保护有了新标准_天天热推荐

“快递单”个人信息保护有了新标准,让人头疼不已的“快递单”个人信息泄露现象将会得到遏制。近日国家...

04-21 07:36:28

灵耀14 2023直降千元?第13代酷睿+75Wh长续航,高性价比真的香

从第13代酷睿发布以来,不少的数码厂商都对自家的产品进行更新迭代,给用户带来了更多的选择。但对于不...

04-21 07:05:39

美股异动|诺基亚跌8% 营业利润同比下滑18% 经调整每股收益逊预期_世界视讯

格隆汇4月20日丨诺基亚(NOK US)跌8 23%,报4 24,总市值239亿美元。4月20日,诺基亚公布2023年第

04-21 05:55:35

【天天快播报】维远股份(600955):4月20日北向资金减持3.25万股

4月20日北向资金减持3 25万股维远股份。近5个交易日中,获北向资金减持的有4天,累计净减持15 97万股...

04-21 05:52:16

当前焦点!桡动脉止血器_桡动脉多深可以割破吗

1、病情分析:桡动脉在腕掌上部位置很浅,可见其脉搏。2、除了特别肥胖的人。3、皮下不到4毫米。4、这个...

04-21 05:49:56

孚日股份:低毛利率之谜丨年报风云47

“毛巾大王”、“代工大王”,这两个标签贴给孚日股份(002083 SZ)不过分,从数据中便可窥见一二。

04-21 05:24:27

重庆世贸大厦房产拟转让 系中西部地区的金融商务地标

观点网讯:4月20日消息,重庆世界贸易中心大厦拟进行转让。该大厦是重庆市的一座标志性摩天大楼,位于渝...

04-21 04:50:02

判断一个人会不会带人,就看这3点 天天报资讯

坚持做,难而正确的事情。---李十玥对于想在职场中,做好人才培养的管理者,我建议你首先要学会去提高一...

04-21 04:51:14

飞猪:“五一”假期国内机票、酒店、景区门票、跟团游等预订量均超2019年|全球关注

4月19日,飞猪发布《2023年“五一”出游风向标》,距离“五一”假期不到10天,截至目前,国内机票、酒店...

04-21 04:15:18

刚刚,SpaceX星舰首次发射失败! 世界通讯

刚刚,美国太空探索技术公司(SpaceX)的重型运载火箭“星舰”首次发射失败。来源:@环球时报流程编辑:TF060

04-21 03:47:40

vivo Y02t 4G 手机曝光:6.5 英寸屏幕、5000mAh 电池

IT之家4月20日消息,vivo正在开发一款vivoY02t4G手机,爆料人士ParasGugla

04-21 03:32:04

播报:多地针对多孩家庭出台住房政策,浙江上虞补贴30万元

为减轻企业负担,上虞将对企业在女职工产假期间支付的社会保险费用给予补贴,生育二孩的给予50%的社会保...

04-21 03:41:44

国际黄金为什么是国际通用货币 世界热闻

1、黄金与人类社会生活结合以后,与经济产生了极大的关系,即黄金具有了难以解除的经济属性。黄金逐渐成...

04-21 02:14:56

当前视讯!公民科内地考察团需关注学校管理学生和内地接待能力

​随着香港与内地恢复通关,香港教育局公布2022 23学年高中公民与社会发展科内地考察安排,“首发团”...

04-21 02:10:45

天天热点评!哪个关键词与你紧密相关?从一季度经济数据看生活消费

来自国家统计局的数据显示,一季度最终消费对经济增长贡献率达66 6%,比去年全年明显回升

04-21 01:58:13

突发!美国再盯上4家芯片贸易商

当地时间2023年4月19日,美国财政部海外资产控制办公室(OFAC)依据第13382号行政令,以向PASNA(一个涉...

04-21 01:45:33

连数字8的英文都不会拼写了?拜登口误引嘲讽|全球聚看点

据《纽约邮报》报道,当地时间4月19日,美国总统拜登在马里兰州发表演讲时,将数字8的英文“eight”拼错...

04-21 01:42:42

当前滚动:因道路施工,今天起这条公交线路临时绕行调整

因望田路道路施工,途经该路段的V1路需绕行,经过工作人员实地察看,拟定于4月19日对V1线路进行绕行调整...

04-21 00:52:20

今日精选:马斯克自曝继续降价:我甚至可能0利润销售车辆!18万的Model 3还会远吗?

马斯克自曝继续降价:我甚至可能0利润销售车辆!18万的Model3还会远吗?,马斯克自曝继续降价!0利润也要...

04-21 00:30:04

2023年西安中宝达共有产权房资格复核通知公示名单

按照《西安市共有产权住房建设管理实施细则》《西安市共有产权住房购房资格审核轮候分配暂行规定》的规...

04-21 00:32:01

租赁圆桌快讯 | 霍炳辉:不担心住房租赁供应会出现市场挤占情形|环球热讯

2023观点租赁圆桌·现场快讯:4月18日上午,在2023观点租赁圆桌上,金地商置财务管理中心总经理、金地草...

04-20 23:49:44

全球球精选!时滞效应导致信贷通胀背离,央行称全年CPI将呈“U”形

时滞效应导致信贷通胀背离,央行称全年CPI将呈“U”形,央行,贷款,货币政策,实体经济,时滞效应,信贷通胀,...

04-20 23:40:33

新华财经直播人民银行新闻发布会 每日短讯

4月20日15:00,人民银行将举行新闻发布会,就2023年一季度金融统计数据有关情况答记者问,届时中国人民...

04-20 22:56:11

全球微资讯!理财锁定票息是什么意思?理财产品锁定期可以赎回吗?

理财锁定票息是什么意思?是指理财产品在封闭期内不能赎回。当投资人购买固定期限理财产品或期限理财产品...

04-20 23:04:46

cpa考试报名时间2023

cpa考试报名时间2023由注册会计师考试栏目提供,查找更多考试报名资讯、准考证打印、成绩查询或cpa考试报...

04-20 22:08:10

上证综指4月20日跌0.09%-天天即时

上证综指4月20日走低,以3367 03点报收,比上个交易日跌3 10点,跌幅0 09%。当日,上证综指成交金额4997亿元。

04-20 21:48:38

4月20日获机构调研最多的10只股票|今日最新

4月20日,获机构调研最多的股票分别为:普联软件(行情300996,诊股)、吉宏股份(行情002803,诊股)、红旗连...

04-20 21:02:13

33万左右大众6座中大型SUV推荐,大家最喜欢的车都在这儿!

如果想买一款33万左右大众6座中大型SUV车型,想必很多人在买之前,都会难以抉择,今天小编根据众车网购...

04-20 21:00:03

一个洗衣粉袋子的循环之路:如何突破塑料回收瓶颈
产业观察|算力板块抢眼 年内涨超50%
开好局 起好步——青海各行各业这样干|青海省卫健委
森源电气(002358)4月21日主力资金净买入404.07万元
绥化工务段加强检查力度提高设备整修质量
天天时讯:行走黄渤海丨传统民居变“网红”民宿,“五一”假期东楮岛村海草房一房难求
世界实时:外汇局:外商在中国的投资能够共享中国经济高质量发展的投资红利、转型红利以及市场红利
天天观察:农行四川分行:邂逅春日里的“天府粮仓”
梦见大火是啥情况_梦见大火是什么原因
尼克斯VS骑士NBA篮球预测今日推荐:两队关键之战
华福证券:给予宁德时代买入评级,目标价位702.0元 全球看点
当期是什么意思通俗一点_当期是什么意思
辽宁省人大常委会原副主任孙国相受贿案一审开庭 天天时快讯
聚焦茶叶过度包装:为让茶叶变高档散茶也要高端装
续约曼城!瓜帅神了,50万周薪,取消解约,名记揭内幕,皇马失望 每日视讯
武陵源区:开展监督检查 保护群众舌尖上的安全
热资讯!仙鹤股份:4月20日融资买入544.39万元,融资融券余额9452.85万元
讯息:青岛机场到青岛北站地铁时刻表_青岛机场到青岛北站
不再盲目穿衣,50岁女性必备穿搭技巧,教你如何展现优雅与自信
京东发布《2023线上图书消费观察》:女性比男性更爱买书|世界热头条
海正药业:4月20日融资净买入1751.8万元,连续3日累计净买入2273.71万元|环球关注
两项新国标公布 “快递单”个人信息保护有了新标准_天天热推荐
灵耀14 2023直降千元?第13代酷睿+75Wh长续航,高性价比真的香
美股异动|诺基亚跌8% 营业利润同比下滑18% 经调整每股收益逊预期_世界视讯
【天天快播报】维远股份(600955):4月20日北向资金减持3.25万股
当前焦点!桡动脉止血器_桡动脉多深可以割破吗
孚日股份:低毛利率之谜丨年报风云47
重庆世贸大厦房产拟转让 系中西部地区的金融商务地标
判断一个人会不会带人,就看这3点 天天报资讯
飞猪:“五一”假期国内机票、酒店、景区门票、跟团游等预订量均超2019年|全球关注
刚刚,SpaceX星舰首次发射失败! 世界通讯
vivo Y02t 4G 手机曝光:6.5 英寸屏幕、5000mAh 电池
播报:多地针对多孩家庭出台住房政策,浙江上虞补贴30万元
国际黄金为什么是国际通用货币 世界热闻
当前视讯!公民科内地考察团需关注学校管理学生和内地接待能力
天天热点评!哪个关键词与你紧密相关?从一季度经济数据看生活消费
突发!美国再盯上4家芯片贸易商
连数字8的英文都不会拼写了?拜登口误引嘲讽|全球聚看点
当前滚动:因道路施工,今天起这条公交线路临时绕行调整
今日精选:马斯克自曝继续降价:我甚至可能0利润销售车辆!18万的Model 3还会远吗?
2023年西安中宝达共有产权房资格复核通知公示名单
租赁圆桌快讯 | 霍炳辉:不担心住房租赁供应会出现市场挤占情形|环球热讯
全球球精选!时滞效应导致信贷通胀背离,央行称全年CPI将呈“U”形
新华财经直播人民银行新闻发布会 每日短讯
全球微资讯!理财锁定票息是什么意思?理财产品锁定期可以赎回吗?
cpa考试报名时间2023
上证综指4月20日跌0.09%-天天即时
4月20日获机构调研最多的10只股票|今日最新
33万左右大众6座中大型SUV推荐,大家最喜欢的车都在这儿!
成都市29.8亿元成功出让3宗宅地 越秀入驻成华区崔家店板块
X 广告
资讯
X 广告

Copyright ©  2015-2022 中公畜牧网版权所有  备案号:沪ICP备2022005074号-18   联系邮箱:5855973@qq.com