feat: scaffold monorepo with shared types and NestJS server

- pnpm workspace with packages/app, packages/server, packages/shared
- @mp-pilates/shared: enums, constants, TypeScript interfaces for all 8 data models
- @mp-pilates/server: NestJS bootstrap with health check, validation pipe, CORS
- Base TypeScript config with strict mode
This commit is contained in:
richarjiang
2026-04-02 11:37:35 +08:00
parent 05337944d8
commit 90b54d1138
31 changed files with 7437 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
{
"name": "@mp-pilates/shared",
"version": "0.1.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
},
"devDependencies": {
"typescript": "^5.4.0"
}
}

View File

@@ -0,0 +1,21 @@
/** 默认免费取消截止小时数 */
export const DEFAULT_CANCEL_HOURS_LIMIT = 2
/** 默认时段容量(私教 = 1 */
export const DEFAULT_SLOT_CAPACITY = 1
/** 自动生成时段的天数范围 */
export const SLOT_GENERATION_DAYS = 14
/** 时段筛选区间 */
export const TIME_PERIODS = {
MORNING: { label: '上午', start: '06:00', end: '12:00' },
AFTERNOON: { label: '下午', start: '12:00', end: '18:00' },
EVENING: { label: '晚上', start: '18:00', end: '22:00' },
} as const
/** 日期选择器展示天数 */
export const DATE_SELECTOR_DAYS = 7
/** 星期映射 */
export const WEEKDAY_LABELS = ['', '周一', '周二', '周三', '周四', '周五', '周六', '周日'] as const

View File

@@ -0,0 +1,46 @@
// ===== User =====
export enum UserRole {
MEMBER = 'MEMBER',
ADMIN = 'ADMIN',
}
// ===== CardType =====
export enum CardTypeCategory {
TIMES = 'TIMES',
DURATION = 'DURATION',
TRIAL = 'TRIAL',
}
// ===== Membership =====
export enum MembershipStatus {
ACTIVE = 'ACTIVE',
EXPIRED = 'EXPIRED',
USED_UP = 'USED_UP',
}
// ===== TimeSlot =====
export enum TimeSlotStatus {
OPEN = 'OPEN',
FULL = 'FULL',
CLOSED = 'CLOSED',
}
export enum TimeSlotSource {
TEMPLATE = 'TEMPLATE',
MANUAL = 'MANUAL',
}
// ===== Booking =====
export enum BookingStatus {
CONFIRMED = 'CONFIRMED',
CANCELLED = 'CANCELLED',
COMPLETED = 'COMPLETED',
NO_SHOW = 'NO_SHOW',
}
// ===== Order =====
export enum OrderStatus {
PENDING = 'PENDING',
PAID = 'PAID',
REFUNDED = 'REFUNDED',
}

View File

@@ -0,0 +1,52 @@
// Enums
export {
UserRole,
CardTypeCategory,
MembershipStatus,
TimeSlotStatus,
TimeSlotSource,
BookingStatus,
OrderStatus,
} from './enums'
// Constants
export {
DEFAULT_CANCEL_HOURS_LIMIT,
DEFAULT_SLOT_CAPACITY,
SLOT_GENERATION_DAYS,
TIME_PERIODS,
DATE_SELECTOR_DAYS,
WEEKDAY_LABELS,
} from './constants'
// Types
export type {
User,
UserProfileResponse,
UpdateProfileDto,
UserStatsResponse,
CardType,
CreateCardTypeDto,
UpdateCardTypeDto,
Membership,
MembershipWithCardType,
WeekTemplate,
WeekTemplateInput,
TimeSlot,
TimeSlotWithBookingStatus,
CreateManualSlotDto,
Booking,
BookingWithDetails,
CreateBookingDto,
Order,
OrderWithDetails,
CreateOrderDto,
PaymentParams,
CreateOrderResponse,
StudioConfig,
UpdateStudioConfigDto,
ApiResponse,
PaginatedData,
PaginatedResponse,
PaginationQuery,
} from './types/index'

View File

@@ -0,0 +1,19 @@
export interface ApiResponse<T> {
readonly success: boolean
readonly data: T | null
readonly message: string | null
}
export interface PaginatedData<T> {
readonly items: readonly T[]
readonly total: number
readonly page: number
readonly limit: number
}
export type PaginatedResponse<T> = ApiResponse<PaginatedData<T>>
export interface PaginationQuery {
readonly page?: number
readonly limit?: number
}

View File

@@ -0,0 +1,31 @@
import { BookingStatus } from '../enums'
export interface Booking {
readonly id: string
readonly userId: string
readonly timeSlotId: string
readonly membershipId: string
readonly status: BookingStatus
readonly cancelledAt: string | null
readonly createdAt: string
readonly updatedAt: string
}
export interface BookingWithDetails extends Booking {
readonly timeSlot: {
readonly date: string
readonly startTime: string
readonly endTime: string
}
readonly membership: {
readonly id: string
readonly cardType: {
readonly name: string
}
}
}
export interface CreateBookingDto {
readonly timeSlotId: string
readonly membershipId: string
}

View File

@@ -0,0 +1,38 @@
import { CardTypeCategory } from '../enums'
export interface CardType {
readonly id: string
readonly name: string
readonly type: CardTypeCategory
readonly totalTimes: number | null
readonly durationDays: number
readonly price: number
readonly originalPrice: number | null
readonly description: string | null
readonly isActive: boolean
readonly sortOrder: number
readonly createdAt: string
readonly updatedAt: string
}
export interface CreateCardTypeDto {
readonly name: string
readonly type: CardTypeCategory
readonly totalTimes?: number
readonly durationDays: number
readonly price: number
readonly originalPrice?: number
readonly description?: string
readonly sortOrder?: number
}
export interface UpdateCardTypeDto {
readonly name?: string
readonly totalTimes?: number
readonly durationDays?: number
readonly price?: number
readonly originalPrice?: number
readonly description?: string
readonly isActive?: boolean
readonly sortOrder?: number
}

View File

@@ -0,0 +1,9 @@
export type { User, UserProfileResponse, UpdateProfileDto, UserStatsResponse } from './user'
export type { CardType, CreateCardTypeDto, UpdateCardTypeDto } from './card-type'
export type { Membership, MembershipWithCardType } from './membership'
export type { WeekTemplate, WeekTemplateInput } from './week-template'
export type { TimeSlot, TimeSlotWithBookingStatus, CreateManualSlotDto } from './time-slot'
export type { Booking, BookingWithDetails, CreateBookingDto } from './booking'
export type { Order, OrderWithDetails, CreateOrderDto, PaymentParams, CreateOrderResponse } from './order'
export type { StudioConfig, UpdateStudioConfigDto } from './studio'
export type { ApiResponse, PaginatedData, PaginatedResponse, PaginationQuery } from './api'

View File

@@ -0,0 +1,18 @@
import { MembershipStatus } from '../enums'
import { CardType } from './card-type'
export interface Membership {
readonly id: string
readonly userId: string
readonly cardTypeId: string
readonly remainingTimes: number | null
readonly startDate: string
readonly expireDate: string
readonly status: MembershipStatus
readonly createdAt: string
readonly updatedAt: string
}
export interface MembershipWithCardType extends Membership {
readonly cardType: CardType
}

View File

@@ -0,0 +1,42 @@
import { OrderStatus } from '../enums'
export interface Order {
readonly id: string
readonly userId: string
readonly cardTypeId: string
readonly orderNo: string
readonly amount: number
readonly status: OrderStatus
readonly wxTransactionId: string | null
readonly paidAt: string | null
readonly createdAt: string
readonly updatedAt: string
}
export interface OrderWithDetails extends Order {
readonly cardType: {
readonly name: string
readonly type: string
}
readonly user?: {
readonly nickname: string
readonly phone: string | null
}
}
export interface CreateOrderDto {
readonly cardTypeId: string
}
export interface PaymentParams {
readonly timeStamp: string
readonly nonceStr: string
readonly package: string
readonly signType: string
readonly paySign: string
}
export interface CreateOrderResponse {
readonly order: Order
readonly paymentParams: PaymentParams
}

View File

@@ -0,0 +1,25 @@
export interface StudioConfig {
readonly id: string
readonly name: string
readonly logo: string | null
readonly bannerUrl: string | null
readonly address: string
readonly phone: string
readonly latitude: number | null
readonly longitude: number | null
readonly cancelHoursLimit: number
readonly photos: string[]
readonly updatedAt: string
}
export interface UpdateStudioConfigDto {
readonly name?: string
readonly logo?: string
readonly bannerUrl?: string
readonly address?: string
readonly phone?: string
readonly latitude?: number
readonly longitude?: number
readonly cancelHoursLimit?: number
readonly photos?: string[]
}

View File

@@ -0,0 +1,29 @@
import { TimeSlotStatus, TimeSlotSource } from '../enums'
export interface TimeSlot {
readonly id: string
readonly date: string
readonly startTime: string
readonly endTime: string
readonly capacity: number
readonly bookedCount: number
readonly status: TimeSlotStatus
readonly source: TimeSlotSource
readonly templateId: string | null
readonly createdAt: string
readonly updatedAt: string
}
export interface TimeSlotWithBookingStatus extends TimeSlot {
/** 当前用户是否已预约此时段 */
readonly isBookedByMe: boolean
/** 当前用户在此时段的预约 ID用于取消 */
readonly myBookingId: string | null
}
export interface CreateManualSlotDto {
readonly date: string
readonly startTime: string
readonly endTime: string
readonly capacity?: number
}

View File

@@ -0,0 +1,36 @@
import { UserRole } from '../enums'
export interface User {
readonly id: string
readonly openid: string
readonly unionid: string | null
readonly phone: string | null
readonly nickname: string
readonly avatarUrl: string | null
readonly role: UserRole
readonly createdAt: string
readonly updatedAt: string
}
export interface UserProfileResponse {
readonly id: string
readonly phone: string | null
readonly nickname: string
readonly avatarUrl: string | null
readonly role: UserRole
readonly activeMembershipCount: number
readonly createdAt: string
}
export interface UpdateProfileDto {
readonly nickname?: string
readonly avatarUrl?: string
}
export interface UserStatsResponse {
readonly totalBookings: number
readonly totalDays: number
readonly monthBookings: number
readonly monthDays: number
readonly monthHours: number
}

View File

@@ -0,0 +1,18 @@
export interface WeekTemplate {
readonly id: string
readonly dayOfWeek: number
readonly startTime: string
readonly endTime: string
readonly capacity: number
readonly isActive: boolean
readonly createdAt: string
readonly updatedAt: string
}
export interface WeekTemplateInput {
readonly dayOfWeek: number
readonly startTime: string
readonly endTime: string
readonly capacity?: number
readonly isActive?: boolean
}

View File

@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"declarationMap": true
},
"include": ["src"]
}