摸鱼神器——python命令行小说阅读器实现

分享几个YYDS的Pycharm插件 既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!那在用pycharm这个python开发大杀器的同时,一行也来分享几个它很好用的插件,来给你的搬砖提提速。用上Pycharm的CodeGlance也能用上这个功能了,在你的代码旁边内置了一个小地图。跟json一样,数据分析最让人头痛的还是数据列不对应,一行行的找数据对应的表头确实会眼花。这个kite插件利用AI算法,可以自动对整行代码进行预测,帮助你更快的完成任务。 阅读详情
AI摘要

该博客介绍了如何在命令行中实现一个小说阅读器,利用Python读取TXT小说文件,按章节拆分,实现翻页功能。阅读器能够记录阅读历史,支持上一章、下一章和退出操作。源码中包含了对SQLite数据库的操作,用于存储阅读进度。

一、摸鱼思路

在命令行中进行小说阅读,可以通过我们IDE编码界面做掩护,通过IDE开启一个小的终端以命令行的方式进行阅读,这样可以表现得正在努力调试代码的样子。

二、阅读器实现思路

  • 准备好测试数据(小说的txt文件)
  • 将小说的源文档按章节进行拆分
    – 按章节阅读,上下章翻页
  • 每次打开时能紧接着上次阅读的内容

三、实现

基于上述的思路,我们的这个类就初始化出来了

class Reader:
    def __init__(self, book_name, txt_path):
        self.book_name = book_name 
        self.txt_path = txt_path
        self.db = DbConfig(self.book_name, "./{}.sqlite3".format(self.book_name))# 该方法见文章底部
        self.db.initial_table()  # 初始化数据库,用来存放阅读历史
        self.contents = []  # 章节名称
        self.book = self.split_book_chapter() # 拆分后的章节字典


1、拆分章节

拆分章节我们可以直接使用re.split()通过正在表达式作为分隔符进行拆分。
一般小说每一章的结构为 第***章 XXXXXX,基于此,我们可以通过两种正则表达式进行拆分
一个是re.split('第,*章',mybook),
另一个是 re.split('\n第',mybook)
实现如下

    def split_book_chapter(self):
        """
        读取文本文件,并将文本按章节划分
        :param txt_path:
        :return:
        """
        book_content = {}
        with codecs.open(self.txt_path, "r", encoding="gbk") as f:
            text = f.read().strip()
            chapters = re.split("\n第", text)
            for chapter in chapters:
                text = chapter.split("\n")
                title = text[0]
                text = "\n".join(text)
                book_content[title] = text
        self.contents = list(book_content.keys())
        return book_content

划分完成后存入字典(PS:python3.6以后的字典都变成了有序字典,所以在上述倒数第二行代码的位置,直接取字典的keys作为文本的目录。如果是python3.6以下的版本记得使用OrderDict进行操作)

2、翻页

翻页则是根据当前阅读的章节名称,从目录中获取当前章节前一章与后一章的名称

    def get_index(self, key_name):
        """
        根据书的章节名称,获取前一章与后一章
        :param key_name:
        :return:
        """
        this_index = self.contents.index(key_name)
        last_chapter = self.contents[this_index - 1]
        next_chapter = self.contents[this_index + 1]
        return last_chapter, next_chapter

3、阅读主逻辑

这里阅读的主逻辑,当每次开始阅读时,从数据库中获取历史记录(上一次退出脚本时,看到的章节),如果没有记录,则从第一章开始。
注意:python的版本需要达到3.6以及以上,因为3.6以后的字典为有序字典,低于这个版本的,需要把字典修改为OrderDict。
每次监听到翻页命令时,则对当前的章节名称进行存储。

    def start_read(self, chapter_name=None):
        """
        开始阅读, 章节名称为空,且数据库中没有历史记录时,从第一章开始
        :param chapter_name: 章节名称
        :return:
        """

        last_chapter = self.db.get_params(["last_chapter"])
        # 如果没有历史记录或者指定章节,则是第一次阅读,在数据库中新增
        if not last_chapter:
            self.db.add_one(["last_chapter"], [self.contents[0]])
        if not chapter_name:
            chapter_name = last_chapter[0] if last_chapter else self.contents[0]
        chapter_name = chapter_name[0] if isinstance(chapter_name, tuple) else chapter_name
        read_content = "".join(self.book.get(chapter_name,"当前章节不存在"))
        print(read_content)
        forward = ""
        while forward not in ["n", "b", "q"]:
            forward = input("""n、下一章\nb、上一章\nq、退出""")

        last_chapter_name, next_chapter_name = self.get_index(chapter_name)
        if forward == "q":
            sys.exit(0)
        # 根据命令选择上一章或者下一章,并更新历史记录
        if forward == "n":
            self.db.update_one("last_chapter", next_chapter_name)
            return next_chapter_name
        if forward == "b":
            self.db.update_one("last_chapter", last_chapter_name)
            return last_chapter_name

4、完整代码

操作sqlite的工具类db.utils.py

import sqlite3
import traceback


class DbConfig:
    """
    从数据库中获取小说的相关章节
    """

    def __init__(self, table, db_path):
        self.table = table
        self.db_path = db_path

    def initial_table(self):
        """
        初始化数据以及库表
        :return:
        """
        conn = sqlite3.connect(self.db_path, check_same_thread=False)
        cursor = conn.cursor()
        cursor.execute("""
            create table if not exists gmzz
            (
                chapter_name text,
                chapter_content text,
                last_chapter text,
                update_time bigint
            );
        """)
        conn.commit()
        conn.close()

    def get_params(self, *params):
        """
        自定义获取参数
        :param params:list: 想要获取的字段
        :return:
        """
        try:
            conn = sqlite3.connect(self.db_path, check_same_thread=False)
            cursor = conn.cursor()
            sql = "select {} from {} ".format(",".join(list(params[0])), self.table)
            rtn = cursor.execute(sql)
            result = list(rtn)
            conn.close()
            return result
        except Exception as e:
            traceback.print_exc()

    def simple_query(self, sql):
        try:
            conn = sqlite3.connect(self.db_path, check_same_thread=False)
            cursor = conn.cursor()
            rtn = cursor.execute(sql)
            conn.close()
            return list(rtn)
        except Exception as e:
            traceback.print_exc()

    def add_one(self,fields, values):
        try:
            conn = sqlite3.connect(self.db_path, check_same_thread=False)
            cursor = conn.cursor()
            sql = "insert into {} ({}) values ('{}')".format(self.table, ",".join(fields), ",".join(values))
            print(sql)
            rtn = cursor.execute(sql)
            conn.commit()
            conn.close()
            return rtn
        except Exception as e:
            traceback.print_exc()

    def update_one(self, field_name, update_value):
        """
        更新某个字段
        :param field_name: 字段名称
        :param update_value: 跟新值
        :return:
        """
        try:
            sql = "update {table} set {field_name} = '{update_value}'".format(
                table=self.table,
                field_name=field_name,
                update_value=update_value
            )
            conn = sqlite3.connect(self.db_path, check_same_thread=False)
            cursor = conn.cursor()
            cursor.execute(sql)
            conn.commit()
            conn.close()
        except Exception as e:
            traceback.print_exc()

脚本主程序

# -*- coding: utf-8 -*-
import codecs
import os
import re
import sys
import platform

from db_utils import DbConfig


class Reader:

    def __init__(self, book_name, txt_path):
        self.book_name = book_name
        self.txt_path = txt_path
        self.db = DbConfig(self.book_name, "./{}.sqlite3".format(self.book_name))
        self.db.initial_table()  # 初始化数据库,用来存放阅读历史
        self.contents = []  # 章节名称
        self.book = self.split_book_chapter()

    def split_book_chapter(self):
        """
        读取文本文件,并将文本按章节划分
        :param txt_path:
        :return:
        """
        book_content = {}
        with codecs.open(self.txt_path, "r", encoding="gbk") as f:
            text = f.read().strip()
            chapters = re.split("\n第", text)
            for chapter in chapters:
                text = chapter.split("\n")
                title = text[0]
                text = "\n".join(text)
                book_content[title] = text
        self.contents = list(book_content.keys())
        return book_content

    def get_index(self, key_name):
        """
        根据书的章节名称,获取前一章与后一章
        :param key_name:
        :return:
        """
        this_index = self.contents.index(key_name)
        last_chapter = self.contents[this_index - 1]
        next_chapter = self.contents[this_index + 1]
        return last_chapter, next_chapter

    def start_read(self, chapter_name=None):
        """
        开始阅读, 章节名称为空,且数据库中没有历史记录时,从第一章开始
        :param chapter_name: 章节名称
        :return:
        """

        last_chapter = self.db.get_params(["last_chapter"])
        # 如果没有历史记录或者指定章节,则是第一次阅读,在数据库中新增
        if not last_chapter:
            self.db.add_one(["last_chapter"], [self.contents[0]])
        if not chapter_name:
            chapter_name = last_chapter[0] if last_chapter else self.contents[0]
        chapter_name = chapter_name[0] if isinstance(chapter_name, tuple) else chapter_name
        read_content = "".join(self.book.get(chapter_name,"当前章节不存在"))
        print(read_content)
        forward = ""
        while forward not in ["n", "b", "q"]:
            forward = input("""n、下一章\nb、上一章\nq、退出""")

        last_chapter_name, next_chapter_name = self.get_index(chapter_name)
        if forward == "q":
            sys.exit(0)
        # 根据命令选择上一章或者下一章,并更新历史记录
        if forward == "n":
            self.db.update_one("last_chapter", next_chapter_name)
            return next_chapter_name
        if forward == "b":
            self.db.update_one("last_chapter", last_chapter_name)
            return last_chapter_name


if __name__ == '__main__':
    mybook = Reader("gmzz", "test.txt")
    chapter_name = None
    while True:
        if platform.system().lower() == "windows":
            os.system('cls')
        if platform.system().lower() == "linux":
            os.system('clear')
        chapter_name = mybook.start_read(chapter_name)



四、效果展示 & 源码(测试数据——诡秘之主)获取

最后运行的效果就如下图所示
在这里插入图片描述

源码+测试数据获取,关注以下公众号,回复0070获取
在这里插入图片描述

【详解Python文件: .py、.ipynb、.pyi、.pyc、​.pyd !】 如果你也想自学Python,可以关注我。我会把踩过的坑分享给你,让你不要踩坑,提高学习速度,还整理出了一套系统的学习路线,这套资料涵盖了诸多学习内容:开发工具,基础视频教程,项目实战源码,51本电子书籍,100道练习题等。相信可以帮助大家在最短的时间内,能达到事半功倍效果,用来复习也是非常不错的。关注我,学习更多python知识!!希望这篇文章对你有帮助,也希望能帮到大家,因为你我都是热爱python的编程语言爱好者。 阅读详情

相关推荐

Python命令行小说阅读器开发实战:从curses界面到摸鱼神器

命令行界面(CLI)是开发者与系统交互的核心工具,其通过文本指令实现高效操作。基于事件驱动的终端UI库如curses,能够在命令行环境中构建丰富的交互式应用,其原理是通过控制终端字符界面实现动态刷新。这种技术价值在于为服务器环境或无图形界面的系统提供可视化交互方案,广泛应用于系统监控、日志查看和文本编辑等场景。结合Python的跨平台特性和文本处理能力,可以开发出如命令行小说阅读器这样的实用工具,实现文件解析、分页显示和搜索导航等核心功能。通过编码自动检测(如chardet库)和伪装模式设计,该工具巧妙解决

weixin_34248487的博客 428

Python实战之小说下载神器(一)看小说怎么能少了这款宝藏神器呢?全网小说书籍随便下,随便看,爆赞(你准备好了吗?)

这次的是一个系列内容给大家讲解一下何一步一步实现一个完整的实战项目案例系列之小说下载神器(GUI界面化程序)我是爱看小说的栗子同学。按照一定的规则, 去采集互联网上面数据。最后,栗子温馨提示,对于看小说。各位小伙伴要取之有度,用之有节吖!万不可沉溺于小说哦!(白天看晚上看熬夜看不可取啦)可能说的就是我 23333。

xy258009的博客 3265

PC端小说阅读神器:电脑小说阅读器实战应用

通过 File API 动态注入:important;⚠️ 注意兼容性问题:- iOS Safari 对外部字体支持较差- Windows/macOS 渲染差异- 安全沙箱限制解决方案:预置开源字体(如思源宋体)、子集化、缓存管理。看完这一整套流程,你会发现——最好的阅读器,不是功能最多的,而是最懂用户的。它知道你在夜里怕蓝光,知道你在上班时想偷偷追更,也知道你不想被各种格式搞得焦头烂额。

weixin_33670640的博客 1027

使用Python看网络小说

本文所述内容及其低级,仅为入门水平,高手勿阅。 时值疫情期间,女友来家竟然惨遭滞留。本人因看网络小说,经常遭遇女友与老妈的双重监管(他们两个商量好了要督促我学习) 被逼无奈,使用Python书写基础功能的看小说工具(仅使用控制台打印),可自动换页与换章。 详情如下: 使用PyCharm+Anaconda3 环境,包缺啥装啥,直接在PyCharm里装,及其简单粗暴。 import tim...

学习日常 3146

python小工具-摸鱼神器-小说阅读器

配置好小说后,运行Python脚本,点击开始测试后即可观看小说,但鼠标移开界面后,文字立刻消失。 查找功能:在查找功能中输入正在看的内容,可直接找到对应行数,点击next直接跳转到下一位置!

宝马项目化流程标准(BMW ABC flyer requirement)

宝马项目化流程标准(BMW ABC flyer requirement)

于大博 7243

Python小工具—摸鱼神器—小说阅读器:轻松愉悦的阅读体验

Python小工具—摸鱼神器—小说阅读器:轻松愉悦的阅读体验 【下载地址】python小工具-摸鱼神器-小说阅读器 这款Python小工具——小说阅读器,专为喜欢在电脑上阅读小说的用户设计,带来轻松愉悦的阅读体验。它支持自动翻页功能,配置好小说文件后,运行脚本即可自动观看。当鼠标移开界面时,文字会立即消失,确保隐私安全。...

gitblog_06736的博客 885

树莓派5使用CSI摄像头 | 树莓派5接上CSI摄像头后命令行操作显示检测无设备

此系列用于记录树莓派5的一些操作,这篇文章讲述树莓派5如何使用CSI摄像头。

beyourself_he的博客 8794

摸鱼神器推荐-透明小说阅读器

配置文件示例(支持JSON/YAML) display: font_size: 14 hotkey: show: Ctrl+Space hide: Alt+H auto_save_interval: 300 # 每5分钟自动存盘。通过Ctrl+鼠标滚轮实时调整窗口透明度,可完美融入IDE、文档等办公界面,实测在VS Code深色主题下设置65%透明度时文字辨识度最佳。近期体验的小说阅读器完美解决这些问题,其「光学隐身」特性堪称职场生存黑科技。▲ 彻底隐藏任务栏图标,进程零痕迹(素材包内示意图)

Jiuannnn的博客 758

统信UOS系统网络服务异常排查:WIFI图标消失与网络设置缺失的解决方案

本文详细解析了统信UOS系统WIFI图标消失与网络设置缺失的常见问题,提供了从基础修复到进阶排查的完整解决方案。重点分析了NetworkManager服务异常的根本原因,包括系统更新中断、权限配置错误等,并给出实用的命令行操作指南和预防措施,帮助用户快速恢复网络功能。

weixin_29234239的博客 613

别再盯着那 100 条数据了,RAP OData 分页机制到底在保护什么

摘要: SAP BTP ABAP Environment 的 OData 服务默认采用分页保护机制,未指定 $top 时仅返回100条数据并附带下一页链接。这种设计旨在防止资源滥用,而非服务故障。客户端应遵循OData协议规范,直接使用服务端返回的 next link 进行分页请求,而非自行构造URL。当指定 $top 参数时,若数值超过5000仍会被强制分页。该机制在SAPUI5控件中被自动处理,但自定义客户端需主动实现分页逻辑。对于自定义实体(Custom Entity),开发者需在Query Prov

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 857

无边框透明.txt小说阅读器——牛马摸鱼神器

透明度调节:用户可以通过左右箭头键轻松调节窗口的透明度,使其更好地融入桌面环境。无标题栏设计:窗口没有传统的标题栏,用户可以通过点击并拖动窗口的任意部分来移动窗口。文件打开功能:支持打开文本文件,并显示文件内容。滚动功能:用户可以通过上下箭头键滚动文本内容。窗口大小调整:用户可以通过拖动右下角的手柄来调整窗口的大小。全屏置顶:窗口始终保持在最前面,方便用户在阅读时进行其他操作。简洁界面:采用深色背景和白色文字,提供舒适的阅读体验。退出快捷键:按下Escape键可以关闭窗口。启动程序。

wanghao312的博客 2529

VSCode PlantUML 插件超详细教程:从安装配置到实战绘图全流程

VSCode PlantUML 插件超详细教程:从安装配置到实战绘图全流程

weixin_69237381的博客 907

【效率工具】隐形小说阅读器:职场摸鱼神器の极致伪装方案

《隐形小说阅读器:职场摸鱼神器》介绍了一款专为职场人士设计的隐蔽阅读工具。该应用通过彻底隐藏任务栏图标、动态透明度调节(0-90%无极调节)和智能显隐逻辑(鼠标/热键触发),实现完美伪装于办公界面。特色功能包括15MB低内存占用、多编码文本支持、本地AES-256加密,以及内置钉钉弹窗等10余种伪装模板。开发者友好设计支持JSON/YAML配置,适合程序员在工作间隙调节大脑状态,同时保持专业形象。该工具解决了传统阅读方式易暴露、效率低等问题。

Jiuannnn的博客 383

【K210+STM32项目】基于K210与STM32协同工作的智能垃圾分类系统设计与实现(更新3.0版本)(完整工程资料源码)

STM32F407STM32F407_2(含有app功能)

嵌入式技术开发 2489

30天入门 SAP BTP 开发系列之四:SAP BTP 控制台里 Global Account 的常规操作介绍

笔者前一篇文章,介绍了 SAP BTP 中 Global Account 的概念。本文咱们一起来看看 SAP BTP 控制台里,给 Global Account 开放了哪些操作功能。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 864

Python命令行小说阅读器:从文件解析到终端分页的完整实现

命令行界面(CLI)程序以其轻量、高效和高度可集成的特性,在开发工具和系统管理领域占据重要地位。其核心原理在于通过文本流与用户交互,无需图形界面开销,能直接调用系统资源。这种技术价值体现在快速启动、低资源占用和易于自动化集成上,常被用于日志查看、数据处理和系统监控等场景。本文聚焦于一个具体应用:构建命令行小说阅读器,其中涉及文件I/O、字符串处理和终端控制等关键技术。通过解析TXT文件、实现终端分页显示和跨平台按键监听,开发者可以深入理解流式读取、ANSI转义序列和状态持久化等概念。该项目不仅是一个实用的阅

weixin_34175509的博客 454

工业质检封神!YOLOv8实战金属表面缺陷检测,自建数据集mAP突破95%

本文详细介绍了基于YOLOv8的金属表面缺陷检测实战方案。针对工业质检中的光照不均、纹理复杂、小样本等挑战,作者构建了12000张图像的数据集,提出针对性数据增强策略和两阶段训练方法。通过优化损失函数、调整超参数等技术手段,最终在测试集上达到95.1%的mAP@0.5,检测速度32FPS。相比传统方法,该方案在精度和鲁棒性上均有显著提升,为工业视觉质检提供了可复现的落地解决方案。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 426

Python命令行小说阅读器开发:从curses交互到进度持久化实战

命令行界面(CLI)是开发者与系统交互的核心方式,其通过文本指令实现高效操作。在Python生态中,curses库提供了强大的终端控制能力,能够实现光标定位、颜色控制和屏幕刷新,为构建丰富的命令行应用奠定了基础。这种技术不仅提升了工具的专业性,还能通过自动化脚本解决重复性工作,具有很高的工程实践价值。在文本处理、系统监控、快速原型开发等场景中尤为实用。本文聚焦于利用curses和sqlite3,开发一个具备翻页、进度保存和书籍管理功能的命令行小说阅读器,其中涉及了curses库的精细控制和sqlite3的轻

weixin_34417183的博客 402

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

【Python】实现一个类似于Thief的摸鱼软件

实用摸鱼小软件,可以悄咪咪地看本地的txt电子书或者爬取有效电子书网站(例如:纵横中文网)的电子书资源,提供设置界面以便修改配置文件。

sinat_33408502的博客 1612

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

上班摸鱼看小说神器-鼠标移动立马隐藏

使用Python写了一个类似电子书阅读器一样的程序,可以读取小说,点击翻页,当鼠标不在程序窗口时,立即最小化窗口。

ccy0_0123的博客 628

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

阅读器推荐

【效率工具】隐形小说阅读器:职场摸鱼神器の极致伪装方案▲ 彻底隐藏任务栏图标,进程零痕迹(素材包内示意图)▲ 托盘图标双重隐匿模式,支持快捷键唤醒(素材包内示意图)

Jiuannnn的博客 532

都在卷?我用企业级AI智能体写了一个“斗破苍穹”伪装阅读器,老板以为我在修改内核

虽然我使用做了一个不太实用的工具,但在这个过程中,我切实深入地了解了AI 智能体(代理)对生产力的变革。以前:我有想法 -> 查PyQt文档 -> 查键盘监听库 -> 写UI -> 调Bug -> 甚至放弃。现在:我有一个想法 -> CodeRider 拆解任务 -> 自动生成 -> 自动纠错 -> 落地。它把从“灵感”到“产品”的历程走了90%。谁能想到,这个设计用来帮助企业保障代码安全、进行自动化研发的,被我拿来写了一个“摸鱼神器”?

D17513377570的博客 7205

给大家推荐一款很棒的电脑上看小说阅读器亲测好用

在你干完活又没事干的时候,偷偷看个小说可调整背景透明度、字体大小和颜色,可以设置鼠标拖动文字消失还有退出快捷键极其小巧精致,可放心地偷偷看小说。4、重点安利部分,鼠标移动到窗口文字就会自动显示,鼠标移开窗口就会自动消失,这真的很适合工位很刁钻的姐妹!官网链接:http://yuedu.chaojizhu.xyz。2、背景可以调成全透明,字体也可以换颜色。3、对话框可以放大缩小调整到所需的尺寸。1、下载很方便,不需要会员。

m0_74999111的博客 49

揭秘,程序员的 10 个高效“充电”神器

本文揭秘了程序员提升效率的10个神器,包括命令行工具、VSCode插件和Python脚本等。重点介绍了如何在碎片时间使用命令行刷LeetCode、通过VSCode隐藏模式学习技术文档,以及利用Python自动化处理技术播客和代码审查。这些工具帮助开发者高效'充电',大幅提升编码能力和知识储备。

weixin_42629815的博客 270
上一篇: Python后端开发效率提升工具推荐(一)
下一篇: 同为消息中间件,Kafka与ActiveMQ的区别对比
Demonslzh6
博客等级 码龄9年 1508粉丝 87原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Demonslzh6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值