课程名称:web前端架构师
课程章节:第16周 第八章 编写作品相关代码以及学习装饰器的使用
主讲老师:张轩
课程内容:作品的查询、更新,编写权限认证装饰器
作品的查询
首先编写查询条件接口
export interface IndexCondition {
pageIndex?: number // 页码
pageSize?: number // 数量
select?: string | string[] // 查询字段
// 关联结合
populate?: string | {
path?: string
select?: string
}
customSort?: Record<string, any> // 排序
find?: Record<string, any> // 查询条件
}
定义默认的查询条件
const defaultIndexCondition:Required<IndexCondition> = {
pageIndex: 0,
pageSize: 10,
populate: '',
select: '',
customSort: { createdAt: -1 },
find: {},
};
获取列表
// app/service/Work.ts
public async getList(condition: IndexCondition) {
const { ctx } = this;
const findCondition = { ...defaultIndexCondition, ...condition };
const { find, pageIndex, pageSize, select, populate, customSort } = findCondition;
const list = await ctx.model.Work.find(find).select(select)
.populate(populate)
.skip(pageIndex * pageSize)
.limit(pageSize)
.sort(customSort)
.lean();
const count = await ctx.model.Work.find(find).count();
return {
list,
count,
};
}
获取用户传入的查询条件并将它改造成我们定义的查询条件的格式
// app/controller/work.ts
async myList() {
const { ctx, service } = this;
const userId = ctx.state.user._id;
const { pageIndex, pageSize, isTemplate, title } = ctx.query;
const listCondition: IndexCondition = {
select: 'id author coverImg desc title user isHot createAt',
populate: {
path: 'user',
select: 'username nickname picture',
},
find: {
user: userId,
...(title && { title: { $regex: title } }),
...(isTemplate && { userId: !!parseInt(isTemplate) }),
},
...(pageIndex && { pageIndex: parseInt(pageIndex) }),
...(pageSize && { pageSize: parseInt(pageSize) }),
};
const res = await service.work.getList(listCondition);
ctx.helper.success({ ctx, res });
}
更新和删除作品
在更新作品和删除作品之前都需要进行权限认证,判断是不是当前用户的作品,是的话才能进行删除
更新和删除的权限认证代码相似,所以我们可以将权限校验的代码写成装饰器,这样方便复用
下面编写权限认证装饰器, 用户可以传入结合名称和用户的 key,例如我们查询作品
const userId = ctx.state.user._id; // 用户 id
// 查询到该作品
const work = await ctx.model.Work.findOne({ id });
if(work.user.toString() !== userId){
// 不是该作品的作者,没有权限修改
...
}
// 有权限修改
...
import { Controller } from 'egg';
import { errMsgType } from '../error';
/**
* @param modelName 集合名称
* @param errType 错误类型
* @param userKey 用户在文档中的 key, 默认是 user
* @return
*/
export default function checkPermission(modelName: string, errType: errMsgType, userKey = 'user') {
return (_target, _propertyKey, descriptor: PropertyDescriptor) => {
const originMethod = descriptor.value;
descriptor.value = async function(...args:any[]) {
const that = this as Controller;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const { ctx } = that;
const userId = ctx.state.user._id;
const id = ctx.params.id;
const work = await ctx.model[modelName].findOne({ id });
if (!work || work[userKey].toString() !== userId) {
return ctx.helper.error({ ctx, errType });
}
return originMethod.apply(this, ...args);
};
};
}
发布作品和发布为模版
发布作品和发布模版与修改作品很类似
- 发布作品 status 修改为 2
- status 1 未发布, 2 发布, 3 强制下线
- 发布为模版 isTemplate 修改为 true
- 修改作品 可以修改所有字段
// app/service/Work.ts
public async publish(id: string, isTemplate = false) {
const { ctx } = this;
const res = await ctx.model.Work.findOneAndUpdate({ id }, {
status: 2,
isTemplate,
}, { new: true });
return res;
}
发布和发布为模版只有 isTemplate 这一个字段不同
@checkPermission('Work', 'workNoPermissonFail')
async publish(isTemplate: boolean) {
const { ctx, service } = this;
const id = ctx.params.id;
const res = await service.work.publish(id, isTemplate);
ctx.helper.success({ ctx, res });
}
async publishWork() {
await this.publish(true);
}
async publishTemplate() {
await this.publish(false);
}