Python正课58 —— 小说阅读项目 初级
本文内容皆为作者原创,如需转载,请注明出处:https://www.cnblogs.com/xuexianqi/p/12600935.html
一:图片
目录结构:
bin
start.py
conf
settings.py
core
src.py
db
foctions
egon流浪记 - 序曲.txt
成长之路 - 起源.txt
绿色的光 - 毁灭.txt
egon再临 - 重生.txt
父子局 - 轮回.txt
egon养成攻略 - 终章.txt
db.txt
db_handler
story_class.txt
lib
common.py
log
log.log
README.TXT
二:代码
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消费) 金额"
# 附加:
# 可以拓展作者模块,作者可以上传自己的作品