feat: 意见反馈功能
新增完整反馈闭环: - 后端: Feedback 模块 (schema+service+controller+module) - POST /api/feedback (用户提交) - GET /api/feedback (管理员列表) - PATCH /api/feedback/:id/resolve (管理员标记已处理) - 前端: pages/feedback/feedback.vue - 三种反馈类型: 问题反馈/改进建议/点赞鼓励 - 文本输入 + 联系方式选填 - 提交后显示成功提示 - 用户页新增'意见反馈'菜单入口 - 管理后台可通过 API 查看和管理反馈
This commit is contained in:
@@ -9,6 +9,7 @@ import { APP_GUARD } from '@nestjs/core'
|
||||
import { JwtStrategy } from './common/strategies/jwt.strategy'
|
||||
import { JwtAuthGuard } from './common/guards/jwt-auth.guard'
|
||||
import { AiModule } from './modules/ai/ai.module'
|
||||
import { FeedbackModule } from './modules/feedback/feedback.module'
|
||||
import { UserModule } from './modules/user/user.module'
|
||||
import { InterviewModule } from './modules/interview/interview.module'
|
||||
import { ResumeModule } from './modules/resume/resume.module'
|
||||
@@ -47,6 +48,7 @@ const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/zhiyin
|
||||
}]),
|
||||
NestScheduleModule.forRoot(),
|
||||
UserModule,
|
||||
FeedbackModule,
|
||||
AiModule,
|
||||
InterviewModule,
|
||||
AnalyzeModule,
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Controller, Post, Get, Patch, Param, Body, Query, UseGuards, HttpException, HttpStatus } from '@nestjs/common'
|
||||
import { FeedbackService } from './feedback.service'
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'
|
||||
import { AdminGuard } from '../../common/guards/admin.guard'
|
||||
import { CurrentUser } from '../../common/decorators/current-user.decorator'
|
||||
|
||||
@Controller('feedback')
|
||||
export class FeedbackController {
|
||||
constructor(private service: FeedbackService) {}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post()
|
||||
async create(@CurrentUser('userId') userId: string, @Body() body: { type?: string; content: string; contact?: string }) {
|
||||
if (!body.content || body.content.length < 2) {
|
||||
throw new HttpException('请填写反馈内容', HttpStatus.BAD_REQUEST)
|
||||
}
|
||||
return this.service.create({ userId, type: body.type || 'suggestion', content: body.content, contact: body.contact })
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard, AdminGuard)
|
||||
@Get()
|
||||
async list(@Query('page') page?: string, @Query('limit') limit?: string) {
|
||||
return this.service.findAll(Number(page) || 1, Number(limit) || 20)
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard, AdminGuard)
|
||||
@Patch(':id/resolve')
|
||||
async resolve(@Param('id') id: string) {
|
||||
return this.service.markResolved(id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { MongooseModule } from '@nestjs/mongoose'
|
||||
import { FeedbackController } from './feedback.controller'
|
||||
import { FeedbackService } from './feedback.service'
|
||||
import { Feedback, FeedbackSchema } from './feedback.schema'
|
||||
|
||||
@Module({
|
||||
imports: [MongooseModule.forFeature([{ name: Feedback.name, schema: FeedbackSchema }])],
|
||||
controllers: [FeedbackController],
|
||||
providers: [FeedbackService],
|
||||
exports: [FeedbackService],
|
||||
})
|
||||
export class FeedbackModule {}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
|
||||
import { Document, Types } from 'mongoose'
|
||||
|
||||
export type FeedbackDocument = Feedback & Document
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class Feedback {
|
||||
@Prop({ type: Types.ObjectId, ref: 'User', required: true })
|
||||
userId: Types.ObjectId
|
||||
|
||||
@Prop({ default: 'suggestion' })
|
||||
type: string
|
||||
|
||||
@Prop({ required: true })
|
||||
content: string
|
||||
|
||||
@Prop({ default: '' })
|
||||
contact: string
|
||||
|
||||
@Prop({ default: 'pending' })
|
||||
status: string
|
||||
}
|
||||
|
||||
export const FeedbackSchema = SchemaFactory.createForClass(Feedback)
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { InjectModel } from '@nestjs/mongoose'
|
||||
import { Model } from 'mongoose'
|
||||
import { Feedback } from './feedback.schema'
|
||||
|
||||
@Injectable()
|
||||
export class FeedbackService {
|
||||
constructor(@InjectModel(Feedback.name) private model: Model<Feedback>) {}
|
||||
|
||||
async create(data: { userId: string; type: string; content: string; contact?: string }) {
|
||||
return this.model.create(data)
|
||||
}
|
||||
|
||||
async findAll(page = 1, limit = 20) {
|
||||
const skip = (page - 1) * limit
|
||||
const [items, total] = await Promise.all([
|
||||
this.model.find().sort({ createdAt: -1 }).skip(skip).limit(limit).populate('userId', 'nickname phone email').lean(),
|
||||
this.model.countDocuments(),
|
||||
])
|
||||
return { items, total, page, limit }
|
||||
}
|
||||
|
||||
async markResolved(id: string) {
|
||||
return this.model.findByIdAndUpdate(id, { status: 'resolved' }, { new: true })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user