代码仓库:https://github.com/changeclass/koa2-weibo

用户数据

  1. 视图路由层

    1
    2
    3
    4
    router.get('/profile', loginRedirect, async (ctx, next) => {
    const { userName } = ctx.session.userInfo
    ctx.redirect(`/profile/${userName}`)
    })

    当访问路由没有用户名时,默认跳转到自己用户名下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    router.get('/profile/:userName', loginRedirect, async (ctx, next) => {
    const { userName: curUserName } = ctx.params
    // 获取微博第一页数据
    // controller
    const result = await getProfileBlogList(curUserName, 0)
    const { isEmpty, blogList, pageSize, pageIndex, count } = result.data
    await ctx.render('profile', {
    blogData: {
    isEmpty,
    blogList,
    pageSize,
    pageIndex,
    count
    },
    userData: {
    userInfo: ctx.session.userInfo,
    fansData: {
    count: 0,
    list: []
    },
    followersData: {
    count: 0,
    list: []
    }
    }
    })
    })
  2. 控制器层

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /**
    * @description: 个人主页
    * @author: 小康
    * @url: https://xiaokang.me
    * @Date: 2020-12-19 09:47:36
    * @LastEditTime: 2020-12-19 09:47:36
    * @LastEditors: 小康
    */

    const { getBlogListByUser } = require('../services/blog')
    const { PAGE_SIZE } = require('../config/constant')
    const { SuccessModel } = require('../model/ResModel')
    /**
    * @author: 小康
    * @url: https://xiaokang.me
    * @param {string} userName 用户名
    * @param {number} pageIndex 当前页面索引 默认0
    * @description: 创建微博
    */
    async function getProfileBlogList(userName, pageIndex = 0) {
    const result = await getBlogListByUser({ userName, pageIndex, PAGE_SIZE })
    const blogList = result.blogList
    return new SuccessModel({
    isEmpty: blogList.length === 0,
    blogList,
    pageSize: PAGE_SIZE,
    pageIndex,
    count: result.count
    })
    }

    module.exports = { getProfileBlogList }
  3. 服务层

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    const { Blog, User } = require('../db/model/index')
    const { formatUser } = require('./_format')
    /**
    * @author: 小康
    * @url: https://xiaokang.me
    * @param {*} userName 用户名
    * @param {*} pageIndex 页面索引
    * @param {*} pageSize 页面大小
    * @description: 根据用户名查询微博
    */
    async function getBlogListByUser({ userName, pageIndex = 0, pageSize = 10 }) {
    // 拼接查询条件
    const userWhereOpts = {}
    if (userName) {
    userWhereOpts.userName = userName
    }
    // 执行查询
    const result = await Blog.findAndCountAll({
    limit: pageSize, // 每页多少条
    offset: pageSize * pageIndex, // 跳过多少条
    order: [['id', 'desc']],
    include: [
    {
    model: User,
    attributes: ['userName', 'nickName', 'picture'],
    where: userWhereOpts
    }
    ]
    })
    // 获取dataValues
    let blogList = result.rows.map((row) => row.dataValues)
    blogList = blogList.map((blogItem) => {
    const user = blogItem.user.dataValues
    blogItem.user = formatUser(user)
    return blogItem
    })
    return {
    count: result.count,
    blogList
    }
    }

格式化时间、用户数据

用户数据主页是判断访问的用户是否存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const { isExist } = require('../../controller/user')

router.get('/profile/:userName', loginRedirect, async (ctx, next) => {
// 已登录用户的信息
const myUserInfo = ctx.session.userInfo
const myUserName = myUserInfo.userName

let curUserInfo
const { userName: curUserName } = ctx.params
const isMe = myUserName === curUserName
if (isMe) {
// 是当前登录用户
curUserInfo = myUserInfo
} else {
// 不是当前登录用户
const existResult = await isExist(curUserName)
if (existResult.errno !== 0) {
// 用户名不存在
return
}
// 用户名存在
curUserInfo = existResult.data
}
// ....
})

格式化时间主要是格式化微博内容,处理在服务层。

  1. blog.js
1
2
3
4
5
6
7
8
9
10
11
12
const { formatUser, formatBlog } = require('./_format')

// 获取dataValues
let blogList = result.rows.map((row) => row.dataValues)

// 格式化
blogList = formatBlog(blogList)
blogList = blogList.map((blogItem) => {
const user = blogItem.user.dataValues
blogItem.user = formatUser(user)
return blogItem
})
  1. _format
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**
* @description: 数据格式化
* @author: 小康
* @url: https://xiaokang.me
* @Date: 2020-12-17 14:32:10
* @LastEditTime: 2020-12-17 14:32:10
* @LastEditors: 小康
*/
const { DEFAULT_PICTURE, REG_FOR_AT_WHO } = require('../config/constant')
const { timeFormat } = require('../utils/dt')
/**
* 格式化头像
* @author 小康
* @date 2020-12-17
* @param {Object} obj 用户对象
* @returns {Object} 处理后的结果
*/
function _formatUserPicture(obj) {
if (obj.picture == null) {
obj.picture = DEFAULT_PICTURE
}
return obj
}

/**
* 格式化用户
* @author 小康
* @date 2020-12-17
* @param {Array|Object} list 用户列表或单个用户对象
* @returns {any}
*/
function formatUser(list) {
if (list == null) {
return list
}
if (list instanceof Array) {
// 数组 用户列表
return list.map(_formatUserPicture)
}
// 单个对象
let result = list
result = _formatUserPicture(result)
return result
}

/**
* 格式化数据的时间
* @param {Object} obj 数据
*/
function _formatDBTime(obj) {
obj.createdAtFormat = timeFormat(obj.createdAt)
obj.updatedAtFormat = timeFormat(obj.updatedAt)
return obj
}

/**
* 格式化微博内容
* @param {Object} obj 微博数据对象
*/
function _formatContent(obj) {
obj.contentFormat = obj.content

// 格式化 @
// from '哈喽 @张三 - zhangsan 你好'
// to '哈喽 <a href="/profile/zhangsan">张三</a> 你好'
obj.contentFormat = obj.contentFormat.replace(
REG_FOR_AT_WHO,
(matchStr, nickName, userName) => {
return `<a href="/profile/${userName}">@${nickName}</a>`
}
)

return obj
}

/**
* 格式化微博信息
* @param {Array|Object} list 微博列表或者单个微博对象
*/
function formatBlog(list) {
if (list == null) {
return list
}

if (list instanceof Array) {
// 数组
return list.map(_formatDBTime).map(_formatContent)
}
// 对象
let result = list
result = _formatDBTime(result)
result = _formatContent(result)
return result
}

module.exports = { formatUser, formatBlog }
  1. dt.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @description:
* @author: 小康
* @url: https://xiaokang.me
* @Date: 2020-12-19 10:54:11
* @LastEditTime: 2020-12-19 10:54:12
* @LastEditors: 小康
*/

const { format } = require('date-fns')

/**
* 格式化时间,如 09.05 23:02
* @param {string} str 时间字符串
*/
function timeFormat(str) {
return format(new Date(str), 'MM.dd HH:mm')
}

module.exports = {
timeFormat
}

加载更多

  1. api路由层

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /**
    * @description: 个人主页 API 路由
    * @author: 小康
    * @url: https://xiaokang.me
    * @Date: 2020-12-19 11:20:02
    * @LastEditTime: 2020-12-19 11:20:02
    * @LastEditors: 小康
    */

    const router = require('koa-router')()

    const { getProfileBlogList } = require('../../controller/blog-profile')
    const { loginCheck } = require('../../middlewares/loginChecks')
    const { getBlogListStr } = require('../../utils/blog')

    router.prefix('/api/profile')

    // 加载更多
    router.get('/loadMore/:userName/:pageIndex', loginCheck, async (ctx, next) => {
    let { userName, pageIndex } = ctx.params
    pageIndex = parseInt(pageIndex)
    const result = await getProfileBlogList(userName, pageIndex)
    result.data.blogListTpl = getBlogListStr(result.data.blogList)
    ctx.body = result
    })

    module.exports = router

  2. 工具方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    /**
    * @description: 微博数据相关的工具方法
    * @author: 小康
    * @url: https://xiaokang.me
    * @Date: 2020-12-19 11:30:22
    * @LastEditTime: 2020-12-19 11:30:22
    * @LastEditors: 小康
    */

    const fs = require('fs')
    const path = require('path')
    const ejs = require('ejs')

    // 获取 blog-list.ejs 文件内容
    const BLOG_LIST_TPL = fs
    .readFileSync(path.join(__dirname, '..', 'views', 'widgets', 'blog-list.ejs'))
    .toString()

    /**
    * @author: 小康
    * @url: https://xiaokang.me
    * @param {Array} blogList 微博列表
    * @param {Boolean} canReply 是否可以回复
    * @description: 根据blogList渲染出HTML字符串
    */
    function getBlogListStr(blogList = [], canReply = false) {
    return ejs.render(BLOG_LIST_TPL, {
    blogList,
    canReply
    })
    }

    module.exports = { getBlogListStr }