feat(system): 添加用户意见反馈功能
- 新增意见反馈数据对象和数据库映射 - 实现意见反馈的增删改查和状态更新接口 - 添加意见反馈分类和处理状态字典常量 - 实现意见反馈分页查询和统计功能 - 定义意见反馈相关的请求响应对象 - 添加意见反馈不存在的错误码定义
This commit is contained in:
@@ -42,4 +42,9 @@ public interface DictTypeConstants {
|
||||
*/
|
||||
String RDMS_OVERTIME_DURATION = "rdms_overtime_duration";
|
||||
|
||||
/** 意见反馈分类 */
|
||||
String FEEDBACK_TYPE = "feedback_type";
|
||||
/** 意见反馈处理状态 */
|
||||
String FEEDBACK_STATUS = "feedback_status";
|
||||
|
||||
}
|
||||
|
||||
@@ -99,6 +99,9 @@ public interface ErrorCodeConstants {
|
||||
// ========== 通知公告 1-002-008-000 ==========
|
||||
ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1_002_008_001, "当前通知公告不存在");
|
||||
|
||||
// ========== 用户意见反馈 1-002-009-000 ==========
|
||||
ErrorCode FEEDBACK_NOT_FOUND = new ErrorCode(1_002_009_001, "用户意见反馈不存在");
|
||||
|
||||
// ========= 文件相关 1-001-003-000 =================
|
||||
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1_001_003_000, "文件路径已存在");
|
||||
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1_001_003_001, "文件不存在");
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.njcn.rdms.module.system.controller.admin.feedback;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackPageReqVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackRespVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackSaveReqVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackStatRespVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackStatusReqVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackUpdateReqVO;
|
||||
import com.njcn.rdms.module.system.service.feedback.FeedbackService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
// 临时功能:意见反馈过测试阶段即下线。全部接口均「有意」不挂后端鉴权(仅过 token,需登录),
|
||||
// 按钮可见性(仅 admin 可见)由前端控制;并非漏挂。如需恢复鉴权,给对应接口加回
|
||||
// @PreAuthorize("@ss.hasPermission('system:feedback:xxx')") 并配 system_menu 权限码 + 角色授权。
|
||||
@Tag(name = "管理后台 - 用户意见反馈")
|
||||
@RestController
|
||||
@RequestMapping("/system/feedback")
|
||||
@Validated
|
||||
public class FeedbackController {
|
||||
|
||||
@Resource
|
||||
private FeedbackService feedbackService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "提交意见反馈")
|
||||
public CommonResult<Long> createFeedback(@Valid @RequestBody FeedbackSaveReqVO reqVO) {
|
||||
return success(feedbackService.createFeedback(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改意见反馈")
|
||||
public CommonResult<Boolean> updateFeedback(@Valid @RequestBody FeedbackUpdateReqVO reqVO) {
|
||||
feedbackService.updateFeedback(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得意见反馈分页(全量)")
|
||||
public CommonResult<PageResult<FeedbackRespVO>> getFeedbackPage(@Valid FeedbackPageReqVO pageReqVO) {
|
||||
return success(feedbackService.getFeedbackPage(pageReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/stat")
|
||||
@Operation(summary = "意见反馈统计(全量:总数 + 按分类 / 按状态分组计数)")
|
||||
public CommonResult<FeedbackStatRespVO> getFeedbackStat() {
|
||||
return success(feedbackService.getFeedbackStat());
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得意见反馈详情")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public CommonResult<FeedbackRespVO> getFeedback(@RequestParam("id") Long id) {
|
||||
return success(feedbackService.getFeedback(id));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/status")
|
||||
@Operation(summary = "修改意见反馈处理状态")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public CommonResult<Boolean> updateFeedbackStatus(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody FeedbackStatusReqVO reqVO) {
|
||||
feedbackService.updateFeedbackStatus(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除意见反馈")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public CommonResult<Boolean> deleteFeedback(@RequestParam("id") Long id) {
|
||||
feedbackService.deleteFeedback(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.njcn.rdms.module.system.controller.admin.feedback.vo;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 用户意见反馈分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class FeedbackPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "反馈分类(字典 feedback_type)")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "处理状态(字典 feedback_status)")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "标题关键词")
|
||||
private String title;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.rdms.module.system.controller.admin.feedback.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 用户意见反馈 Response VO")
|
||||
@Data
|
||||
public class FeedbackRespVO {
|
||||
|
||||
@Schema(description = "主键 ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "反馈分类(字典 feedback_type)")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "详细描述")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "附件 URL 列表(JSON 数组字符串)")
|
||||
private String attachmentUrls;
|
||||
|
||||
@Schema(description = "联系方式")
|
||||
private String contact;
|
||||
|
||||
@Schema(description = "处理状态(字典 feedback_status)")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "提交人 user id")
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "提交人姓名(后端按 creator 翻译,查不到为空串)")
|
||||
private String creatorName;
|
||||
|
||||
@Schema(description = "提交时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") // 本字段按可读字符串返回前端;全局默认是 Long 时间戳,此处显式覆盖
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.njcn.rdms.module.system.controller.admin.feedback.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 用户意见反馈提交 Request VO")
|
||||
@Data
|
||||
public class FeedbackSaveReqVO {
|
||||
|
||||
@Schema(description = "反馈分类(字典 feedback_type)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "反馈分类不能为空")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "导出按钮点了没反应")
|
||||
@NotBlank(message = "标题不能为空")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "详细描述", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "详细描述不能为空")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "附件 URL 列表(JSON 数组字符串)", example = "[\"https://.../a.png\"]")
|
||||
private String attachmentUrls;
|
||||
|
||||
@Schema(description = "联系方式", example = "微信 xxx")
|
||||
private String contact;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.rdms.module.system.controller.admin.feedback.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 用户意见反馈统计 Response VO")
|
||||
@Data
|
||||
public class FeedbackStatRespVO {
|
||||
|
||||
@Schema(description = "全部未软删反馈总数")
|
||||
private Integer total;
|
||||
|
||||
@Schema(description = "按分类分组的计数(覆盖字典 feedback_type 全部码值,无数据补 0;顺序按字典 sort)")
|
||||
private List<TypeCount> typeCounts;
|
||||
|
||||
@Schema(description = "按处理状态分组的计数(覆盖字典 feedback_status 全部码值,无数据补 0;顺序按字典 sort)")
|
||||
private List<StatusCount> statusCounts;
|
||||
|
||||
@Schema(description = "分类计数项")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class TypeCount {
|
||||
|
||||
@Schema(description = "分类码值(字典 feedback_type)")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "该分类下未软删反馈数")
|
||||
private Integer count;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "状态计数项")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class StatusCount {
|
||||
|
||||
@Schema(description = "状态码值(字典 feedback_status)")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "该状态下未软删反馈数")
|
||||
private Integer count;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.njcn.rdms.module.system.controller.admin.feedback.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 修改意见反馈处理状态 Request VO")
|
||||
@Data
|
||||
public class FeedbackStatusReqVO {
|
||||
|
||||
@Schema(description = "目标状态(字典 feedback_status)", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||
@NotNull(message = "目标状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.njcn.rdms.module.system.controller.admin.feedback.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 用户意见反馈更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class FeedbackUpdateReqVO extends FeedbackSaveReqVO {
|
||||
|
||||
@Schema(description = "主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "反馈编号不能为空")
|
||||
private Long id;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.rdms.module.system.dal.dataobject.feedback;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 用户意见反馈 DO
|
||||
*/
|
||||
@TableName("system_feedback")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class FeedbackDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
/** 反馈分类:字典 {@link com.njcn.rdms.module.system.enums.DictTypeConstants#FEEDBACK_TYPE}(1缺陷 2体验问题 3功能建议) */
|
||||
private Integer type;
|
||||
/** 标题 */
|
||||
private String title;
|
||||
/** 详细描述 */
|
||||
private String content;
|
||||
/** 附件列表(JSON 数组字符串),可空。PUT 全量替换语义下前端传 null 需真正清空,故用 ALWAYS 跳过全局 NOT_NULL 策略(见 CLAUDE.md「接口语义」节) */
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String attachmentUrls;
|
||||
/** 联系方式,可空。同上:PUT 传 null 需落库清空 */
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String contact;
|
||||
/** 处理状态:字典 {@link com.njcn.rdms.module.system.enums.DictTypeConstants#FEEDBACK_STATUS}(1待处理 2处理中 3已处理 4已忽略) */
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.rdms.module.system.dal.mysql.feedback;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackPageReqVO;
|
||||
import com.njcn.rdms.module.system.dal.dataobject.feedback.FeedbackDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface FeedbackMapper extends BaseMapperX<FeedbackDO> {
|
||||
|
||||
default PageResult<FeedbackDO> selectPage(FeedbackPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<FeedbackDO>()
|
||||
.eqIfPresent(FeedbackDO::getType, reqVO.getType())
|
||||
.eqIfPresent(FeedbackDO::getStatus, reqVO.getStatus())
|
||||
.likeIfPresent(FeedbackDO::getTitle, reqVO.getTitle())
|
||||
.orderByDesc(FeedbackDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.njcn.rdms.module.system.service.feedback;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackPageReqVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackRespVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackSaveReqVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackStatRespVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackStatusReqVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackUpdateReqVO;
|
||||
|
||||
public interface FeedbackService {
|
||||
|
||||
/** 提交意见反馈,返回新建 id */
|
||||
Long createFeedback(FeedbackSaveReqVO reqVO);
|
||||
|
||||
/** 修改意见反馈内容(PUT 全量替换;不改 status/creator)。临时功能,后端不鉴权、仅过 token;编辑权限由前端按钮可见性控制 */
|
||||
void updateFeedback(FeedbackUpdateReqVO reqVO);
|
||||
|
||||
/** 分页查询(全量,不按提交人过滤);creatorName 已按 creator 翻译为提交人姓名 */
|
||||
PageResult<FeedbackRespVO> getFeedbackPage(FeedbackPageReqVO reqVO);
|
||||
|
||||
/** 查询详情,不存在抛 FEEDBACK_NOT_FOUND;creatorName 已翻译 */
|
||||
FeedbackRespVO getFeedback(Long id);
|
||||
|
||||
/** 修改处理状态(临时功能,后端不鉴权、仅过 token;按钮可见性由前端控制) */
|
||||
void updateFeedbackStatus(Long id, FeedbackStatusReqVO reqVO);
|
||||
|
||||
/** 删除(临时功能,后端不鉴权、仅过 token;删除权限由前端按钮可见性控制) */
|
||||
void deleteFeedback(Long id);
|
||||
|
||||
/**
|
||||
* 全量统计:总数 + 按分类(feedback_type)/ 按状态(feedback_status)分组计数。
|
||||
* 排除软删;两个维度均按字典全集补零(无数据的码值 count=0),顺序按字典 sort。
|
||||
*/
|
||||
FeedbackStatRespVO getFeedbackStat();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.njcn.rdms.module.system.service.feedback;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.common.util.object.BeanUtils;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackPageReqVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackRespVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackSaveReqVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackStatRespVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackStatusReqVO;
|
||||
import com.njcn.rdms.module.system.controller.admin.feedback.vo.FeedbackUpdateReqVO;
|
||||
import com.njcn.rdms.module.system.dal.dataobject.feedback.FeedbackDO;
|
||||
import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.njcn.rdms.module.system.dal.mysql.feedback.FeedbackMapper;
|
||||
import com.njcn.rdms.module.system.enums.DictTypeConstants;
|
||||
import com.njcn.rdms.module.system.service.dict.DictDataService;
|
||||
import com.njcn.rdms.module.system.service.user.AdminUserService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.FEEDBACK_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* 用户意见反馈 Service 实现类
|
||||
*/
|
||||
@Service
|
||||
public class FeedbackServiceImpl implements FeedbackService {
|
||||
|
||||
@Resource
|
||||
private FeedbackMapper feedbackMapper;
|
||||
@Resource
|
||||
private AdminUserService adminUserService;
|
||||
@Resource
|
||||
private DictDataService dictDataService;
|
||||
|
||||
@Override
|
||||
public Long createFeedback(FeedbackSaveReqVO reqVO) {
|
||||
FeedbackDO feedback = BeanUtils.toBean(reqVO, FeedbackDO.class);
|
||||
feedback.setStatus(1); // 默认「待处理」,不信任前端传入
|
||||
feedbackMapper.insert(feedback);
|
||||
return feedback.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFeedback(FeedbackUpdateReqVO reqVO) {
|
||||
// 临时功能,后端不鉴权(仅过 token);编辑权限由前端按钮可见性控制
|
||||
validateFeedbackExists(reqVO.getId());
|
||||
// 只更新内容字段:toBean 不带 status/creator,updateById 不会动它们(status 走专用接口、creator 为审计字段);
|
||||
// attachmentUrls/contact 传 null 也会落库清空(DO 上 FieldStrategy.ALWAYS)
|
||||
FeedbackDO update = BeanUtils.toBean(reqVO, FeedbackDO.class);
|
||||
feedbackMapper.updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<FeedbackRespVO> getFeedbackPage(FeedbackPageReqVO reqVO) {
|
||||
PageResult<FeedbackDO> pageResult = feedbackMapper.selectPage(reqVO);
|
||||
return new PageResult<>(buildRespList(pageResult.getList()), pageResult.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeedbackRespVO getFeedback(Long id) {
|
||||
FeedbackDO feedback = validateFeedbackExists(id);
|
||||
return buildRespList(Collections.singletonList(feedback)).get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFeedbackStatus(Long id, FeedbackStatusReqVO reqVO) {
|
||||
validateFeedbackExists(id);
|
||||
FeedbackDO update = new FeedbackDO();
|
||||
update.setId(id);
|
||||
update.setStatus(reqVO.getStatus());
|
||||
feedbackMapper.updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteFeedback(Long id) {
|
||||
// 临时功能,后端不鉴权(仅过 token);删除权限由前端按钮可见性控制
|
||||
validateFeedbackExists(id);
|
||||
feedbackMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeedbackStatRespVO getFeedbackStat() {
|
||||
// 一次性全量加载(软删自动排除),内存按两个维度聚合:同一数据快照天然保证 total 与各维度计数之和一致
|
||||
List<FeedbackDO> all = feedbackMapper.selectList();
|
||||
Map<Integer, Long> typeCountMap = all.stream()
|
||||
.filter(item -> item.getType() != null)
|
||||
.collect(Collectors.groupingBy(FeedbackDO::getType, Collectors.counting()));
|
||||
Map<Integer, Long> statusCountMap = all.stream()
|
||||
.filter(item -> item.getStatus() != null)
|
||||
.collect(Collectors.groupingBy(FeedbackDO::getStatus, Collectors.counting()));
|
||||
|
||||
FeedbackStatRespVO stat = new FeedbackStatRespVO();
|
||||
stat.setTotal(all.size());
|
||||
// 按字典全集补零:无数据的码值也返回 count=0,保证消费端结构稳定,无需自己补零
|
||||
stat.setTypeCounts(dictCodes(DictTypeConstants.FEEDBACK_TYPE).stream()
|
||||
.map(code -> new FeedbackStatRespVO.TypeCount(code, countOf(typeCountMap, code)))
|
||||
.collect(Collectors.toList()));
|
||||
stat.setStatusCounts(dictCodes(DictTypeConstants.FEEDBACK_STATUS).stream()
|
||||
.map(code -> new FeedbackStatRespVO.StatusCount(code, countOf(statusCountMap, code)))
|
||||
.collect(Collectors.toList()));
|
||||
return stat;
|
||||
}
|
||||
|
||||
/** 取字典某类型下全部码值(含停用项),顺序按字典 sort;非数字 value 跳过 */
|
||||
private List<Integer> dictCodes(String dictType) {
|
||||
return dictDataService.getDictDataListByDictType(dictType).stream()
|
||||
.map(dict -> parseDictValue(dict.getValue()))
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Integer countOf(Map<Integer, Long> countMap, Integer code) {
|
||||
return countMap.getOrDefault(code, 0L).intValue();
|
||||
}
|
||||
|
||||
/** 字典 value 解析为整数码值;空 / 非数字返回 null,避免脏字典数据炸接口 */
|
||||
private Integer parseDictValue(String value) {
|
||||
if (StrUtil.isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.valueOf(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public FeedbackDO validateFeedbackExists(Long id) {
|
||||
FeedbackDO feedback = feedbackMapper.selectById(id);
|
||||
if (feedback == null) {
|
||||
throw exception(FEEDBACK_NOT_FOUND);
|
||||
}
|
||||
return feedback;
|
||||
}
|
||||
|
||||
/** DO 列表转 RespVO,并把 creator(user id 字符串)批量翻译成提交人姓名,避免逐行查库 */
|
||||
private List<FeedbackRespVO> buildRespList(List<FeedbackDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Set<Long> userIds = list.stream()
|
||||
.map(feedback -> parseUserId(feedback.getCreator()))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
Map<Long, AdminUserDO> userMap = adminUserService.getUserMap(userIds);
|
||||
return BeanUtils.toBean(list, FeedbackRespVO.class, resp -> {
|
||||
Long uid = parseUserId(resp.getCreator());
|
||||
AdminUserDO user = uid == null ? null : userMap.get(uid);
|
||||
resp.setCreatorName(user != null ? user.getNickname() : "");
|
||||
});
|
||||
}
|
||||
|
||||
/** creator 是 String 形态的 user id,解析为 Long;空/非数字返回 null */
|
||||
private Long parseUserId(String creator) {
|
||||
if (StrUtil.isBlank(creator)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Long.valueOf(creator.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user