基于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>

用戶完成充值/注冊/消費時用于返回主頁

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,837評論 18 139
  • 我是縱橫荒野的一股清流 潺潺萬水千山的叮咚 綿延濃濃月色的層層相思 遺憾古往今來 未曾圓滿的故事 習慣了燈紅酒綠中...
    妄與梔子閱讀 626評論 4 5
  • 今天下課時,看到我以前幼兒園的好朋友豐家成同學,但是我們都沒打招呼。今天我們學的兔子舞,我們要好好學,爭取讓我們班...
    張廣遠一班閱讀 227評論 0 0
  • 不記得自己什么時候真正的瘦過,有個從小到大的閨蜜,可以說是形影不離直到各自成家生子,跟她在一起我們倆永遠是鮮明對比...
    簡簡丹丹1閱讀 154評論 3 1
  • 《說話的魅力》文/劉墉 自認是個很會說話,說話很得體的人。看完《說話的魅力》才知道會說話涉及到方方面面,這本書不是...
    Dolphin池池閱讀 538評論 0 0