perf: 完善订单管理

This commit is contained in:
richarjiang
2026-04-05 21:03:18 +08:00
parent fdb13c32c2
commit 4633ceea8c
29 changed files with 1000 additions and 261 deletions

View File

@@ -1,7 +1,7 @@
import { Injectable, NotFoundException } from '@nestjs/common'
import { MembershipStatus, BookingStatus, UserRole } from '@mp-pilates/shared'
import type { PaginatedData, UserProfileResponse, UserStatsResponse } from '@mp-pilates/shared'
import { PrismaService } from '../prisma/prisma.service'
import type { UserProfileResponse, UserStatsResponse } from '@mp-pilates/shared'
@Injectable()
export class UserService {
@@ -117,4 +117,89 @@ export class UserService {
monthHours,
}
}
// ─── Admin: paginated member list ─────────────────────────────────────────
async getMembers(
page: number,
limit: number,
search?: string,
): Promise<PaginatedData<{
userId: string
openid: string
nickname: string
phone: string | null
avatarUrl: string | null
totalBookings: number
completedBookings: number
cancelledBookings: number
}>> {
const where = search
? {
OR: [
{ nickname: { contains: search, mode: 'insensitive' as const } },
{ openid: { contains: search, mode: 'insensitive' as const } },
{ phone: { contains: search } },
],
}
: {}
const [users, total] = await Promise.all([
this.prisma.user.findMany({
where,
select: {
id: true,
openid: true,
nickname: true,
phone: true,
avatarUrl: true,
_count: {
select: {
bookings: true,
},
},
},
orderBy: { createdAt: 'desc' },
skip: (page - 1) * limit,
take: limit,
}),
this.prisma.user.count({ where }),
])
// Batch-fetch booking stats for the page of users
const userIds = users.map((u) => u.id)
const bookingStats = userIds.length
? await this.prisma.booking.groupBy({
by: ['userId', 'status'],
where: { userId: { in: userIds } },
_count: { id: true },
})
: []
const statsMap = new Map<string, { total: number; completed: number; cancelled: number }>()
for (const stat of bookingStats) {
const entry = statsMap.get(stat.userId) ?? { total: 0, completed: 0, cancelled: 0 }
entry.total += stat._count.id
if (stat.status === BookingStatus.COMPLETED) entry.completed += stat._count.id
if (stat.status === BookingStatus.CANCELLED) entry.cancelled += stat._count.id
statsMap.set(stat.userId, entry)
}
const items = users.map((u) => {
const s = statsMap.get(u.id) ?? { total: 0, completed: 0, cancelled: 0 }
return {
userId: u.id,
openid: u.openid,
nickname: u.nickname,
phone: u.phone,
avatarUrl: u.avatarUrl,
totalBookings: s.total,
completedBookings: s.completed,
cancelledBookings: s.cancelled,
}
})
return { items, total, page, limit }
}
}