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. 403
      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. 131
      chenhai-system/src/main/java/com/chenhai/vet/domain/VetTrainingVideo.java
  15. 96
      chenhai-system/src/main/java/com/chenhai/vet/mapper/VetTrainingVideoMapper.java
  16. 130
      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. 580
      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. 348
      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. 179
      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);
}
}

403
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);
}
/**
* 导出兽医培训视频列表
*/
@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, "兽医培训视频数据");
}
/**
* 查看我上传的视频
* 获取兽医培训视频详细信息
*/
@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:query') or @ss.hasRole('muhu')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(vetTrainingVideoService.selectVetTrainingVideoById(id));
}
startPage();
Long userId = getCurrentUserId();
List<VetTrainingVideo> list = trainingVideoService.getMyVideos(userId, title, category, status, auditStatus);
return getDataTable(list);
/**
* 新增兽医培训视频
*/
@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));
}
/**
* 查看所有公开的视频
* 修改兽医培训视频
*/
@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:edit') or @ss.hasRole('muhu')")
@Log(title = "兽医培训视频", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody VetTrainingVideo vetTrainingVideo)
{
return toAjax(vetTrainingVideoService.updateVetTrainingVideo(vetTrainingVideo));
}
startPage();
List<VetTrainingVideo> list = trainingVideoService.getPublicVideos(title, category, userName);
return getDataTable(list);
/**
* 删除兽医培训视频
*/
@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));
}
/**
* 查看视频详情
* 上传培训视频
*/
@GetMapping("/video/{videoId}")
public AjaxResult getVideoDetail(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
VetTrainingVideo video = trainingVideoService.getVideoDetail(videoId, userId);
@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("上传文件不能为空");
}
if (video == null) {
return error("视频不存在或无权限查看");
String fileName = file.getOriginalFilename();
if (StringUtils.isEmpty(fileName)) {
return AjaxResult.error("文件名不能为空");
}
// 如果是审核通过且公开的视频增加观看次数
if ("2".equals(video.getAuditStatus()) && // 2-审核通过
"1".equals(video.getStatus())) { // 1-已上架/公开
trainingVideoService.incrementViewCount(videoId);
video.setViewCount(video.getViewCount() + 1);
// 验证文件类型
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;
}
}
return success(video);
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());
}
}
/**
* 提交审核手动提交
* 获取文件扩展名
*/
@PostMapping("/submit-audit/{videoId}")
public AjaxResult submitForAudit(@PathVariable Long videoId) {
private String getFileExtension(String fileName) {
if (StringUtils.isNotEmpty(fileName) && fileName.contains(".")) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
return "";
}
/**
* 批量提交审核
*/
@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);
// 调用单个审核的 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);
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);
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("视频不可访问");
}
return toAjax(trainingVideoService.deleteVideoById(videoId));
// 增加观看次数
if (video.getViewCount() == null) {
video.setViewCount(0L);
}
video.setViewCount(video.getViewCount() + 1);
vetTrainingVideoService.updateVetTrainingVideo(video);
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();
}
}

131
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,17 +77,31 @@ 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;
/** 搜索关键词(非数据库字段,用于多字段模糊搜索) */
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String searchKey;
/** 用户名称(非数据库字段) */
private String userName;
private String userAvatar;
/** 发布时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "发布时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date publishTime;
public void setId(Long id)
{
@ -164,12 +183,12 @@ public class VetTrainingVideo extends BaseEntity
return tags;
}
public void setDuration(Integer duration)
public void setDuration(Long duration)
{
this.duration = duration;
}
public Integer getDuration()
public Long getDuration()
{
return duration;
}
@ -184,12 +203,12 @@ public class VetTrainingVideo extends BaseEntity
return fileSize;
}
public void setViewCount(Integer viewCount)
public void setViewCount(Long viewCount)
{
this.viewCount = viewCount;
}
public Integer getViewCount()
public Long getViewCount()
{
return viewCount;
}
@ -204,6 +223,16 @@ public class VetTrainingVideo extends BaseEntity
return status;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public String getDelFlag()
{
return delFlag;
}
public void setAuditStatus(String auditStatus)
{
this.auditStatus = auditStatus;
@ -244,33 +273,43 @@ public class VetTrainingVideo extends BaseEntity
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)
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("userId", getUserId())
.append("title", getTitle())
@ -283,14 +322,16 @@ public class VetTrainingVideo extends BaseEntity
.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("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("publisherName", getPublisherName())
.append("publisherAvatar", getPublisherAvatar())
.append("publishTime", getPublishTime())
.toString();
}
}

96
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);
}

130
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);
/**
* 审核视频管理员
*/
boolean auditVideo(Long videoId, String auditStatus, String auditOpinion, Long auditUserId);
/**
* 上架视频审核通过后
* 修复缺少用户ID的视频记录
* @return 修复的记录数
*/
boolean publishVideo(Long videoId, Long userId);
int fixMissingUserIds();
/**
* 下架视频
* 查询公开视频列表已上架且已审核通过
*
* @param vetTrainingVideo 查询条件
* @return 公开视频集合
*/
boolean offlineVideo(Long videoId, Long userId);
public List<VetTrainingVideo> selectPublicVideoList(VetTrainingVideo vetTrainingVideo);
/**
* 获取待审核视频列表管理员
* 分页查询公开视频列表已上架且已审核通过
*
* @param vetTrainingVideo 查询条件
* @return 公开视频集合
*/
List<VetTrainingVideo> getPendingAuditVideos(String title, String userName);
public List<VetTrainingVideo> selectPublicVideoPageList(VetTrainingVideo vetTrainingVideo);
/**
* 编辑视频信息
*/
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业务层处理

580
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();
public VetTrainingVideo selectVetTrainingVideoById(Long id)
{
return vetTrainingVideoMapper.selectVetTrainingVideoById(id);
}
// 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());
/**
* 查询兽医培训视频列表
*
* @param vetTrainingVideo 兽医培训视频
* @return 兽医培训视频
*/
@Override
public List<VetTrainingVideo> selectVetTrainingVideoList(VetTrainingVideo vetTrainingVideo)
{
return vetTrainingVideoMapper.selectVetTrainingVideoList(vetTrainingVideo);
}
// 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;
/**
* 新增兽医培训视频
*
* @param vetTrainingVideo 兽医培训视频
* @return 结果
*/
@Override
public int insertVetTrainingVideo(VetTrainingVideo vetTrainingVideo)
{
// 强制设置当前用户ID确保不为空
Long userId = SecurityUtils.getUserId();
if (userId == null) {
throw new RuntimeException("用户未登录");
}
// 5. 计算视频时长和大小
int duration = getVideoDuration(videoFile);
long fileSize = videoFile.getSize();
vetTrainingVideo.setUserId(userId); // 确保设置用户ID
vetTrainingVideo.setStatus("0"); // 私有
vetTrainingVideo.setAuditStatus("0"); // 待审核
vetTrainingVideo.setDelFlag("0"); // 正常
// 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());
vetTrainingVideo.setCreateTime(DateUtils.getNowDate());
return vetTrainingVideoMapper.insertVetTrainingVideo(vetTrainingVideo);
}
videoMapper.insertVideo(video);
// 添加一个修复历史数据的方法
@Override
@Transactional(rollbackFor = Exception.class)
public int fixMissingUserIds() {
// 查找 user_id null 的视频记录
VetTrainingVideo query = new VetTrainingVideo();
query.setUserId(null); // 查找用户ID为空的记录
return "上传成功!视频已自动提交审核,请耐心等待管理员审核。";
List<VetTrainingVideo> videos = vetTrainingVideoMapper.selectVetTrainingVideoList(query);
int fixedCount = 0;
} catch (IOException e) {
throw new RuntimeException("文件保存失败", e);
for (VetTrainingVideo video : videos) {
if (video.getUserId() == null) {
// 这里可以根据实际情况设置一个默认用户ID
// 例如设置为系统管理员ID或第一个用户
video.setUserId(1L); // 假设1是系统管理员
video.setUpdateTime(DateUtils.getNowDate());
vetTrainingVideoMapper.updateVetTrainingVideo(video);
fixedCount++;
}
}
return fixedCount;
}
/**
* 修改兽医培训视频
*
* @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 int updateVetTrainingVideo(VetTrainingVideo vetTrainingVideo)
{
vetTrainingVideo.setUpdateTime(DateUtils.getNowDate());
return vetTrainingVideoMapper.updateVetTrainingVideo(vetTrainingVideo);
}
/**
* 批量删除兽医培训视频
*
* @param ids 需要删除的兽医培训视频主键
* @return 结果
*/
@Override
public int deleteVetTrainingVideoByIds(Long[] ids)
{
return vetTrainingVideoMapper.deleteVetTrainingVideoByIds(ids);
}
/**
* 删除兽医培训视频信息
*
* @param id 兽医培训视频主键
* @return 结果
*/
@Override
public List<VetTrainingVideo> getPublicVideos(String title, String category, String vetName) {
// 只显示审核通过且公开的视频
return videoMapper.selectPublicVideos(title, category, vetName);
public int deleteVetTrainingVideoById(Long id)
{
return vetTrainingVideoMapper.deleteVetTrainingVideoById(id);
}
@Override
public VetTrainingVideo getVideoDetail(Long videoId, Long currentVetId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
@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) {
return null;
logger.warn("视频不存在,ID: {}", videoId);
continue;
}
// 权限校验只能查看自己的视频或审核通过公开的视频
boolean canView = ("2".equals(video.getAuditStatus()) && // 2-审核通过新编码
"1".equals(video.getStatus())) || // 1-已上架/公开
currentVetId.equals(video.getUserId());
return canView ? video : null;
// 验证权限只能操作自己的视频
if (!userId.equals(video.getUserId())) {
logger.warn("用户 {} 无权限操作视频 {}", userId, videoId);
continue;
}
@Override
public String getVideoPlayUrl(Long videoId, Long currentVetId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
// 状态验证只有审核拒绝或无需审核状态才能重新提交审核
String auditStatus = video.getAuditStatus();
if (!"2".equals(auditStatus) && !"3".equals(auditStatus)) {
logger.warn("视频 {} 当前状态 {} 不能提交审核", videoId, auditStatus);
continue;
}
if (video == null) {
return null;
// 更新为待审核状态
video.setAuditStatus("0"); // 待审核
video.setUpdateTime(DateUtils.getNowDate());
int result = vetTrainingVideoMapper.updateVetTrainingVideo(video);
if (result > 0) {
successCount++;
}
// 权限校验只能播放自己的视频或审核通过公开的视频
boolean canPlay = ("2".equals(video.getAuditStatus()) && // 2-审核通过新编码
"1".equals(video.getStatus())) || // 1-已上架/公开
currentVetId.equals(video.getUserId());
return canPlay ? video.getVideoUrl() : null;
} catch (Exception e) {
logger.error("批量提交审核失败,视频ID: {}", videoId, e);
// 继续处理下一个不中断批量操作
}
}
@Override
public void incrementViewCount(Long videoId) {
videoMapper.incrementViewCount(videoId);
return successCount;
}
@Override
public List<VetTrainingVideo> getHotVideos(Integer limit) {
return videoMapper.selectHotVideos(limit);
@Transactional(rollbackFor = Exception.class)
public int batchAuditVideo(List<Long> videoIds, String auditStatus, String auditOpinion, Long auditUserId) {
int successCount = 0;
// 如果是拒绝审核必须填写意见
if ("2".equals(auditStatus) && (auditOpinion == null || auditOpinion.trim().isEmpty())) {
throw new RuntimeException("审核拒绝时必须填写审核意见");
}
@Override
public List<VetTrainingVideo> searchVideos(String keyword) {
return videoMapper.searchVideos(keyword);
for (Long videoId : videoIds) {
try {
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (video == null) {
logger.warn("视频不存在,ID: {}", videoId);
continue;
}
@Override
@Transactional
public int deleteVideoById(Long videoId) {
return videoMapper.deleteVideoById(videoId);
// 状态验证只有待审核状态才能审核
if (!"0".equals(video.getAuditStatus())) {
logger.warn("视频 {} 当前状态 {} 不能审核", videoId, video.getAuditStatus());
continue;
}
@Override
@Transactional
public boolean submitForAudit(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
// 更新审核信息
video.setAuditStatus(auditStatus);
video.setAuditOpinion(auditOpinion);
video.setAuditUserId(auditUserId);
video.setAuditTime(DateUtils.getNowDate());
video.setUpdateTime(DateUtils.getNowDate());
if (video == null || !userId.equals(video.getUserId())) {
return false;
int result = vetTrainingVideoMapper.updateVetTrainingVideo(video);
if (result > 0) {
successCount++;
}
// 只能提交无需审核或审核拒绝的视频
if (!"4".equals(video.getAuditStatus()) && // 3-无需审核
!"3".equals(video.getAuditStatus())) { // 2-审核拒绝
return false;
} 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 batchPublishVideo(List<Long> videoIds, Long userId) {
int successCount = 0;
if (video == null || !userId.equals(video.getUserId())) {
return false;
for (Long videoId : videoIds) {
try {
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (video == null) {
logger.warn("视频不存在,ID: {}", videoId);
continue;
}
if (!"1".equals(video.getAuditStatus())) { // 1-审核中
return false;
// 验证权限只能操作自己的视频
if (!userId.equals(video.getUserId())) {
logger.warn("用户 {} 无权限操作视频 {}", userId, videoId);
continue;
}
// 更新为无需审核状态
int result = videoMapper.updateAuditStatus(videoId,
"4", // 4-无需审核
"用户取消审核",
null,
new Date());
return result > 0;
// 状态验证只有审核通过且未上架的视频才能上架
if (!"2".equals(video.getAuditStatus())) {
logger.warn("视频 {} 未通过审核,当前审核状态: {}", videoId, video.getAuditStatus());
continue;
}
@Override
@Transactional
public boolean resubmitForAudit(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if ("1".equals(video.getStatus())) {
logger.warn("视频 {} 已上架", videoId);
continue;
}
if (video == null || !userId.equals(video.getUserId())) {
return false;
// 更新为上架状态
video.setStatus("1"); // 已上架
video.setUpdateTime(DateUtils.getNowDate());
int result = vetTrainingVideoMapper.updateVetTrainingVideo(video);
if (result > 0) {
successCount++;
}
// 只能重新提交审核拒绝状态的视频
if (!"3".equals(video.getAuditStatus())) { // 2-审核拒绝
return false;
} 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;
}
@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);
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null) {
return false;
logger.warn("视频不存在,ID: {}", videoId);
continue;
}
if (!"1".equals(video.getAuditStatus())) { // 1-审核中
return false;
// 验证权限只能操作自己的视频
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);
// 继续处理下一个不中断批量操作
}
}
return successCount;
}
@Override
public boolean auditVideo(Long videoId, String auditStatus, String auditOpinion, Long auditUserId) {
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
int result = videoMapper.updateAuditStatus(videoId,
auditStatus,
auditOpinion,
auditUserId,
new Date());
if (video == null) {
throw new RuntimeException("视频不存在");
}
// 如果审核通过自动设置为私有状态等待用户上架
if (result > 0 && "2".equals(auditStatus)) { // 1-审核通过
videoMapper.updateStatus(videoId, "0"); // 0-未上架/私有
// 只有待审核状态才能审核
if (!"0".equals(video.getAuditStatus())) {
throw new RuntimeException("当前状态不能审核");
}
return result > 0;
// 审核拒绝时必须填写审核意见
if ("2".equals(auditStatus) && (auditOpinion == null || auditOpinion.trim().isEmpty())) {
throw new RuntimeException("审核拒绝时必须填写审核意见");
}
// 更新审核信息
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("视频不存在");
}
@Override
@Transactional
public boolean updateVideoInfo(Long videoId, VetTrainingVideo video, Long userId) {
VetTrainingVideo existingVideo = videoMapper.selectVideoById(videoId);
// ============ 注意这里移除了权限检查 ============
// 原来的权限检查代码删除了
// if (!userId.equals(video.getUserId())) {
// throw new RuntimeException("无权操作此视频");
// }
// =================================================
if (existingVideo == null || !userId.equals(existingVideo.getUserId())) {
return false;
// 状态验证只有审核拒绝或无需审核状态才能提交审核
String auditStatus = video.getAuditStatus();
if (!"2".equals(auditStatus) && !"3".equals(auditStatus)) {
throw new RuntimeException("当前状态不能提交审核");
}
// 只能编辑无需审核审核拒绝或私有状态的视频
boolean canEdit = "4".equals(existingVideo.getAuditStatus()) || // 4-无需审核
"3".equals(existingVideo.getAuditStatus()) || // 3-审核拒绝
"0".equals(existingVideo.getStatus()); // 0-未上架/私有
// 更新为待审核状态
video.setAuditStatus("0"); // 待审核
video.setUpdateTime(DateUtils.getNowDate());
if (!canEdit) {
return false;
return vetTrainingVideoMapper.updateVetTrainingVideo(video) > 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean cancelAudit(Long videoId, Long userId) {
VetTrainingVideo video = vetTrainingVideoMapper.selectVetTrainingVideoById(videoId);
if (video == null) {
throw new RuntimeException("视频不存在");
}
// 状态验证只有待审核状态才能取消审核
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";
/**
* 检查用户是否是管理员
*/
private boolean checkUserIsAdmin(Long userId) {
// 这里需要根据你的权限系统来实现
// 例如查询用户角色
// 暂时返回false你需要根据实际情况实现
return false;
}
return fileName.substring(fileName.lastIndexOf(".") + 1);
@Override
public boolean resubmitForAudit(Long videoId, Long userId) {
// 重新提交审核和提交审核逻辑相同
return submitForAudit(videoId, userId);
}
private int getVideoDuration(MultipartFile videoFile) {
// 这里需要实现获取视频时长的方法
return 60; // 默认60秒
/**
* 查询公开视频列表已上架且已审核通过
*/
@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>

348
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}
</sql>
<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 != ''">
AND v.title LIKE CONCAT('%', #{title}, '%')
<bind name="titleLike" value="'%' + title + '%'" />
and v.title like #{titleLike}
</if>
<if test="category != null and category != ''">
AND v.category = #{category}
<if test="description != null and description != ''">
<bind name="descriptionLike" value="'%' + description + '%'" />
and v.description like #{descriptionLike}
</if>
<if test="status != null and status != ''">
AND v.status = #{status}
<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="auditStatus != null and auditStatus != ''">
AND v.audit_status = #{auditStatus}
</if>
ORDER BY v.create_time DESC
</select>
<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 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="category != null and category != ''">
AND v.category = #{category}
<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="userName != null and userName != ''">
AND u.nick_name LIKE CONCAT('%', #{userName}, '%')
<!-- 搜索关键词:同时搜索标题、描述、标签、发布者姓名 -->
<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>
ORDER BY v.create_time DESC
<if test="publishTime != null "> and v.publish_time = #{publishTime}</if>
</where>
</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="selectVetTrainingVideoById" parameterType="Long" resultMap="VetTrainingVideoResult">
<include refid="selectVetTrainingVideoVo"/>
where id = #{id}
</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>
<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>
<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>
<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="deleteVideoById">
DELETE FROM vet_training_video
WHERE id = #{id}
<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="userId != null "> and v.user_id = #{userId}</if>
<if test="title != null and title != ''">
AND v.title LIKE CONCAT('%', #{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="userName != null and userName != ''">
AND u.nick_name LIKE CONCAT('%', #{userName}, '%')
<if test="publisherName != null and publisherName != ''">
<bind name="publisherLike" value="'%' + publisherName + '%'" />
and u.nick_name like #{publisherLike}
</if>
ORDER BY v.create_time DESC
<!-- 搜索关键词:同时搜索标题、描述、标签、发布者姓名 -->
<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'
})
}

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

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