Bootstrap

餐厅点餐系统(go语言 + 控制台程序)

DineGoSystem餐厅点餐系统(控制台版本)

描述

简单模拟一个餐厅的点餐过程,用户可以查看菜品,加入购物车,模拟支付,查看订单等。

功能模块

  1. 点餐
  2. 查看购物车
  3. 订单记录

技术说明

使用 golang 语言开发,后台使用 gorm 框架连接数据库,使用 MVC 三层实现的一个控制台的小程序。
由于所涉及的文件太多,这里就只展示连接数据库Dao层、业务逻辑层Service、handler 的实现。

1. 点餐

菜品只做了查询操作,数据从数据库中获取。

Dao层

// QueryMenu
// @LastUpdateTime 2024/3/12 22:06:00
// @Description 查询菜品列表
// @Demand Version V1.0
func (dao *MenuDao) QueryMenu(menus *[]model.Menu) error {
	return model.DB.Where("del_flag = 0").
		Find(&menus).Error
}

Service层

package service

import (
	"dine_go_system/dao"
	"dine_go_system/model"
)

type MenuService struct {
	menuDao *dao.MenuDao
}

// NewMenuService
// @LastUpdateTime 2024/3/12 22:08:00
// @Description 构造函数
// @Demand Version V1.0
func NewMenuService() *MenuService {
	return &MenuService{menuDao: &dao.MenuDao{}}
}

// QueryMenu
// @LastUpdateTime 2024/3/12 22:11:00
// @Description 查询菜品列表数据集
// @Demand Version V1.0
func (s *MenuService) QueryMenu() []model.Menu {
	var menus []model.Menu
	// 查询菜品列表数据集
	if err := s.menuDao.QueryMenu(&menus); err != nil {
		return nil
	}
	return menus
}

handler

package handler

import (
	"dine_go_system/model"
	"dine_go_system/service"
	"dine_go_system/tool"
	"fmt"
	"strconv"
	"time"
)

type MenuHandler struct {
	menuService  *service.MenuService
	orderService *service.OrderService
}

// NewMenuHandler
// @LastUpdateTime 2024/3/13 22:08:00
// @Description 构造函数
// @Demand Version V1.0
func NewMenuHandler() *MenuHandler {
	return &MenuHandler{
		menuService:  service.NewMenuService(),
		orderService: service.NewOrderService(),
	}
}

// DisplayMenu
// @LastUpdateTime 2024/3/13 22:09:00
// @Description 展示菜单列表
// @Demand Version V1.0
func (h *MenuHandler) DisplayMenu() {
	// 清空控制台
	tool.ClearConsole()
	// 获取菜品数据
	menus := h.menuService.QueryMenu()
	if len(menus) > 0 {
		// 标题
		fmt.Println("╔═════════════════════════════════════════════════╗")
		fmt.Printf(" ")
		fmt.Printf(" %s%-4s%-8s%-5s%-8s%-9s%s\n", model.Green, "编号", "菜名", "数量", "价格(元)", "描述", model.Reset)
		// 输出数据
		for _, item := range menus {
			fmt.Printf(" ")
			fmt.Printf(" %-6s%-7s%-7s%-9s%-20s\n", strconv.Itoa(item.MenuId), item.MenuName, strconv.Itoa(item.Quantity), "¥"+strconv.FormatFloat(item.Price, 'f', 2, 32), item.Desc)
		}
		fmt.Println("╚═════════════════════════════════════════════════╝")
	}
	// 选择菜品
	var orderDts []model.OrderDts
	for {
		// 获取控制台输入
		var menuChoice int
		fmt.Printf("请输入菜品编号(0 结束点餐):")
		fmt.Scanln(&menuChoice)
		// 如果是 0 直接退出
		if menuChoice == 0 {
			if len(orderDts) > 0 {
				_, msg := h.orderService.SaveOrder(orderDts)
				fmt.Println(msg)
			}
			break
		}
		// 检查是否找到了菜品
		var findMenu model.Menu
		var found bool
		for _, menuItem := range menus {
			if menuItem.MenuId == menuChoice {
				findMenu = menuItem
				found = true
				break
			}
		}
		if !found {
			fmt.Println("您选择的菜品不存在!")
		}
		// 选择数量
		var quantityInput int
		fmt.Printf("请输入数量:")
		fmt.Scanln(&quantityInput)
		if quantityInput > findMenu.Quantity {
			fmt.Println("数量不可大于【" + strconv.Itoa(findMenu.Quantity) + "】!")
			continue
		}
		fmt.Println("您选择的菜品是【" + model.Green + findMenu.MenuName + model.Reset + "】,单价为【" + model.Green + strconv.FormatFloat(findMenu.Price, 'f', 2, 32) + model.Reset +
			"】元,数量为【" + model.Green + strconv.Itoa(quantityInput) + model.Reset +
			"】,合计价格为【" + model.Green + strconv.FormatFloat(findMenu.Price*float64(quantityInput), 'f', 2, 32) + model.Reset + "】元")
		// 组装明细数据
		orderDts = append(orderDts, model.OrderDts{
			MenuId:   findMenu.MenuId,
			Quantity: quantityInput,
			Price:    findMenu.Price,
			CreateAt: time.Now(),
		})
	}
}

2. 查看购物车和查看订单

  1. 第一步点餐,已经实现将菜品放到购物车了,这一步就一起说一下这一部分,查看购物车和查看订单我采用的方法大致一样,都是查询的 Order 和 OrderDts 这两个表。
  2. 其中我将 Order 中的状态 Status 为 0 的定义为购物车里面的数据,也就是未支付的。
  3. 然后 Status 为 1 的代表已支付,就作为订单部分展示。

    Dao层
package dao

import "dine_go_system/model"

type OrderDao struct {
}

// SaveOrder
// @LastUpdateTime 2024/3/14 21:08:00
// @Description 保存订单(加入购物车)
// @Demand Version V1.0
func (dao *OrderDao) SaveOrder(order model.Order, orderDts []model.OrderDts) error {
	// 开启事务
	tx := model.DB.Begin()
	// 保存订单主表
	if err := tx.Create(&order).Error; err != nil {
		tx.Rollback()
		return err
	}
	// 订单明细表,给每个订单明细的 OrderId字段赋值为订单的Id
	for i := range orderDts {
		orderDts[i].OrderId = order.OrderId
	}
	// 保存到数据库
	if err := tx.Create(&orderDts).Error; err != nil {
		tx.Rollback()
		return err
	}
	// 提交事务
	tx.Commit()
	return nil
}

// QueryOrder
// @LastUpdateTime 2024/3/15 11:44:00
// @Description 查询订单
// @Demand Version V1.0
func (dao *OrderDao) QueryOrder(paidStatus int, results *[]model.OrderQueryResult) error {
	return model.DB.Raw("CALL dine_query_orders(?)", paidStatus).Scan(&results).Error
}

// ChangePaidAmount
// @LastUpdateTime 2024/3/17 17:25:00
// @Description 模拟付款
// @Demand Version V1.0
func (dao *OrderDao) ChangePaidAmount(orderId int, paidAmount float64) error {
	return model.DB.Model(&model.Order{}).Where("order_id = ?", orderId).Updates(model.Order{PaidAmount: paidAmount, Status: 1}).Error
}

Service层

package service

import (
	"dine_go_system/dao"
	"dine_go_system/model"
	"dine_go_system/tool"
	"time"
)

type OrderService struct {
	orderDao *dao.OrderDao
}

// NewOrderService
// @LastUpdateTime 2024/3/14 21:35:00
// @Description 构造函数
// @Demand Version V1.0
func NewOrderService() *OrderService {
	return &OrderService{orderDao: &dao.OrderDao{}}
}

// SaveOrder
// @LastUpdateTime 2024/3/14 22:37:00
// @Description 保存订单成功
// @Demand Version V1.0
func (s *OrderService) SaveOrder(dts []model.OrderDts) (bool, string) {
	var order model.Order
	// 主表
	order.OrderCode = tool.GenerateOrderCode()
	var totalAmount float64
	for _, item := range dts {
		totalAmount += item.Price * float64(item.Quantity)
	}
	order.DueAmount = totalAmount
	order.Status = 0
	order.CreateAt = time.Now()
	if err := s.orderDao.SaveOrder(order, dts); err != nil {
		return false, err.Error()
	}
	return true, "加入购物车成功"
}

// QueryOrder
// @LastUpdateTime 2024/3/15 13:55:00
// @Description 查询订单
// @Demand Version V1.0
func (s *OrderService) QueryOrder(paidStatus int) []model.OrderQueryResult {
	var orderResults []model.OrderQueryResult
	// 查询购物车
	if err := s.orderDao.QueryOrder(paidStatus, &orderResults); err != nil {
		return nil
	}
	return orderResults
}

// ChangePaidAmount
// @LastUpdateTime 2024/3/17 17:28:00
// @Description 模拟付款
// @Demand Version V1.0
func (s *OrderService) ChangePaidAmount(orderId int, paidAmount float64) (bool, string) {
	if orderId == 0 {
		return false, "订单不存在"
	}
	if paidAmount == 0 {
		return false, "付款金额为0"
	}
	if err := s.orderDao.ChangePaidAmount(orderId, paidAmount); err != nil {
		return false, "付款失败,错误信息:" + err.Error()
	}
	return true, "付款成功"
}

Handler

package handler

import (
	"dine_go_system/model"
	"dine_go_system/service"
	"dine_go_system/tool"
	"fmt"
	"strconv"
)

type OrderHandler struct {
	orderService *service.OrderService
}

// NewOrderHandler
// @LastUpdateTime 2024/3/15 14:17:00
// @Description 构造函数
// @Demand Version V1.0
func NewOrderHandler() *OrderHandler {
	return &OrderHandler{
		orderService: service.NewOrderService(),
	}
}

// DisplayCart
// @LastUpdateTime 2024/3/15 15:15:00
// @Description 查看购物车
// @Demand Version V1.0
func (h *OrderHandler) DisplayCart() {
	// 清空控制台
	tool.ClearConsole()
	// 获取购物车数据
	orderResults := h.orderService.QueryOrder(0)
	if len(orderResults) > 0 {
		orderDueAmount := orderResults[0].DueAmount
		fmt.Println("--------------------------------")
		fmt.Println(" 时    间:" + orderResults[0].CreateAt.Format("2006-01-02 15:04:05"))
		fmt.Println(" 订 单 号:" + orderResults[0].OrderCode)
		fmt.Println(" 应付金额:" + model.Green + strconv.FormatFloat(orderDueAmount, 'f', 2, 32) + model.Reset)
		fmt.Println(" 支付状态:" + model.Red + model.StatusMap[orderResults[0].Status] + model.Reset)
		fmt.Println("--------------------------------")
		// 展示订单明细
		fmt.Printf(" %s%-8s%-5s%-8s%s\n", model.Green, "菜名", "数量", "价格(元)", model.Reset)
		// 输出数据
		for _, item := range orderResults {
			fmt.Printf(" %-7s%-7s%-9s\n", item.MenuName, strconv.Itoa(item.Quantity), "¥"+strconv.FormatFloat(item.Price, 'f', 2, 32))
		}
		fmt.Println("--------------------------------")
		var amountInput float64
		for {
			fmt.Println("请输入应付款,(0 无付款退出):")
			fmt.Scanln(&amountInput)
			// 退出
			if amountInput == 0 || amountInput == orderDueAmount {
				break
			}
			if amountInput != orderDueAmount {
				fmt.Println("您输入的金额与应付款不一致,请重新输入")
			}
		}
		// 付款
		if amountInput != 0 && amountInput == orderDueAmount {
			isSuccess, msg := h.orderService.ChangePaidAmount(orderResults[0].OrderId, amountInput)
			if !isSuccess {
				fmt.Println(model.Red + msg + model.Reset)
			}
			fmt.Println(model.Green + msg + model.Reset)
		}
	} else {
		fmt.Println("没有更多了!!")
	}
}

// DisplayOrder
// @LastUpdateTime 2024/3/17 17:42:00
// @Description 展示订单
// @Demand Version V1.0
func (h *OrderHandler) DisplayOrder() {
	// 清空控制台
	tool.ClearConsole()
	// 获取订单数据
	orderResults := h.orderService.QueryOrder(1)
	// 去重
	mergedOrders := make(map[string]model.OrderQueryResult)
	for _, order := range orderResults {
		mergedOrders[order.OrderCode] = order
	}
	if len(mergedOrders) > 0 {
		fmt.Println("╔═════════════════════════════════════════════════╗")
		fmt.Printf(" %s%-18s%-8s%-5s%s\n", model.Green, "单号", "应付金额", "实付金额", model.Reset)
		// 输出数据
		for _, item := range mergedOrders {
			fmt.Printf(" %-20s%-11s%-7s\n", item.OrderCode, "¥"+strconv.FormatFloat(item.DueAmount, 'f', 2, 32), "¥"+strconv.FormatFloat(item.PaidAmount, 'f', 2, 32))
		}
		fmt.Println("╚═════════════════════════════════════════════════╝")
	}
	fmt.Println("没有更多订单了!!")
}

最后来个展示效果图吧

菜单列表
菜单
1. 点餐
点餐
2. 查看购物车
查看购物车
查看订单
查看订单

补充

对了,再附一下关于控制台字体颜色的输出。
这个是用了 ANSI 颜色码,我的是 window 系统,别的系统可能展示的颜色不同。
我先定义了几个颜色码:

// ANSI颜色码
const (
	Reset = "\033[0m"
	Green = "\033[32m"
	Red   = "\033[31m"
)

然后在 fmt.Println 中使用就就可以了

fmt.Println(model.Green + "你好" + model.Reset)

后面需要再加上这个 (\033[0m) 也就是上面的 model.Reset 是为了把颜色恢复到之前,不然到时候就会显示的整个控制台都是绿色了。

最后,代码写的肯定是不够好的,欢迎大家指正!!!!!!!

;