@svton/cli 1.2.0 → 1.2.2

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.
Files changed (103) hide show
  1. package/dist/index.js +20 -7
  2. package/dist/index.mjs +20 -7
  3. package/features.json +339 -0
  4. package/package.json +4 -1
  5. package/templates/apps/admin/next-env.d.ts +2 -0
  6. package/templates/apps/admin/next.config.js +15 -0
  7. package/templates/apps/admin/package.json.tpl +54 -0
  8. package/templates/apps/admin/postcss.config.js +6 -0
  9. package/templates/apps/admin/src/app/globals.css +37 -0
  10. package/templates/apps/admin/src/app/layout.tsx +19 -0
  11. package/templates/apps/admin/src/app/login/page.tsx +96 -0
  12. package/templates/apps/admin/src/app/page.tsx +8 -0
  13. package/templates/apps/admin/src/app/users/page.tsx +165 -0
  14. package/templates/apps/admin/src/components/ui/switch.tsx +29 -0
  15. package/templates/apps/admin/src/hooks/useAPI.ts +130 -0
  16. package/templates/apps/admin/src/lib/api-client.ts +112 -0
  17. package/templates/apps/admin/src/lib/api-server.ts +95 -0
  18. package/templates/apps/admin/tailwind.config.js +54 -0
  19. package/templates/apps/admin/tsconfig.json +22 -0
  20. package/templates/apps/backend/.env.example +29 -0
  21. package/templates/apps/backend/nest-cli.json +8 -0
  22. package/templates/apps/backend/package.json.tpl +57 -0
  23. package/templates/apps/backend/prisma/schema.prisma +72 -0
  24. package/templates/apps/backend/prisma/seed.ts +32 -0
  25. package/templates/apps/backend/src/app.controller.ts +15 -0
  26. package/templates/apps/backend/src/app.module.ts +85 -0
  27. package/templates/apps/backend/src/app.service.ts +12 -0
  28. package/templates/apps/backend/src/auth/auth.controller.ts +31 -0
  29. package/templates/apps/backend/src/auth/auth.module.ts +27 -0
  30. package/templates/apps/backend/src/auth/auth.service.ts +89 -0
  31. package/templates/apps/backend/src/auth/jwt-auth.guard.ts +5 -0
  32. package/templates/apps/backend/src/auth/jwt.strategy.ts +27 -0
  33. package/templates/apps/backend/src/config/env.schema.ts +35 -0
  34. package/templates/apps/backend/src/main.ts +51 -0
  35. package/templates/apps/backend/src/object-storage/object-storage.controller.ts +114 -0
  36. package/templates/apps/backend/src/object-storage/object-storage.module.ts +7 -0
  37. package/templates/apps/backend/src/prisma/prisma.module.ts +9 -0
  38. package/templates/apps/backend/src/prisma/prisma.service.ts +13 -0
  39. package/templates/apps/backend/src/user/user.controller.ts +50 -0
  40. package/templates/apps/backend/src/user/user.module.ts +12 -0
  41. package/templates/apps/backend/src/user/user.service.ts +117 -0
  42. package/templates/apps/backend/tsconfig.json +23 -0
  43. package/templates/apps/mobile/babel.config.js +8 -0
  44. package/templates/apps/mobile/config/index.ts +65 -0
  45. package/templates/apps/mobile/package.json.tpl +48 -0
  46. package/templates/apps/mobile/project.config.json.tpl +17 -0
  47. package/templates/apps/mobile/src/app.config.ts +9 -0
  48. package/templates/apps/mobile/src/app.scss +4 -0
  49. package/templates/apps/mobile/src/app.ts +8 -0
  50. package/templates/apps/mobile/src/hooks/useAPI.ts +285 -0
  51. package/templates/apps/mobile/src/pages/index/index.scss +7 -0
  52. package/templates/apps/mobile/src/pages/index/index.tsx +49 -0
  53. package/templates/apps/mobile/src/services/api.ts +155 -0
  54. package/templates/apps/mobile/src/services/upload.service.ts +41 -0
  55. package/templates/apps/mobile/tsconfig.json +21 -0
  56. package/templates/configs/authz.config.ts +10 -0
  57. package/templates/configs/cache.config.ts +14 -0
  58. package/templates/configs/oauth.config.ts +20 -0
  59. package/templates/configs/payment.config.ts +44 -0
  60. package/templates/configs/queue.config.ts +21 -0
  61. package/templates/configs/rate-limit.config.ts +16 -0
  62. package/templates/configs/sms.config.ts +11 -0
  63. package/templates/configs/storage.config.ts +14 -0
  64. package/templates/examples/README.md +258 -0
  65. package/templates/examples/authz/README.md +273 -0
  66. package/templates/examples/authz/roles.guard.ts +37 -0
  67. package/templates/examples/authz/user.controller.ts +116 -0
  68. package/templates/examples/cache/README.md +82 -0
  69. package/templates/examples/cache/user.controller.ts +42 -0
  70. package/templates/examples/cache/user.service.ts +78 -0
  71. package/templates/examples/oauth/README.md +192 -0
  72. package/templates/examples/oauth/auth.controller.ts +99 -0
  73. package/templates/examples/oauth/auth.service.ts +97 -0
  74. package/templates/examples/payment/README.md +151 -0
  75. package/templates/examples/payment/order.controller.ts +56 -0
  76. package/templates/examples/payment/order.service.ts +132 -0
  77. package/templates/examples/payment/webhook.controller.ts +73 -0
  78. package/templates/examples/queue/README.md +134 -0
  79. package/templates/examples/queue/email.controller.ts +34 -0
  80. package/templates/examples/queue/email.processor.ts +68 -0
  81. package/templates/examples/queue/email.service.ts +64 -0
  82. package/templates/examples/rate-limit/README.md +249 -0
  83. package/templates/examples/rate-limit/api.controller.ts +113 -0
  84. package/templates/examples/sms/README.md +121 -0
  85. package/templates/examples/sms/sms.service.ts +69 -0
  86. package/templates/examples/sms/verification.controller.ts +100 -0
  87. package/templates/examples/storage/README.md +224 -0
  88. package/templates/examples/storage/upload.controller.ts +117 -0
  89. package/templates/examples/storage/upload.service.ts +123 -0
  90. package/templates/packages/types/package.json.tpl +16 -0
  91. package/templates/packages/types/src/api.ts +88 -0
  92. package/templates/packages/types/src/common.ts +89 -0
  93. package/templates/packages/types/src/index.ts +3 -0
  94. package/templates/packages/types/tsconfig.json +16 -0
  95. package/templates/skills/authz.skill.md +42 -0
  96. package/templates/skills/base.skill.md +57 -0
  97. package/templates/skills/cache.skill.md +88 -0
  98. package/templates/skills/oauth.skill.md +41 -0
  99. package/templates/skills/payment.skill.md +129 -0
  100. package/templates/skills/queue.skill.md +140 -0
  101. package/templates/skills/rate-limit.skill.md +38 -0
  102. package/templates/skills/sms.skill.md +39 -0
  103. package/templates/skills/storage.skill.md +42 -0
@@ -0,0 +1,64 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { QueueService } from '@svton/nestjs-queue';
3
+ import { EmailData } from './email.processor';
4
+
5
+ @Injectable()
6
+ export class EmailService {
7
+ constructor(private readonly queueService: QueueService) {}
8
+
9
+ /**
10
+ * 发送单个邮件
11
+ */
12
+ async sendEmail(data: EmailData): Promise<void> {
13
+ await this.queueService.addJob('email', 'send', data);
14
+ }
15
+
16
+ /**
17
+ * 延迟发送邮件
18
+ */
19
+ async sendEmailDelayed(data: EmailData, delayMs: number): Promise<void> {
20
+ await this.queueService.addJob('email', 'send', data, {
21
+ delay: delayMs,
22
+ });
23
+ }
24
+
25
+ /**
26
+ * 发送邮件(带重试)
27
+ */
28
+ async sendEmailWithRetry(data: EmailData): Promise<void> {
29
+ await this.queueService.addJob('email', 'send', data, {
30
+ attempts: 3,
31
+ backoff: {
32
+ type: 'exponential',
33
+ delay: 1000,
34
+ },
35
+ });
36
+ }
37
+
38
+ /**
39
+ * 批量发送邮件
40
+ */
41
+ async sendBatchEmails(emails: EmailData[]): Promise<void> {
42
+ await this.queueService.addJob('email', 'sendBatch', { emails });
43
+ }
44
+
45
+ /**
46
+ * 定时发送邮件
47
+ */
48
+ async scheduleEmail(data: EmailData, cron: string): Promise<void> {
49
+ await this.queueService.addJob('email', 'send', data, {
50
+ repeat: {
51
+ cron, // 例如: '0 9 * * *' 每天 9 点
52
+ },
53
+ });
54
+ }
55
+
56
+ /**
57
+ * 高优先级邮件
58
+ */
59
+ async sendUrgentEmail(data: EmailData): Promise<void> {
60
+ await this.queueService.addJob('email', 'send', data, {
61
+ priority: 1, // 数字越小优先级越高
62
+ });
63
+ }
64
+ }
@@ -0,0 +1,249 @@
1
+ # 限流功能示例
2
+
3
+ 本示例展示如何使用 `@svton/nestjs-rate-limit` 模块实现接口限流。
4
+
5
+ ## 文件说明
6
+
7
+ - `api.controller.ts` - API 控制器,展示不同的限流策略
8
+
9
+ ## 核心装饰器
10
+
11
+ ### @RateLimit - 接口限流
12
+
13
+ ```typescript
14
+ @RateLimit({ ttl: 60, limit: 10 })
15
+ @Get('api')
16
+ async api() {
17
+ return { message: 'Success' };
18
+ }
19
+ ```
20
+
21
+ 参数说明:
22
+ - `ttl`:时间窗口(秒)
23
+ - `limit`:时间窗口内最大请求数
24
+
25
+ ### @UseGuards(RateLimitGuard) - 启用限流
26
+
27
+ ```typescript
28
+ @Controller('api')
29
+ @UseGuards(RateLimitGuard)
30
+ export class ApiController {
31
+ // 所有接口都会应用限流
32
+ }
33
+ ```
34
+
35
+ ## 限流策略
36
+
37
+ ### 1. 普通接口限流
38
+
39
+ 适用于一般 API 接口:
40
+
41
+ ```typescript
42
+ @RateLimit({ ttl: 60, limit: 10 }) // 每分钟 10 次
43
+ ```
44
+
45
+ ### 2. 严格限流
46
+
47
+ 适用于敏感操作:
48
+
49
+ ```typescript
50
+ @RateLimit({ ttl: 60, limit: 3 }) // 每分钟 3 次
51
+ ```
52
+
53
+ ### 3. 防暴力破解
54
+
55
+ 适用于登录、注册等接口:
56
+
57
+ ```typescript
58
+ @RateLimit({ ttl: 60, limit: 5 }) // 每分钟 5 次
59
+ ```
60
+
61
+ ### 4. 验证码限流
62
+
63
+ 防止验证码被刷:
64
+
65
+ ```typescript
66
+ @RateLimit({ ttl: 60, limit: 1 }) // 每分钟 1 次
67
+ ```
68
+
69
+ ### 5. 搜索接口限流
70
+
71
+ 高频接口:
72
+
73
+ ```typescript
74
+ @RateLimit({ ttl: 1, limit: 10 }) // 每秒 10 次
75
+ ```
76
+
77
+ ## 测试接口
78
+
79
+ ### 普通限流(每分钟 10 次)
80
+
81
+ ```bash
82
+ # 快速请求 15 次,第 11 次开始会被限流
83
+ for i in {1..15}; do
84
+ curl http://localhost:3000/examples/api/normal
85
+ echo ""
86
+ done
87
+ ```
88
+
89
+ ### 严格限流(每分钟 3 次)
90
+
91
+ ```bash
92
+ # 快速请求 5 次,第 4 次开始会被限流
93
+ for i in {1..5}; do
94
+ curl http://localhost:3000/examples/api/strict
95
+ echo ""
96
+ done
97
+ ```
98
+
99
+ ### 验证码限流(每分钟 1 次)
100
+
101
+ ```bash
102
+ # 请求 2 次,第 2 次会被限流
103
+ curl -X POST http://localhost:3000/examples/api/send-code
104
+ curl -X POST http://localhost:3000/examples/api/send-code
105
+ ```
106
+
107
+ ## 限流响应
108
+
109
+ 当请求被限流时,会返回 429 状态码:
110
+
111
+ ```json
112
+ {
113
+ "statusCode": 429,
114
+ "message": "Too Many Requests",
115
+ "error": "Rate limit exceeded"
116
+ }
117
+ ```
118
+
119
+ 响应头会包含限流信息:
120
+
121
+ ```
122
+ X-RateLimit-Limit: 10
123
+ X-RateLimit-Remaining: 0
124
+ X-RateLimit-Reset: 1234567890
125
+ ```
126
+
127
+ ## 环境变量配置
128
+
129
+ 在 `.env` 文件中配置:
130
+
131
+ ```env
132
+ REDIS_HOST=localhost
133
+ REDIS_PORT=6379
134
+ REDIS_PASSWORD=
135
+ ```
136
+
137
+ ## 全局限流配置
138
+
139
+ 在 `app.module.ts` 中配置全局限流:
140
+
141
+ ```typescript
142
+ RateLimitModule.forRoot({
143
+ redis: {
144
+ host: process.env.REDIS_HOST,
145
+ port: parseInt(process.env.REDIS_PORT),
146
+ },
147
+ global: {
148
+ ttl: 60,
149
+ limit: 100,
150
+ },
151
+ });
152
+ ```
153
+
154
+ ## 自定义限流 Key
155
+
156
+ 默认使用 IP 地址作为限流 key,可以自定义:
157
+
158
+ ```typescript
159
+ @RateLimit({
160
+ ttl: 60,
161
+ limit: 10,
162
+ keyGenerator: (req) => {
163
+ // 使用用户 ID 作为 key
164
+ return req.user?.id || req.ip;
165
+ },
166
+ })
167
+ ```
168
+
169
+ ## 最佳实践
170
+
171
+ 1. **合理设置限流**:根据接口特点设置不同的限流策略
172
+ 2. **分级限流**:普通用户和 VIP 用户使用不同的限流规则
173
+ 3. **白名单机制**:内部服务或管理员不受限流限制
174
+ 4. **友好提示**:返回清晰的错误信息和重试时间
175
+ 5. **监控告警**:监控限流触发情况,及时发现异常
176
+
177
+ ## 常见场景
178
+
179
+ ### 登录接口
180
+
181
+ ```typescript
182
+ @Post('login')
183
+ @RateLimit({ ttl: 60, limit: 5 }) // 防暴力破解
184
+ async login() { }
185
+ ```
186
+
187
+ ### 注册接口
188
+
189
+ ```typescript
190
+ @Post('register')
191
+ @RateLimit({ ttl: 3600, limit: 3 }) // 每小时 3 次
192
+ async register() { }
193
+ ```
194
+
195
+ ### 发送验证码
196
+
197
+ ```typescript
198
+ @Post('send-code')
199
+ @RateLimit({ ttl: 60, limit: 1 }) // 每分钟 1 次
200
+ async sendCode() { }
201
+ ```
202
+
203
+ ### 搜索接口
204
+
205
+ ```typescript
206
+ @Get('search')
207
+ @RateLimit({ ttl: 1, limit: 10 }) // 每秒 10 次
208
+ async search() { }
209
+ ```
210
+
211
+ ### 文件上传
212
+
213
+ ```typescript
214
+ @Post('upload')
215
+ @RateLimit({ ttl: 60, limit: 5 }) // 每分钟 5 次
216
+ async upload() { }
217
+ ```
218
+
219
+ ## 高级用法
220
+
221
+ ### 动态限流
222
+
223
+ 根据用户等级动态调整限流:
224
+
225
+ ```typescript
226
+ @RateLimit({
227
+ ttl: 60,
228
+ limit: (req) => {
229
+ const user = req.user;
230
+ if (user?.vip) return 100; // VIP 用户
231
+ return 10; // 普通用户
232
+ },
233
+ })
234
+ ```
235
+
236
+ ### 组合限流
237
+
238
+ 同时应用多个限流规则:
239
+
240
+ ```typescript
241
+ @RateLimit({ ttl: 1, limit: 10 }) // 每秒 10 次
242
+ @RateLimit({ ttl: 60, limit: 100 }) // 每分钟 100 次
243
+ @Get('api')
244
+ async api() { }
245
+ ```
246
+
247
+ ## 更多信息
248
+
249
+ 查看官方文档:https://751848178.github.io/svton/packages/nestjs-rate-limit
@@ -0,0 +1,113 @@
1
+ import { Controller, Get, Post, UseGuards } from '@nestjs/common';
2
+ import { RateLimit, RateLimitGuard } from '@svton/nestjs-rate-limit';
3
+
4
+ @Controller('examples/api')
5
+ @UseGuards(RateLimitGuard)
6
+ export class ApiController {
7
+ /**
8
+ * 普通接口 - 每分钟 10 次
9
+ */
10
+ @Get('normal')
11
+ @RateLimit({ ttl: 60, limit: 10 })
12
+ normal() {
13
+ return {
14
+ message: 'Normal API',
15
+ timestamp: Date.now(),
16
+ };
17
+ }
18
+
19
+ /**
20
+ * 严格限流 - 每分钟 3 次
21
+ */
22
+ @Get('strict')
23
+ @RateLimit({ ttl: 60, limit: 3 })
24
+ strict() {
25
+ return {
26
+ message: 'Strict rate limit API',
27
+ timestamp: Date.now(),
28
+ };
29
+ }
30
+
31
+ /**
32
+ * 宽松限流 - 每分钟 100 次
33
+ */
34
+ @Get('loose')
35
+ @RateLimit({ ttl: 60, limit: 100 })
36
+ loose() {
37
+ return {
38
+ message: 'Loose rate limit API',
39
+ timestamp: Date.now(),
40
+ };
41
+ }
42
+
43
+ /**
44
+ * 短时限流 - 每 10 秒 5 次
45
+ */
46
+ @Get('short-window')
47
+ @RateLimit({ ttl: 10, limit: 5 })
48
+ shortWindow() {
49
+ return {
50
+ message: 'Short window rate limit API',
51
+ timestamp: Date.now(),
52
+ };
53
+ }
54
+
55
+ /**
56
+ * 长时限流 - 每小时 1000 次
57
+ */
58
+ @Get('long-window')
59
+ @RateLimit({ ttl: 3600, limit: 1000 })
60
+ longWindow() {
61
+ return {
62
+ message: 'Long window rate limit API',
63
+ timestamp: Date.now(),
64
+ };
65
+ }
66
+
67
+ /**
68
+ * 登录接口 - 每分钟 5 次(防暴力破解)
69
+ */
70
+ @Post('login')
71
+ @RateLimit({ ttl: 60, limit: 5 })
72
+ login() {
73
+ return {
74
+ message: 'Login API',
75
+ token: 'mock_token',
76
+ };
77
+ }
78
+
79
+ /**
80
+ * 发送验证码 - 每分钟 1 次
81
+ */
82
+ @Post('send-code')
83
+ @RateLimit({ ttl: 60, limit: 1 })
84
+ sendCode() {
85
+ return {
86
+ message: 'Verification code sent',
87
+ code: '123456', // 开发环境返回
88
+ };
89
+ }
90
+
91
+ /**
92
+ * 搜索接口 - 每秒 10 次
93
+ */
94
+ @Get('search')
95
+ @RateLimit({ ttl: 1, limit: 10 })
96
+ search() {
97
+ return {
98
+ message: 'Search API',
99
+ results: [],
100
+ };
101
+ }
102
+
103
+ /**
104
+ * 无限流接口(用于测试)
105
+ */
106
+ @Get('unlimited')
107
+ unlimited() {
108
+ return {
109
+ message: 'Unlimited API',
110
+ timestamp: Date.now(),
111
+ };
112
+ }
113
+ }
@@ -0,0 +1,121 @@
1
+ # 短信功能示例
2
+
3
+ 本示例展示如何使用 `@svton/nestjs-sms` 模块发送短信。
4
+
5
+ ## 文件说明
6
+
7
+ - `sms.service.ts` - 短信服务,封装发送逻辑
8
+ - `verification.controller.ts` - 验证码控制器,提供发送和验证接口
9
+
10
+ ## 核心功能
11
+
12
+ ### 发送验证码
13
+
14
+ ```typescript
15
+ await this.smsService.sendVerificationCode('13800138000', '123456');
16
+ ```
17
+
18
+ ### 发送通知短信
19
+
20
+ ```typescript
21
+ await this.smsService.sendNotification('13800138000', {
22
+ message: '您的订单已发货',
23
+ });
24
+ ```
25
+
26
+ ### 批量发送
27
+
28
+ ```typescript
29
+ await this.smsService.sendBatch(
30
+ ['13800138000', '13800138001'],
31
+ 'SMS_123456',
32
+ { code: '123456' },
33
+ );
34
+ ```
35
+
36
+ ## 测试接口
37
+
38
+ ```bash
39
+ # 发送验证码
40
+ curl -X POST http://localhost:3000/examples/verification/send-code \
41
+ -H "Content-Type: application/json" \
42
+ -d '{"phoneNumber":"13800138000"}'
43
+
44
+ # 验证验证码
45
+ curl -X POST http://localhost:3000/examples/verification/verify-code \
46
+ -H "Content-Type: application/json" \
47
+ -d '{
48
+ "phoneNumber":"13800138000",
49
+ "code":"123456"
50
+ }'
51
+
52
+ # 发送通知短信
53
+ curl -X POST http://localhost:3000/examples/verification/send-notification \
54
+ -H "Content-Type: application/json" \
55
+ -d '{
56
+ "phoneNumber":"13800138000",
57
+ "message":"您的订单已发货"
58
+ }'
59
+ ```
60
+
61
+ ## 环境变量配置
62
+
63
+ 在 `.env` 文件中配置:
64
+
65
+ ```env
66
+ SMS_PROVIDER=aliyun
67
+ SMS_ACCESS_KEY_ID=your_access_key_id
68
+ SMS_ACCESS_KEY_SECRET=your_access_key_secret
69
+ SMS_SIGN_NAME=your_sign_name
70
+ ```
71
+
72
+ ## 短信模板配置
73
+
74
+ 需要在阿里云/腾讯云后台配置短信模板:
75
+
76
+ 1. 登录短信服务控制台
77
+ 2. 创建短信模板
78
+ 3. 等待审核通过
79
+ 4. 将模板 ID 替换到代码中的 `SMS_123456`
80
+
81
+ ## 最佳实践
82
+
83
+ 1. **频率限制**:同一手机号 1 分钟内只能发送一次
84
+ 2. **验证码有效期**:建议 5 分钟
85
+ 3. **使用 Redis**:生产环境使用 Redis 存储验证码
86
+ 4. **防刷机制**:添加图形验证码或滑块验证
87
+ 5. **日志记录**:记录发送日志,便于排查问题
88
+
89
+ ## 常见场景
90
+
91
+ ### 注册验证
92
+
93
+ ```typescript
94
+ // 1. 发送验证码
95
+ await this.smsService.sendVerificationCode(phoneNumber, code);
96
+
97
+ // 2. 用户输入验证码
98
+ // 3. 验证通过后创建账号
99
+ ```
100
+
101
+ ### 登录验证
102
+
103
+ ```typescript
104
+ // 1. 发送验证码
105
+ await this.smsService.sendVerificationCode(phoneNumber, code);
106
+
107
+ // 2. 验证码验证通过后生成 token
108
+ ```
109
+
110
+ ### 订单通知
111
+
112
+ ```typescript
113
+ await this.smsService.sendNotification(phoneNumber, {
114
+ orderNo: 'ORDER_001',
115
+ status: '已发货',
116
+ });
117
+ ```
118
+
119
+ ## 更多信息
120
+
121
+ 查看官方文档:https://751848178.github.io/svton/packages/nestjs-sms
@@ -0,0 +1,69 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { SmsService as SvtonSmsService } from '@svton/nestjs-sms';
3
+
4
+ @Injectable()
5
+ export class SmsService {
6
+ constructor(private readonly smsService: SvtonSmsService) {}
7
+
8
+ /**
9
+ * 发送验证码短信
10
+ */
11
+ async sendVerificationCode(phoneNumber: string, code: string): Promise<void> {
12
+ await this.smsService.send({
13
+ phoneNumber,
14
+ templateCode: 'SMS_123456', // TODO: 替换为实际的模板 ID
15
+ templateParams: {
16
+ code,
17
+ },
18
+ });
19
+ }
20
+
21
+ /**
22
+ * 发送通知短信
23
+ */
24
+ async sendNotification(
25
+ phoneNumber: string,
26
+ params: Record<string, string>,
27
+ ): Promise<void> {
28
+ await this.smsService.send({
29
+ phoneNumber,
30
+ templateCode: 'SMS_234567', // TODO: 替换为实际的模板 ID
31
+ templateParams: params,
32
+ });
33
+ }
34
+
35
+ /**
36
+ * 批量发送短信
37
+ */
38
+ async sendBatch(
39
+ phoneNumbers: string[],
40
+ templateCode: string,
41
+ templateParams: Record<string, string>,
42
+ ): Promise<void> {
43
+ await Promise.all(
44
+ phoneNumbers.map((phoneNumber) =>
45
+ this.smsService.send({
46
+ phoneNumber,
47
+ templateCode,
48
+ templateParams,
49
+ }),
50
+ ),
51
+ );
52
+ }
53
+
54
+ /**
55
+ * 发送营销短信
56
+ */
57
+ async sendMarketing(
58
+ phoneNumber: string,
59
+ content: string,
60
+ ): Promise<void> {
61
+ await this.smsService.send({
62
+ phoneNumber,
63
+ templateCode: 'SMS_345678', // TODO: 替换为实际的模板 ID
64
+ templateParams: {
65
+ content,
66
+ },
67
+ });
68
+ }
69
+ }
@@ -0,0 +1,100 @@
1
+ import { Controller, Post, Body, HttpCode } from '@nestjs/common';
2
+ import { SmsService } from './sms.service';
3
+
4
+ interface SendCodeDto {
5
+ phoneNumber: string;
6
+ }
7
+
8
+ interface VerifyCodeDto {
9
+ phoneNumber: string;
10
+ code: string;
11
+ }
12
+
13
+ @Controller('examples/verification')
14
+ export class VerificationController {
15
+ // 简单的内存存储,实际项目应使用 Redis
16
+ private verificationCodes = new Map<string, { code: string; expiresAt: number }>();
17
+
18
+ constructor(private readonly smsService: SmsService) {}
19
+
20
+ /**
21
+ * 发送验证码
22
+ */
23
+ @Post('send-code')
24
+ @HttpCode(200)
25
+ async sendCode(@Body() dto: SendCodeDto) {
26
+ // 生成 6 位验证码
27
+ const code = Math.floor(100000 + Math.random() * 900000).toString();
28
+
29
+ // 存储验证码(5 分钟有效期)
30
+ this.verificationCodes.set(dto.phoneNumber, {
31
+ code,
32
+ expiresAt: Date.now() + 5 * 60 * 1000,
33
+ });
34
+
35
+ // 发送短信
36
+ await this.smsService.sendVerificationCode(dto.phoneNumber, code);
37
+
38
+ return {
39
+ message: 'Verification code sent successfully',
40
+ // 开发环境返回验证码,生产环境不应返回
41
+ ...(process.env.NODE_ENV === 'development' && { code }),
42
+ };
43
+ }
44
+
45
+ /**
46
+ * 验证验证码
47
+ */
48
+ @Post('verify-code')
49
+ @HttpCode(200)
50
+ async verifyCode(@Body() dto: VerifyCodeDto) {
51
+ const stored = this.verificationCodes.get(dto.phoneNumber);
52
+
53
+ if (!stored) {
54
+ return {
55
+ success: false,
56
+ message: 'Verification code not found',
57
+ };
58
+ }
59
+
60
+ if (Date.now() > stored.expiresAt) {
61
+ this.verificationCodes.delete(dto.phoneNumber);
62
+ return {
63
+ success: false,
64
+ message: 'Verification code expired',
65
+ };
66
+ }
67
+
68
+ if (stored.code !== dto.code) {
69
+ return {
70
+ success: false,
71
+ message: 'Invalid verification code',
72
+ };
73
+ }
74
+
75
+ // 验证成功,删除验证码
76
+ this.verificationCodes.delete(dto.phoneNumber);
77
+
78
+ return {
79
+ success: true,
80
+ message: 'Verification successful',
81
+ };
82
+ }
83
+
84
+ /**
85
+ * 发送通知短信
86
+ */
87
+ @Post('send-notification')
88
+ @HttpCode(200)
89
+ async sendNotification(
90
+ @Body() dto: { phoneNumber: string; message: string },
91
+ ) {
92
+ await this.smsService.sendNotification(dto.phoneNumber, {
93
+ message: dto.message,
94
+ });
95
+
96
+ return {
97
+ message: 'Notification sent successfully',
98
+ };
99
+ }
100
+ }