Files
frontend/src/services/message/groupService.ts

461 lines
12 KiB
TypeScript
Raw Normal View History

/**
*
*
* 使 RESTful API: /api/v1/groups
*/
2026-04-13 01:30:37 +08:00
import { api } from '../core/api';
import {
GroupResponse,
GroupMemberResponse,
GroupAnnouncementResponse,
GroupListResponse,
GroupMemberListResponse,
GroupAnnouncementListResponse,
CreateGroupRequest,
InviteMembersRequest,
TransferOwnerRequest,
SetRoleRequest,
SetNicknameRequest,
SetMuteAllRequest,
SetJoinTypeRequest,
CreateAnnouncementRequest,
MyMemberInfoResponse,
SetGroupAvatarRequest,
HandleGroupRequestAction,
CursorPaginationRequest,
CursorPaginationResponse,
2026-04-13 01:30:37 +08:00
} from '@/types/dto';
function asGroupId(raw: unknown): string {
return String(raw);
}
function normalizeGroup(g: GroupResponse): GroupResponse {
return { ...g, id: asGroupId(g.id) };
}
function normalizeMember(m: GroupMemberResponse): GroupMemberResponse {
return { ...m, group_id: asGroupId(m.group_id) };
}
function normalizeAnnouncement(a: GroupAnnouncementResponse): GroupAnnouncementResponse {
return { ...a, group_id: asGroupId(a.group_id) };
}
function normalizeGroupListResponse(r: GroupListResponse): GroupListResponse {
return { ...r, list: r.list.map(normalizeGroup) };
}
function normalizeMemberListResponse(r: GroupMemberListResponse): GroupMemberListResponse {
return { ...r, list: r.list.map(normalizeMember) };
}
function normalizeAnnouncementListResponse(
r: GroupAnnouncementListResponse
): GroupAnnouncementListResponse {
return { ...r, list: r.list.map(normalizeAnnouncement) };
}
function normalizeGroupCursor(
r: CursorPaginationResponse<GroupResponse>
): CursorPaginationResponse<GroupResponse> {
return { ...r, list: r.list.map(normalizeGroup) };
}
function normalizeMemberCursor(
r: CursorPaginationResponse<GroupMemberResponse>
): CursorPaginationResponse<GroupMemberResponse> {
return { ...r, list: r.list.map(normalizeMember) };
}
function normalizeAnnouncementCursor(
r: CursorPaginationResponse<GroupAnnouncementResponse>
): CursorPaginationResponse<GroupAnnouncementResponse> {
return { ...r, list: r.list.map(normalizeAnnouncement) };
}
// 群组服务类(纯 API 层)
// SQLite/内存缓存编排由 groupManager 统一处理
class GroupService {
// ==================== 群组管理 ====================
/**
*
* GET /api/v1/groups
*/
async getGroups(page = 1, pageSize = 20): Promise<GroupListResponse> {
const response = await api.get<GroupListResponse>('/groups', {
page,
page_size: pageSize,
});
return normalizeGroupListResponse(response.data);
}
/**
*
* GET /api/v1/groups/:id
*/
async getGroup(id: string): Promise<GroupResponse> {
const response = await api.get<GroupResponse>(`/groups/${encodeURIComponent(id)}`);
return normalizeGroup(response.data);
}
/**
*
* GET /api/v1/groups/:id/members
*/
async getMembers(groupId: string, page = 1, pageSize = 50): Promise<GroupMemberListResponse> {
const response = await api.get<GroupMemberListResponse>(
`/groups/${encodeURIComponent(groupId)}/members`,
{
page,
page_size: pageSize,
}
);
return normalizeMemberListResponse(response.data);
}
/**
*
* POST /api/v1/groups
*/
async createGroup(data: CreateGroupRequest): Promise<GroupResponse> {
const response = await api.post<GroupResponse>('/groups', data);
return normalizeGroup(response.data);
}
/**
*
* GET /api/v1/groups/:id/me
*/
async getMyMemberInfo(groupId: string): Promise<MyMemberInfoResponse> {
const response = await api.get<MyMemberInfoResponse>(
`/groups/${encodeURIComponent(groupId)}/me`
);
return {
...response.data,
member: normalizeMember(response.data.member),
};
}
/**
*
* DELETE /api/v1/groups/:id
*/
async dissolveGroup(id: string): Promise<void> {
await api.delete(`/groups/${encodeURIComponent(id)}`);
}
/**
*
* POST /api/v1/groups/:id/transfer
*/
async transferOwner(id: string, data: TransferOwnerRequest): Promise<void> {
await api.post(`/groups/${encodeURIComponent(id)}/transfer`, data);
}
// ==================== 成员管理 ====================
/**
*
* POST /api/v1/groups/:id/invitations
*/
async inviteMembers(groupId: string, data: InviteMembersRequest): Promise<void> {
await api.post(`/groups/${encodeURIComponent(groupId)}/invitations`, data);
}
/**
*
* POST /api/v1/groups/:id/join-requests
*/
async joinGroup(groupId: string): Promise<void> {
await api.post(`/groups/${encodeURIComponent(groupId)}/join-requests`);
}
/**
* /
* POST /api/v1/groups/:id/join-requests/respond
*/
async respondInvite(groupId: string, data: HandleGroupRequestAction): Promise<void> {
await api.post(
`/groups/${encodeURIComponent(groupId)}/join-requests/respond`,
data
);
}
/**
*
* POST /api/v1/groups/:id/join-requests/handle
*/
async reviewJoinRequest(groupId: string, data: HandleGroupRequestAction): Promise<void> {
await api.post(
`/groups/${encodeURIComponent(groupId)}/join-requests/handle`,
data
);
}
/**
* 退
* POST /api/v1/groups/:id/leave
*/
async leaveGroup(groupId: string): Promise<void> {
await api.post(`/groups/${encodeURIComponent(groupId)}/leave`);
}
/**
*
* POST /api/v1/groups/:id/members/kick
*/
async removeMember(groupId: string, userId: string): Promise<void> {
await api.post(`/groups/${encodeURIComponent(groupId)}/members/kick`, {
user_id: userId,
reject_add_request: false,
});
}
/**
* /
* PUT /api/v1/groups/:id/members/:user_id/admin
*/
async setMemberRole(
groupId: string,
userId: string,
data: SetRoleRequest
): Promise<void> {
await api.put(
`/groups/${encodeURIComponent(groupId)}/members/${encodeURIComponent(userId)}/admin`,
{
enable: data.role === 'admin',
}
);
}
/**
*
* PUT /api/v1/groups/:id/members/me/nickname
*/
async setNickname(groupId: string, data: SetNicknameRequest): Promise<void> {
await api.put(`/groups/${encodeURIComponent(groupId)}/members/me/nickname`, data);
}
/**
* /
* POST /api/v1/groups/:id/members/ban
* @param groupId ID
* @param userId ID
* @param duration 0
*/
async muteMember(
groupId: string,
userId: string,
duration: number = 0
): Promise<void> {
await api.post(`/groups/${encodeURIComponent(groupId)}/members/ban`, {
user_id: userId,
duration: duration,
});
}
// ==================== 群设置 ====================
/**
*
* PUT /api/v1/groups/:id/ban
*/
async setMuteAll(groupId: string, data: SetMuteAllRequest): Promise<void> {
await api.put(`/groups/${encodeURIComponent(groupId)}/ban`, {
enable: data.mute_all,
});
}
/**
*
* PUT /api/v1/groups/:id/join-type
*/
async setJoinType(groupId: string, data: SetJoinTypeRequest): Promise<void> {
await api.put(`/groups/${encodeURIComponent(groupId)}/join-type`, data);
}
/**
*
* PUT /api/v1/groups/:id/name
*/
async setGroupName(groupId: string, groupName: string): Promise<void> {
await api.put(`/groups/${encodeURIComponent(groupId)}/name`, {
group_name: groupName,
});
}
/**
*
* PUT /api/v1/groups/:id/avatar
*/
async setGroupAvatar(groupId: string, avatarUrl: string): Promise<GroupResponse> {
const response = await api.put<GroupResponse>(
`/groups/${encodeURIComponent(groupId)}/avatar`,
{
avatar: avatarUrl,
}
);
return normalizeGroup(response.data);
}
// ==================== 群公告 ====================
/**
*
* POST /api/v1/groups/:id/announcements
*/
async createAnnouncement(
groupId: string,
data: CreateAnnouncementRequest
): Promise<GroupAnnouncementResponse> {
const response = await api.post<GroupAnnouncementResponse>(
`/groups/${encodeURIComponent(groupId)}/announcements`,
data
);
return normalizeAnnouncement(response.data);
}
/**
*
* GET /api/v1/groups/:id/announcements
*/
async getAnnouncements(
groupId: string,
page = 1,
pageSize = 20
): Promise<GroupAnnouncementListResponse> {
try {
const response = await api.get<GroupAnnouncementListResponse>(
`/groups/${encodeURIComponent(groupId)}/announcements`,
{
page,
page_size: pageSize,
}
);
return normalizeAnnouncementListResponse(response.data);
} catch (error) {
console.error('获取群公告列表失败:', error);
return {
list: [],
total: 0,
page: 1,
page_size: pageSize,
total_pages: 0,
};
}
}
/**
*
* DELETE /api/v1/groups/:id/announcements/:announcement_id
*/
async deleteAnnouncement(groupId: string, announcementId: number): Promise<void> {
await api.delete(
`/groups/${encodeURIComponent(groupId)}/announcements/${encodeURIComponent(String(announcementId))}`
);
}
// ==================== 游标分页方法 ====================
/**
*
* GET /api/v1/groups/cursor
* @param params
*/
async getGroupsCursor(
params: CursorPaginationRequest = {}
): Promise<CursorPaginationResponse<GroupResponse>> {
try {
const response = await api.get<CursorPaginationResponse<GroupResponse>>('/groups/cursor', params);
return normalizeGroupCursor(response.data);
} catch (error) {
console.error('获取群组列表失败:', error);
return {
list: [],
next_cursor: null,
prev_cursor: null,
has_more: false,
};
}
}
/**
*
* GET /api/v1/groups/:id/members/cursor
* @param groupId ID
* @param params
*/
async getGroupMembersCursor(
groupId: string,
params: CursorPaginationRequest = {}
): Promise<CursorPaginationResponse<GroupMemberResponse>> {
try {
const response = await api.get<CursorPaginationResponse<GroupMemberResponse>>(
`/groups/${encodeURIComponent(groupId)}/members/cursor`,
params
);
return normalizeMemberCursor(response.data);
} catch (error) {
console.error('获取群组成员列表失败:', error);
return {
list: [],
next_cursor: null,
prev_cursor: null,
has_more: false,
};
}
}
/**
*
* GET /api/v1/groups/:id/announcements/cursor
* @param groupId ID
* @param params
*/
async getGroupAnnouncementsCursor(
groupId: string,
params: CursorPaginationRequest = {}
): Promise<CursorPaginationResponse<GroupAnnouncementResponse>> {
try {
const response = await api.get<CursorPaginationResponse<GroupAnnouncementResponse>>(
`/groups/${encodeURIComponent(groupId)}/announcements/cursor`,
params
);
return normalizeAnnouncementCursor(response.data);
} catch (error) {
console.error('获取群公告列表失败:', error);
return {
list: [],
next_cursor: null,
prev_cursor: null,
has_more: false,
};
}
}
}
// 导出群组服务实例
export const groupService = new GroupService();
// 导出类型以方便使用
export type {
GroupResponse,
GroupMemberResponse,
GroupAnnouncementResponse,
GroupListResponse,
GroupMemberListResponse,
GroupAnnouncementListResponse,
CreateGroupRequest,
InviteMembersRequest,
TransferOwnerRequest,
SetRoleRequest,
SetNicknameRequest,
SetMuteAllRequest,
SetJoinTypeRequest,
CreateAnnouncementRequest,
SetGroupAvatarRequest,
};