Browse Source

视频培训功能

master
maotiantian 3 weeks ago
parent
commit
1fc0b43915
  1. 176
      chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysAnswersController.java
  2. 129
      chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysQuestionsController.java
  3. 417
      chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetTrainingVideoController.java
  4. 160
      chenhai-system/src/main/java/com/chenhai/system/domain/SysAnswers.java
  5. 13
      chenhai-system/src/main/java/com/chenhai/system/domain/SysPolicyInterpretation.java
  6. 186
      chenhai-system/src/main/java/com/chenhai/system/domain/SysQuestions.java
  7. 78
      chenhai-system/src/main/java/com/chenhai/system/mapper/SysAnswersMapper.java
  8. 94
      chenhai-system/src/main/java/com/chenhai/system/mapper/SysQuestionsMapper.java
  9. 93
      chenhai-system/src/main/java/com/chenhai/system/service/ISysAnswersService.java
  10. 106
      chenhai-system/src/main/java/com/chenhai/system/service/ISysQuestionsService.java
  11. 243
      chenhai-system/src/main/java/com/chenhai/system/service/impl/SysAnswersServiceImpl.java
  12. 248
      chenhai-system/src/main/java/com/chenhai/system/service/impl/SysQuestionsServiceImpl.java
  13. 12
      chenhai-system/src/main/java/com/chenhai/vet/domain/VetKnowledge.java
  14. 227
      chenhai-system/src/main/java/com/chenhai/vet/domain/VetTrainingVideo.java
  15. 98
      chenhai-system/src/main/java/com/chenhai/vet/mapper/VetTrainingVideoMapper.java
  16. 132
      chenhai-system/src/main/java/com/chenhai/vet/service/IVetTrainingVideoService.java
  17. 3
      chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetProductServiceImpl.java
  18. 620
      chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetTrainingVideoServiceImpl.java
  19. 112
      chenhai-system/src/main/resources/mapper/system/SysAnswersMapper.xml
  20. 19
      chenhai-system/src/main/resources/mapper/system/SysPolicyInterpretationMapper.xml
  21. 143
      chenhai-system/src/main/resources/mapper/system/SysQuestionsMapper.xml
  22. 9
      chenhai-system/src/main/resources/mapper/vet/VetKnowledgeMapper.xml
  23. 370
      chenhai-system/src/main/resources/mapper/vet/VetTrainingVideoMapper.xml
  24. 44
      chenhai-ui/src/api/system/answers.js
  25. 44
      chenhai-ui/src/api/system/questions.js
  26. 269
      chenhai-ui/src/api/vet/training.js
  27. 252
      chenhai-ui/src/views/system/answers/index.vue
  28. 584
      chenhai-ui/src/views/system/questions/index.vue
  29. 1547
      chenhai-ui/src/views/vet/training/TrainingHome.vue
  30. 1120
      chenhai-ui/src/views/vet/training/index.vue

176
chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysAnswersController.java

@ -0,0 +1,176 @@
package com.chenhai.web.controller.system;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.chenhai.common.annotation.Log;
import com.chenhai.common.core.controller.BaseController;
import com.chenhai.common.core.domain.AjaxResult;
import com.chenhai.common.enums.BusinessType;
import com.chenhai.system.domain.SysAnswers;
import com.chenhai.system.service.ISysAnswersService;
import com.chenhai.system.service.ISysQuestionsService;
import com.chenhai.common.utils.poi.ExcelUtil;
import com.chenhai.common.core.page.TableDataInfo;
/**
* 答复Controller
*
* @author ruoyi
* @date 2026-01-29
*/
@RestController
@RequestMapping("/system/answers")
public class SysAnswersController extends BaseController
{
@Autowired
private ISysAnswersService sysAnswersService;
@Autowired
private ISysQuestionsService sysQuestionsService;
/**
* 查询答复列表
*/
@PreAuthorize("@ss.hasPermi('system:answers:list')")
@GetMapping("/list")
public TableDataInfo list(SysAnswers sysAnswers)
{
startPage();
List<SysAnswers> list = sysAnswersService.selectSysAnswersList(sysAnswers);
return getDataTable(list);
}
/**
* 根据问题ID查询答复列表
*/
@PreAuthorize("@ss.hasPermi('system:answers:list')")
@GetMapping("/question/{questionId}")
public TableDataInfo listByQuestionId(@PathVariable("questionId") Long questionId)
{
startPage();
List<SysAnswers> list = sysAnswersService.selectAnswersByQuestionId(questionId);
return getDataTable(list);
}
/**
* 导出答复列表
*/
@PreAuthorize("@ss.hasPermi('system:answers:export')")
@Log(title = "答复", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, SysAnswers sysAnswers)
{
List<SysAnswers> list = sysAnswersService.selectSysAnswersList(sysAnswers);
ExcelUtil<SysAnswers> util = new ExcelUtil<SysAnswers>(SysAnswers.class);
util.exportExcel(response, list, "答复数据");
}
/**
* 获取答复详细信息
*/
@PreAuthorize("@ss.hasPermi('system:answers:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(sysAnswersService.selectSysAnswersById(id));
}
/**
* 新增答复使用当前登录用户信息
*/
@PreAuthorize("@ss.hasPermi('system:answers:add')")
@Log(title = "答复", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysAnswers sysAnswers)
{
// 验证问题是否存在
if (sysAnswers.getQuestionId() == null) {
return error("问题ID不能为空");
}
// 检查问题是否存在
try {
sysQuestionsService.selectSysQuestionsById(sysAnswers.getQuestionId());
} catch (Exception e) {
return error("问题不存在或已被删除");
}
return toAjax(sysAnswersService.insertSysAnswersWithCurrentUser(sysAnswers));
}
/**
* 修改答复
*/
@PreAuthorize("@ss.hasPermi('system:answers:edit')")
@Log(title = "答复", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysAnswers sysAnswers)
{
return toAjax(sysAnswersService.updateSysAnswers(sysAnswers));
}
/**
* 删除答复
*/
@PreAuthorize("@ss.hasPermi('system:answers:remove')")
@Log(title = "答复", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(sysAnswersService.deleteSysAnswersByIds(ids));
}
/**
* 获取当前用户的答复列表
*/
@PreAuthorize("@ss.hasPermi('system:answers:list')")
@GetMapping("/myList")
public TableDataInfo myList(SysAnswers sysAnswers)
{
// 获取当前登录用户ID
Long userId = getUserId();
// 设置查询条件只查询当前用户的回答
sysAnswers.setUserId(String.valueOf(userId));
startPage();
List<SysAnswers> list = sysAnswersService.selectSysAnswersList(sysAnswers);
return getDataTable(list);
}
/**
* 获取问题的答案数量
*/
@PreAuthorize("@ss.hasPermi('system:answers:query')")
@GetMapping("/count/{questionId}")
public AjaxResult countByQuestionId(@PathVariable("questionId") Long questionId)
{
Long count = sysAnswersService.countAnswersByQuestionId(questionId);
return success(count);
}
/**
* 同步更新问题的答案数量管理员使用
*/
@PreAuthorize("@ss.hasPermi('system:answers:edit')")
@Log(title = "同步答案数量", businessType = BusinessType.UPDATE)
@PostMapping("/syncCount/{questionId}")
public AjaxResult syncAnswerCount(@PathVariable("questionId") Long questionId)
{
try {
sysQuestionsService.updateAnswerCountFromDb(questionId);
return success("同步成功");
} catch (Exception e) {
return error("同步失败: " + e.getMessage());
}
}
}

129
chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysQuestionsController.java

@ -0,0 +1,129 @@
package com.chenhai.web.controller.system;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.chenhai.common.annotation.Log;
import com.chenhai.common.core.controller.BaseController;
import com.chenhai.common.core.domain.AjaxResult;
import com.chenhai.common.core.domain.model.LoginUser;
import com.chenhai.common.enums.BusinessType;
import com.chenhai.system.domain.SysQuestions;
import com.chenhai.system.service.ISysQuestionsService;
import com.chenhai.common.utils.poi.ExcelUtil;
import com.chenhai.common.core.page.TableDataInfo;
import com.chenhai.common.utils.SecurityUtils;
/**
* 问答Controller
*
* @author ruoyi
* @date 2026-01-29
*/
@RestController
@RequestMapping("/system/questions")
public class SysQuestionsController extends BaseController
{
@Autowired
private ISysQuestionsService sysQuestionsService;
/**
* 获取当前登录用户ID
*/
private Long getCurrentUserId() {
LoginUser loginUser = SecurityUtils.getLoginUser();
return loginUser.getUserId();
}
/**
* 查询问答列表
*/
@PreAuthorize("@ss.hasPermi('system:questions:list')")
@GetMapping("/list")
public TableDataInfo list(SysQuestions sysQuestions)
{
startPage();
List<SysQuestions> list = sysQuestionsService.selectSysQuestionsList(sysQuestions);
return getDataTable(list);
}
/**
* 导出问答列表
*/
@PreAuthorize("@ss.hasPermi('system:questions:export')")
@Log(title = "问答", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, SysQuestions sysQuestions)
{
List<SysQuestions> list = sysQuestionsService.selectSysQuestionsList(sysQuestions);
ExcelUtil<SysQuestions> util = new ExcelUtil<SysQuestions>(SysQuestions.class);
util.exportExcel(response, list, "问答数据");
}
/**
* 获取问答详细信息
*/
@PreAuthorize("@ss.hasPermi('system:questions:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(sysQuestionsService.selectSysQuestionsById(id));
}
/**
* 新增问答
*/
@PreAuthorize("@ss.hasPermi('system:questions:add')")
@Log(title = "问答", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysQuestions sysQuestions)
{
Long userId = getCurrentUserId();
return toAjax(sysQuestionsService.insertSysQuestions(sysQuestions, userId));
}
/**
* 修改问答
*/
@PreAuthorize("@ss.hasPermi('system:questions:edit')")
@Log(title = "问答", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysQuestions sysQuestions)
{
Long userId = getCurrentUserId();
return toAjax(sysQuestionsService.updateSysQuestions(sysQuestions, userId));
}
/**
* 删除问答
*/
@PreAuthorize("@ss.hasPermi('system:questions:remove')")
@Log(title = "问答", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
Long userId = getCurrentUserId();
return toAjax(sysQuestionsService.deleteSysQuestionsByIds(ids, userId));
}
/**
* 检查问题权限
*/
@PreAuthorize("@ss.hasPermi('system:questions:query')")
@GetMapping("/checkPermission/{id}")
public AjaxResult checkPermission(@PathVariable("id") Long id)
{
Long userId = getCurrentUserId();
boolean hasPermission = sysQuestionsService.isQuestionBelongsToUser(id, userId);
return success(hasPermission);
}
}

417
chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetTrainingVideoController.java

@ -1,145 +1,268 @@
package com.chenhai.web.controller.vet;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.chenhai.common.utils.SecurityUtils;
import com.chenhai.common.utils.StringUtils;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.chenhai.common.annotation.Log;
import com.chenhai.common.core.controller.BaseController;
import com.chenhai.common.core.domain.AjaxResult;
import com.chenhai.common.core.page.TableDataInfo;
import com.chenhai.common.utils.SecurityUtils;
import com.chenhai.common.enums.BusinessType;
import com.chenhai.vet.domain.VetTrainingVideo;
import com.chenhai.vet.service.IVetTrainingVideoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.chenhai.common.utils.poi.ExcelUtil;
import com.chenhai.common.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
/**
* 兽医培训视频Controller
*
* @author ruoyi
* @date 2026-01-28
*/
@RestController
@RequestMapping("/vet/training")
public class VetTrainingVideoController extends BaseController {
public class VetTrainingVideoController extends BaseController
{
@Autowired
private IVetTrainingVideoService trainingVideoService;
private IVetTrainingVideoService vetTrainingVideoService;
// 从配置文件读取上传路径默认值为 D:/ymtx/uploadPath
@Value("${chenhai.profile:D:/ymtx/uploadPath}")
private String uploadPath;
/**
* 获取当前用户ID
*/
private Long getCurrentUserId() {
// 使用 RuoYi SecurityUtils
return SecurityUtils.getUserId();
}
/**
* 上传培训视频
* 查询兽医培训视频列表
*/
@PostMapping("/upload")
public AjaxResult uploadVideo(
@RequestParam("title") String title,
@RequestParam("videoFile") MultipartFile videoFile,
@RequestParam(value = "description", required = false) String description,
@RequestParam(value = "category", required = false) String category,
@RequestParam(value = "coverImage", required = false) MultipartFile coverImage) {
Long userId = getCurrentUserId();
try {
VetTrainingVideo video = new VetTrainingVideo();
video.setUserId(userId);
video.setTitle(title);
video.setDescription(description);
video.setCategory(category);
String result = trainingVideoService.uploadAndSave(video, videoFile, coverImage);
return success(result);
} catch (Exception e) {
e.printStackTrace();
return error("上传失败:" + e.getMessage());
}
@PreAuthorize("@ss.hasPermi('vet:training:list') or @ss.hasRole('muhu')")
@GetMapping("/list")
public TableDataInfo list(VetTrainingVideo vetTrainingVideo)
{
startPage();
List<VetTrainingVideo> list = vetTrainingVideoService.selectVetTrainingVideoList(vetTrainingVideo);
return getDataTable(list);
}
/**
* 查看我上传的视频
* 导出兽医培训视频列表
*/
@GetMapping("/my-videos")
public TableDataInfo getMyVideos(
@RequestParam(required = false) String title,
@RequestParam(required = false) String category,
@RequestParam(required = false) String status,
@RequestParam(required = false) String auditStatus) {
@PreAuthorize("@ss.hasPermi('vet:training:export') or @ss.hasRole('muhu')")
@Log(title = "兽医培训视频", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, VetTrainingVideo vetTrainingVideo)
{
List<VetTrainingVideo> list = vetTrainingVideoService.selectVetTrainingVideoList(vetTrainingVideo);
ExcelUtil<VetTrainingVideo> util = new ExcelUtil<VetTrainingVideo>(VetTrainingVideo.class);
util.exportExcel(response, list, "兽医培训视频数据");
}
startPage();
Long userId = getCurrentUserId();
List<VetTrainingVideo> list = trainingVideoService.getMyVideos(userId, title, category, status, auditStatus);
return getDataTable(list);
/**
* 获取兽医培训视频详细信息
*/
@PreAuthorize("@ss.hasPermi('vet:training:query') or @ss.hasRole('muhu')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(vetTrainingVideoService.selectVetTrainingVideoById(id));
}
/**
* 查看所有公开的视频
* 新增兽医培训视频
*/
@GetMapping("/public-videos")
public TableDataInfo getPublicVideos(
@RequestParam(required = false) String title,
@RequestParam(required = false) String category,
@RequestParam(required = false) String userName) {
@PreAuthorize("@ss.hasPermi('vet:training:add') or @ss.hasRole('muhu')")
@Log(title = "兽医培训视频", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody VetTrainingVideo vetTrainingVideo)
{
return toAjax(vetTrainingVideoService.insertVetTrainingVideo(vetTrainingVideo));
}
startPage();
List<VetTrainingVideo> list = trainingVideoService.getPublicVideos(title, category, userName);
return getDataTable(list);
/**
* 修改兽医培训视频
*/
@PreAuthorize("@ss.hasPermi('vet:training:edit') or @ss.hasRole('muhu')")
@Log(title = "兽医培训视频", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody VetTrainingVideo vetTrainingVideo)
{
return toAjax(vetTrainingVideoService.updateVetTrainingVideo(vetTrainingVideo));
}
/**
* 查看视频详情
* 删除兽医培训视频
*/
@GetMapping("/video/{videoId}")
public AjaxResult getVideoDetail(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
VetTrainingVideo video = trainingVideoService.getVideoDetail(videoId, userId);
@PreAuthorize("@ss.hasPermi('vet:training:remove') or @ss.hasRole('muhu')")
@Log(title = "兽医培训视频", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(vetTrainingVideoService.deleteVetTrainingVideoByIds(ids));
}
if (video == null) {
return error("视频不存在或无权限查看");
/**
* 上传培训视频
*/
@PreAuthorize("@ss.hasPermi('vet:training:add') or @ss.hasRole('muhu')")
@Log(title = "兽医培训视频", businessType = BusinessType.OTHER)
@PostMapping("/uploadVideo")
public AjaxResult uploadVideo(@RequestParam("file") MultipartFile file)
{
try {
// 基本验证
if (file == null || file.isEmpty()) {
return AjaxResult.error("上传文件不能为空");
}
String fileName = file.getOriginalFilename();
if (StringUtils.isEmpty(fileName)) {
return AjaxResult.error("文件名不能为空");
}
// 验证文件类型
String extension = getFileExtension(fileName).toLowerCase();
String[] allowedExtensions = {"mp4", "avi", "mov", "wmv", "flv", "mkv"};
boolean isValidExtension = false;
for (String ext : allowedExtensions) {
if (ext.equals(extension)) {
isValidExtension = true;
break;
}
}
if (!isValidExtension) {
return AjaxResult.error("不支持的文件格式,请上传MP4、AVI、MOV、WMV、FLV、MKV格式的视频文件");
}
// 验证文件大小限制200MB
long maxSize = 200 * 1024 * 1024; // 200MB
if (file.getSize() > maxSize) {
return AjaxResult.error("文件大小不能超过200MB");
}
// 创建视频上传目录
String videoDir = uploadPath + "/video/";
File dir = new File(videoDir);
if (!dir.exists()) {
boolean created = dir.mkdirs();
if (!created) {
return AjaxResult.error("创建上传目录失败: " + videoDir);
}
}
// 生成唯一文件名避免重复
String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
String newFileName = System.currentTimeMillis() + "_" + uuid + "." + extension;
String filePath = videoDir + newFileName;
// 保存文件
File dest = new File(filePath);
file.transferTo(dest);
// 构建返回的访问路径相对路径
// 这里返回 /profile/video/xxx.mp4前端可以通过 baseUrl + /profile/video/xxx.mp4 访问
String accessPath = "/profile/video/" + newFileName;
// 如果是Windows路径需要将路径中的\替换为/
accessPath = accessPath.replace("\\", "/");
return AjaxResult.success("上传成功", accessPath);
} catch (IOException e) {
logger.error("保存视频文件失败", e);
return AjaxResult.error("保存文件失败:" + e.getMessage());
} catch (Exception e) {
logger.error("上传视频失败", e);
return AjaxResult.error("上传失败:" + e.getMessage());
}
// 如果是审核通过且公开的视频增加观看次数
if ("2".equals(video.getAuditStatus()) && // 2-审核通过
"1".equals(video.getStatus())) { // 1-已上架/公开
trainingVideoService.incrementViewCount(videoId);
video.setViewCount(video.getViewCount() + 1);
}
/**
* 获取文件扩展名
*/
private String getFileExtension(String fileName) {
if (StringUtils.isNotEmpty(fileName) && fileName.contains(".")) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
return success(video);
return "";
}
/**
* 提交审核手动提交
* 批量提交审核
*/
@PostMapping("/submit-audit/{videoId}")
public AjaxResult submitForAudit(@PathVariable Long videoId) {
@PreAuthorize("@ss.hasRole('muhu')")
@PostMapping("/batch-submit-audit")
public AjaxResult batchSubmitForAudit(@RequestBody List<Long> videoIds) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.submitForAudit(videoId, userId);
return success ? success("提交审核成功") : error("提交审核失败");
int successCount = vetTrainingVideoService.batchSubmitForAudit(videoIds, userId); // 改为 vetTrainingVideoService
return success(successCount + "个视频已提交审核");
}
/**
* 取消审核
* 批量审核管理员接口
*/
@PostMapping("/cancel-audit/{videoId}")
public AjaxResult cancelAudit(@PathVariable Long videoId) {
@PreAuthorize("@ss.hasRole('muhu')")
@PostMapping("/batch-audit")
public AjaxResult batchAuditVideo(@RequestBody Map<String, Object> batchAuditData) {
@SuppressWarnings("unchecked")
List<Long> videoIds = (List<Long>) batchAuditData.get("videoIds");
String auditStatus = (String) batchAuditData.get("auditStatus");
String auditOpinion = (String) batchAuditData.get("auditOpinion");
if (videoIds == null || videoIds.isEmpty()) {
return error("请选择要审核的视频");
}
if (auditStatus == null || auditStatus.trim().isEmpty()) {
return error("审核状态不能为空");
}
Long auditUserId = getCurrentUserId();
int successCount = vetTrainingVideoService.batchAuditVideo(videoIds, auditStatus, auditOpinion, auditUserId); // 改为 vetTrainingVideoService
return success("成功审核" + successCount + "个视频");
}
/**
* 批量上架
*/
@PreAuthorize("@ss.hasRole('muhu')")
@PostMapping("/batch-publish")
public AjaxResult batchPublishVideo(@RequestBody List<Long> videoIds) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.cancelAudit(videoId, userId);
return success ? success("取消审核成功") : error("取消审核失败");
int successCount = vetTrainingVideoService.batchPublishVideo(videoIds, userId); // 改为 vetTrainingVideoService
return success(successCount + "个视频已上架");
}
/**
* 重新提交审核
* 批量下架
*/
@PostMapping("/resubmit-audit/{videoId}")
public AjaxResult resubmitForAudit(@PathVariable Long videoId) {
@PreAuthorize("@ss.hasRole('muhu')")
@PostMapping("/batch-offline")
public AjaxResult batchOfflineVideo(@RequestBody List<Long> videoIds) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.resubmitForAudit(videoId, userId);
return success ? success("重新提交审核成功") : error("重新提交审核失败");
int successCount = vetTrainingVideoService.batchOfflineVideo(videoIds, userId); // 改为 vetTrainingVideoService
return success(successCount + "个视频已下架");
}
/**
* 审核视频管理员接口- 使用JSON接收
* 审核单个视频管理员接口
*/
@PreAuthorize("@ss.hasRole('muhu')")
@PostMapping("/audit/{videoId}")
public AjaxResult auditVideo(@PathVariable Long videoId,
@RequestBody Map<String, String> auditData) {
@ -152,70 +275,130 @@ public class VetTrainingVideoController extends BaseController {
}
Long auditUserId = getCurrentUserId();
boolean success = trainingVideoService.auditVideo(videoId, auditStatus, auditOpinion, auditUserId);
return success ? success("审核操作成功") : error("审核操作失败");
// 调用单个审核的 Service 方法
try {
boolean success = vetTrainingVideoService.auditVideo(videoId, auditStatus, auditOpinion, auditUserId);
return success ? success("审核操作成功") : error("审核操作失败");
} catch (Exception e) {
return error("审核失败: " + e.getMessage());
}
}
/**
* 上架视频审核通过后才能上架
* 上架单个视频审核通过后才能上架
*/
@PreAuthorize("@ss.hasRole('muhu')")
@PostMapping("/publish/{videoId}")
public AjaxResult publishVideo(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.publishVideo(videoId, userId);
return success ? success("视频已上架") : error("上架失败,请确保视频已通过审核");
try {
boolean success = vetTrainingVideoService.publishVideo(videoId, userId);
return success ? success("视频已上架") : error("上架失败,请确保视频已通过审核");
} catch (Exception e) {
return error("上架失败: " + e.getMessage());
}
}
/**
* 下架视频
* 下架单个视频
*/
@PreAuthorize("@ss.hasRole('muhu')")
@PostMapping("/offline/{videoId}")
public AjaxResult offlineVideo(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.offlineVideo(videoId, userId);
return success ? success("视频已下架") : error("下架失败");
try {
boolean success = vetTrainingVideoService.offlineVideo(videoId, userId);
return success ? success("视频已下架") : error("下架失败");
} catch (Exception e) {
return error("下架失败: " + e.getMessage());
}
}
/**
* 获取待审核视频列表管理员接口
* 提交审核单个视频
*/
@GetMapping("/pending-audit")
public TableDataInfo getPendingAuditVideos(
@RequestParam(required = false) String title,
@RequestParam(required = false) String userName) {
startPage();
List<VetTrainingVideo> list = trainingVideoService.getPendingAuditVideos(title, userName);
return getDataTable(list);
@PreAuthorize("@ss.hasRole('muhu')")
@PostMapping("/submit-audit/{videoId}")
public AjaxResult submitForAudit(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
try {
boolean success = vetTrainingVideoService.submitForAudit(videoId, userId);
return success ? success("提交审核成功") : error("提交审核失败");
} catch (Exception e) {
return error("提交审核失败: " + e.getMessage());
}
}
/**
* 编辑视频信息
* 取消审核单个视频
*/
@PutMapping("/update/{videoId}")
public AjaxResult updateVideoInfo(@PathVariable Long videoId,
@RequestBody VetTrainingVideo video) {
@PreAuthorize("@ss.hasRole('muhu')")
@PostMapping("/cancel-audit/{videoId}")
public AjaxResult cancelAudit(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.updateVideoInfo(videoId, video, userId);
return success ? success("更新成功") : error("更新失败");
// 查询视频信息
VetTrainingVideo video = vetTrainingVideoService.selectVetTrainingVideoById(videoId);
if (video == null) {
return error("视频不存在");
}
// 检查权限管理员或视频所有者
boolean isAdmin = SecurityUtils.getLoginUser().getUser().isAdmin();
if (!isAdmin && !userId.equals(video.getUserId())) {
return error("无权操作此视频");
}
try {
boolean success = vetTrainingVideoService.cancelAudit(videoId, userId);
return success ? success("取消审核成功") : error("取消审核失败");
} catch (Exception e) {
return error("取消审核失败: " + e.getMessage());
}
}
/**
* 删除我的视频
* 查询公开视频列表无需登录
* 已上架status=1且已审核通过audit_status=1
*/
@DeleteMapping("/{videoId}")
public AjaxResult deleteVideo(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
VetTrainingVideo video = trainingVideoService.getVideoDetail(videoId, userId);
@PreAuthorize("@ss.hasRole('muhu')")
@GetMapping("/public/list")
public TableDataInfo publicList(VetTrainingVideo vetTrainingVideo)
{
startPage();
// 使用分页查询公开视频
List<VetTrainingVideo> list = vetTrainingVideoService.selectPublicVideoPageList(vetTrainingVideo);
return getDataTable(list);
}
/**
* 获取公开视频详情无需登录
* 只能查看已上架且审核通过的视频
*/
@PreAuthorize("@ss.hasRole('muhu')")
@GetMapping("/public/{id}")
public AjaxResult getPublicInfo(@PathVariable("id") Long id)
{
VetTrainingVideo video = vetTrainingVideoService.selectVetTrainingVideoById(id);
if (video == null) {
return error("视频不存在");
}
if (!userId.equals(video.getUserId())) {
return error("无权删除此视频");
// 检查视频状态必须已上架且审核通过
if (!"1".equals(video.getStatus()) || !"1".equals(video.getAuditStatus())) {
return error("视频不可访问");
}
// 增加观看次数
if (video.getViewCount() == null) {
video.setViewCount(0L);
}
video.setViewCount(video.getViewCount() + 1);
vetTrainingVideoService.updateVetTrainingVideo(video);
return toAjax(trainingVideoService.deleteVideoById(videoId));
return success(video);
}
}
}

160
chenhai-system/src/main/java/com/chenhai/system/domain/SysAnswers.java

@ -0,0 +1,160 @@
package com.chenhai.system.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.chenhai.common.annotation.Excel;
import com.chenhai.common.core.domain.BaseEntity;
/**
* 答复对象 sys_answers
*
* @author ruoyi
* @date 2026-01-29
*/
public class SysAnswers extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 对应的问题ID */
@Excel(name = "对应的问题ID")
private Long questionId;
/** 用户ID */
@Excel(name = "用户ID")
private String userId;
/** 用户名 */
@Excel(name = "用户名")
private String username;
/** 用户昵称 */
@Excel(name = "用户昵称")
private String nickName;
/** 用户头像 */
private String avatar;
/** 回答内容 */
@Excel(name = "回答内容")
private String content;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date createdAt;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date updatedAt;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setQuestionId(Long questionId)
{
this.questionId = questionId;
}
public Long getQuestionId()
{
return questionId;
}
public void setUserId(String userId)
{
this.userId = userId;
}
public String getUserId()
{
return userId;
}
public void setUsername(String username)
{
this.username = username;
}
public String getUsername()
{
return username;
}
public void setNickName(String nickName)
{
this.nickName = nickName;
}
public String getNickName()
{
return nickName;
}
public void setAvatar(String avatar)
{
this.avatar = avatar;
}
public String getAvatar()
{
return avatar;
}
public void setContent(String content)
{
this.content = content;
}
public String getContent()
{
return content;
}
public void setCreatedAt(Date createdAt)
{
this.createdAt = createdAt;
}
public Date getCreatedAt()
{
return createdAt;
}
public void setUpdatedAt(Date updatedAt)
{
this.updatedAt = updatedAt;
}
public Date getUpdatedAt()
{
return updatedAt;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("questionId", getQuestionId())
.append("userId", getUserId())
.append("username", getUsername())
.append("nickName", getNickName())
.append("avatar", getAvatar())
.append("content", getContent())
.append("createdAt", getCreatedAt())
.append("updatedAt", getUpdatedAt())
.toString();
}
}

13
chenhai-system/src/main/java/com/chenhai/system/domain/SysPolicyInterpretation.java

@ -53,6 +53,9 @@ public class SysPolicyInterpretation extends BaseEntity
@Excel(name = "政策分类") // 添加dictType用于字典转换
private String policyCategory;
/** 搜索关键词(非数据库字段) */
private String searchKey;
/** 删除标志(0代表存在 2代表删除) */
private String delFlag;
@ -156,6 +159,15 @@ public class SysPolicyInterpretation extends BaseEntity
return policyCategory;
}
public String getSearchKey() {
return searchKey;
}
public void setSearchKey(String searchKey) {
this.searchKey = searchKey;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -174,6 +186,7 @@ public class SysPolicyInterpretation extends BaseEntity
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.append("policyCategory", getPolicyCategory())
.append("searchKey", getSearchKey())
.toString();
}
}

186
chenhai-system/src/main/java/com/chenhai/system/domain/SysQuestions.java

@ -0,0 +1,186 @@
package com.chenhai.system.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.chenhai.common.annotation.Excel;
import com.chenhai.common.core.domain.BaseEntity;
/**
* 问答对象 sys_questions
*
* @author ruoyi
* @date 2026-01-29
*/
public class SysQuestions extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 用户ID */
@Excel(name = "用户ID")
private Long userId;
/** 用户名 */
@Excel(name = "用户名")
private String username;
/** 问题标题 */
@Excel(name = "问题标题")
private String title;
/** 问题内容 */
@Excel(name = "问题内容")
private String content;
/** 标签 */
@Excel(name = "标签")
private String tags;
/** 回答数 */
@Excel(name = "回答数")
private Long answerCount;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date createdAt;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date updatedAt;
/** 用户昵称 */
@Excel(name = "用户昵称")
private String nickName;
/** 用户头像 */
private String avatar;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public Long getUserId()
{
return userId;
}
public void setUsername(String username)
{
this.username = username;
}
public String getUsername()
{
return username;
}
public void setTitle(String title)
{
this.title = title;
}
public String getTitle()
{
return title;
}
public void setContent(String content)
{
this.content = content;
}
public String getContent()
{
return content;
}
public void setTags(String tags)
{
this.tags = tags;
}
public String getTags()
{
return tags;
}
public void setAnswerCount(Long answerCount)
{
this.answerCount = answerCount;
}
public Long getAnswerCount()
{
return answerCount;
}
public void setCreatedAt(Date createdAt)
{
this.createdAt = createdAt;
}
public Date getCreatedAt()
{
return createdAt;
}
public void setUpdatedAt(Date updatedAt)
{
this.updatedAt = updatedAt;
}
public Date getUpdatedAt()
{
return updatedAt;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("userId", getUserId())
.append("username", getUsername())
.append("title", getTitle())
.append("content", getContent())
.append("tags", getTags())
.append("answerCount", getAnswerCount())
.append("createdAt", getCreatedAt())
.append("updatedAt", getUpdatedAt())
.append("nickName", getNickName())
.append("avatar", getAvatar())
.toString();
}
}

78
chenhai-system/src/main/java/com/chenhai/system/mapper/SysAnswersMapper.java

@ -0,0 +1,78 @@
package com.chenhai.system.mapper;
import java.util.List;
import com.chenhai.system.domain.SysAnswers;
import org.apache.ibatis.annotations.Param;
/**
* 答复Mapper接口
*
* @author ruoyi
* @date 2026-01-29
*/
public interface SysAnswersMapper
{
/**
* 查询答复
*
* @param id 答复主键
* @return 答复
*/
public SysAnswers selectSysAnswersById(Long id);
/**
* 查询答复列表
*
* @param sysAnswers 答复
* @return 答复集合
*/
public List<SysAnswers> selectSysAnswersList(SysAnswers sysAnswers);
/**
* 新增答复
*
* @param sysAnswers 答复
* @return 结果
*/
public int insertSysAnswers(SysAnswers sysAnswers);
/**
* 修改答复
*
* @param sysAnswers 答复
* @return 结果
*/
public int updateSysAnswers(SysAnswers sysAnswers);
/**
* 删除答复
*
* @param id 答复主键
* @return 结果
*/
public int deleteSysAnswersById(Long id);
/**
* 批量删除答复
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteSysAnswersByIds(Long[] ids);
/**
* 根据问题ID删除答复
*
* @param questionId 问题ID
* @return 结果
*/
public int deleteAnswersByQuestionId(Long questionId);
/**
* 获取问题的答案数量
*
* @param questionId 问题ID
* @return 答案数量
*/
public Long countAnswersByQuestionId(Long questionId);
}

94
chenhai-system/src/main/java/com/chenhai/system/mapper/SysQuestionsMapper.java

@ -0,0 +1,94 @@
package com.chenhai.system.mapper;
import java.util.List;
import com.chenhai.system.domain.SysQuestions;
import org.apache.ibatis.annotations.Param;
/**
* 问答Mapper接口
*
* @author ruoyi
* @date 2026-01-29
*/
public interface SysQuestionsMapper
{
/**
* 查询问答
*
* @param id 问答主键
* @return 问答
*/
public SysQuestions selectSysQuestionsById(Long id);
/**
* 查询问答列表
*
* @param sysQuestions 问答
* @return 问答集合
*/
public List<SysQuestions> selectSysQuestionsList(SysQuestions sysQuestions);
/**
* 新增问答
*
* @param sysQuestions 问答
* @return 结果
*/
public int insertSysQuestions(SysQuestions sysQuestions);
/**
* 修改问答
*
* @param sysQuestions 问答
* @return 结果
*/
public int updateSysQuestions(SysQuestions sysQuestions);
/**
* 删除问答
*
* @param id 问答主键
* @return 结果
*/
public int deleteSysQuestionsById(Long id);
/**
* 批量删除问答
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteSysQuestionsByIds(Long[] ids);
/**
* 增加问题的答案数量
*
* @param questionId 问题ID
* @return 结果
*/
public int increaseAnswerCount(Long questionId);
/**
* 减少问题的答案数量
*
* @param questionId 问题ID
* @return 结果
*/
public int decreaseAnswerCount(Long questionId);
/**
* 更新问题的答案数量从数据库实时统计
*
* @param questionId 问题ID
* @return 结果
*/
public int updateAnswerCountFromDb(Long questionId);
/**
* 获取问题的答案数量
*
* @param questionId 问题ID
* @return 答案数量
*/
public Long getAnswerCount(Long questionId);
}

93
chenhai-system/src/main/java/com/chenhai/system/service/ISysAnswersService.java

@ -0,0 +1,93 @@
package com.chenhai.system.service;
import java.util.List;
import com.chenhai.system.domain.SysAnswers;
/**
* 答复Service接口
*
* @author ruoyi
* @date 2026-01-29
*/
public interface ISysAnswersService
{
/**
* 查询答复
*
* @param id 答复主键
* @return 答复
*/
public SysAnswers selectSysAnswersById(Long id);
/**
* 查询答复列表
*
* @param sysAnswers 答复
* @return 答复集合
*/
public List<SysAnswers> selectSysAnswersList(SysAnswers sysAnswers);
/**
* 新增答复
*
* @param sysAnswers 答复
* @return 结果
*/
public int insertSysAnswers(SysAnswers sysAnswers);
/**
* 新增答复自动设置当前用户信息
*
* @param sysAnswers 答复
* @return 结果
*/
public int insertSysAnswersWithCurrentUser(SysAnswers sysAnswers);
/**
* 修改答复
*
* @param sysAnswers 答复
* @return 结果
*/
public int updateSysAnswers(SysAnswers sysAnswers);
/**
* 批量删除答复
*
* @param ids 需要删除的答复主键集合
* @return 结果
*/
public int deleteSysAnswersByIds(Long[] ids);
/**
* 删除答复信息
*
* @param id 答复主键
* @return 结果
*/
public int deleteSysAnswersById(Long id);
/**
* 根据问题ID删除答复
*
* @param questionId 问题ID
* @return 结果
*/
public int deleteSysAnswersByQuestionId(Long questionId);
/**
* 获取问题的答案列表
*
* @param questionId 问题ID
* @return 答案集合
*/
public List<SysAnswers> selectAnswersByQuestionId(Long questionId);
/**
* 获取问题的答案数量
*
* @param questionId 问题ID
* @return 答案数量
*/
public Long countAnswersByQuestionId(Long questionId);
}

106
chenhai-system/src/main/java/com/chenhai/system/service/ISysQuestionsService.java

@ -0,0 +1,106 @@
package com.chenhai.system.service;
import java.util.List;
import com.chenhai.system.domain.SysQuestions;
/**
* 问答Service接口
*
* @author ruoyi
* @date 2026-01-29
*/
public interface ISysQuestionsService
{
/**
* 查询问答
*
* @param id 问答主键
* @return 问答
*/
public SysQuestions selectSysQuestionsById(Long id);
/**
* 查询问答列表
*
* @param sysQuestions 问答
* @return 问答集合
*/
public List<SysQuestions> selectSysQuestionsList(SysQuestions sysQuestions);
/**
* 新增问答自动关联当前用户
*
* @param sysQuestions 问答
* @param userId 当前用户ID
* @return 结果
*/
public int insertSysQuestions(SysQuestions sysQuestions, Long userId);
/**
* 修改问答权限验证
*
* @param sysQuestions 问答
* @param userId 当前用户ID
* @return 结果
*/
public int updateSysQuestions(SysQuestions sysQuestions, Long userId);
/**
* 批量删除问答权限验证
*
* @param ids 需要删除的问答主键集合
* @param userId 当前用户ID
* @return 结果
*/
public int deleteSysQuestionsByIds(Long[] ids, Long userId);
/**
* 删除问答信息权限验证
*
* @param id 问答主键
* @param userId 当前用户ID
* @return 结果
*/
public int deleteSysQuestionsById(Long id, Long userId);
/**
* 检查问题是否属于当前用户
*
* @param questionId 问题ID
* @param userId 用户ID
* @return 结果
*/
public boolean isQuestionBelongsToUser(Long questionId, Long userId);
/**
* 增加问题的答案数量
*
* @param questionId 问题ID
* @return 结果
*/
public int increaseAnswerCount(Long questionId);
/**
* 减少问题的答案数量
*
* @param questionId 问题ID
* @return 结果
*/
public int decreaseAnswerCount(Long questionId);
/**
* 更新问题的答案数量从数据库实时统计
*
* @param questionId 问题ID
* @return 结果
*/
public int updateAnswerCountFromDb(Long questionId);
/**
* 获取问题的答案数量
*
* @param questionId 问题ID
* @return 答案数量
*/
public Long getAnswerCount(Long questionId);
}

243
chenhai-system/src/main/java/com/chenhai/system/service/impl/SysAnswersServiceImpl.java

@ -0,0 +1,243 @@
package com.chenhai.system.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.chenhai.common.core.domain.entity.SysUser;
import com.chenhai.common.utils.SecurityUtils;
import com.chenhai.common.utils.StringUtils;
import com.chenhai.system.mapper.SysAnswersMapper;
import com.chenhai.system.domain.SysAnswers;
import com.chenhai.system.service.ISysAnswersService;
import com.chenhai.system.service.ISysQuestionsService;
import com.chenhai.common.exception.ServiceException;
/**
* 答复Service业务层处理
*
* @author ruoyi
* @date 2026-01-29
*/
@Service
public class SysAnswersServiceImpl implements ISysAnswersService
{
@Autowired
private SysAnswersMapper sysAnswersMapper;
@Autowired
private ISysQuestionsService sysQuestionsService;
/**
* 查询答复
*
* @param id 答复主键
* @return 答复
*/
@Override
public SysAnswers selectSysAnswersById(Long id)
{
return sysAnswersMapper.selectSysAnswersById(id);
}
/**
* 查询答复列表
*
* @param sysAnswers 答复
* @return 答复
*/
@Override
public List<SysAnswers> selectSysAnswersList(SysAnswers sysAnswers)
{
return sysAnswersMapper.selectSysAnswersList(sysAnswers);
}
/**
* 新增答复
*
* @param sysAnswers 答复
* @return 结果
*/
@Override
@Transactional
public int insertSysAnswers(SysAnswers sysAnswers)
{
// 验证问题ID是否存在
if (sysAnswers.getQuestionId() == null) {
throw new ServiceException("问题ID不能为空");
}
int result = sysAnswersMapper.insertSysAnswers(sysAnswers);
if (result > 0) {
// 增加问题的答案数量
sysQuestionsService.increaseAnswerCount(sysAnswers.getQuestionId());
}
return result;
}
/**
* 新增答复自动设置当前用户信息
*
* @param sysAnswers 答复
* @return 结果
*/
@Override
@Transactional
public int insertSysAnswersWithCurrentUser(SysAnswers sysAnswers)
{
// 验证问题ID是否存在
if (sysAnswers.getQuestionId() == null) {
throw new ServiceException("问题ID不能为空");
}
// 获取当前登录用户信息
SysUser currentUser = SecurityUtils.getLoginUser().getUser();
// 设置用户信息
sysAnswers.setUserId(String.valueOf(currentUser.getUserId()));
sysAnswers.setUsername(currentUser.getUserName());
// 如果前端没有传递昵称使用系统昵称
if (StringUtils.isEmpty(sysAnswers.getNickName())) {
sysAnswers.setNickName(currentUser.getNickName());
}
// 设置头像
sysAnswers.setAvatar(currentUser.getAvatar());
int result = sysAnswersMapper.insertSysAnswers(sysAnswers);
if (result > 0) {
// 增加问题的答案数量
sysQuestionsService.increaseAnswerCount(sysAnswers.getQuestionId());
}
return result;
}
/**
* 修改答复
*
* @param sysAnswers 答复
* @return 结果
*/
@Override
@Transactional
public int updateSysAnswers(SysAnswers sysAnswers)
{
return sysAnswersMapper.updateSysAnswers(sysAnswers);
}
/**
* 批量删除答复
*
* @param ids 需要删除的答复主键
* @return 结果
*/
@Override
@Transactional
public int deleteSysAnswersByIds(Long[] ids)
{
// 先获取所有要删除的答案记录对应的问题ID
for (Long id : ids) {
SysAnswers answer = sysAnswersMapper.selectSysAnswersById(id);
if (answer != null) {
// 减少问题的答案数量
sysQuestionsService.decreaseAnswerCount(answer.getQuestionId());
}
}
return sysAnswersMapper.deleteSysAnswersByIds(ids);
}
/**
* 删除答复信息
*
* @param id 答复主键
* @return 结果
*/
@Override
@Transactional
public int deleteSysAnswersById(Long id)
{
SysAnswers answer = sysAnswersMapper.selectSysAnswersById(id);
if (answer == null) {
throw new ServiceException("答复不存在");
}
Long questionId = answer.getQuestionId();
int result = sysAnswersMapper.deleteSysAnswersById(id);
if (result > 0) {
// 减少问题的答案数量
sysQuestionsService.decreaseAnswerCount(questionId);
}
return result;
}
/**
* 根据问题ID删除答复
*
* @param questionId 问题ID
* @return 结果
*/
@Override
@Transactional
public int deleteSysAnswersByQuestionId(Long questionId)
{
// 获取该问题的所有答案
List<SysAnswers> answers = selectAnswersByQuestionId(questionId);
// 删除所有答案
int result = sysAnswersMapper.deleteAnswersByQuestionId(questionId);
if (result > 0) {
// 重置问题的答案数量为0
updateAnswerCountToZero(questionId);
}
return result;
}
/**
* 获取问题的答案列表
*
* @param questionId 问题ID
* @return 答案集合
*/
@Override
public List<SysAnswers> selectAnswersByQuestionId(Long questionId)
{
SysAnswers query = new SysAnswers();
query.setQuestionId(questionId);
return sysAnswersMapper.selectSysAnswersList(query);
}
/**
* 获取问题的答案数量
*
* @param questionId 问题ID
* @return 答案数量
*/
@Override
public Long countAnswersByQuestionId(Long questionId)
{
SysAnswers query = new SysAnswers();
query.setQuestionId(questionId);
List<SysAnswers> answers = sysAnswersMapper.selectSysAnswersList(query);
return (long) answers.size();
}
/**
* 重置问题的答案数量为0私有方法
*
* @param questionId 问题ID
*/
private void updateAnswerCountToZero(Long questionId)
{
// 这里需要调用Questions的更新方法
// 由于service间循环依赖问题这里直接更新数据库
// 或者可以在QuestionsService中添加一个重置方法
}
}

248
chenhai-system/src/main/java/com/chenhai/system/service/impl/SysQuestionsServiceImpl.java

@ -0,0 +1,248 @@
package com.chenhai.system.service.impl;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.chenhai.system.mapper.SysQuestionsMapper;
import com.chenhai.system.domain.SysQuestions;
import com.chenhai.system.service.ISysQuestionsService;
import com.chenhai.common.exception.ServiceException;
import com.chenhai.common.utils.MessageUtils;
/**
* 问答Service业务层处理
*
* @author ruoyi
* @date 2026-01-29
*/
@Service
public class SysQuestionsServiceImpl implements ISysQuestionsService
{
@Autowired
private SysQuestionsMapper sysQuestionsMapper;
/**
* 查询问答
*
* @param id 问答主键
* @return 问答
*/
@Override
public SysQuestions selectSysQuestionsById(Long id)
{
return sysQuestionsMapper.selectSysQuestionsById(id);
}
/**
* 查询问答列表
*
* @param sysQuestions 问答
* @return 问答
*/
@Override
public List<SysQuestions> selectSysQuestionsList(SysQuestions sysQuestions)
{
return sysQuestionsMapper.selectSysQuestionsList(sysQuestions);
}
/**
* 新增问答自动关联当前用户
*
* @param sysQuestions 问答
* @param userId 当前用户ID
* @return 结果
*/
@Override
@Transactional
public int insertSysQuestions(SysQuestions sysQuestions, Long userId)
{
// 设置当前用户ID
sysQuestions.setUserId(userId);
// 设置默认值
if (sysQuestions.getAnswerCount() == null) {
sysQuestions.setAnswerCount(0L);
}
Date now = new Date();
if (sysQuestions.getCreatedAt() == null) {
sysQuestions.setCreatedAt(now);
}
if (sysQuestions.getUpdatedAt() == null) {
sysQuestions.setUpdatedAt(now);
}
return sysQuestionsMapper.insertSysQuestions(sysQuestions);
}
/**
* 修改问答权限验证
*
* @param sysQuestions 问答
* @param userId 当前用户ID
* @return 结果
*/
@Override
@Transactional
public int updateSysQuestions(SysQuestions sysQuestions, Long userId)
{
// 检查问题是否存在
SysQuestions existingQuestion = sysQuestionsMapper.selectSysQuestionsById(sysQuestions.getId());
if (existingQuestion == null) {
throw new ServiceException("问题不存在");
}
// 检查权限只能修改自己的问题
if (!existingQuestion.getUserId().equals(userId)) {
throw new ServiceException("没有权限修改此问题");
}
// 设置更新时间
sysQuestions.setUpdatedAt(new Date());
return sysQuestionsMapper.updateSysQuestions(sysQuestions);
}
/**
* 批量删除问答权限验证
*
* @param ids 需要删除的问答主键
* @param userId 当前用户ID
* @return 结果
*/
@Override
@Transactional
public int deleteSysQuestionsByIds(Long[] ids, Long userId)
{
// 检查权限只能删除自己的问题
for (Long id : ids) {
SysQuestions question = sysQuestionsMapper.selectSysQuestionsById(id);
if (question == null) {
throw new ServiceException("问题不存在,ID: " + id);
}
if (!question.getUserId().equals(userId)) {
throw new ServiceException("没有权限删除此问题,ID: " + id);
}
}
return sysQuestionsMapper.deleteSysQuestionsByIds(ids);
}
/**
* 删除问答信息权限验证
*
* @param id 问答主键
* @param userId 当前用户ID
* @return 结果
*/
@Override
@Transactional
public int deleteSysQuestionsById(Long id, Long userId)
{
// 检查问题是否存在
SysQuestions question = sysQuestionsMapper.selectSysQuestionsById(id);
if (question == null) {
throw new ServiceException("问题不存在");
}
// 检查权限只能删除自己的问题
if (!question.getUserId().equals(userId)) {
throw new ServiceException("没有权限删除此问题");
}
return sysQuestionsMapper.deleteSysQuestionsById(id);
}
/**
* 检查问题是否属于当前用户
*
* @param questionId 问题ID
* @param userId 用户ID
* @return 结果
*/
@Override
public boolean isQuestionBelongsToUser(Long questionId, Long userId)
{
SysQuestions question = sysQuestionsMapper.selectSysQuestionsById(questionId);
if (question == null) {
return false;
}
return question.getUserId().equals(userId);
}
/**
* 增加问题的答案数量
*
* @param questionId 问题ID
* @return 结果
*/
@Override
@Transactional
public int increaseAnswerCount(Long questionId)
{
// 检查问题是否存在
SysQuestions question = sysQuestionsMapper.selectSysQuestionsById(questionId);
if (question == null) {
throw new ServiceException("问题不存在,ID: " + questionId);
}
return sysQuestionsMapper.increaseAnswerCount(questionId);
}
/**
* 减少问题的答案数量
*
* @param questionId 问题ID
* @return 结果
*/
@Override
@Transactional
public int decreaseAnswerCount(Long questionId)
{
// 检查问题是否存在
SysQuestions question = sysQuestionsMapper.selectSysQuestionsById(questionId);
if (question == null) {
throw new ServiceException("问题不存在,ID: " + questionId);
}
return sysQuestionsMapper.decreaseAnswerCount(questionId);
}
/**
* 更新问题的答案数量从数据库实时统计
*
* @param questionId 问题ID
* @return 结果
*/
@Override
@Transactional
public int updateAnswerCountFromDb(Long questionId)
{
// 检查问题是否存在
SysQuestions question = sysQuestionsMapper.selectSysQuestionsById(questionId);
if (question == null) {
throw new ServiceException("问题不存在,ID: " + questionId);
}
return sysQuestionsMapper.updateAnswerCountFromDb(questionId);
}
/**
* 获取问题的答案数量
*
* @param questionId 问题ID
* @return 答案数量
*/
@Override
public Long getAnswerCount(Long questionId)
{
SysQuestions question = sysQuestionsMapper.selectSysQuestionsById(questionId);
if (question == null) {
throw new ServiceException("问题不存在,ID: " + questionId);
}
return question.getAnswerCount();
}
}

12
chenhai-system/src/main/java/com/chenhai/vet/domain/VetKnowledge.java

@ -66,6 +66,9 @@ public class VetKnowledge extends BaseEntity
@Excel(name = "副标题")
private String subtitle;
/** 搜索关键词(非数据库字段,用于搜索) */
private String searchKey;
/** 专家ID */
@Excel(name = "专家ID")
private Long expertId;
@ -206,6 +209,14 @@ public class VetKnowledge extends BaseEntity
this.subtitle = subtitle;
}
public String getSearchKey() {
return searchKey;
}
public void setSearchKey(String searchKey) {
this.searchKey = searchKey;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -227,6 +238,7 @@ public class VetKnowledge extends BaseEntity
.append("expertId", getExpertId())
.append("coverImage", getCoverImage())
.append("subtitle", getSubtitle())
.append("searchKey", getSearchKey())
.toString();
}
}

227
chenhai-system/src/main/java/com/chenhai/vet/domain/VetTrainingVideo.java

@ -1,26 +1,28 @@
package com.chenhai.vet.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.chenhai.common.annotation.Excel;
import com.chenhai.common.core.domain.BaseEntity;
import java.util.Date;
/**
* 兽医培训视频对象 vet_training_video
*
*
* @author ruoyi
* @date 2026-01-08
* @date 2026-01-28
*/
public class VetTrainingVideo extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键 */
/** 主键ID */
private Long id;
/** 用户ID */
/** 兽医ID */
@Excel(name = "兽医ID")
private Long userId;
/** 视频标题 */
@ -31,40 +33,43 @@ public class VetTrainingVideo extends BaseEntity
@Excel(name = "视频描述")
private String description;
/** 视频URL */
@Excel(name = "视频URL")
/** 视频地址 */
@Excel(name = "视频地址")
private String videoUrl;
/** 封面图片 */
@Excel(name = "封面图片")
private String coverImage;
/** 视频分类(手术技巧/疾病诊断/药物使用/其他) */
@Excel(name = "视频分类", dictType = "video_category")
/** 分类 */
@Excel(name = "分类")
private String category;
/** 视频标签 */
@Excel(name = "视频标签")
/** 标签(逗号分隔) */
@Excel(name = "标签", readConverterExp = "逗=号分隔")
private String tags;
/** 视频时长(秒) */
@Excel(name = "视频时长")
private Integer duration;
@Excel(name = "视频时长", readConverterExp = "秒=")
private Long duration;
/** 文件大小(字节) */
@Excel(name = "文件大小")
@Excel(name = "文件大小", readConverterExp = "字=节")
private Long fileSize;
/** 观看次数 */
@Excel(name = "观看次数")
private Integer viewCount;
private Long viewCount;
/** 上架状态(0-私有 1-公开) */
@Excel(name = "上架状态", dictType = "sys_publish_status")
/** 状态(0-私有 1-公开) */
@Excel(name = "状态", readConverterExp = "0=-私有,1=-公开")
private String status;
/** 审核状态(0-待审核 1-审核通过 2-审核拒绝 3-无需审核) */
@Excel(name = "审核状态", dictType = "audit_status")
/** 删除标志(0=正常,1=删除) */
private String delFlag;
/** 审核状态:0-待审核 1-审核通过 2-审核拒绝 3-无需审核 */
@Excel(name = "审核状态:0-待审核 1-审核通过 2-审核拒绝 3-无需审核")
private String auditStatus;
/** 审核意见 */
@ -72,225 +77,261 @@ public class VetTrainingVideo extends BaseEntity
private String auditOpinion;
/** 审核人ID */
@Excel(name = "审核人ID")
private Long auditUserId;
/** 审核时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "审核时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date auditTime;
/** 格式化后的时长(如:12:30) */
private String durationStr;
/** 发布者姓名(非数据库字段) */
@JsonProperty("publisherName")
@Excel(name = "发布者")
private String publisherName;
/** 发布者头像(非数据库字段) */
@JsonProperty("publisherAvatar")
private String publisherAvatar;
/** 用户名称(非数据库字段) */
private String userName;
private String userAvatar;
/** 搜索关键词(非数据库字段,用于多字段模糊搜索) */
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String searchKey;
public void setId(Long id)
/** 发布时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "发布时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date publishTime;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
public Long getId()
{
return id;
}
public void setUserId(Long userId)
public void setUserId(Long userId)
{
this.userId = userId;
}
public Long getUserId()
public Long getUserId()
{
return userId;
}
public void setTitle(String title)
public void setTitle(String title)
{
this.title = title;
}
public String getTitle()
public String getTitle()
{
return title;
}
public void setDescription(String description)
public void setDescription(String description)
{
this.description = description;
}
public String getDescription()
public String getDescription()
{
return description;
}
public void setVideoUrl(String videoUrl)
public void setVideoUrl(String videoUrl)
{
this.videoUrl = videoUrl;
}
public String getVideoUrl()
public String getVideoUrl()
{
return videoUrl;
}
public void setCoverImage(String coverImage)
public void setCoverImage(String coverImage)
{
this.coverImage = coverImage;
}
public String getCoverImage()
public String getCoverImage()
{
return coverImage;
}
public void setCategory(String category)
public void setCategory(String category)
{
this.category = category;
}
public String getCategory()
public String getCategory()
{
return category;
}
public void setTags(String tags)
public void setTags(String tags)
{
this.tags = tags;
}
public String getTags()
public String getTags()
{
return tags;
}
public void setDuration(Integer duration)
public void setDuration(Long duration)
{
this.duration = duration;
}
public Integer getDuration()
public Long getDuration()
{
return duration;
}
public void setFileSize(Long fileSize)
public void setFileSize(Long fileSize)
{
this.fileSize = fileSize;
}
public Long getFileSize()
public Long getFileSize()
{
return fileSize;
}
public void setViewCount(Integer viewCount)
public void setViewCount(Long viewCount)
{
this.viewCount = viewCount;
}
public Integer getViewCount()
public Long getViewCount()
{
return viewCount;
}
public void setStatus(String status)
public void setStatus(String status)
{
this.status = status;
}
public String getStatus()
public String getStatus()
{
return status;
}
public void setAuditStatus(String auditStatus)
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public String getDelFlag()
{
return delFlag;
}
public void setAuditStatus(String auditStatus)
{
this.auditStatus = auditStatus;
}
public String getAuditStatus()
public String getAuditStatus()
{
return auditStatus;
}
public void setAuditOpinion(String auditOpinion)
public void setAuditOpinion(String auditOpinion)
{
this.auditOpinion = auditOpinion;
}
public String getAuditOpinion()
public String getAuditOpinion()
{
return auditOpinion;
}
public void setAuditUserId(Long auditUserId)
public void setAuditUserId(Long auditUserId)
{
this.auditUserId = auditUserId;
}
public Long getAuditUserId()
public Long getAuditUserId()
{
return auditUserId;
}
public void setAuditTime(Date auditTime)
public void setAuditTime(Date auditTime)
{
this.auditTime = auditTime;
}
public Date getAuditTime()
public Date getAuditTime()
{
return auditTime;
}
public String getDurationStr() {
return durationStr;
public String getPublisherName() {
return publisherName;
}
public void setPublisherName(String publisherName) {
this.publisherName = publisherName;
}
public void setDurationStr(String durationStr) {
this.durationStr = durationStr;
public String getPublisherAvatar() {
return publisherAvatar;
}
public String getUserName() {
return userName;
public void setPublisherAvatar(String publisherAvatar) {
this.publisherAvatar = publisherAvatar;
}
public void setUserName(String userName) {
this.userName = userName;
public String getSearchKey() {
return searchKey;
}
public String getUserAvatar() {
return userAvatar;
public void setSearchKey(String searchKey) {
this.searchKey = searchKey;
}
public void setUserAvatar(String userAvatar) {
this.userAvatar = userAvatar;
public void setPublishTime(Date publishTime)
{
this.publishTime = publishTime;
}
public Date getPublishTime()
{
return publishTime;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("userId", getUserId())
.append("title", getTitle())
.append("description", getDescription())
.append("videoUrl", getVideoUrl())
.append("coverImage", getCoverImage())
.append("category", getCategory())
.append("tags", getTags())
.append("duration", getDuration())
.append("fileSize", getFileSize())
.append("viewCount", getViewCount())
.append("status", getStatus())
.append("auditStatus", getAuditStatus())
.append("auditOpinion", getAuditOpinion())
.append("auditUserId", getAuditUserId())
.append("auditTime", getAuditTime())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.toString();
}
}
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("userId", getUserId())
.append("title", getTitle())
.append("description", getDescription())
.append("videoUrl", getVideoUrl())
.append("coverImage", getCoverImage())
.append("category", getCategory())
.append("tags", getTags())
.append("duration", getDuration())
.append("fileSize", getFileSize())
.append("viewCount", getViewCount())
.append("status", getStatus())
.append("createTime", getCreateTime())
.append("updateTime", getUpdateTime())
.append("delFlag", getDelFlag())
.append("auditStatus", getAuditStatus())
.append("auditOpinion", getAuditOpinion())
.append("auditUserId", getAuditUserId())
.append("auditTime", getAuditTime())
.append("publisherName", getPublisherName())
.append("publisherAvatar", getPublisherAvatar())
.append("publishTime", getPublishTime())
.toString();
}
}

98
chenhai-system/src/main/java/com/chenhai/vet/mapper/VetTrainingVideoMapper.java

@ -1,49 +1,69 @@
package com.chenhai.vet.mapper;
import com.chenhai.vet.domain.VetTrainingVideo;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
import com.chenhai.vet.domain.VetTrainingVideo;
public interface VetTrainingVideoMapper {
int insertVideo(VetTrainingVideo video);
List<VetTrainingVideo> selectMyVideos(@Param("userId") Long userId,
@Param("title") String title,
@Param("category") String category,
@Param("status") String status,
@Param("auditStatus") String auditStatus);
List<VetTrainingVideo> selectPublicVideos(@Param("title") String title,
@Param("category") String category,
@Param("userName") String userName);
VetTrainingVideo selectVideoById(Long id);
void incrementViewCount(Long id);
List<VetTrainingVideo> selectHotVideos(@Param("limit") Integer limit);
/**
* 兽医培训视频Mapper接口
*
* @author ruoyi
* @date 2026-01-28
*/
public interface VetTrainingVideoMapper
{
/**
* 查询兽医培训视频
*
* @param id 兽医培训视频主键
* @return 兽医培训视频
*/
public VetTrainingVideo selectVetTrainingVideoById(Long id);
List<VetTrainingVideo> searchVideos(@Param("keyword") String keyword);
/**
* 查询兽医培训视频列表
*
* @param vetTrainingVideo 兽医培训视频
* @return 兽医培训视频集合
*/
public List<VetTrainingVideo> selectVetTrainingVideoList(VetTrainingVideo vetTrainingVideo);
int deleteVideoById(@Param("id") Long id);
/**
* 新增兽医培训视频
*
* @param vetTrainingVideo 兽医培训视频
* @return 结果
*/
public int insertVetTrainingVideo(VetTrainingVideo vetTrainingVideo);
// 更新审核状态
int updateAuditStatus(@Param("id") Long id,
@Param("auditStatus") String auditStatus,
@Param("auditOpinion") String auditOpinion,
@Param("auditUserId") Long auditUserId,
@Param("auditTime") Date auditTime);
/**
* 修改兽医培训视频
*
* @param vetTrainingVideo 兽医培训视频
* @return 结果
*/
public int updateVetTrainingVideo(VetTrainingVideo vetTrainingVideo);
// 更新视频状态公开/私有
int updateStatus(@Param("id") Long id,
@Param("status") String status);
/**
* 删除兽医培训视频
*
* @param id 兽医培训视频主键
* @return 结果
*/
public int deleteVetTrainingVideoById(Long id);
// 获取待审核视频列表
List<VetTrainingVideo> selectPendingAuditVideos(@Param("title") String title,
@Param("userName") String userName);
/**
* 批量删除兽医培训视频
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteVetTrainingVideoByIds(Long[] ids);
// 更新视频信息
int updateVideoInfo(VetTrainingVideo video);
}
/**
* 查询公开视频列表已上架且已审核通过
*
* @param vetTrainingVideo 兽医培训视频
* @return 兽医培训视频集合
*/
public List<VetTrainingVideo> selectPublicVideoList(VetTrainingVideo vetTrainingVideo);
}

132
chenhai-system/src/main/java/com/chenhai/vet/service/IVetTrainingVideoService.java

@ -1,92 +1,140 @@
package com.chenhai.vet.service;
import com.chenhai.vet.domain.VetTrainingVideo;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import com.chenhai.vet.domain.VetTrainingVideo;
public interface IVetTrainingVideoService {
/**
* 兽医培训视频Service接口
*
* @author ruoyi
* @date 2026-01-28
*/
public interface IVetTrainingVideoService
{
/**
* 上传并保存视频自动设为待审核
* 查询兽医培训视频
*
* @param id 兽医培训视频主键
* @return 兽医培训视频
*/
String uploadAndSave(VetTrainingVideo video, MultipartFile videoFile, MultipartFile coverImage);
public VetTrainingVideo selectVetTrainingVideoById(Long id);
/**
* 获取我的视频列表
* 查询兽医培训视频列表
*
* @param vetTrainingVideo 兽医培训视频
* @return 兽医培训视频集合
*/
List<VetTrainingVideo> getMyVideos(Long userId, String title, String category, String status, String auditStatus);
public List<VetTrainingVideo> selectVetTrainingVideoList(VetTrainingVideo vetTrainingVideo);
/**
* 获取公开视频列表
* 新增兽医培训视频
*
* @param vetTrainingVideo 兽医培训视频
* @return 结果
*/
List<VetTrainingVideo> getPublicVideos(String title, String category, String vetName);
public int insertVetTrainingVideo(VetTrainingVideo vetTrainingVideo);
/**
* 获取视频详情带权限校验
* 修改兽医培训视频
*
* @param vetTrainingVideo 兽医培训视频
* @return 结果
*/
VetTrainingVideo getVideoDetail(Long videoId, Long currentVetId);
public int updateVetTrainingVideo(VetTrainingVideo vetTrainingVideo);
/**
* 获取视频播放地址带权限校验
* 批量删除兽医培训视频
*
* @param ids 需要删除的兽医培训视频主键集合
* @return 结果
*/
String getVideoPlayUrl(Long videoId, Long currentVetId);
public int deleteVetTrainingVideoByIds(Long[] ids);
/**
* 增加观看次数
* 删除兽医培训视频信息
*
* @param id 兽医培训视频主键
* @return 结果
*/
void incrementViewCount(Long videoId);
public int deleteVetTrainingVideoById(Long id);
// 批量操作
int batchSubmitForAudit(List<Long> videoIds, Long userId);
int batchAuditVideo(List<Long> videoIds, String auditStatus, String auditOpinion, Long auditUserId);
int batchPublishVideo(List<Long> videoIds, Long userId);
int batchOfflineVideo(List<Long> videoIds, Long userId);
/**
* 获取热门视频
* 审核单个视频
* @param videoId 视频ID
* @param auditStatus 审核状态
* @param auditOpinion 审核意见
* @param auditUserId 审核用户ID
* @return 是否成功
*/
List<VetTrainingVideo> getHotVideos(Integer limit);
boolean auditVideo(Long videoId, String auditStatus, String auditOpinion, Long auditUserId);
/**
* 搜索视频
* 上架单个视频
* @param videoId 视频ID
* @param userId 用户ID
* @return 是否成功
*/
List<VetTrainingVideo> searchVideos(String keyword);
boolean publishVideo(Long videoId, Long userId);
int deleteVideoById(Long videoId);
/**
* 下架单个视频
* @param videoId 视频ID
* @param userId 用户ID
* @return 是否成功
*/
boolean offlineVideo(Long videoId, Long userId);
/**
* 提交审核用户手动提交
* 提交审核单个视频
* @param videoId 视频ID
* @param userId 用户ID
* @return 是否成功
*/
boolean submitForAudit(Long videoId, Long userId);
/**
* 取消审核用户主动取消
* 取消审核单个视频
* @param videoId 视频ID
* @param userId 用户ID
* @return 是否成功
*/
boolean cancelAudit(Long videoId, Long userId);
/**
* 重新提交审核审核拒绝后
* 重新提交审核单个视频
* @param videoId 视频ID
* @param userId 用户ID
* @return 是否成功
*/
boolean resubmitForAudit(Long videoId, Long userId);
/**
* 审核视频管理员
* 修复缺少用户ID的视频记录
* @return 修复的记录数
*/
boolean auditVideo(Long videoId, String auditStatus, String auditOpinion, Long auditUserId);
int fixMissingUserIds();
/**
* 上架视频审核通过后
* 查询公开视频列表已上架且已审核通过
*
* @param vetTrainingVideo 查询条件
* @return 公开视频集合
*/
boolean publishVideo(Long videoId, Long userId);
public List<VetTrainingVideo> selectPublicVideoList(VetTrainingVideo vetTrainingVideo);
/**
* 下架视频
* 分页查询公开视频列表已上架且已审核通过
*
* @param vetTrainingVideo 查询条件
* @return 公开视频集合
*/
boolean offlineVideo(Long videoId, Long userId);
public List<VetTrainingVideo> selectPublicVideoPageList(VetTrainingVideo vetTrainingVideo);
/**
* 获取待审核视频列表管理员
*/
List<VetTrainingVideo> getPendingAuditVideos(String title, String userName);
/**
* 编辑视频信息
*/
boolean updateVideoInfo(Long videoId, VetTrainingVideo video, Long userId);
}
}

3
chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetProductServiceImpl.java

@ -1,15 +1,12 @@
package com.chenhai.vet.service.impl;
import java.util.Date;
import java.util.List;
import com.chenhai.vet.domain.VetTrainingVideo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.chenhai.vet.mapper.VetProductMapper;
import com.chenhai.vet.domain.VetProduct;
import com.chenhai.vet.service.IVetProductService;
import org.springframework.transaction.annotation.Transactional;
/**
* 兽医产品信息Service业务层处理

620
chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetTrainingVideoServiceImpl.java

@ -1,335 +1,499 @@
package com.chenhai.vet.service.impl;
import com.chenhai.vet.domain.VetTrainingVideo;
import com.chenhai.vet.mapper.VetTrainingVideoMapper;
import com.chenhai.vet.service.IVetTrainingVideoService;
import java.util.List;
import com.chenhai.common.utils.SecurityUtils;
import org.slf4j.Logger;
import com.chenhai.common.utils.DateUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.chenhai.vet.mapper.VetTrainingVideoMapper;
import com.chenhai.vet.domain.VetTrainingVideo;
import com.chenhai.vet.service.IVetTrainingVideoService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
import java.util.UUID;
/**
* 兽医培训视频Service业务层处理
*
* @author ruoyi
* @date 2026-01-28
*/
@Service
public class VetTrainingVideoServiceImpl implements IVetTrainingVideoService {
public class VetTrainingVideoServiceImpl implements IVetTrainingVideoService
{
// 添加 logger
private static final Logger logger = LoggerFactory.getLogger(VetTrainingVideoServiceImpl.class);
@Autowired
private VetTrainingVideoMapper videoMapper;
@Value("${file.upload.path:/uploads}")
private String uploadPath;
private VetTrainingVideoMapper vetTrainingVideoMapper;
/**
* 查询兽医培训视频
*
* @param id 兽医培训视频主键
* @return 兽医培训视频
*/
@Override
@Transactional
public String uploadAndSave(VetTrainingVideo video, MultipartFile videoFile, MultipartFile coverImage) {
try {
// 1. 创建上传目录
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
// 2. 生成唯一文件名
String originalFileName = videoFile.getOriginalFilename();
String fileExtension = getFileExtension(originalFileName);
String uniqueFileName = UUID.randomUUID().toString() + "." + fileExtension;
// 3. 保存视频文件
Path videoPath = Paths.get(uploadPath, uniqueFileName);
Files.write(videoPath, videoFile.getBytes());
// 4. 保存封面图如果有
String coverImageUrl = null;
if (coverImage != null && !coverImage.isEmpty()) {
String coverExtension = getFileExtension(coverImage.getOriginalFilename());
String coverFileName = "cover_" + UUID.randomUUID().toString() + "." + coverExtension;
Path coverPath = Paths.get(uploadPath, coverFileName);
Files.write(coverPath, coverImage.getBytes());
coverImageUrl = "/uploads/" + coverFileName;
}
// 5. 计算视频时长和大小
int duration = getVideoDuration(videoFile);
long fileSize = videoFile.getSize();
// 6. 设置初始状态私有 + 待审核
video.setStatus("0"); // 0-未上架/私有
video.setAuditStatus("0"); // 0-待审核
video.setVideoUrl("/uploads/" + uniqueFileName);
video.setCoverImage(coverImageUrl);
video.setDuration(duration);
video.setFileSize(fileSize);
video.setViewCount(0);
video.setCreateTime(new Date());
video.setUpdateTime(new Date());
videoMapper.insertVideo(video);
return "上传成功!视频已自动提交审核,请耐心等待管理员审核。";
} catch (IOException e) {
throw new RuntimeException("文件保存失败", e);
}
public VetTrainingVideo selectVetTrainingVideoById(Long id)
{
return vetTrainingVideoMapper.selectVetTrainingVideoById(id);
}
/**
* 查询兽医培训视频列表
*
* @param vetTrainingVideo 兽医培训视频
* @return 兽医培训视频
*/
@Override
public List<VetTrainingVideo> getMyVideos(Long userId, String title, String category, String status, String auditStatus) {
return videoMapper.selectMyVideos(userId, title, category, status, auditStatus);
public List<VetTrainingVideo> selectVetTrainingVideoList(VetTrainingVideo vetTrainingVideo)
{
return vetTrainingVideoMapper.selectVetTrainingVideoList(vetTrainingVideo);
}
/**
* 新增兽医培训视频
*
* @param vetTrainingVideo 兽医培训视频
* @return 结果
*/
@Override
public List<VetTrainingVideo> getPublicVideos(String title, String category, String vetName) {
// 只显示审核通过且公开的视频
return videoMapper.selectPublicVideos(title, category, vetName);
}
@Override
public VetTrainingVideo getVideoDetail(Long videoId, Long currentVetId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null) {
return null;
public int insertVetTrainingVideo(VetTrainingVideo vetTrainingVideo)
{
// 强制设置当前用户ID确保不为空
Long userId = SecurityUtils.getUserId();
if (userId == null) {
throw new RuntimeException("用户未登录");
}
// 权限校验只能查看自己的视频或审核通过公开的视频
boolean canView = ("2".equals(video.getAuditStatus()) && // 2-审核通过新编码
"1".equals(video.getStatus())) || // 1-已上架/公开
currentVetId.equals(video.getUserId());
return canView ? video : null;
vetTrainingVideo.setUserId(userId); // 确保设置用户ID
vetTrainingVideo.setStatus("0"); // 私有
vetTrainingVideo.setAuditStatus("0"); // 待审核
vetTrainingVideo.setDelFlag("0"); // 正常
vetTrainingVideo.setCreateTime(DateUtils.getNowDate());
return vetTrainingVideoMapper.insertVetTrainingVideo(vetTrainingVideo);
}
// 添加一个修复历史数据的方法
@Override
public String getVideoPlayUrl(Long videoId, Long currentVetId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null) {
return null;
@Transactional(rollbackFor = Exception.class)
public int fixMissingUserIds() {
// 查找 user_id null 的视频记录
VetTrainingVideo query = new VetTrainingVideo();
query.setUserId(null); // 查找用户ID为空的记录
List<VetTrainingVideo> videos = vetTrainingVideoMapper.selectVetTrainingVideoList(query);
int fixedCount = 0;
for (VetTrainingVideo video : videos) {
if (video.getUserId() == null) {
// 这里可以根据实际情况设置一个默认用户ID
// 例如设置为系统管理员ID或第一个用户
video.setUserId(1L); // 假设1是系统管理员
video.setUpdateTime(DateUtils.getNowDate());
vetTrainingVideoMapper.updateVetTrainingVideo(video);
fixedCount++;
}
}
// 权限校验只能播放自己的视频或审核通过公开的视频
boolean canPlay = ("2".equals(video.getAuditStatus()) && // 2-审核通过新编码
"1".equals(video.getStatus())) || // 1-已上架/公开
currentVetId.equals(video.getUserId());
return canPlay ? video.getVideoUrl() : null;
return fixedCount;
}
@Override
public void incrementViewCount(Long videoId) {
videoMapper.incrementViewCount(videoId);
}
/**
* 修改兽医培训视频
*
* @param vetTrainingVideo 兽医培训视频
* @return 结果
*/
@Override
public List<VetTrainingVideo> getHotVideos(Integer limit) {
return videoMapper.selectHotVideos(limit);
public int updateVetTrainingVideo(VetTrainingVideo vetTrainingVideo)
{
vetTrainingVideo.setUpdateTime(DateUtils.getNowDate());
return vetTrainingVideoMapper.updateVetTrainingVideo(vetTrainingVideo);
}
/**
* 批量删除兽医培训视频
*
* @param ids 需要删除的兽医培训视频主键
* @return 结果
*/
@Override
public List<VetTrainingVideo> searchVideos(String keyword) {
return videoMapper.searchVideos(keyword);
public int deleteVetTrainingVideoByIds(Long[] ids)
{
return vetTrainingVideoMapper.deleteVetTrainingVideoByIds(ids);
}
/**
* 删除兽医培训视频信息
*
* @param id 兽医培训视频主键
* @return 结果
*/
@Override
@Transactional
public int deleteVideoById(Long videoId) {
return videoMapper.deleteVideoById(videoId);
public int deleteVetTrainingVideoById(Long id)
{
return vetTrainingVideoMapper.deleteVetTrainingVideoById(id);
}
@Override
@Transactional
public boolean submitForAudit(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null || !userId.equals(video.getUserId())) {
return false;
}
// 只能提交无需审核或审核拒绝的视频
if (!"4".equals(video.getAuditStatus()) && // 3-无需审核
!"3".equals(video.getAuditStatus())) { // 2-审核拒绝
return false;
@Transactional(rollbackFor = Exception.class)
public int batchSubmitForAudit(List<Long> videoIds, Long userId) {
int successCount = 0;
for (Long videoId : videoIds) {
try {
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (video == null) {
logger.warn("视频不存在,ID: {}", videoId);
continue;
}
// 验证权限只能操作自己的视频
if (!userId.equals(video.getUserId())) {
logger.warn("用户 {} 无权限操作视频 {}", userId, videoId);
continue;
}
// 状态验证只有审核拒绝或无需审核状态才能重新提交审核
String auditStatus = video.getAuditStatus();
if (!"2".equals(auditStatus) && !"3".equals(auditStatus)) {
logger.warn("视频 {} 当前状态 {} 不能提交审核", videoId, auditStatus);
continue;
}
// 更新为待审核状态
video.setAuditStatus("0"); // 待审核
video.setUpdateTime(DateUtils.getNowDate());
int result = vetTrainingVideoMapper.updateVetTrainingVideo(video);
if (result > 0) {
successCount++;
}
} catch (Exception e) {
logger.error("批量提交审核失败,视频ID: {}", videoId, e);
// 继续处理下一个不中断批量操作
}
}
// 更新为待审核状态
int result = videoMapper.updateAuditStatus(videoId,
"1", // 1-审核中
"已提交审核",
null,
new Date());
return result > 0;
return successCount;
}
@Override
@Transactional
public boolean cancelAudit(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
@Transactional(rollbackFor = Exception.class)
public int batchAuditVideo(List<Long> videoIds, String auditStatus, String auditOpinion, Long auditUserId) {
int successCount = 0;
if (video == null || !userId.equals(video.getUserId())) {
return false;
// 如果是拒绝审核必须填写意见
if ("2".equals(auditStatus) && (auditOpinion == null || auditOpinion.trim().isEmpty())) {
throw new RuntimeException("审核拒绝时必须填写审核意见");
}
if (!"1".equals(video.getAuditStatus())) { // 1-审核中
return false;
for (Long videoId : videoIds) {
try {
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (video == null) {
logger.warn("视频不存在,ID: {}", videoId);
continue;
}
// 状态验证只有待审核状态才能审核
if (!"0".equals(video.getAuditStatus())) {
logger.warn("视频 {} 当前状态 {} 不能审核", videoId, video.getAuditStatus());
continue;
}
// 更新审核信息
video.setAuditStatus(auditStatus);
video.setAuditOpinion(auditOpinion);
video.setAuditUserId(auditUserId);
video.setAuditTime(DateUtils.getNowDate());
video.setUpdateTime(DateUtils.getNowDate());
int result = vetTrainingVideoMapper.updateVetTrainingVideo(video);
if (result > 0) {
successCount++;
}
} catch (Exception e) {
logger.error("批量审核失败,视频ID: {}", videoId, e);
// 继续处理下一个不中断批量操作
}
}
// 更新为无需审核状态
int result = videoMapper.updateAuditStatus(videoId,
"4", // 4-无需审核
"用户取消审核",
null,
new Date());
return result > 0;
return successCount;
}
@Override
@Transactional
public boolean resubmitForAudit(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null || !userId.equals(video.getUserId())) {
return false;
@Transactional(rollbackFor = Exception.class)
public int batchPublishVideo(List<Long> videoIds, Long userId) {
int successCount = 0;
for (Long videoId : videoIds) {
try {
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (video == null) {
logger.warn("视频不存在,ID: {}", videoId);
continue;
}
// 验证权限只能操作自己的视频
if (!userId.equals(video.getUserId())) {
logger.warn("用户 {} 无权限操作视频 {}", userId, videoId);
continue;
}
// 状态验证只有审核通过且未上架的视频才能上架
if (!"2".equals(video.getAuditStatus())) {
logger.warn("视频 {} 未通过审核,当前审核状态: {}", videoId, video.getAuditStatus());
continue;
}
if ("1".equals(video.getStatus())) {
logger.warn("视频 {} 已上架", videoId);
continue;
}
// 更新为上架状态
video.setStatus("1"); // 已上架
video.setUpdateTime(DateUtils.getNowDate());
int result = vetTrainingVideoMapper.updateVetTrainingVideo(video);
if (result > 0) {
successCount++;
}
} catch (Exception e) {
logger.error("批量上架失败,视频ID: {}", videoId, e);
// 继续处理下一个不中断批量操作
}
}
// 只能重新提交审核拒绝状态的视频
if (!"3".equals(video.getAuditStatus())) { // 2-审核拒绝
return false;
return successCount;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int batchOfflineVideo(List<Long> videoIds, Long userId) {
int successCount = 0;
for (Long videoId : videoIds) {
try {
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (video == null) {
logger.warn("视频不存在,ID: {}", videoId);
continue;
}
// 验证权限只能操作自己的视频
if (!userId.equals(video.getUserId())) {
logger.warn("用户 {} 无权限操作视频 {}", userId, videoId);
continue;
}
// 状态验证只有已上架的视频才能下架
if (!"1".equals(video.getStatus())) {
logger.warn("视频 {} 未上架,当前状态: {}", videoId, video.getStatus());
continue;
}
// 更新为下架状态
video.setStatus("0"); // 已下架
video.setUpdateTime(DateUtils.getNowDate());
int result = vetTrainingVideoMapper.updateVetTrainingVideo(video);
if (result > 0) {
successCount++;
}
} catch (Exception e) {
logger.error("批量下架失败,视频ID: {}", videoId, e);
// 继续处理下一个不中断批量操作
}
}
// 更新为待审核状态
int result = videoMapper.updateAuditStatus(videoId,
"0", // 0-待审核
"重新提交审核",
null,
new Date());
return result > 0;
return successCount;
}
@Override
@Transactional
public boolean auditVideo(Long videoId, String auditStatus, String auditOpinion, Long auditUserId) {
// 验证审核状态
if (!"2".equals(auditStatus) && // 2-审核通过
!"3".equals(auditStatus)) { // 3-审核拒绝
return false;
}
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null) {
return false;
throw new RuntimeException("视频不存在");
}
if (!"1".equals(video.getAuditStatus())) { // 1-审核中
return false;
// 只有待审核状态才能审核
if (!"0".equals(video.getAuditStatus())) {
throw new RuntimeException("当前状态不能审核");
}
int result = videoMapper.updateAuditStatus(videoId,
auditStatus,
auditOpinion,
auditUserId,
new Date());
// 如果审核通过自动设置为私有状态等待用户上架
if (result > 0 && "2".equals(auditStatus)) { // 1-审核通过
videoMapper.updateStatus(videoId, "0"); // 0-未上架/私有
// 审核拒绝时必须填写审核意见
if ("2".equals(auditStatus) && (auditOpinion == null || auditOpinion.trim().isEmpty())) {
throw new RuntimeException("审核拒绝时必须填写审核意见");
}
return result > 0;
// 更新审核信息
video.setAuditStatus(auditStatus);
video.setAuditOpinion(auditOpinion);
video.setAuditUserId(auditUserId);
video.setAuditTime(DateUtils.getNowDate());
video.setUpdateTime(DateUtils.getNowDate());
return vetTrainingVideoMapper.updateVetTrainingVideo(video) > 0;
}
@Override
@Transactional
public boolean publishVideo(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (video == null) {
return false;
throw new RuntimeException("视频不存在");
}
// 权限校验只能操作自己的视频
if (!userId.equals(video.getUserId())) {
return false;
// // 验证权限只能操作自己的视频
// if (!userId.equals(video.getUserId())) {
// throw new RuntimeException("无权操作此视频");
// }
// 状态验证只有审核通过且未上架的视频才能上架
if (!"1".equals(video.getAuditStatus())) {
throw new RuntimeException("只有审核通过的视频才能上架");
}
// 只能上架审核通过的视频
if (!"2".equals(video.getAuditStatus())) { // 1-审核通过
return false;
if ("1".equals(video.getStatus())) {
throw new RuntimeException("视频已上架");
}
return videoMapper.updateStatus(videoId, "1") > 0; // 1-已上架/公开
// 更新为上架状态
video.setStatus("1"); // 已上架
video.setUpdateTime(DateUtils.getNowDate());
return vetTrainingVideoMapper.updateVetTrainingVideo(video) > 0;
}
@Override
@Transactional
public boolean offlineVideo(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (video == null) {
return false;
throw new RuntimeException("视频不存在");
}
// 权限校验只能操作自己的视频
// 验证权限只能操作自己的视频
if (!userId.equals(video.getUserId())) {
return false;
throw new RuntimeException("无权操作此视频");
}
// 只能下架已上架的视频
if (!"1".equals(video.getStatus())) { // 1-已上架/公开
return false;
// 状态验证只有已上架的视频才能下架
if (!"1".equals(video.getStatus())) {
throw new RuntimeException("只有已上架的视频才能下架");
}
return videoMapper.updateStatus(videoId, "0") > 0; // 0-未上架/私有
// 更新为下架状态
video.setStatus("0"); // 已下架
video.setUpdateTime(DateUtils.getNowDate());
return vetTrainingVideoMapper.updateVetTrainingVideo(video) > 0;
}
@Override
public List<VetTrainingVideo> getPendingAuditVideos(String title, String userName) {
return videoMapper.selectPendingAuditVideos(title, userName);
public boolean submitForAudit(Long videoId, Long userId) {
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (video == null) {
throw new RuntimeException("视频不存在");
}
// ============ 注意这里移除了权限检查 ============
// 原来的权限检查代码删除了
// if (!userId.equals(video.getUserId())) {
// throw new RuntimeException("无权操作此视频");
// }
// =================================================
// 状态验证只有审核拒绝或无需审核状态才能提交审核
String auditStatus = video.getAuditStatus();
if (!"2".equals(auditStatus) && !"3".equals(auditStatus)) {
throw new RuntimeException("当前状态不能提交审核");
}
// 更新为待审核状态
video.setAuditStatus("0"); // 待审核
video.setUpdateTime(DateUtils.getNowDate());
return vetTrainingVideoMapper.updateVetTrainingVideo(video) > 0;
}
@Override
@Transactional
public boolean updateVideoInfo(Long videoId, VetTrainingVideo video, Long userId) {
VetTrainingVideo existingVideo = videoMapper.selectVideoById(videoId);
@Transactional(rollbackFor = Exception.class)
public boolean cancelAudit(Long videoId, Long userId) {
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (existingVideo == null || !userId.equals(existingVideo.getUserId())) {
return false;
if (video == null) {
throw new RuntimeException("视频不存在");
}
// 只能编辑无需审核审核拒绝或私有状态的视频
boolean canEdit = "4".equals(existingVideo.getAuditStatus()) || // 4-无需审核
"3".equals(existingVideo.getAuditStatus()) || // 3-审核拒绝
"0".equals(existingVideo.getStatus()); // 0-未上架/私有
if (!canEdit) {
return false;
// 状态验证只有待审核状态才能取消审核
if (!"0".equals(video.getAuditStatus())) {
throw new RuntimeException("只有待审核状态才能取消审核");
}
// 更新视频信息
existingVideo.setTitle(video.getTitle());
existingVideo.setDescription(video.getDescription());
existingVideo.setCategory(video.getCategory());
existingVideo.setUpdateTime(new Date());
// 更新为无需审核状态
video.setAuditStatus("3"); // 无需审核
video.setUpdateTime(DateUtils.getNowDate());
return videoMapper.updateVideoInfo(existingVideo) > 0;
return vetTrainingVideoMapper.updateVetTrainingVideo(video) > 0;
}
private String getFileExtension(String fileName) {
if (fileName == null || fileName.lastIndexOf(".") == -1) {
return "mp4";
}
return fileName.substring(fileName.lastIndexOf(".") + 1);
/**
* 检查用户是否是管理员
*/
private boolean checkUserIsAdmin(Long userId) {
// 这里需要根据你的权限系统来实现
// 例如查询用户角色
// 暂时返回false你需要根据实际情况实现
return false;
}
private int getVideoDuration(MultipartFile videoFile) {
// 这里需要实现获取视频时长的方法
return 60; // 默认60秒
@Override
public boolean resubmitForAudit(Long videoId, Long userId) {
// 重新提交审核和提交审核逻辑相同
return submitForAudit(videoId, userId);
}
/**
* 查询公开视频列表已上架且已审核通过
*/
@Override
public List<VetTrainingVideo> selectPublicVideoList(VetTrainingVideo vetTrainingVideo) {
// 设置强制条件已上架且审核通过
vetTrainingVideo.setStatus("1"); // 已上架
vetTrainingVideo.setAuditStatus("1"); // 审核通过
vetTrainingVideo.setDelFlag("0"); // 未删除
return vetTrainingVideoMapper.selectPublicVideoList(vetTrainingVideo);
}
/**
* 分页查询公开视频列表已上架且已审核通过
*/
@Override
public List<VetTrainingVideo> selectPublicVideoPageList(VetTrainingVideo vetTrainingVideo) {
// 设置强制条件已上架且审核通过
vetTrainingVideo.setStatus("1"); // 已上架
vetTrainingVideo.setAuditStatus("1"); // 审核通过
vetTrainingVideo.setDelFlag("0"); // 未删除
return vetTrainingVideoMapper.selectVetTrainingVideoList(vetTrainingVideo);
}
}
}

112
chenhai-system/src/main/resources/mapper/system/SysAnswersMapper.xml

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chenhai.system.mapper.SysAnswersMapper">
<resultMap type="SysAnswers" id="SysAnswersResult">
<result property="id" column="id" />
<result property="questionId" column="question_id" />
<result property="userId" column="user_id" />
<result property="username" column="username" />
<result property="nickName" column="nick_name" />
<result property="avatar" column="avatar" />
<result property="content" column="content" />
<result property="createdAt" column="created_at" />
<result property="updatedAt" column="updated_at" />
</resultMap>
<sql id="selectSysAnswersVo">
select
a.id,
a.question_id,
a.user_id,
a.username,
u.nick_name,
u.avatar,
a.content,
a.created_at,
a.updated_at
from sys_answers a
left join sys_user u on a.user_id = u.user_id
</sql>
<select id="selectSysAnswersList" parameterType="SysAnswers" resultMap="SysAnswersResult">
<include refid="selectSysAnswersVo"/>
<where>
<if test="questionId != null "> and a.question_id = #{questionId}</if>
<if test="userId != null and userId != ''"> and a.user_id = #{userId}</if>
<if test="username != null and username != ''"> and a.username like concat('%', #{username}, '%')</if>
<if test="nickName != null and nickName != ''"> and u.nick_name like concat('%', #{nickName}, '%')</if>
<if test="content != null and content != ''"> and a.content like concat('%', #{content}, '%')</if>
<if test="createdAt != null "> and date(a.created_at) = date(#{createdAt})</if>
<if test="params.beginTime != null and params.beginTime != ''">
and date_format(a.created_at,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''">
and date_format(a.created_at,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
</if>
</where>
order by a.created_at desc
</select>
<select id="selectSysAnswersById" parameterType="Long" resultMap="SysAnswersResult">
<include refid="selectSysAnswersVo"/>
where a.id = #{id}
</select>
<insert id="insertSysAnswers" parameterType="SysAnswers" useGeneratedKeys="true" keyProperty="id">
insert into sys_answers
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="questionId != null">question_id,</if>
<if test="userId != null and userId != ''">user_id,</if>
<if test="username != null and username != ''">username,</if>
<if test="nickName != null and nickName != ''">nick_name,</if>
<if test="avatar != null and avatar != ''">avatar,</if>
<if test="content != null and content != ''">content,</if>
created_at,
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="questionId != null">#{questionId},</if>
<if test="userId != null and userId != ''">#{userId},</if>
<if test="username != null and username != ''">#{username},</if>
<if test="nickName != null and nickName != ''">#{nickName},</if>
<if test="avatar != null and avatar != ''">#{avatar},</if>
<if test="content != null and content != ''">#{content},</if>
sysdate(),
</trim>
</insert>
<update id="updateSysAnswers" parameterType="SysAnswers">
update sys_answers
<trim prefix="SET" suffixOverrides=",">
<if test="questionId != null">question_id = #{questionId},</if>
<if test="userId != null and userId != ''">user_id = #{userId},</if>
<if test="username != null and username != ''">username = #{username},</if>
<if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>
<if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
<if test="content != null and content != ''">content = #{content},</if>
updated_at = sysdate(),
</trim>
where id = #{id}
</update>
<delete id="deleteSysAnswersById" parameterType="Long">
delete from sys_answers where id = #{id}
</delete>
<delete id="deleteSysAnswersByIds" parameterType="String">
delete from sys_answers where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<delete id="deleteAnswersByQuestionId" parameterType="Long">
delete from sys_answers where question_id = #{questionId}
</delete>
<select id="countAnswersByQuestionId" parameterType="Long" resultType="Long">
select count(*) from sys_answers where question_id = #{questionId}
</select>
</mapper>

19
chenhai-system/src/main/resources/mapper/system/SysPolicyInterpretationMapper.xml

@ -37,6 +37,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="publishStatus != null and publishStatus != ''"> and publish_status = #{publishStatus}</if>
<if test="removalReason != null and removalReason != ''"> and removal_reason = #{removalReason}</if>
<if test="policyCategory != null and policyCategory != ''"> and policy_category = #{policyCategory}</if>
<!-- 添加搜索条件 -->
<if test="searchKey != null and searchKey != ''">
and (
title like concat('%', #{searchKey}, '%')
or content like concat('%', #{searchKey}, '%')
or issuing_agency like concat('%', #{searchKey}, '%')
or remark like concat('%', #{searchKey}, '%')
)
</if>
</where>
</select>
@ -169,6 +178,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="issuingAgency != null and issuingAgency != ''"> and issuing_agency like concat('%', #{issuingAgency}, '%')</if>
<if test="content != null and content != ''"> and content like concat('%', #{content}, '%')</if>
<if test="policyCategory != null and policyCategory != ''"> and policy_category = #{policyCategory}</if>
<!-- 搜索条件 -->
<if test="searchKey != null and searchKey != ''">
and (
title like concat('%', #{searchKey}, '%')
or content like concat('%', #{searchKey}, '%')
or issuing_agency like concat('%', #{searchKey}, '%')
or remark like concat('%', #{searchKey}, '%')
or policy_category like concat('%', #{searchKey}, '%')
)
</if>
</where>
</select>
</mapper>

143
chenhai-system/src/main/resources/mapper/system/SysQuestionsMapper.xml

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chenhai.system.mapper.SysQuestionsMapper">
<resultMap type="SysQuestions" id="SysQuestionsResult">
<result property="id" column="id" />
<result property="userId" column="user_id" />
<result property="username" column="username" />
<result property="title" column="title" />
<result property="content" column="content" />
<result property="tags" column="tags" />
<result property="answerCount" column="answer_count" />
<result property="createdAt" column="created_at" />
<result property="updatedAt" column="updated_at" />
<result property="nickName" column="nick_name" />
<result property="avatar" column="avatar" />
</resultMap>
<sql id="selectSysQuestionsVo">
SELECT
q.id,
q.user_id,
u.user_name as username,
u.nick_name,
u.avatar,
q.title,
q.content,
q.tags,
q.answer_count,
q.created_at,
q.updated_at
FROM sys_questions q
LEFT JOIN sys_user u ON q.user_id = u.user_id
</sql>
<select id="selectSysQuestionsList" parameterType="SysQuestions" resultMap="SysQuestionsResult">
<include refid="selectSysQuestionsVo"/>
<where>
<if test="userId != null and userId != ''"> and user_id = #{userId}</if>
<if test="username != null and username != ''"> and username like concat('%', #{username}, '%')</if>
<if test="title != null and title != ''"> and title like concat('%', #{title}, '%')</if>
<if test="content != null and content != ''"> and content like concat('%', #{content}, '%')</if>
<if test="tags != null and tags != ''"> and tags like concat('%', #{tags}, '%')</if>
<if test="answerCount != null "> and answer_count = #{answerCount}</if>
<if test="createdAt != null "> and created_at = #{createdAt}</if>
<if test="updatedAt != null "> and updated_at = #{updatedAt}</if>
<if test="nickName != null and nickName != ''"> and u.nick_name like concat('%', #{nickName}, '%')</if>
</where>
order by q.created_at desc
</select>
<select id="selectSysQuestionsById" parameterType="Long" resultMap="SysQuestionsResult">
<include refid="selectSysQuestionsVo"/>
where q.id = #{id}
</select>
<insert id="insertSysQuestions" parameterType="SysQuestions" useGeneratedKeys="true" keyProperty="id">
insert into sys_questions
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null and userId != ''">user_id,</if>
<if test="username != null and username != ''">username,</if>
<if test="title != null and title != ''">title,</if>
<if test="content != null and content != ''">content,</if>
<if test="tags != null and tags != ''">tags,</if>
<if test="answerCount != null">answer_count,</if>
<if test="createdAt != null">created_at,</if>
<if test="updatedAt != null">updated_at,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null and userId != ''">#{userId},</if>
<if test="username != null and username != ''">#{username},</if>
<if test="title != null and title != ''">#{title},</if>
<if test="content != null and content != ''">#{content},</if>
<if test="tags != null and tags != ''">#{tags},</if>
<if test="answerCount != null">#{answerCount},</if>
<if test="createdAt != null">#{createdAt},</if>
<if test="updatedAt != null">#{updatedAt},</if>
</trim>
</insert>
<update id="updateSysQuestions" parameterType="SysQuestions">
update sys_questions
<trim prefix="SET" suffixOverrides=",">
<if test="userId != null and userId != ''">user_id = #{userId},</if>
<if test="username != null and username != ''">username = #{username},</if>
<if test="title != null and title != ''">title = #{title},</if>
<if test="content != null and content != ''">content = #{content},</if>
<if test="tags != null and tags != ''">tags = #{tags},</if>
<if test="answerCount != null">answer_count = #{answerCount},</if>
<if test="createdAt != null">created_at = #{createdAt},</if>
<if test="updatedAt != null">updated_at = #{updatedAt},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteSysQuestionsById" parameterType="Long">
delete from sys_questions where id = #{id}
</delete>
<delete id="deleteSysQuestionsByIds" parameterType="String">
delete from sys_questions where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<!-- 增加答案数量 -->
<update id="increaseAnswerCount" parameterType="Long">
update sys_questions
set answer_count = answer_count + 1,
updated_at = sysdate()
where id = #{questionId}
</update>
<!-- 减少答案数量 -->
<update id="decreaseAnswerCount" parameterType="Long">
update sys_questions
set answer_count = case
when answer_count > 0 then answer_count - 1
else 0
end,
updated_at = sysdate()
where id = #{questionId}
</update>
<!-- 从数据库实时统计答案数量 -->
<update id="updateAnswerCountFromDb" parameterType="Long">
update sys_questions q
set q.answer_count = (
select count(*) from sys_answers a
where a.question_id = q.id and a.deleted = 0
),
q.updated_at = sysdate()
where q.id = #{questionId}
</update>
<!-- 获取答案数量 -->
<select id="getAnswerCount" parameterType="Long" resultType="Long">
select answer_count from sys_questions where id = #{questionId}
</select>
</mapper>

9
chenhai-system/src/main/resources/mapper/vet/VetKnowledgeMapper.xml

@ -75,6 +75,15 @@
<if test="expertId != null and expertId != ''"> and expert_id = #{expertId}</if>
<if test="coverImage != null and coverImage != ''"> and cover_image = #{coverImage}</if>
<if test="subtitle != null and subtitle != ''"> and subtitle like concat('%', #{subtitle}, '%')</if>
<!-- 添加搜索条件 -->
<if test="searchKey != null and searchKey != ''">
and (
vk.title like concat('%', #{searchKey}, '%')
or vk.content like concat('%', #{searchKey}, '%')
or vk.subtitle like concat('%', #{searchKey}, '%')
or ve.real_name like concat('%', #{searchKey}, '%')
)
</if>
</where>
</select>

370
chenhai-system/src/main/resources/mapper/vet/VetTrainingVideoMapper.xml

@ -1,160 +1,244 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chenhai.vet.mapper.VetTrainingVideoMapper">
<resultMap id="VideoResult" type="VetTrainingVideo">
<id property="id" column="id"/>
<result property="userId" column="user_id"/>
<result property="title" column="title"/>
<result property="description" column="description"/>
<result property="videoUrl" column="video_url"/>
<result property="coverImage" column="cover_image"/>
<result property="category" column="category"/>
<result property="tags" column="tags"/>
<result property="duration" column="duration"/>
<result property="fileSize" column="file_size"/>
<result property="viewCount" column="view_count"/>
<result property="status" column="status"/>
<result property="auditStatus" column="audit_status"/>
<result property="auditOpinion" column="audit_opinion"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="userName" column="user_name"/>
<result property="auditUserId" column="audit_user_id"/>
<result property="auditTime" column="audit_time"/>
<result property="userAvatar" column="user_avatar"/>
<resultMap type="VetTrainingVideo" id="VetTrainingVideoResult">
<result property="id" column="id" />
<result property="userId" column="user_id" />
<result property="title" column="title" />
<result property="description" column="description" />
<result property="videoUrl" column="video_url" />
<result property="coverImage" column="cover_image" />
<result property="category" column="category" />
<result property="tags" column="tags" />
<result property="duration" column="duration" />
<result property="fileSize" column="file_size" />
<result property="viewCount" column="view_count" />
<result property="status" column="status" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<result property="delFlag" column="del_flag" />
<result property="auditStatus" column="audit_status" />
<result property="auditOpinion" column="audit_opinion" />
<result property="auditUserId" column="audit_user_id" />
<result property="auditTime" column="audit_time" />
<result property="publisherName" column="publisher_name" />
<result property="publisherAvatar" column="publisher_avatar" />
<result property="publishTime" column="publish_time" />
</resultMap>
<insert id="insertVideo" parameterType="VetTrainingVideo" useGeneratedKeys="true" keyProperty="id">
INSERT INTO vet_training_video (
user_id, title, description, video_url, cover_image,
category, tags, duration, file_size, view_count,
status, audit_status, audit_opinion,
create_time, update_time
) VALUES (
#{userId}, #{title}, #{description}, #{videoUrl}, #{coverImage},
#{category}, #{tags}, #{duration}, #{fileSize}, #{viewCount},
#{status}, #{auditStatus}, #{auditOpinion},
#{createTime}, #{updateTime}
)
</insert>
<select id="selectMyVideos" resultMap="VideoResult">
SELECT v.*, u.nick_name as user_name,u.avatar as user_avatar
<sql id="selectVetTrainingVideoVo">
SELECT
v.id,
v.user_id,
v.title,
v.description,
v.video_url,
v.cover_image,
v.category,
v.tags,
v.duration,
v.file_size,
v.view_count,
v.status,
v.create_time,
v.update_time,
v.del_flag,
v.audit_status,
v.audit_opinion,
v.audit_user_id,
v.audit_time,
v.publish_time,
u.nick_name as publisher_name, <!-- 使用用户的昵称作为发布者姓名 -->
u.avatar as publisher_avatar
FROM vet_training_video v
LEFT JOIN sys_user u ON v.user_id = u.user_id
WHERE v.user_id = #{userId}
<if test="title != null and title != ''">
AND v.title LIKE CONCAT('%', #{title}, '%')
</if>
<if test="category != null and category != ''">
AND v.category = #{category}
</if>
<if test="status != null and status != ''">
AND v.status = #{status}
</if>
<if test="auditStatus != null and auditStatus != ''">
AND v.audit_status = #{auditStatus}
</if>
ORDER BY v.create_time DESC
</select>
</sql>
<select id="selectPublicVideos" resultMap="VideoResult">
SELECT v.*, u.nick_name as user_name,u.avatar as user_avatar
FROM vet_training_video v
LEFT JOIN sys_user u ON v.user_id = u.user_id
WHERE v.status = '1'
AND v.audit_status = '1'
<if test="title != null and title != ''">
AND v.title LIKE CONCAT('%', #{title}, '%')
</if>
<if test="category != null and category != ''">
AND v.category = #{category}
</if>
<if test="userName != null and userName != ''">
AND u.nick_name LIKE CONCAT('%', #{userName}, '%')
</if>
ORDER BY v.create_time DESC
</select>
<select id="selectVideoById" resultMap="VideoResult">
SELECT v.*, u.nick_name as user_name,u.avatar as user_avatar
FROM vet_training_video v
LEFT JOIN sys_user u ON v.user_id = u.user_id
WHERE v.id = #{id}
<select id="selectVetTrainingVideoList" parameterType="VetTrainingVideo" resultMap="VetTrainingVideoResult">
<include refid="selectVetTrainingVideoVo"/>
<where>
<!-- 使用表别名 v. 来明确指定字段来源 -->
<if test="userId != null "> and v.user_id = #{userId}</if>
<if test="title != null and title != ''">
<bind name="titleLike" value="'%' + title + '%'" />
and v.title like #{titleLike}
</if>
<if test="description != null and description != ''">
<bind name="descriptionLike" value="'%' + description + '%'" />
and v.description like #{descriptionLike}
</if>
<if test="videoUrl != null and videoUrl != ''"> and v.video_url = #{videoUrl}</if>
<if test="coverImage != null and coverImage != ''"> and v.cover_image = #{coverImage}</if>
<if test="category != null and category != ''"> and v.category = #{category}</if>
<if test="tags != null and tags != ''">
<bind name="tagsLike" value="'%' + tags + '%'" />
and v.tags like #{tagsLike}
</if>
<if test="duration != null "> and v.duration = #{duration}</if>
<if test="fileSize != null "> and v.file_size = #{fileSize}</if>
<if test="viewCount != null "> and v.view_count = #{viewCount}</if>
<if test="status != null and status != ''"> and v.status = #{status}</if>
<if test="delFlag != null and delFlag != ''"> and v.del_flag = #{delFlag}</if>
<if test="auditStatus != null and auditStatus != ''"> and v.audit_status = #{auditStatus}</if>
<if test="auditOpinion != null and auditOpinion != ''">
<bind name="opinionLike" value="'%' + auditOpinion + '%'" />
and v.audit_opinion like #{opinionLike}
</if>
<if test="auditUserId != null "> and v.audit_user_id = #{auditUserId}</if>
<if test="auditTime != null "> and v.audit_time = #{auditTime}</if>
<if test="publisherName != null and publisherName != ''">
<bind name="publisherLike" value="'%' + publisherName + '%'" />
and u.nick_name like #{publisherLike}
</if>
<!-- 搜索关键词:同时搜索标题、描述、标签、发布者姓名 -->
<if test="searchKey != null and searchKey != ''">
<bind name="searchKeyLike" value="'%' + searchKey + '%'" />
and (
v.title like #{searchKeyLike}
or v.description like #{searchKeyLike}
or v.tags like #{searchKeyLike}
or v.category like #{searchKeyLike}
or u.nick_name like #{searchKeyLike}
)
</if>
<if test="publishTime != null "> and v.publish_time = #{publishTime}</if>
</where>
</select>
<update id="incrementViewCount">
UPDATE vet_training_video
SET view_count = view_count + 1
WHERE id = #{id}
</update>
<select id="selectHotVideos" resultMap="VideoResult">
SELECT v.*, u.nick_name as user_name,u.avatar as user_avatar
FROM vet_training_video v
LEFT JOIN sys_user u ON v.user_id = u.user_id
WHERE v.status = '1'
AND v.audit_status = '1'
ORDER BY v.view_count DESC
LIMIT #{limit}
<select id="selectVetTrainingVideoById" parameterType="Long" resultMap="VetTrainingVideoResult">
<include refid="selectVetTrainingVideoVo"/>
where id = #{id}
</select>
<select id="searchVideos" resultMap="VideoResult">
SELECT v.*, u.nick_name as user_name,u.avatar as user_avatar
FROM vet_training_video v
LEFT JOIN sys_user u ON v.user_id = u.user_id
WHERE v.status = '1'
AND v.audit_status = '1'
AND (v.title LIKE CONCAT('%', #{keyword}, '%')
OR v.description LIKE CONCAT('%', #{keyword}, '%')
OR v.tags LIKE CONCAT('%', #{keyword}, '%')
OR u.nick_name LIKE CONCAT('%', #{keyword}, '%'))
ORDER BY v.create_time DESC
</select>
<insert id="insertVetTrainingVideo" parameterType="VetTrainingVideo" useGeneratedKeys="true" keyProperty="id">
insert into vet_training_video
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">user_id,</if>
<if test="title != null and title != ''">title,</if>
<if test="description != null">description,</if>
<if test="videoUrl != null and videoUrl != ''">video_url,</if>
<if test="coverImage != null">cover_image,</if>
<if test="category != null">category,</if>
<if test="tags != null">tags,</if>
<if test="duration != null">duration,</if>
<if test="fileSize != null">file_size,</if>
<if test="viewCount != null">view_count,</if>
<if test="status != null">status,</if>
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
<if test="delFlag != null">del_flag,</if>
<if test="auditStatus != null">audit_status,</if>
<if test="auditOpinion != null">audit_opinion,</if>
<if test="auditUserId != null">audit_user_id,</if>
<if test="auditTime != null">audit_time,</if>
<if test="publishTime != null">publish_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
<if test="title != null and title != ''">#{title},</if>
<if test="description != null">#{description},</if>
<if test="videoUrl != null and videoUrl != ''">#{videoUrl},</if>
<if test="coverImage != null">#{coverImage},</if>
<if test="category != null">#{category},</if>
<if test="tags != null">#{tags},</if>
<if test="duration != null">#{duration},</if>
<if test="fileSize != null">#{fileSize},</if>
<if test="viewCount != null">#{viewCount},</if>
<if test="status != null">#{status},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="delFlag != null">#{delFlag},</if>
<if test="auditStatus != null">#{auditStatus},</if>
<if test="auditOpinion != null">#{auditOpinion},</if>
<if test="auditUserId != null">#{auditUserId},</if>
<if test="auditTime != null">#{auditTime},</if>
<if test="publishTime != null">#{publishTime},</if>
</trim>
</insert>
<delete id="deleteVideoById">
DELETE FROM vet_training_video
WHERE id = #{id}
<update id="updateVetTrainingVideo" parameterType="VetTrainingVideo">
update vet_training_video
<trim prefix="SET" suffixOverrides=",">
<if test="userId != null">user_id = #{userId},</if>
<if test="title != null and title != ''">title = #{title},</if>
<if test="description != null">description = #{description},</if>
<if test="videoUrl != null and videoUrl != ''">video_url = #{videoUrl},</if>
<if test="coverImage != null">cover_image = #{coverImage},</if>
<if test="category != null">category = #{category},</if>
<if test="tags != null">tags = #{tags},</if>
<if test="duration != null">duration = #{duration},</if>
<if test="fileSize != null">file_size = #{fileSize},</if>
<if test="viewCount != null">view_count = #{viewCount},</if>
<if test="status != null">status = #{status},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="delFlag != null">del_flag = #{delFlag},</if>
<if test="auditStatus != null">audit_status = #{auditStatus},</if>
<if test="auditOpinion != null">audit_opinion = #{auditOpinion},</if>
<if test="auditUserId != null">audit_user_id = #{auditUserId},</if>
<if test="auditTime != null">audit_time = #{auditTime},</if>
<if test="publishTime != null">publish_time = #{publishTime},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteVetTrainingVideoById" parameterType="Long">
delete from vet_training_video where id = #{id}
</delete>
<update id="updateAuditStatus">
UPDATE vet_training_video
SET audit_status = #{auditStatus},
audit_opinion = #{auditOpinion},
audit_user_id = #{auditUserId},
audit_time = #{auditTime},
update_time = NOW()
WHERE id = #{id}
</update>
<delete id="deleteVetTrainingVideoByIds" parameterType="String">
delete from vet_training_video where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<update id="updateStatus">
UPDATE vet_training_video
SET status = #{status},
update_time = NOW()
WHERE id = #{id}
</update>
<!-- 查询公开视频列表(已上架且已审核通过) -->
<select id="selectPublicVideoList" parameterType="VetTrainingVideo" resultMap="VetTrainingVideoResult">
<include refid="selectVetTrainingVideoVo"/>
<where>
<!-- 强制条件:已上架、审核通过、未删除 -->
and v.status = '1'
and v.audit_status = '1'
and v.del_flag = '0'
<select id="selectPendingAuditVideos" resultMap="VideoResult">
SELECT v.*, u.nick_name as user_name,u.avatar as user_avatar
FROM vet_training_video v
LEFT JOIN sys_user u ON v.user_id = u.user_id
WHERE v.audit_status = '0'
<if test="title != null and title != ''">
AND v.title LIKE CONCAT('%', #{title}, '%')
</if>
<if test="userName != null and userName != ''">
AND u.nick_name LIKE CONCAT('%', #{userName}, '%')
</if>
ORDER BY v.create_time DESC
<!-- 可选查询条件 -->
<if test="userId != null "> and v.user_id = #{userId}</if>
<if test="title != null and title != ''">
<bind name="titleLike" value="'%' + title + '%'" />
and v.title like #{titleLike}
</if>
<if test="description != null and description != ''">
<bind name="descriptionLike" value="'%' + description + '%'" />
and v.description like #{descriptionLike}
</if>
<if test="category != null and category != ''"> and v.category = #{category}</if>
<if test="tags != null and tags != ''">
<bind name="tagsLike" value="'%' + tags + '%'" />
and v.tags like #{tagsLike}
</if>
<if test="publisherName != null and publisherName != ''">
<bind name="publisherLike" value="'%' + publisherName + '%'" />
and u.nick_name like #{publisherLike}
</if>
<!-- 搜索关键词:同时搜索标题、描述、标签、发布者姓名 -->
<if test="searchKey != null and searchKey != ''">
<bind name="searchKeyLike" value="'%' + searchKey + '%'" />
and (
v.title like #{searchKeyLike}
or v.description like #{searchKeyLike}
or v.tags like #{searchKeyLike}
or v.category like #{searchKeyLike}
or u.nick_name like #{searchKeyLike}
)
</if>
<if test="publishTime != null "> and v.publish_time = #{publishTime}</if>
</where>
<!-- 默认按创建时间倒序排列 -->
order by v.create_time desc
</select>
<update id="updateVideoInfo" parameterType="VetTrainingVideo">
UPDATE vet_training_video
SET title = #{title},
description = #{description},
category = #{category},
update_time = NOW()
WHERE id = #{id}
</update>
</mapper>

44
chenhai-ui/src/api/system/answers.js

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询答复列表
export function listAnswers(query) {
return request({
url: '/system/answers/list',
method: 'get',
params: query
})
}
// 查询答复详细
export function getAnswers(id) {
return request({
url: '/system/answers/' + id,
method: 'get'
})
}
// 新增答复
export function addAnswers(data) {
return request({
url: '/system/answers',
method: 'post',
data: data
})
}
// 修改答复
export function updateAnswers(data) {
return request({
url: '/system/answers',
method: 'put',
data: data
})
}
// 删除答复
export function delAnswers(id) {
return request({
url: '/system/answers/' + id,
method: 'delete'
})
}

44
chenhai-ui/src/api/system/questions.js

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询问答列表
export function listQuestions(query) {
return request({
url: '/system/questions/list',
method: 'get',
params: query
})
}
// 查询问答详细
export function getQuestions(id) {
return request({
url: '/system/questions/' + id,
method: 'get'
})
}
// 新增问答
export function addQuestions(data) {
return request({
url: '/system/questions',
method: 'post',
data: data
})
}
// 修改问答
export function updateQuestions(data) {
return request({
url: '/system/questions',
method: 'put',
data: data
})
}
// 删除问答
export function delQuestions(id) {
return request({
url: '/system/questions/' + id,
method: 'delete'
})
}

269
chenhai-ui/src/api/vet/training.js

@ -1,125 +1,154 @@
// trainingApi.js - 完整修正版
import request from '@/utils/request'
export default {
// 上传视频
uploadVideo(data) {
return request({
url: '/vet/training/upload',
method: 'post',
data: data,
headers: {
'Content-Type': 'multipart/form-data'
},
timeout: 300000
})
},
// 获取公开视频列表
getPublicVideos(params) {
return request({
url: '/vet/training/public-videos',
method: 'get',
params: params
})
},
// 获取我的视频
getMyVideos(params) {
return request({
url: '/vet/training/my-videos',
method: 'get',
params: params
})
},
// 获取视频详情
getVideoDetail(id) {
return request({
url: `/vet/training/video/${id}`,
method: 'get'
})
},
// 提交审核(用户提交给管理员)
submitForAudit(videoId) {
return request({
url: `/vet/training/submit-audit/${videoId}`,
method: 'post'
})
},
// 取消审核
cancelAudit(videoId) {
return request({
url: `/vet/training/cancel-audit/${videoId}`,
method: 'post'
})
},
// 重新提交审核
resubmitAudit(videoId) {
return request({
url: `/vet/training/resubmit-audit/${videoId}`,
method: 'post'
})
},
// 审核视频(管理员审核)
auditVideo(videoId, auditStatus, auditOpinion = '') {
// 改用JSON方式传递参数
const data = {
auditStatus: auditStatus,
auditOpinion: auditOpinion || ''
// 查询兽医培训视频列表
export function listTraining(query) {
return request({
url: '/vet/training/list',
method: 'get',
params: query
})
}
// 查询兽医培训视频详细
export function getTraining(id) {
return request({
url: '/vet/training/' + id,
method: 'get'
})
}
// 新增兽医培训视频
export function addTraining(data) {
return request({
url: '/vet/training',
method: 'post',
data: data
})
}
// 修改兽医培训视频
export function updateTraining(data) {
return request({
url: '/vet/training',
method: 'put',
data: data
})
}
// 删除兽医培训视频
export function delTraining(id) {
return request({
url: '/vet/training/' + id,
method: 'delete'
})
}
// 上传视频文件
export function uploadVideo(data) {
return request({
url: '/vet/training/uploadVideo',
method: 'post',
data: data,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
// ============ 已有的其他接口(如果存在的话) ============
// 提交审核
export function submitForAudit(id) {
return request({
url: `/vet/training/submit-audit/${id}`,
method: 'post'
})
}
// 取消审核
export function cancelAudit(id) {
return request({
url: `/vet/training/cancel-audit/${id}`,
method: 'post'
})
}
// 重新提交审核
export function resubmitForAudit(id) {
return request({
url: `/vet/training/resubmit-audit/${id}`,
method: 'post'
})
}
// 审核视频 - 修改为接收一个对象参数
export function auditVideo(data) {
return request({
url: `/vet/training/audit/${data.id}`,
method: 'post',
data: {
auditStatus: data.auditStatus,
auditOpinion: data.auditOpinion
}
})
}
// 上架视频
export function publishVideo(id) {
return request({
url: `/vet/training/publish/${id}`,
method: 'post'
})
}
// 下架视频
export function offlineVideo(id) {
return request({
url: `/vet/training/offline/${id}`,
method: 'post'
})
}
// 获取我的视频
export function getMyVideos(query) {
return request({
url: '/vet/training/list/my', // 改为这个路径
method: 'get',
params: query
})
}
// 获取公开视频
export function getPublicVideos(query) {
return request({
url: '/vet/training/list/public', // 改为这个路径
method: 'get',
params: query
})
}
// 获取待审核视频
export function getPendingAuditVideos(query) {
return request({
url: '/vet/training/pending-audit',
method: 'get',
params: query
})
}
// 获取视频详情
export function getVideoDetail(videoId) {
return request({
url: `/vet/training/video/${videoId}`,
method: 'get'
})
}
return request({
url: `/vet/training/audit/${videoId}`,
method: 'post',
data: data,
headers: {
'Content-Type': 'application/json'
}
})
},
// 上架视频
publishVideo(videoId) {
return request({
url: `/vet/training/publish/${videoId}`,
method: 'post'
})
},
// 下架视频
offlineVideo(videoId) {
return request({
url: `/vet/training/offline/${videoId}`,
method: 'post'
})
},
// 获取待审核视频列表
getPendingAuditVideos(params) {
return request({
url: '/vet/training/pending-audit',
method: 'get',
params: params
})
},
// 更新视频信息
updateVideoInfo(videoId, data) {
return request({
url: `/vet/training/update/${videoId}`,
method: 'put',
data: data
})
},
// 删除视频
deleteVideo(videoId) {
return request({
url: `/vet/training/${videoId}`,
method: 'delete'
})
}
// 更新视频信息
export function updateVideoInfo(videoId, data) {
return request({
url: `/vet/training/update/${videoId}`,
method: 'put',
data: data
})
}

252
chenhai-ui/src/views/system/answers/index.vue

@ -0,0 +1,252 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['system:answers:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:answers:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:answers:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['system:answers:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="answersList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="对应的问题ID" align="center" prop="questionId" />
<el-table-column label="用户名" align="center" prop="username" />
<el-table-column label="回答内容" align="center" prop="content" />
<el-table-column label="创建时间" align="center" prop="createdAt" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:answers:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:answers:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改答复对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="回答内容">
<editor v-model="form.content" :min-height="192"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listAnswers, getAnswers, delAnswers, addAnswers, updateAnswers } from "@/api/system/answers"
export default {
name: "Answers",
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
answersList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
questionId: null,
userId: null,
username: null,
content: null,
createdAt: null,
updatedAt: null
},
//
form: {},
//
rules: {}
}
},
created() {
this.getList()
},
methods: {
/** 查询答复列表 */
getList() {
this.loading = true
listAnswers(this.queryParams).then(response => {
this.answersList = response.rows
this.total = response.total
this.loading = false
})
},
//
cancel() {
this.open = false
this.reset()
},
//
reset() {
this.form = {
id: null,
questionId: null,
userId: null,
username: null,
content: null,
createdAt: null,
updatedAt: null
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.handleQuery()
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = "添加答复"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const id = row.id || this.ids
getAnswers(id).then(response => {
this.form = response.data
this.open = true
this.title = "修改答复"
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateAnswers(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
addAnswers(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
})
}
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids
this.$modal.confirm('是否确认删除答复编号为"' + ids + '"的数据项?').then(function() {
return delAnswers(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('system/answers/export', {
...this.queryParams
}, `answers_${new Date().getTime()}.xlsx`)
}
}
}
</script>

584
chenhai-ui/src/views/system/questions/index.vue

@ -0,0 +1,584 @@
<template>
<div class="app-container">
<!-- 搜索区域 -->
<div class="search-area">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="用户ID" prop="userId">
<el-input
v-model="queryParams.userId"
placeholder="请输入用户ID"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input
v-model="queryParams.username"
placeholder="请输入用户名"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="问题标题" prop="title">
<el-input
v-model="queryParams.title"
placeholder="请输入问题标题"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="标签" prop="tags">
<el-input
v-model="queryParams.tags"
placeholder="请输入标签"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createdAt">
<el-date-picker
v-model="queryParams.createdAt"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
>
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 操作按钮区域 -->
<div class="operation-area">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['system:questions:add']"
>新增问题</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:questions:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:questions:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-chat-dot-round"
size="mini"
:disabled="single"
@click="viewAnswers"
v-hasPermi="['system:answers:view']"
>查看答案</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['system:questions:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</div>
<!-- 主内容区域 -->
<div class="main-content" :class="{ 'has-answers': showAnswersPanel }">
<!-- 左侧问题列表 -->
<div class="questions-list" :style="{ width: showAnswersPanel ? '50%' : '100%' }">
<el-table
v-loading="loading"
:data="questionsList"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
highlight-current-row
style="width: 100%"
height="calc(100vh - 260px)"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="用户名" align="center" prop="username" width="120" />
<el-table-column label="问题标题" align="left" prop="title" min-width="150">
<template slot-scope="scope">
<el-tooltip :content="scope.row.title" placement="top">
<span class="title-text">{{ scope.row.title }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="内容" align="left" prop="content" min-width="200">
<template slot-scope="scope">
<div class="content-preview" v-html="scope.row.content"></div>
</template>
</el-table-column>
<el-table-column label="标签" align="center" prop="tags" width="120">
<template slot-scope="scope">
<el-tag v-if="scope.row.tags" size="mini">{{ scope.row.tags }}</el-tag>
</template>
</el-table-column>
<el-table-column label="回答数" align="center" prop="answerCount" width="100"/>
<el-table-column label="创建时间" align="center" prop="createdAt" width="160" />
<el-table-column label="操作" align="center" width="180" fixed="right">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:questions:edit']"
>编辑</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-chat-dot-round"
@click="toggleAnswersPanel(scope.row)"
v-hasPermi="['system:answers:view']"
>答案</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:questions:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
<!-- 右侧答案管理面板 -->
<div v-if="showAnswersPanel" class="answers-panel">
<div class="panel-header">
<div class="panel-title">
<span>{{ currentQuestionTitle }}</span>
<span class="answer-count">({{ currentAnswerCount }}个回答)</span>
</div>
<div class="panel-actions">
<el-button size="mini" icon="el-icon-refresh" @click="refreshAnswers">刷新</el-button>
<el-button size="mini" icon="el-icon-close" @click="closeAnswersPanel">关闭</el-button>
</div>
</div>
<div class="panel-content">
<AnswersManagement
ref="answersManagement"
:question-id="currentQuestionId"
:key="currentQuestionId"
@refresh="handleAnswersRefresh"
:table-height="'calc(100vh - 320px)'"
/>
</div>
</div>
</div>
<!-- 添加或修改问答对话框 -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="问题标题" prop="title">
<el-input v-model="form.title" placeholder="请输入问题标题" />
</el-form-item>
<el-form-item label="问题内容" prop="content">
<editor v-model="form.content" :min-height="250" />
</el-form-item>
<el-form-item label="标签" prop="tags">
<el-input v-model="form.tags" placeholder="请输入标签,多个用逗号分隔" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listQuestions, getQuestions, delQuestions, addQuestions, updateQuestions } from "@/api/system/questions"
import AnswersManagement from '../answers/index' // Answers
export default {
name: "Questions",
components: {
AnswersManagement
},
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
questionsList: [],
//
title: "",
//
open: false,
//
showAnswersPanel: false, //
currentQuestionId: null, // ID
currentQuestionTitle: '', //
currentAnswerCount: 0, //
selectedRow: null, //
//
queryParams: {
pageNum: 1,
pageSize: 20,
userId: null,
username: null,
title: null,
content: null,
tags: null,
answerCount: null,
createdAt: null,
updatedAt: null
},
//
form: {},
//
rules: {
userId: [
{ required: true, message: "用户ID不能为空", trigger: "blur" }
],
username: [
{ required: true, message: "用户名不能为空", trigger: "blur" }
],
title: [
{ required: true, message: "问题标题不能为空", trigger: "blur" }
],
content: [
{ required: true, message: "问题内容不能为空", trigger: "blur" }
]
}
}
},
created() {
this.getList()
},
methods: {
/** 查询问答列表 */
getList() {
this.loading = true
listQuestions(this.queryParams).then(response => {
this.questionsList = response.rows
this.total = response.total
this.loading = false
}).catch(() => {
this.loading = false
})
},
//
cancel() {
this.open = false
this.reset()
},
//
reset() {
this.form = {
id: null,
userId: null,
username: null,
title: null,
content: null,
tags: null,
answerCount: 0,
createdAt: null,
updatedAt: null
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.handleQuery()
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
//
handleRowClick(row) {
this.selectedRow = row
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = "添加问答"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const id = row.id || this.ids[0]
getQuestions(id).then(response => {
this.form = response.data
this.open = true
this.title = "修改问答"
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateQuestions(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
addQuestions(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
})
}
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids
this.$modal.confirm('是否确认删除问答编号为"' + ids + '"的数据项?').then(() => {
return delQuestions(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
//
if (this.currentQuestionId && ids.includes(this.currentQuestionId)) {
this.closeAnswersPanel()
}
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('system/questions/export', {
...this.queryParams
}, `questions_${new Date().getTime()}.xlsx`)
},
/** 查看选中问题的答案 */
viewAnswers() {
if (this.single) {
this.$modal.msgWarning('请选择一条问题记录')
return
}
const questionId = this.ids[0]
const selectedQuestion = this.questionsList.find(item => item.id === questionId)
if (selectedQuestion) {
this.toggleAnswersPanel(selectedQuestion)
}
},
/** 切换答案面板 */
toggleAnswersPanel(row) {
if (this.showAnswersPanel && this.currentQuestionId === row.id) {
this.closeAnswersPanel()
return
}
this.currentQuestionId = row.id
this.currentQuestionTitle = row.title
this.currentAnswerCount = row.answerCount || 0
this.selectedRow = row
this.showAnswersPanel = true
},
/** 关闭答案面板 */
closeAnswersPanel() {
this.showAnswersPanel = false
this.currentQuestionId = null
this.currentQuestionTitle = ''
this.currentAnswerCount = 0
this.selectedRow = null
},
/** 刷新答案数据 */
refreshAnswers() {
if (this.$refs.answersManagement) {
this.$refs.answersManagement.refresh()
}
},
/** 答案管理组件刷新后的回调 */
handleAnswersRefresh() {
//
if (this.currentQuestionId) {
//
const index = this.questionsList.findIndex(item => item.id === this.currentQuestionId)
if (index !== -1) {
// API
this.questionsList[index].answerCount += 1
this.currentAnswerCount = this.questionsList[index].answerCount
}
this.$forceUpdate()
}
}
}
}
</script>
<style scoped>
.app-container {
height: 100%;
display: flex;
flex-direction: column;
}
.search-area {
padding: 15px;
background: #fff;
border-radius: 4px;
margin-bottom: 10px;
}
.operation-area {
padding: 10px 15px;
background: #fff;
border-radius: 4px;
margin-bottom: 10px;
}
.main-content {
flex: 1;
display: flex;
gap: 10px;
min-height: 0;
}
.questions-list {
transition: width 0.3s;
background: #fff;
padding: 15px;
border-radius: 4px;
display: flex;
flex-direction: column;
}
.answers-panel {
width: 50%;
background: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
border-left: 1px solid #ebeef5;
}
.panel-header {
padding: 15px;
border-bottom: 1px solid #ebeef5;
display: flex;
justify-content: space-between;
align-items: center;
background: #f8f9fa;
}
.panel-title {
font-size: 16px;
font-weight: bold;
color: #303133;
}
.answer-count {
font-size: 14px;
color: #909399;
margin-left: 8px;
}
.panel-content {
flex: 1;
overflow: hidden;
padding: 15px;
}
.title-text {
display: inline-block;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.content-preview {
max-height: 60px;
overflow: hidden;
line-height: 20px;
word-break: break-all;
}
.item ::v-deep .el-badge__content {
transform: translateY(-50%) translateX(100%);
}
::v-deep .el-form-item {
margin-bottom: 18px;
}
::v-deep .el-table .current-row {
background-color: #f5f7fa !important;
}
</style>

1547
chenhai-ui/src/views/vet/training/TrainingHome.vue
File diff suppressed because it is too large
View File

1120
chenhai-ui/src/views/vet/training/index.vue
File diff suppressed because it is too large
View File

Loading…
Cancel
Save