@svton/cli 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +148 -75
- package/dist/index.mjs +148 -75
- package/package.json +1 -2
- package/templates/apps/admin/next-env.d.ts +0 -2
- package/templates/apps/admin/next.config.js +0 -15
- package/templates/apps/admin/package.json.tpl +0 -54
- package/templates/apps/admin/postcss.config.js +0 -6
- package/templates/apps/admin/src/app/globals.css +0 -37
- package/templates/apps/admin/src/app/layout.tsx +0 -19
- package/templates/apps/admin/src/app/login/page.tsx +0 -96
- package/templates/apps/admin/src/app/page.tsx +0 -8
- package/templates/apps/admin/src/app/users/page.tsx +0 -165
- package/templates/apps/admin/src/components/ui/switch.tsx +0 -29
- package/templates/apps/admin/src/hooks/useAPI.ts +0 -130
- package/templates/apps/admin/src/lib/api-client.ts +0 -100
- package/templates/apps/admin/tailwind.config.js +0 -54
- package/templates/apps/admin/tsconfig.json +0 -22
- package/templates/apps/backend/.env.example +0 -14
- package/templates/apps/backend/nest-cli.json +0 -8
- package/templates/apps/backend/package.json.tpl +0 -57
- package/templates/apps/backend/prisma/schema.prisma +0 -72
- package/templates/apps/backend/prisma/seed.ts +0 -32
- package/templates/apps/backend/src/app.controller.ts +0 -15
- package/templates/apps/backend/src/app.module.ts +0 -19
- package/templates/apps/backend/src/app.service.ts +0 -12
- package/templates/apps/backend/src/auth/auth.controller.ts +0 -31
- package/templates/apps/backend/src/auth/auth.module.ts +0 -27
- package/templates/apps/backend/src/auth/auth.service.ts +0 -89
- package/templates/apps/backend/src/auth/jwt-auth.guard.ts +0 -5
- package/templates/apps/backend/src/auth/jwt.strategy.ts +0 -27
- package/templates/apps/backend/src/main.ts +0 -40
- package/templates/apps/backend/src/prisma/prisma.module.ts +0 -9
- package/templates/apps/backend/src/prisma/prisma.service.ts +0 -13
- package/templates/apps/backend/src/user/user.controller.ts +0 -50
- package/templates/apps/backend/src/user/user.module.ts +0 -12
- package/templates/apps/backend/src/user/user.service.ts +0 -117
- package/templates/apps/backend/tsconfig.json +0 -23
- package/templates/apps/mobile/babel.config.js +0 -8
- package/templates/apps/mobile/config/index.ts +0 -65
- package/templates/apps/mobile/package.json.tpl +0 -48
- package/templates/apps/mobile/project.config.json.tpl +0 -17
- package/templates/apps/mobile/src/app.config.ts +0 -9
- package/templates/apps/mobile/src/app.scss +0 -4
- package/templates/apps/mobile/src/app.ts +0 -8
- package/templates/apps/mobile/src/pages/index/index.scss +0 -7
- package/templates/apps/mobile/src/pages/index/index.tsx +0 -49
- package/templates/apps/mobile/tsconfig.json +0 -21
- package/templates/packages/types/package.json.tpl +0 -16
- package/templates/packages/types/src/api.ts +0 -88
- package/templates/packages/types/src/common.ts +0 -89
- package/templates/packages/types/src/index.ts +0 -3
- package/templates/packages/types/tsconfig.json +0 -16
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{ORG_NAME}}/backend",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "{{PROJECT_NAME}} 后端 API",
|
|
5
|
-
"prisma": {
|
|
6
|
-
"seed": "ts-node prisma/seed.ts"
|
|
7
|
-
},
|
|
8
|
-
"scripts": {
|
|
9
|
-
"build": "nest build",
|
|
10
|
-
"dev": "NODE_ENV=development nest start --watch",
|
|
11
|
-
"start": "NODE_ENV=production node dist/main",
|
|
12
|
-
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
|
13
|
-
"test": "jest",
|
|
14
|
-
"type-check": "tsc --noEmit",
|
|
15
|
-
"prisma:generate": "prisma generate",
|
|
16
|
-
"prisma:migrate": "prisma migrate dev",
|
|
17
|
-
"prisma:migrate:deploy": "prisma migrate deploy",
|
|
18
|
-
"prisma:seed": "ts-node prisma/seed.ts",
|
|
19
|
-
"prisma:studio": "prisma studio",
|
|
20
|
-
"db:init": "pnpm prisma:generate && pnpm prisma:migrate && pnpm prisma:seed",
|
|
21
|
-
"clean": "rm -rf dist"
|
|
22
|
-
},
|
|
23
|
-
"dependencies": {
|
|
24
|
-
"@nestjs/common": "^10.3.0",
|
|
25
|
-
"@nestjs/config": "^3.1.1",
|
|
26
|
-
"@nestjs/core": "^10.3.0",
|
|
27
|
-
"@nestjs/jwt": "^10.2.0",
|
|
28
|
-
"@nestjs/passport": "^10.0.3",
|
|
29
|
-
"@nestjs/platform-express": "^10.3.0",
|
|
30
|
-
"@nestjs/swagger": "^7.1.17",
|
|
31
|
-
"@prisma/client": "^5.7.1",
|
|
32
|
-
"@svton/types": "^1.0.0",
|
|
33
|
-
"bcrypt": "^5.1.1",
|
|
34
|
-
"class-transformer": "^0.5.1",
|
|
35
|
-
"class-validator": "^0.14.0",
|
|
36
|
-
"ioredis": "^5.3.2",
|
|
37
|
-
"passport": "^0.7.0",
|
|
38
|
-
"passport-jwt": "^4.0.1",
|
|
39
|
-
"reflect-metadata": "^0.2.1",
|
|
40
|
-
"rxjs": "^7.8.1"
|
|
41
|
-
},
|
|
42
|
-
"devDependencies": {
|
|
43
|
-
"@nestjs/cli": "^10.2.1",
|
|
44
|
-
"@nestjs/schematics": "^10.0.3",
|
|
45
|
-
"@nestjs/testing": "^10.2.10",
|
|
46
|
-
"@types/bcrypt": "^5.0.2",
|
|
47
|
-
"@types/express": "^4.17.21",
|
|
48
|
-
"@types/jest": "^29.5.11",
|
|
49
|
-
"@types/node": "^20.10.0",
|
|
50
|
-
"@types/passport-jwt": "^3.0.13",
|
|
51
|
-
"jest": "^29.7.0",
|
|
52
|
-
"prisma": "^5.7.0",
|
|
53
|
-
"ts-jest": "^29.1.1",
|
|
54
|
-
"ts-node": "^10.9.2",
|
|
55
|
-
"typescript": "^5.3.0"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
generator client {
|
|
2
|
-
provider = "prisma-client-js"
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
datasource db {
|
|
6
|
-
provider = "mysql"
|
|
7
|
-
url = env("DATABASE_URL")
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// 用户模型
|
|
11
|
-
model User {
|
|
12
|
-
id Int @id @default(autoincrement())
|
|
13
|
-
phone String @unique
|
|
14
|
-
password String
|
|
15
|
-
nickname String
|
|
16
|
-
avatar String?
|
|
17
|
-
email String?
|
|
18
|
-
bio String? @db.Text
|
|
19
|
-
role String @default("user") // user, admin, super_admin
|
|
20
|
-
status Int @default(1) // 0: 禁用, 1: 启用
|
|
21
|
-
lastLoginAt DateTime?
|
|
22
|
-
createdAt DateTime @default(now())
|
|
23
|
-
updatedAt DateTime @updatedAt
|
|
24
|
-
|
|
25
|
-
contents Content[]
|
|
26
|
-
comments Comment[]
|
|
27
|
-
|
|
28
|
-
@@index([phone])
|
|
29
|
-
@@index([role])
|
|
30
|
-
@@index([status])
|
|
31
|
-
@@map("users")
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// 内容模型
|
|
35
|
-
model Content {
|
|
36
|
-
id Int @id @default(autoincrement())
|
|
37
|
-
title String
|
|
38
|
-
content String @db.Text
|
|
39
|
-
images String? @db.Text // JSON array
|
|
40
|
-
status String @default("draft") // draft, pending, published, rejected
|
|
41
|
-
viewCount Int @default(0)
|
|
42
|
-
likeCount Int @default(0)
|
|
43
|
-
commentCount Int @default(0)
|
|
44
|
-
authorId Int
|
|
45
|
-
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
|
46
|
-
createdAt DateTime @default(now())
|
|
47
|
-
updatedAt DateTime @updatedAt
|
|
48
|
-
|
|
49
|
-
comments Comment[]
|
|
50
|
-
|
|
51
|
-
@@index([authorId])
|
|
52
|
-
@@index([status])
|
|
53
|
-
@@index([createdAt])
|
|
54
|
-
@@map("contents")
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 评论模型
|
|
58
|
-
model Comment {
|
|
59
|
-
id Int @id @default(autoincrement())
|
|
60
|
-
content String @db.Text
|
|
61
|
-
contentId Int
|
|
62
|
-
content_ Content @relation(fields: [contentId], references: [id], onDelete: Cascade)
|
|
63
|
-
authorId Int
|
|
64
|
-
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
|
65
|
-
likeCount Int @default(0)
|
|
66
|
-
createdAt DateTime @default(now())
|
|
67
|
-
updatedAt DateTime @updatedAt
|
|
68
|
-
|
|
69
|
-
@@index([contentId])
|
|
70
|
-
@@index([authorId])
|
|
71
|
-
@@map("comments")
|
|
72
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { PrismaClient } from '@prisma/client';
|
|
2
|
-
|
|
3
|
-
const prisma = new PrismaClient();
|
|
4
|
-
|
|
5
|
-
async function main() {
|
|
6
|
-
console.log('🌱 开始初始化数据...');
|
|
7
|
-
|
|
8
|
-
// 创建管理员用户
|
|
9
|
-
const admin = await prisma.user.upsert({
|
|
10
|
-
where: { phone: '13800000000' },
|
|
11
|
-
update: {},
|
|
12
|
-
create: {
|
|
13
|
-
phone: '13800000000',
|
|
14
|
-
password: '$2b$10$example', // 需要用 bcrypt 生成
|
|
15
|
-
nickname: '管理员',
|
|
16
|
-
role: 'admin',
|
|
17
|
-
status: 1,
|
|
18
|
-
},
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
console.log('✅ 管理员用户:', admin);
|
|
22
|
-
console.log('🎉 数据初始化完成!');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
main()
|
|
26
|
-
.catch((e) => {
|
|
27
|
-
console.error(e);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
})
|
|
30
|
-
.finally(async () => {
|
|
31
|
-
await prisma.$disconnect();
|
|
32
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Controller, Get } from '@nestjs/common';
|
|
2
|
-
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
|
3
|
-
import { AppService } from './app.service';
|
|
4
|
-
|
|
5
|
-
@ApiTags('健康检查')
|
|
6
|
-
@Controller()
|
|
7
|
-
export class AppController {
|
|
8
|
-
constructor(private readonly appService: AppService) {}
|
|
9
|
-
|
|
10
|
-
@Get()
|
|
11
|
-
@ApiOperation({ summary: '健康检查' })
|
|
12
|
-
getHealth() {
|
|
13
|
-
return this.appService.getHealth();
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Module } from '@nestjs/common';
|
|
2
|
-
import { ConfigModule } from '@nestjs/config';
|
|
3
|
-
import { AppController } from './app.controller';
|
|
4
|
-
import { AppService } from './app.service';
|
|
5
|
-
import { PrismaModule } from './prisma/prisma.module';
|
|
6
|
-
import { AuthModule } from './auth/auth.module';
|
|
7
|
-
import { UserModule } from './user/user.module';
|
|
8
|
-
|
|
9
|
-
@Module({
|
|
10
|
-
imports: [
|
|
11
|
-
ConfigModule.forRoot({ isGlobal: true }),
|
|
12
|
-
PrismaModule,
|
|
13
|
-
AuthModule,
|
|
14
|
-
UserModule,
|
|
15
|
-
],
|
|
16
|
-
controllers: [AppController],
|
|
17
|
-
providers: [AppService],
|
|
18
|
-
})
|
|
19
|
-
export class AppModule {}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Controller, Post, Body, Get, UseGuards, Request } from '@nestjs/common';
|
|
2
|
-
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
|
3
|
-
import { AuthService } from './auth.service';
|
|
4
|
-
import { JwtAuthGuard } from './jwt-auth.guard';
|
|
5
|
-
import type { LoginDto, RegisterDto } from '{{ORG_NAME}}/types';
|
|
6
|
-
|
|
7
|
-
@ApiTags('认证')
|
|
8
|
-
@Controller('auth')
|
|
9
|
-
export class AuthController {
|
|
10
|
-
constructor(private authService: AuthService) {}
|
|
11
|
-
|
|
12
|
-
@Post('login')
|
|
13
|
-
@ApiOperation({ summary: '登录' })
|
|
14
|
-
async login(@Body() dto: LoginDto) {
|
|
15
|
-
return this.authService.login(dto);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
@Post('register')
|
|
19
|
-
@ApiOperation({ summary: '注册' })
|
|
20
|
-
async register(@Body() dto: RegisterDto) {
|
|
21
|
-
return this.authService.register(dto);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@Get('me')
|
|
25
|
-
@UseGuards(JwtAuthGuard)
|
|
26
|
-
@ApiBearerAuth()
|
|
27
|
-
@ApiOperation({ summary: '获取当前用户信息' })
|
|
28
|
-
async getProfile(@Request() req: any) {
|
|
29
|
-
return this.authService.validateUser(req.user.sub);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Module } from '@nestjs/common';
|
|
2
|
-
import { JwtModule } from '@nestjs/jwt';
|
|
3
|
-
import { PassportModule } from '@nestjs/passport';
|
|
4
|
-
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
5
|
-
import { AuthController } from './auth.controller';
|
|
6
|
-
import { AuthService } from './auth.service';
|
|
7
|
-
import { JwtStrategy } from './jwt.strategy';
|
|
8
|
-
import { PrismaModule } from '../prisma/prisma.module';
|
|
9
|
-
|
|
10
|
-
@Module({
|
|
11
|
-
imports: [
|
|
12
|
-
PrismaModule,
|
|
13
|
-
PassportModule.register({ defaultStrategy: 'jwt' }),
|
|
14
|
-
JwtModule.registerAsync({
|
|
15
|
-
imports: [ConfigModule],
|
|
16
|
-
useFactory: async (configService: ConfigService) => ({
|
|
17
|
-
secret: configService.get<string>('JWT_SECRET'),
|
|
18
|
-
signOptions: { expiresIn: '7d' },
|
|
19
|
-
}),
|
|
20
|
-
inject: [ConfigService],
|
|
21
|
-
}),
|
|
22
|
-
],
|
|
23
|
-
controllers: [AuthController],
|
|
24
|
-
providers: [AuthService, JwtStrategy],
|
|
25
|
-
exports: [AuthService],
|
|
26
|
-
})
|
|
27
|
-
export class AuthModule {}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { Injectable, UnauthorizedException, ConflictException } from '@nestjs/common';
|
|
2
|
-
import { JwtService } from '@nestjs/jwt';
|
|
3
|
-
import * as bcrypt from 'bcrypt';
|
|
4
|
-
import { PrismaService } from '../prisma/prisma.service';
|
|
5
|
-
import type { LoginDto, RegisterDto, LoginVo } from '{{ORG_NAME}}/types';
|
|
6
|
-
|
|
7
|
-
@Injectable()
|
|
8
|
-
export class AuthService {
|
|
9
|
-
constructor(
|
|
10
|
-
private prisma: PrismaService,
|
|
11
|
-
private jwtService: JwtService,
|
|
12
|
-
) {}
|
|
13
|
-
|
|
14
|
-
async login(dto: LoginDto): Promise<LoginVo> {
|
|
15
|
-
const user = await this.prisma.user.findUnique({
|
|
16
|
-
where: { phone: dto.phone },
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
if (!user) {
|
|
20
|
-
throw new UnauthorizedException('用户不存在');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const isPasswordValid = await bcrypt.compare(dto.password, user.password);
|
|
24
|
-
if (!isPasswordValid) {
|
|
25
|
-
throw new UnauthorizedException('密码错误');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (user.status === 0) {
|
|
29
|
-
throw new UnauthorizedException('账号已被禁用');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const payload = { sub: user.id, phone: user.phone, role: user.role };
|
|
33
|
-
const accessToken = this.jwtService.sign(payload);
|
|
34
|
-
const refreshToken = this.jwtService.sign(payload, { expiresIn: '30d' });
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
accessToken,
|
|
38
|
-
refreshToken,
|
|
39
|
-
user: {
|
|
40
|
-
id: user.id,
|
|
41
|
-
phone: user.phone,
|
|
42
|
-
nickname: user.nickname,
|
|
43
|
-
avatar: user.avatar,
|
|
44
|
-
role: user.role as any,
|
|
45
|
-
status: user.status as any,
|
|
46
|
-
createdAt: user.createdAt.toISOString(),
|
|
47
|
-
updatedAt: user.updatedAt.toISOString(),
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async register(dto: RegisterDto): Promise<LoginVo> {
|
|
53
|
-
const existingUser = await this.prisma.user.findUnique({
|
|
54
|
-
where: { phone: dto.phone },
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (existingUser) {
|
|
58
|
-
throw new ConflictException('手机号已被注册');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const hashedPassword = await bcrypt.hash(dto.password, 10);
|
|
62
|
-
|
|
63
|
-
const user = await this.prisma.user.create({
|
|
64
|
-
data: {
|
|
65
|
-
phone: dto.phone,
|
|
66
|
-
password: hashedPassword,
|
|
67
|
-
nickname: dto.nickname,
|
|
68
|
-
role: 'user',
|
|
69
|
-
status: 1,
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
return this.login({ phone: dto.phone, password: dto.password });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async validateUser(userId: number) {
|
|
77
|
-
return this.prisma.user.findUnique({
|
|
78
|
-
where: { id: userId },
|
|
79
|
-
select: {
|
|
80
|
-
id: true,
|
|
81
|
-
phone: true,
|
|
82
|
-
nickname: true,
|
|
83
|
-
avatar: true,
|
|
84
|
-
role: true,
|
|
85
|
-
status: true,
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2
|
-
import { PassportStrategy } from '@nestjs/passport';
|
|
3
|
-
import { ExtractJwt, Strategy } from 'passport-jwt';
|
|
4
|
-
import { ConfigService } from '@nestjs/config';
|
|
5
|
-
import { AuthService } from './auth.service';
|
|
6
|
-
|
|
7
|
-
@Injectable()
|
|
8
|
-
export class JwtStrategy extends PassportStrategy(Strategy) {
|
|
9
|
-
constructor(
|
|
10
|
-
private configService: ConfigService,
|
|
11
|
-
private authService: AuthService,
|
|
12
|
-
) {
|
|
13
|
-
super({
|
|
14
|
-
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
15
|
-
ignoreExpiration: false,
|
|
16
|
-
secretOrKey: configService.get<string>('JWT_SECRET'),
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async validate(payload: any) {
|
|
21
|
-
const user = await this.authService.validateUser(payload.sub);
|
|
22
|
-
if (!user) {
|
|
23
|
-
throw new UnauthorizedException();
|
|
24
|
-
}
|
|
25
|
-
return { sub: payload.sub, phone: payload.phone, role: payload.role };
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { NestFactory } from '@nestjs/core';
|
|
2
|
-
import { ValidationPipe } from '@nestjs/common';
|
|
3
|
-
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|
4
|
-
import { AppModule } from './app.module';
|
|
5
|
-
|
|
6
|
-
async function bootstrap() {
|
|
7
|
-
const app = await NestFactory.create(AppModule);
|
|
8
|
-
|
|
9
|
-
// 全局验证管道
|
|
10
|
-
app.useGlobalPipes(
|
|
11
|
-
new ValidationPipe({
|
|
12
|
-
whitelist: true,
|
|
13
|
-
transform: true,
|
|
14
|
-
forbidNonWhitelisted: true,
|
|
15
|
-
}),
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
// CORS
|
|
19
|
-
app.enableCors({
|
|
20
|
-
origin: true,
|
|
21
|
-
credentials: true,
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// Swagger 文档
|
|
25
|
-
const config = new DocumentBuilder()
|
|
26
|
-
.setTitle('API 文档')
|
|
27
|
-
.setDescription('项目 API 接口文档')
|
|
28
|
-
.setVersion('1.0')
|
|
29
|
-
.addBearerAuth()
|
|
30
|
-
.build();
|
|
31
|
-
const document = SwaggerModule.createDocument(app, config);
|
|
32
|
-
SwaggerModule.setup('api-docs', app, document);
|
|
33
|
-
|
|
34
|
-
const port = process.env.PORT || 3000;
|
|
35
|
-
await app.listen(port);
|
|
36
|
-
console.log(`🚀 Server running on http://localhost:${port}`);
|
|
37
|
-
console.log(`📚 API Docs: http://localhost:${port}/api-docs`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
bootstrap();
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
|
2
|
-
import { PrismaClient } from '@prisma/client';
|
|
3
|
-
|
|
4
|
-
@Injectable()
|
|
5
|
-
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
|
6
|
-
async onModuleInit() {
|
|
7
|
-
await this.$connect();
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async onModuleDestroy() {
|
|
11
|
-
await this.$disconnect();
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Controller,
|
|
3
|
-
Get,
|
|
4
|
-
Put,
|
|
5
|
-
Delete,
|
|
6
|
-
Body,
|
|
7
|
-
Param,
|
|
8
|
-
Query,
|
|
9
|
-
UseGuards,
|
|
10
|
-
ParseIntPipe,
|
|
11
|
-
} from '@nestjs/common';
|
|
12
|
-
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
|
13
|
-
import { UserService } from './user.service';
|
|
14
|
-
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
|
15
|
-
import type { UserListParams, UpdateUserDto } from '{{ORG_NAME}}/types';
|
|
16
|
-
|
|
17
|
-
@ApiTags('用户管理')
|
|
18
|
-
@Controller('users')
|
|
19
|
-
@UseGuards(JwtAuthGuard)
|
|
20
|
-
@ApiBearerAuth()
|
|
21
|
-
export class UserController {
|
|
22
|
-
constructor(private userService: UserService) {}
|
|
23
|
-
|
|
24
|
-
@Get()
|
|
25
|
-
@ApiOperation({ summary: '获取用户列表' })
|
|
26
|
-
async findAll(@Query() params: UserListParams) {
|
|
27
|
-
return this.userService.findAll(params);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
@Get(':id')
|
|
31
|
-
@ApiOperation({ summary: '获取用户详情' })
|
|
32
|
-
async findOne(@Param('id', ParseIntPipe) id: number) {
|
|
33
|
-
return this.userService.findOne(id);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
@Put(':id')
|
|
37
|
-
@ApiOperation({ summary: '更新用户' })
|
|
38
|
-
async update(
|
|
39
|
-
@Param('id', ParseIntPipe) id: number,
|
|
40
|
-
@Body() dto: UpdateUserDto,
|
|
41
|
-
) {
|
|
42
|
-
return this.userService.update(id, dto);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
@Delete(':id')
|
|
46
|
-
@ApiOperation({ summary: '删除用户' })
|
|
47
|
-
async remove(@Param('id', ParseIntPipe) id: number) {
|
|
48
|
-
return this.userService.remove(id);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Module } from '@nestjs/common';
|
|
2
|
-
import { UserController } from './user.controller';
|
|
3
|
-
import { UserService } from './user.service';
|
|
4
|
-
import { PrismaModule } from '../prisma/prisma.module';
|
|
5
|
-
|
|
6
|
-
@Module({
|
|
7
|
-
imports: [PrismaModule],
|
|
8
|
-
controllers: [UserController],
|
|
9
|
-
providers: [UserService],
|
|
10
|
-
exports: [UserService],
|
|
11
|
-
})
|
|
12
|
-
export class UserModule {}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
2
|
-
import { PrismaService } from '../prisma/prisma.service';
|
|
3
|
-
import type { UserListParams, UpdateUserDto, PaginatedResponse, UserVo } from '{{ORG_NAME}}/types';
|
|
4
|
-
|
|
5
|
-
@Injectable()
|
|
6
|
-
export class UserService {
|
|
7
|
-
constructor(private prisma: PrismaService) {}
|
|
8
|
-
|
|
9
|
-
async findAll(params: UserListParams): Promise<PaginatedResponse<UserVo>> {
|
|
10
|
-
const { page = 1, pageSize = 10, keyword, role, status } = params;
|
|
11
|
-
const skip = (page - 1) * pageSize;
|
|
12
|
-
|
|
13
|
-
const where: any = {};
|
|
14
|
-
if (keyword) {
|
|
15
|
-
where.OR = [
|
|
16
|
-
{ phone: { contains: keyword } },
|
|
17
|
-
{ nickname: { contains: keyword } },
|
|
18
|
-
];
|
|
19
|
-
}
|
|
20
|
-
if (role) where.role = role;
|
|
21
|
-
if (status !== undefined) where.status = status;
|
|
22
|
-
|
|
23
|
-
const [list, total] = await Promise.all([
|
|
24
|
-
this.prisma.user.findMany({
|
|
25
|
-
where,
|
|
26
|
-
skip,
|
|
27
|
-
take: pageSize,
|
|
28
|
-
orderBy: { createdAt: 'desc' },
|
|
29
|
-
select: {
|
|
30
|
-
id: true,
|
|
31
|
-
phone: true,
|
|
32
|
-
nickname: true,
|
|
33
|
-
avatar: true,
|
|
34
|
-
role: true,
|
|
35
|
-
status: true,
|
|
36
|
-
createdAt: true,
|
|
37
|
-
updatedAt: true,
|
|
38
|
-
},
|
|
39
|
-
}),
|
|
40
|
-
this.prisma.user.count({ where }),
|
|
41
|
-
]);
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
list: list.map((u) => ({
|
|
45
|
-
...u,
|
|
46
|
-
role: u.role as any,
|
|
47
|
-
status: u.status as any,
|
|
48
|
-
createdAt: u.createdAt.toISOString(),
|
|
49
|
-
updatedAt: u.updatedAt.toISOString(),
|
|
50
|
-
})),
|
|
51
|
-
total,
|
|
52
|
-
page,
|
|
53
|
-
pageSize,
|
|
54
|
-
totalPages: Math.ceil(total / pageSize),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async findOne(id: number): Promise<UserVo> {
|
|
59
|
-
const user = await this.prisma.user.findUnique({
|
|
60
|
-
where: { id },
|
|
61
|
-
select: {
|
|
62
|
-
id: true,
|
|
63
|
-
phone: true,
|
|
64
|
-
nickname: true,
|
|
65
|
-
avatar: true,
|
|
66
|
-
role: true,
|
|
67
|
-
status: true,
|
|
68
|
-
createdAt: true,
|
|
69
|
-
updatedAt: true,
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
if (!user) {
|
|
74
|
-
throw new NotFoundException('用户不存在');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
...user,
|
|
79
|
-
role: user.role as any,
|
|
80
|
-
status: user.status as any,
|
|
81
|
-
createdAt: user.createdAt.toISOString(),
|
|
82
|
-
updatedAt: user.updatedAt.toISOString(),
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async update(id: number, dto: UpdateUserDto): Promise<UserVo> {
|
|
87
|
-
await this.findOne(id);
|
|
88
|
-
|
|
89
|
-
const user = await this.prisma.user.update({
|
|
90
|
-
where: { id },
|
|
91
|
-
data: dto,
|
|
92
|
-
select: {
|
|
93
|
-
id: true,
|
|
94
|
-
phone: true,
|
|
95
|
-
nickname: true,
|
|
96
|
-
avatar: true,
|
|
97
|
-
role: true,
|
|
98
|
-
status: true,
|
|
99
|
-
createdAt: true,
|
|
100
|
-
updatedAt: true,
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
...user,
|
|
106
|
-
role: user.role as any,
|
|
107
|
-
status: user.status as any,
|
|
108
|
-
createdAt: user.createdAt.toISOString(),
|
|
109
|
-
updatedAt: user.updatedAt.toISOString(),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async remove(id: number): Promise<void> {
|
|
114
|
-
await this.findOne(id);
|
|
115
|
-
await this.prisma.user.delete({ where: { id } });
|
|
116
|
-
}
|
|
117
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"module": "commonjs",
|
|
4
|
-
"declaration": true,
|
|
5
|
-
"removeComments": true,
|
|
6
|
-
"emitDecoratorMetadata": true,
|
|
7
|
-
"experimentalDecorators": true,
|
|
8
|
-
"allowSyntheticDefaultImports": true,
|
|
9
|
-
"target": "ES2021",
|
|
10
|
-
"sourceMap": true,
|
|
11
|
-
"outDir": "./dist",
|
|
12
|
-
"baseUrl": "./",
|
|
13
|
-
"incremental": true,
|
|
14
|
-
"skipLibCheck": true,
|
|
15
|
-
"strictNullChecks": true,
|
|
16
|
-
"noImplicitAny": true,
|
|
17
|
-
"strictBindCallApply": true,
|
|
18
|
-
"forceConsistentCasingInFileNames": true,
|
|
19
|
-
"noFallthroughCasesInSwitch": true,
|
|
20
|
-
"esModuleInterop": true,
|
|
21
|
-
"resolveJsonModule": true
|
|
22
|
-
}
|
|
23
|
-
}
|