基于Python的运动计费管理系统

动机

小伙伴们最近迷恋上羽毛球,组织了个小群,办了公用的运动卡用于开场,考虑不是每次活动都是全员参与,需要一个计费的系统来计算每个人需要交的费用。商讨后决定采用“预充-扣费”的方式,则需要一个系统进行计费和扣费。

技术路线规划

模块名 语言 备注
管理核心 Python 使用JSON存储信息
Web后端 Python Flask框架
Web前端 HTML Jinja框架渲染

实现

核心模块——用户状态管理

该部分是整个计费系统的核心,用于管理每个用户的余额。使用一个类表示用户,需要的属性为

  • 状态列表(用户名,ID,使用次数,余额)

需要的方法有:

  • 创建用户(创建新的JSON文件)
  • 读取用户状态(从已有的JSON文件中)
  • 扣费(使用次数增加1,余额减小)
  • 充值(余额增加)
  • 保存状态(将现有的状态写入JSON文件)

代码如下

# -*- coding: utf-8 -*-
import json
import os


class UserHanlde(object):
    """docstring for UserHanlde"""

    def __init__(self, UserID, UserName=""):
        super(UserHanlde, self).__init__()
        if self.UserExsist(UserID):
            self.UserInfo = self.LoadUserInfo(UserID)
        else:
            self.UserInfo = self.CreateNewUser(UserName, UserID)

构造函数,若该用户ID存在则读取状态,否则创建

    def UserExsist(self, UserID):
        return os.path.exists("./Users/%s.json" % UserID)

判断该ID的JSON文件是否存在

    def CreateNewUser(self, UserName, UserID):
        UserInfo = {
            "name": UserName,
            "id": UserID,
            "num": 0,
            "balance": 50
        }
        with open("./Users/%s.json" % UserID, "w") as jsonfile:
            json.dump(UserInfo, jsonfile, ensure_ascii=False, indent=4)
        return UserInfo

创建新用户,将初始余额设为50并保存JSON文件

    def LoadUserInfo(self, UserID):
        with open("./Users/%s.json" % UserID, "r") as jsonfile:
            return json.load(jsonfile)

从JSON文件中载入用户状态

    def PlayOneTime(self, Pay):
        self.UserInfo["num"] += 1
        self.UserInfo["balance"] = self.UserInfo["balance"] - Pay

扣费,扣除指定的费用并在将扣费次数+1

    def Recharge(self, Pay):
        self.UserInfo["balance"] += Pay

充值,费用加上指定值

    def DeleteUser(self):
        os.remove("./Users/%s.json" % self.UserInfo["id"])

删除用户,删除指定的JSON文件

    def SaveInfo(self):
        with open("./Users/%s.json" % self.UserInfo["id"], "w") as jsonfile:
            json.dump(self.UserInfo, jsonfile, ensure_ascii=False, indent=4)

保存状态,将当前状态写入对应的JSON文件

Web后端

web后端使用Python的Flask框架构造,代码如下

from flask import Flask, render_template, request
from UserHanlde import UserHanlde
import os
app = Flask(__name__)


def GetUserIDList():
    return [x[:-5] for x in os.listdir("./Users") if ".json" in x]

def GetUserInfoList():
    UserInfoList = dict()
    for UserID in GetUserIDList():
        UserData = UserHanlde(UserID)
        UserInfoList[UserID] = UserData.UserInfo
    return UserInfoList

常用部分的封装:

  • GetUserIDList():返回已经存在的用户ID列表
  • GetUserInfoList():返回已经存在的用户状态列表
@app.route("/index")
def ViewInfo():
    return render_template("index.html", user_list=GetUserInfoList())


@app.route("/recharge")
def GetReChargeInfo():
    return render_template("recharge.html", user_list=GetUserInfoList())


@app.route("/recharge_handle", methods=["GET", "POST"])
def Recharge():
    UserID = request.values.get("id")
    UserRecharge = request.values.get("pay")
    if UserRecharge.isdigit() is True:
        UserHanlder = UserHanlde(UserID)
        UserHanlder.Recharge(int(UserRecharge))
        UserHanlder.SaveInfo()
        return render_template("back.html")
    else:
        return "fail"


@app.route("/register")
def GetRegisterInfo():
    return render_template("register.html")


@app.route("/register_handle", methods=["GET", "POST"])
def Register():
    UserID = request.values.get("id")
    UserName = request.values.get("name")
    UserHanlder = UserHanlde(UserID, UserName=UserName)
    return render_template("back.html")


@app.route("/pay")
def GetPayName():
    return render_template("pay.html", user_list=GetUserInfoList())


@app.route("/pay_handle", methods=["GET", "POST"])
def Pay():
    UserIDList = request.values.getlist("vehicle")
    UserIDPay = request.values.get("pay")
    if UserIDPay.isdigit() is True:
        PayNum = int(UserIDPay) / len(UserIDList)
        for UserID in UserIDList:
            UserHanlder = UserHanlde(UserID)
            UserHanlder.PlayOneTime(PayNum)
            UserHanlder.SaveInfo()
        return render_template("back.html")
    else:
        return "fail"

路由部分

  • /index:主页,包括导航和状态显示,所有用户的消费次数和余额将在这里显示
  • /recharge/recharge_handle:充值页面, /recharge为操作页面,用户在这里填写表单数据,随后表单数据被提交到/recharge_handle处理充值业务
  • /register/register_handle:注册页面,与/recharge/recharge_handle关系相同
  • /pay/pay_handle:扣费页面,与/recharge/recharge_handle关系相同
app.run(host="0.0.0.0")

运行,监听所有IP,这样在局域网就可以访问了

Web前端

Web使用HTML代码提供GUI,使用Jinja框架分离数据与模板

  • index界面
<!DOCTYPE html>
<html>
<head>
    <title>index</title>
</head>
<body>
    <div>
        <h1>羽毛球运动管理系统</h1>
    </div>
    <div>
        <table border="1">
            <thead>
                <tr>
                    <th>用户</th>
                    <th>次数</th>
                    <th>余额</th>
                </tr>
            </thead>
            <tbody>
                {% for user_id in user_list -%}
                <tr>
                    <td>{{user_list[user_id]["name"]}}</td>
                    <td>{{user_list[user_id]["num"]}}</td>
                    <td>{{user_list[user_id]["balance"]}}</td>
                </tr>
                {%- endfor %}
            </tbody>
        </table>
    </div>

用户状态显示,使用for循环生成表格

    <div>
        <a href="register">register</a>
        <a href="recharge">recharge</a>
        <a href="pay">pay</a>
    </div>
</body>
</html>

超链接部分,用于导航

  • register界面
<!DOCTYPE html>
<html>
<head>
    <title>register</title>
</head>
<body>
    <h1>羽毛球运动管理系统--注册</h1>
    <div>
        <form action="register_handle" method="post" accept-charset="utf-8">
            name<input type="text" name="name">
            id<input type="text" name="id">
            <input type="submit" name="Submit">
        </form>
    </div>
    <a href="/index">back to index</a>
</body>
</html>

使用两个文本输入框表单输入用户名与用户ID

  • recharge界面
<!DOCTYPE html>
<html>
<head>
    <title>recharge</title>
</head>
<body>
    <div>
        <h1>羽毛球运动管理系统--充值</h1>
    </div>
    <div>
        <form action="recharge_handle" method="post" accept-charset="utf-8">
            <select name="id">
                {% for userid in user_list -%}
                <option value="{{userid}}">{{user_list[userid]["name"]}}</option>
                {%- endfor %}
            </select>
            recharge¥<input type="text" name="pay">
            <input type="submit" name="Submit">
        </form>
    </div>
    <a href="/index">back to index</a>
</body>
</html>

使用下拉菜单提供可供选择的用户名,文本输入充值金额

  • pay界面
<!DOCTYPE html>
<html>
<head>
    <title>pay</title>
</head>
<body>
    <h1>羽毛球运动管理系统--消费</h1>
    <div>
        <form action="pay_handle" method="post" accept-charset="utf-8">
            <div>
                {%for userid in user_list%}
                <input type="checkbox" name="vehicle" value="{{userid}}">{{user_list[userid]["name"]}}<br>
                {% endfor %}
            </div>
            pay¥<input type="text" name="pay">
            <input type="submit" name="Submit">
        </form>
    </div>
    <a href="/index">back to index</a>
</body>
</html>

使用复选框列出所有用户提供选择,文本输入总输入金额,复选框这种表单数据在后端使用request.values.getlist("name")获取为一个列表

  • back界面
<!DOCTYPE html>
<html>
<head>
    <title>back</title>
</head>
<body>
    <a href="/index">back to index</a>
</body>
</html>

用户完成充值/注册/消费时用于返回主页

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,810评论 19 139
  • 我是纵横荒野的一股清流 潺潺万水千山的叮咚 绵延浓浓月色的层层相思 遗憾古往今来 未曾圆满的故事 习惯了灯红酒绿中...
    妄与栀子阅读 3,798评论 4 5
  • 今天下课时,看到我以前幼儿园的好朋友丰家成同学,但是我们都没打招呼。今天我们学的兔子舞,我们要好好学,争取让我们班...
    张广远一班阅读 1,580评论 0 0
  • 不记得自己什么时候真正的瘦过,有个从小到大的闺蜜,可以说是形影不离直到各自成家生子,跟她在一起我们俩永远是鲜明对比...
    简简丹丹1阅读 1,315评论 3 1
  • 《说话的魅力》文/刘墉 自认是个很会说话,说话很得体的人。看完《说话的魅力》才知道会说话涉及到方方面面,这本书不是...
    Dolphin池池阅读 3,579评论 0 0

友情链接更多精彩内容