create-dp-koa 1.0.0 → 1.0.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.
- package/package.json +2 -2
- package/template/.cursor/commands/cheatsheet-backend-controller.md +45 -0
- package/template/.cursor/commands/implement-backend-api-controller.md +60 -0
- package/template/.cursor/commands/plan-backend.md +97 -0
- package/template/.cursor/rules/00-backend-core.skill.md +61 -0
- package/template/.cursor/rules/01-backend-skill-router.skill.md +57 -0
- package/template/.cursor/rules/10-backend-api.skill.md +55 -0
- package/template/.cursor/rules/11-backend-controller-recipes.skill.md +188 -0
- package/template/.cursor/rules/20-backend-repository.skill.md +25 -0
- package/template/.cursor/rules/21-backend-service.skill.md +137 -0
- package/template/.cursor/rules/25-backend-comments-and-doc.skill.md +98 -0
- package/template/.cursor/rules/30-backend-validation.skill.md +342 -0
- package/template/.cursor/rules/40-backend-error-logging.skill.md +21 -0
- package/template/.cursor/rules/50-backend-bootstrap-lifecycle.skill.md +105 -0
- package/template/.cursor/rules/60-backend-router-registration.skill.md +73 -0
- package/template/.cursor/rules/70-backend-middleware.skill.md +100 -0
- package/template/.cursor/rules/80-backend-utils-and-libs.skill.md +108 -0
- package/template/.cursor/rules/85-backend-plugins.rule.md +65 -0
- package/template/.cursor/rules/90-backend-testing.skill.md +29 -0
- package/template/.cursor/rules/README.md +49 -0
- package/template/.trae/skills/11-backend-controller-recipes.skill.md +91 -10
- package/template/scripts/sync-template.mjs +0 -1
- package/template/src/controllers/example/ExampleController.ts +14 -0
- package/template/src/entity/index.ts +1 -15
- package/template/src/framework/decorator/processor/AnnotationProcessor.ts +5 -1
- package/template/src/routers/index.ts +0 -35
- package/template/src/utils/testDataInitializer.ts +2 -269
- package/template/test/controllers/example/ExampleController.test.ts +29 -31
- package/template/test/framework/annotation/AnnotationDecorators.test.ts +15 -15
- package/template/test/framework/annotation/AnnotationExecutor.test.ts +27 -32
- package/template/test/framework/annotation/AnnotationProcessor.test.ts +25 -24
- package/template/test/framework/annotation/CustomProcessors.test.ts +15 -25
- package/template/test/framework/annotation/NewRouter.test.ts +9 -7
- package/template/test/framework/annotation/ProcessorManager.test.ts +14 -27
- package/template/test/framework/databaseConfig.test.ts +2 -2
- package/template/test/integration/integration.test.ts +15 -72
- package/template/src/controllers/cacheManagement.controller.ts +0 -131
- package/template/src/controllers/captcha.controller.ts +0 -57
- package/template/src/controllers/example/NewAnnotationExampleController.ts +0 -159
- package/template/src/controllers/example/SwaggerExampleController.ts +0 -205
- package/template/src/controllers/example/TransactionExample.controller.ts +0 -336
- package/template/src/controllers/health.controller.ts +0 -235
- package/template/src/controllers/home/register.controller.ts +0 -58
- package/template/src/controllers/home/ytGoods.controller.ts +0 -92
- package/template/src/controllers/home/ytShop.controller.ts +0 -135
- package/template/src/controllers/home/ytUser.controller.ts +0 -89
- package/template/src/controllers/logManagement.controller.ts +0 -396
- package/template/src/controllers/public/emailSend.controller.ts +0 -65
- package/template/src/controllers/public/ytUserAuth.controller.ts +0 -174
- package/template/src/controllers/testData.controller.ts +0 -253
- package/template/src/dto/controller/example/NewAnnotationExampleController.dto.ts +0 -73
- package/template/src/dto/controller/home/emailSend.controller.dto.ts +0 -40
- package/template/src/dto/controller/home/register.controller.dto.ts +0 -45
- package/template/src/dto/controller/home/ytGoods.controller.dto.ts +0 -55
- package/template/src/dto/controller/home/ytShop.controller.dto.ts +0 -69
- package/template/src/dto/controller/home/ytUser.controller.dto.ts +0 -44
- package/template/src/dto/controller/public/ytUserAuth.controller.dto.ts +0 -63
- package/template/src/dto/goods.dto.ts +0 -212
- package/template/src/dto/service/ytService.dto.ts +0 -13
- package/template/src/dto/user.dto.ts +0 -177
- package/template/src/entity/columnTypes.ts +0 -13
- package/template/src/entity/goodsImagesUnlockKey.entity.ts +0 -33
- package/template/src/entity/goodsUnlocker.entity.ts +0 -34
- package/template/src/entity/shop.entity.ts +0 -52
- package/template/src/entity/shopUser.entity.ts +0 -41
- package/template/src/entity/ytGoods.entity.ts +0 -94
- package/template/src/entity/ytUser.entity.ts +0 -96
- package/template/src/examples/SwaggerProcessorExample.ts +0 -169
- package/template/src/examples/TransactionManagerDemo.ts +0 -377
- package/template/src/framework/utils/dynamicSwagger.ts +0 -410
- package/template/src/repository/UserRepository.ts +0 -122
- package/template/src/service/paramValidateTest.service.ts +0 -139
- package/template/src/service/ytGoods.service.ts +0 -42
- package/template/src/service/ytShop.service.ts +0 -90
- package/template/src/service/ytUser.service.ts +0 -451
- package/template/src/test/swaggerParameterTest.ts +0 -90
- package/template/test/controllers/controllers.test.ts +0 -173
- package/template/test/controllers/example/NewAnnotationExampleController.test.ts +0 -200
- package/template/test/framework/TransactionManagerDemo.test.ts +0 -363
- package/template/test/service/business.test.ts +0 -87
- package/template/test/service/paramValidateTest.service.test.ts +0 -184
- package/template/test/service/ytUser.service.test.ts +0 -566
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { Body,Post, ResponseValidateIf, ResponseValidator } from "@src/framework/decorator/controller";
|
|
2
|
-
import { BaseController, ControllerResponse } from "@src/controllers/base.controller";
|
|
3
|
-
import { SendEmailDto, SendEmailResponseDto } from "@src/dto/controller/home/emailSend.controller.dto";
|
|
4
|
-
import { Inject } from "dp-ioc2";
|
|
5
|
-
import * as jwt from "jsonwebtoken";
|
|
6
|
-
import { YtUserService } from "@src/service/ytUser.service";
|
|
7
|
-
import { logger } from "@src/framework/utils/logger";
|
|
8
|
-
import { AokEmailSender } from "@src/libs/aokEmailSender";
|
|
9
|
-
|
|
10
|
-
const emailSender = new AokEmailSender(process.env.aok_email_api_key ?? "330b853af20ef6762f259545ef7b4c17")
|
|
11
|
-
|
|
12
|
-
export class EmailSendController extends BaseController {
|
|
13
|
-
|
|
14
|
-
@Inject(YtUserService)
|
|
15
|
-
ytUserService: YtUserService;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* 短信验证码邮件
|
|
19
|
-
* @param body
|
|
20
|
-
* @returns
|
|
21
|
-
*/
|
|
22
|
-
@ResponseValidateIf(SendEmailResponseDto, (data) => data && data.data)
|
|
23
|
-
@Post()
|
|
24
|
-
async sendRegisterEmail(
|
|
25
|
-
@Body(SendEmailDto) body: SendEmailDto
|
|
26
|
-
): Promise<ControllerResponse<SendEmailResponseDto>> {
|
|
27
|
-
|
|
28
|
-
// 1. 检查邮箱是否已注册
|
|
29
|
-
// const exist = await this.ytUserService.findByEmail(body.email);
|
|
30
|
-
// if (exist) {
|
|
31
|
-
// return this.fail(-1, "该邮箱已注册");
|
|
32
|
-
// }
|
|
33
|
-
|
|
34
|
-
// 2. 生成随机验证码(4位数字)
|
|
35
|
-
const vcode = Math.floor(1000 + Math.random() * 9000).toString();
|
|
36
|
-
|
|
37
|
-
// 3. 设置验证码过期时间(5分钟后)
|
|
38
|
-
const expire = Date.now() + 5 * 60 * 1000;
|
|
39
|
-
|
|
40
|
-
// 4. 生成JWT token包含验证码和过期时间
|
|
41
|
-
const vcodeToken = jwt.sign(
|
|
42
|
-
{ vcode, expire, email: body.email },
|
|
43
|
-
process.env.jwt_secret ?? "123456",
|
|
44
|
-
{ expiresIn: "5m" }
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
// 5. 发送邮件验证码(模拟)
|
|
48
|
-
const result = await emailSender.sendEmail({
|
|
49
|
-
templateId: "E_106565864351",
|
|
50
|
-
templateData: {
|
|
51
|
-
vcode: String(vcode),
|
|
52
|
-
login_link: `
|
|
53
|
-
<a href="https://ytfx.yndaopu.com/login/${vcode}">快捷登录链接</a>
|
|
54
|
-
`
|
|
55
|
-
},
|
|
56
|
-
to: body.email,
|
|
57
|
-
})
|
|
58
|
-
// console.log(result)
|
|
59
|
-
// logger.info(`发送邮件验证码到${body.email}: ${vcode}`);
|
|
60
|
-
return this.success({
|
|
61
|
-
expire,
|
|
62
|
-
vcodeToken
|
|
63
|
-
}, "验证码发送成功");
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { Body, ControllerCache, Get, Post, Query, ResponseValidateIf } from "@src/framework/decorator/controller";
|
|
2
|
-
import { BaseController, ControllerResponse } from "@src/controllers/base.controller";
|
|
3
|
-
import { Inject } from "dp-ioc2";
|
|
4
|
-
import { YtUserService } from "@src/service/ytUser.service";
|
|
5
|
-
import jwt from 'jsonwebtoken';
|
|
6
|
-
import md5 from 'md5';
|
|
7
|
-
import svgCaptcha from 'svg-captcha';
|
|
8
|
-
import { GetYtUserBasicInfoDto, GetYtUserBasicInfoResponseDto, YtUserLoginAutoDto, YtUserLoginDto } from "@src/dto/controller/public/ytUserAuth.controller.dto";
|
|
9
|
-
import { YtUserEntity, YtUserStatusEnum, YtUserTypeEnum } from "@src/entity/ytUser.entity";
|
|
10
|
-
import { CommonServiceResultCode } from "@src/framework/types/ServiceResult";
|
|
11
|
-
import { logger } from "@src/framework/utils/logger";
|
|
12
|
-
import { isDebug } from "@src/framework/utils/function";
|
|
13
|
-
|
|
14
|
-
interface IvcodeInfo {
|
|
15
|
-
vcode: string
|
|
16
|
-
expire: number
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class YtUserAuthController extends BaseController {
|
|
20
|
-
@Inject(YtUserService)
|
|
21
|
-
ytUserService: YtUserService;
|
|
22
|
-
|
|
23
|
-
@Post()
|
|
24
|
-
async login(
|
|
25
|
-
@Body(YtUserLoginDto) body: YtUserLoginDto
|
|
26
|
-
): Promise<ControllerResponse<string>> {
|
|
27
|
-
|
|
28
|
-
// 验证注册码
|
|
29
|
-
let vcodeInfo: IvcodeInfo | null = null;
|
|
30
|
-
try {
|
|
31
|
-
vcodeInfo = jwt.verify(body.vcodeToken, process.env.jwt_secret ?? "123456") as any;
|
|
32
|
-
} catch (err) {
|
|
33
|
-
return this.fail(-1, "验证码可能失效");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!vcodeInfo) {
|
|
37
|
-
return this.fail(-1, "验证码解析失败");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (vcodeInfo?.expire < new Date().getTime()) {
|
|
41
|
-
return this.fail(-1, "验证码过期");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (vcodeInfo.vcode !== body.vcode) {
|
|
45
|
-
return this.fail(-1, "验证码不正确");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// 1. 验证用户是否存在
|
|
49
|
-
let user = await this.ytUserService.findByEmail(body.email);
|
|
50
|
-
if (!user) {
|
|
51
|
-
const ytUser = new YtUserEntity();
|
|
52
|
-
ytUser.nickName = '用户' + body.email.split("@")[0].slice(0, 5);
|
|
53
|
-
ytUser.email = body.email;
|
|
54
|
-
ytUser.avatar = "https://ytfx.yndaopu.com/static/images/Avatar-1.png";
|
|
55
|
-
// ytUser.password = md5(body.password);
|
|
56
|
-
let result = await this.ytUserService.add(ytUser);
|
|
57
|
-
// if (result.code != CommonServiceResultCode.SUCCESS) {
|
|
58
|
-
// logger.warn(`注册注册失败` + JSON.stringify(body), result.message);
|
|
59
|
-
// return this.fail(-1, "注册失败");
|
|
60
|
-
// }
|
|
61
|
-
user = result
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (user.status == YtUserStatusEnum.FORBIDDEN) {
|
|
65
|
-
return this.fail(-1, "用户已被禁用");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// 3. 生成JWT token
|
|
70
|
-
const token = jwt.sign(
|
|
71
|
-
{ userId: user.id, type: user.userType },
|
|
72
|
-
process.env.jwt_secret ?? "123456",
|
|
73
|
-
{ expiresIn: "7d" }
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
return this.success(token, "登录成功");
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* 自动注册和登录
|
|
82
|
-
* @param body
|
|
83
|
-
* @returns
|
|
84
|
-
*/
|
|
85
|
-
@Post()
|
|
86
|
-
async loginAuto(
|
|
87
|
-
@Body(YtUserLoginAutoDto) body: YtUserLoginAutoDto
|
|
88
|
-
): Promise<ControllerResponse<string>> {
|
|
89
|
-
|
|
90
|
-
// 验证注册码
|
|
91
|
-
let vcodeInfo: IvcodeInfo | null = null;
|
|
92
|
-
try {
|
|
93
|
-
vcodeInfo = jwt.verify(body.vcodeToken, process.env.jwt_secret ?? "123456") as any;
|
|
94
|
-
} catch (err) {
|
|
95
|
-
return this.fail(-1, "验证码可能失效");
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!vcodeInfo) {
|
|
99
|
-
return this.fail(-1, "验证码解析失败");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (vcodeInfo?.expire < new Date().getTime()) {
|
|
103
|
-
return this.fail(-1, "验证码过期");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (String(vcodeInfo.vcode).toLowerCase() !== String(body.vcode).toLowerCase()) {
|
|
107
|
-
return this.fail(-1, "验证码不正确");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
let user = new YtUserEntity();
|
|
111
|
-
user.nickName = `新用户-${vcodeInfo.vcode}`;
|
|
112
|
-
user.email = `${vcodeInfo.vcode}@example.com`
|
|
113
|
-
user.avatar = "https://ytfx.yndaopu.com/static/images/Avatar-1.png";
|
|
114
|
-
user = await this.ytUserService.add(user)
|
|
115
|
-
// 3. 生成JWT token
|
|
116
|
-
const token = jwt.sign(
|
|
117
|
-
{ userId: user.id, type: user.userType },
|
|
118
|
-
process.env.jwt_secret ?? "123456",
|
|
119
|
-
{ expiresIn: "360d" }
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
return this.success(token, "登录成功");
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
@ControllerCache((query: GetYtUserBasicInfoDto) => `YtUserAuthController-getYtUserBasicInfo-${query.id}`, {
|
|
126
|
-
ttl: {
|
|
127
|
-
max: 60 * 5,
|
|
128
|
-
min: 30,
|
|
129
|
-
},
|
|
130
|
-
enable: !isDebug()
|
|
131
|
-
})
|
|
132
|
-
@ResponseValidateIf(GetYtUserBasicInfoResponseDto, (data) => data && data.data)
|
|
133
|
-
@Get()
|
|
134
|
-
async getYtUserBasicInfo(
|
|
135
|
-
@Query(GetYtUserBasicInfoDto) query: GetYtUserBasicInfoDto,
|
|
136
|
-
): Promise<ControllerResponse<GetYtUserBasicInfoResponseDto | null>> {
|
|
137
|
-
let userResult = await this.ytUserService.getById(YtUserEntity, query.id);
|
|
138
|
-
|
|
139
|
-
if (!userResult) {
|
|
140
|
-
return this.fail(-1, "用户不存在");
|
|
141
|
-
}
|
|
142
|
-
return this.success({
|
|
143
|
-
id: userResult.id,
|
|
144
|
-
name: userResult.nickName,
|
|
145
|
-
avatar: userResult.avatar,
|
|
146
|
-
}, "获取成功");
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* 创建验证码
|
|
153
|
-
* @returns
|
|
154
|
-
*/
|
|
155
|
-
@Get()
|
|
156
|
-
async getCaptcha() {
|
|
157
|
-
const captcha = svgCaptcha.create({
|
|
158
|
-
size: 4,
|
|
159
|
-
ignoreChars: '0o1i',
|
|
160
|
-
noise: 2,
|
|
161
|
-
color: true
|
|
162
|
-
});
|
|
163
|
-
const token = jwt.sign(
|
|
164
|
-
{ vcode: captcha.text, expire: new Date().getTime() + 1000 * 60 * 5 },
|
|
165
|
-
process.env.jwt_secret ?? "123456"
|
|
166
|
-
);
|
|
167
|
-
return {
|
|
168
|
-
svg: captcha.data,
|
|
169
|
-
token
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import { Get, Post } from '@src/framework/decorator/controller';
|
|
2
|
-
import { Body } from '@src/framework/decorator/controller';
|
|
3
|
-
import { BaseController } from '@src/controllers/base.controller';
|
|
4
|
-
import { testDataInitializer } from '@src/utils/testDataInitializer';
|
|
5
|
-
import { getDataSource } from '@src/framework/utils/db';
|
|
6
|
-
import { logger } from '@src/framework/utils/logger';
|
|
7
|
-
|
|
8
|
-
export class TestDataController extends BaseController {
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 检查是否为内存数据库模式
|
|
12
|
-
*/
|
|
13
|
-
private isMemoryDatabase(): boolean {
|
|
14
|
-
const dataSource = getDataSource();
|
|
15
|
-
if (!dataSource || !dataSource.isInitialized) {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const driver = (dataSource as any).driver;
|
|
20
|
-
return driver && driver.options && driver.options.database === ':memory:';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 获取测试数据状态
|
|
25
|
-
*/
|
|
26
|
-
@Get('/test-data/status')
|
|
27
|
-
async getTestDataStatus() {
|
|
28
|
-
if (!this.isMemoryDatabase()) {
|
|
29
|
-
return this.fail(400, '当前不是内存数据库模式');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
const dataSource = getDataSource();
|
|
34
|
-
if (!dataSource) {
|
|
35
|
-
return this.fail(500, '数据库连接未初始化');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 统计各表的数据量
|
|
39
|
-
const userCount = await dataSource.query("SELECT COUNT(*) as count FROM yt_user");
|
|
40
|
-
const shopCount = await dataSource.query("SELECT COUNT(*) as count FROM shop");
|
|
41
|
-
const goodsCount = await dataSource.query("SELECT COUNT(*) as count FROM yt_goods");
|
|
42
|
-
const unlockKeyCount = await dataSource.query("SELECT COUNT(*) as count FROM goods_images_unlock_key");
|
|
43
|
-
const shopUserCount = await dataSource.query("SELECT COUNT(*) as count FROM shop_user");
|
|
44
|
-
|
|
45
|
-
return this.success({
|
|
46
|
-
isMemoryDatabase: true,
|
|
47
|
-
dataCounts: {
|
|
48
|
-
users: userCount[0]?.count || 0,
|
|
49
|
-
shops: shopCount[0]?.count || 0,
|
|
50
|
-
goods: goodsCount[0]?.count || 0,
|
|
51
|
-
unlockKeys: unlockKeyCount[0]?.count || 0,
|
|
52
|
-
shopUsers: shopUserCount[0]?.count || 0
|
|
53
|
-
},
|
|
54
|
-
timestamp: new Date().toISOString()
|
|
55
|
-
});
|
|
56
|
-
} catch (error) {
|
|
57
|
-
logger.error('获取测试数据状态失败:', error as Error);
|
|
58
|
-
return this.fail(500, `获取测试数据状态失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* 初始化测试数据
|
|
64
|
-
*/
|
|
65
|
-
@Post('/test-data/init')
|
|
66
|
-
async initTestData() {
|
|
67
|
-
if (!this.isMemoryDatabase()) {
|
|
68
|
-
return this.fail(400, '当前不是内存数据库模式');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
await testDataInitializer.initializeTestData();
|
|
73
|
-
logger.info('手动初始化测试数据完成');
|
|
74
|
-
|
|
75
|
-
return this.success({
|
|
76
|
-
message: '测试数据初始化完成',
|
|
77
|
-
timestamp: new Date().toISOString()
|
|
78
|
-
});
|
|
79
|
-
} catch (error) {
|
|
80
|
-
logger.error('初始化测试数据失败:', error as Error);
|
|
81
|
-
return this.fail(500, `初始化测试数据失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 清空测试数据
|
|
87
|
-
*/
|
|
88
|
-
@Post('/test-data/clear')
|
|
89
|
-
async clearTestData() {
|
|
90
|
-
if (!this.isMemoryDatabase()) {
|
|
91
|
-
return this.fail(400, '当前不是内存数据库模式');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
await testDataInitializer.clearTestData();
|
|
96
|
-
logger.info('手动清空测试数据完成');
|
|
97
|
-
|
|
98
|
-
return this.success({
|
|
99
|
-
message: '测试数据已清空',
|
|
100
|
-
timestamp: new Date().toISOString()
|
|
101
|
-
});
|
|
102
|
-
} catch (error) {
|
|
103
|
-
logger.error('清空测试数据失败:', error as Error);
|
|
104
|
-
return this.fail(500, `清空测试数据失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 重新初始化测试数据(先清空再初始化)
|
|
110
|
-
*/
|
|
111
|
-
@Post('/test-data/reset')
|
|
112
|
-
async resetTestData() {
|
|
113
|
-
if (!this.isMemoryDatabase()) {
|
|
114
|
-
return this.fail(400, '当前不是内存数据库模式');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
await testDataInitializer.clearTestData();
|
|
119
|
-
await testDataInitializer.initializeTestData();
|
|
120
|
-
logger.info('手动重置测试数据完成');
|
|
121
|
-
|
|
122
|
-
return this.success({
|
|
123
|
-
message: '测试数据已重置',
|
|
124
|
-
timestamp: new Date().toISOString()
|
|
125
|
-
});
|
|
126
|
-
} catch (error) {
|
|
127
|
-
logger.error('重置测试数据失败:', error as Error);
|
|
128
|
-
return this.fail(500, `重置测试数据失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* 获取测试用户信息(用于测试API)
|
|
134
|
-
*/
|
|
135
|
-
@Get('/test-data/users')
|
|
136
|
-
async getTestUsers() {
|
|
137
|
-
if (!this.isMemoryDatabase()) {
|
|
138
|
-
return this.fail(400, '当前不是内存数据库模式');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
const dataSource = getDataSource();
|
|
143
|
-
if (!dataSource) {
|
|
144
|
-
return this.fail(500, '数据库连接未初始化');
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const users = await dataSource.query(`
|
|
148
|
-
SELECT
|
|
149
|
-
u.id,
|
|
150
|
-
u.nickName,
|
|
151
|
-
u.email,
|
|
152
|
-
u.telnumber,
|
|
153
|
-
u.userType,
|
|
154
|
-
u.status,
|
|
155
|
-
u.avatar,
|
|
156
|
-
u.gender,
|
|
157
|
-
u.age,
|
|
158
|
-
u.constellation
|
|
159
|
-
FROM yt_user u
|
|
160
|
-
ORDER BY u.id
|
|
161
|
-
`);
|
|
162
|
-
|
|
163
|
-
return this.success({
|
|
164
|
-
users,
|
|
165
|
-
count: users.length,
|
|
166
|
-
timestamp: new Date().toISOString()
|
|
167
|
-
});
|
|
168
|
-
} catch (error) {
|
|
169
|
-
logger.error('获取测试用户失败:', error as Error);
|
|
170
|
-
return this.fail(500, `获取测试用户失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* 获取测试店铺信息
|
|
176
|
-
*/
|
|
177
|
-
@Get('/test-data/shops')
|
|
178
|
-
async getTestShops() {
|
|
179
|
-
if (!this.isMemoryDatabase()) {
|
|
180
|
-
return this.fail(400, '当前不是内存数据库模式');
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const dataSource = getDataSource();
|
|
185
|
-
if (!dataSource) {
|
|
186
|
-
return this.fail(500, '数据库连接未初始化');
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const shops = await dataSource.query(`
|
|
190
|
-
SELECT
|
|
191
|
-
s.id,
|
|
192
|
-
s.name,
|
|
193
|
-
s.description,
|
|
194
|
-
s.status,
|
|
195
|
-
u.nickName as ownerName,
|
|
196
|
-
u.email as ownerEmail
|
|
197
|
-
FROM shop s
|
|
198
|
-
LEFT JOIN shop_user su ON s.id = su.shopId
|
|
199
|
-
LEFT JOIN yt_user u ON su.userId = u.id
|
|
200
|
-
ORDER BY s.id
|
|
201
|
-
`);
|
|
202
|
-
|
|
203
|
-
return this.success({
|
|
204
|
-
shops,
|
|
205
|
-
count: shops.length,
|
|
206
|
-
timestamp: new Date().toISOString()
|
|
207
|
-
});
|
|
208
|
-
} catch (error) {
|
|
209
|
-
logger.error('获取测试店铺失败:', error as Error);
|
|
210
|
-
return this.fail(500, `获取测试店铺失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* 获取测试商品信息
|
|
216
|
-
*/
|
|
217
|
-
@Get('/test-data/goods')
|
|
218
|
-
async getTestGoods() {
|
|
219
|
-
if (!this.isMemoryDatabase()) {
|
|
220
|
-
return this.fail(400, '当前不是内存数据库模式');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
const dataSource = getDataSource();
|
|
225
|
-
if (!dataSource) {
|
|
226
|
-
return this.fail(500, '数据库连接未初始化');
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const goods = await dataSource.query(`
|
|
230
|
-
SELECT
|
|
231
|
-
g.id,
|
|
232
|
-
g.name,
|
|
233
|
-
g.description,
|
|
234
|
-
g.price,
|
|
235
|
-
g.albums,
|
|
236
|
-
g.status,
|
|
237
|
-
s.name as shopName
|
|
238
|
-
FROM yt_goods g
|
|
239
|
-
LEFT JOIN shop s ON g.shopId = s.id
|
|
240
|
-
ORDER BY g.id
|
|
241
|
-
`);
|
|
242
|
-
|
|
243
|
-
return this.success({
|
|
244
|
-
goods,
|
|
245
|
-
count: goods.length,
|
|
246
|
-
timestamp: new Date().toISOString()
|
|
247
|
-
});
|
|
248
|
-
} catch (error) {
|
|
249
|
-
logger.error('获取测试商品失败:', error as Error);
|
|
250
|
-
return this.fail(500, `获取测试商品失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { IsString, IsOptional, IsNumber, IsEmail, MinLength, MaxLength } from 'class-validator';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 示例查询参数 DTO
|
|
5
|
-
*/
|
|
6
|
-
export class ExampleQueryDto {
|
|
7
|
-
@IsOptional()
|
|
8
|
-
@IsString()
|
|
9
|
-
id?: string;
|
|
10
|
-
|
|
11
|
-
@IsOptional()
|
|
12
|
-
@IsString()
|
|
13
|
-
@MinLength(1)
|
|
14
|
-
@MaxLength(50)
|
|
15
|
-
name?: string;
|
|
16
|
-
|
|
17
|
-
@IsOptional()
|
|
18
|
-
@IsString()
|
|
19
|
-
category?: string;
|
|
20
|
-
|
|
21
|
-
@IsOptional()
|
|
22
|
-
@IsNumber()
|
|
23
|
-
page?: number;
|
|
24
|
-
|
|
25
|
-
@IsOptional()
|
|
26
|
-
@IsNumber()
|
|
27
|
-
limit?: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* 示例请求体 DTO
|
|
32
|
-
*/
|
|
33
|
-
export class ExampleBodyDto {
|
|
34
|
-
@IsString()
|
|
35
|
-
@MinLength(1)
|
|
36
|
-
@MaxLength(100)
|
|
37
|
-
name: string;
|
|
38
|
-
|
|
39
|
-
@IsString()
|
|
40
|
-
@MinLength(1)
|
|
41
|
-
@MaxLength(500)
|
|
42
|
-
value: string;
|
|
43
|
-
|
|
44
|
-
@IsOptional()
|
|
45
|
-
@IsEmail()
|
|
46
|
-
email?: string;
|
|
47
|
-
|
|
48
|
-
@IsOptional()
|
|
49
|
-
@IsNumber()
|
|
50
|
-
age?: number;
|
|
51
|
-
|
|
52
|
-
@IsOptional()
|
|
53
|
-
@IsString()
|
|
54
|
-
description?: string;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 示例响应 DTO
|
|
59
|
-
*/
|
|
60
|
-
export class ExampleResponseDto {
|
|
61
|
-
@IsString()
|
|
62
|
-
message: string;
|
|
63
|
-
|
|
64
|
-
@IsOptional()
|
|
65
|
-
data?: any;
|
|
66
|
-
|
|
67
|
-
@IsOptional()
|
|
68
|
-
timestamp?: string;
|
|
69
|
-
|
|
70
|
-
@IsOptional()
|
|
71
|
-
@IsNumber()
|
|
72
|
-
code?: number;
|
|
73
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { Transform } from "@src/framework/decorator/controller";
|
|
2
|
-
import { Trim } from "@src/framework/utils/transform";
|
|
3
|
-
import { IsEmail, IsNumber, IsString, Length } from "class-validator";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 发送邮件请求参数
|
|
7
|
-
*/
|
|
8
|
-
export class SendEmailDto {
|
|
9
|
-
|
|
10
|
-
@IsString({
|
|
11
|
-
message: "邮箱必须是字符串"
|
|
12
|
-
})
|
|
13
|
-
@Transform((val) => {
|
|
14
|
-
const str = String(val);
|
|
15
|
-
return Trim(str);
|
|
16
|
-
})
|
|
17
|
-
@IsEmail({}, {
|
|
18
|
-
message: "请输入有效的邮箱"
|
|
19
|
-
})
|
|
20
|
-
email: string;
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* 发送邮件响应参数
|
|
26
|
-
*/
|
|
27
|
-
export class SendEmailResponseDto {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@Length(1, 256, {
|
|
31
|
-
message: "token长度必须在1-256之间"
|
|
32
|
-
})
|
|
33
|
-
@IsString({
|
|
34
|
-
message: "响应token必须是字符串"
|
|
35
|
-
})
|
|
36
|
-
vcodeToken: string;
|
|
37
|
-
|
|
38
|
-
@IsNumber()
|
|
39
|
-
expire: number
|
|
40
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { IsEmail, IsString, Length } from "class-validator";
|
|
2
|
-
import { Transform } from "@src/framework/decorator/controller";
|
|
3
|
-
import { Trim } from "@src/framework/utils/transform";
|
|
4
|
-
|
|
5
|
-
export class YtUserRegisterDto {
|
|
6
|
-
|
|
7
|
-
@IsString({
|
|
8
|
-
message: "邮箱必须是字符串"
|
|
9
|
-
})
|
|
10
|
-
@Transform((val) => {
|
|
11
|
-
const str = String(val);
|
|
12
|
-
return Trim(str);
|
|
13
|
-
})
|
|
14
|
-
@IsEmail({}, {
|
|
15
|
-
message: "请输入有效的邮箱"
|
|
16
|
-
})
|
|
17
|
-
email: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@Transform((val) => {
|
|
21
|
-
const str = String(val);
|
|
22
|
-
return Trim(str);
|
|
23
|
-
})
|
|
24
|
-
@Length(6, 12, {
|
|
25
|
-
message: "密码长度为6-12"
|
|
26
|
-
})
|
|
27
|
-
@IsString()
|
|
28
|
-
password: string
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* 短信验证码
|
|
32
|
-
*/
|
|
33
|
-
@Length(4, 6)
|
|
34
|
-
@IsString()
|
|
35
|
-
@Transform(Trim)
|
|
36
|
-
vcode: string
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 短信验证码的token
|
|
40
|
-
*/
|
|
41
|
-
@IsString()
|
|
42
|
-
@Transform(Trim)
|
|
43
|
-
vcodeToken: string
|
|
44
|
-
}
|
|
45
|
-
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { Transform } from "@src/framework/decorator/controller";
|
|
2
|
-
import { IsArray, IsNumber, IsString } from "class-validator";
|
|
3
|
-
|
|
4
|
-
export class GetGoodsInfoDto {
|
|
5
|
-
|
|
6
|
-
@IsNumber()
|
|
7
|
-
@Transform(id => Number(id))
|
|
8
|
-
id: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class GetGoodsInfoResultDto {
|
|
12
|
-
|
|
13
|
-
@IsNumber()
|
|
14
|
-
id: number
|
|
15
|
-
|
|
16
|
-
@IsString()
|
|
17
|
-
name: string
|
|
18
|
-
|
|
19
|
-
@IsArray()
|
|
20
|
-
albums: string[]
|
|
21
|
-
|
|
22
|
-
@IsNumber()
|
|
23
|
-
price: number
|
|
24
|
-
|
|
25
|
-
@IsString()
|
|
26
|
-
description: string
|
|
27
|
-
|
|
28
|
-
@IsString()
|
|
29
|
-
content: string;
|
|
30
|
-
|
|
31
|
-
@IsArray()
|
|
32
|
-
imagesContent: string[]
|
|
33
|
-
|
|
34
|
-
@IsArray()
|
|
35
|
-
tags: string[]
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class GetGoodsUnlockInfoDto {
|
|
40
|
-
|
|
41
|
-
@IsNumber()
|
|
42
|
-
@Transform(id => Number(id))
|
|
43
|
-
goodsId: number
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export class UnlockGoodsImageDto {
|
|
47
|
-
|
|
48
|
-
@IsString()
|
|
49
|
-
key: string
|
|
50
|
-
|
|
51
|
-
@IsNumber()
|
|
52
|
-
@Transform(id => Number(id))
|
|
53
|
-
goodsId: number
|
|
54
|
-
}
|
|
55
|
-
// export class GetGoodsUnlockInfoResponseDto {}
|