1-1 用户路由表
src/routes/user.ts
import Router from '@koa/router'
import {
getAllUserController,
updateUserController,
allocUserRoleController,
removeUserController
} from '../controller/user'
const router = new Router({
prefix: '/api/user'
})
/**
* 获取用户列表
* get /api/user
*/
router.get('/', async ctx => {
const { pageNum = 0, pageSize = 10, ...query } = ctx.request.query
ctx.body = await getAllUserController({
offset: Number(pageNum),
limit: Number(pageSize),
query
})
})
/**
* 编辑用户
* post /api/user/:id
*/
router.put('/:id', async ctx => {
const { id } = ctx.params
ctx.body = await updateUserController(Number(id), ctx.request.body)
})
/**
* 给用户分配角色
* post /api/user/role/:id
*/
router.post('/role/:id', async ctx => {
const { id } = ctx.params
const { roles } = ctx.request.body
ctx.body = await allocUserRoleController(Number(id), roles)
})
/**
* 删除用户
* delete /api/user/:id
*/
router.delete('/:id', async ctx => {
const { id } = ctx.params
ctx.body = await removeUserController(Number(id))
})
export default router
1-2 用户controller
src/controller/user.ts
import {
getAllUserService,
updateUserService,
allocUserRoleService,
destroyUserRoleByUserID,
removeUserService
} from '../services/user'
import { createErrorResponse, SuccessResponse } from '../utils/Response'
import errorInfo from '../constants/errorInfo'
import { RegisterModel } from '../db/models/user'
import { getUserInfo } from '../services/auth'
import { RegisterPropsWithRoles } from './types'
const {
updateUserExistFailInfo,
getUserListFailInfo,
allocUserRoleFailInfo,
deleteUserInfoFailInfo
} = errorInfo
// 获取全部菜单
export interface WhereQuery {
name: string;
status: number;
mobile: string;
}
export interface UserListParams {
offset: number;
limit: number;
query: Record<string, any>;
}
// 获取全部用户列表
export const getAllUserController = async ({ offset, limit, query }: UserListParams) => {
try {
const result = await getAllUserService(offset, limit, query)
return new SuccessResponse(result)
} catch (error) {
console.error(error.message)
return createErrorResponse(getUserListFailInfo)
}
}
// 更改用户信息
export const updateUserController = async (id: number, data: RegisterPropsWithRoles) => {
const {
username,
email,
mobile,
description,
status,
roleIds,
} = data
console.log('roleIds',roleIds)
// 判断修改后的用户名是否已经存在其他重名用户
const userInfo = await getUserInfo({ username })
if (userInfo && userInfo.id !== id) {
return createErrorResponse(updateUserExistFailInfo)
}
try {
await updateUserService(id, {
username,
email,
mobile,
description,
status
} as RegisterModel)
await allocUserRoleController(id, roleIds)
return new SuccessResponse(null, '用户信息修改成功')
} catch (error) {
console.error(error.message)
return createErrorResponse(getUserListFailInfo)
}
}
// 分配用户角色
export const allocUserRoleController = async (id: number, roles: number[] = []) => {
// 移除之前该用户与角色记录
await destroyUserRoleByUserID(id)
try {
await allocUserRoleService(id, roles)
return new SuccessResponse(null, '用户角色分配成功')
} catch (error) {
console.error(error.message)
return createErrorResponse(allocUserRoleFailInfo)
}
}
// 删除用户
export const removeUserController = async (id: number) => {
try {
await removeUserService(id)
return new SuccessResponse(null, '用户删除成功')
} catch (error) {
console.error(error.message)
return createErrorResponse(deleteUserInfoFailInfo)
}
}
1-3 用户services
src/services/user.ts
import { UserInfo } from './types'
import { RolesModel, UserModel, UserRoleModel } from '../db/models'
import { RegisterModel } from '../db/models/user'
// 获取全部用户
export const getAllUserService = async (offset = 0, limit = 10, query: Record<string, any>) => {
const whereProps = {} as Record<string, any>
// 检索条件处理
if (query.mobile) {
whereProps.mobile = query.mobile
}
if (query.username) {
whereProps.username = query.username
}
if (!isNaN(query.status)) {
whereProps.status = Number(query.status)
}
const { count, rows } = await UserModel.findAndCountAll({
attributes: ['id', 'username', 'email', 'mobile', 'isSuper', 'status', 'avatar', 'description', 'createdAt'],
where: whereProps,
limit,
offset: limit * offset,
// https://blog.csdn.net/Tirst_/article/details/109677451
distinct: true, //去重 解决findAndCountAll联表查询时 count数不准确问题解决
include: [ // 联表查询
{
model: UserRoleModel,
attributes: ['id'],
include: [
{
model: RolesModel,
attributes: ['id', 'name', 'description']
}
]
}
]
})
// 数据格式化
const users = rows.map(row => {
const user = row.toJSON() as UserInfo
user.roles = user.UserRoles?.map(item => item.Role)
delete user.UserRoles
return user
})
return {
users,
count
}
}
// 修改用户
export const updateUserService = async (id: number, data: RegisterModel) => {
const result = await UserModel.update(data, {
where: {
id
}
})
return result
}
/**
* 删除与该用户相关联记录
* @param id 角色id
*/
export const destroyUserRoleByUserID = async (id: number) => {
const result = await UserRoleModel.destroy({
where: {
user_id: id
}
})
return result
}
// 分配用户角色
export const allocUserRoleService = async (id: number, data: number[]) => {
const roles = data.map(rid => ({
user_id: id,
role_id: rid
}))
const result = await UserRoleModel.bulkCreate(roles)
return result
}
// 根据用户id删除用户
export const removeUserService = async (id: number) => {
const result = await UserModel.destroy({
where: {
id
}
})
return result
}
1-4 auth services
src/services/auth.ts
import UserModel, { RegisterModel, UserModelProps } from '../db/models/user'
import { UserWhereProps } from './types'
import { createMd5 } from '../utils/createMD5'
import { RolesModel, UserRoleModel } from '../db/models'
import { UserInfo } from './types'
/**
* 创建用户
*/
export const createUser = async ({ username, password, email, mobile, status, avatar }: RegisterModel): Promise<UserModelProps> => {
const result = await UserModel.create({
username,
password,
email,
mobile,
status
})
return result.toJSON() as UserModelProps
}
/**
* 根据用户名 获取用户信息
* @param username 用户名
* @param password 密码
* @param id 用户id
* @returns 用户信息
*/
export const getUserInfo = async ({ username, password, id }: UserWhereProps): Promise<UserModelProps | null> => {
const where: UserWhereProps = {
username
}
if (password) {
where.password = createMd5(password)
}
if (typeof id != 'undefined') {
where.id = id
}
const result = await UserModel.findOne({
attributes: {
exclude: ['password', 'createdAt', 'updatedAt']
},
where,
})
if (result == null) return null
return result.toJSON() as UserModelProps
}
// 获取用户信息 包含 角色信息
export const getUserInfoAndRoles = async (id: number) => {
const result = await UserModel.findOne({
attributes: ['id', 'username', 'email', 'mobile', 'isSuper', 'status', 'avatar', 'description'],
where: {
id
},
include: [ // 联表查询
{
model: UserRoleModel,
attributes: ['id'],
include: [
{
model: RolesModel,
attributes: ['id', 'name', 'description']
}
]
}
]
})
if (!result) return null
const user = result.toJSON() as UserInfo
user.roles = user.UserRoles?.map(item => item.Role)
delete user.UserRoles
return user
}
1-5 user model
src/db/models/user.ts
import {
Model,
DataTypes,
Optional
} from 'sequelize'
import seq from '../seq'
// sequelize+typescript 参考文档
// https://sequelize.org/master/manual/typescript.html
// model类型
export interface UserModelProps {
id: number;
username: string;
password: string;
email: string | null;
mobile: string | null;
avatar: string;
description: string;
isSuper: 0 | 1;
status: 0 | 1;
}
// 注册接口params类型 id 和 isSuper创建时候可以不用定义自动分配
export type RegisterModel = Omit<UserModelProps, 'id'|'isSuper'>
// 在“User.build”和“User.create”调用中,有些属性是可选的
interface UserCreationAttributes extends Optional<UserModelProps, "id" | "isSuper" | "status" | "avatar"|"description"> {}
// Model实例接口
interface UserInstance
extends Model<UserModelProps, UserCreationAttributes>,
UserModelProps {}
// 创建User模型 数据表的名字是users
const User = seq.define<UserInstance>('User', {
id: {
primaryKey: true,
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true
},
// id会自动创建 并设为主键、自增
username: {
type: DataTypes.STRING,
allowNull: false,
comment: '用户名'
},
password: {
type: DataTypes.STRING,
allowNull: false,
comment: '密码'
},
email: {
type: DataTypes.STRING,
comment: '用户邮箱'
},
mobile: {
type: DataTypes.STRING,
comment: '手机号'
},
avatar: {
type: DataTypes.STRING,
comment: '头像'
},
isSuper: {
type: DataTypes.BOOLEAN, // TINYINT(1)
comment: '超级管理员 1是 0不是',
defaultValue: 0
},
description: {
type: DataTypes.TEXT,
comment: '描述说明'
},
status: {
type: DataTypes.BOOLEAN, // TINYINT(1)
comment: '账户禁用状态 1正常 0禁用',
defaultValue: 1
}
})
export default User
导入models/index
src/db/models/index.ts
import UserModel from './user'
export {
UserModel,
}
同步下model信息
npm run db
1-6 errorinfo
目前最新错误信息
src/constants/errorInfo.ts
/**
* @description 失败信息集合,包括 code 和 message
* @author 拔都
*/
export default {
// 用户名已存在
registerUserNameExistInfo: {
code: 10001,
message: '用户名已存在'
},
// 注册失败
registerFailInfo: {
code: 10002,
message: '注册失败,请重试'
},
// 用户名不存在
registerUserNameNotExistInfo: {
code: 10003,
message: '用户名未存在'
},
// 登录失败
loginFailInfo: {
code: 10004,
message: '登录失败,用户名或密码错误'
},
// 未登录
loginCheckFailInfo: {
code: 10005,
message: '您尚未登录'
},
// 修改密码失败
changePasswordFailInfo: {
code: 10006,
message: '修改密码失败,请重试'
},
// 用户信息失败
getUserInfoFailInfo: {
code: 10007,
message: '用户信息获取失败 token验证无效'
},
getUserListFailInfo: {
code: 10008,
message: '用户列表获取失败, 请重试'
},
editUserInfoFailInfo: {
code: 10009,
message: '用户信息修改失败, 请重试'
},
deleteUserInfoFailInfo: {
code: 10010,
message: '用户删除失败, 请重试'
},
updateUserRoleFailInfo: {
code: 10011,
message: '用户角色修改失败,请重试'
},
addAccessFailInfo: {
code: 10012,
message: '菜单添加失败'
},
getAccessAllFailInfo: {
code: 10013,
message: '获取全部菜单失败'
},
removeAccessFailInfo: {
code: 10014,
message: '删除菜单失败'
},
updateAccessFailInfo: {
code: 10015,
message: '编辑菜单失败'
},
addRoleFailInfo: {
code: 10016,
message: '添加角色失败'
},
addRoleNameExistInfo: {
code: 10017,
message: '角色已存在, 不能重复添加'
},
updateRoleFailInfo: {
code: 10018,
message: '编辑角色失败'
},
updateRoleNameExistInfo: {
code: 10019,
message: '编辑失败,已存在同名角色'
},
removeRoleFailInfo: {
code: 10020,
message: '角色删除失败'
},
allocRoleAccessFailInfo: {
code: 10021,
message: '角色分配权限失败'
},
getRoleAccessFailInfo: {
code: 10022,
message: '根据角色获取权限失败'
},
updateUserExistFailInfo: {
code: 10023,
message: '用户信息修改失败,已存在同名用户'
},
allocUserRoleFailInfo: {
code: 10024,
message: '用户角色分配失败'
},
accountForbiddenFailInfo: {
code: 10027,
message: '登录失败,账号已被禁用!'
}
}