基于SpringBoot的食堂窗口销售与库存管理平台 - 源码深度解析

JavaJavaScriptMavenHTMLCSSSSM框架MySQLSpringboot框架
2026-02-088 浏览

文章摘要

基于SpringBoot的食堂窗口销售与库存管理平台是一款针对高校、企业及机关单位食堂运营场景设计的业务管理系统。该平台旨在解决传统食堂管理中普遍存在的手工记录效率低下、库存数据不准确、销售与采购环节脱节等核心痛点。通过将窗口销售数据与后端库存变动实时联动,系统能够自动扣减库存并生成动态的销售统计报...

在现代团餐管理领域,高效精准的运营管控已成为提升食堂服务质量与经济效益的关键。传统食堂管理普遍面临手工记录效率低下、库存数据不准确、销售与采购环节脱节等痛点,亟需一套集成了销售、库存、采购等多模块的一体化解决方案。针对这一市场需求,我们设计并实现了基于SpringBoot的企业级智慧食堂运营管理平台,该系统通过自动化数据流和实时业务联动,为高校、企业及机关单位食堂提供全方位的数字化管理支持。

系统架构与技术栈

该平台采用经典的分层架构设计,以SpringBoot为核心框架,结合MySQL数据库和前端技术栈,构建了高内聚低耦合的企业级应用系统。

技术栈配置:

# 应用基础配置
debug: true
server:
  port: 8080
  servlet:
    context-path: /boot_stangsys
    session:
      timeout: -1

# 数据库连接池配置
spring:
  datasource:
    url: jdbc:mysql://www.csbishe.cn/boot_stangsys?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
    username: boot_stangsys
    password: boot_stangsys
    driver-class-name: com.mysql.cj.jdbc.Driver
    dbcp2:
      max-wait-millis: 10000
      min-idle: 5
      initial-size: 5
      validation-query: SELECT 1 From dual
  
  # JPA配置
  jpa:
    show-sql: true
  servlet:
    multipart:
      max-file-size: 100MB
      max-request-size: 100MB

# MyBatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.iuexam.entity

系统架构分为表现层、控制层、服务层、数据访问层四个主要层次。表现层采用JSP模板引擎渲染页面,控制层通过Spring MVC接收前端请求,服务层封装核心业务逻辑,数据访问层基于Spring Data JPA实现与数据库的交互。

数据库设计亮点

数据库设计充分考虑了食堂业务的特性和性能要求,通过合理的表结构设计和关系建模,确保了数据的一致性和查询效率。

产品信息表设计

CREATE TABLE `product` (
  `proid` int(25) NOT NULL AUTO_INCREMENT COMMENT '产品ID',
  `pname` varchar(25) NOT NULL COMMENT '产品名称',
  `price` double(25,3) NOT NULL COMMENT '销售价格',
  `inprice` double(25,3) DEFAULT NULL COMMENT '进货价格',
  `prodate` date DEFAULT NULL COMMENT '生产日期',
  `reledate` date DEFAULT NULL COMMENT '保质期',
  `supname` varchar(25) DEFAULT NULL COMMENT '供应商名称',
  `protype` varchar(25) DEFAULT NULL COMMENT '产品类型',
  `unit` varchar(25) DEFAULT NULL COMMENT '单位',
  `marks` varchar(25) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`proid`)
) ENGINE=InnoDB AUTO_INCREMENT=8556505 DEFAULT CHARSET=utf8 COMMENT='产品表'

设计亮点分析:

  • 主键设计:采用自增整数作为主键,既保证了唯一性又提高了插入性能
  • 精度控制:价格字段使用double(25,3)类型,确保金额计算的精确度
  • 业务字段完整性:包含生产日期、保质期等关键信息,支持食品溯源管理
  • 索引优化:虽然没有显式定义二级索引,但主键索引已能满足大部分查询需求

销售与库存联动设计

CREATE TABLE `sale` (
  `saleid` int(25) NOT NULL COMMENT '销售ID',
  `proid` int(25) NOT NULL COMMENT '产品ID',
  `pname` varchar(25) NOT NULL COMMENT '产品名称',
  `price` double(25,3) DEFAULT NULL COMMENT '单价',
  `num` int(25) DEFAULT NULL COMMENT '销售数量',
  `total` varchar(25) DEFAULT NULL COMMENT '总金额',
  `saledate` date DEFAULT NULL COMMENT '销售日期',
  `cusname` varchar(255) DEFAULT NULL COMMENT '客户名称',
  `cusid` varchar(25) DEFAULT NULL COMMENT '客户ID',
  `marks` varchar(25) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`saleid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='销售表'

CREATE TABLE `ckin` (
  `inid` int(7) NOT NULL AUTO_INCREMENT COMMENT '入库ID',
  `proid` int(7) NOT NULL COMMENT '产品ID',
  `pname` varchar(25) NOT NULL COMMENT '产品名称',
  `num` int(25) unsigned DEFAULT 100 COMMENT '入库数量',
  `indate` date DEFAULT NULL COMMENT '入库日期',
  `marks` varchar(25) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`inid`)
) ENGINE=InnoDB AUTO_INCREMENT=133 DEFAULT CHARSET=utf8 COMMENT='入库表'

业务关联设计:

  • 数据冗余优化:销售表中冗余存储产品名称,避免频繁的表连接查询
  • 数量约束:入库数量使用unsigned约束,防止负数输入
  • 日期管理:分别记录销售日期和入库日期,支持时间维度分析
  • 默认值设置:入库数量默认100,简化日常操作

库存管理界面

核心功能实现

1. 入库管理模块

入库管理模块负责处理食材和产品的入库操作,确保库存数据的准确性。系统通过事务管理保证入库操作的原子性。

实体类设计:

package com.iuexam.entity;

import java.util.Date;

public class Ckin {
    private String inid;
    private String proid;
    private String pname;
    private Integer num;
    private Date indate;
    private String marks;
    
    // Getter和Setter方法
    public String getInid() {
        return inid;
    }

    public void setInid(String inid) {
        this.inid = inid == null ? null : inid.trim();
    }

    public String getProid() {
        return proid;
    }

    public void setProid(String proid) {
        this.proid = proid == null ? null : proid.trim();
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname == null ? null : pname.trim();
    }

    public Integer getNum() {
        return num;
    }

    public void setNum(Integer num) {
        this.num = num;
    }

    public Date getIndate() {
        return indate;
    }

    public void setIndate(Date indate) {
        this.indate = indate;
    }

    public String getMarks() {
        return marks;
    }

    public void setMarks(String marks) {
        this.marks = marks == null ? null : marks.trim();
    }
}

控制器实现:

package com.iuexam.controller;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.iuexam.entity.Ckin;
import com.iuexam.service.imp.CkinServiceImp;

@Controller
@RequestMapping("/staff/flatform/ckin")
public class CkinController {
    
    @Autowired
    CkinServiceImp ckinServiceImp;

    // 获取所有入库信息(分页查询)
    @RequestMapping("getall")
    public String getlist(ModelMap model,
            @RequestParam(defaultValue="1",required=true,value="pn") Integer pn) {
        PageHelper.startPage(pn, 4);
        List<Ckin> ckin = ckinServiceImp.getall();
        PageInfo<Ckin> pageInfo = new PageInfo<Ckin>(ckin);
        model.addAttribute("pageInfo", pageInfo);
        return "getall_ckin";
    }

    // 根据ID查询单个入库信息
    @RequestMapping("/getckin")  
    public String getbyid(String inid, HttpServletRequest request, Model model){  
        request.setAttribute("ckin", ckinServiceImp.getbyid(inid));
        model.addAttribute("ckin", ckinServiceImp.getbyid(inid));  
        return "getckin";  
    }

    // 编辑入库信息
    @RequestMapping("edit")
    public String edit(Ckin ckin, HttpServletRequest request, Model model){
        model.addAttribute("ckin", ckinServiceImp.getbyid(ckin.getInid()));
        return "editckin";
    }

    // 更新入库信息
    @RequestMapping("update")
    public String update(Ckin ckin, HttpServletRequest request, Model model){  
        if(ckinServiceImp.update(ckin)) {
            ckin = ckinServiceImp.getbyid(ckin.getInid());
            model.addAttribute("ckin", ckin);
            return "redirect:getall"; 
        }
        return null;
    }

    // 删除入库记录
    @RequestMapping("/delete")  
    public String deletete(String inid, HttpServletRequest request, Model model){  
        ckinServiceImp.delete(inid);
        return "redirect:getall";  
    }

    // 跳转到新增页面
    @RequestMapping("/toadd")  
    public String toadd() {  
        return "addckin";
    } 

    // 新增入库记录(智能判断新增或更新)
    @RequestMapping("/insert")  
    public String insert(Ckin ckin, HttpServletRequest request, Model model){  
        if(null == ckinServiceImp.getbyid(ckin.getInid())) {
            ckinServiceImp.insert(ckin);    		
        } else {
            ckinServiceImp.update(ckin);
        }
        return "redirect:getall";
    } 

    // 日期格式绑定
    @InitBinder
    protected void init(HttpServletRequest request, ServletRequestDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }
}

采购信息管理

2. 销售与库存联动机制

系统实现了销售数据与库存变动的实时联动,确保库存数据的准确性和一致性。

销售业务逻辑实现:

// 销售服务层核心逻辑
@Service
public class SaleService {
    
    @Autowired
    private SaleRepository saleRepository;
    
    @Autowired
    private ProductRepository productRepository;
    
    @Autowired
    private InventoryRepository inventoryRepository;
    
    @Transactional
    public SaleRecord createSale(SaleRequest request) {
        // 1. 验证产品库存
        Product product = productRepository.findById(request.getProid());
        Inventory inventory = inventoryRepository.findByProductId(request.getProid());
        
        if (inventory.getCurrentStock() < request.getQuantity()) {
            throw new InsufficientStockException("库存不足");
        }
        
        // 2. 创建销售记录
        SaleRecord sale = new SaleRecord();
        sale.setProid(request.getProid());
        sale.setPname(product.getPname());
        sale.setPrice(product.getPrice());
        sale.setNum(request.getQuantity());
        sale.setTotal(calculateTotal(product.getPrice(), request.getQuantity()));
        sale.setSaledate(new Date());
        sale.setCusname(request.getCustomerName());
        
        SaleRecord savedSale = saleRepository.save(sale);
        
        // 3. 更新库存
        inventory.setCurrentStock(inventory.getCurrentStock() - request.getQuantity());
        inventoryRepository.save(inventory);
        
        // 4. 记录库存变动日志
        inventoryLogRepository.save(createInventoryLog(inventory, savedSale));
        
        return savedSale;
    }
    
    private double calculateTotal(double price, int quantity) {
        return price * quantity;
    }
}

3. 多角色权限管理

系统支持管理员和普通员工两种角色,不同角色拥有不同的操作权限。

管理员表结构:

CREATE TABLE `manager` (
  `managerid` varchar(25) NOT NULL COMMENT '管理员ID',
  `managername` varchar(25) NOT NULL COMMENT '管理员姓名',
  `card` varchar(25) NOT NULL COMMENT '身份证号',
  `sex` varchar(25) DEFAULT NULL COMMENT '性别',
  `tel` varchar(25) DEFAULT NULL COMMENT '电话',
  `stafftype` varchar(25) NOT NULL COMMENT '员工类型',
  `pwd` varchar(25) NOT NULL COMMENT '密码',
  PRIMARY KEY (`managerid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='管理员表'

权限控制实现:

// 基于角色的访问控制
@Component
public class RoleBasedAccessControl {
    
    private static final Map<String, List<String>> ROLE_PERMISSIONS = new HashMap<>();
    
    static {
        // 管理员权限
        ROLE_PERMISSIONS.put("admin", Arrays.asList(
            "product:create", "product:update", "product:delete",
            "sale:view", "sale:export", "inventory:manage",
            "employee:manage", "report:generate"
        ));
        
        // 普通员工权限
        ROLE_PERMISSIONS.put("staff", Arrays.asList(
            "sale:create", "sale:view", "inventory:view"
        ));
    }
    
    public boolean hasPermission(String role, String permission) {
        return ROLE_PERMISSIONS.getOrDefault(role, Collections.emptyList())
                .contains(permission);
    }
}

管理员登录界面

实体模型设计

系统采用面向对象的设计思想,通过实体类映射数据库表结构,实现了数据访问的面向对象化。

产品实体类扩展设计:

@Entity
@Table(name = "product")
public class Product {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "proid")
    private Integer productId;
    
    @Column(name = "pname", nullable = false, length = 25)
    private String productName;
    
    @Column(name = "price", nullable = false, precision = 25, scale = 3)
    private Double price;
    
    @Column(name = "inprice", precision = 25, scale = 3)
    private Double purchasePrice;
    
    @Temporal(TemporalType.DATE)
    @Column(name = "prodate")
    private Date productionDate;
    
    @Temporal(TemporalType.DATE)
    @Column(name = "reledate")
    private Date expirationDate;
    
    @Column(name = "supname", length = 25)
    private String supplierName;
    
    @Column(name = "protype", length = 25)
    private String productType;
    
    @Column(name = "unit", length = 25)
    private String unit;
    
    @Column(name = "marks", length = 25)
    private String remarks;
    
    // 关联关系定义
    @OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
    private List<Sale> sales = new ArrayList<>();
    
    @OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
    private List<Ckin> stockIns = new ArrayList<>();
    
    // 业务方法
    public boolean isExpired() {
        return expirationDate != null && expirationDate.before(new Date());
    }
    
    public boolean isExpiringSoon() {
        if (expirationDate == null) return false;
        
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, 7);
        return expirationDate.before(calendar.getTime());
    }
    
    // Getter和Setter方法
    // ... 省略详细实现
}

功能展望与优化

基于当前系统架构和业务需求,以下是可以进一步优化的方向:

1. 引入Redis缓存提升性能

实现思路: 将热点数据如产品信息、库存数据缓存到Redis中,减少数据库访问压力。

// 缓存配置示例
@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()));
        
        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .build();
    }
}

// 缓存使用示例
@Service
public class ProductService {
    
    @Cacheable(value = "products", key = "#productId")
    public Product getProductById(Integer productId) {
        return productRepository.findById(productId);
    }
    
    @CacheEvict(value = "products", key = "#product.productId")
    public Product updateProduct(Product product) {
        return productRepository.save(product);
    }
}

2. 消息队列实现异步处理

应用场景: 销售记录生成、库存更新、报表统计等耗时操作可以异步化处理。

// 消息队列配置
@Configuration
public class RabbitMQConfig {
    
    @Bean
    public Queue inventoryQueue() {
        return new Queue("inventory.update", true);
    }
    
    @Bean
    public Queue reportQueue() {
        return new Queue("report.generate", true);
    }
}

// 异步消息处理
@Service
public class SaleAsyncService
本文关键词
SpringBoot食堂管理销售管理库存管理系统架构

上下篇

上一篇
没有更多文章
下一篇
没有更多文章