feat(admin): Implement admin module foundation

This commit is contained in:
2025-04-11 00:26:30 +08:00
parent 2f49bed861
commit 36380d50a7
71 changed files with 4682 additions and 15 deletions
@@ -0,0 +1,55 @@
package com.nuaa.video.content.controller;
import com.nuaa.video.common.model.vo.Result;
import com.nuaa.video.content.entity.ContentCategory;
import com.nuaa.video.content.service.CategoryService;
import com.nuaa.video.content.vo.CategoryVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 分类管理控制器 - 供管理后台调用
*/
@Slf4j
@RestController
@RequestMapping("/content/category/admin")
@RequiredArgsConstructor
public class AdminCategoryController {
private final CategoryService categoryService;
/**
* 获取分类列表 - 管理接口
*/
@GetMapping("/list")
public Result<List<CategoryVO>> getCategoryList() {
return categoryService.getCategoryList();
}
/**
* 添加分类 - 管理接口
*/
@PostMapping
public Result<Void> addCategory(@RequestBody ContentCategory category) {
return categoryService.addCategory(category);
}
/**
* 更新分类 - 管理接口
*/
@PutMapping
public Result<Void> updateCategory(@RequestBody ContentCategory category) {
return categoryService.updateCategory(category);
}
/**
* 删除分类 - 管理接口
*/
@DeleteMapping("/{categoryId}")
public Result<Void> deleteCategory(@PathVariable Long categoryId) {
return categoryService.deleteCategory(categoryId);
}
}
@@ -0,0 +1,80 @@
package com.nuaa.video.content.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nuaa.video.common.model.vo.Result;
import com.nuaa.video.content.entity.ContentVideo;
import com.nuaa.video.content.dto.VideoQueryDTO;
import com.nuaa.video.content.service.VideoService;
import com.nuaa.video.content.vo.VideoInfoVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
* 视频管理控制器 - 供管理后台调用
*/
@Slf4j
@RestController
@RequestMapping("/content/video/admin")
@RequiredArgsConstructor
public class AdminVideoController {
private final VideoService videoService;
/**
* 获取视频列表 - 管理接口
*/
@GetMapping("/list")
public Result<Page<VideoInfoVO>> getVideoList(
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size,
@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "categoryId", required = false) Long categoryId,
@RequestParam(value = "status", required = false) Integer status) {
VideoQueryDTO queryDTO = new VideoQueryDTO();
queryDTO.setKeyword(keyword);
queryDTO.setCategoryId(categoryId);
queryDTO.setStatus(status);
queryDTO.setPageNum(page);
queryDTO.setPageSize(size);
Page<ContentVideo> pageParam = new Page<>(page, size);
return videoService.getAdminVideoList(pageParam, queryDTO);
}
/**
* 获取视频详情 - 管理接口
*/
@GetMapping("/{videoId}")
public Result<VideoInfoVO> getVideoDetail(@PathVariable Long videoId) {
return videoService.getAdminVideoDetail(videoId);
}
/**
* 审核视频 - 管理接口
*/
@PostMapping("/review/{videoId}")
public Result<Void> reviewVideo(
@PathVariable Long videoId,
@RequestParam Integer status,
@RequestParam(required = false) String reason) {
return videoService.reviewVideo(videoId, null, status, reason);
}
/**
* 下架视频 - 管理接口
*/
@PutMapping("/disable/{videoId}")
public Result<Void> disableVideo(@PathVariable Long videoId) {
return videoService.disableVideo(videoId);
}
/**
* 获取视频统计信息 - 管理接口
*/
@GetMapping("/stats")
public Result<Object> getVideoStats() {
return videoService.getVideoStats();
}
}
@@ -155,4 +155,24 @@ public interface VideoService extends IService<ContentVideo> {
* @return 结果
*/
Result<Void> reviewVideo(Long videoId, Long reviewerId, Integer status, String reason);
/**
* 获取视频列表 - 管理接口
*/
Result<Page<VideoInfoVO>> getAdminVideoList(Page<ContentVideo> page, VideoQueryDTO queryDTO);
/**
* 获取视频详情 - 管理接口
*/
Result<VideoInfoVO> getAdminVideoDetail(Long videoId);
/**
* 下架视频 - 管理接口
*/
Result<Void> disableVideo(Long videoId);
/**
* 获取视频统计信息 - 管理接口
*/
Result<Object> getVideoStats();
}
@@ -27,8 +27,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -724,4 +726,195 @@ public class VideoServiceImpl extends ServiceImpl<ContentVideoMapper, ContentVid
return Result.success();
}
// 在 com.nuaa.video.content.service.impl.VideoServiceImpl 类中添加:
@Override
public Result<Page<VideoInfoVO>> getAdminVideoList(Page<ContentVideo> page, VideoQueryDTO queryDTO) {
// 管理员可查看所有状态的视频,不需要过滤status
Page<ContentVideo> videoPage = videoMapper.selectVideoList(page, queryDTO);
// 转换为VO
Page<VideoInfoVO> voPage = new Page<>();
BeanUtils.copyProperties(videoPage, voPage, "records");
List<VideoInfoVO> voList = new ArrayList<>();
for (ContentVideo video : videoPage.getRecords()) {
VideoInfoVO vo = new VideoInfoVO();
BeanUtils.copyProperties(video, vo);
// 获取分类名称
if (video.getCategoryId() != null) {
ContentCategory category = categoryMapper.selectById(video.getCategoryId());
if (category != null) {
vo.setCategoryName(category.getName());
}
}
// 获取标签
Result<List<TagVO>> tagResult = tagService.getVideoTags(video.getId());
if (tagResult.getCode() == 200 && tagResult.getData() != null) {
List<TagVO> tags = tagResult.getData();
vo.setTagIds(tags.stream().map(TagVO::getId).collect(Collectors.toList()));
vo.setTagNames(tags.stream().map(TagVO::getName).collect(Collectors.toList()));
}
// 获取用户信息
try {
Result<Object> userResult = userFeignClient.getUserProfile(video.getUserId());
if (userResult.getCode() == 200 && userResult.getData() != null) {
@SuppressWarnings("unchecked")
Map<String, Object> userData = (Map<String, Object>) userResult.getData();
vo.setUserNickname((String) userData.get("nickname"));
vo.setUserAvatar((String) userData.get("avatar"));
}
} catch (Exception e) {
log.error("获取用户信息失败: {}", e.getMessage());
}
voList.add(vo);
}
voPage.setRecords(voList);
return Result.success(voPage);
}
@Override
public Result<VideoInfoVO> getAdminVideoDetail(Long videoId) {
// 查询视频信息
ContentVideo video = getById(videoId);
if (video == null) {
return Result.error("视频不存在");
}
// 转换为VO
VideoInfoVO vo = new VideoInfoVO();
BeanUtils.copyProperties(video, vo);
// 获取分类名称
if (video.getCategoryId() != null) {
ContentCategory category = categoryMapper.selectById(video.getCategoryId());
if (category != null) {
vo.setCategoryName(category.getName());
}
}
// 获取标签
Result<List<TagVO>> tagResult = tagService.getVideoTags(video.getId());
if (tagResult.getCode() == 200 && tagResult.getData() != null) {
List<TagVO> tags = tagResult.getData();
vo.setTagIds(tags.stream().map(TagVO::getId).collect(Collectors.toList()));
vo.setTagNames(tags.stream().map(TagVO::getName).collect(Collectors.toList()));
}
// 获取用户信息
try {
Result<Object> userResult = userFeignClient.getUserProfile(video.getUserId());
if (userResult.getCode() == 200 && userResult.getData() != null) {
@SuppressWarnings("unchecked")
Map<String, Object> userData = (Map<String, Object>) userResult.getData();
vo.setUserNickname((String) userData.get("nickname"));
vo.setUserAvatar((String) userData.get("avatar"));
}
} catch (Exception e) {
log.error("获取用户信息失败: {}", e.getMessage());
}
// 获取审核记录
ContentVideoReview latestReview = videoReviewMapper.selectOne(
new LambdaQueryWrapper<ContentVideoReview>()
.eq(ContentVideoReview::getVideoId, videoId)
.orderByDesc(ContentVideoReview::getCreateTime)
.last("LIMIT 1")
);
// 可以将审核记录信息添加到VO中返回
if (latestReview != null) {
vo.setReviewStatus(latestReview.getStatus());
vo.setReviewReason(latestReview.getReason());
vo.setReviewerId(latestReview.getReviewerId());
vo.setReviewTime(latestReview.getCreateTime());
}
return Result.success(vo);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Result<Void> disableVideo(Long videoId) {
// 查询视频是否存在
ContentVideo video = getById(videoId);
if (video == null) {
return Result.error("视频不存在");
}
// 更新视频状态为已下架
ContentVideo updateVideo = new ContentVideo();
updateVideo.setId(videoId);
updateVideo.setStatus(2); // 已下架
updateVideo.setUpdatedBy("admin");
updateVideo.setUpdateTime(LocalDateTime.now());
updateById(updateVideo);
// 记录操作日志
ContentVideoReview review = new ContentVideoReview();
review.setVideoId(videoId);
review.setReviewerId(0L); // 系统/管理员ID
review.setStatus(2); // 下架
review.setReason("管理员下架操作");
review.setCreatedBy("admin");
review.setCreateTime(LocalDateTime.now());
videoReviewMapper.insert(review);
return Result.success();
}
@Override
public Result<Object> getVideoStats() {
// 收集视频统计数据
Map<String, Object> stats = new HashMap<>();
// 总视频数
long totalVideos = count();
stats.put("totalVideos", totalVideos);
// 今日新增视频
LocalDate today = LocalDate.now();
long todayNewVideos = count(new LambdaQueryWrapper<ContentVideo>()
.ge(ContentVideo::getCreateTime, today.atStartOfDay()));
stats.put("todayNewVideos", todayNewVideos);
// 待审核视频数
long pendingReviewVideos = count(new LambdaQueryWrapper<ContentVideo>()
.eq(ContentVideo::getStatus, 0));
stats.put("pendingReviewVideos", pendingReviewVideos);
// 已发布视频数
long publishedVideos = count(new LambdaQueryWrapper<ContentVideo>()
.eq(ContentVideo::getStatus, 1));
stats.put("publishedVideos", publishedVideos);
// 已下架视频数
long disabledVideos = count(new LambdaQueryWrapper<ContentVideo>()
.eq(ContentVideo::getStatus, 2));
stats.put("disabledVideos", disabledVideos);
// 审核未通过视频数
long rejectedVideos = count(new LambdaQueryWrapper<ContentVideo>()
.eq(ContentVideo::getStatus, 3));
stats.put("rejectedVideos", rejectedVideos);
// 各分类视频数量分布
Map<String, Long> categoryDistribution = new HashMap<>();
List<ContentCategory> categories = categoryMapper.selectList(null);
for (ContentCategory category : categories) {
long count = count(new LambdaQueryWrapper<ContentVideo>()
.eq(ContentVideo::getCategoryId, category.getId()));
categoryDistribution.put(category.getName(), count);
}
stats.put("categoryDistribution", categoryDistribution);
return Result.success(stats);
}
}
@@ -142,4 +142,24 @@ public class VideoInfoVO implements Serializable {
* 发布时间
*/
private LocalDateTime publishTime;
/**
* 审核员ID
*/
private Long reviewerId;
/**
* 审核状态(1:通过,2:不通过)
*/
private Integer reviewStatus;
/**
* 审核原因
*/
private String reviewReason;
/**
* 创建时间
*/
private LocalDateTime reviewTime;
}