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:
41
.gitignore
vendored
Normal file
41
.gitignore
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
dist-ssr/
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Test coverage
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# Prisma
|
||||||
|
packages/server/prisma/*.db
|
||||||
|
packages/server/prisma/migrations/**/migration_lock.toml
|
||||||
|
|
||||||
|
# uni-app build
|
||||||
|
packages/app/dist/
|
||||||
|
packages/app/unpackage/
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
*.tgz
|
||||||
|
.cache/
|
||||||
1120
docs/superpowers/plans/2026-04-02-pilates-booking-miniprogram.md
Normal file
1120
docs/superpowers/plans/2026-04-02-pilates-booking-miniprogram.md
Normal file
File diff suppressed because it is too large
Load Diff
25
package.json
Normal file
25
package.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "mp-pilates",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev:server": "pnpm --filter @mp-pilates/server dev",
|
||||||
|
"dev:app": "pnpm --filter @mp-pilates/app dev:mp-weixin",
|
||||||
|
"build:shared": "pnpm --filter @mp-pilates/shared build",
|
||||||
|
"build:server": "pnpm --filter @mp-pilates/server build",
|
||||||
|
"build:app": "pnpm --filter @mp-pilates/app build:mp-weixin",
|
||||||
|
"test": "pnpm -r test",
|
||||||
|
"lint": "pnpm -r lint"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18",
|
||||||
|
"pnpm": ">=8"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"onlyBuiltDependencies": [
|
||||||
|
"@nestjs/core",
|
||||||
|
"@prisma/client",
|
||||||
|
"@prisma/engines",
|
||||||
|
"prisma"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
19
packages/server/.env.example
Normal file
19
packages/server/.env.example
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Database
|
||||||
|
DATABASE_URL=postgresql://user:pass@localhost:5432/mp_pilates
|
||||||
|
|
||||||
|
# JWT
|
||||||
|
JWT_SECRET=change-me-to-a-secure-random-string
|
||||||
|
|
||||||
|
# WeChat Mini Program
|
||||||
|
WX_APPID=your-appid
|
||||||
|
WX_SECRET=your-secret
|
||||||
|
|
||||||
|
# WeChat Pay
|
||||||
|
WX_MCH_ID=your-mch-id
|
||||||
|
WX_MCH_KEY=your-mch-key
|
||||||
|
WX_MCH_SERIAL_NO=your-serial-no
|
||||||
|
WX_MCH_CERT_PATH=./certs/apiclient_cert.pem
|
||||||
|
WX_MCH_KEY_PATH=./certs/apiclient_key.pem
|
||||||
|
|
||||||
|
# Server
|
||||||
|
PORT=3000
|
||||||
8
packages/server/nest-cli.json
Normal file
8
packages/server/nest-cli.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/nest-cli",
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"compilerOptions": {
|
||||||
|
"deleteOutDir": true
|
||||||
|
}
|
||||||
|
}
|
||||||
64
packages/server/package.json
Normal file
64
packages/server/package.json
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"name": "@mp-pilates/server",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "nest build",
|
||||||
|
"dev": "nest start --watch",
|
||||||
|
"start": "nest start",
|
||||||
|
"start:prod": "node dist/main",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:cov": "jest --coverage",
|
||||||
|
"prisma:generate": "prisma generate",
|
||||||
|
"prisma:migrate": "prisma migrate dev",
|
||||||
|
"prisma:seed": "ts-node prisma/seed.ts",
|
||||||
|
"lint": "eslint \"{src,test}/**/*.ts\""
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@mp-pilates/shared": "workspace:*",
|
||||||
|
"@nestjs/common": "^10.4.0",
|
||||||
|
"@nestjs/config": "^3.2.0",
|
||||||
|
"@nestjs/core": "^10.4.0",
|
||||||
|
"@nestjs/jwt": "^10.2.0",
|
||||||
|
"@nestjs/passport": "^10.0.3",
|
||||||
|
"@nestjs/platform-express": "^10.4.0",
|
||||||
|
"@nestjs/schedule": "^4.1.0",
|
||||||
|
"@prisma/client": "^5.19.0",
|
||||||
|
"class-transformer": "^0.5.1",
|
||||||
|
"class-validator": "^0.14.1",
|
||||||
|
"passport": "^0.7.0",
|
||||||
|
"passport-jwt": "^4.0.1",
|
||||||
|
"reflect-metadata": "^0.2.2",
|
||||||
|
"rxjs": "^7.8.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "^10.4.0",
|
||||||
|
"@nestjs/schematics": "^10.1.0",
|
||||||
|
"@nestjs/testing": "^10.4.0",
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/jest": "^29.5.12",
|
||||||
|
"@types/passport-jwt": "^4.0.1",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"prisma": "^5.19.0",
|
||||||
|
"source-map-support": "^0.5.21",
|
||||||
|
"supertest": "^7.0.0",
|
||||||
|
"ts-jest": "^29.2.0",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.4.0"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"moduleFileExtensions": ["js", "json", "ts"],
|
||||||
|
"rootDir": "src",
|
||||||
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
},
|
||||||
|
"collectCoverageFrom": ["**/*.(t|j)s", "!**/*.module.ts", "!main.ts"],
|
||||||
|
"coverageDirectory": "../coverage",
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"moduleNameMapper": {
|
||||||
|
"^@mp-pilates/shared(.*)$": "<rootDir>/../../shared/src$1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
packages/server/src/app.controller.ts
Normal file
9
packages/server/src/app.controller.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Controller, Get } from '@nestjs/common'
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class AppController {
|
||||||
|
@Get('health')
|
||||||
|
health() {
|
||||||
|
return { status: 'ok', timestamp: new Date().toISOString() }
|
||||||
|
}
|
||||||
|
}
|
||||||
14
packages/server/src/app.module.ts
Normal file
14
packages/server/src/app.module.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Module } from '@nestjs/common'
|
||||||
|
import { ConfigModule } from '@nestjs/config'
|
||||||
|
import { AppController } from './app.controller'
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot({
|
||||||
|
isGlobal: true,
|
||||||
|
envFilePath: ['.env.local', '.env'],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
controllers: [AppController],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
24
packages/server/src/main.ts
Normal file
24
packages/server/src/main.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core'
|
||||||
|
import { ValidationPipe } from '@nestjs/common'
|
||||||
|
import { ConfigService } from '@nestjs/config'
|
||||||
|
import { AppModule } from './app.module'
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule)
|
||||||
|
const configService = app.get(ConfigService)
|
||||||
|
|
||||||
|
app.setGlobalPrefix('api')
|
||||||
|
app.useGlobalPipes(
|
||||||
|
new ValidationPipe({
|
||||||
|
whitelist: true,
|
||||||
|
forbidNonWhitelisted: true,
|
||||||
|
transform: true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
app.enableCors()
|
||||||
|
|
||||||
|
const port = configService.get<number>('PORT', 3000)
|
||||||
|
await app.listen(port)
|
||||||
|
console.log(`Server running on http://localhost:${port}`)
|
||||||
|
}
|
||||||
|
bootstrap()
|
||||||
7
packages/server/tsconfig.build.json
Normal file
7
packages/server/tsconfig.build.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist", "test", "**/*.spec.ts"]
|
||||||
|
}
|
||||||
18
packages/server/tsconfig.json
Normal file
18
packages/server/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"target": "ES2021",
|
||||||
|
"sourceMap": true,
|
||||||
|
"incremental": true,
|
||||||
|
"declaration": false,
|
||||||
|
"declarationMap": false
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist", "test"]
|
||||||
|
}
|
||||||
13
packages/shared/package.json
Normal file
13
packages/shared/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
21
packages/shared/src/constants.ts
Normal file
21
packages/shared/src/constants.ts
Normal 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
|
||||||
46
packages/shared/src/enums.ts
Normal file
46
packages/shared/src/enums.ts
Normal 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',
|
||||||
|
}
|
||||||
52
packages/shared/src/index.ts
Normal file
52
packages/shared/src/index.ts
Normal 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'
|
||||||
19
packages/shared/src/types/api.ts
Normal file
19
packages/shared/src/types/api.ts
Normal 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
|
||||||
|
}
|
||||||
31
packages/shared/src/types/booking.ts
Normal file
31
packages/shared/src/types/booking.ts
Normal 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
|
||||||
|
}
|
||||||
38
packages/shared/src/types/card-type.ts
Normal file
38
packages/shared/src/types/card-type.ts
Normal 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
|
||||||
|
}
|
||||||
9
packages/shared/src/types/index.ts
Normal file
9
packages/shared/src/types/index.ts
Normal 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'
|
||||||
18
packages/shared/src/types/membership.ts
Normal file
18
packages/shared/src/types/membership.ts
Normal 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
|
||||||
|
}
|
||||||
42
packages/shared/src/types/order.ts
Normal file
42
packages/shared/src/types/order.ts
Normal 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
|
||||||
|
}
|
||||||
25
packages/shared/src/types/studio.ts
Normal file
25
packages/shared/src/types/studio.ts
Normal 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[]
|
||||||
|
}
|
||||||
29
packages/shared/src/types/time-slot.ts
Normal file
29
packages/shared/src/types/time-slot.ts
Normal 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
|
||||||
|
}
|
||||||
36
packages/shared/src/types/user.ts
Normal file
36
packages/shared/src/types/user.ts
Normal 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
|
||||||
|
}
|
||||||
18
packages/shared/src/types/week-template.ts
Normal file
18
packages/shared/src/types/week-template.ts
Normal 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
|
||||||
|
}
|
||||||
10
packages/shared/tsconfig.json
Normal file
10
packages/shared/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
5657
pnpm-lock.yaml
generated
Normal file
5657
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
packages:
|
||||||
|
- 'packages/*'
|
||||||
20
tsconfig.base.json
Normal file
20
tsconfig.base.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2021",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"paths": {
|
||||||
|
"@mp-pilates/shared": ["./packages/shared/src/index.ts"],
|
||||||
|
"@mp-pilates/shared/*": ["./packages/shared/src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user