优化
This commit is contained in:
1314
server/services/core/group/group.service.ts
Normal file
1314
server/services/core/group/group.service.ts
Normal file
File diff suppressed because it is too large
Load Diff
190
server/services/core/group/groupExtra.service.ts
Normal file
190
server/services/core/group/groupExtra.service.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
import _ from 'lodash';
|
||||
import type {
|
||||
GroupExtraDocument,
|
||||
GroupExtraModel,
|
||||
} from '../../../models/group/group-extra';
|
||||
import {
|
||||
TcService,
|
||||
TcContext,
|
||||
TcDbService,
|
||||
call,
|
||||
PERMISSION,
|
||||
t,
|
||||
NoPermissionError,
|
||||
} from 'tailchat-server-sdk';
|
||||
|
||||
interface GroupExtraService
|
||||
extends TcService,
|
||||
TcDbService<GroupExtraDocument, GroupExtraModel> {}
|
||||
class GroupExtraService extends TcService {
|
||||
get serviceName(): string {
|
||||
return 'group.extra';
|
||||
}
|
||||
|
||||
onInit(): void {
|
||||
this.registerLocalDb(require('../../../models/group/group-extra').default);
|
||||
|
||||
this.registerAction('getGroupData', this.getGroupData, {
|
||||
params: {
|
||||
groupId: 'string',
|
||||
name: 'string',
|
||||
},
|
||||
cache: {
|
||||
keys: ['groupId', 'name'],
|
||||
ttl: 60 * 60, // 1 hour
|
||||
},
|
||||
});
|
||||
this.registerAction('saveGroupData', this.saveGroupData, {
|
||||
params: {
|
||||
groupId: 'string',
|
||||
name: 'string',
|
||||
data: 'string',
|
||||
},
|
||||
});
|
||||
this.registerAction('getPanelData', this.getPanelData, {
|
||||
params: {
|
||||
groupId: 'string',
|
||||
panelId: 'string',
|
||||
name: 'string',
|
||||
},
|
||||
cache: {
|
||||
keys: ['groupId', 'panelId', 'name'],
|
||||
ttl: 60 * 60, // 1 hour
|
||||
},
|
||||
});
|
||||
this.registerAction('savePanelData', this.savePanelData, {
|
||||
params: {
|
||||
groupId: 'string',
|
||||
panelId: 'string',
|
||||
name: 'string',
|
||||
data: 'string',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getGroupData(
|
||||
ctx: TcContext<{
|
||||
groupId: string;
|
||||
name: string;
|
||||
}>
|
||||
) {
|
||||
const { groupId, name } = ctx.params;
|
||||
|
||||
const res = await this.adapter.findOne({
|
||||
groupId,
|
||||
panelId: null,
|
||||
name,
|
||||
});
|
||||
|
||||
return { data: res?.data ?? null };
|
||||
}
|
||||
|
||||
async saveGroupData(
|
||||
ctx: TcContext<{
|
||||
groupId: string;
|
||||
name: string;
|
||||
data: string;
|
||||
}>
|
||||
) {
|
||||
const { groupId, name, data } = ctx.params;
|
||||
const userId = ctx.meta.userId;
|
||||
|
||||
const [hasPermission] = await call(ctx).checkUserPermissions(
|
||||
groupId,
|
||||
userId,
|
||||
[PERMISSION.core.groupConfig]
|
||||
);
|
||||
if (!hasPermission) {
|
||||
throw new NoPermissionError(t('没有操作权限'));
|
||||
}
|
||||
|
||||
await this.adapter.model.findOneAndUpdate(
|
||||
{
|
||||
groupId,
|
||||
panelId: null,
|
||||
name,
|
||||
},
|
||||
{
|
||||
data: String(data),
|
||||
},
|
||||
{
|
||||
upsert: true, // Create if not exist
|
||||
}
|
||||
);
|
||||
|
||||
await this.cleanGroupDataCache(groupId, name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async getPanelData(
|
||||
ctx: TcContext<{
|
||||
groupId: string;
|
||||
panelId: string;
|
||||
name: string;
|
||||
}>
|
||||
) {
|
||||
const { groupId, panelId, name } = ctx.params;
|
||||
|
||||
const res = await this.adapter.findOne({
|
||||
groupId,
|
||||
panelId,
|
||||
name,
|
||||
});
|
||||
|
||||
return { data: res?.data ?? null };
|
||||
}
|
||||
|
||||
async savePanelData(
|
||||
ctx: TcContext<{
|
||||
groupId: string;
|
||||
panelId: string;
|
||||
name: string;
|
||||
data: string;
|
||||
}>
|
||||
) {
|
||||
const { groupId, panelId, name, data } = ctx.params;
|
||||
const userId = ctx.meta.userId;
|
||||
|
||||
const [hasPermission] = await call(ctx).checkUserPermissions(
|
||||
groupId,
|
||||
userId,
|
||||
[PERMISSION.core.managePanel]
|
||||
);
|
||||
if (!hasPermission) {
|
||||
throw new NoPermissionError(t('没有操作权限'));
|
||||
}
|
||||
|
||||
await this.adapter.model.findOneAndUpdate(
|
||||
{
|
||||
groupId,
|
||||
panelId,
|
||||
name,
|
||||
},
|
||||
{
|
||||
data: String(data),
|
||||
},
|
||||
{
|
||||
upsert: true, // Create if not exist
|
||||
}
|
||||
);
|
||||
|
||||
await this.cleanGroupPanelDataCache(groupId, panelId, name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private cleanGroupDataCache(groupId: string, name: string) {
|
||||
return this.cleanActionCache('getGroupData', [groupId, name]);
|
||||
}
|
||||
|
||||
private cleanGroupPanelDataCache(
|
||||
groupId: string,
|
||||
panelId: string,
|
||||
name: string
|
||||
) {
|
||||
return this.cleanActionCache('getPanelData', [groupId, panelId, name]);
|
||||
}
|
||||
}
|
||||
|
||||
export default GroupExtraService;
|
||||
263
server/services/core/group/invite.service.ts
Normal file
263
server/services/core/group/invite.service.ts
Normal file
@@ -0,0 +1,263 @@
|
||||
import _ from 'lodash';
|
||||
import type {
|
||||
GroupInvite,
|
||||
GroupInviteDocument,
|
||||
GroupInviteModel,
|
||||
} from '../../../models/group/invite';
|
||||
import {
|
||||
TcService,
|
||||
TcContext,
|
||||
TcDbService,
|
||||
PureContext,
|
||||
call,
|
||||
NoPermissionError,
|
||||
PERMISSION,
|
||||
db,
|
||||
} from 'tailchat-server-sdk';
|
||||
|
||||
interface GroupService
|
||||
extends TcService,
|
||||
TcDbService<GroupInviteDocument, GroupInviteModel> {}
|
||||
class GroupService extends TcService {
|
||||
get serviceName(): string {
|
||||
return 'group.invite';
|
||||
}
|
||||
|
||||
onInit(): void {
|
||||
this.registerLocalDb(require('../../../models/group/invite').default);
|
||||
|
||||
this.registerAction('createGroupInvite', this.createGroupInvite, {
|
||||
params: {
|
||||
groupId: 'string',
|
||||
inviteType: { type: 'enum', values: ['normal', 'permanent'] },
|
||||
},
|
||||
});
|
||||
this.registerAction('editGroupInvite', this.editGroupInvite, {
|
||||
params: {
|
||||
code: 'string',
|
||||
groupId: 'string',
|
||||
expiredAt: { type: 'number', optional: true },
|
||||
usageLimit: { type: 'number', optional: true },
|
||||
},
|
||||
});
|
||||
this.registerAction('getAllGroupInviteCode', this.getAllGroupInviteCode, {
|
||||
params: {
|
||||
groupId: 'string',
|
||||
},
|
||||
});
|
||||
this.registerAction('findInviteByCode', this.findInviteByCode, {
|
||||
params: {
|
||||
code: 'string',
|
||||
},
|
||||
});
|
||||
this.registerAction('applyInvite', this.applyInvite, {
|
||||
params: {
|
||||
code: 'string',
|
||||
},
|
||||
});
|
||||
this.registerAction('deleteInvite', this.deleteInvite, {
|
||||
params: {
|
||||
groupId: 'string',
|
||||
inviteId: 'string',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建群组邀请
|
||||
*/
|
||||
async createGroupInvite(
|
||||
ctx: TcContext<{
|
||||
groupId: string;
|
||||
inviteType: 'normal' | 'permanent';
|
||||
}>
|
||||
): Promise<GroupInvite> {
|
||||
const { groupId, inviteType } = ctx.params;
|
||||
const { userId, t } = ctx.meta;
|
||||
|
||||
const [hasNormalPermission, hasUnlimitedPermission] = await call(
|
||||
ctx
|
||||
).checkUserPermissions(groupId, userId, [
|
||||
PERMISSION.core.invite,
|
||||
PERMISSION.core.unlimitedInvite,
|
||||
]);
|
||||
|
||||
if (
|
||||
(inviteType === 'normal' && !hasNormalPermission) ||
|
||||
(inviteType === 'permanent' && !hasUnlimitedPermission)
|
||||
) {
|
||||
throw new NoPermissionError(t('没有创建邀请码权限'));
|
||||
}
|
||||
|
||||
const invite = await this.adapter.model.createGroupInvite(
|
||||
groupId,
|
||||
userId,
|
||||
inviteType
|
||||
);
|
||||
return await this.transformDocuments(ctx, {}, invite);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑群组邀请码
|
||||
*/
|
||||
async editGroupInvite(
|
||||
ctx: TcContext<{
|
||||
code: string;
|
||||
groupId: string;
|
||||
expiredAt?: number; // 时间戳,单位ms
|
||||
usageLimit?: number;
|
||||
}>
|
||||
) {
|
||||
const { code, groupId, expiredAt, usageLimit } = ctx.params;
|
||||
const { userId, t } = ctx.meta;
|
||||
|
||||
// 检查权限
|
||||
const [hasEditPermission] = await call(ctx).checkUserPermissions(
|
||||
groupId,
|
||||
userId,
|
||||
[PERMISSION.core.editInvite]
|
||||
);
|
||||
|
||||
if (!hasEditPermission) {
|
||||
throw new NoPermissionError(t('没有编辑邀请码权限'));
|
||||
}
|
||||
|
||||
const update = {};
|
||||
if (expiredAt) {
|
||||
_.set(update, ['expiredAt'], new Date(expiredAt));
|
||||
} else {
|
||||
_.set(update, ['$unset', 'expiredAt'], 1);
|
||||
}
|
||||
|
||||
if (usageLimit) {
|
||||
_.set(update, ['usageLimit'], usageLimit);
|
||||
} else {
|
||||
_.set(update, ['$unset', 'usageLimit'], 1);
|
||||
}
|
||||
|
||||
await this.adapter.model.updateOne({ groupId, code }, update);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有群组邀请码
|
||||
*/
|
||||
async getAllGroupInviteCode(
|
||||
ctx: TcContext<{
|
||||
groupId: string;
|
||||
}>
|
||||
) {
|
||||
const groupId = ctx.params.groupId;
|
||||
const { t, userId } = ctx.meta;
|
||||
|
||||
const [hasPermission] = await call(ctx).checkUserPermissions(
|
||||
groupId,
|
||||
userId,
|
||||
[PERMISSION.core.manageInvite]
|
||||
);
|
||||
if (!hasPermission) {
|
||||
throw new NoPermissionError(t('没有查看权限'));
|
||||
}
|
||||
|
||||
const list = await this.adapter.model.find({
|
||||
groupId,
|
||||
});
|
||||
|
||||
return await this.transformDocuments(ctx, {}, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过邀请码查找群组邀请信息
|
||||
*/
|
||||
async findInviteByCode(
|
||||
ctx: PureContext<{
|
||||
code: string;
|
||||
}>
|
||||
): Promise<GroupInvite | null> {
|
||||
const code = ctx.params.code;
|
||||
|
||||
const invite = await this.adapter.model.findOne({
|
||||
code,
|
||||
});
|
||||
|
||||
return await this.transformDocuments(ctx, {}, invite);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用群组邀请(通过群组邀请加入群组)
|
||||
*/
|
||||
async applyInvite(ctx: TcContext<{ code: string }>): Promise<void> {
|
||||
const code = ctx.params.code;
|
||||
const t = ctx.meta.t;
|
||||
|
||||
const invite = await this.adapter.model.findOne({
|
||||
code,
|
||||
});
|
||||
|
||||
if (typeof invite.usageLimit === 'number') {
|
||||
const usage = invite.usage || 0;
|
||||
if (usage >= invite.usageLimit) {
|
||||
throw new Error(t('该邀请码使用次数耗尽'));
|
||||
}
|
||||
}
|
||||
|
||||
if (new Date(invite.expiredAt).valueOf() < Date.now()) {
|
||||
throw new Error(t('该邀请码已过期'));
|
||||
}
|
||||
|
||||
const groupId = invite.groupId;
|
||||
if (_.isNil(groupId)) {
|
||||
throw new Error(t('群组邀请失效: 群组id为空'));
|
||||
}
|
||||
|
||||
await ctx.call('group.joinGroup', {
|
||||
groupId: String(groupId),
|
||||
});
|
||||
|
||||
await this.adapter.model.updateOne(
|
||||
{
|
||||
_id: new db.Types.ObjectId(invite._id),
|
||||
},
|
||||
{
|
||||
$inc: {
|
||||
usage: 1,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const creatorInfo = await call(ctx).getUserInfo(String(invite.creator));
|
||||
await call(ctx).addGroupSystemMessage(
|
||||
String(groupId),
|
||||
t('{{nickname}} 通过 {{creator}} 的邀请码加入群组', {
|
||||
nickname: ctx.meta.user.nickname,
|
||||
creator: creatorInfo.nickname,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除邀请码
|
||||
*/
|
||||
async deleteInvite(ctx: TcContext<{ groupId: string; inviteId: string }>) {
|
||||
const groupId = ctx.params.groupId;
|
||||
const inviteId = ctx.params.inviteId;
|
||||
const { t, userId } = ctx.meta;
|
||||
|
||||
const [hasPermission] = await call(ctx).checkUserPermissions(
|
||||
groupId,
|
||||
userId,
|
||||
[PERMISSION.core.manageInvite]
|
||||
);
|
||||
if (!hasPermission) {
|
||||
throw new NoPermissionError(t('没有删除权限'));
|
||||
}
|
||||
|
||||
await this.adapter.model.deleteOne({
|
||||
_id: inviteId,
|
||||
groupId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default GroupService;
|
||||
Reference in New Issue
Block a user