Skip to content

Commit

Permalink
feat: 邀请创建接口改动:邀请发起者必须是项目的拥有者;新增接口:接受 邀请;
Browse files Browse the repository at this point in the history
邀请创建接口改动:邀请发起者必须是项目的拥有者;新增接口:接受 邀请;
  • Loading branch information
Imfdj committed May 13, 2021
1 parent 01ba38b commit cb02cb8
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 20 deletions.
3 changes: 3 additions & 0 deletions app/contract/request/invite.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,7 @@ module.exports = {
example: [1, 2],
},
},
inviteUUID: {
uuid: body.inviteBodyReq.uuid,
},
};
25 changes: 23 additions & 2 deletions app/controller/v1/invites.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class RoleController extends Controller {
};
ctx.validate(rule, ctx.request.body);
const res = await ctx.service.invites.create(ctx.request.body);
if (res === false) return;
ctx.helper.body.CREATED_UPDATE({ ctx, res });
}

Expand All @@ -75,7 +76,7 @@ class RoleController extends Controller {
delete ctx.request.body.expires;
ctx.validate(ctx.rule.invitePutBodyReq, ctx.request.body);
const res = await service.invites.update(ctx.request.body);
res && res[0] !== 0 ? ctx.helper.body.CREATED_UPDATE({ ctx }) : ctx.helper.body.NOT_FOUND({ ctx });
res && res[1] && res[1].length ? ctx.helper.body.CREATED_UPDATE({ ctx }) : ctx.helper.body.NOT_FOUND({ ctx });
}

/**
Expand Down Expand Up @@ -103,7 +104,11 @@ class RoleController extends Controller {
async findValidOne() {
const { ctx, service } = this;
const rule = {
group: ctx.rule.inviteBodyReq.group,
group: {
type: 'enum',
required: true,
values: ['Projects'],
},
group_id: ctx.rule.inviteBodyReq.group_id,
};
ctx.validate(rule, ctx.query);
Expand All @@ -130,6 +135,22 @@ class RoleController extends Controller {
const res = await service.invites.findOneByUUID(ctx.query.uuid);
res ? ctx.helper.body.SUCCESS({ ctx, res }) : ctx.helper.body.NOT_FOUND({ ctx });
}

/**
* @apikey
* @summary 接受 邀请
* @description 接受 邀请
* @router put /api/v1/invites/accept
* @request body inviteUUID
*/
async acceptInvite() {
const { ctx, service } = this;
delete ctx.request.body.expires;
ctx.validate(ctx.rule.inviteUUID, ctx.request.body);
const res = await service.invites.acceptInvite(ctx.request.body);
if (res === false) return;
ctx.helper.body.CREATED_UPDATE({ ctx });
}
}

module.exports = RoleController;
4 changes: 2 additions & 2 deletions app/model/invites.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ module.exports = app => {
);
invite.addHook('afterUpdate', async (invite, options) => {
const ctx = await app.createAnonymousContext();
// 如果此次将is_accept从0修改为1,则视为一次接受修改,则为发起者创建一次站内信,说明邀请已被接受
if (invite.dataValues.is_accept === 1 && invite._previousDataValues.is_accept === 0) {
// 如果此次将is_accept从0修改为1,则视为一次接受修改,且存在接受者id, 则为发起者创建一次站内信,说明邀请已被接受
if (invite.dataValues.is_accept === 1 && invite._previousDataValues.is_accept === 0 && invite.receiver_id) {
if (invite.group === 'Projects') {
const project = await ctx.model.Projects.findOne({ where: { id: invite.group_id } });
ctx.model.Messages.create({
Expand Down
1 change: 1 addition & 0 deletions app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ module.exports = app => {
router.delete('/api/v1/invites', controller.v1.invites.destroy);
router.get('/api/v1/invites/valid', controller.v1.invites.findValidOne);
router.get('/api/v1/invites/uuid', controller.v1.invites.findOneByUUID);
router.put('/api/v1/invites/accept', controller.v1.invites.acceptInvite);

/**
* 操作日志
Expand Down
102 changes: 88 additions & 14 deletions app/service/invites.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,37 @@ class _objectName_Service extends Service {
.add(app.config.inviteExpiresRange, 'minute')
.format('YYYY-MM-DD HH:mm:ss');
}
const transaction = await ctx.model.transaction();
try {
await ctx.model.Invites.create(payload, { transaction });
if (payload.receiver_id && payload.group === 'Projects') {
const project = await ctx.model.Projects.findOne({ where: { id: payload.group_id } });
payload.content = `邀请你加入项目 <span class="project-name">${project.name}</span>`;
payload.type = 'personal';
payload.url = `/invite/project/${payload.uuid}`;
await ctx.model.Messages.create(payload, { transaction });

// 如果指定邀请某人,加入项目
if (payload.group === 'Projects') {
// 邀请发起者必须是项目的拥有者
const project = await ctx.model.Projects.findOne({
where: {
id: payload.group_id,
manager_id: ctx.currentRequestData.userInfo.id,
},
});
if (!project) {
ctx.helper.body.UNAUTHORIZED({ ctx, msg: '如果是项目成员邀请,则邀请发起者必须是项目的拥有者' });
return false;
}
const transaction = await ctx.model.transaction();
try {
await ctx.model.Invites.create(payload, { transaction });
// 如果指定了邀请接受者, 则创建站内消息
if (payload.receiver_id) {
payload.content = `邀请你加入项目 <span class="project-name">${project.name}</span>`;
payload.type = 'personal';
payload.url = `/invite/project/${payload.uuid}`;
await ctx.model.Messages.create(payload, { transaction });
}
await transaction.commit();
return true;
} catch (e) {
await transaction.rollback();
app.logger.errorAndSentry(e);
throw e;
}
await transaction.commit();
return true;
} catch (e) {
await transaction.rollback();
app.logger.errorAndSentry(e);
}
}

Expand Down Expand Up @@ -111,6 +127,64 @@ class _objectName_Service extends Service {
}
return false;
}

async acceptInvite(payload) {
const { ctx, app } = this;
const invite = await ctx.model.Invites.findOne({
where: payload,
});
if (invite) {
// 有效时间expires是否大于当前时间,是则是可用的
const valid = app.dayjs(invite.expires)
.isAfter(app.dayjs());
if (!valid) {
ctx.helper.body.INVALID_REQUEST({ ctx, msg: '邀请已过期' });
return false;
}
if (invite.is_accept === 1) {
ctx.helper.body.INVALID_REQUEST({ ctx, msg: '邀请已使用过了' });
return false;
}
} else {
ctx.helper.body.INVALID_REQUEST({ ctx, msg: '邀请不存在' });
return false;
}
const transaction = await ctx.model.transaction();
try {
// 如果有接受者,则是单独邀请
if (invite.receiver_id) {
if (invite.receiver_id !== ctx.currentRequestData.userInfo.id) {
ctx.helper.body.INVALID_REQUEST({ ctx, msg: '调用者不是邀请指定者,无法接受此邀请' });
return false;
}
// 调用者是邀请指定者,则置为已接受
await ctx.model.Invites.update(
{ is_accept: 1 },
{
where: payload,
individualHooks: true,
transaction,
}
);
}
// 如果邀请团体类型为Projects,项目成员邀请
if (invite.group === 'Projects') {
await ctx.model.UserProjects.create(
{
user_id: ctx.currentRequestData.userInfo.id,
project_id: invite.group_id,
},
{ transaction }
);
}
await transaction.commit();
return true;
} catch (e) {
await transaction.rollback();
app.logger.errorAndSentry(e);
throw e;
}
}
}

module.exports = _objectName_Service;
25 changes: 23 additions & 2 deletions test/app/controller/invites.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('test/app/controller/invites.test.js', () => {
.send({
group: 'Projects',
group_id: 999999,
receiver_id: 1,
receiver_id: 2,
});
assert(res.status === 201);
assert(res.body.code === 0);
Expand Down Expand Up @@ -62,7 +62,6 @@ describe('test/app/controller/invites.test.js', () => {
id: createMenuData.id,
group: 'Projects',
group_id: 999999,
is_accept: 1,
});
assert(res.status === 201);
assert(res.body.code === 0);
Expand Down Expand Up @@ -95,6 +94,28 @@ describe('test/app/controller/invites.test.js', () => {
});
});

describe('PUT /api/v1/invites/accept', () => {
it('should work', async () => {
app.mockCookies({ EGG_SESS: app.__cookies });
const data = await app
.httpRequest()
.post('/api/v1/users/login')
.send({
username: 'root',
password: '123123',
})
.expect(200);
const res = await app.httpRequest()
.put('/api/v1/invites/accept')
.set('authorization', `Bearer ${data.body.data.accessToken}`)
.send({
uuid: createMenuData.uuid,
});
assert(res.status === 201);
assert(res.body.code === 0);
});
});

describe('DELETE /api/v1/invites', () => {
it('should work', async () => {
app.mockCookies({ EGG_SESS: app.__cookies });
Expand Down

0 comments on commit cb02cb8

Please sign in to comment.