Blog中新闻主页开发设计文档

新闻主页

一、功能需求分析

  1. 轮播图
  2. 热门文章列表
  3. 文章标签导航
  4. 文章列表
  5. 分类文章

二、模型设计

这一部分是编写程序,实现功能的==最重要的==一环。如果表设计不好,那么整个软件的架构就会显得非常混乱并且毫无章法,最重要的是到了后期非常难以维护,甚至会导致用户数据丢失。

2.1 表和字段分析

  1. 文章分类表
  2. 文章表
  3. 文章评论表
  4. 热门文章表
  5. 轮播图表

看起来很宏观,好像对表之间的关系无从下手对吧?没关系!我们来上图!

006tNc79gy1g632t6kz6hj31400m9gpk.png

哇看起来还是好复杂!不过还算不错,至少基本整理出了所有的字段和表!后面我们再慢慢地拆分吧!【图有点丑,对不起,手残:cry:】

2.2模型定义

1 基本抽象模型

细心的朋友可能会发现,在这里并没有在其他表中给出创建时间和更改时间,这怎么行呢?!可是如果到每一个需要创建时间和更新时间的表中去创建字段又显得太过冗余,所以!我们需要自定义一个抽象模型类。这种抽象模型类在django执行与数据库的映射时并不会真正的创建对应的表,但是可以在模型设计的时候被继承,以减少模型字段的冗余。DjangoORM真是非常贴心啦!

其次,这里还设置了一个is_delete字段,用于表示模型是否可以删除【并不是所有数据都是想删就删的】

话不多说,我们来创建

# 在utils目录下,创建一个models.py文件,在其中定义一个基类模型
from django.db import models


class BaseModel(models.Model):
    """
    基类,公共字段用于去除冗余
    """
    create_time = models.DateTimeField('创建时间', auto_now_add=True)
    update_time = models.DateTimeField('更新时间', auto_now=True)
    is_delete = models.BooleanField('逻辑删除', default=False)#这个字段用于定义表是否会逻辑删除

    class Meta:
        # 抽象类,用于继承,迁移时不会创建
        abstract = True

2 其它模型建立

0.Notes!

在设置字段时,有三个值得注意的点:

  • ==图片==:一般我们不建议在数据库中直接存储图片,首先因为需要将图片转换成二进制格式,其次是图片的数据非常大【对于存入数据库】,所以一般我们都采用url的方式存入图片.

  • 字段中定义一个名为 ==‘Meta‘==的源信息类,其中包含了

    • db_name:即你要在数据库中创建的表名,实际上Django为了节省开发者的时间,会根据app的名字自动在数据库中创建用下划线隔开的表名,类似假如你的app名字是myapp,其中的模型类名叫mymodel,那么django对于==mymode==模型类相应创建的名字就是tb_myapp_mymodel ,这样虽然方便,但是名字有些过于长了,所以如果你想要==自己创建表名==,就在class Meta中采用db_table = “你想要的表名” 来设置,参见官方文档[https://docs.djangoproject.com/en/2.2/ref/models/options/]
    • ordering:设定字段查询返回时的顺序,如果是逆序,则在字段名前加一个‘-’号即可,也可以设立两个字段,在前一字段相同时,按照第二个字段的顺序来排序,与mysql有些类似,这个会涉及到时间成本上的耗费,因为使用了这个操作的模型类,在查询时返回的的外键也会按照设定的order顺序返回,具体的说明也请参看官方文档[https://docs.djangoproject.com/en/2.2/ref/models/options/]
    • verbose_name和verbose_name_plural 这两个都是为模型设立一个简单易读的名字,前者是单数,后者是复数,如果后者没有设立那么,假如verbose_name = 'story',Django自动给它设置的复数名即为==‘storys’==而不是大家所知道的==‘stories’==
  • ____==str==____方法: 我们在查询对象时,一般希望输出的是字符串,而不是一堆无意义的数字和乱吗,此时就需要用到这个方法

1. 分类表
from django.db import models
from utils.models import BaseModel

class Tag(BaseModel):
#文章分类表
  name = models.CharField('标签名', max_length=64, help_text='标签名')
  class Meta:#源信息
    ordering = ['-update_time', '-id']      # 排序
    db_table = "tb_tag"                     # 数据库中建立表名
    verbose_name = "文章标签"                # 在admin站点中显示的名称
    verbose_name_plural = verbose_name      # 显示的复数名称,因为是中文,所以复数名和非复数名无关

def __str__(self):
    return self.name
2. 文章表模型

在创建之前,我们需要回到之前的那个表关系图,理一理关于文章的表关系:

  • 一个分类表中可以有多篇文章,一篇文章仅有一个分类【一对多】
  • 一个作者可以发布多篇文章,一篇文章仅有一个作者【一对多】
class News(BaseModel):
    """
    文章模型
    """
    title = models.CharField('标题', max_length=150, help_text='标题')
    digest = models.CharField('摘要', max_length=200, help_text='摘要')
    content = models.TextField('内容', help_text='内容')
    clicks = models.IntegerField('点击量', default=0, help_text='点击量')
    image_url = models.URLField('图片url', default='', help_text='图片url')
    
    tag = models.ForeignKey('Tag', on_delete=models.SET_NULL, null=True)    
    author = models.ForeignKey('user.User', on_delete=models.SET_NULL, null=True)

class Meta:
    ordering = ['-update_time', '-id']  # 排序
    db_table = "tb_news"  # 指明数据库表名
    verbose_name = "新闻"  # 在admin站点中显示的名称
    verbose_name_plural = verbose_name  # 显示的复数名称

def __str__(self):
    return self.title
3.评论表

表关系:

  • 一篇文章下可以有多条评论,评论只能是一篇文章的【一对多】
  • 一个用户可以发出多条评论,一条评论只能来自于一个用户【一对多】
  • 一条评论下可以有多条子评论【多对多】//
class Comments(BaseModel):
    """
    评论模型
    """
    content = models.TextField('内容', help_text='内容')
    
    author = models.ForeignKey('user.User', on_delete=models.SET_NULL, null=True)
    news = models.ForeignKey('News', on_delete=models.CASCADE)
    
    class Meta:
    ordering = ['-update_time', '-id']  # 排序
    db_table = "tb_comments"  # 指明数据库表名
    verbose_name = "评论"  # 在admin站点中显示的名称
    verbose_name_plural = verbose_name  # 显示的复数名称

def __str__(self):
    return '<评论{}>'.format(self.id)
4.热门文章模型

要求:

  • 按照优先度对文章热度进行排序【点击量、发布时间】
  • 只取前三
class HotNews(BaseModel):
    """
    热门文章表
    """
    priority = models.IntegerField('优先级', help_text='优先级')
    news = models.OneToOneField('News', on_delete=models.CASCADE)
    
    class Meta:
    ordering = ['-update_time', '-id']  # 排序
    db_table = "tb_hotnews"  # 指明数据库表名
    verbose_name = "热门新闻"  # 在admin站点中显示的名称
    verbose_name_plural = verbose_name  # 显示的复数名称

def __str__(self):
    return '<热门新闻{}>'.format(self.id)
5. 轮播图表

要求:

  • 按照内部优先顺序对文章排序
  • 可删除

表关系:

  • 与文章一一对应
class Banner(BaseModel):
    """
    轮播图
    """
    image_url = models.URLField('轮播图url', help_text='轮播图url')
    priority = models.IntegerField('优先级', help_text='优先级')
    
    news = models.OneToOneField('News', on_delete=models.CASCADE)

    class Meta:
        ordering = ['priority', '-update_time', '-id']  # 排序
        db_table = "tb_banner"  # 指明数据库表名
        verbose_name = "轮播图"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    def __str__(self):
        return '<轮播图{}>'.format(self.id)
    

三、文章标签导航

1、接口设计

类目 说明
请求方式 GET
url /news/index/
传入参数 无参数
  • 返回参数

    返回新闻页面,只需要在模版渲染即可

2、后端

1. 说明:

  • only方法:只拿取only中列出的字段。与之类似的是defer,是除了defer中列出的字段,其余都拿。
  • 是否可以逻辑删除:否,因为文章标签删除之后,我们并不希望文章删除

2. 代码:

def index(request):
    '''
    index of news
    新闻首页视图
    url: '/news/index/'
    :param request:
    :return:
    '''
    #新闻标签
    tags = Tag.objects.only('id','name').filter(is_delete = False)

    context = {
        'tags':tags,
    }

    return render(request,'news/index.html',context=context)

3、前端

# 修改templates/news/index.html中 news-nav部分代码如:
        <!--  news-nav start-->
          <nav class="news-nav">
              <ul class="clearfix">
                  <li class="active"><a href="javascript:void(0)">最新资讯</a></li>
        //迭代出所有标签
                    {% for tag in tags %}
                        <li><a href="javascript:void(0)" data-id="{{ tag.id }}">{{ tag.name }}</a>
                  </li>
                    {% endfor %}
              </ul>
          </nav>
        <!--  news-nav end -->

四、新闻列表功能

1、业务流程分析

  • 判断前端传递的标签

2、接口设计

类目 说明
请求方式 GET
url /news/list/
参数格式 查询参数
  • 参数说明

    参数说明 类型 是否必须 描述
    tag integrity 标签分类id
    page integrity 当前文章页数
  • 返回结果

    {
        "errno": "0", 
      "errmsg": "", 
        "data": {
            "total_pages": 61,
            "news": [
                {
                    "digest": "在python用import或者from...import或者from...import...as...来导入相应的模块,作用和使用方法与C语言的include头文件类似。其实就是引入...",
                    "title": "import方法引入模块详解",
                    "author": "python",
                    "image_url": "/media/jichujiaochen.jpeg",
                    "tag_name": "Python基础",
                    "update_time": "2018年12月17日 14:48"
                },
                {
                    "digest": "如果你原来是一个php程序员,你对于php函数非常了解(PS:站长原来就是一个php程序员),但是现在由于工作或者其他原因要学习python,但是p...",
                    "title": "给曾经是phper的程序员推荐个学习网站",
                    "author": "python",
                    "image_url": "/media/jichujiaochen.jpeg",
                    "tag_name": "Python基础",
                    "update_time": "2018年12月17日 14:48"
                }
            ]
        }
    }
    

3、后端

这里采用了一个在查询较大量的数据时,比较高效率的操作:==select_related==,此操作查询数据会把相关的所有外键字段全部放到临时内存中存好,也就是牺牲了一部分的空间,但优点是节省了一次次去数据库中拿数据的时间,当拿的数据较多时,建议采用这种“空间换时间”的操作。当然!也需要你有空间啦:dog:

class NewListview(View):
    '''
    新闻列表视图
    url:'/news/list/'
    参数:tag,page
    '''

    def get(self, request):
        #1、得到文章类别的id
        try:
            # 这里强转int有可能会报错,所以try一下
            tag_id = int(request.GET.get('tag', 0))
        except Exception as e:
            logger.error('标签错误:\n{}'.format(e))
            tag_id = 0
        #2、得到页数,与tag同理
        try:
            page = int(request.GET.get('page', 0))
        except Exception as e:
            logger.error('页码错误:\n{}'.format(e))
            page = 1

        #3、得到新闻的相关字段
        #这里采用annonate【意为注释,类似于数据库中的as】
        news_queryset = News.objects.values('id', 'title', 'digest', 'image_url', 'update_time').   annotate(
            tag_name=F('tag__name'), author=F('author__username'))

        #后一个条件是,假如没有tag_id
        news = news_queryset.filter(is_delete=False, tag_id=tag_id) or news_queryset.filter(is_delete=False)

        #4、希望对新闻进行分页操作,导入一个paginator
        paginator = Paginator(news, constants.PERPAGE_NEWS_COUNT)

        #5 获取页面数据,get_page可以容错
        news_info = paginator.get_page(page)#如果传入的page不存在,也可以返回一个合法的页面

        data = {
            'total_pages': paginator.num_pages,
            'news': list(news_info)
        }
        return Json_response(data=data)

因为在这里,Json_response【这是自定义的,与原生的JsonResponse不同,并非编者写错】并不能返回我们熟知的时间,所以我们要自定义一个序列化器

# json编码器
#放在一个固定的py文件中,那个文件用于定义返回数据格式,然后再采用import方式导入
# 自定义序列化器,处理时间字段
class MyJSONEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, datetime.datetime):
            return o.astimezone().strftime('%Y-%m-%d %H:%M:%S')  # 转换为本地时间


def json_response(errno=Code.OK, errmsg='', data=None, kwargs=None):
    json_dict = {
        'errno': errno,
        'errmsg': errmsg,
        'data': data
    }
    if kwargs and isinstance(kwargs, dict) :
        json_dict.update(kwargs)

    return JsonResponse(json_dict, encoder=MyJSONEncoder)

4、前端

<!-- news-contain start 清空 ul中的内容 -->
<div class="news-contain">
    <ul class="news-list">



    </ul>
</div>
<!-- news-contain end -->

采用js

// static/js/news/index.js
$(function () {
    // 新闻列表
    let $newNavLi = $('.news-nav ul li');   // 标签li
    let iPage = 1;                          // 默认第一页
    let iTotalPage = 1;                     // 默认总页数为1
    let iCurrentTagId = 0;                  // 默认分类标签为0
    let bIsLoadData = true;                 // 是否正在向后台加载数据
    
    fn_load_content();

    // 点击分类标签
    $newNavLi.click(function () {
        // 点击分类标签,则为点击的标签加上一个active的class属性
        // 并移除其他兄弟元素上的active的class属性
        $(this).addClass('active').siblings('li').removeClass('active');
        // 获取绑定在data-id属性上的tag_id
        let iClickTagId = $(this).children('a').attr('data-id');
        if (iClickTagId !== iCurrentTagId){
            iCurrentTagId = iClickTagId;  // 记录当前分类id
            // 重置分页参数
            iPage = 1;
            iTotalPage = 1;
            fn_load_content()
        }

    });

    // 页面滚动加载
    $(window).scroll(function () {
       // 浏览器窗口高度
        let showHeigtht = $(window).height();
       // 整个网页高度
        let pageHeight = $(document).height();
        //页面可以滚动的距离
        let canScrollHeight = pageHeight - showHeigtht;
        // 页面滚动了多少, 整个是随着页面滚动实时变化的
        let nowScroll = $(document).scrollTop();
        if ((canScrollHeight - nowScroll) < 100){
            if(!bIsLoadData){
                bIsLoadData = true;
                //判断页数,去更新新闻,小于总数才加载
                if(iPage < iTotalPage){
                    iPage += 1;
                    fn_load_content();

                }else {
                    message.showInfo('已全部加载,没有更多内容!');
                    $('a.btn-more').html('已全部加载,没有更多内容!')
                }

            }
        }
    });

    // 向后端获取新闻列表数据,ur;与之对应
    function fn_load_content() {
        $.ajax({
            url: '/news/list/',
            type: 'GET',
            data:{
                tag: iCurrentTagId,
                page: iPage
            },
            dataType: 'json',
            success: function (res) {
                if(res.errno === '0'){
                    iTotalPage = res.data.total_pages;
                    if(iPage === 1){
                        // 第一页清空内容
                        $('.news-list').html('')
                    }
                    res.data.news.forEach(function (one_news) {
                        let content = `                  <li class="news-item">
                      <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"
                         target="_blank">
                          <img src="${one_news.image_url}" alt="${one_news.title}"
                               title="${one_news.title}">
                      </a>
                      <div class="news-content">
                          <h4 class="news-title"><a
                                  href="#">${one_news.title}</a>
                          </h4>
                          <p class="news-details">${one_news.digest}</p>
                          <div class="news-other">
                              <span class="news-type">${one_news.tag_name}</span>
                              <span class="news-time">${one_news.update_time}</span>
                              <span class="news-author">${one_news.author}</span>
                          </div>
                      </div>
                  </li>`;
                        $('.news-list').append(content);
                    });
                  // $('.news-list').append($('<a href="javascript:void(0);" class="btn-more">滚动加载更多</a>'));
                  //数据加载完毕,设置正在加载数据变量为false,表示当前没有加载数据
                  bIsLoadData = false;
                  $('a.btn-more').html('滚动加载更多')
                }else {
                    // 加载失败,打印错误信息
                    message.showError(res.errmsg)
                }
            },
            error: function () {
                message.showError('服务器超时,请重试!')
            }
        });
    }
});

五、热门新闻功能

1、接口设计

类目 说明
请求方式 GET
url /news/index/
参数格式 无参数

功能比较简单,直接返回新闻页面,采用模版渲染的方式

2、后端

同样地,采用select_related,为防止硬编码,我们设置一个显示热门新闻条数的变量存入constants.py中,命名为HOTNEWS_SHOW_COUNT = 3

def index(request):
    '''
    index of news
    新闻首页视图
    url:/news/index/
    :param request:
    :return:
    '''
    #新闻标签
    tags = Tag.objects.only('id','name').filter(is_delete = False)

    #热门新闻
    hotnews = HotNews.objects.select_related('news').only('news__title','news__image_url','news_id')\
        .filter(is_delete = False).order_by('priority','-news__clicks')[:constants.HOTNEWS_SHOW_COUNT]
    context = {
        'tags':tags,
        'hotnews':hotnews
    }

    return render(request,'news/index.html',context=context)

3、前端

<!-- 修改templates/news/index.html -->
<ul class="recommend-news">
  //迭代出hotnews
    {% for item in hot_news %}
    <li>
        <a href="https://www.shiguangkey.com/course/2432" target="_blank">
            <div class="recommend-thumbnail">
                <img src="{{ item.news.image_url }}" alt="title">
            </div>
            <p class="info">{{ item.news.title }}</p>
        </a>
    </li>
    {% endfor %}
</ul>
<!-- recommend-news end -->

六、轮播图功能

1、接口设计

类目 说明
请求方式 GET
url /news/bannner/
参数格式 无参数

返回结果

{
    "errno": "0", 
    "errmsg": "OK", 
    "data": {
        "banners": [
            {
                'image_url': '/media/jichujiaochen.jpeg',
                'news_id': 221,
                'news_title': "python 算法快速排序"
            },
            {
                "image_url": "/media/python_advanced.jpg",
                "news_id": 707,
                "news_title": "Python 序列与映射的解包操作"
            }
        ]
    }
}

2、后端

1 说明:
  • annotate: 这里使用到annotate函数,此函数会在返回的对象中给相应的字段加上别名,此时在返回的数据集中,可以采用在annotate中声明的别名来查询数据,想看更多的细节,请参考官方文档[https://docs.djangoproject.com/en/2.2/topics/db/queries/]
  • constants.BANNER_SHOW_COUNT: 如前所说,是一个常量
2 代码
class NewsBannerView(View):
    '''
    轮播图视图
    显示constants。BANNNER_SHOW_COUNT = 6条轮播图
    与文章形成一对一关系
    url:/news/banner/
    '''
    def get(self,request):
        banners = Banner.objects.values('image_url', 'news_id').annotate(
            news_title=F('news__title')
        ).filter(is_delete=False)[:constants.BANNER_SHOW_COUNT]
        data = {
            'banners': list(banners)
        }
        return Json_response(data=data)

3、前端

html页面的代码

<!-- 修改templates/news/index.html中banner部分的代码如下 -->
<!-- banner start -->
        <div class="banner">
            <ul class="pic">
                <!--淡入淡出banner-->

            </ul>
            <a href="javascript:void(0);" class="btn prev">
                <i class="PyWhich py-arrow-left"></i></a>
            <a href="javascript:void(0);" class="btn next">
                <i class="PyWhich py-arrow-right"></i></a>
            <ul class="tab">
                <!-- 按钮数量必须和图片一致 -->

            </ul>
        </div>
      <!-- banner end -->

js代码


(function () { // 新闻列表 letnewNavLi = $('.news-nav ul li'); // 标签li
let iPage = 1; // 默认第一页
let iTotalPage = 1; // 默认总页数为1
let iCurrentTagId = 0; // 默认分类标签为0
let bIsLoadData = true; // 是否正在向后台加载数据

fn_load_content();

// 点击分类标签
$newNavLi.click(function () {
    // 点击分类标签,则为点击的标签加上一个active的class属性
    // 并移除其他兄弟元素上的active的class属性
    $(this).addClass('active').siblings('li').removeClass('active');
    // 获取绑定在data-id属性上的tag_id
    let iClickTagId = $(this).children('a').attr('data-id');
    if (iClickTagId !== iCurrentTagId){
        iCurrentTagId = iClickTagId;  // 记录当前分类id
        // 重置分页参数
        iPage = 1;
        iTotalPage = 1;
        fn_load_content()
    }

});

// 页面滚动加载
$(window).scroll(function () {
   // 浏览器窗口高度
    let showHeigtht = $(window).height();
   // 整个网页高度
    let pageHeight = $(document).height();
    //页面可以滚动的距离
    let canScrollHeight = pageHeight - showHeigtht;
    // 页面滚动了多少, 整个是随着页面滚动实时变化的
    let nowScroll = $(document).scrollTop();
    if ((canScrollHeight - nowScroll) < 100){
        if(!bIsLoadData){
            bIsLoadData = true;
            //判断页数,去更新新闻,小于总数才加载
            if(iPage < iTotalPage){
                iPage += 1;
                fn_load_content();

            }else {
                message.showInfo('已全部加载,没有更多内容!');
                $('a.btn-more').html('已全部加载,没有更多内容!')
            }

        }
    }
});

// 向后端获取新闻列表数据
function fn_load_content() {
    $.ajax({
        url: '/news/',
        type: 'GET',
        data:{
            tag: iCurrentTagId,
            page: iPage
        },
        dataType: 'json',
        success: function (res) {
            if(res.errno === '0'){
                iTotalPage = res.data.total_pages;
                if(iPage === 1){
                    // 第一页清空内容
                    $('.news-list').html('')
                }
                res.data.news.forEach(function (one_news) {
                    let content = `                  <li class="news-item">
                  <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"
                     target="_blank">
                      <img src="${one_news.image_url}" alt="${one_news.title}"
                           title="${one_news.title}">
                  </a>
                  <div class="news-content">
                      <h4 class="news-title"><a
                              href="#">${one_news.title}</a>
                      </h4>
                      <p class="news-details">${one_news.digest}</p>
                      <div class="news-other">
                          <span class="news-type">${one_news.tag_name}</span>
                          <span class="news-time">${one_news.update_time}</span>
                          <span class="news-author">${one_news.author}</span>
                      </div>
                  </div>
              </li>`;
                    $('.news-list').append(content);
                });
              // $('.news-list').append($('<a href="javascript:void(0);" class="btn-more">滚动加载更多</a>'));
              //数据加载完毕,设置正在加载数据变量为false,表示当前没有加载数据
              bIsLoadData = false;
              $('a.btn-more').html('滚动加载更多')
            }else {
                // 加载失败,打印错误信息
                message.showError(res.errmsg)
            }
        },
        error: function () {
            message.showError('服务器超时,请重试!')
        }
    })

}

// 新闻轮播图功能
fn_load_banner();                   // 先加载banner

let $banner = $('.banner');         // banner容器div
let $picLi = $('.banner .pic li');  // 图片li标签
let $pre = $('.banner .prev');      // 上一张
let $next = $('.banner .next');     // 下一张
let $tabLi = $('.banner .tab li');  // 按钮
let index = 0;                      // 当前索引

// 导航小圆点
$tabLi.click(function () {
    index = $(this).index();
    $(this).addClass('active').siblings('li').removeClass('active');//点击小圆点,小圆点颜色变化
    $picLi.eq(index).fadeIn(1500).siblings('li').fadeOut(1500);
});

// 点击切换上一张
$pre.click(()=> {
    index --;
    if(index<0){
        index = $tabLi.length - 1       // 最后一张
    }
    $tabLi.eq(index).addClass('active').siblings('li').removeClass('active');
    $picLi.eq(index).fadeIn(1500).siblings('li').fadeOut(1500);
});

// 点击切换下一张
$next.click(()=>{
    auto();
});

// 图片向前滑动
function auto() {
    index ++;
    index %= $tabLi.length;
    $tabLi.eq(index).addClass('active').siblings('li').removeClass('active');
    $picLi.eq(index).fadeIn(1500).siblings('li').fadeOut(1500)

}
// 定时器
let timer = setInterval(auto, 2500);
$banner.hover(
    ()=>{
        clearInterval(timer)
    },
    ()=>{
        timer = setInterval(auto, 2500);
    }
);
// 定义向后端获取banner
});

七、联系方式

邮箱:psywency@foxmail.com

[Wency03lk@outlook.com]

码云仓库:https://gitee.com/Wencyyyyyy/SeniorDjprj

简书:wencyyyyyy

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容