Browse Source

文章发布、视频培训:添加审核状态字段,设计提交审核接口,上架下架功能

master
ChaiNingQi 4 weeks ago
parent
commit
4a73376b73
  1. 56
      chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetKnowledgeController.java
  2. 2
      chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetProductController.java
  3. 152
      chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetTrainingVideoController.java
  4. 46
      chenhai-system/src/main/java/com/chenhai/vet/domain/VetKnowledge.java
  5. 283
      chenhai-system/src/main/java/com/chenhai/vet/domain/VetTrainingVideo.java
  6. 23
      chenhai-system/src/main/java/com/chenhai/vet/mapper/VetTrainingVideoMapper.java
  7. 30
      chenhai-system/src/main/java/com/chenhai/vet/service/IVetKnowledgeService.java
  8. 1
      chenhai-system/src/main/java/com/chenhai/vet/service/IVetProductService.java
  9. 44
      chenhai-system/src/main/java/com/chenhai/vet/service/IVetTrainingVideoService.java
  10. 235
      chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetKnowledgeServiceImpl.java
  11. 4
      chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetProductServiceImpl.java
  12. 223
      chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetTrainingVideoServiceImpl.java
  13. 31
      chenhai-system/src/main/resources/mapper/vet/VetKnowledgeMapper.xml
  14. 2
      chenhai-system/src/main/resources/mapper/vet/VetProductMapper.xml
  15. 62
      chenhai-system/src/main/resources/mapper/vet/VetTrainingVideoMapper.xml
  16. 39
      chenhai-ui/src/api/vet/knowledge.js
  17. 101
      chenhai-ui/src/api/vet/training.js
  18. 585
      chenhai-ui/src/views/vet/knowledge/index.vue
  19. 1771
      chenhai-ui/src/views/vet/product/index.vue
  20. 2090
      chenhai-ui/src/views/vet/training/TrainingHome.vue

56
chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetKnowledgeController.java

@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.chenhai.common.annotation.Log; import com.chenhai.common.annotation.Log;
import com.chenhai.common.core.controller.BaseController; import com.chenhai.common.core.controller.BaseController;
@ -77,7 +78,8 @@ public class VetKnowledgeController extends BaseController
@PostMapping @PostMapping
public AjaxResult add(@RequestBody VetKnowledge vetKnowledge) public AjaxResult add(@RequestBody VetKnowledge vetKnowledge)
{ {
vetKnowledge.setStatus("0");
vetKnowledge.setArticleStatus("0"); // 草稿
vetKnowledge.setAuditStatus("0"); // 待审核
return toAjax(vetKnowledgeService.insertVetKnowledge(vetKnowledge)); return toAjax(vetKnowledgeService.insertVetKnowledge(vetKnowledge));
} }
@ -104,17 +106,32 @@ public class VetKnowledgeController extends BaseController
} }
/** /**
* 上传文章待审核
* 提交审核
*/ */
@PreAuthorize("@ss.hasPermi('vet:knowledge:upload')")
@Log(title = "兽医文章", businessType = BusinessType.INSERT)
@PostMapping("/upload")
public AjaxResult upload(@RequestBody VetKnowledge vetKnowledge) {
return vetKnowledgeService.uploadVetKnowledge(vetKnowledge);
@PreAuthorize("@ss.hasPermi('vet:knowledge:submit')")
@Log(title = "兽医文章", businessType = BusinessType.UPDATE)
@PutMapping("/submit/{id}")
public AjaxResult submitForAudit(@PathVariable Long id) {
return vetKnowledgeService.submitForAudit(id);
} }
/** /**
* 发布文章更新状态
* 审核文章
*/
@PreAuthorize("@ss.hasPermi('vet:knowledge:audit')")
@Log(title = "兽医文章", businessType = BusinessType.UPDATE)
@PutMapping("/audit/{id}")
public AjaxResult audit(@PathVariable Long id,
@RequestParam String auditStatus, // 注意参数名改为auditStatus
@RequestParam(required = false) String auditComment) {
// 验证状态值是否合法
if (!"2".equals(auditStatus) && !"3".equals(auditStatus)) {
return AjaxResult.error("无效的审核状态值");
}
return vetKnowledgeService.auditVetKnowledge(id, auditStatus, auditComment);
}
/**
* 发布文章
*/ */
@PreAuthorize("@ss.hasPermi('vet:knowledge:publish')") @PreAuthorize("@ss.hasPermi('vet:knowledge:publish')")
@Log(title = "兽医文章", businessType = BusinessType.UPDATE) @Log(title = "兽医文章", businessType = BusinessType.UPDATE)
@ -122,4 +139,27 @@ public class VetKnowledgeController extends BaseController
public AjaxResult publish(@PathVariable Long id) { public AjaxResult publish(@PathVariable Long id) {
return vetKnowledgeService.publishVetKnowledge(id); return vetKnowledgeService.publishVetKnowledge(id);
} }
/**
* 上架文章
*/
@PreAuthorize("@ss.hasPermi('vet:knowledge:publish')")
@Log(title = "兽医文章", businessType = BusinessType.UPDATE)
@PutMapping("/online/{id}")
public AjaxResult online(@PathVariable Long id) {
return vetKnowledgeService.onlineVetKnowledge(id);
}
/**
* 下架文章
*/
@PreAuthorize("@ss.hasPermi('vet:knowledge:publish')")
@Log(title = "兽医文章", businessType = BusinessType.UPDATE)
@PutMapping("/offline/{id}")
public AjaxResult offline(@PathVariable Long id, @RequestParam(required = false) String auditComment) {
if (auditComment == null || auditComment.trim().isEmpty()) {
return AjaxResult.error("请输入下架原因");
}
return vetKnowledgeService.offlineVetKnowledge(id, auditComment);
}
} }

2
chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetProductController.java

@ -101,4 +101,6 @@ public class VetProductController extends BaseController
{ {
return toAjax(vetProductService.deleteVetProductByIds(ids)); return toAjax(vetProductService.deleteVetProductByIds(ids));
} }
} }

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

@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.util.List; import java.util.List;
import java.util.Map;
@RestController @RestController
@RequestMapping("/vet/training") @RequestMapping("/vet/training")
@ -31,11 +32,11 @@ public class VetTrainingVideoController extends BaseController {
*/ */
@PostMapping("/upload") @PostMapping("/upload")
public AjaxResult uploadVideo( public AjaxResult uploadVideo(
@RequestParam("title") String title, // 改为 @RequestParam
@RequestParam("videoFile") MultipartFile videoFile, // 文件字段保持 @RequestPart 或改为 @RequestParam
@RequestParam("title") String title,
@RequestParam("videoFile") MultipartFile videoFile,
@RequestParam(value = "description", required = false) String description, @RequestParam(value = "description", required = false) String description,
@RequestParam(value = "category", required = false) String category, @RequestParam(value = "category", required = false) String category,
@RequestParam(value = "status", required = false, defaultValue = "1") String status) {
@RequestParam(value = "coverImage", required = false) MultipartFile coverImage) {
Long userId = getCurrentUserId(); Long userId = getCurrentUserId();
@ -45,9 +46,8 @@ public class VetTrainingVideoController extends BaseController {
video.setTitle(title); video.setTitle(title);
video.setDescription(description); video.setDescription(description);
video.setCategory(category); video.setCategory(category);
video.setStatus(status);
String result = trainingVideoService.uploadAndSave(video, videoFile, null);
String result = trainingVideoService.uploadAndSave(video, videoFile, coverImage);
return success(result); return success(result);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -55,37 +55,6 @@ public class VetTrainingVideoController extends BaseController {
} }
} }
/**
* 批量上传视频可选功能
*/
@PostMapping("/upload/batch")
public AjaxResult uploadBatch(
@RequestParam String[] titles,
@RequestParam MultipartFile[] videoFiles,
@RequestParam(required = false) String[] categories) {
Long userId = getCurrentUserId();
try {
int successCount = 0;
for (int i = 0; i < videoFiles.length; i++) {
if (i < titles.length) {
VetTrainingVideo video = new VetTrainingVideo();
video.setUserId(userId); // 修正变量名
video.setTitle(titles[i]);
video.setCategory(categories != null && i < categories.length ? categories[i] : null);
video.setStatus("1");
trainingVideoService.uploadAndSave(video, videoFiles[i], null);
successCount++;
}
}
return success("成功上传 " + successCount + " 个视频");
} catch (Exception e) {
return error("批量上传失败:" + e.getMessage());
}
}
/** /**
* 查看我上传的视频 * 查看我上传的视频
*/ */
@ -93,11 +62,12 @@ public class VetTrainingVideoController extends BaseController {
public TableDataInfo getMyVideos( public TableDataInfo getMyVideos(
@RequestParam(required = false) String title, @RequestParam(required = false) String title,
@RequestParam(required = false) String category, @RequestParam(required = false) String category,
@RequestParam(required = false) String status) {
@RequestParam(required = false) String status,
@RequestParam(required = false) String auditStatus) {
startPage(); startPage();
Long userId = getCurrentUserId(); // 修正变量名
List<VetTrainingVideo> list = trainingVideoService.getMyVideos(userId, title, category, status);
Long userId = getCurrentUserId();
List<VetTrainingVideo> list = trainingVideoService.getMyVideos(userId, title, category, status, auditStatus);
return getDataTable(list); return getDataTable(list);
} }
@ -108,7 +78,7 @@ public class VetTrainingVideoController extends BaseController {
public TableDataInfo getPublicVideos( public TableDataInfo getPublicVideos(
@RequestParam(required = false) String title, @RequestParam(required = false) String title,
@RequestParam(required = false) String category, @RequestParam(required = false) String category,
@RequestParam(required = false) String userName) { // 参数名改为userName
@RequestParam(required = false) String userName) {
startPage(); startPage();
List<VetTrainingVideo> list = trainingVideoService.getPublicVideos(title, category, userName); List<VetTrainingVideo> list = trainingVideoService.getPublicVideos(title, category, userName);
@ -120,15 +90,16 @@ public class VetTrainingVideoController extends BaseController {
*/ */
@GetMapping("/video/{videoId}") @GetMapping("/video/{videoId}")
public AjaxResult getVideoDetail(@PathVariable Long videoId) { public AjaxResult getVideoDetail(@PathVariable Long videoId) {
Long userId = getCurrentUserId(); // 修正变量名
Long userId = getCurrentUserId();
VetTrainingVideo video = trainingVideoService.getVideoDetail(videoId, userId); VetTrainingVideo video = trainingVideoService.getVideoDetail(videoId, userId);
if (video == null) { if (video == null) {
return error("视频不存在或无权限查看"); return error("视频不存在或无权限查看");
} }
// 如果是公开视频或自己的视频增加观看次数
if ("1".equals(video.getStatus()) || userId.equals(video.getUserId())) {
// 如果是审核通过且公开的视频增加观看次数
if ("1".equals(video.getAuditStatus()) && // 1-审核通过
"1".equals(video.getStatus())) { // 1-已上架/公开
trainingVideoService.incrementViewCount(videoId); trainingVideoService.incrementViewCount(videoId);
video.setViewCount(video.getViewCount() + 1); video.setViewCount(video.getViewCount() + 1);
} }
@ -137,45 +108,104 @@ public class VetTrainingVideoController extends BaseController {
} }
/** /**
* 获取视频播放地址带权限校验- 可选功能
* 提交审核手动提交
*/
@PostMapping("/submit-audit/{videoId}")
public AjaxResult submitForAudit(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.submitForAudit(videoId, userId);
return success ? success("提交审核成功") : error("提交审核失败");
}
/**
* 取消审核
*/
@PostMapping("/cancel-audit/{videoId}")
public AjaxResult cancelAudit(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.cancelAudit(videoId, userId);
return success ? success("取消审核成功") : error("取消审核失败");
}
/**
* 重新提交审核
*/
@PostMapping("/resubmit-audit/{videoId}")
public AjaxResult resubmitForAudit(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.resubmitForAudit(videoId, userId);
return success ? success("重新提交审核成功") : error("重新提交审核失败");
}
/**
* 审核视频管理员接口- 使用JSON接收
*/ */
@GetMapping("/video/play/{videoId}")
public AjaxResult getPlayUrl(@PathVariable Long videoId) {
Long userId = getCurrentUserId(); // 修正变量名
String playUrl = trainingVideoService.getVideoPlayUrl(videoId, userId);
@PostMapping("/audit/{videoId}")
public AjaxResult auditVideo(@PathVariable Long videoId,
@RequestBody Map<String, String> auditData) {
String auditStatus = auditData.get("auditStatus");
String auditOpinion = auditData.get("auditOpinion");
if (playUrl == null) {
return error("无权限播放此视频");
if (auditStatus == null || auditStatus.trim().isEmpty()) {
return error("审核状态不能为空");
} }
return success(playUrl);
Long auditUserId = getCurrentUserId();
boolean success = trainingVideoService.auditVideo(videoId, auditStatus, auditOpinion, auditUserId);
return success ? success("审核操作成功") : error("审核操作失败");
} }
/** /**
* 获取热门视频按观看次数排序
* 上架视频审核通过后才能上架
*/ */
@GetMapping("/hot-videos")
public AjaxResult getHotVideos(@RequestParam(defaultValue = "10") Integer limit) {
List<VetTrainingVideo> list = trainingVideoService.getHotVideos(limit);
return success(list);
@PostMapping("/publish/{videoId}")
public AjaxResult publishVideo(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.publishVideo(videoId, userId);
return success ? success("视频已上架") : error("上架失败,请确保视频已通过审核");
} }
/** /**
* 搜索视频
* 下架视频
*/ */
@GetMapping("/search")
public TableDataInfo searchVideos(@RequestParam String keyword) {
@PostMapping("/offline/{videoId}")
public AjaxResult offlineVideo(@PathVariable Long videoId) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.offlineVideo(videoId, userId);
return success ? success("视频已下架") : error("下架失败");
}
/**
* 获取待审核视频列表管理员接口
*/
@GetMapping("/pending-audit")
public TableDataInfo getPendingAuditVideos(
@RequestParam(required = false) String title,
@RequestParam(required = false) String userName) {
startPage(); startPage();
List<VetTrainingVideo> list = trainingVideoService.searchVideos(keyword);
List<VetTrainingVideo> list = trainingVideoService.getPendingAuditVideos(title, userName);
return getDataTable(list); return getDataTable(list);
} }
/**
* 编辑视频信息
*/
@PutMapping("/update/{videoId}")
public AjaxResult updateVideoInfo(@PathVariable Long videoId,
@RequestBody VetTrainingVideo video) {
Long userId = getCurrentUserId();
boolean success = trainingVideoService.updateVideoInfo(videoId, video, userId);
return success ? success("更新成功") : error("更新失败");
}
/** /**
* 删除我的视频 * 删除我的视频
*/ */
@DeleteMapping("/{videoId}") @DeleteMapping("/{videoId}")
public AjaxResult deleteVideo(@PathVariable Long videoId) { public AjaxResult deleteVideo(@PathVariable Long videoId) {
Long userId = getCurrentUserId(); // 修正变量名
Long userId = getCurrentUserId();
VetTrainingVideo video = trainingVideoService.getVideoDetail(videoId, userId); VetTrainingVideo video = trainingVideoService.getVideoDetail(videoId, userId);
if (video == null) { if (video == null) {

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

@ -27,16 +27,24 @@ public class VetKnowledge extends BaseEntity
private String content; private String content;
/** 文章分类(治疗防治/饲养管理/其他) */ /** 文章分类(治疗防治/饲养管理/其他) */
@Excel(name = "文章分类", readConverterExp = "治=疗防治/饲养管理/其他")
@Excel(name = "文章分类", dictType = "article_category")
private String category; private String category;
/** 检测出的敏感词 */ /** 检测出的敏感词 */
@Excel(name = "检测出的敏感词") @Excel(name = "检测出的敏感词")
private String sensitiveWords; private String sensitiveWords;
/** 状态(0-待审核 1-已发布 2-敏感内容驳回) */
@Excel(name = "状态", readConverterExp = "0=-待审核,1=-已发布,2=-敏感内容驳回")
private String status;
/** 文章状态(0-草稿 1-已发布 2-已下架) */
@Excel(name = "文章状态", dictType = "article_status")
private String articleStatus;
/** 审核状态(0-待审核 1-审核中 2-审核通过 3-审核驳回 4-敏感内容驳回) */
@Excel(name = "审核状态", dictType = "vet_audit_status")
private String auditStatus;
/** 审核意见 */
@Excel(name = "审核意见")
private String auditComment;
public void setId(Long id) public void setId(Long id)
{ {
@ -88,14 +96,28 @@ public class VetKnowledge extends BaseEntity
return sensitiveWords; return sensitiveWords;
} }
public void setStatus(String status)
{
this.status = status;
public String getArticleStatus() {
return articleStatus;
} }
public String getStatus()
{
return status;
public void setArticleStatus(String articleStatus) {
this.articleStatus = articleStatus;
}
public String getAuditStatus() {
return auditStatus;
}
public void setAuditStatus(String auditStatus) {
this.auditStatus = auditStatus;
}
public String getAuditComment() {
return auditComment;
}
public void setAuditComment(String auditComment) {
this.auditComment = auditComment;
} }
@Override @Override
@ -106,7 +128,9 @@ public class VetKnowledge extends BaseEntity
.append("content", getContent()) .append("content", getContent())
.append("category", getCategory()) .append("category", getCategory())
.append("sensitiveWords", getSensitiveWords()) .append("sensitiveWords", getSensitiveWords())
.append("status", getStatus())
.append("articleStatus", getArticleStatus())
.append("auditStatus", getAuditStatus())
.append("auditComment", getAuditComment())
.append("createBy", getCreateBy()) .append("createBy", getCreateBy())
.append("createTime", getCreateTime()) .append("createTime", getCreateTime())
.append("updateBy", getUpdateBy()) .append("updateBy", getUpdateBy())

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

@ -1,26 +1,287 @@
package com.chenhai.vet.domain; package com.chenhai.vet.domain;
import lombok.Data;
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; import java.util.Date;
@Data
public class VetTrainingVideo {
/**
* 兽医培训视频对象 vet_training_video
*
* @author ruoyi
* @date 2026-01-08
*/
public class VetTrainingVideo extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键 */
private Long id; private Long id;
/** 用户ID */
private Long userId; private Long userId;
/** 视频标题 */
@Excel(name = "视频标题")
private String title; private String title;
/** 视频描述 */
@Excel(name = "视频描述")
private String description; private String description;
/** 视频URL */
@Excel(name = "视频URL")
private String videoUrl; private String videoUrl;
/** 封面图片 */
@Excel(name = "封面图片")
private String coverImage; private String coverImage;
/** 视频分类(手术技巧/疾病诊断/药物使用/其他) */
@Excel(name = "视频分类", dictType = "video_category")
private String category; private String category;
/** 视频标签 */
@Excel(name = "视频标签")
private String tags; private String tags;
private Integer duration; // 视频时长
private Long fileSize; // 文件大小字节
/** 视频时长(秒) */
@Excel(name = "视频时长")
private Integer duration;
/** 文件大小(字节) */
@Excel(name = "文件大小")
private Long fileSize;
/** 观看次数 */
@Excel(name = "观看次数")
private Integer viewCount; private Integer viewCount;
private String status; // 0-私有 1-公开
private Date createTime;
private Date updateTime;
// 非数据库字段
private String userName; // 兽医姓名
private String durationStr; // 格式化后的时长12:30
/** 上架状态(0-私有 1-公开) */
@Excel(name = "上架状态", dictType = "video_status")
private String status;
/** 审核状态(0-待审核 1-审核通过 2-审核拒绝 3-无需审核) */
@Excel(name = "审核状态", dictType = "video_audit_status")
private String auditStatus;
/** 审核意见 */
@Excel(name = "审核意见")
private String auditOpinion;
/** 审核人ID */
private Long auditUserId;
/** 审核时间 */
private Date auditTime;
/** 格式化后的时长(如:12:30) */
private String durationStr;
/** 用户名称(非数据库字段) */
private String userName;
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 setTitle(String title)
{
this.title = title;
}
public String getTitle()
{
return title;
}
public void setDescription(String description)
{
this.description = description;
}
public String getDescription()
{
return description;
}
public void setVideoUrl(String videoUrl)
{
this.videoUrl = videoUrl;
}
public String getVideoUrl()
{
return videoUrl;
}
public void setCoverImage(String coverImage)
{
this.coverImage = coverImage;
}
public String getCoverImage()
{
return coverImage;
}
public void setCategory(String category)
{
this.category = category;
}
public String getCategory()
{
return category;
}
public void setTags(String tags)
{
this.tags = tags;
}
public String getTags()
{
return tags;
}
public void setDuration(Integer duration)
{
this.duration = duration;
}
public Integer getDuration()
{
return duration;
}
public void setFileSize(Long fileSize)
{
this.fileSize = fileSize;
}
public Long getFileSize()
{
return fileSize;
}
public void setViewCount(Integer viewCount)
{
this.viewCount = viewCount;
}
public Integer getViewCount()
{
return viewCount;
}
public void setStatus(String status)
{
this.status = status;
}
public String getStatus()
{
return status;
}
public void setAuditStatus(String auditStatus)
{
this.auditStatus = auditStatus;
}
public String getAuditStatus()
{
return auditStatus;
}
public void setAuditOpinion(String auditOpinion)
{
this.auditOpinion = auditOpinion;
}
public String getAuditOpinion()
{
return auditOpinion;
}
public void setAuditUserId(Long auditUserId)
{
this.auditUserId = auditUserId;
}
public Long getAuditUserId()
{
return auditUserId;
}
public void setAuditTime(Date auditTime)
{
this.auditTime = auditTime;
}
public Date getAuditTime()
{
return auditTime;
}
public String getDurationStr() {
return durationStr;
}
public void setDurationStr(String durationStr) {
this.durationStr = durationStr;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("userId", getUserId())
.append("title", getTitle())
.append("description", getDescription())
.append("videoUrl", getVideoUrl())
.append("coverImage", getCoverImage())
.append("category", getCategory())
.append("tags", getTags())
.append("duration", getDuration())
.append("fileSize", getFileSize())
.append("viewCount", getViewCount())
.append("status", getStatus())
.append("auditStatus", getAuditStatus())
.append("auditOpinion", getAuditOpinion())
.append("auditUserId", getAuditUserId())
.append("auditTime", getAuditTime())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.toString();
}
} }

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

@ -2,6 +2,7 @@ package com.chenhai.vet.mapper;
import com.chenhai.vet.domain.VetTrainingVideo; import com.chenhai.vet.domain.VetTrainingVideo;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List; import java.util.List;
public interface VetTrainingVideoMapper { public interface VetTrainingVideoMapper {
@ -11,7 +12,8 @@ public interface VetTrainingVideoMapper {
List<VetTrainingVideo> selectMyVideos(@Param("userId") Long userId, List<VetTrainingVideo> selectMyVideos(@Param("userId") Long userId,
@Param("title") String title, @Param("title") String title,
@Param("category") String category, @Param("category") String category,
@Param("status") String status);
@Param("status") String status,
@Param("auditStatus") String auditStatus);
List<VetTrainingVideo> selectPublicVideos(@Param("title") String title, List<VetTrainingVideo> selectPublicVideos(@Param("title") String title,
@Param("category") String category, @Param("category") String category,
@ -24,5 +26,24 @@ public interface VetTrainingVideoMapper {
List<VetTrainingVideo> selectHotVideos(@Param("limit") Integer limit); List<VetTrainingVideo> selectHotVideos(@Param("limit") Integer limit);
List<VetTrainingVideo> searchVideos(@Param("keyword") String keyword); List<VetTrainingVideo> searchVideos(@Param("keyword") String keyword);
int deleteVideoById(@Param("id") Long id); int deleteVideoById(@Param("id") Long id);
// 更新审核状态
int updateAuditStatus(@Param("id") Long id,
@Param("auditStatus") String auditStatus,
@Param("auditOpinion") String auditOpinion,
@Param("auditUserId") Long auditUserId,
@Param("auditTime") Date auditTime);
// 更新视频状态公开/私有
int updateStatus(@Param("id") Long id,
@Param("status") String status);
// 获取待审核视频列表
List<VetTrainingVideo> selectPendingAuditVideos(@Param("title") String title,
@Param("userName") String userName);
// 更新视频信息
int updateVideoInfo(VetTrainingVideo video);
} }

30
chenhai-system/src/main/java/com/chenhai/vet/service/IVetKnowledgeService.java

@ -62,13 +62,37 @@ public interface IVetKnowledgeService
public int deleteVetKnowledgeById(Long id); public int deleteVetKnowledgeById(Long id);
/** /**
* 上传文章待审核
* 提交审核
* @param id 文章ID
* @return 结果
*/
AjaxResult submitForAudit(Long id);
/**
* 审核文章
* @param id 文章ID
* @param auditStatus 审核状态2-审核通过 3-审核驳回
* @param auditComment 审核意见
* @return 结果
*/ */
AjaxResult uploadVetKnowledge(VetKnowledge vetKnowledge);
AjaxResult auditVetKnowledge(Long id, String auditStatus, String auditComment);
/** /**
* 发布文章更新状态为已发布
* 发布文章更新文章状态为已发布
*/ */
AjaxResult publishVetKnowledge(Long id); AjaxResult publishVetKnowledge(Long id);
/**
* 上架文章
* @param id 文章ID
* @return 结果
*/
AjaxResult onlineVetKnowledge(Long id);
/**
* 下架文章
* @param id 文章ID
* @return 结果
*/
AjaxResult offlineVetKnowledge(Long id, String auditComment);
} }

1
chenhai-system/src/main/java/com/chenhai/vet/service/IVetProductService.java

@ -58,4 +58,5 @@ public interface IVetProductService
* @return 结果 * @return 结果
*/ */
public int deleteVetProductById(Long id); public int deleteVetProductById(Long id);
} }

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

@ -8,14 +8,14 @@ import java.util.List;
public interface IVetTrainingVideoService { public interface IVetTrainingVideoService {
/** /**
* 上传并保存视频
* 上传并保存视频自动设为待审核
*/ */
String uploadAndSave(VetTrainingVideo video, MultipartFile videoFile, MultipartFile coverImage); String uploadAndSave(VetTrainingVideo video, MultipartFile videoFile, MultipartFile coverImage);
/** /**
* 获取我的视频列表 * 获取我的视频列表
*/ */
List<VetTrainingVideo> getMyVideos(Long userId, String title, String category, String status);
List<VetTrainingVideo> getMyVideos(Long userId, String title, String category, String status, String auditStatus);
/** /**
* 获取公开视频列表 * 获取公开视频列表
@ -48,4 +48,44 @@ public interface IVetTrainingVideoService {
List<VetTrainingVideo> searchVideos(String keyword); List<VetTrainingVideo> searchVideos(String keyword);
int deleteVideoById(Long videoId); int deleteVideoById(Long videoId);
/**
* 提交审核用户手动提交
*/
boolean submitForAudit(Long videoId, Long userId);
/**
* 取消审核用户主动取消
*/
boolean cancelAudit(Long videoId, Long userId);
/**
* 重新提交审核审核拒绝后
*/
boolean resubmitForAudit(Long videoId, Long userId);
/**
* 审核视频管理员
*/
boolean auditVideo(Long videoId, String auditStatus, String auditOpinion, Long auditUserId);
/**
* 上架视频审核通过后
*/
boolean publishVideo(Long videoId, Long userId);
/**
* 下架视频
*/
boolean offlineVideo(Long videoId, Long userId);
/**
* 获取待审核视频列表管理员
*/
List<VetTrainingVideo> getPendingAuditVideos(String title, String userName);
/**
* 编辑视频信息
*/
boolean updateVideoInfo(Long videoId, VetTrainingVideo video, Long userId);
} }

235
chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetKnowledgeServiceImpl.java

@ -25,9 +25,6 @@ public class VetKnowledgeServiceImpl implements IVetKnowledgeService
/** /**
* 查询兽医文章 * 查询兽医文章
*
* @param id 兽医文章主键
* @return 兽医文章
*/ */
@Override @Override
public VetKnowledge selectVetKnowledgeById(Long id) public VetKnowledge selectVetKnowledgeById(Long id)
@ -37,9 +34,6 @@ public class VetKnowledgeServiceImpl implements IVetKnowledgeService
/** /**
* 查询兽医文章列表 * 查询兽医文章列表
*
* @param vetKnowledge 兽医文章
* @return 兽医文章
*/ */
@Override @Override
public List<VetKnowledge> selectVetKnowledgeList(VetKnowledge vetKnowledge) public List<VetKnowledge> selectVetKnowledgeList(VetKnowledge vetKnowledge)
@ -49,22 +43,23 @@ public class VetKnowledgeServiceImpl implements IVetKnowledgeService
/** /**
* 新增兽医文章 * 新增兽医文章
*
* @param vetKnowledge 兽医文章
* @return 结果
*/ */
@Override @Override
public int insertVetKnowledge(VetKnowledge vetKnowledge) public int insertVetKnowledge(VetKnowledge vetKnowledge)
{ {
vetKnowledge.setCreateTime(DateUtils.getNowDate()); vetKnowledge.setCreateTime(DateUtils.getNowDate());
// 新增文章时默认状态为草稿审核状态为待审核
if (vetKnowledge.getArticleStatus() == null) {
vetKnowledge.setArticleStatus("0"); // 草稿
}
if (vetKnowledge.getAuditStatus() == null) {
vetKnowledge.setAuditStatus("0"); // 待审核
}
return vetKnowledgeMapper.insertVetKnowledge(vetKnowledge); return vetKnowledgeMapper.insertVetKnowledge(vetKnowledge);
} }
/** /**
* 修改兽医文章 * 修改兽医文章
*
* @param vetKnowledge 兽医文章
* @return 结果
*/ */
@Override @Override
public int updateVetKnowledge(VetKnowledge vetKnowledge) public int updateVetKnowledge(VetKnowledge vetKnowledge)
@ -75,9 +70,6 @@ public class VetKnowledgeServiceImpl implements IVetKnowledgeService
/** /**
* 批量删除兽医文章 * 批量删除兽医文章
*
* @param ids 需要删除的兽医文章主键
* @return 结果
*/ */
@Override @Override
public int deleteVetKnowledgeByIds(Long[] ids) public int deleteVetKnowledgeByIds(Long[] ids)
@ -87,9 +79,6 @@ public class VetKnowledgeServiceImpl implements IVetKnowledgeService
/** /**
* 删除兽医文章信息 * 删除兽医文章信息
*
* @param id 兽医文章主键
* @return 结果
*/ */
@Override @Override
public int deleteVetKnowledgeById(Long id) public int deleteVetKnowledgeById(Long id)
@ -98,37 +87,223 @@ public class VetKnowledgeServiceImpl implements IVetKnowledgeService
} }
/** /**
* 上传文章默认状态为0-待审核
* 提交审核将审核状态改为审核中
*/ */
@Override @Override
public AjaxResult uploadVetKnowledge(VetKnowledge vetKnowledge) {
// 填充基础信息
vetKnowledge.setCreateBy(SecurityUtils.getUsername());
vetKnowledge.setCreateTime(DateUtils.getNowDate());
vetKnowledge.setStatus("0"); // 固定为待审核
public AjaxResult submitForAudit(Long id) {
try {
VetKnowledge current = vetKnowledgeMapper.selectVetKnowledgeById(id);
int result = vetKnowledgeMapper.insertVetKnowledge(vetKnowledge);
if (current == null) {
return AjaxResult.error("文章不存在");
}
// 只有草稿状态(0)且待审核状态(0)的文章才能提交审核
if (!"0".equals(current.getArticleStatus())) {
return AjaxResult.error("只有草稿状态的文章可以提交审核");
}
if (!"0".equals(current.getAuditStatus())) {
return AjaxResult.error("文章当前状态不能提交审核");
}
String username = SecurityUtils.getUsername();
if (username == null || username.trim().isEmpty()) {
username = "system";
}
System.out.println("当前用户: " + username);
VetKnowledge vetKnowledge = new VetKnowledge();
vetKnowledge.setId(id);
vetKnowledge.setAuditStatus("1"); // 审核中
vetKnowledge.setUpdateBy(username);
vetKnowledge.setUpdateTime(DateUtils.getNowDate());
int result = vetKnowledgeMapper.updateVetKnowledge(vetKnowledge);
// 验证更新是否真的成功
if (result > 0) { if (result > 0) {
return AjaxResult.success("文章上传成功,等待审核");
// 重新查询验证
VetKnowledge updated = vetKnowledgeMapper.selectVetKnowledgeById(id);
return AjaxResult.success("文章已提交审核,等待审核");
} else {
return AjaxResult.error("提交审核失败");
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("提交审核失败: " + e.getMessage());
} }
return AjaxResult.error("文章上传失败");
} }
/** /**
* 发布文章更新状态为1-已发布
* 审核文章
*/
@Override
public AjaxResult auditVetKnowledge(Long id, String auditStatus, String auditComment) {
try {
VetKnowledge current = vetKnowledgeMapper.selectVetKnowledgeById(id);
if (current == null) {
return AjaxResult.error("文章不存在");
}
// 只有审核中状态(1)才能进行审核
if (!"1".equals(current.getAuditStatus())) {
return AjaxResult.error("只有审核中状态的文章可以审核");
}
VetKnowledge vetKnowledge = new VetKnowledge();
vetKnowledge.setId(id);
vetKnowledge.setAuditStatus(auditStatus); // 审核状态
vetKnowledge.setAuditComment(auditComment); // 审核意见
String username = SecurityUtils.getUsername();
if (username == null || username.trim().isEmpty()) {
username = "system";
}
vetKnowledge.setUpdateBy(username);
vetKnowledge.setUpdateTime(DateUtils.getNowDate());
int result = vetKnowledgeMapper.updateVetKnowledge(vetKnowledge);
if (result > 0) {
String message = "2".equals(auditStatus) ? "文章审核通过" : "文章审核驳回";
return AjaxResult.success(message);
}
return AjaxResult.error("审核操作失败");
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("审核操作失败: " + e.getMessage());
}
}
/**
* 发布文章审核通过的文章才能发布
*/ */
@Override @Override
public AjaxResult publishVetKnowledge(Long id) { public AjaxResult publishVetKnowledge(Long id) {
try {
VetKnowledge current = vetKnowledgeMapper.selectVetKnowledgeById(id);
if (current == null) {
return AjaxResult.error("文章不存在");
}
// 只有审核通过(2)且为草稿状态(0)的文章才能发布
if (!"2".equals(current.getAuditStatus())) {
return AjaxResult.error("只有审核通过的文章可以发布");
}
if (!"0".equals(current.getArticleStatus())) {
return AjaxResult.error("文章当前状态不能发布");
}
VetKnowledge vetKnowledge = new VetKnowledge(); VetKnowledge vetKnowledge = new VetKnowledge();
vetKnowledge.setId(id); vetKnowledge.setId(id);
vetKnowledge.setStatus("1"); // 已发布
vetKnowledge.setUpdateBy(SecurityUtils.getUsername());
vetKnowledge.setArticleStatus("1"); // 已发布
String username = SecurityUtils.getUsername();
if (username == null || username.trim().isEmpty()) {
username = "system";
}
vetKnowledge.setUpdateBy(username);
vetKnowledge.setUpdateTime(DateUtils.getNowDate()); vetKnowledge.setUpdateTime(DateUtils.getNowDate());
int result = vetKnowledgeMapper.updateVetKnowledge(vetKnowledge); int result = vetKnowledgeMapper.updateVetKnowledge(vetKnowledge);
if (result > 0) { if (result > 0) {
return AjaxResult.success("文章发布成功"); return AjaxResult.success("文章发布成功");
} }
return AjaxResult.error("文章发布失败"); return AjaxResult.error("文章发布失败");
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("文章发布失败: " + e.getMessage());
}
}
/**
* 上架文章将已下架的文章重新上架
*/
@Override
public AjaxResult onlineVetKnowledge(Long id) {
try {
VetKnowledge current = vetKnowledgeMapper.selectVetKnowledgeById(id);
if (current == null) {
return AjaxResult.error("文章不存在");
}
// 只有已下架状态(2)的文章才能上架
if (!"2".equals(current.getArticleStatus())) {
return AjaxResult.error("只有已下架的文章可以上架");
}
VetKnowledge vetKnowledge = new VetKnowledge();
vetKnowledge.setId(id);
vetKnowledge.setArticleStatus("1"); // 上架后变为已发布
String username = SecurityUtils.getUsername();
if (username == null || username.trim().isEmpty()) {
username = "system";
}
vetKnowledge.setUpdateBy(username);
vetKnowledge.setUpdateTime(DateUtils.getNowDate());
int result = vetKnowledgeMapper.updateVetKnowledge(vetKnowledge);
if (result > 0) {
return AjaxResult.success("文章上架成功");
}
return AjaxResult.error("文章上架失败");
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("文章上架失败: " + e.getMessage());
}
}
/**
* 下架文章将已发布的文章下架增加下架原因参数
*/
@Override
public AjaxResult offlineVetKnowledge(Long id, String auditComment) {
try {
VetKnowledge current = vetKnowledgeMapper.selectVetKnowledgeById(id);
if (current == null) {
return AjaxResult.error("文章不存在");
}
// 只有已发布状态(1)的文章才能下架
if (!"1".equals(current.getArticleStatus())) {
return AjaxResult.error("只有已发布的文章可以下架");
}
// 验证下架原因前端已经验证这里再次确认
if (auditComment == null || auditComment.trim().length() < 2) {
return AjaxResult.error("下架原因不能少于2个字");
}
if (auditComment.trim().length() > 200) {
return AjaxResult.error("下架原因不能超过200字");
}
VetKnowledge vetKnowledge = new VetKnowledge();
vetKnowledge.setId(id);
vetKnowledge.setArticleStatus("2"); // 已下架
vetKnowledge.setAuditComment(auditComment.trim()); // 存储下架原因到审核意见字段
String username = SecurityUtils.getUsername();
if (username == null || username.trim().isEmpty()) {
username = "system";
}
vetKnowledge.setUpdateBy(username);
vetKnowledge.setUpdateTime(DateUtils.getNowDate());
int result = vetKnowledgeMapper.updateVetKnowledge(vetKnowledge);
if (result > 0) {
return AjaxResult.success("文章下架成功");
}
return AjaxResult.error("文章下架失败");
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("文章下架失败: " + e.getMessage());
}
} }
} }

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

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

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

@ -6,6 +6,7 @@ import com.chenhai.vet.service.IVetTrainingVideoService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.File; import java.io.File;
@ -27,6 +28,7 @@ public class VetTrainingVideoServiceImpl implements IVetTrainingVideoService {
private String uploadPath; private String uploadPath;
@Override @Override
@Transactional
public String uploadAndSave(VetTrainingVideo video, MultipartFile videoFile, MultipartFile coverImage) { public String uploadAndSave(VetTrainingVideo video, MultipartFile videoFile, MultipartFile coverImage) {
try { try {
// 1. 创建上传目录 // 1. 创建上传目录
@ -55,10 +57,12 @@ public class VetTrainingVideoServiceImpl implements IVetTrainingVideoService {
} }
// 5. 计算视频时长和大小 // 5. 计算视频时长和大小
int duration = getVideoDuration(videoFile); // 需要实现这个方法
int duration = getVideoDuration(videoFile);
long fileSize = videoFile.getSize(); long fileSize = videoFile.getSize();
// 6. 保存到数据库
// 6. 设置初始状态私有 + 待审核
video.setStatus("0"); // 0-未上架/私有
video.setAuditStatus("0"); // 0-待审核
video.setVideoUrl("/uploads/" + uniqueFileName); video.setVideoUrl("/uploads/" + uniqueFileName);
video.setCoverImage(coverImageUrl); video.setCoverImage(coverImageUrl);
video.setDuration(duration); video.setDuration(duration);
@ -69,7 +73,7 @@ public class VetTrainingVideoServiceImpl implements IVetTrainingVideoService {
videoMapper.insertVideo(video); videoMapper.insertVideo(video);
return "上传成功!视频ID:" + video.getId();
return "上传成功!视频已自动提交审核,请耐心等待管理员审核。";
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("文件保存失败", e); throw new RuntimeException("文件保存失败", e);
@ -77,12 +81,13 @@ public class VetTrainingVideoServiceImpl implements IVetTrainingVideoService {
} }
@Override @Override
public List<VetTrainingVideo> getMyVideos(Long userId, String title, String category, String status) {
return videoMapper.selectMyVideos(userId, title, category, status);
public List<VetTrainingVideo> getMyVideos(Long userId, String title, String category, String status, String auditStatus) {
return videoMapper.selectMyVideos(userId, title, category, status, auditStatus);
} }
@Override @Override
public List<VetTrainingVideo> getPublicVideos(String title, String category, String vetName) { public List<VetTrainingVideo> getPublicVideos(String title, String category, String vetName) {
// 只显示审核通过且公开的视频
return videoMapper.selectPublicVideos(title, category, vetName); return videoMapper.selectPublicVideos(title, category, vetName);
} }
@ -94,8 +99,10 @@ public class VetTrainingVideoServiceImpl implements IVetTrainingVideoService {
return null; return null;
} }
// 权限校验只能查看公开视频或自己的视频
boolean canView = "1".equals(video.getStatus()) || currentVetId.equals(video.getUserId());
// 权限校验只能查看自己的视频或审核通过公开的视频
boolean canView = "1".equals(video.getAuditStatus()) && // 1-审核通过
"1".equals(video.getStatus()) || // 1-已上架/公开
currentVetId.equals(video.getUserId());
return canView ? video : null; return canView ? video : null;
} }
@ -107,8 +114,10 @@ public class VetTrainingVideoServiceImpl implements IVetTrainingVideoService {
return null; return null;
} }
// 权限校验
boolean canPlay = "1".equals(video.getStatus()) || currentVetId.equals(video.getUserId());
// 权限校验只能播放自己的视频或审核通过公开的视频
boolean canPlay = "1".equals(video.getAuditStatus()) && // 1-审核通过
"1".equals(video.getStatus()) || // 1-已上架/公开
currentVetId.equals(video.getUserId());
return canPlay ? video.getVideoUrl() : null; return canPlay ? video.getVideoUrl() : null;
} }
@ -127,20 +136,200 @@ public class VetTrainingVideoServiceImpl implements IVetTrainingVideoService {
return videoMapper.searchVideos(keyword); return videoMapper.searchVideos(keyword);
} }
@Override
@Transactional
public int deleteVideoById(Long videoId) {
return videoMapper.deleteVideoById(videoId);
}
@Override
@Transactional
public boolean submitForAudit(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null || !userId.equals(video.getUserId())) {
return false;
}
// 只能提交无需审核或审核拒绝的视频
if (!"3".equals(video.getAuditStatus()) && // 3-无需审核
!"2".equals(video.getAuditStatus())) { // 2-审核拒绝
return false;
}
// 更新为待审核状态
int result = videoMapper.updateAuditStatus(videoId,
"0", // 0-待审核
"已提交审核",
null,
new Date());
return result > 0;
}
@Override
@Transactional
public boolean cancelAudit(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null || !userId.equals(video.getUserId())) {
return false;
}
// 只能取消待审核状态的视频
if (!"0".equals(video.getAuditStatus())) { // 0-待审核
return false;
}
// 更新为无需审核状态
int result = videoMapper.updateAuditStatus(videoId,
"3", // 3-无需审核
"用户取消审核",
null,
new Date());
return result > 0;
}
@Override
@Transactional
public boolean resubmitForAudit(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null || !userId.equals(video.getUserId())) {
return false;
}
// 只能重新提交审核拒绝状态的视频
if (!"2".equals(video.getAuditStatus())) { // 2-审核拒绝
return false;
}
// 更新为待审核状态
int result = videoMapper.updateAuditStatus(videoId,
"0", // 0-待审核
"重新提交审核",
null,
new Date());
return result > 0;
}
@Override
@Transactional
public boolean auditVideo(Long videoId, String auditStatus, String auditOpinion, Long auditUserId) {
// 验证审核状态
if (!"1".equals(auditStatus) && // 1-审核通过
!"2".equals(auditStatus)) { // 2-审核拒绝
return false;
}
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null) {
return false;
}
// 只能审核待审核状态的视频
if (!"0".equals(video.getAuditStatus())) { // 0-待审核
return false;
}
int result = videoMapper.updateAuditStatus(videoId,
auditStatus,
auditOpinion,
auditUserId,
new Date());
// 如果审核通过自动设置为私有状态等待用户上架
if (result > 0 && "1".equals(auditStatus)) { // 1-审核通过
videoMapper.updateStatus(videoId, "0"); // 0-未上架/私有
}
return result > 0;
}
@Override
@Transactional
public boolean publishVideo(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null) {
return false;
}
// 权限校验只能操作自己的视频
if (!userId.equals(video.getUserId())) {
return false;
}
// 只能上架审核通过的视频
if (!"1".equals(video.getAuditStatus())) { // 1-审核通过
return false;
}
return videoMapper.updateStatus(videoId, "1") > 0; // 1-已上架/公开
}
@Override
@Transactional
public boolean offlineVideo(Long videoId, Long userId) {
VetTrainingVideo video = videoMapper.selectVideoById(videoId);
if (video == null) {
return false;
}
// 权限校验只能操作自己的视频
if (!userId.equals(video.getUserId())) {
return false;
}
// 只能下架已上架的视频
if (!"1".equals(video.getStatus())) { // 1-已上架/公开
return false;
}
return videoMapper.updateStatus(videoId, "0") > 0; // 0-未上架/私有
}
@Override
public List<VetTrainingVideo> getPendingAuditVideos(String title, String userName) {
return videoMapper.selectPendingAuditVideos(title, userName);
}
@Override
@Transactional
public boolean updateVideoInfo(Long videoId, VetTrainingVideo video, Long userId) {
VetTrainingVideo existingVideo = videoMapper.selectVideoById(videoId);
if (existingVideo == null || !userId.equals(existingVideo.getUserId())) {
return false;
}
// 只能编辑无需审核审核拒绝或私有状态的视频
boolean canEdit = "3".equals(existingVideo.getAuditStatus()) || // 3-无需审核
"2".equals(existingVideo.getAuditStatus()) || // 2-审核拒绝
"0".equals(existingVideo.getStatus()); // 0-未上架/私有
if (!canEdit) {
return false;
}
// 更新视频信息
existingVideo.setTitle(video.getTitle());
existingVideo.setDescription(video.getDescription());
existingVideo.setCategory(video.getCategory());
existingVideo.setUpdateTime(new Date());
return videoMapper.updateVideoInfo(existingVideo) > 0;
}
private String getFileExtension(String fileName) { private String getFileExtension(String fileName) {
if (fileName == null || fileName.lastIndexOf(".") == -1) {
return "mp4";
}
return fileName.substring(fileName.lastIndexOf(".") + 1); return fileName.substring(fileName.lastIndexOf(".") + 1);
} }
private int getVideoDuration(MultipartFile videoFile) { private int getVideoDuration(MultipartFile videoFile) {
// 这里需要实现获取视频时长的方法 // 这里需要实现获取视频时长的方法
// 可以使用 FFmpeg Java 的库来获取
// 暂时返回一个默认值
return 60; // 默认60秒 return 60; // 默认60秒
} }
@Override
public int deleteVideoById(Long videoId) {
// 逻辑删除设置 del_flag = '1'
return videoMapper.deleteVideoById(videoId);
}
} }

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

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chenhai.vet.mapper.VetKnowledgeMapper"> <mapper namespace="com.chenhai.vet.mapper.VetKnowledgeMapper">
<resultMap type="VetKnowledge" id="VetKnowledgeResult"> <resultMap type="VetKnowledge" id="VetKnowledgeResult">
@ -10,7 +10,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="content" column="content" /> <result property="content" column="content" />
<result property="category" column="category" /> <result property="category" column="category" />
<result property="sensitiveWords" column="sensitive_words" /> <result property="sensitiveWords" column="sensitive_words" />
<result property="status" column="status" />
<result property="articleStatus" column="article_status" />
<result property="auditStatus" column="audit_status" />
<result property="auditComment" column="audit_comment" />
<result property="createBy" column="create_by" /> <result property="createBy" column="create_by" />
<result property="createTime" column="create_time" /> <result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" /> <result property="updateBy" column="update_by" />
@ -19,17 +21,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap> </resultMap>
<sql id="selectVetKnowledgeVo"> <sql id="selectVetKnowledgeVo">
select id, title, content, category, sensitive_words, status, create_by, create_time, update_by, update_time, remark from vet_knowledge
select id, title, content, category, sensitive_words, article_status, audit_status, audit_comment, create_by, create_time, update_by, update_time, remark from vet_knowledge
</sql> </sql>
<select id="selectVetKnowledgeList" parameterType="VetKnowledge" resultMap="VetKnowledgeResult"> <select id="selectVetKnowledgeList" parameterType="VetKnowledge" resultMap="VetKnowledgeResult">
<include refid="selectVetKnowledgeVo"/> <include refid="selectVetKnowledgeVo"/>
<where> <where>
<if test="title != null and title != ''"> and title = #{title}</if>
<if test="content != null and content != ''"> and content = #{content}</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="category != null and category != ''"> and category = #{category}</if> <if test="category != null and category != ''"> and category = #{category}</if>
<if test="sensitiveWords != null and sensitiveWords != ''"> and sensitive_words = #{sensitiveWords}</if>
<if test="status != null and status != ''"> and status = #{status}</if>
<if test="sensitiveWords != null and sensitiveWords != ''"> and sensitive_words like concat('%', #{sensitiveWords}, '%')</if>
<if test="articleStatus != null and articleStatus != ''"> and article_status = #{articleStatus}</if>
<if test="auditStatus != null and auditStatus != ''"> and audit_status = #{auditStatus}</if>
</where> </where>
</select> </select>
@ -45,7 +48,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="content != null and content != ''">content,</if> <if test="content != null and content != ''">content,</if>
<if test="category != null and category != ''">category,</if> <if test="category != null and category != ''">category,</if>
<if test="sensitiveWords != null">sensitive_words,</if> <if test="sensitiveWords != null">sensitive_words,</if>
<if test="status != null">status,</if>
<if test="articleStatus != null">article_status,</if>
<if test="auditStatus != null">audit_status,</if>
<if test="auditComment != null">audit_comment,</if>
<if test="createBy != null">create_by,</if> <if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if> <if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if> <if test="updateBy != null">update_by,</if>
@ -57,7 +62,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="content != null and content != ''">#{content},</if> <if test="content != null and content != ''">#{content},</if>
<if test="category != null and category != ''">#{category},</if> <if test="category != null and category != ''">#{category},</if>
<if test="sensitiveWords != null">#{sensitiveWords},</if> <if test="sensitiveWords != null">#{sensitiveWords},</if>
<if test="status != null">#{status},</if>
<if test="articleStatus != null">#{articleStatus},</if>
<if test="auditStatus != null">#{auditStatus},</if>
<if test="auditComment != null">#{auditComment},</if>
<if test="createBy != null">#{createBy},</if> <if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if> <if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if> <if test="updateBy != null">#{updateBy},</if>
@ -73,7 +80,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="content != null and content != ''">content = #{content},</if> <if test="content != null and content != ''">content = #{content},</if>
<if test="category != null and category != ''">category = #{category},</if> <if test="category != null and category != ''">category = #{category},</if>
<if test="sensitiveWords != null">sensitive_words = #{sensitiveWords},</if> <if test="sensitiveWords != null">sensitive_words = #{sensitiveWords},</if>
<if test="status != null">status = #{status},</if>
<if test="articleStatus != null">article_status = #{articleStatus},</if>
<if test="auditStatus != null">audit_status = #{auditStatus},</if>
<if test="auditComment != null">audit_comment = #{auditComment},</if>
<if test="createBy != null">create_by = #{createBy},</if> <if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if> <if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if> <if test="updateBy != null">update_by = #{updateBy},</if>

2
chenhai-system/src/main/resources/mapper/vet/VetProductMapper.xml

@ -36,7 +36,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap> </resultMap>
<sql id="selectVetProductVo"> <sql id="selectVetProductVo">
select id, name, type, category, specification, unit, manufacturer, approval_number, ingredients, indications, usage_dosage, price, cost_price, stock, min_stock, main_image, images, treat_animals, treat_diseases, treatment_content, treatment_duration, precautions, status, is_deleted, clinic_id, vet_id, created_at, updated_at from vet_product
select id, name, type, category, specification, unit, manufacturer, approval_number, ingredients, indications, usage_dosage, price, cost_price, stock, min_stock, main_image, images, treat_animals, treat_diseases, treatment_content, treatment_duration, precautions, status, is_deleted, clinic_id, user_id, created_at, updated_at from vet_product
</sql> </sql>
<select id="selectVetProductList" parameterType="VetProduct" resultMap="VetProductResult"> <select id="selectVetProductList" parameterType="VetProduct" resultMap="VetProductResult">

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

@ -1,7 +1,5 @@
<?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"> <mapper namespace="com.chenhai.vet.mapper.VetTrainingVideoMapper">
<resultMap id="VideoResult" type="VetTrainingVideo"> <resultMap id="VideoResult" type="VetTrainingVideo">
@ -17,20 +15,26 @@
<result property="fileSize" column="file_size"/> <result property="fileSize" column="file_size"/>
<result property="viewCount" column="view_count"/> <result property="viewCount" column="view_count"/>
<result property="status" column="status"/> <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="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/> <result property="updateTime" column="update_time"/>
<result property="userName" column="user_name"/> <result property="userName" column="user_name"/>
<result property="auditUserId" column="audit_user_id"/>
<result property="auditTime" column="audit_time"/>
</resultMap> </resultMap>
<insert id="insertVideo" parameterType="VetTrainingVideo" useGeneratedKeys="true" keyProperty="id"> <insert id="insertVideo" parameterType="VetTrainingVideo" useGeneratedKeys="true" keyProperty="id">
INSERT INTO vet_training_video ( INSERT INTO vet_training_video (
user_id, title, description, video_url, cover_image, user_id, title, description, video_url, cover_image,
category, tags, duration, file_size, view_count, category, tags, duration, file_size, view_count,
status, create_time, update_time
status, audit_status, audit_opinion,
create_time, update_time
) VALUES ( ) VALUES (
#{userId}, #{title}, #{description}, #{videoUrl}, #{coverImage}, #{userId}, #{title}, #{description}, #{videoUrl}, #{coverImage},
#{category}, #{tags}, #{duration}, #{fileSize}, #{viewCount}, #{category}, #{tags}, #{duration}, #{fileSize}, #{viewCount},
#{status}, #{createTime}, #{updateTime}
#{status}, #{auditStatus}, #{auditOpinion},
#{createTime}, #{updateTime}
) )
</insert> </insert>
@ -48,6 +52,9 @@
<if test="status != null and status != ''"> <if test="status != null and status != ''">
AND v.status = #{status} AND v.status = #{status}
</if> </if>
<if test="auditStatus != null and auditStatus != ''">
AND v.audit_status = #{auditStatus}
</if>
ORDER BY v.create_time DESC ORDER BY v.create_time DESC
</select> </select>
@ -56,6 +63,7 @@
FROM vet_training_video v FROM vet_training_video v
LEFT JOIN sys_user u ON v.user_id = u.user_id LEFT JOIN sys_user u ON v.user_id = u.user_id
WHERE v.status = '1' WHERE v.status = '1'
AND v.audit_status = '1'
<if test="title != null and title != ''"> <if test="title != null and title != ''">
AND v.title LIKE CONCAT('%', #{title}, '%') AND v.title LIKE CONCAT('%', #{title}, '%')
</if> </if>
@ -86,6 +94,7 @@
FROM vet_training_video v FROM vet_training_video v
LEFT JOIN sys_user u ON v.user_id = u.user_id LEFT JOIN sys_user u ON v.user_id = u.user_id
WHERE v.status = '1' WHERE v.status = '1'
AND v.audit_status = '1'
ORDER BY v.view_count DESC ORDER BY v.view_count DESC
LIMIT #{limit} LIMIT #{limit}
</select> </select>
@ -95,6 +104,7 @@
FROM vet_training_video v FROM vet_training_video v
LEFT JOIN sys_user u ON v.user_id = u.user_id LEFT JOIN sys_user u ON v.user_id = u.user_id
WHERE v.status = '1' WHERE v.status = '1'
AND v.audit_status = '1'
AND (v.title LIKE CONCAT('%', #{keyword}, '%') AND (v.title LIKE CONCAT('%', #{keyword}, '%')
OR v.description LIKE CONCAT('%', #{keyword}, '%') OR v.description LIKE CONCAT('%', #{keyword}, '%')
OR v.tags LIKE CONCAT('%', #{keyword}, '%') OR v.tags LIKE CONCAT('%', #{keyword}, '%')
@ -106,4 +116,44 @@
DELETE FROM vet_training_video DELETE FROM vet_training_video
WHERE id = #{id} WHERE id = #{id}
</delete> </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>
<update id="updateStatus">
UPDATE vet_training_video
SET status = #{status},
update_time = NOW()
WHERE id = #{id}
</update>
<select id="selectPendingAuditVideos" resultMap="VideoResult">
SELECT v.*, u.nick_name as user_name
FROM vet_training_video v
LEFT JOIN sys_user u ON v.user_id = u.user_id
WHERE v.audit_status = '0'
<if test="title != null and title != ''">
AND v.title LIKE CONCAT('%', #{title}, '%')
</if>
<if test="userName != null and userName != ''">
AND u.nick_name LIKE CONCAT('%', #{userName}, '%')
</if>
ORDER BY v.create_time DESC
</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> </mapper>

39
chenhai-ui/src/api/vet/knowledge.js

@ -42,12 +42,22 @@ export function delKnowledge(id) {
method: 'delete' method: 'delete'
}) })
} }
// 上传文章(待审核)
export function uploadKnowledge(data) {
// 提交审核
export function submitForAudit(id) {
return request({ return request({
url: '/vet/knowledge/upload',
method: 'post',
data: data
url: '/vet/knowledge/submit/' + id,
method: 'put'
})
}
export function auditVetKnowledge(id, auditStatus, auditComment) {
return request({
url: '/vet/knowledge/audit/' + id,
method: 'put',
params: { // 使用params传递查询参数
auditStatus: auditStatus, // 参数名改为auditStatus
auditComment: auditComment
}
}) })
} }
@ -58,3 +68,22 @@ export function publishKnowledge(id) {
method: 'put' method: 'put'
}) })
} }
// 上架文章
export function onlineVetKnowledge(id) {
return request({
url: '/vet/knowledge/online/' + id,
method: 'put'
})
}
// 下架文章
export function offlineVetKnowledge(id, auditComment) {
return request({
url: '/vet/knowledge/offline/' + id,
method: 'put',
params: {
auditComment: auditComment // 使用 auditComment 作为参数名,后端会存储到 audit_comment 字段
}
})
}

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

@ -1,21 +1,18 @@
// src/api/vet/training.js
// trainingApi.js - 完整修正版
import request from '@/utils/request' import request from '@/utils/request'
// 兽医培训视频相关接口
export default { export default {
// 上传视频 // 上传视频
uploadVideo(data) { uploadVideo(data) {
// 注意:这里直接返回请求,不要重新创建 FormData
return request({ return request({
url: '/vet/training/upload', url: '/vet/training/upload',
method: 'post', method: 'post',
data: data, // data 已经是 FormData 对象
data: data,
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data'
}, },
timeout: 300000 timeout: 300000
}) })
}, },
// 获取公开视频列表 // 获取公开视频列表
@ -23,22 +20,16 @@ export default {
return request({ return request({
url: '/vet/training/public-videos', url: '/vet/training/public-videos',
method: 'get', method: 'get',
params
params: params
}) })
}, },
// 获取我的视频 // 获取我的视频
getMyVideos(params = {}) {
getMyVideos(params) {
return request({ return request({
url: '/vet/training/my-videos', url: '/vet/training/my-videos',
method: 'get', method: 'get',
params: {
pageNum: params.pageNum || 1,
pageSize: params.pageSize || 10,
title: params.title || '',
category: params.category || '',
status: params.status || ''
}
params: params
}) })
}, },
@ -50,37 +41,85 @@ export default {
}) })
}, },
// 获取播放地址(可选
getPlayUrl(id) {
// 提交审核(用户提交给管理员
submitForAudit(videoId) {
return request({ return request({
url: `/vet/training/video/play/${id}`,
method: 'get'
url: `/vet/training/submit-audit/${videoId}`,
method: 'post'
}) })
}, },
// 删除视频
deleteVideo(id) {
// 取消审核
cancelAudit(videoId) {
return request({ return request({
url: `/vet/training/${id}`,
method: 'delete'
url: `/vet/training/cancel-audit/${videoId}`,
method: 'post'
}) })
}, },
// 搜索视频
searchVideos(keyword) {
// 重新提交审核
resubmitAudit(videoId) {
return request({ return request({
url: '/vet/training/search',
method: 'get',
params: { keyword }
url: `/vet/training/resubmit-audit/${videoId}`,
method: 'post'
}) })
}, },
// 审核视频(管理员审核)
auditVideo(videoId, auditStatus, auditOpinion = '') {
// 改用JSON方式传递参数
const data = {
auditStatus: auditStatus,
auditOpinion: auditOpinion || ''
}
// 获取热门视频
getHotVideos(limit = 10) {
return request({ return request({
url: '/vet/training/hot-videos',
url: `/vet/training/audit/${videoId}`,
method: 'post',
data: data,
headers: {
'Content-Type': 'application/json'
}
})
},
// 上架视频
publishVideo(videoId) {
return request({
url: `/vet/training/publish/${videoId}`,
method: 'post'
})
},
// 下架视频
offlineVideo(videoId) {
return request({
url: `/vet/training/offline/${videoId}`,
method: 'post'
})
},
// 获取待审核视频列表
getPendingAuditVideos(params) {
return request({
url: '/vet/training/pending-audit',
method: 'get', method: 'get',
params: { limit }
params: params
})
},
// 更新视频信息
updateVideoInfo(videoId, data) {
return request({
url: `/vet/training/update/${videoId}`,
method: 'put',
data: data
})
},
// 删除视频
deleteVideo(videoId) {
return request({
url: `/vet/training/${videoId}`,
method: 'delete'
}) })
} }
} }

585
chenhai-ui/src/views/vet/knowledge/index.vue

@ -10,27 +10,45 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="文章分类" prop="category"> <el-form-item label="文章分类" prop="category">
<!-- 优化分类改为下拉选择避免手动输入不规范 -->
<el-select <el-select
v-model="queryParams.category" v-model="queryParams.category"
placeholder="请选择文章分类" placeholder="请选择文章分类"
clearable clearable
@keyup.enter.native="handleQuery"
> >
<el-option label="治疗防治" value="治疗防治" />
<el-option label="饲养管理" value="饲养管理" />
<el-option label="其他" value="其他" />
<el-option
v-for="dict in categoryOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status">
<el-form-item label="文章状态" prop="articleStatus">
<el-select <el-select
v-model="queryParams.status"
placeholder="请选择状态"
v-model="queryParams.articleStatus"
placeholder="请选择文章状态"
clearable clearable
@keyup.enter.native="handleQuery"
> >
<el-option label="待审核" value="0" />
<el-option label="已发布" value="1" />
<el-option
v-for="dict in articleStatusOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select>
</el-form-item>
<el-form-item label="审核状态" prop="auditStatus">
<el-select
v-model="queryParams.auditStatus"
placeholder="请选择审核状态"
clearable
>
<el-option
v-for="dict in auditStatusOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -44,33 +62,44 @@
<el-button <el-button
type="primary" type="primary"
plain plain
icon="el-icon-upload2"
icon="el-icon-plus"
size="mini" size="mini"
@click="handleUpload"
v-hasPermi="['vet:knowledge:upload']"
>上传文章</el-button>
@click="handleAdd"
v-hasPermi="['vet:knowledge:add']"
>新增文章</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button
type="success"
type="info"
plain plain
icon="el-icon-check"
icon="el-icon-s-promotion"
size="mini" size="mini"
:disabled="single" :disabled="single"
@click="handlePublish"
v-hasPermi="['vet:knowledge:publish']"
>发布文章</el-button>
@click="handleSubmitAudit"
v-hasPermi="['vet:knowledge:submit']"
>提交审核</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button
type="warning"
type="primary"
plain plain
icon="el-icon-edit"
icon="el-icon-finished"
size="mini" size="mini"
:disabled="single" :disabled="single"
@click="handleUpdate"
v-hasPermi="['vet:knowledge:edit']"
>修改</el-button>
@click="handleAuditDialog"
v-hasPermi="['vet:knowledge:audit']"
>审核</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-check"
size="mini"
:disabled="single"
@click="handlePublish"
v-hasPermi="['vet:knowledge:publish']"
>发布文章</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button
@ -98,41 +127,110 @@
<el-table v-loading="loading" :data="knowledgeList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="knowledgeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键" align="center" prop="id" />
<el-table-column label="文章标题" align="center" prop="title" min-width="200" />
<el-table-column label="文章分类" align="center" prop="category" width="120" />
<!-- 优化状态显示为标签样式更直观 -->
<el-table-column label="状态" align="center" prop="status" width="100">
<el-table-column label="文章标题" align="center" prop="title" min-width="200" show-overflow-tooltip />
<!-- 文章分类列 - 使用 el-tag 显示颜色 -->
<el-table-column label="文章分类" align="center" prop="category" width="120">
<template slot-scope="scope">
<el-tag :type="getCategoryTagType(scope.row.category)">
{{ scope.row.category }}
</el-tag>
</template>
</el-table-column>
<!-- 文章状态列 - 使用 el-tag 显示颜色 -->
<el-table-column label="文章状态" align="center" prop="articleStatus" width="100">
<template slot-scope="scope">
<el-tag :type="getArticleStatusTagType(scope.row.articleStatus)" size="small">
{{ getArticleStatusLabel(scope.row.articleStatus) }}
</el-tag>
</template>
</el-table-column>
<!-- 审核状态列 - 使用 el-tag 显示颜色 -->
<el-table-column label="审核状态" align="center" prop="auditStatus" width="120">
<template slot-scope="scope">
<el-tag :type="getAuditStatusTagType(scope.row.auditStatus)" size="small">
{{ getAuditStatusLabel(scope.row.auditStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="审核意见" align="center" prop="auditComment" width="150" show-overflow-tooltip>
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.status === '0'" type="warning">待审核</el-tag>
<el-tag v-if="scope.row.status === '1'" type="success">已发布</el-tag>
<span v-if="scope.row.auditComment">{{ scope.row.auditComment }}</span>
<span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="创建人" align="center" prop="createBy" width="100" /> <el-table-column label="创建人" align="center" prop="createBy" width="100" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" /> <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="handleView(scope.row)"
>查看</el-button>
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
icon="el-icon-edit" icon="el-icon-edit"
@click="handleUpdate(scope.row)" @click="handleUpdate(scope.row)"
v-hasPermi="['vet:knowledge:edit']" v-hasPermi="['vet:knowledge:edit']"
v-if="scope.row.articleStatus === '0' && scope.row.auditStatus === '0'"
>修改</el-button> >修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-s-promotion"
@click="handleSubmitAudit(scope.row.id)"
v-hasPermi="['vet:knowledge:submit']"
v-if="scope.row.articleStatus === '0' && scope.row.auditStatus === '0'"
>提交审核</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-finished"
@click="handleAuditDialog(scope.row.id)"
v-hasPermi="['vet:knowledge:audit']"
v-if="scope.row.auditStatus === '1'"
>审核</el-button>
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
icon="el-icon-check" icon="el-icon-check"
@click="handlePublish(scope.row.id)" @click="handlePublish(scope.row.id)"
v-hasPermi="['vet:knowledge:publish']" v-hasPermi="['vet:knowledge:publish']"
v-if="scope.row.status === '0'"
v-if="scope.row.auditStatus === '2' && scope.row.articleStatus === '0'"
>发布</el-button> >发布</el-button>
<!-- 下架按钮已发布状态显示 -->
<el-button
size="mini"
type="text"
icon="el-icon-bottom"
style="color: #E6A23C"
@click="handleOffline(scope.row)"
v-hasPermi="['vet:knowledge:publish']"
v-if="scope.row.articleStatus === '1'"
>下架</el-button>
<!-- 上架按钮已下架状态显示 -->
<el-button
size="mini"
type="text"
icon="el-icon-top"
style="color: #67C23A"
@click="handleOnline(scope.row)"
v-hasPermi="['vet:knowledge:publish']"
v-if="scope.row.articleStatus === '2'"
>上架</el-button>
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
icon="el-icon-delete" icon="el-icon-delete"
@click="handleDelete(scope.row)" @click="handleDelete(scope.row)"
v-hasPermi="['vet:knowledge:remove']" v-hasPermi="['vet:knowledge:remove']"
v-if="scope.row.articleStatus === '0'"
>删除</el-button> >删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -146,21 +244,23 @@
@pagination="getList" @pagination="getList"
/> />
<!-- 添加/上传/修改兽医文章对话框 -->
<el-dialog :title="title" :visible.sync="open" width="700px" append-to-body>
<!-- 新增/修改文章对话框 -->
<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 ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="文章标题" prop="title"> <el-form-item label="文章标题" prop="title">
<el-input v-model="form.title" placeholder="请输入文章标题" /> <el-input v-model="form.title" placeholder="请输入文章标题" />
</el-form-item> </el-form-item>
<el-form-item label="文章内容" prop="content"> <el-form-item label="文章内容" prop="content">
<editor v-model="form.content" :min-height="200"/>
<editor v-model="form.content" :min-height="300"/>
</el-form-item> </el-form-item>
<el-form-item label="文章分类" prop="category"> <el-form-item label="文章分类" prop="category">
<!-- 优化分类下拉选择固定选项 -->
<el-select v-model="form.category" placeholder="请选择文章分类"> <el-select v-model="form.category" placeholder="请选择文章分类">
<el-option label="治疗防治" value="治疗防治" />
<el-option label="饲养管理" value="饲养管理" />
<el-option label="其他" value="其他" />
<el-option
v-for="dict in categoryOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
@ -172,12 +272,81 @@
<el-button @click="cancel"> </el-button> <el-button @click="cancel"> </el-button>
</div> </div>
</el-dialog> </el-dialog>
<!-- 审核对话框 -->
<el-dialog title="文章审核" :visible.sync="auditOpen" width="500px" append-to-body>
<el-form ref="auditForm" :model="auditForm" label-width="80px">
<el-form-item label="审核结果" prop="auditStatus" required>
<el-radio-group v-model="auditForm.auditStatus">
<el-radio label="2">审核通过</el-radio>
<el-radio label="3">审核驳回</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审核意见" prop="auditComment">
<el-input
v-model="auditForm.auditComment"
type="textarea"
:rows="4"
placeholder="请输入审核意见(可选)"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitAudit"> </el-button>
<el-button @click="cancelAudit"> </el-button>
</div>
</el-dialog>
<!-- 查看详情对话框 -->
<el-dialog title="文章详情" :visible.sync="viewOpen" width="900px" append-to-body>
<el-descriptions :column="2" border>
<el-descriptions-item label="文章标题">{{ viewData.title }}</el-descriptions-item>
<el-descriptions-item label="文章分类">
<el-tag :type="getCategoryTagType(viewData.category)">
{{ viewData.category }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="文章状态">
<el-tag :type="getArticleStatusTagType(viewData.articleStatus)">
{{ getArticleStatusLabel(viewData.articleStatus) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="审核状态">
<el-tag :type="getAuditStatusTagType(viewData.auditStatus)">
{{ getAuditStatusLabel(viewData.auditStatus) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="审核意见" :span="2">
{{ viewData.auditComment || '无' }}
</el-descriptions-item>
<el-descriptions-item label="创建人">{{ viewData.createBy }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ viewData.createTime }}</el-descriptions-item>
</el-descriptions>
<el-divider>文章内容</el-divider>
<div class="content-view" v-html="viewData.content"></div>
<div slot="footer" class="dialog-footer">
<el-button @click="viewOpen = false"> </el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
// APIuploadKnowledgepublishKnowledge
import { listKnowledge, getKnowledge, delKnowledge, addKnowledge, updateKnowledge, uploadKnowledge, publishKnowledge } from "@/api/vet/knowledge"
import {
listKnowledge,
getKnowledge,
delKnowledge,
addKnowledge,
updateKnowledge,
submitForAudit,
auditVetKnowledge,
publishKnowledge,
onlineVetKnowledge,
offlineVetKnowledge
} from "@/api/vet/knowledge"
import { getDicts } from "@/api/system/dict/data" //
export default { export default {
name: "Knowledge", name: "Knowledge",
@ -201,16 +370,33 @@ export default {
title: "", title: "",
// //
open: false, open: false,
//
auditOpen: false,
//
viewOpen: false,
// //
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
title: null, title: null,
category: null, category: null,
status: null, //
articleStatus: null,
auditStatus: null,
}, },
// //
form: {}, form: {},
//
auditForm: {
id: null,
auditStatus: "2", //
auditComment: ""
},
//
viewData: {},
//
categoryOptions: [], //
articleStatusOptions: [], //
auditStatusOptions: [], //
// //
rules: { rules: {
title: [ title: [
@ -226,23 +412,124 @@ export default {
} }
}, },
created() { created() {
this.getDictsData() //
this.getList() this.getList()
}, },
methods: { methods: {
/** 获取字典数据 */
getDictsData() {
//
getDicts("article_category").then(response => {
this.categoryOptions = response.data
}).catch(error => {
console.error("获取文章分类字典失败:", error)
this.categoryOptions = []
})
//
getDicts("article_status").then(response => {
this.articleStatusOptions = response.data
}).catch(error => {
console.error("获取文章状态字典失败:", error)
this.articleStatusOptions = []
})
//
getDicts("vet_audit_status").then(response => {
this.auditStatusOptions = response.data
}).catch(error => {
console.error("获取审核状态字典失败:", error)
this.auditStatusOptions = []
})
},
/** 查询兽医文章列表 */ /** 查询兽医文章列表 */
getList() { getList() {
this.loading = true this.loading = true
listKnowledge(this.queryParams).then(response => { listKnowledge(this.queryParams).then(response => {
this.knowledgeList = response.rows
//
const list = response.rows.map(item => ({
...item,
articleStatus: String(item.articleStatus || '0'),
auditStatus: String(item.auditStatus || '0')
}))
this.knowledgeList = list
this.total = response.total this.total = response.total
this.loading = false this.loading = false
}).catch(error => {
console.error("获取文章列表失败:", error)
this.loading = false
this.$modal.msgError("获取文章列表失败")
}) })
}, },
//
getCategoryTagType(category) {
const map = {
'治疗防治': 'primary', //
'饲养管理': 'success', // 绿
'其他': 'info' //
}
return map[category] || 'info'
},
//
getArticleStatusTagType(status) {
const map = {
'0': 'info', // 稿 -
'1': 'success', // - 绿
'2': 'warning' // -
}
return map[status] || 'info'
},
//
getArticleStatusLabel(status) {
const map = {
'0': '草稿',
'1': '已发布',
'2': '已下架'
}
return map[status] || status
},
//
getAuditStatusTagType(status) {
const map = {
'0': 'info', // -
'1': 'warning', // -
'2': 'success', // - 绿
'3': 'danger', // -
'4': 'danger' // -
}
return map[status] || 'info'
},
//
getAuditStatusLabel(status) {
const map = {
'0': '待审核',
'1': '审核中',
'2': '审核通过',
'3': '审核驳回',
'4': '敏感内容驳回'
}
return map[status] || status
},
// //
cancel() { cancel() {
this.open = false this.open = false
this.reset() this.reset()
}, },
//
cancelAudit() {
this.auditOpen = false
this.resetAuditForm()
},
// //
reset() { reset() {
this.form = { this.form = {
@ -250,7 +537,9 @@ export default {
title: null, title: null,
content: null, content: null,
category: null, category: null,
status: null, //
articleStatus: "0", // 稿
auditStatus: "0", //
auditComment: null,
createBy: null, createBy: null,
createTime: null, createTime: null,
updateBy: null, updateBy: null,
@ -259,11 +548,25 @@ export default {
} }
this.resetForm("form") this.resetForm("form")
}, },
//
resetAuditForm() {
this.auditForm = {
id: null,
auditStatus: "2",
auditComment: ""
}
if (this.$refs.auditForm) {
this.$refs.auditForm.resetFields()
}
},
/** 搜索按钮操作 */ /** 搜索按钮操作 */
handleQuery() { handleQuery() {
this.queryParams.pageNum = 1 this.queryParams.pageNum = 1
this.getList() this.getList()
}, },
/** 重置按钮操作 */ /** 重置按钮操作 */
resetQuery() { resetQuery() {
this.resetForm("queryForm") this.resetForm("queryForm")
@ -272,48 +575,185 @@ export default {
pageSize: 10, pageSize: 10,
title: null, title: null,
category: null, category: null,
status: null,
articleStatus: null,
auditStatus: null,
} }
this.handleQuery() this.handleQuery()
}, },
// //
handleSelectionChange(selection) { handleSelectionChange(selection) {
this.ids = selection.map(item => item.id) this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.single = selection.length !== 1
this.multiple = !selection.length this.multiple = !selection.length
}, },
/** 上传文章按钮操作(替代原新增) */
handleUpload() {
/** 新增文章按钮操作 */
handleAdd() {
this.reset() this.reset()
this.open = true this.open = true
this.title = "上传兽医文章"
this.title = "新增文章"
},
/** 提交审核按钮操作 */
handleSubmitAudit(id) {
const articleId = id || this.ids[0]
if (!articleId) {
this.$modal.msgWarning("请选择需要提交审核的文章")
return
}
this.$modal.confirm('是否确认提交文章进行审核?').then(() => {
return submitForAudit(articleId)
}).then(response => {
this.getList()
this.$modal.msgSuccess(response.msg || "提交审核成功")
}).catch(() => {})
},
/** 打开审核对话框 */
handleAuditDialog(id) {
const articleId = id || this.ids[0]
if (!articleId) {
this.$modal.msgWarning("请选择需要审核的文章")
return
}
this.auditForm.id = articleId
this.auditOpen = true
},
/** 提交审核 */
submitAudit() {
this.$refs["auditForm"].validate(valid => {
if (valid) {
auditVetKnowledge(this.auditForm.id, this.auditForm.auditStatus, this.auditForm.auditComment).then(response => {
this.$modal.msgSuccess(response.msg || "审核成功")
this.auditOpen = false
this.getList()
this.resetAuditForm()
})
}
})
}, },
/** 发布文章按钮操作 */ /** 发布文章按钮操作 */
handlePublish(id) { handlePublish(id) {
// /
const ids = id || this.ids
if (ids.length === 0) {
const articleId = id || this.ids[0]
if (!articleId) {
this.$modal.msgWarning("请选择需要发布的文章") this.$modal.msgWarning("请选择需要发布的文章")
return return
} }
this.$modal.confirm('是否确认发布选中的文章?').then(() => {
return publishKnowledge(ids)
}).then(() => {
this.$modal.confirm('是否确认发布文章?').then(() => {
return publishKnowledge(articleId)
}).then(response => {
this.getList() this.getList()
this.$modal.msgSuccess("发布成功")
this.$modal.msgSuccess(response.msg || "发布成功")
}).catch(() => {}) }).catch(() => {})
}, },
/** 上架文章操作 */
handleOnline(row) {
const articleId = row.id || this.ids[0]
if (!articleId) {
this.$modal.msgWarning("请选择需要上架的文章")
return
}
this.$confirm('确认要上架此文章吗?', '上架确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.loading = true
onlineVetKnowledge(articleId).then(response => {
this.loading = false
if (response.code === 200) {
this.$modal.msgSuccess('上架成功')
this.getList() //
} else {
this.$modal.msgError(response.msg || '上架失败')
}
}).catch(error => {
this.loading = false
this.$modal.msgError(error.msg || '上架失败')
})
}).catch(() => {
//
})
},
/** 下架文章操作 */
handleOffline(row) {
const articleId = row.id || this.ids[0]
if (!articleId) {
this.$modal.msgWarning("请选择需要下架的文章")
return
}
this.$prompt('请输入下架原因', '下架确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPlaceholder: '请输入下架原因(如:内容过时、违规操作等)',
inputValidator: (value) => {
if (!value || value.trim().length < 2) {
return '下架原因不能少于2个字'
}
if (value.trim().length > 200) {
return '下架原因不能超过200字'
}
return true
}
}).then(({ value }) => {
this.loading = true
offlineVetKnowledge(articleId, value.trim()).then(response => {
this.loading = false
if (response.code === 200) {
this.$modal.msgSuccess('下架成功')
this.getList() //
} else {
this.$modal.msgError(response.msg || '下架失败')
}
}).catch(error => {
this.loading = false
this.$modal.msgError(error.msg || '下架失败')
})
}).catch(() => {
//
})
},
/** 修改按钮操作 */ /** 修改按钮操作 */
handleUpdate(row) { handleUpdate(row) {
this.reset() this.reset()
const id = row.id || this.ids
const id = row.id || this.ids[0]
getKnowledge(id).then(response => { getKnowledge(id).then(response => {
this.form = response.data
const data = response.data
//
this.form = {
...data,
articleStatus: String(data.articleStatus || '0'),
auditStatus: String(data.auditStatus || '0')
}
this.open = true this.open = true
this.title = "修改兽医文章"
this.title = "修改文章"
}) })
}, },
/** 提交按钮(区分上传/修改) */
/** 查看按钮操作 */
handleView(row) {
const id = row.id || this.ids[0]
getKnowledge(id).then(response => {
const data = response.data
//
this.viewData = {
...data,
articleStatus: String(data.articleStatus || '0'),
auditStatus: String(data.auditStatus || '0')
}
this.viewOpen = true
})
},
/** 提交按钮(区分新增/修改) */
submitForm() { submitForm() {
this.$refs["form"].validate(valid => { this.$refs["form"].validate(valid => {
if (valid) { if (valid) {
@ -325,9 +765,9 @@ export default {
this.getList() this.getList()
}) })
} else { } else {
//
uploadKnowledge(this.form).then(response => {
this.$modal.msgSuccess(response.msg || "上传成功")
//
addKnowledge(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false this.open = false
this.getList() this.getList()
}) })
@ -335,16 +775,18 @@ export default {
} }
}) })
}, },
/** 删除按钮操作 */ /** 删除按钮操作 */
handleDelete(row) { handleDelete(row) {
const ids = row.id || this.ids const ids = row.id || this.ids
this.$modal.confirm('是否确认删除兽医文章编号为"' + ids + '"的数据项?').then(() => {
this.$modal.confirm('是否确认删除选中的文章?').then(() => {
return delKnowledge(ids) return delKnowledge(ids)
}).then(() => { }).then(() => {
this.getList() this.getList()
this.$modal.msgSuccess("删除成功") this.$modal.msgSuccess("删除成功")
}).catch(() => {}) }).catch(() => {})
}, },
/** 导出按钮操作 */ /** 导出按钮操作 */
handleExport() { handleExport() {
this.download('vet/knowledge/export', { this.download('vet/knowledge/export', {
@ -354,3 +796,14 @@ export default {
} }
} }
</script> </script>
<style scoped>
.content-view {
padding: 10px;
border: 1px solid #ebeef5;
border-radius: 4px;
min-height: 200px;
max-height: 400px;
overflow-y: auto;
}
</style>

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

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

Loading…
Cancel
Save