bbs项目(部分讲解)

项目开发基本流程

  1. 需求分析
  2. 架构设计
  3. 分组开发
  4. 提交测试
  5. 交付上线

项目分析流程

仿BBS项目:

  • 仿造博客园项目
    • 核心:文章的增删改查
  • 技术栈 DjangoMySQL
  • 功能
    • 注册 (forms校验,页面渲染,上传头像)
    • 登录 (自定义图片验证码)
    • 首页:文章展示、侧边栏过滤(分类,标签,时间)
    • 文章详情:点赞点踩、评论(父评论和子评论)
    • 后台管理:当前用户文章展示(文章增删改查)
    • 发布文章
  • 项目版本信息:python3.8django2.2.2mysql:5.7jquery2.xbootstrap3

项目表设计及关联

创建数据库bbs

create database bbs

表分析

一共需要创建七张表

  1. 用户表(基于auth模块的user表扩写)
  2. 个人站点表(跟用户表一对一关系)
  3. 分类表(和个人站点表一对多、和文章表一对多)
  4. 标签表(和个人站点表一对多、和文章表多对多)
  5. 点赞点踩表(和用户表一对多、和文章表一对多)
  6. 评论表(和用户表一对多,和文章表一对多)
  7. 文章表(和个人站点表一对多)

image

前期准备

创建项目

1.安装django 2.2.2版本

pip3 install django==2.2.2

2.使用pycharm创建django项目

配置setting.py

TEMPLATES = {
"DIRS":[os.path.joi(BASE_DIR, "templates")]
}

配置语言环境

LANGUAGE = 'zh-hans'  # 语言汉化
TIME-ZONE = 'Asia/Shanghai'  # 时区使用上海时区
USE_I18N = True
USE_L10N = True
USE_TZ = False

配置数据库

DATABASES = {
	'default': {
		'ENGINE':'django.db.backends.mysql',
		'NAME': 'bbs',
		'HOST': '127.0.0.1',
		'PORT': 3306,
		'USER': 'root',
		'PASSWORD':'password'
	}
}

在models中写表模型

from django.db import models

# Create your models here.
from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):  # 继承AbstractUser表 只用写auth表中没有的字段
    phone = models.CharField(max_length=32, null=True, verbose_name='用户手机号')
    # upload_to是文件保存在什么路径
    icon = models.FileField(upload_to='icon/', default='icon/default.png', null=True, verbose_name='用户头像')
    # 用户表和博客表一对一
    blog = models.OneToOneField(to='Blog', on_delete=models.CASCADE, null=True)


class Blog(models.Model):
    title = models.CharField(max_length=32, null=True, verbose_name='主标题')
    site_title = models.CharField(max_length=32, null=True, verbose_name='副标题')
    site_style = models.CharField(max_length=64, null=True, verbose_name='站点样式')


class Tag(models.Model):
    name = models.CharField(max_length=32, verbose_name='标签名', null=True)
    # 标签和博客是一对多 一个博客有多个标签
    blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)


class Classify(models.Model):
    name = models.CharField(max_length=32, verbose_name='分类名')
    # 分类和博客是一对多关系 一个博客有多个分类
    blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)


class Article(models.Model):
    title = models.CharField(max_length=32, verbose_name='文章标题')
    desc = models.CharField(max_length=255, verbose_name='文章摘要')
    content = models.TextField(verbose_name='文章内容')
    create_time = models.DateTimeField(auto_now_add=True)  # 第一次创建时自动添加时间
    # 文章和分类表是一对多 一个分类有多篇文章
    classify = models.ForeignKey(to='Classify', on_delete=models.CASCADE)
    # 文章和标签是多对多关系 自动创建第三张表
    tag = models.ManyToManyField(to='Tag')
    # 文章和博客是一对多关系 一个博客对应多篇文章
    blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)


class UpAndDown(models.Model):
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='点赞点踩时间')
    # 和用户表是一对多关系 一个用户可以有多条点赞点踩记录
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)
    # 和文章也是一对多
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
    # 1代表点赞 0代表点踩
    is_up = models.BooleanField(verbose_name='是否点赞')


class Comment(models.Model):
    content = models.CharField(max_length=64, verbose_name='评论内容')
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='评论时间')
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE, null=True)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE, null=True)
    # 自关联字段 只能存已有评论的主键值
    parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
    # 自关联的其他方式
    # parent = models.ForeignKey(to='Comment', on_delete=models.CASCADE)
    # parent = models.IntegerField(null=Ture)

终端执行数据库迁移命令

python38 manage.py makemigretions
python38 manage.py migrater

注册功能

注册forms编写

在根目录下创建blog_forms.py文件

from django import forms
from django.forms import widgets
from blog.models import UserInfo
from django.core.exceptions import ValidationError  # 合法性错误


class User(forms.Form):
    # 用户名 密码 确认密码 邮箱
    username = forms.CharField(max_length=8, min_length=3, label='用户名', required=True,
                               error_messages={'max_length': '用户名最多只能输入8位',
                                               'min_length': '用户名最少输入3位',
                                               'required': '用户名必须填'
                                               },
                               # 添加bootstr样式
                               widget=widgets.TextInput(attrs={'class': 'form-control'})
                               )
    password = forms.CharField(max_length=16, min_length=8, required=True, label='密码',
                               error_messages={
                                   'max_length': '密码最长16位',
                                   'min_length': '密码最短8位',
                                   'required': '密码不能为空',
                               },
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'})
                               )
    re_password = forms.CharField(max_length=16, min_length=8, required=True, label='密码',
                                  error_messages={
                                      'max_length': '密码最长16位',
                                      'min_length': '密码最短8位',
                                      'required': '密码不能为空',
                                  },
                                  widget=widgets.PasswordInput(attrs={'class': 'form-control'})
                                  )
    email = forms.EmailField(label='邮箱地址', widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    # 局部钩子 校验用户名是否存在
    def clean_username(self):
        name = self.cleaned_data.get('username')
        if UserInfo.objects.filter(username=name).first():
            # 用户已存在
            raise ValidationError('用户名已存在')  # 校验错误抛出异常
        else:
            return name

    # 局部钩子 校验用户名是否存在
    # def clean_username(self):
    #     username = self.cleaned_data.get('username')
    #     try:
    #         UserInfo.objects.get(username=username)
    #         print(UserInfo.objects.get(username=username), type(UserInfo.objects.get(username=username)))
    #         raise ValidationError('用户名已存在')
    #     except Exception:
    #         return username

    # 全局钩子 校验两次输入密码是否一致
    def clean(self):
        pwd = self.cleaned_data.get('password')
        re_pwd = self.cleaned_data.get('re_password')
        if pwd != re_pwd:
            raise ValidationError('两次密码不一致')  # 主动抛出合法性错误
        else:
            return self.cleaned_data

路由配置

在项目同名文件夹下的urls.py中配置路由

from django.contrib import admin
from django.urls import path
from blog import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('register/', views.register),
]

编写视图函数

views.py

from django.shortcuts import render
from blog.blog_forms import User


def register(request):
    form_obj = User()
    if request.method == 'GET':  # 当请求为get时返回注册界面,并返回forms组件对象进行数据校验
        return render(request, 'register.html', {'form_obj': form_obj})

前端模板编写

register.html

需要先配置静态文件
	-在setting.py中
		STATICFILES_DIRS = [
			os.path.join(BASE_DIR, 'static')
	] 
	-把bootstrap和jquery导入模板中
<body>
	<div class="container-fluid">
	    <div class="row">
	        <div class="col-md-6 col-md-offset-3">
	            <h1 class="text-center text-info">注册功能</h1>
	            <form action="" id="id_form">
	                {% csrf_token %}
	                {% for foo in form_obj %}
	                    <div class="form-group">
	                        <label for="{{ foo.id_for_label }}">{{ foo.label }}</label>
	                        {{ foo }}
	                        <span class="pull-right text-danger"></span>
	                    </div>
	                {% endfor %}
	                <div class="form-group">
	                    <label for="id_file">头像
	                        <img src="/static/default.png" alt="" height="100px" width="100px" style="margin-right: 20px"
	                             id="id_img">
	                        <input type="file" id="id_file" accept="image/*" style="display: none">
	                    </label>
	                </div>
	                <div class="form-group text-center">
	                    <!--提交按钮不能是submit或者单独的button按钮 如果写了ajax 点击提交 就会发送两次请求-->
	                    <input type="button" value="注册" class="btn btn-success" id="id_submit">
	                    <span class="text-danger error"></span>
	                </div>
	            </form>
	        </div>
	    </div>
	</div>
</body>

头像动态显示

<script>
    // 头像动态显示
    $('#id_file').change(function () {
        // 将上传的头像展示到img标签内 修改img标签内的src参数

        // 读出图片文件 借助于文件阅读器
        let reader = new FileReader()

        // 拿到文件对象
        let file = $('#id_file')[0].files[0]

        // 将文件对象读到文件阅读器中
        reader.readAsDataURL(file)

        // 文件加载完后修改img标签的src参数
        reader.onload = function () {
            // $('#id_img')[0].src=reader.result
            $('#id_img').attr('src', reader.result)  # jquery对象方法
        }
    })
</script>

发送ajax请求

// 发送ajax请求
    $('#id_submit').click(function () {
        let data = new FormData  // 可以传递文件数据

        // 方式一:根据id获取标签数据添加至data中
        
        // data.append('username', $('#id_username').val())
        // data.append('password', $('#id_password').val())
        // data.append('re_password', $('#id_re_password').val())
        // data.append('email', $('#id_email').val())
        // data.append('icon', $('#id_file')[0].files[0])
        // data.append('csrfmiddlewaretoken', $("[name='csrfmiddlewaretoken']").val())
        // ...发送ajax请求


        // 方式二:利用form组件批量处理
        let data_arr = $('#id_form').serializeArray()  // 序列化数组
        console.log(data_arr)  // 是一个数组套对象 对象中k是name v是value 自动添加csrf

        // 使用for循环把数据添加到data对象中
        $.each(data_arr, function (i, v) {
            console.log("index:",i)
            console.log("value:", v)
            console.log("-----------------------")
            data.append(v.name, v.value)
        })

        // 文件需要单独放入
        data.append('icon', $('#id_file')[0].files[0])

        // 使用ajax发送请求
        $.ajax({
            url: '/register/',
            type: 'post',
            data: data,
            processData: false,
            contentType: false,
            success: function (data) {

			}

image

打印结果

image

现在后端可以收到数据 继续写后端

views.py

def register(request):
    form_obj = User()
    if request.method == 'GET':
        return render(request, 'register.html', {'form_obj': form_obj})
    else:  # 当发送post请求
        res = {'code': 100, 'msg': '注册成功'}
        forms_obj = User(data=request.POST)  # forms组件检验
        if forms_obj.is_valid():  # 如果数据全部合法
            register_data = forms_obj.cleaned_data  # 拿出所有的合法数据
            register_data.pop('re_password')  # 弹出二次输入密码 因为用户表中不需要改字段
            if request.FILES.get('icon'):  # 判断是否上传了图片文件
                register_data['icon'] = request.FILES.get('icon')  # 上传了的话就添加进去
            # 一定要用create_user 密码是密文 后面才可以使用auth模块的功能
            UserInfo.objects.create_user(**register_data)  # 将register_data打散保存至数据库
            return JsonResponse(res)  # 注册成功返回信息
        else:  # 弱国数据不是全部合法
            res['code'] = 101
            res['msg'] = '注册失败'
            res['errors'] = forms_obj.errors  # 返回错误信息
            return JsonResponse(res)

前端ajax可以接受到后端返回的json字符串

$.ajax({
     url: '/register/',
     type: 'post',
     data: data,
     processData: false,
     contentType: false,
     success: function (data) {
         console.log(data)
         if (data.code === 100) {
             // 注册成功跳转至登录界面
             location.href = '/login/'
         } else {
             // 在前端渲染出错误信息
             console.log(data)
             $.each(data.errors, function (k, v) {// for循环错误字典
                 if (k === '__all__') {
                     // 全局钩子错误 两次密码不一致
                     $('.error').html(data.errors['__all__'][0])
                 } else {
                     // 其他错误找到相应的input框后的span标签渲染 父类标签加上has-error属性变红
                     $('#id_' + k).next().html(v[0]).parent().addClass('has-error')
                 }
             })
         }
     }
 })

打印结果

image

此时的错误提示信息不会消失 需要绑定一个定时任务

// 定时任务 渲染的错误信息三秒后清除
setTimeout(function () {
    // 把所有的span标签的内容清除 父类中的属性has-error去除
    $('.text-danger').html('').parent().removeClass('has-error')
}, 3000)

校验用户是否存在

需求:当用户输入用户名后鼠标离开用户名框,校验用户名是否存在且不能刷新页面

前端

<script>
	// 后端ajax校验用户名是否存在
    // 前端使用get请求传入用户名
    
    // 绑定一个失去焦点事件
    $('#id_username').blur(function () {
        $.ajax({
            url: '/check_name/?name=' + $('#id_username').val(),
            type: 'get',
            success: function (data) {
                if (data.code === 110) {// 当用户名存在 添加提示信息
                    $('#id_username').next().html(data.msg)
                }else {// 当用户不存在时清除提示信息
                    $('#id_username').next().html('')
                }
            }
        })
    })
</script>

后端

urls.py

path('check_name/', views.check_name),

views.py

def check_name(request):
    # print(request.GET)
    res = {'msg': '用户已存在', 'code': 110}
    name = request.GET.get('name')
    obj = UserInfo.objects.filter(username=name).first()
    if obj:
        return JsonResponse(res)
    else:
        res['code'] = 100
        res['msg'] = '用户不存在'
        return JsonResponse(res)

登录功能

登陆界面搭建

注册成功跳转至/login/,创建login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/jQuery.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.min.css">
    <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center text-info">登录功能</h1>
            <form action="" id="id_form" method="post">
                {% csrf_token %}  <!--跨站请求伪造-->
                <div class="form-group">
                    <label for="id_username">用户名</label>
                    <input type="text" id="id_username" name="username" class="form-control">
                </div>
                <div class="form-group">
                    <label for="id_password">密码</label>
                    <input type="password" id="id_password" name="password" class="form-control">
                </div>
                <div class="row">
                    <div class="col-md-6 form-group">
                        <label for="id_code">验证码</label>
                        <input type="text" id="id_code" class="form-control" name="code">
                    </div>
                    <div class="col-md-6">
                        <img src="/get_code/" alt="" id="id_img" width="350px" height="50px">  <!--去后端获取随机验证码-->
                    </div>
                </div>
                <div class="form-group">
                    <input type="button" value="登录" class="btn btn-block btn-danger" id="id_submit">
                    <div class="text-center">
                        <span  class="text-danger error"></span>
                    </div>
                </div>
            </form>

            <script>
                // 点击验证码图片刷新验证码
                $('#id_img').click(function () {
                    let time = new Date().getTime()
                    console.log(time)
                    // 再次获取随机验证码图片
                    $('#id_img')[0].src = '/get_code/?t=' + time
                })

                // 提交ajax
                $('#id_submit').click(function () {
                	// 将form表单的input标签数据序列化成数组套对象 name value
                    dataArray = $('#id_form').serializeArray()
                    $.ajax({
                        url: '/login/',
                        type: 'post',
                        data: dataArray,
                        success: function (data) {
                            console.log(data)
                            if(data.code===100){
                                location.href = '/'
                            }else {
                                $('.error').html(data.msg)
                            }
                        }
                    })
                })


                // 定时器任务 自动关闭错误提示信息
                let test = function () {
                    $('.error').html('')
                }
                // 可重复关闭
                timer = setInterval(test, 2000)

				//60秒后关闭循环定时任务
                setTimeout(function () {
                    clearTimeout(timer)
                },60*1000)
            </script>
        </div>
    </div>
</div>
</body>
</html>

自定义图片验证码

验证码:字母数字共五位

views.py

from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import random

def get_code(request):
	# 1 生成一张图片 pillow模块
	img = Image.new('RGB', (350, 50), color=(255, 255, 255))
	# 2 生成一个画图对象 将img传入
	draw = ImageDraw.Draw(img)
	# 3 生成字体对象
	font = ImageFont.truetype(font='./static/font/1641263938811335.ttf', size=50)
	# 4 生成随机字符串
	ran_str = ''
	for i in range(5):
		ran_num = str(random.randint(0, 9))
		ran_upper = chr(random.randint(65,90))
		# 去除I和L
		while (ran_upper == 'L' or ran_upper == 'I'):
			ran_upper = chr(random.randint(65,90))
		ran_lower = chr(random.randint(97, 122))
		# 去除i和l
		while (ran_lower == i or ran_lower == l):
			ran_lower = chr(random.randint(97, 122))
		res = random.choice([ran_num , ran_upper, ran_lower])
		# 将生成的随机字符画到图片中
		# fill=get_color 字体颜色也随机
		draw.text(xy=(10 + i * 60, 0), text=res, font=font, fill=get_color())
	# 5 画线
	    for i in range(10):
        draw.line([(random.randint(0, 350), random.randint(0, 50)), (random.randint(0, 350), random.randint(0, 50))],
                  fill=get_color())  # 起点和终点
    # 6 画点
    for i in range(100):
        draw.point((random.randint(0, 350), random.randint(0, 50)), fill=get_color())
	# 7 将图片保存在内存中 BytesIo模块 并返回给前端
	byte_io = BytesIo()
	img.save(fp=byte_io, format='png')
	# 怎样校验前端传过来的验证码?
	# 可以存在session表中 前端访问返回给前端 前端再次访问携带session 后端取出data进行校验
	request.session['code'] = res
	return HttpResponse(byte_io.getvalue())
	


def get_color():
	x, y = 0, 255	
	return (random.randint(x, y), random.randint(x, y), random.randint(x, y))

上面是自定义的图片验证码,也可以使用第三方模块,比如gvcode模块

from gvcode import VFCode

"""
使用方法:
vc = VFCode(
        width=200,                       # 图片宽度
        height=80,                       # 图片高度
        fontsize=50,                     # 字体尺寸
        font_color_values=[
            '#ffffff',
            '#000000',
            '#3e3e3e',
            '#ff1107',
            '#1bff46',
            '#ffbf13',
            '#235aff'
        ],                                # 字体颜色值
        font_background_value='#ffffff',  # 背景颜色值
        draw_dots=False,                  # 是否画干扰点
        dots_width=1,                     # 干扰点宽度
        draw_lines=True,                  # 是否画干扰线
        lines_width=3,                    # 干扰线宽度
        mask=False,                       # 是否使用磨砂效果
        font='arial.ttf'                  # 字体 内置可选字体 arial.ttf calibri.ttf simsun.ttc
    )
    # 验证码类型
    # 自定义验证码
    # vc.generate('abcd')

    # 数字验证码(默认5位)
    # vc.generate_digit()
    # vc.generate_digit(4)

    # 字母验证码(默认5位)
    # vc.generate_alpha()
    # vc.generate_alpha(5)

    # 数字字母混合验证码(默认5位)
    # vc.generate_mix()
    # vc.generate_mix(6)

    # 数字加减验证码(默认加法)
    vc.generate_op()
    # 数字加减验证码(加法)
    # vc.generate_op('+')
    # 数字加减验证码(减法)
    # vc.generate_op('-')

    # 图片字节码
    # print(vc.get_img_bytes())
    # 图片base64编码
    print(vc.get_img_base64())
    # 保存图片
    vc.save()
"""
def get_code(request):
    vc = VFCode(width=350, height=50)
    vc.generate_mix()
    # vc.generate_op()
    print(vc.get_img_base64()[0])
    byte_io = BytesIO()
    vc.save(byte_io, fm='png')
    request.session['code'] = vc.get_img_base64()[0]
    return HttpResponse(byte_io.getvalue())

登陆界面前端发送数据

login.html

<script>

// 提交ajax
$('#id_submit').click(function () {
     let dataArray = $('#id_form').serializeArray()
     console.log(dataArray)
     $.ajax({
         url: '/login/',
         type: 'post',
         data: dataArray,
         success: function (data) {
             console.log(data)
             if(data.code===100){
             	 // 登陆成功 去首页
                 location.href = '/'
             }else {
             	 // 登陆失败 显示错误信息
                 $('.error').html(data.msg)
             }
         }
     })
 })

// 计时器 关闭错误提示
let test = function () {
	$('.error').html('')
}
// 循环执行
timer = setInterval(test, 2000)

//60秒后关闭循环定时任务
setTimeout(function () {
	clearTimeout(timer)
},60*1000)
														            
</script>

登录后端

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    res = {'code': 100, 'msg': '登陆成功'}
    code = request.POST.get('code')
    # 校验验证码
    if request.session.get('code').lower() == code.lower():
        username = request.POST.get('username')
        password = request.POST.get('password')
        
        # 如果认证成功(用户名和密码正确有效),便会返回一个 User 对象。
        obj = authenticate(username=username, password=password)
        if obj:
            return JsonResponse(res)
        res['code'] = 110
        res['msg'] = '用户名或密码错误'
        return JsonResponse(res)
    res['code'] = '120'
    res['msg'] = '验证码错误'
    return JsonResponse(res)
posted @   吴仁耀  阅读(84)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
  1. 1 原来你也在这里 周笔畅
  2. 2 世间美好与你环环相扣 柏松
  3. 3 起风了 吴青峰
  4. 4 极恶都市 夏日入侵企划
极恶都市 - 夏日入侵企划
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 王星

作曲 : 灰鸿啊/皮皮

编曲 : 夏日入侵企画

制作人 : 邢硕

节奏吉他 : 肯尼

主音吉他 : 张伟楠

贝斯 : 皮皮

鼓 : 海鑫

和声 : 邢硕

音效制作 : 邢硕

录音 : 邢硕/夏国兴

混音 : 于昊

特别鸣谢 : 张伟楠

这城市的车流和这地表的颤抖

像一颗石子落入地心之后泛起的温柔

暗涌

河水流过转角她的楼

被梦魇

轻声呓语唤醒身后的幼兽

失效感官焦灼只剩下

麻木愚钝无从感受

共同支撑全都瓦解

只是我们现在都

已忘记到底是

谁隐藏春秋

谁在大雨之后

把旗帜插在最高的楼

过去陈旧的还在坚守

内心已腐朽

摇摇欲坠不停退后

毁灭即拯救

夏日掠夺春秋

结局无法看透

眼看这情节开始变旧

所有的城池已失守

最终无法占有

无眠辗转

伴着人间破碎的旧梦

像繁星

退却后只剩下混沌的夜空

炙热

掩盖风声鹤唳的担忧

把所有失落无助反手推入

无尽的白昼

失效感官焦灼只剩下

麻木愚钝无从感受

共同支撑全都瓦解

只是我们现在都已经忘记到底是

谁隐藏春秋

谁在大雨之后

把旗帜插在最高的楼

过去的陈旧还在坚守

内心已腐朽

摇摇欲坠不停退后

毁灭即拯救

夏日掠夺春秋

结局无法看透

眼看这情节开始变旧

所有的城池早已失守

惶恐难以接受

缠绵往复不肯放手

最终无法占有

谁隐藏春秋

谁在大雨之后

把旗帜插在最高的楼

过去的陈旧还在坚守

内心已腐朽

摇摇欲坠不停退后

毁 灭 即 拯 救

谁掠夺春秋

谁在大雨之后

把旗帜插在最高的楼

过去的陈旧还在坚守

内心已腐朽

摇摇欲坠不停退后

毁灭即拯救

夏日掠夺春秋

结局无法看透

明知城池已失守

缠绵往复不肯放手

最终无法占有

点击右上角即可分享
微信分享提示