数据库设计——医药销售管理系统

泪湿孤枕 提交于 2019-12-01 02:11:47

开发环境和开发工具

操作系统:win8.1
开发环境:Mysql、Web
开发工具:Workbench、Eclipse、JDBC

功能需求分析

  • 员工有权查看、添加会员,查看、添加供应商,查询药品(输入药品编号或名称、类别等查询该药品或该类药品库存),添加药品采购记录,销售药品,处理退货,盘点仓库,查看销售、退货、入库记录,修改个人信息
  • 经理有权查看、添加、删除会员,查看、添加、删除供应商,查看、添加、删除员工,盘点仓库,查看销售、退货、入库记录,修改个人信息,无权进行销售和退货业务
  • 供应商和顾客对此系统没有使用权限 系统设计

这里写图片描述

系统设计

  • 数据流
    这里写图片描述
  • E-R图
    这里写图片描述
  • 数据库关系模式设计

    登录用户(用户编号,用户名,密码,类别)
    员工(员工编号,员工姓名,联系电话,用户编号)
    经理(经理编号,用户编号)
    财政收支(收支编号,药品编号,员工编号,数量,日期,总额,类型)
    供应商(供应商编号,供应商名称,联系人,联系方式,所在城市)
    会员(客户编号,客户姓名,联系方式)
    入库记录(入库记录编号,供应商编号,收支编号)
    退货管理(退货编号,销售编号,收支编号)
    销售管理(销售编号,客户编号,收支编号)
    药品(药品编号,药品名称,供应商编号,生产批号,产地,所属类别,进价,单价,会员折扣,库存,包装规格,生产日期,有效期)

  • 数据库物理结构设计

    本次项目使用的引擎是InnoDB,MySQL的数据库引擎之一。InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。InnoDB存储它的表&索引在一个表空间中,表空间可以包含数个文件(或原始磁盘分区)。这与MyISAM表不同,比如在MyISAM表中每个表被存在分离的文件中。InnoDB 表可以是任何尺寸,即使在文件尺寸被限制为2GB的操作系统上。InnoDB默认地被包含在MySQL二进制分发中。Windows Essentials installer使InnoDB成为Windows上MySQL的默认表。

    此外还使用了数据库索引,索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。各表索引如下:
    会员:primary key(客户编号)
    药品:primary key(药品编号),
    INDEX 供应商编号_idx (供应商编号 ASC),
    供应商:primary key(供应商编号)
    登录用户:primary key(用户编号)
    用户名 unique,
    经理:primary key(经理编号),
    INDEX 经理编号_idx (用户编号 ASC),
    员工:primary key(员工编号),
    INDEX 员工编号_idx (用户编号 ASC),
    财政收支:PRIMARY KEY (收支编号),
    INDEX 药品编号_idx (药品编号 ASC),
    INDEX 员工编号_idx (员工编号 ASC),
    入库记录:primary key(入库记录编号),
    INDEX 供应商编号_idx (供应商编号 ASC),
    INDEX 收支编号_idx (收支编号 ASC),
    销售管理:PRIMARY KEY (销售编号),
    INDEX 客户编号_idx (客户编号 ASC),
    INDEX 收支编号_idx (收支编号 ASC),
    退货管理:PRIMARY KEY (退货编号),
    INDEX 销售编号_idx (销售编号 ASC),
    INDEX 收支编号_idx (收支编号 ASC),

系统功能的实现

  • 建表

    drop database if exists 医药销售管理系统;
    create database 医药销售管理系统;
    use 医药销售管理系统;
    //建立表 会员
    create table `会员`(
        客户编号      int auto_increment,
        客户姓名      varchar(50),
        联系方式      varchar(100),
    primary key(客户编号));
    //建立表 供应商
    create table `供应商`(
        供应商编号     int auto_increment,
        供应商名称     varchar(50),
        联系人         varchar(50),
        联系方式       varchar(50),
        所在城市       varchar(50),
    primary key(供应商编号));
    //建立表 药品
    create table `药品`(
        药品编号    int auto_increment,
        药品名称    varchar(50) not null,
        供应商编号  int not null,
        生产批号    varchar(100),
        产地        varchar(50),
        所属类别    varchar(50),
        进价        decimal(10,2) not null,
        单价        decimal(10,2) not null,
        会员折扣    decimal(3,2),
        库存        int not null,
        包装规格    varchar(50),
        生产日期    varchar(50),
        有效期      varchar(50),
    primary key(药品编号),
    INDEX `供应商编号_idx` (`供应商编号` ASC),
    CONSTRAINT `供应商编号`
    FOREIGN KEY (`供应商编号`)
    REFERENCES `医药销售管理系统`.`供应商` (`供应商编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE);
    //建立表登录用户,可以登录医药销售管理系统,类别为1代表员工,2代表经理
    create table `登录用户`(
        用户编号         int auto_increment,
        用户名          varchar(40) not null unique,
        密码           varchar(40) not null,
        类别             int not null,
    primary key(用户编号));
    //建立表经理,具有登录用户编号,可登录系统
    create table `经理`(
        经理编号      int auto_increment,
        用户编号      int not null,
    primary key(经理编号),
    INDEX `经理编号_idx` (`用户编号` ASC),
    CONSTRAINT `经理登陆编号`
    FOREIGN KEY (`用户编号`)
    REFERENCES `医药销售管理系统`.`登录用户` (`用户编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE);
    //建立表员工,具有登录用户编号,可登录系统
    create table `员工`(
        员工编号      int auto_increment,
        员工姓名      varchar(50),
        联系电话      varchar(100),
        用户编号      int not null,
    primary key(员工编号),
    INDEX `员工编号_idx` (`用户编号` ASC),
    CONSTRAINT `员工登陆编号`
    FOREIGN KEY (`用户编号`)
    REFERENCES `医药销售管理系统`.`登录用户` (`用户编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE);
    //建立表财政开支,涉及药品编号、负责员工、数量、金额等信息,类型有入库、销售、退货等
    CREATE TABLE `财政收支` (
    `收支编号` int auto_increment,
    `药品编号` int,
    `员工编号` int not null,
    `数量` int,
    `日期` datetime NOT NULL,
    `总额` decimal(10,2) NOT NULL,
    `类型` VARCHAR(20)  NOT NULL,
    PRIMARY KEY (`收支编号`),
    INDEX `药品编号_idx` (`药品编号` ASC),
    CONSTRAINT `药品编号`
    FOREIGN KEY (`药品编号`)
    REFERENCES `医药销售管理系统`.`药品` (`药品编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    INDEX `员工编号_idx` (`员工编号` ASC),
    CONSTRAINT `负责员工编号`
    FOREIGN KEY (`员工编号`)
    REFERENCES `医药销售管理系统`.`员工` (`员工编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE);
    //建立表入库记录,包含供应商、收支编号
    create table `入库记录`(
        入库记录编号   int auto_increment,
        供应商编号     int not null,
        收支编号       int not null,
    primary key(入库记录编号),
    INDEX `供应商编号_idx` (`供应商编号` ASC),
    INDEX `收支编号_idx` (`收支编号` ASC),
    CONSTRAINT `入货供应商编号`
    FOREIGN KEY (`供应商编号`)
    REFERENCES `医药销售管理系统`.`供应商` (`供应商编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    CONSTRAINT `入库收支编号`
    FOREIGN KEY (`收支编号`)
    REFERENCES `医药销售管理系统`.`财政收支` (`收支编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE);
    //建立表销售管理,包含客户、收支编号
    CREATE TABLE `销售管理`(
    `销售编号` int auto_increment,
    `客户编号` int,
    `收支编号` int not null,
    PRIMARY KEY (`销售编号`),
    INDEX `客户编号_idx` (`客户编号` ASC),
    INDEX `收支编号_idx` (`收支编号` ASC),
    CONSTRAINT `销售客户编号`
    FOREIGN KEY (`客户编号`)
    REFERENCES `医药销售管理系统`.`会员` (`客户编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    CONSTRAINT `销售收支编号`
    FOREIGN KEY (`收支编号`)
    REFERENCES `医药销售管理系统`.`财政收支` (`收支编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE);
    //建立表退货管理,包含销售、收支编号
    CREATE TABLE `退货管理` (
    `退货编号` int auto_increment,
    `销售编号` int NOT NULL,
    `收支编号` int NOT NULL,
    PRIMARY KEY (`退货编号`),
    INDEX `销售编号_idx` (`销售编号` ASC),
    INDEX `收支编号_idx` (`收支编号` ASC),
    CONSTRAINT `退货销售编号`
    FOREIGN KEY (`销售编号`)
    REFERENCES `医药销售管理系统`.`销售管理` (`销售编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    CONSTRAINT `退货收支编号`
    FOREIGN KEY (`收支编号`)
    REFERENCES `医药销售管理系统`.`财政收支` (`收支编号`)
    ON DELETE CASCADE
    ON UPDATE CASCADE);
  • 创建视图方便查询

    
    /*仓库*/
    drop view if exists warehouse; 
    create view warehouse as
    select 药品编号,药品名称,供应商名称,生产批号,产地,所属类别,进价,单价,会员折扣,库存,包装规格,生产日期,有效期
    from 药品,供应商 where 药品.供应商编号=供应商.供应商编号; 
    /*入库记录*/
    drop view if exists InDrug_records; 
    create view InDrug_records as
    select 入库记录编号,药品名称,供应商名称,员工姓名,数量,日期,总额 
    from 入库记录,供应商,财政收支,员工,药品
    where 入库记录.供应商编号=供应商.供应商编号 and 入库记录.收支编号=财政收支.收支编号 and
    财政收支.员工编号=员工.员工编号 and 财政收支.药品编号=药品.药品编号;
    /*销售记录*/
    drop view if exists sales_records; 
    create view sales_records as
    select 销售编号,药品.药品编号,药品名称,单价,会员折扣,库存,客户姓名,员工姓名,数量,日期,总额
    from 销售管理,会员,财政收支,员工,药品
    where 销售管理.客户编号=会员.客户编号 and 销售管理.收支编号=财政收支.收支编号
    and 财政收支.员工编号=员工.员工编号 and 财政收支.药品编号=药品.药品编号;
    /*退货记录*/
    drop view if exists reject_records;
    create view reject_records as
    select 退货编号,退货管理.销售编号,药品名称,客户姓名,员工姓名,数量,日期,总额
    from 退货管理,销售管理,会员,财政收支,员工,药品
    where 退货管理.销售编号=销售管理.销售编号 and 销售管理.客户编号=会员.客户编号 
    and 退货管理.收支编号=财政收支.收支编号 and 财政收支.员工编号=员工.员工编号 
    and 财政收支.药品编号=药品.药品编号;
    /*员工信息*/
    drop view if exists employee_info;
    create view employee_info as
    select 员工编号,员工姓名,联系电话,员工.用户编号,用户名 from `员工` natural join `登录用户`;
    /*收支记录*/
    drop view if exists financial_records;
    create view financial_records as
    select 类型,药品名称,数量,日期,总额 
    from `财政收支` left join `药品` on `财政收支`.药品编号=`药品`.`药品编号`;
  • 使用过程实现“带参数的视图“

    drop procedure if exists payments_statistics;
    delimiter //
    create procedure payments_statistics(in date_limit varchar(20))
    begin
        select count(*) as 数目,sum(`总额`) as 盈亏, 
        (select sum(`总额`) from `财政收支` where `总额` < 0 and `日期` like date_limit) as '支出', 
        (select sum(`总额`) from `财政收支` where `总额` >= 0 and `日期` like date_limit) as '收入' 
        from `财政收支` where `日期` like date_limit;
    end//

    JDBC调用方法如下

                //调用过程  -- 统计盈亏、收入、支出
                CallableStatement cStmt = con.prepareCall("{call payments_statistics(?)}");
                cStmt.setString(1,"%"+date+"%");
                cStmt.execute();
                rs = cStmt.getResultSet();
                if(rs.next()){
                    count = rs.getInt(1);
                    all = rs.getString(2);
                    allOut = rs.getString(3);
                    allIn = rs.getString(4);
                }
                rs.close();
  • 建立触发器保证逻辑正确

    
    drop trigger if exists stock_update;
    delimiter //
    create trigger stock_update before update on 药品
    for each row
    begin
        if new.库存 < 0
        /* MySQL不支持直接使用rollback回滚事务,可以利用delete当前表造成异常使事务回滚 */
        then delete from 药品 where 药品编号=new.药品编号;  
        end if;
    end //
    update 药品 set 库存=-2 where 药品编号=1;
    
    drop trigger if exists drugs_insert;
    delimiter //
    create trigger drugs_insert before insert on 药品
    for each row
    begin
        if new.库存 < 0 or new.单价 < 0 or new.进价 < 0 or new.会员折扣 > 1 or new.会员折扣 < 0
        then delete from 药品 where 药品编号=new.药品编号;
        end if;
    end //
    /*退货数量不应比售出的多*/
    drop trigger if exists refunds_insert;
    delimiter //
    create trigger refunds_insert after insert on 退货管理
    for each row
    begin
        if (select 数量 from sales_records where 销售编号 = new.销售编号) < (select 数量 from reject_records where 退货编号 = new.退货编号)
        then delete from 退货管理 where 退货编号=new.退货编号;
        end if;
    end //
  • 将业务逻辑封装为事务,如销售事务

    PreparedStatement ps=null;
    Connection con = DriverManager.getConnection(connectString,"root", "2333");
    ...
            con.setAutoCommit(false);//设置自动提交为false
            ...
            //销售事务 
            //更新库存
            String fmt1="update 药品  set `库存`='%d' where `药品编号`='%s'";
            String sql1 = String.format(fmt1,drug_rest-pcount,drug_id); 
            ps = con.prepareStatement(sql1);
            ps.executeUpdate();
            //插入财政收支记录
            String fmt2="INSERT INTO `财政收支` (`药品编号`,`员工编号`,`数量`,`日期`,`总额`,`类型`) VALUES ('%s','%d','%d','%s','%s','销售')";
            String sql2 = String.format(fmt2,drug_id,employee_id,pcount,today,money); 
            ps = con.prepareStatement(sql2);
            ps.executeUpdate(); 
            //获取插入的财政收支编号
            ps = con.prepareStatement("select @@identity;");
            rs = ps.executeQuery();
            if(rs.next()){
                financial_id=rs.getInt(1);
            }
            rs.close();
            //插入销售记录
            String fmt3="insert into 销售管理(客户编号,收支编号) values(%s,'%d')";
            String sql3 = String.format(fmt3,customer_id,financial_id); 
            ps = con.prepareStatement(sql3);
            ps.executeUpdate();
            //提交事务
            con.commit();
      }catch(Exception e){
         try {
             con.rollback();
         } catch (Exception e1) {
             e1.printStackTrace();
         }

界面效果

登陆
这里写图片描述
主页
这里写图片描述
仓库
这里写图片描述
点击新增进行药品入库
这里写图片描述
入库/销售/退货记录
这里写图片描述
员工可查看/增加客户,供应商
这里写图片描述
经理可增删员工、客户、供应商
这里写图片描述
销售药品
这里写图片描述
这里写图片描述
退货
这里写图片描述
财务统计
这里写图片描述

(项目见https://github.com/14353350/Drug_Sales_Management

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!