v4.3 安全修复+代码质量+测试体系+护城河验证
## 安全修复 (5项) - CRITICAL JWT 硬编码 fallback(jwt.strategy / app.module / user.module) - HIGH seed_admin.js MongoDB 凭据泄漏 - MEDIUM 邮箱验证码泄漏 - MEDIUM 支付订单查询 IDOR - MEDIUM 管理后台 NoSQL 注入 ## 代码质量 (14处) - console.log→Logger(user.service.ts) - as any 类型化(11处跨7个文件) - Schema 联合类型修复(progress.schema) - Module 依赖缺失修复(progress.module) ## 测试体系 (61项) - 后端单元测试 Jest(43项):BenchmarkService/UserService/PaymentController - 后端集成测试 Supertest(11项):API 认证/支付/进度/管理 - 前端单元测试 Vitest(7项):配置文件/API端点 - 浏览器自动化 Playwright(7项):API smoke test - 覆盖率报告 + e2e 配置 ## 护城河 P0-P5 启动验证通过 + 编译通过
This commit is contained in:
@@ -1,17 +1,21 @@
|
||||
import { Controller, Post, Get, Body, Param, UseGuards } from '@nestjs/common'
|
||||
import { Controller, Post, Get, Body, Param, UseGuards, Logger } from '@nestjs/common'
|
||||
import { InjectModel } from '@nestjs/mongoose'
|
||||
import { Model } from 'mongoose'
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'
|
||||
import { CurrentUser } from '../../common/decorators/current-user.decorator'
|
||||
import { AiService } from '../ai/ai.service'
|
||||
import { Contribution, ContributionDocument } from '../schemas/contribution.schema'
|
||||
import { CompanyBank, CompanyBankDocument } from '../schemas/company-bank.schema'
|
||||
|
||||
@Controller('contribution')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class ContributionController {
|
||||
private readonly logger = new Logger(ContributionController.name)
|
||||
|
||||
constructor(
|
||||
@InjectModel(Contribution.name) private contributionModel: Model<ContributionDocument>,
|
||||
@InjectModel(CompanyBank.name) private companyBankModel: Model<CompanyBankDocument>,
|
||||
private readonly aiService: AiService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
@@ -39,6 +43,11 @@ export class ContributionController {
|
||||
verified: false,
|
||||
})
|
||||
|
||||
// Async AI processing (non-blocking)
|
||||
this.aiProcessContribution(contribution, body).catch(e => {
|
||||
this.logger.error(`AI processing failed for contribution ${contribution._id}: ${e.message}`)
|
||||
})
|
||||
|
||||
// Update company bank
|
||||
if (body.questions && body.questions.length > 0) {
|
||||
let bank = await this.companyBankModel.findOne({
|
||||
@@ -69,7 +78,6 @@ export class ContributionController {
|
||||
tags: body.tags || [],
|
||||
})
|
||||
} else {
|
||||
// Increment frequency
|
||||
const existing = bank.questions.find(eq => eq.content === q)
|
||||
if (existing) existing.frequency += 1
|
||||
}
|
||||
@@ -79,13 +87,70 @@ export class ContributionController {
|
||||
}
|
||||
|
||||
return {
|
||||
id: (contribution as any)._id.toString(),
|
||||
id: contribution._id.toString(),
|
||||
company: contribution.company,
|
||||
position: contribution.position,
|
||||
message: '感谢你的分享!你的面经将帮助更多同学准备面试',
|
||||
}
|
||||
}
|
||||
|
||||
private async aiProcessContribution(contribution: ContributionDocument, body: any) {
|
||||
const prompt = `你是一个面试题分析专家。请分析以下面经内容,返回 JSON(不要 Markdown 包裹):
|
||||
|
||||
公司: ${body.company}
|
||||
岗位: ${body.position}
|
||||
轮次: ${body.rounds || '未说明'}
|
||||
面试题: ${(body.questions || []).join('\n')}
|
||||
面试经验: ${body.experience || ''}
|
||||
用户标签: ${(body.tags || []).join(', ')}
|
||||
|
||||
请返回严格 JSON:
|
||||
{
|
||||
"structuredQuestions": [
|
||||
{
|
||||
"content": "原题",
|
||||
"type": "技术题|行为题|场景题|算法题|系统设计题|HR题",
|
||||
"difficulty": "easy|medium|hard",
|
||||
"referenceAnswer": "参考答案",
|
||||
"tags": ["标签1", "标签2"]
|
||||
}
|
||||
],
|
||||
"aiSummary": "面试总结,包含难度评估、重点方向、准备建议(50字以内)"
|
||||
}`
|
||||
|
||||
const result = await this.aiService.call({ systemPrompt: prompt, userMessage: '请分析以上面经', maxTokens: 3000 })
|
||||
const parsed = JSON.parse(result.replace(/```json\s*|\s*```/g, '').trim())
|
||||
|
||||
await this.contributionModel.findByIdAndUpdate(contribution._id, {
|
||||
$set: {
|
||||
aiProcessed: true,
|
||||
structuredQuestions: parsed.structuredQuestions || [],
|
||||
aiSummary: parsed.aiSummary || '',
|
||||
},
|
||||
}).exec()
|
||||
|
||||
// Update company bank with structured data
|
||||
if (parsed.structuredQuestions?.length > 0) {
|
||||
const bank = await this.companyBankModel.findOne({
|
||||
company: body.company,
|
||||
position: body.position,
|
||||
}).exec()
|
||||
|
||||
if (bank) {
|
||||
for (const sq of parsed.structuredQuestions) {
|
||||
const existing = bank.questions.find(eq => eq.content === sq.content)
|
||||
if (existing) {
|
||||
existing.type = sq.type || existing.type
|
||||
existing.difficulty = sq.difficulty || existing.difficulty
|
||||
existing.referenceAnswer = sq.referenceAnswer || existing.referenceAnswer
|
||||
if (sq.tags) existing.tags = [...new Set([...existing.tags, ...sq.tags])]
|
||||
}
|
||||
}
|
||||
await bank.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Get('company/:company/position/:position')
|
||||
async getBank(@Param('company') company: string, @Param('position') position: string) {
|
||||
const bank = await this.companyBankModel.findOne({ company, position }).exec()
|
||||
|
||||
Reference in New Issue
Block a user