当前位置:   article > 正文

前端毕业设计:Nodejs+Vue菜鸟驿站仓库管理系统的设计与实现_菜鸟驿站的仓管系统架构

菜鸟驿站的仓管系统架构

作者主页:编程千纸鹤

作者简介:Java、前端、Pythone开发多年,做过高程,项目经理,架构师

主要内容:Java项目开发、毕业设计开发、面试技术整理、最新技术分享

项目编号:BS-QD-004        

前言:

当前时代,全球的经济已经从工业经济到知识经济的改变,过去专家说知识经济的两个首要属性是信息化和全球化,要完成化和全球化,这时就需要稳定的网络和完备的数据库。但时至今日中国的服务价值意识提高,人力成本开始骤增,对于很多快递企业来说一个10元成本的单子,有5元是在最后一公里被消耗了。研究快递驿站的智能物流货架的应用方案,随着社会和科学技术的演变,仓储管理的方法也不断优化。从全部人工管理的方法,这样不仅效率低,工作量大,并且准确率差。尤其是当今快递行业飞速发展,货流量大,人工管理更显不足。为了提高仓库管理效率,减少仓库管理成本,特开发菜鸟驿站库存管理系统。

一,项目简介

本系统使用 Node.JS 语言开发,该编程语言简单易学,能够与多个框架完美结合,又具备面向对象、与平台无关、多线程等特点,因此比较容易进行开发工作。开发工具使用Visual Studio Code,它强大的整合能力能更好的对项目进行管理,丰富的提示功能可以使开发人员更加高效的进行开发工作。数据库使用MySQL,其简单易用、开源免费、社区庞大而完善的特点,对于初次尝试人员非常友好。系统的架构是B/S,也就是浏览器/服务器模型,客户端完成主要的业务,一些交互或发起请求在前端完成。对于用户非常简单,通过一个浏览器就可以与系统进行访问和交互。为了实现高效的开发和后期维护,系统采用了MVC架构,即数据模型层、视图表现层、路由控制层,客户通过视图交互实现数据功能的使用效果,路由控制层主要处理前端传过来的各种请求,通过路由和参数以及身份认证来处理逻辑,并把最终的结果给到前端,数据模型层主要是对数据的增删改查等操作,当然主要依赖于路由控制层。如上的开发工具和技术都能够满足开发需求,故在技术上是可行的。随着科学技术的不断提高,计算机科学应用也越来越普及,相比手工管理,使用计算机软件对仓储信息进行管理,无论从效率、科学性还是规范性方面都有着巨大的优势,该软件的开发不会侵犯国家、集体和他人的利益,所以其具备社会公认的可行性。系统开发不需要高端设备,大部分技术开源免费,哪怕开发过程中发生重大失误,改正即可,不会损耗材料,系统开发出来,可以无限备份投入使用,非常完全符合经济上的可行性。

业务流程图

系统数据流程图

二,环境介绍

2.2相关技术和开发环境

2.2.1 相关技术

(1)B/S结构(Browser/Server结构)简介

B/S(Brower/Server,浏览器/服务器)模式又称B/S结构,是Web兴起后的一种网络结构模式。Web浏览器是客户端最主要的应用软件。这种模式统一了客户端,将系统功能实现的核心部分集中到服务器上,简化了系统的开发、维护和使用;客户机上只需要安装一个浏览器,服务器上安装SQL Server, Oracle, MySql等数据库;浏览器通过Web Server同数据库进行数据交互。Browser指的是Web浏览器,极少数事务逻辑在前端实现,但主要事务逻辑在服务器端实现。B/S架构的系统无须特别安装,只有Web浏览器即可。

其实就是我们前端现在做的一些事情,大部分的逻辑交给后台来实现,我们前端大部分是做一些数据渲染,请求等比较少的逻辑。通过三层结构模型,大大减轻了客户端的压力,降低了开发和维护的成本,也降低了客户的总成本。

(2)Mysql简介

MySQL是Web世界中使用最广泛的数据库服务器。SQLite的特点是轻量级、可嵌入,但不能承受高并发访问,适合桌面和移动应用。而MySQL是为服务器端设计的数据库,能承受高并发访问,同时占用的内存也远远大于SQLite。

此外,MySQL内部有多种数据库引擎,最常用的引擎是支持数据库事务的InnoDB。存储引擎就是存储数据,建立索引,更新、查询数据等技术的实现方式。存储引擎是基于表的。mysql支持多种存储引擎,而oracle、sqlserver等只有一种存储引擎

即市场占有率最大的关系型数据库,类似于excel表格

DML:select、insert、update、delete

DDL:drop、create等

  1. MySQL数据库的优点

 MySQL的主要优势如下:

1、速度: 系统运行速度非常快。

2、价格:MySQL对多数个人来说是免费使用的。

3、容易使用:相比于其他数据库的设置和管理相比,相对于比较简单,容易学习。

4、可移植性: 跨平台能力,可以适用于不同的系统平台之下,例如:Windows 、Linux、Unix、MacOS等。

5、丰富的接口: 提供了用于C 、C++、Eiffel、Java、Perl、PHP、Python、Rudy和TCL 等语言的API。

6、支持查询语言:MySQL可以利用标准SQL语法和支持ODBC(开放式数据库连接)的应用程序。

7、安全性和连接性; 非常灵活的安全和校验机制,允许主机验证。连接到服务器时,所有的密码

都采用加密形式,从而保证了密码安全。并且由于MySQL时网络化的,因此可以在因特网网上的任何地方访问,提高数据共享效率。

(4)HTML简介

HTML 是用来描述网页的一种语言。

HTML 指的是超文本标记语言 (Hyper Text Markup Language)

HTML 不是一种编程语言,而是一种标记语言 (markup language)

标记语言是一套标记标签 (markup tag)

HTML 使用标记标签来描述网页

HTML 标签

HTML 标记标签通常被称为 HTML 标签 (HTML tag)。

HTML 标签是由尖括号包围的关键词,比如 <html>

HTML 标签通常是成对出现的,比如 <b> 和 </b>

标签对中的第一个标签是开始标签,第二个标签是结束标签

开始和结束标签也被称为开放标签和闭合标签

HTML 文档 = 网页

HTML 文档描述网页

HTML 文档包含 HTML 标签和纯文本

HTML 文档也被称为网页

(5)Node.js简介

Node.js是基于google公司旗下的产品Chrome浏览器,其中使用的V8引擎就来自于此。由于浏览器的特殊性,引擎最显著的特点就是事件驱动、异步的I/O模式,在不断的优化下,非常的高效和轻量

(6)Visual Studio Code简介

相比于Visual Studio的重量级产品不同,Visual Studio Code可谓非常的轻量级,它诞生于2015年4月30日的开发者大会上,并且跨平台的特性,以及针对现代web应用和云平台的源代码编辑器。软件只有几十兆,因为不绑定任何的插件,但是因为拥有强大的插件系统和插件商城,所以开发者可以有选择的使用各种插件,提高效率,对于内置支持的Javascript和TypeScript,并且可以通过扩展来支持其他语言,比如C++、C、JAVA、Python等语言。

(7)Nginx简介

Nginx是支持多线程的方式的,只是我们主流的方式还是多进程的方式,也是nginx的默认方式。

  master进程主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。

  worker进程则是处理基本的网络事件。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。

2.2.2 系统的开发环境

系统设计平台:Microsoft Windows 10

数据库设计工具:MySQL

程序设计工具:Visual studio code

三,系统展示

4.2.1 注册界面

注册界面是管理系统最重要的环节,只有注册才能有能力访问管理后台系统,以及进一步的操作,这样也就规避了不同人员在没有权限下,操作管理后台。

没有权限操作数据,会对整个数据造成破坏,产生不可挽回的影响。

具体代码如下:

let { name, password, password2, phone, username } = ctx.request.body;

let adminUserList = await query('select * from admin_user');

let time = dayjs().format('YYYY-MM-DD HH:mm:ss')

if (adminUserList.some(item => item.username === username)) {

   ctx.body = {

code: -1,

data: null,

message:  '该用户已经注册'

};

 } else {

  try {

    let str = `INSERT INTO admin_user(username, password, name, phone, time) VALUES('${username}', '${password}', '${name}', '${phone}', '${time}')`;

    console.log(str);

    await query(str);

    ctx.response.redirect('/login');

 } catch(err) {

    console.log(err);

   ctx.response.redirect('/register');

 }

}

注册操作需要的信息有:用户名(登录名)、用户密码、确认密码、真实姓名、用户手机号。具体操作页面如4-1所示:

4.2.2 登录界面

管理员登录是验证用户身份最主要的手段,后台系统通过token保存用户身份,前端把token放到浏览器storage中存储,每次请求带上token数据。整体的实现也非常简单,通过输入用户名和密码,前端发起http请求,给到后端,后端拿到用户名和密码,进一步判断是否与数据库的数据一致,如果通过验证则跳到首页,否则就会返回给客户端相应的错误。通过认证后,用户就会与之创建连接,就可以完成之后的后续操作了。

具体实现代码如下所示:

let adminUserList = await query(`

SELECT id, username, name, phone

FROM admin_use

WHERE username='${username}' && password='${password}'

`);

 if (adminUserList.length > 0) {

   let token = jwt.sign({ ...adminUserList[0] }, secret);

   ctx.body = {

    code: 1,

      data: {

        token,

            username: username

      },

 message: '登录成功'

}

具体的功能页面如下图4-2所示:

图4-2 登录界面

4.2.3 入库界面

每为确保公司货货物进出入库能得到管制,确保仓库库存数据的准确,必须规范好商品的出入库

流程。最重要的就是入库管理,通过点击顶部的商品入库跳到对应页面

具体代码如下:

操作界面如图4-3:

  router.post('/instock', async function (ctx, next) {

    let { token } = ctx.request.header;

    let { name, phone, goodsName } = ctx.request.body;

    try {

      let userInfo = jwt.verify(token, secret);

      let time = dayjs().format('YYYY-MM-DD HH:mm:ss');

      console.log(name, phone, goodsName, userInfo);

      // 1. 先用手机号查customer表,是否有此用户

      let customerList = await query(`select id, phone from customer`);

      let curCustomer = customerList.find(item => item.phone == phone);

      let customerId;

     

      if (!curCustomer) {

        // 先加一下这个用户

        customerId = customerList.length + 1;

        await query(`insert into customer(id, name, phone, time) values(${customerId}, '${name}', '${phone}', '${time}')`);

      } else {

        customerId = curCustomer.id;

      }

     

      // 2. 找一个空的货架单元

      // 把所有库存的仓库都找出来

      let storehouseGoodsList = await query(`select storehouse_id from storehouse_goods where is_out<>1`);

      let storehouseList = await query(`select id from storehouse`);

      let emptyStoreId = '';

      if (storehouseGoodsList.length > 0) {

        let emptyStoreList = storehouseList.filter(item => !storehouseGoodsList.some(it => it.storehouse_id === item.id))

        if (emptyStoreList.length === 0) throw new Error('没有空余的货架,请出库以腾出空位!');

        emptyStoreId = emptyStoreList[0].id;

      } else {

        emptyStoreId = storehouseList[0].id;

      }

     

      // 写入出库

      let goodsList = await query(`select id from goods`);

      let goodsId = goodsList.length + 1;

      await query(`insert into goods(id, name, customer_id, admin_id) values(${goodsId}, '${goodsName}', ${customerId}, ${userInfo.id})`);

      await query(`insert into storehouse_goods(goods_id, storehouse_id, is_out, instock_time) values(${goodsId}, ${emptyStoreId}, 0 ,'${time}');`);

      ctx.body = {

        code: 1,

        data: null,

        message: '商品入库成功!'

      }

    } catch(err) {

      console.log(err);

      ctx.response.redirect('/login');

    }

  });

图4-3 商品入库 

4.2.4 出库界面

仓当客户收到取件码后,通过扫码就可以获得对应的库存id(因为没有具体的物理设备,此操作通过查询步骤,给出库存id),通过检验身份信息,此功能通过点击顶部的商品出库跳到对应的页面完成

相关代码如下:

具体操作页面如图4-4:

  router.post('/outstock', async function (ctx, next) {

    let { token } = ctx.request.header;

    let { val } = ctx.request.body;

    try {

      let userInfo = jwt.verify(token, secret);

      let sgList = await query(`select id, goods_id, is_out from storehouse_goods where id=${val}`);

      if (sgList.length === 0) throw new Error('库存id无效');

      if (sgList[0].is_out === 1) throw new Error('该商品已经出库');

      let time = dayjs().format('YYYY-MM-DD HH:mm:ss');

      await query(`UPDATE storehouse_goods SET is_out=1, outstock_time='${time}' where id=${val}`);

      ctx.body = {

        code: 1,

        data: null,

        message: '出库成功'

      }

    } catch(err) {

      ctx.body = {

        code: -1,

        data: null,

        message: err.message

      }

    }

  });

图4-4 商品出库界面

4.2.5 查询界面

相关代码如下:

具体操作页面如图4-5所示:

  router.get('/queryorder', async function (ctx, next) {

    let { token } = ctx.request.header;

    let { value } = ctx.request.query;

    try {

      let userInfo = jwt.verify(token, secret);

      let reg_tel = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/;

      let arr = [];

     

      // , G.is_out, G.instock_time, G.outstock_time

      // 如果是手机号

      if (reg_tel.test(value)) {

        let customerList = await query(`SELECT id FROM customer WHERE phone='${value}'`);

   

        if (customerList.length === 0) throw new Error('没有这个手机号的记录');

        // 找到这个商品

        let goodsList = await query(`SELECT G.id, G.name AS 'good_name', A.name AS 'admin_name', C.name AS 'customer_name' FROM goods G, admin_user A, customer C WHERE G.customer_id=${customerList[0].id} AND C.id=${customerList[0].id} AND A.id=G.admin_id`);

       

        if (goodsList.length > 0) {

          for (let i=0; i<goodsList.length; i++) {

            let _item = { ...goodsList[i] };

           

            let sgList = await query(`SELECT * FROM storehouse_goods WHERE goods_id=${_item.id}`);

            if (sgList.length === 0) throw new Error('没有找到此库存');

            let storeList = await query(`SELECT * FROM storehouse WHERE id=${sgList[0].storehouse_id}`);

            if (storeList.length === 0) throw new Error('没有找到此仓库');

            // 找这个仓库

            _item.store_id = storeList[0].id;

            _item.store_code = storeList[0].name;

            _item.sg_id = sgList[0].id;

            _item.is_out = sgList[0].is_out;

            _item.instock_time = sgList[0].instock_time;

            _item.outstock_time = sgList[0].outstock_time;

            arr.push(_item);

          }

         

        }

        console.log(goodsList);

      } else {

        // 使用取件码查询

        // 1. 查询库存表是否有这个仓库

        let storeList = await query(`SELECT id, name FROM storehouse WHERE name='${value}'`);

        if (storeList.length === 0) throw new Error(`${value}这个取件码无效`);

        // 2. 找到这个库存

        let sgList = await query(`SELECT * FROM storehouse_goods WHERE storehouse_id=${storeList[0].id}`);

        if (sgList.length === 0) throw new Error(`没有找到这个库存`);

        // 3. 找到这个商品

        let goodsList = await query(`SELECT G.id, G.name AS 'good_name', A.name AS 'admin_name', C.name AS 'customer_name' FROM goods G, admin_user A, customer C WHERE G.id=${sgList[0].id} AND C.id=G.customer_id AND A.id=G.admin_id`);

        if (goodsList.length > 0) {

          arr.push(goodsList[0]);

          arr[0].store_id = storeList[0].id;

          arr[0].store_code = storeList[0].name;

          arr[0].sg_id = sgList[0].id;

          arr[0].is_out = sgList[0].is_out;

          arr[0].instock_time = sgList[0].instock_time;

          arr[0].outstock_time = sgList[0].outstock_time;

        }

      }

      ctx.body = {

        code: arr.length > 0? 1: -1,

        data: arr,

        message: arr.length > 0? 'query ok': '没有找到指定的库存'

      }

     

    } catch(err) {

      ctx.body = {

        code: -1,

        data: null,

        message: err.message

      }

    }

  })

图4-5 库存查询页面

4.2.6 库存异常界面

在常规的快递库存管理过程中,不可避免的会遇到快递长期无人领取,一方面有可能是客户工作繁忙等原因,也有可能是因为某些原因客户没有收到通知短信,导致库存长期被占用。这里有两个问题:第一、因为库存被占用,不能存放其他快递;第二、客户没有接收到快递,降低用户体验,并且有可能造成用户的经济损失。基于以上原因,特设库存异常功能,解决此类问题,相关代码如下:

      let arr = [];

      let timeWhere = '';

      switch (val) {

        case '1': // 超时一周

          timeWhere = `instock_time < '${ dayjs(dayjs() - 7*24*60*60*1000).format('YYYY-MM-DD HH:mm:ss') }'`;

          break;

        case '2': // 超时3周

          timeWhere = `instock_time < '${ dayjs(dayjs() - 3*7*24*60*60*1000).format('YYYY-MM-DD HH:mm:ss') }'`;

          break;

        case '3': // 超时一个月

          timeWhere = `instock_time < '${ dayjs(dayjs() - 30*24*60*60*1000).format('YYYY-MM-DD HH:mm:ss') }'`;

          break;

        case '4': // 超时三个月

          timeWhere = `instock_time < '${ dayjs(dayjs() - 90*24*60*60*1000).format('YYYY-MM-DD HH:mm:ss') }'`;

          break;

        case '5': // 超时一年

          timeWhere = `instock_time < '${ dayjs(dayjs() - 365*24*60*60*1000).format('YYYY-MM-DD HH:mm:ss') }'`;

          break;

      }

      let sgList = await query(`select * from storehouse_goods where is_out<>1 and ${timeWhere}`);

      for (let i=0; i<sgList.length; i++) {

        let item = { ...sgList[i], instock_time: dayjs(sgList[i].instock_time).format('YYYY-MM-DD HH:mm:ss') };

        let storeList = await query(`select id, name from storehouse where id=${item.storehouse_id}`);

        let goodsList = await query(`select G.id, G.name AS 'good_name', A.name AS 'admin_name', C.name AS 'customer_name' from goods G, admin_user A, customer C where G.id=${item.goods_id} AND C.id=G.customer_id AND A.id=G.admin_id`);

        if (goodsList.length > 0) {

          item.store_id = storeList[0].id;

          item.store_code = storeList[0].name;

          item.admin_name = goodsList[0].admin_name;

          item.customer_name = goodsList[0].customer_name;

          item.good_name = goodsList[0].good_name;

        }

        arr.push(item);

      }

具体操作界面如图4-6所示:

图4-6 库存异常界面

4.2.7 客户信息

有些场景我们需要查找客户信息,比如某一快递异常,找客户的手机号,给客户发消息,这时就需要客户信息功能界面,可以模糊查询。

具体代码如下:

queryWhere = `where name like '%${val}%'`;

let arr = await query(`select * from customer ${queryWhere}`);

arr = arr.map(item => ({ ...item, time: dayjs(item.time).format('YYYY-MM-DD HH-mm-ss') }));

具体界面如图4-7所示:

图4-7 客户信息界面

四,项目总结

5.1测试的目的

系统测试(System Testing),系统测试是把已经完成的硬件、软件、外设、网络等所有组成部分结合在一起,对整个系统进行单元测试和总体测试,通过与系统的需求做比较,找到所开发的系统与用户需求之间的差别,进而优化系统的手段。目前无论大企业还是中小企业,都再利用互联网进行信息的管理和分享,所以一个软件系统进行系统的测试和总结,是必要的和必需的,而且通过测试工作的进行,可以反映出最初没有考虑到的细节或流程,也有查漏补缺的功能,进而提高软件的质量。

5.2测试的方法

5.2.1 白盒测试

白盒测试又被称为:结构测试或者叫做逻辑驱动测试,这是一种需要基于代码的测试,白盒是一种形象的比喻,系统程序就像透明盒子一样,是可以看见的,也就是我们可以系统的各个模块是如何运行和调用的。比如:大部分公司首先会进行白盒测试,也就是按功能模块系统的对子模块进行系统的测试,包括运行的异常和文本域或模拟用户行为创建并不符合系统的输入,从而建立全面准确并具有说服力了的测试用例。虽然白盒测试有很多优点,但也有几个无法规避的问题,一个系统程序运行会有很多条类似于tree结构的不同路径,不可能测试所有的情况,也为程序不稳定埋下了伏笔。

5.2.2 黑盒测试

黑盒测试通常也成为数据驱动的测试方法,黑盒顾名思义就是,把系统程序看作看不见的黑盒子,完全不用考虑系统的流程,数据的流动,以及各个字段的类型。在完全不考虑程序本身的前提下,更多从用户使用情况或用户体验出发,测试每个模块是否可以正常稳定运行,按照程序需求文档,逐条验证是否可以输入数据而产生预期的正确结果,白盒测试着眼于内部,而黑盒子着眼于外部,当然这里有一个弊端,就是黑盒测试不能系统功能是否设计合理等细节问题。

5.2.3 灰盒测试

白盒测试和黑盒测试都有各自的优缺点,而灰盒测试介于黑盒测试和白盒测试之间,综合了各自的优点,有又规避了其中的缺点,不仅像白盒测试那样测试系统程序内部的功能逻辑,还会重点关注输入、输出的准确性。比如某个程序段运行时,通过外部的异常表现,转而进入程序内部,结合程序内部逻辑结构综合分析,这样就可以全面的诊断和测试系统程序。

5.2.4 静态测试

静态测试是比较独特,不像其他测试方法,需要运行被测试的系统程序,静态测试通过系统的分析程序结构,语法,接口规范,过程来检查程序的手段。

5.2.5 动态测试

动态测试是运行被测试的系统程序,分析程序运行的结果和预期达到的结果的差异,并总结出具体的程序性能指标,正确率等。

5.3测试用例

5.3.1 系统登录功能测试

系统的测试是甲方验收的重要的依据,通过输入所需信息查看是否和预期一样。是否可以完整成功运行整个系统。如表5-1所示

图5-1 测试用例

名称

功能

操作

期望结果

实际结果

测试状态

用户注册

注册管理员

输入用户名:wxs;

密码:12345678;

确认密码:12345678

真实名字:王先生

手机号:18201107931

注册成功

与期望相同

用户登录

管理员登录

用户名:wxs;

密码:12345678

登录成功

与期望相同

查询库存

库存列表

输入用户手机号:,查询它的商品快递

查询成功

与期望相同

商品入库

商品入库

收件人姓名:李先生;

收件人手机号:18435250911;

商品名称:小米手环S1

入库成功

与期望相同

商品出库

商品出库

输入库存id:6

出库成功

与期望相同

库存异常

查询异常库存

选择超时一年未取的选项

查询成功

与期望相同

客户信息

客户信息

输入用户姓名

查询成功

与期望相同

5.4测试总结

时间不知不觉程序已经测试完成,虽然功能总体来说比较简单,但是通过自己的双手一步一步的完成此论文,非常的激动。系统总体达到了总目标,编码结果和测试结果完成预期规划,此次测试从系统的各个环节进行了全面测试,基本达到系统独立应用的能力,基本功能已经实现。

通过测试,功能更加齐全和稳定,在测试中不断会有bug产生,通过不断的寻找解决方法,最终完成预期效果。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/155615
推荐阅读
相关标签
  

闽ICP备14008679号