# 用户管理
# 建立用户模型
const seq = require('../seq')
const { STRING, DECIMAL } = require('../types')
// users
const User = seq.define('user', {
userName: {
type: STRING,
allowNull: false,
unique: true,
comment: '用户名,唯一'
},
password: {
type: STRING,
allowNull: false,
comment: '密码'
},
nickName: {
type: STRING,
allowNull: false,
comment: '昵称'
},
gender: {
type: DECIMAL,
allowNull: false,
defaultValue: 3,
comment: '性别(1 男性,2 女性,3 保密)'
},
picture: {
type: STRING,
comment: '头像,图片地址'
},
city: {
type: STRING,
comment: '城市'
}
})
module.exports = User
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
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
# 用户注册
# 添加用户注册view
const router = require("koa-router")()
/**
* 获取登录信息
* @param {Object} ctx ctx
*/
function getLoginInfo(ctx) {
let data = {
isLogin: false // 默认未登录
}
const userInfo = ctx.session.userInfo
if (userInfo) {
data = {
isLogin: true,
userName: userInfo.userName
}
}
return data
}
router.get('/login', async (ctx, next) => {
await ctx.render('login', getLoginInfo(ctx))
})
router.get('/register', async (ctx, next) => {
await ctx.render('register', getLoginInfo(ctx))
})
module.exports = router
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
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
# 添加用户注册api
注册接口
用户名是否存在接口
const router = require("koa-router")()
const { isExist, register } = require("../../controller/user")
router.prefix("/api/user")
//注册路由
router.post('/register', async (ctx, next) => {
const { gender, password, userName } = ctx.request.body
ctx.body = await register({
userName,
password,
gender
})
})
// 用户名是否存在
router.post('/isExist', async (ctx, next) => {
const { userName } = ctx.request.body
ctx.body = await isExist(userName)
})
module.exports = router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 添加用户注册controller
const {
getUserInfo,
createUser
} = require('../services/user')
const { SuccessModel, ErrorModel } = require('../model/ResModel')
const {
registerUserNameNotExistInfo
} = require('../model/ErrorInfo')
/**
* 用户名是否存在
* @param {string} userName 用户名
*/
async function isExist(userName) {
const userInfo = await getUserInfo(userName)
if (userInfo) {
//用户名已存在
return new SuccessModel(userInfo)
} else {
//用户名不存在
return new ErrorModel(registerUserNameNotExistInfo)
}
}
/**
* 注册
* @param {string} userName 用户名
* @param {string} password 密码
* @param {number} gender 性别(1 男,2 女,3 保密)
*/
async function register({ userName, password, gender }) {
const userInfo = await getUserInfo(userName)
if (userInfo) {
// 用户名已存在
return new ErrorModel(registerUserNameExistInfo)
}
try {
await createUser({
userName,
password: password, //doCrypto(password),
gender
})
return new SuccessModel()
} catch (ex) {
console.error(ex.message, ex.stack)
return new ErrorModel(registerFailInfo)
}
}
module.exports = {
isExist, register
}
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
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
# 添加用户注册services
const { User } = require('../db/model/index')
const { formatUser } = require('./_format')
/**
* 获取用户信息
* @param {string} userName 用户名
* @param {string} password 密码
*/
async function getUserInfo(userName, password) {
//查询条件
const whereOpt = {
userName
}
if (password) {
Object.assign(whereOpt, { password })
}
//查询
const result = await User.findOne({
attributes: ['id', 'userName', 'nickName', 'picture', 'city'],
where: whereOpt
})
if (result == null) {
//没有找到
return result
}
return formatUser(result)
}
/**
* 创建用户
* @param {string} userName 用户名
* @param {string} password 密码
* @param {number} gender 性别
* @param {string} nickName 昵称
*/
async function createUser({ userName, password, gender = 3, nickName }) {
const result = await User.create({
userName,
password,
nickName: nickName ? nickName : userName,
gender
})
const data = result.dataValues
return data
}
module.exports = {
getUserInfo,
createUser
}
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
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
# 用户登陆
# 抽离用户登陆校验中间件
const { ErrorModel } = require('../model/ResModel')
const { loginCheckFailInfo } = require('../model/ErrorInfo')
/**
* API 登录验证
* @param {Object} ctx ctx
* @param {function} next next
*/
async function loginCheck(ctx, next) {
if (ctx.session && ctx.session.userInfo) {
// 已登录
await next()
return
}
// 未登录
ctx.body = new ErrorModel(loginCheckFailInfo)
}
/**
* 页面登录验证
* @param {Object} ctx ctx
* @param {function} next next
*/
async function loginRedirect(ctx, next) {
if (ctx.session && ctx.session.userInfo) {
// 已登录
await next()
return
}
// 未登录
const curUrl = ctx.url
ctx.redirect('/login?url=' + encodeURIComponent(curUrl))
}
module.exports = {
loginCheck,
loginRedirect
}
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
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
# 用户登陆&删除api
// 登录
router.post('/login', async (ctx, next) => {
const { userName, password } = ctx.request.body
ctx.body = await login(ctx, userName, password)
})
// 删除
router.post('/delete', loginCheck, async (ctx, next) => {
if (isTest) {
// 测试环境下,测试账号登录之后,删除自己
const { userName } = ctx.session.userInfo
ctx.body = await deleteCurUser(userName)
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 用户登陆controller
/**
* 登录
* @param {Object} ctx koa2 ctx
* @param {string} userName 用户名
* @param {string} password 密码
*/
async function login(ctx, userName, password) {
// 获取用户信息
const userInfo = await getUserInfo(userName, doCrypto(password))
if (!userInfo) {
// 登录失败
return new ErrorModel(loginFailInfo)
}
// 登录成功
if (ctx.session.userInfo == null) {
ctx.session.userInfo = userInfo
}
return new SuccessModel()
}
/**
* 删除当前用户
* @param {string} userName 用户名
*/
async function deleteCurUser(userName) {
const result = await deleteUser(userName)
if (result) {
// 成功
return new SuccessModel()
}
// 失败
return new ErrorModel(deleteUserFailInfo)
}
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
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
# 用户删除 services
/**
* 删除用户
* @param {string} userName 用户名
*/
async function deleteUser(userName) {
const result = await User.destroy({
where: {
userName
}
})
// result 删除的行数
return result > 0
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
我们的services 层尽量往表的增删改查上靠
# 用户信息修改
# api
// 修改个人信息
router.patch('/changeInfo', loginCheck, genValidator(userValidate), async (ctx, next) => {
const { nickName, city, picture } = ctx.request.body
ctx.body = await changeInfo(ctx, { nickName, city, picture })
})
// 修改密码
router.patch('/changePassword', loginCheck, genValidator(userValidate), async (ctx, next) => {
const { password, newPassword } = ctx.request.body
const { userName } = ctx.session.userInfo
const data = await changePassword(userName, password, newPassword)
ctx.body = data
})
// 退出登录
router.post('/logout', loginCheck, async (ctx, next) => {
ctx.body = await logout(ctx)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# controller
/**
* 修改个人信息
* @param {Object} ctx ctx
* @param {string} nickName 昵称
* @param {string} city 城市
* @param {string} picture 头像
*/
async function changeInfo(ctx, { nickName, city, picture }) {
const { userName } = ctx.session.userInfo
if (!nickName) {
nickName = userName
}
const result = await updateUser(
{
newNickName: nickName,
newCity: city,
newPicture: picture
},
{ userName }
)
if (result) {
// 执行成功
Object.assign(ctx.session.userInfo, {
nickName,
city,
picture
})
// 返回
return new SuccessModel()
}
// 失败
return new ErrorModel(changeInfoFailInfo)
}
/**
* 修改密码
* @param {string} userName 用户名
* @param {string} password 当前密码
* @param {string} newPassword 新密码
*/
async function changePassword(userName, password, newPassword) {
const result = await updateUser(
{
newPassword: doCrypto(newPassword)
},
{
userName,
password: doCrypto(password)
}
)
if (result) {
// 成功
return new SuccessModel()
}
// 失败
return new ErrorModel(changePasswordFailInfo)
}
/**
* 退出登录
* @param {Object} ctx ctx
*/
async function logout(ctx) {
delete ctx.session.userInfo
return new SuccessModel()
}
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
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
# services
/**
* 更新用户信息
* @param {Object} param0 要修改的内容 { newPassword, newNickName, newPicture, newCity }
* @param {Object} param1 查询条件 { userName, password }
*/
async function updateUser(
{ newPassword, newNickName, newPicture, newCity },
{ userName, password }
) {
// 拼接修改内容
const updateData = {}
if (newPassword) {
updateData.password = newPassword
}
if (newNickName) {
updateData.nickName = newNickName
}
if (newPicture) {
updateData.picture = newPicture
}
if (newCity) {
updateData.city = newCity
}
// 拼接查询条件
const whereData = {
userName
}
if (password) {
whereData.password = password
}
// 执行修改
const result = await User.update(updateData, {
where: whereData
})
return result[0] > 0 // 修改的行数
}
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
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
# 一个公共的文件上传接口
实际项目中,文件管理应该是一个单独的服务
# 上传api
这里我们使用formidable-upload-koa
const router = require('koa-router')()
const { loginCheck } = require('../../middlewares/loginChecks')
const koaFrom = require('formidable-upload-koa')
const { saveFile } = require('../../controller/utils')
router.prefix('/api/utils')
// 上传图片
router.post('/upload', loginCheck, koaFrom(), async (ctx, next) => {
const file = ctx.req.files['file']
if (!file) {
return
}
const { size, path, name, type } = file
ctx.body = await saveFile({
name,
type,
size,
filePath: path
})
})
module.exports = router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# controller
const path = require('path')
const { ErrorModel, SuccessModel } = require('../model/ResModel')
const { uploadFileSizeFailInfo } = require('../model/ErrorInfo')
const fse = require('fs-extra')
// 存储目录
const DIST_FOLDER_PATH = path.join(__dirname, '..', '..', 'uploadFiles')
// 文件最大体积 1M
const MIX_SIZE = 1024 * 1024 * 1024
// 是否需要创建目录
fse.pathExists(DIST_FOLDER_PATH).then(exist => {
if (!exist) {
fse.ensureDir(DIST_FOLDER_PATH)
}
})
/**
* 保存文件
* @param {string} name 文件名
* @param {string} type 文件类型
* @param {number} size 文件体积大小
* @param {string} filePath 文件路径
*/
async function saveFile({ name, type, size, filePath }) {
if (size > MIX_SIZE) {
await fse.remove(filePath)
return new ErrorModel(uploadFileSizeFailInfo)
}
// 移动文件
const fileName = Date.now() + '.' + name // 防止重名
const distFilePath = path.join(DIST_FOLDER_PATH, fileName) // 目的地
await fse.move(filePath, distFilePath)
// 返回信息
return new SuccessModel({
url: '/' + fileName
})
}
module.exports = {
saveFile
}
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
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
# 单元测试
//user api test
const server = require('../server')
// 用户信息
const userName = `u_${Date.now()}`
const password = `p_${Date.now()}`
const testUser = {
userName,
password,
nickName: userName,
gender: 1
}
// 存储 cookie
let COOKIE = ''
// 注册
test('注册一个用户,应该成功', async () => {
const res = await server
.post('/api/user/register')
.send(testUser)
expect(res.body.errno).toBe(0)
})
// 重复注册
test('重复注册用户,应该失败', async () => {
const res = await server
.post('/api/user/register')
.send(testUser)
expect(res.body.errno).not.toBe(0)
})
// 查询用户是否存在
test('查询注册的用户名,应该存在', async () => {
const res = await server
.post('/api/user/isExist')
.send({ userName })
expect(res.body.errno).toBe(0)
})
// json schema 检测
test('json schema 检测,非法的格式,注册应该失败', async () => {
const res = await server
.post('/api/user/register')
.send({
userName: '123', // 用户名不是字母(或下划线)开头
password: 'a', // 最小长度不是 3
// nickName: ''
gender: 'mail' // 不是数字
})
expect(res.body.errno).not.toBe(0)
})
// 登录
test('登录,应该成功', async () => {
const res = await server
.post('/api/user/login')
.send({
userName,
password
})
expect(res.body.errno).toBe(0)
// 获取 cookie
COOKIE = res.headers['set-cookie'].join(';')
})
// 修改基本信息
test('修改基本信息应该成功', async () => {
const res = await server
.patch('/api/user/changeInfo')
.send({
nickName: '测试昵称',
city: '测试城市',
picture: '/test.png'
})
.set('cookie', COOKIE)
expect(res.body.errno).toBe(0)
})
// 修改密码
test('修改密码应该成功', async () => {
const res = await server
.patch('/api/user/changePassword')
.send({
password,
newPassword: `p_${Date.now()}`
})
.set('cookie', COOKIE)
expect(res.body.errno).toBe(0)
})
// 删除
test('删除用户,应该成功', async () => {
const res = await server
.post('/api/user/delete')
.set('cookie', COOKIE)
expect(res.body.errno).toBe(0)
})
// 退出
test('退出登录应该成功', async () => {
const res = await server
.post('/api/user/logout')
.set('cookie', COOKIE)
expect(res.body.errno).toBe(0)
})
// 再次查询用户,应该不存在
test('删除之后,再次查询注册的用户名,应该不存在', async () => {
const res = await server
.post('/api/user/isExist')
.send({ userName })
expect(res.body.errno).not.toBe(0)
})
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
至此用户管理基本完成 可以去git仓库的提交记录里进行查看