完成问题反馈
Some checks failed
CI Build and Test / build (push) Has been cancelled
Deploy to Server / build-and-deploy (push) Has been cancelled

This commit is contained in:
2025-10-20 18:00:32 +08:00
parent cf3a17073d
commit 4602f73957
11 changed files with 764 additions and 1 deletions

View File

@@ -23,6 +23,8 @@ public class SaTokenConfig implements WebMvcConfigurer {
.addPathPatterns("/**")
// 排除登录、注册、发送验证码接口
.excludePathPatterns("/user/login", "/user/register", "/user/sendCode")
// 排除反馈接口(支持匿名提交)
.excludePathPatterns("/feedback", "/feedback/**")
// 排除静态资源
.excludePathPatterns("/", "/index.html", "/*.html", "/*.css", "/*.js", "/*.ico", "/static/**")
// 排除 Druid 监控

View File

@@ -0,0 +1,145 @@
package com.corewing.app.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.corewing.app.common.Result;
import com.corewing.app.dto.FeedbackRequest;
import com.corewing.app.entity.AppFeedback;
import com.corewing.app.service.AppFeedbackService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 问题反馈 Controller
*/
@RestController
@RequestMapping("/feedback")
public class AppFeedbackController {
private final AppFeedbackService feedbackService;
public AppFeedbackController(AppFeedbackService feedbackService) {
this.feedbackService = feedbackService;
}
/**
* 创建反馈(支持匿名提交)
*/
@PostMapping
public Result<String> create(@RequestBody FeedbackRequest request) {
try {
// 尝试获取当前登录用户ID如果未登录则为null
Long userId = null;
try {
userId = StpUtil.getLoginIdAsLong();
} catch (Exception e) {
// 未登录userId 保持为 null
}
AppFeedback feedback = new AppFeedback();
feedback.setUserId(userId);
feedback.setFeedbackType(request.getFeedbackType());
feedback.setTitle(request.getTitle());
feedback.setContent(request.getContent());
feedback.setContact(request.getContact());
boolean success = feedbackService.createFeedback(feedback);
if (success) {
return Result.success("反馈提交成功");
}
return Result.error("反馈提交失败");
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
/**
* 查询当前用户的反馈列表
*/
@GetMapping("/my")
public Result<List<AppFeedback>> getMyFeedbackList() {
try {
// 获取当前登录用户ID
Long userId = StpUtil.getLoginIdAsLong();
List<AppFeedback> feedbackList = feedbackService.listByUserId(userId);
return Result.success(feedbackList);
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
/**
* 根据ID查询反馈详情
*/
@GetMapping("/{id}")
public Result<AppFeedback> getById(@PathVariable Long id) {
try {
AppFeedback feedback = feedbackService.getById(id);
if (feedback != null) {
return Result.success(feedback);
}
return Result.error("反馈不存在");
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
/**
* 分页查询反馈列表
*
* @param current 当前页码
* @param size 每页数量
* @param userId 用户ID可选
* @param feedbackType 问题类型(可选)
* @param status 状态(可选)
*/
@GetMapping("/page")
public Result<IPage<AppFeedback>> getPageList(
@RequestParam(defaultValue = "1") Long current,
@RequestParam(defaultValue = "10") Long size,
@RequestParam(required = false) Long userId,
@RequestParam(required = false) String feedbackType,
@RequestParam(required = false) Integer status) {
try {
Page<AppFeedback> page = new Page<>(current, size);
IPage<AppFeedback> pageResult = feedbackService.pageList(page, userId, feedbackType, status);
return Result.success(pageResult);
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
/**
* 更新反馈状态
*/
@PutMapping("/{id}/status")
public Result<String> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
try {
boolean success = feedbackService.updateStatus(id, status);
if (success) {
return Result.success("状态更新成功");
}
return Result.error("状态更新失败");
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
/**
* 删除反馈
*/
@DeleteMapping("/{id}")
public Result<String> delete(@PathVariable Long id) {
try {
boolean success = feedbackService.removeById(id);
if (success) {
return Result.success("删除成功");
}
return Result.error("删除失败");
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
}

View File

@@ -0,0 +1,34 @@
package com.corewing.app.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 创建反馈请求
*/
@Data
public class FeedbackRequest {
/**
* 问题类型
*/
@NotBlank(message = "问题类型不能为空")
private String feedbackType;
/**
* 问题标题
*/
@NotBlank(message = "问题标题不能为空")
private String title;
/**
* 问题描述
*/
private String content;
/**
* 联系方式
*/
private String contact;
}

View File

@@ -0,0 +1,69 @@
package com.corewing.app.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 问题反馈实体类
*/
@Data
@TableName("app_feedback")
public class AppFeedback implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 反馈ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 问题类型
*/
private String feedbackType;
/**
* 问题标题
*/
private String title;
/**
* 问题描述
*/
private String content;
/**
* 联系方式
*/
private String contact;
/**
* 处理状态0-待处理 1-处理中 2-已完成 3-已关闭
*/
private Integer status;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,57 @@
package com.corewing.app.handler;
import cn.dev33.satoken.exception.NotLoginException;
import com.corewing.app.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理 Sa-Token 未登录异常
*/
@ExceptionHandler(NotLoginException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public Result<String> handleNotLoginException(NotLoginException e) {
// 根据不同的场景返回不同的提示
String message;
switch (e.getType()) {
case NotLoginException.NOT_TOKEN:
message = "未提供登录凭证";
break;
case NotLoginException.INVALID_TOKEN:
message = "登录凭证无效";
break;
case NotLoginException.TOKEN_TIMEOUT:
message = "登录已过期,请重新登录";
break;
case NotLoginException.BE_REPLACED:
message = "账号已在其他地方登录";
break;
case NotLoginException.KICK_OUT:
message = "账号已被踢下线";
break;
default:
message = "未登录,请先登录";
}
return Result.error(HttpStatus.UNAUTHORIZED.value(), message);
}
/**
* 处理其他异常
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result<String> handleException(Exception e) {
log.error(e.getMessage());
return Result.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器内部错误:" + e.getMessage());
}
}

View File

@@ -0,0 +1,13 @@
package com.corewing.app.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.corewing.app.entity.AppFeedback;
import org.apache.ibatis.annotations.Mapper;
/**
* 问题反馈 Mapper 接口
*/
@Mapper
public interface AppFeedbackMapper extends BaseMapper<AppFeedback> {
}

View File

@@ -0,0 +1,50 @@
package com.corewing.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.corewing.app.entity.AppFeedback;
import java.util.List;
/**
* 问题反馈 Service 接口
*/
public interface AppFeedbackService extends IService<AppFeedback> {
/**
* 创建反馈
*
* @param feedback 反馈信息
* @return 是否成功
*/
boolean createFeedback(AppFeedback feedback);
/**
* 根据用户ID查询反馈列表
*
* @param userId 用户ID
* @return 反馈列表
*/
List<AppFeedback> listByUserId(Long userId);
/**
* 分页查询反馈列表
*
* @param page 分页参数
* @param userId 用户ID可选
* @param feedbackType 问题类型(可选)
* @param status 状态(可选)
* @return 分页结果
*/
IPage<AppFeedback> pageList(Page<AppFeedback> page, Long userId, String feedbackType, Integer status);
/**
* 更新反馈状态
*
* @param id 反馈ID
* @param status 状态
* @return 是否成功
*/
boolean updateStatus(Long id, Integer status);
}

View File

@@ -0,0 +1,77 @@
package com.corewing.app.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.corewing.app.entity.AppFeedback;
import com.corewing.app.mapper.AppFeedbackMapper;
import com.corewing.app.service.AppFeedbackService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* 问题反馈 Service 实现类
*/
@Service
public class AppFeedbackServiceImpl extends ServiceImpl<AppFeedbackMapper, AppFeedback> implements AppFeedbackService {
@Override
public boolean createFeedback(AppFeedback feedback) {
// 设置默认状态为待处理
if (feedback.getStatus() == null) {
feedback.setStatus(0);
}
return this.save(feedback);
}
@Override
public List<AppFeedback> listByUserId(Long userId) {
if (userId == null) {
throw new RuntimeException("用户ID不能为空");
}
LambdaQueryWrapper<AppFeedback> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(AppFeedback::getUserId, userId)
.orderByDesc(AppFeedback::getCreateTime);
return this.list(wrapper);
}
@Override
public IPage<AppFeedback> pageList(Page<AppFeedback> page, Long userId, String feedbackType, Integer status) {
LambdaQueryWrapper<AppFeedback> wrapper = new LambdaQueryWrapper<>();
// 条件查询
if (userId != null) {
wrapper.eq(AppFeedback::getUserId, userId);
}
if (StringUtils.hasText(feedbackType)) {
wrapper.eq(AppFeedback::getFeedbackType, feedbackType);
}
if (status != null) {
wrapper.eq(AppFeedback::getStatus, status);
}
// 按创建时间倒序排序
wrapper.orderByDesc(AppFeedback::getCreateTime);
return this.page(page, wrapper);
}
@Override
public boolean updateStatus(Long id, Integer status) {
if (id == null) {
throw new RuntimeException("反馈ID不能为空");
}
if (status == null) {
throw new RuntimeException("状态不能为空");
}
AppFeedback feedback = new AppFeedback();
feedback.setId(id);
feedback.setStatus(status);
return this.updateById(feedback);
}
}

View File

@@ -0,0 +1,18 @@
-- 问题反馈表
DROP TABLE IF EXISTS `app_feedback`;
CREATE TABLE `app_feedback` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '反馈ID',
`user_id` BIGINT(20) DEFAULT NULL COMMENT '用户ID',
`feedback_type` VARCHAR(50) NOT NULL COMMENT '问题类型',
`title` VARCHAR(200) NOT NULL COMMENT '问题标题',
`content` TEXT DEFAULT NULL COMMENT '问题描述',
`contact` VARCHAR(100) DEFAULT NULL COMMENT '联系方式',
`status` TINYINT(1) DEFAULT 0 COMMENT '处理状态0-待处理 1-处理中 2-已完成 3-已关闭',
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='问题反馈表';