feat: 支持会员管理筛选

This commit is contained in:
richarjiang
2026-04-07 09:22:58 +08:00
parent 58c7588a96
commit 0ca93ec97e
6 changed files with 129 additions and 8 deletions

View File

@@ -6,7 +6,7 @@ import {
Query,
UseGuards,
} from '@nestjs/common'
import { UserRole } from '@mp-pilates/shared'
import { UserRole, CardTypeCategory } from '@mp-pilates/shared'
import { JwtAuthGuard } from '../auth/jwt-auth.guard'
import { RolesGuard } from '../auth/roles.guard'
import { Roles } from '../auth/roles.decorator'
@@ -14,6 +14,8 @@ import { CurrentUser } from '../common/decorators/current-user.decorator'
import { UserService } from './user.service'
import { UpdateProfileDto } from './dto/update-profile.dto'
const VALID_CARD_TYPES = new Set<string>(Object.values(CardTypeCategory))
@UseGuards(JwtAuthGuard)
@Controller()
export class UserController {
@@ -46,11 +48,17 @@ export class UserController {
@Query('page') page?: string,
@Query('limit') limit?: string,
@Query('search') search?: string,
@Query('cardType') cardType?: string,
) {
const validCardType =
cardType && cardType !== 'undefined' && (VALID_CARD_TYPES.has(cardType) || cardType === 'NONE')
? cardType
: undefined
return this.userService.getMembers(
page ? Number(page) : 1,
limit ? Number(limit) : 20,
search && search !== 'undefined' ? search : undefined,
validCardType,
)
}
}

View File

@@ -1,8 +1,10 @@
import { Injectable, NotFoundException } from '@nestjs/common'
import { MembershipStatus, BookingStatus, UserRole } from '@mp-pilates/shared'
import { MembershipStatus, BookingStatus, UserRole, CardTypeCategory } from '@mp-pilates/shared'
import type { PaginatedData, UserProfileResponse, UserStatsResponse } from '@mp-pilates/shared'
import { PrismaService } from '../prisma/prisma.service'
const VALID_CARD_TYPES = new Set<string>(Object.values(CardTypeCategory))
@Injectable()
export class UserService {
constructor(private readonly prisma: PrismaService) {}
@@ -124,6 +126,7 @@ export class UserService {
page: number,
limit: number,
search?: string,
cardType?: string,
): Promise<PaginatedData<{
userId: string
openid: string
@@ -134,7 +137,16 @@ export class UserService {
completedBookings: number
cancelledBookings: number
}>> {
const where = search
const where: {
OR?: Array<{ [key: string]: unknown }>
memberships?: {
some: {
status: MembershipStatus
cardType?: { type: CardTypeCategory }
}
}
NOT?: { memberships?: { some: { status: MembershipStatus } } }
} = search
? {
OR: [
{ nickname: { contains: search, mode: 'insensitive' as const } },
@@ -144,6 +156,18 @@ export class UserService {
}
: {}
// cardType filter: NONE = no active membership, otherwise filter by card type category
if (cardType === 'NONE') {
where.NOT = { memberships: { some: { status: MembershipStatus.ACTIVE } } }
} else if (cardType && VALID_CARD_TYPES.has(cardType)) {
where.memberships = {
some: {
status: MembershipStatus.ACTIVE,
cardType: { type: cardType as CardTypeCategory },
},
}
}
const [users, total] = await Promise.all([
this.prisma.user.findMany({
where,

File diff suppressed because one or more lines are too long