暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

用 Python 与 KWDB 打造智能自动售卖机:从搭建到实践

KaiwuDB 2025-09-29
282
 

 

 


 


 

本篇内容出自「KWDB 2025 创作者计划」社区优秀作品,作者:大师兄 6668。




 


 

在开发技术不断革新的当下,将 Python 的灵活编程能力与 KWDB 强大的数据管理性能相结合,能创造出功能丰富且高效的应用程序。本文将带领大家利用这两者打造一个自动售卖机系统,从环境搭建、功能实现到最终优化,逐步揭开项目的神秘面纱。我们将借助 Python 丰富的生态库,进一步来拓展 KWDB 在数据处理和应用开发方面的能力。



开发环境配置

一、安装 Python

Python 作为项目的核心开发语言,可从 Python 官方网站下载安装包进行安装。建议安装最新稳定版本,以获取更好的性能和功能支持。安装过程中,记得勾选“Add Python to PATH”选项,方便后续在命令行中直接调用 Python。

二、安装KWDB

若 KWDB 尚未安装,可通过 Docker 进行快速部署。以 Centos 云服务为例,先确保已安装 Docker,接着拉取 KWDB 镜像:docker pull kwdb/kwdb:latest。之后创建 docker-compose.yml 文件,配置容器参数:

version: '3.3'
services:
  kaiwudb-container:
    image: "kwdb/kwdb:latest"
    container_name: kaiwudb-experience
    hostname: kaiwudb-experience
    ports:
      - 8080:8080
      - 26257:26257
    ulimits:
      memlock: -1
    volumes:
      - /dev:/dev
    networks: 
      - default
    restart: on-failure
    ipc: shareable
    privileged: true
    environment:
      - LD_LIBRARY_PATH=/kaiwudb/lib
    tty: true
    working_dir: /kaiwudb/bin
    command: 
      - /bin/bash
      - -c
      - |
        kaiwudb/
bin/kwbase start-single-node --insecure --listen-addr=0.0.0.0:26257 --advertise-addr=127.0.0.1:26257 --http-addr=0.0.0.0:8080 --store=/kaiwudb/deploy/kaiwudb

保存文件后,在所在目录执行 docker-compose up -d 启动 KWDB 服务。

三、安装相关 Python 库

项目中需使用 psycopg 库连接 KWDB,通过 pip 进行安装:pip install "psycopg[binary]"。若涉及 Web 开发,还可安装 Flask 框架:pip install flask,方便构建售卖机的前端交互界面。



自动售卖机项目搭建

一、数据库设计

在 KWDB 中创建数据库和表存储饮料信息。可使用 kwbase CLI 工具连接 KWDB,创建名为 vending_machine 的数据库:CREATE DATABASE vending_machine;。

接着在数据库内创建 drinks 表,存储饮料名称、价格、库存等信息:

CREATE TABLE vending_machine.drinks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    stock INT NOT NULL
);

插入一些初始饮料数据:

INSERT INTO vending_machine.drinks (name, price, stock) VALUES 
('可乐', 3.00, 10),
('雪碧', 2.50, 10),
('橙汁', 4.00, 10);

二、服务端接口搭建

借助 Flask 框架开发服务端接口,实现饮料查询、购物车结算等功能。创建 app.py 文件,编写如下代码:

from flask import Flask, jsonify, request, render_template
import
 psycopg

app = Flask(__name__)

# 连接KWDB数据库
def connect_kwdb():
    url = "postgresql://root@你的云服务器IP:26257/defaultdb"
    try:
        conn = psycopg.connect(url)
        return
 conn
    except psycopg.Error as e:
        print
(f"连接KWDB失败: {e}")
        return
 None

def query_data():
    goods={}
    conn = connect_kwdb()
    if
 not conn:
        return
 jsonify({"error": "无法连接数据库"}), 500
    cursor = conn.cursor()
    try:
        cursor.execute("SELECT name, price, stock FROM vending_machine.drinks;")
        result = cursor.fetchall()
        data=[]
        if
 len(result) > 0:
            columns = [desc[0] for desc in cursor.description]
            table_data = [{columns[i]: row[i] for i in range(len(columns))} for row in result]
            data.extend(table_data)

        # 提交事务并关闭连接
        conn.commit()

    except psycopg.Error as e:
        return
 jsonify({"error": f"查询饮料失败: {e}"}), 500
    finally:
        cursor.close()
        conn.close()
        for
 i in data:
            goods[i['name']]=i['price']
        return
 goods

# 将数据库取出来的数据赋值给goods
goods=query_data()


@app.route('/')
def index():
    return
 render_template('index.html',goods=goods)

@app.route('/goods', methods=['GET'])
def get_goods():
    return
 jsonify(goods)




# 购物车结算接口
@app.route('/checkout', methods=['POST'])
def checkout():
    cart = request.json.get('cart')
    if
 not cart:
        return
 jsonify({"error": "购物车数据为空"}), 400
    conn = connect_kwdb()
    if
 not conn:
        return
 jsonify({"error": "无法连接数据库"}), 500
    cursor = conn.cursor()
    total_price = 0
    for
 drink, count in cart.items():
        try:
            cursor.execute("SELECT price, stock FROM vending_machine.drinks WHERE name = %s", (drink,))
            drink_info = cursor.fetchone()
            if
 drink_info:
                price, stock = drink_info
                if
 stock < count:
                    return
 jsonify({"error": f"{drink}库存不足"}), 400
                total_price += price * count
        except psycopg.Error as e:
            return
 jsonify({"error": f"结算失败: {e}"}), 500
    try:
        for
 drink, count in cart.items():
            cursor.execute("UPDATE vending_machine.drinks SET stock = stock - %s WHERE name = %s", (count, drink))
        conn.commit()
        return
 jsonify({'message': '结算成功', 'total_price': total_price})
    except psycopg.Error as e:
        conn.rollback()
        return
 jsonify({"error": f"更新库存失败: {e}"}), 500
    finally:
        cursor.close()
        conn.close()



if
 __name__ == '__main__':
    app.run()

三、前端页面搭建

使用 HTML、CSS 和 JavaScript 构建前端页面,展示饮料列表、购物车及结算结果。在项目目录下创建 templates 文件夹,放入 index.html 文件:

<!DOCTYPE html>
<html>
<head>
    <title>饮料自动售货机</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f2f2f2;
        }
      .container {
            max-width: 800px;
            margin: 0 auto;
            background-color: #fff;
            padding: 30px;
            border-radius: 5px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }
        h2 {
            color: #333;
            margin-top: 0;
        }
      .button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 20px;
            border: none;
            cursor: pointer;
            font-size: 16px;
            border-radius: 3px;
        }
      .button:hover {
            opacity: 0.8;
        }
      .goods-list,.cart,.checkout-result {
            margin-bottom: 20px;
        }
      .goods-list-item {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 10px;
            border-bottom: 1px solid #ccc;
        }
      .goods-list-item:last-child {
            border-bottom: none;
        }
      .cart-item {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 10px;
            border-bottom: 1px solid #ccc;
        }
      .cart-item:last-child {
            border-bottom: none;
        }
        .success-message {
            color: #28a745;
            padding: 10px;
            border: 1px solid #28a745;
            border-radius: 4px;
            margin: 10px 0;
        }

        .error-message {
            color: #dc3545;
            padding: 10px;
            border: 1px solid #dc3545;
            border-radius: 4px;
            margin: 10px 0;
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>饮料列表</h2>
        <div class="goods-list" id="goods-list"></div>

        <hr>

        <h2>购物车</h2>
        <div class="cart" id="cart"></div>

        <button class="button" onclick="checkout()">结算</button>

        <hr>

        <h2>结算结果</h2>
        <div class="checkout-result" id="checkout-result"></div>
    </div>

    <script src="https://oss-emcsprod-public.modb.pro/image/auto/modb_20250929_4db716f4-9cea-11f0-829c-8ca982f925c1.png"></script>
    <script>
        const cart = {};

        发起HTTP请求获取商品数据
        axios.get("/goods")
          .then(response => {
                const goodsData = response.data;
                console.log('========',goodsData)

            渲染商品列表
            const goodsListDiv = document.getElementById('goods-list');
            for (const drink in goodsData) {
              const price = goodsData[drink];

              const drinkDiv = document.createElement('div');
              const drinkText = document.createTextNode(drink + ": " + price + " 元");
              drinkDiv.appendChild(drinkText);

              const addButton = document.createElement('button');
              addButton.textContent = "加入购物车";
              addButton.addEventListener('click', function() {
                addToCart(drink);
              });

              drinkDiv.appendChild(addButton);
              goodsListDiv.appendChild(drinkDiv);
            }
            })
          .catch(error => {
                console.error(error);
            });

        添加到购物车的逻辑
        function addToCart(drink) {
            if (cart.hasOwnProperty(drink)) {
                cart[drink]++;
            } else {
                cart[drink] = 1;
            }

            renderCart();
        }

        渲染购物车
        function renderCart() {
            const cartDiv = document.getElementById('cart');
            cartDiv.innerHTML = '';

            for (const drink in cart) {
                const quantity = cart[drink];

                const itemDiv = document.createElement('div');
                const itemText = document.createTextNode(drink + ": " + quantity + "杯");
                itemDiv.appendChild(itemText);

                cartDiv.appendChild(itemDiv);
            }
        }

        结算函数
        function checkout() {
            axios.post('/checkout', {cart: cart})
            .then(response => {
                    const resultDiv = document.getElementById('checkout-result');
                    清空之前的样式和内容
                    resultDiv.innerHTML = '';
                    resultDiv.className = '';
                    
                    处理成功响应
                    if (response.data.message === '结算成功') {
                        const total_price = response.data.total_price;
                        resultDiv.className = 'success-message';
                        resultDiv.innerHTML = `✅ ${response.data.message}<br>总消费金额:${total_price}元`;
                    }
                    处理其他可能的成功状态
                    else {
                        resultDiv.className = 'error-message';
                        resultDiv.textContent = response.data.error || '未知响应';
                    }
                })
            .catch(error => {
                    const resultDiv = document.getElementById('checkout-result');
                    清空之前的样式和内容
                    resultDiv.innerHTML = '';
                    resultDiv.className = 'error-message';
                    
                    if (error.response) {
                        处理HTTP状态码为4xx/5xx的响应
                        const serverError = error.response.data.error;
                        if (serverError) {
                            resultDiv.innerHTML = `❌ 错误:${serverError}`;
                        } else {
                            resultDiv.textContent = `请求错误:${error.response.status}`;
                        }
                    } else if (error.request) {
                        请求已发送但没有收到响应
                        resultDiv.textContent = "网络错误,请检查连接状态";
                    } else {
                        其他错误
                        resultDiv.textContent = `请求失败:${error.message}`;
                    }
                });
        }

    </script>
</body>
</html>

四、运行项目与效果预览

在项目目录下,执行 python app.py 启动服务。若一切正常,可在浏览器中访问 http://127.0.0.1:5000 查看自动售卖机页面。用户能看到饮料列表及库存信息,点击“加入购物车”按钮添加饮料,结算时系统会检查库存并计算总价,同时更新数据库中的库存数据。

  • 初始页面

  • 正常结算页面

  • 异常页面



总结与展望

通过 Python 与 KWDB 的协同工作,成功打造了一个功能完备的自动售卖机系统。KWDB 强大的数据存储和管理能力,确保了饮料数据的高效处理。Python 凭借丰富的库和简洁的语法,让开发过程变得轻松便捷。后续可进一步优化系统,如添加用户登录注册功能、完善库存预警机制等,不断提升用户体验。希望本文能为大家在相关开发领域提供有益参考,激发更多创新实践。




 


 

本文作者

 


https://blog.csdn.net/qq_41308872?type=blog



个人简介

个人简介:大师兄 6668,分享 Python 基础支持、爬虫、数据分析和 Python Web 等知识的 CSDN 博主。对 Python 编程语言充满热爱,并致力于将复杂的概念转化为易于理解和实践的教程。





KWDB 创作者计划是 KWDB 社区今年发布的新项目,我们希望通过计划和广大开发者们一起畅所欲言,共同探索开源分布式多模数据库的创新之道。

2025 KWDB 第一届创作者计划征文大赛已圆满落幕。大赛涌现出了许多优秀作品,从今日起,我们将持续分享本次大赛的优秀创作,让好的内容和创作者被更多人所看到,迸发灵感碰撞思想的火花,携手打造更加多彩的开源多模数据库未来。



最后修改时间:2025-10-10 10:13:17
文章转载自KaiwuDB,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论