feat(server): add membership CRUD methods to UserService
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,8 @@ import { Injectable, NotFoundException } from '@nestjs/common'
|
|||||||
import { MembershipStatus, BookingStatus, UserRole, CardTypeCategory } from '@mp-pilates/shared'
|
import { MembershipStatus, BookingStatus, UserRole, CardTypeCategory } from '@mp-pilates/shared'
|
||||||
import type { PaginatedData, UserProfileResponse, UserStatsResponse } from '@mp-pilates/shared'
|
import type { PaginatedData, UserProfileResponse, UserStatsResponse } from '@mp-pilates/shared'
|
||||||
import { PrismaService } from '../prisma/prisma.service'
|
import { PrismaService } from '../prisma/prisma.service'
|
||||||
|
import { Membership, CardType, Prisma } from '@prisma/client'
|
||||||
|
import { UpdateUserMembershipDto } from './dto/update-user-membership.dto'
|
||||||
|
|
||||||
const VALID_CARD_TYPES = new Set<string>(Object.values(CardTypeCategory))
|
const VALID_CARD_TYPES = new Set<string>(Object.values(CardTypeCategory))
|
||||||
|
|
||||||
@@ -226,4 +228,68 @@ export class UserService {
|
|||||||
|
|
||||||
return { items, total, page, limit }
|
return { items, total, page, limit }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Membership management ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async getUserMembership(userId: string) {
|
||||||
|
const membership = await this.prisma.membership.findFirst({
|
||||||
|
where: { userId },
|
||||||
|
include: { cardType: true },
|
||||||
|
})
|
||||||
|
return membership ? { ...membership, cardType: { ...membership.cardType } } : null
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateUserMembership(userId: string, dto: UpdateUserMembershipDto) {
|
||||||
|
const now = new Date()
|
||||||
|
const expireDate = new Date(dto.expireDate)
|
||||||
|
const remainingTimes = dto.remainingTimes ?? null
|
||||||
|
const isTimeBased = remainingTimes !== null
|
||||||
|
|
||||||
|
let status: MembershipStatus = MembershipStatus.ACTIVE
|
||||||
|
if (expireDate < now) {
|
||||||
|
status = MembershipStatus.EXPIRED
|
||||||
|
} else if (isTimeBased && remainingTimes <= 0) {
|
||||||
|
status = MembershipStatus.USED_UP
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = await this.prisma.membership.findFirst({ where: { userId } })
|
||||||
|
|
||||||
|
let membership: Membership & { cardType: CardType }
|
||||||
|
if (existing) {
|
||||||
|
const updated = await this.prisma.membership.update({
|
||||||
|
where: { id: existing.id },
|
||||||
|
data: {
|
||||||
|
cardTypeId: dto.cardTypeId,
|
||||||
|
remainingTimes: dto.remainingTimes ?? null,
|
||||||
|
startDate: new Date(dto.startDate),
|
||||||
|
expireDate: new Date(dto.expireDate),
|
||||||
|
status,
|
||||||
|
},
|
||||||
|
include: { cardType: true },
|
||||||
|
})
|
||||||
|
membership = { ...updated, cardType: { ...updated.cardType } }
|
||||||
|
} else {
|
||||||
|
const created = await this.prisma.membership.create({
|
||||||
|
data: {
|
||||||
|
userId,
|
||||||
|
cardTypeId: dto.cardTypeId,
|
||||||
|
remainingTimes: dto.remainingTimes ?? null,
|
||||||
|
startDate: new Date(dto.startDate),
|
||||||
|
expireDate: new Date(dto.expireDate),
|
||||||
|
status,
|
||||||
|
},
|
||||||
|
include: { cardType: true },
|
||||||
|
})
|
||||||
|
membership = { ...created, cardType: { ...created.cardType } }
|
||||||
|
}
|
||||||
|
|
||||||
|
return membership
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteUserMembership(userId: string): Promise<void> {
|
||||||
|
await this.prisma.membership.updateMany({
|
||||||
|
where: { userId },
|
||||||
|
data: { status: MembershipStatus.EXPIRED },
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user