Python正课58 —— 小说阅读项目 初级

本文内容皆为作者原创,如需转载,请注明出处:https://www.cnblogs.com/xuexianqi/p/12600935.html

一:图片

目录结构:

dir

bin

start.py

start

conf

settings.py

s

core

src.py

src

db

foctions

egon流浪记 - 序曲.txt

序曲

成长之路 - 起源.txt

起源

绿色的光 - 毁灭.txt

毁灭

egon再临 - 重生.txt

重生

父子局 - 轮回.txt

轮回

egon养成攻略 - 终章.txt

终章

db.txt


db_handler

db_handler

story_class.txt

story_class

lib

common.py

common

log

log.log


README.TXT

readme

二:代码

bin

start.py

# 用于存放启动文件

import os  # 导入os模块
import sys  # 导入sys模块

# 将项目的根目录,添加到sys.path中
sys.path.append(
    os.path.dirname(os.path.dirname(__file__))
)

from core import src  # 导入core中的src模块

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

conf

settings.py

# 用于存放配置文件

import os

# 获取项目根目录Novel - 1的根目录
BASE_PATH = os.path.dirname(os.path.dirname(__file__))

# 获取db目录的路径
DB_PATH = os.path.join(BASE_PATH, 'db')

# 获取db.txt的根目录
DB_TXT_PATH = os.path.join(DB_PATH, 'db.txt')

# story_class文件目录路径
STORY_PATH = os.path.join(DB_PATH, 'story_class.txt')

# 小说存放目录
FICTION_DIR = os.path.join(DB_PATH, 'fictions')

# 日志文件的路径
LOG_PATH = os.path.join(BASE_PATH, 'log', 'log.txt')

core

src.py

# 用于存放核心代码

import time
from db import db_handler
from lib import common

# 定义login_user:若能进入该函数,证明该用户已经登录,还能获取当前用户名
login_user = None


# 0 注册功能
def register():
    print('注册功能执行中...')
    while True:
        username = input('请输入用户名:').strip()

        # 1.先校验用户是否存在
        # 涉及数据的操作:调用查看数据的功能:select
        # 给select函数传入当前输入的用户名,判断该用户是否存在
        user_data = db_handler.select(username)

        # 2.若存在,则让用户重新输入
        if user_data:
            print('当前用户已存在,请重新输入!')
            continue

        password = input('请输入密码:').strip()
        re_password = input('请确认密码:').strip()

        # 3.检测两次输入的密码是否一致
        if password == re_password:

            # 4.将当前用户的数据写入到文件中
            db_handler.save(username, password)
            print(f'用户[{username}]注册成功!')
            break

        else:
            print('两次密码不一致,请重新输入!')


# 1 登录功能
def login():
    print('登录功能执行中...')
    while True:
        username = input('请输入用户名:').strip()

        # 1.查看当前用户是否存在
        user_data = db_handler.select(username)

        # 2.若不存在,则让用户重新输入
        if not user_data:
            print('该用户不存在,请重新输入!')
            continue

        password = input('请输入密码:').strip()

        # 3.校验用户输入的密码是否与db.txt中的密码一致
        if password == user_data[1]:

            # 4.用户登录后,记录登录状态
            global login_user
            login_user = username

            print(f'用户[{username}]登录成功!')
            break

        else:
            print('密码错误,登录失败!')


# 2 充值功能
@common.login_auth
def recharge():
    print('充值功能执行中...')
    while True:
        # 1.让用户输入充值的金额
        balance = input('请输入要充值的金额:').strip()

        # 2.判断用户输入的是否是数字
        if not balance.isdigit():
            print('请输入数字!')
            continue

        balance = int(balance)  # 将字符串类型 转换成 数字类型

        # 3.修改当前用户的金额
        # 3.1.获取当前用户数据
        user, pwd, bal = db_handler.select(login_user)  # [user, pwd, bal]

        # 3.2.先获取用户“修改前”的数据
        old_data = f'{user}:{pwd}:{bal}'

        # 3.3.修改当前用户金额,做加钱操作
        bal = int(bal)
        bal += balance  # bal是取出来的数据,balance是用户输入的要增加的数据

        # 3.4.拼接“修改后”的用户数据
        new_data = f'{user}:{pwd}:{bal}'

        # 3.5.调用修改数据的功能
        db_handler.update(old_data, new_data)
        print(f'当前用户:[{login_user}]充值金额:[{balance}]元,成功!')

        # ending:做充值日志记录
        now_time = time.strftime('%Y-%m-%d %X')
        log_data = f'时间:{now_time} 用户名:{login_user} 充值金额:{balance}'
        print(log_data)
        common.append_log(log_data)  # 将日志信息添加进去
        break


# 3 小说阅读功能
@common.login_auth
def reader():
    print('小说阅读功能执行中...')
    '''
    1.写该功能之前,现将小说数据,存放在story_class.txt文件中
    2.现将story_class.txt文件中的数据读取出来,然后解析成字典类型
    '''
    story_dic = db_handler.get_all_story()

    # 判断story_class.txt文件中是否有小说数据
    if not story_dic:
        print('没有小说,请联系管理员!')
        return

    while True:
        # 1.打印小说的种类选择信息
        print('''
        ======= Welcome To Index Page =======
                0 起源系列
                1 重生系列
                2 轮回系列
        ''')

        # 2.让用户输入小说类型编号
        choice1 = input('请输入小说类型编号:').strip()

        # 3.判断当前用户选择的编号是否存在
        # 若不存在,就重新输入
        if choice1 not in story_dic:
            print('输入有误,请重新输入!')
            continue

        # 4.获取当前小说类型中的所有小说
        fiction_dic = story_dic.get(choice1)

        # 5.打印当前类型的所有小说
        for number, fiction_list in fiction_dic.items():
            name, price = fiction_list
            print(f'小说编号:[{number}]  小说名字:[{name}]  小说价格:[{price}]')

        # 6.让用户选择需要购买的小说
        while True:
            choice2 = input('请输入要购买的小说编号:').strip()
            if choice2 not in fiction_dic:
                print('输入有误,请重新输入!')
                continue

            name, price = fiction_dic.get(choice2)

            # 7.让用户输入y,选择是否要购买商品
            choice3 = input(f'当前选择的小说名为:[{name}],商品单价为:[{price}],请输入 y 购买').strip()

            # 8.判断用户输入的是否是 y
            if choice3 == 'y':

                # 9.校验当前用户的余额是否大于小说单价
                # 9.1.获取当前用户的金额
                user, pwd, bal = db_handler.select(login_user)

                # 9.2.判断金额
                bal = int(bal)
                price = int(price)

                if bal < price:
                    print('穷逼,回家种田去吧!')
                    break

                # 10.如果余额充足,就开始扣费
                # 10.1.拼接用户 “修改前” 的数据
                old_data = f'{user}:{pwd}:{bal}'

                # 10.2.开始扣费
                bal -= price  # bal是用户的原来余额,price是要扣去的小说的价格

                # 10.3.拼接用户 “修改后” 的数据
                new_data = f'{user}:{pwd}:{bal}'
                db_handler.update(old_data, new_data)

                print('当前小说购买成功,自动打开小说进行阅读~')

                # 11.调用获取小说的详情信息
                fiction_data = db_handler.show_fiction_data(name)
                print(f'''
                ======= 当前小说数据如下 =======
                {fiction_data}
                ''')

                # 12.记录购买成功的日志
                now_time = time.strftime('%Y-%m-%d %X')
                log_data = f'时间:[{now_time}] 用户名:[{login_user}] 消费金额:[{price}]'
                print(log_data)
                common.append_log(log_data)
                break


# 函数字典
func_dic = {
    '0': register,
    '1': login,
    '2': recharge,
    '3': reader,  # 这个逗号可有可无,加了也没关系
}


# 启动函数
def run():
    print('启动ing...')
    while True:
        print('''
        ======= 小说阅读器欢迎您 =======
                0 账号注册
                1 账号登录
                2 充值功能
                3 阅读小说
        ''')

        choice = input('请输入功能编号(温馨提示[输入q退出]:').strip()

        if choice == 'q':
            break

        #  判断用户输入的编号是否在函数字典中
        if choice not in func_dic:
            print('当前编号有误,请重新输入!')
            continue

        func_dic.get(choice)()

db

foctions

egon流浪记 - 序曲.txt

egon从小就是一个孤儿
egon从小就是一个孤儿
egon从小就是一个孤儿
egon从小就是一个孤儿
egon从小就是一个孤儿

成长之路 - 起源.txt

egon历经千辛万苦,终于活了下来
egon历经千辛万苦,终于活了下来
egon历经千辛万苦,终于活了下来
egon历经千辛万苦,终于活了下来
egon历经千辛万苦,终于活了下来

绿色的光 - 毁灭.txt

egon头上闪耀着奇异的光芒
egon头上闪耀着奇异的光芒
egon头上闪耀着奇异的光芒
egon头上闪耀着奇异的光芒
egon头上闪耀着奇异的光芒

egon再临 - 重生.txt

egon,复活了
egon,复活了
egon,复活了
egon,复活了
egon,复活了

父子局 - 轮回.txt

Alex和egon相爱相杀
Alex和egon相爱相杀
Alex和egon相爱相杀
Alex和egon相爱相杀
Alex和egon相爱相杀

egon养成攻略 - 终章.txt

把不用的egon撒上鸡蛋液,裹上面包糠,隔壁小孩馋哭了
把不用的egon撒上鸡蛋液,裹上面包糠,隔壁小孩馋哭了
把不用的egon撒上鸡蛋液,裹上面包糠,隔壁小孩馋哭了
把不用的egon撒上鸡蛋液,裹上面包糠,隔壁小孩馋哭了
把不用的egon撒上鸡蛋液,裹上面包糠,隔壁小孩馋哭了

db.txt


db_handler

# 用于存放数据操作代码

from conf import settings
import os


# 查看数据
def select(username):
    # 接受用户名,若存在,就返回当前用户的所有数据;若不存在,就返回None
    with open(settings.DB_TXT_PATH, mode='rt', encoding='UTF-8') as f:
        # 获取db.txt文件中的每一行数据
        for line in f:
            # 在每一行中,判断接收过来的用户名是否存在于db.txt文件中
            if username in line:
                # 若用户存在,则在当前行中,提取该用户的所有数据
                user_data = line.strip().split(':')  # 用split将:切分
                # 将当前数据,返回给调用者
                return user_data


# 保存数据
def save(username, password, balance=0):
    '''
    :param username: 注册的用户名
    :param password: 注册的密码
    :param balance: 注册时,默认余额为0
    :return:
    '''
    with open(settings.DB_TXT_PATH, mode='at', encoding='UTF-8') as f:
        # 将注册的信息添加到db.txt中
        f.write(f'{username}:{password}:{balance}\n')


# 更新数据
def update(old_data, new_data):
    '''
    :param old_data: 用户原来的数据
    :param new_data: 用户的新数据
    :return:
    '''
    # 1.拼接新的文件路径
    new_path = os.path.join(
        settings.DB_PATH, 'new.txt'
    )
    # 2.读取db.txt文件中的数据 进行修改,写入到新文件new.txt中,再更换为db.txt文件名
    with open(settings.DB_TXT_PATH, mode='rt', encoding='UTF-8') as r_f, \
            open(new_path, mode='wt', encoding='UTF-8') as w_f:
        # 2.1.新旧数据替换    尽量让代码更加简洁
        all_user_data = r_f.read()
        all_user_data = all_user_data.replace(old_data, new_data)

        # 2.2.将新的数据写入到新文件new.txt中
        w_f.write(all_user_data)

    # 3.文件名的修改  os.remove()可以不写,因为原来的db.txt会被覆盖掉
    os.replace(new_path, settings.DB_TXT_PATH)


# 获取小说字典数据
def get_all_story():
    with open(settings.STORY_PATH, mode='rt', encoding='UTF-8') as f:
        story_dic = eval(f.read())
        return story_dic


# 查看单本小说
def show_fiction_data(fiction_name):
    # 获取小说的路径
    fiction_path = os.path.join(
        settings.FICTION_DIR, fiction_name
    )

    # 打开文件,获取文件数据,并返回给用户
    with open(fiction_path, mode='rt', encoding='UTF-8') as f:
        fiction_data = f.read()

        return fiction_data

story_class.txt

{
    "0":{
        "0":["egon流浪记 - 序曲.txt",50],
        "1":["成长之路 - 起源.txt",100]
    },
    "1":{
        "0":["绿色的光 - 毁灭.txt",200],
        "1":["egon再临 - 重生.txt",400]
    },
    "2":{
        "0":["E&A父子局 - 轮回.txt",500],
        "1":["egon养老攻略 - 终章.txt",1000]
    },

}

lib

common.py

# 用于存放公共的功能

from conf import settings


# 登录认证装饰器login_auth
def login_auth(func):
    # 解决问题:循环导入问题
    from core import src

    def inner(*args, **kwargs):
        if src.login_user:
            res = func(*args, **kwargs)
            return res
        else:
            print('未登录,不允许使用其他功能,请先登录!')
            src.login()

    return inner


# 记录日志,应该放在公共功能中
def append_log(log_data):
    # 写入日志数据
    with open(settings.LOG_PATH, mode='at', encoding='UTF-8') as f:
        f.write(log_data + '\n')

log

log.log


README.TXT

# 软件的使用规范

一 软件开发目录规范

    - api   存放接口文件,接口主要用于为业务逻辑提供数据操作
        - api.py --> 应用程序编程接口

    - bin   整个项目的启动文件放置在这个文件夹中
        - start.py --> 启动软件入口

    - conf  整个项目的配置文件放置在这个文件夹
        - settings.py   配置文件
            - 比如存放一些固定的路径

    - core  整个项目的核心文件
        - src.py 核心业务逻辑代码

    - db    用于存放数据文件与操作数据的代码文件
        - db_file ---> db.txt ...
        - db_handler.py ---> 操作数据的代码

    - lib   项目的第三方包,共享的一些库
        - common: 存放公共的功能
            - 比如存放 装饰器

    - log   用于存放项目的日志文件
        - log.txt  存放日志的文件

    - README.TXT    整个项目的说明文件,项目需求和大致流程,相当于产品说明书


二 一个项目开发前,有一份开发文档

    项目: 编写小说阅读程序实现下属功能

        # 一:程序运行开始时显示
            0 账号注册
            1 账号登录
            2 充值功能
            3 阅读小说

        # 二: 针对文件db.txt,内容格式为:"用户名:密码:金额",完成下述功能
            2.1、账号注册
            2.2、账号登录
            2.3、充值功能

        # 三:文件story_class.txt存放类别与小说文件路径,如下,读出来后可用eval反解出字典
            {
                "0":{
                    "0":["egon流浪记 - 序曲.txt",50],
                    "1":["成长之路 - 起源.txt",100]
                },
                "1":{
                    "0":["绿色的光 - 毁灭.txt",200],
                    "1":["egon再临 - 重生.txt",400]
                },
                "2":{
                    "0":["父子局 - 轮回.txt",500],
                    "1":["egon养成攻略 - 终章.txt",1000]
                },
            }

            3.1、用户登录成功后显示如下内容,根据用户选择,显示对应品类的小说编号、小说名字、以及小说的价格
            """
            0 起源系列
            1 重生系列
            2 轮回系列
            """

            3.2、用户输入具体的小说编号,提示是否付费,用户输入y确定后,
                 扣费并显示小说内容,如果余额不足则提示余额不足

        # 四:为功能2.2、3.1、3.2编写认证功能装饰器,要求必须登录后才能执行操作

        # 五:为功能2.2、3.2编写记录日志的装饰器,日志格式为:"时间 用户名 操作(充值or消费) 金额"


        # 附加:
        # 可以拓展作者模块,作者可以上传自己的作品
posted @ 2020-03-30 20:30  轻描丨淡写  阅读(504)  评论(0编辑  收藏  举报