Browse Source

兽医消息通知功能

master
ChaiNingQi 2 months ago
parent
commit
6302326a79
  1. 90
      chenhai-admin/src/main/java/com/chenhai/web/controller/vet/CertificateTestController.java
  2. 56
      chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetCertificateController.java
  3. 103
      chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetNotificationController.java
  4. 21
      chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetPersonalInfoController.java
  5. 2
      chenhai-admin/src/main/resources/application.yml
  6. 55
      chenhai-system/src/main/java/com/chenhai/vet/CertificateRemindTask.java
  7. 8
      chenhai-system/src/main/java/com/chenhai/vet/domain/VetCertificate.java
  8. 161
      chenhai-system/src/main/java/com/chenhai/vet/domain/VetNotification.java
  9. 14
      chenhai-system/src/main/java/com/chenhai/vet/domain/VetPersonalInfo.java
  10. 120
      chenhai-system/src/main/java/com/chenhai/vet/mapper/VetNotificationMapper.java
  11. 1
      chenhai-system/src/main/java/com/chenhai/vet/mapper/VetPersonalInfoMapper.java
  12. 64
      chenhai-system/src/main/java/com/chenhai/vet/service/IVetCertificateService.java
  13. 1
      chenhai-system/src/main/java/com/chenhai/vet/service/IVetPersonalInfoService.java
  14. 64
      chenhai-system/src/main/java/com/chenhai/vet/service/VetNotificationService.java
  15. 330
      chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetCertificateServiceImpl.java
  16. 178
      chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetNotificationServiceImpl.java
  17. 5
      chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetPersonalInfoServiceImpl.java
  18. 182
      chenhai-system/src/main/resources/mapper/vet/VetNotificationMapper.xml
  19. 44
      chenhai-system/src/main/resources/mapper/vet/VetPersonalInfoMapper.xml
  20. 44
      chenhai-ui/src/api/vet/notification.js
  21. 4
      chenhai-ui/src/views/vet/certificate/index.vue
  22. 340
      chenhai-ui/src/views/vet/notification/index.vue
  23. 2
      chenhai-ui/vue.config.js

90
chenhai-admin/src/main/java/com/chenhai/web/controller/vet/CertificateTestController.java

@ -0,0 +1,90 @@
/*
package com.chenhai.vet.controller;
import com.chenhai.common.core.controller.BaseController;
import com.chenhai.common.core.domain.AjaxResult;
import com.chenhai.vet.domain.VetCertificate;
import com.chenhai.vet.service.IVetCertificateService;
import com.chenhai.vet.service.VetNotificationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
*/
/**
* 证书提醒测试控制器
*//*
@RestController
@RequestMapping("/test/certificate")
public class CertificateTestController extends BaseController {
@Autowired
private IVetCertificateService vetCertificateService;
@Autowired
private VetNotificationService vetNotificationService;
*/
/**
* 测试单个证书提醒
*//*
@GetMapping("/test-notify/{certId}")
public AjaxResult testNotify(@PathVariable Long certId) {
try {
logger.info("🎯 测试证书提醒,证书ID: {}", certId);
// 1. 获取证书
VetCertificate cert = vetCertificateService.selectVetCertificateById(certId);
if (cert == null) {
return error("证书不存在: " + certId);
}
// 2. 直接调用通知服务
logger.info("📨 调用通知服务,证书: {}", cert.getCertName());
vetNotificationService.sendCertificateExpireRemind(cert);
logger.info("✅ 通知服务调用完成");
// 3. 返回结果
return success("测试完成,证书ID: " + certId +
",证书名称: " + cert.getCertName() +
",请检查数据库通知表");
} catch (Exception e) {
logger.error("测试失败", e);
return error("测试失败: " + e.getMessage());
}
}
*/
/**
* 手动触发定时任务
*//*
@GetMapping("/trigger-task")
public AjaxResult triggerTask() {
try {
logger.info("🚀 手动触发证书检查任务");
vetCertificateService.checkAndSendCertificateReminders();
return success("定时任务已手动触发,请查看控制台日志");
} catch (Exception e) {
logger.error("触发定时任务失败", e);
return error("触发失败: " + e.getMessage());
}
}
*/
/**
* 检查通知表数据
*//*
@GetMapping("/check-notifications")
public AjaxResult checkNotifications() {
try {
// 这里需要查询通知表暂时返回简单信息
return success("请通过SQL查询通知表: SELECT * FROM vet_notification ORDER BY create_time DESC LIMIT 10");
} catch (Exception e) {
return error("检查失败: " + e.getMessage());
}
}
}*/

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

@ -14,6 +14,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 兽医执业证书Controller
@ -22,7 +23,7 @@ import java.util.List;
* @date 2025-12-29
*/
@RestController
@RequestMapping("/system/certificate")
@RequestMapping("/vet/certificate")
public class VetCertificateController extends BaseController
{
@Autowired
@ -31,7 +32,7 @@ public class VetCertificateController extends BaseController
/**
* 查询兽医执业证书列表
*/
@PreAuthorize("@ss.hasPermi('system:certificate:list')")
@PreAuthorize("@ss.hasPermi('vet:certificate:list')")
@GetMapping("/list")
public TableDataInfo list(VetCertificate vetCertificate)
{
@ -43,7 +44,7 @@ public class VetCertificateController extends BaseController
/**
* 导出兽医执业证书列表
*/
@PreAuthorize("@ss.hasPermi('system:certificate:export')")
@PreAuthorize("@ss.hasPermi('vet:certificate:export')")
@Log(title = "兽医执业证书", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, VetCertificate vetCertificate)
@ -56,7 +57,7 @@ public class VetCertificateController extends BaseController
/**
* 获取兽医执业证书详细信息
*/
@PreAuthorize("@ss.hasPermi('system:certificate:query')")
@PreAuthorize("@ss.hasPermi('vet:certificate:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
@ -66,7 +67,7 @@ public class VetCertificateController extends BaseController
/**
* 新增兽医执业证书
*/
@PreAuthorize("@ss.hasPermi('system:certificate:add')")
@PreAuthorize("@ss.hasPermi('vet:certificate:add')")
@Log(title = "兽医执业证书", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody VetCertificate vetCertificate)
@ -77,7 +78,7 @@ public class VetCertificateController extends BaseController
/**
* 修改兽医执业证书
*/
@PreAuthorize("@ss.hasPermi('system:certificate:edit')")
@PreAuthorize("@ss.hasPermi('vet:certificate:edit')")
@Log(title = "兽医执业证书", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody VetCertificate vetCertificate)
@ -88,11 +89,52 @@ public class VetCertificateController extends BaseController
/**
* 删除兽医执业证书
*/
@PreAuthorize("@ss.hasPermi('system:certificate:remove')")
@PreAuthorize("@ss.hasPermi('vet:certificate:remove')")
@Log(title = "兽医执业证书", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(vetCertificateService.deleteVetCertificateByIds(ids));
}
/**
* 根据用户ID获取证书列表
*/
@GetMapping("/user/{userId}")
public AjaxResult getByUserId(@PathVariable Long userId)
{
List<VetCertificate> list = vetCertificateService.selectCertificatesByUserId(userId);
return success(list);
}
/**
* 获取即将过期的证书
*/
@GetMapping("/expiring/{userId}")
public AjaxResult getExpiringCertificates(@PathVariable Long userId)
{
List<VetCertificate> list = vetCertificateService.selectExpiringCertificates(userId);
return success(list);
}
/**
* 获取证书统计信息
*/
@GetMapping("/statistics/{userId}")
public AjaxResult getStatistics(@PathVariable Long userId)
{
Map<String, Object> statistics = vetCertificateService.getCertificateStatistics(userId);
return success(statistics);
}
/**
* 手动触发证书检查
*/
@PostMapping("/manual-check/{userId}")
public AjaxResult manualCheck(@PathVariable Long userId)
{
vetCertificateService.manualCheckCertificates(userId);
return success("检查完成");
}
}

103
chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetNotificationController.java

@ -0,0 +1,103 @@
package com.chenhai.web.controller.vet;
import java.util.List;
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.vet.domain.VetNotification;
import com.chenhai.vet.service.VetNotificationService;
import com.chenhai.common.utils.poi.ExcelUtil;
import com.chenhai.common.core.page.TableDataInfo;
/**
* 兽医通知Controller
*
* @author ruoyi
* @date 2025-12-29
*/
@RestController
@RequestMapping("/vet/notification")
public class VetNotificationController extends BaseController
{
@Autowired
private VetNotificationService vetNotificationService;
/**
* 查询兽医通知列表
*/
@PreAuthorize("@ss.hasPermi('vet:notification:list')")
@GetMapping("/list")
public TableDataInfo list(VetNotification vetNotification)
{
startPage();
List<VetNotification> list = vetNotificationService.selectVetNotificationList(vetNotification);
return getDataTable(list);
}
/**
* 导出兽医通知列表
*/
@PreAuthorize("@ss.hasPermi('vet:notification:export')")
@Log(title = "兽医通知", businessType = BusinessType.EXPORT)
@GetMapping("/export")
public AjaxResult export(VetNotification vetNotification)
{
List<VetNotification> list = vetNotificationService.selectVetNotificationList(vetNotification);
ExcelUtil<VetNotification> util = new ExcelUtil<VetNotification>(VetNotification.class);
return util.exportExcel(list, "兽医通知数据");
}
/**
* 获取兽医通知详细信息
*/
@PreAuthorize("@ss.hasPermi('vet:notification:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(vetNotificationService.selectVetNotificationById(id));
}
/**
* 新增兽医通知
*/
@PreAuthorize("@ss.hasPermi('vet:notification:add')")
@Log(title = "兽医通知", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody VetNotification vetNotification)
{
return toAjax(vetNotificationService.insertVetNotification(vetNotification));
}
/**
* 修改兽医通知
*/
@PreAuthorize("@ss.hasPermi('vet:notification:edit')")
@Log(title = "兽医通知", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody VetNotification vetNotification)
{
return toAjax(vetNotificationService.updateVetNotification(vetNotification));
}
/**
* 删除兽医通知
*/
@PreAuthorize("@ss.hasPermi('vet:notification:remove')")
@Log(title = "兽医通知", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(vetNotificationService.deleteVetNotificationByIds(ids));
}
}

21
chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetPersonalInfoController.java

@ -42,27 +42,6 @@ public class VetPersonalInfoController extends BaseController
return getDataTable(list);
}
/**
* 查询兽医个人信息列表包含证书信息
*/
@PreAuthorize("@ss.hasPermi('vet:info:list')")
@GetMapping("/listWithCertificates")
public TableDataInfo listWithCertificates(VetPersonalInfo vetPersonalInfo) {
startPage();
List<VetPersonalInfo> list = vetPersonalInfoService.selectVetPersonalInfoWithCertificates(vetPersonalInfo);
return getDataTable(list);
}
/**
* 导出兽医个人信息列表包含证书信息
*/
@PreAuthorize("@ss.hasPermi('vet:info:export')")
@Log(title = "兽医个人信息(含证书)", businessType = BusinessType.EXPORT)
@PostMapping("/exportWithCertificates")
public void exportWithCertificates(HttpServletResponse response, VetPersonalInfo vetPersonalInfo) {
List<VetPersonalInfo> list = vetPersonalInfoService.selectVetPersonalInfoWithCertificates(vetPersonalInfo);
ExcelUtil<VetPersonalInfo> util = new ExcelUtil<VetPersonalInfo>(VetPersonalInfo.class);
util.exportExcel(response, list, "兽医个人信息及证书数据");
}
/**
* 获取兽医完整信息包含证书详情

2
chenhai-admin/src/main/resources/application.yml

@ -16,7 +16,7 @@ chenhai:
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
port: 8081
port: 8082
servlet:
# 应用的访问路径
context-path: /

55
chenhai-system/src/main/java/com/chenhai/vet/CertificateRemindTask.java

@ -0,0 +1,55 @@
package com.chenhai.vet;
import com.chenhai.vet.domain.VetCertificate;
import com.chenhai.vet.service.IVetCertificateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 证书提醒定时任务
*
* @author ruoyi
*/
@Component
@EnableScheduling
@Slf4j
public class CertificateRemindTask {
@Autowired
private IVetCertificateService vetCertificateService;
/**
* 每天凌晨2点检查证书过期情况
*/
/* @Scheduled(cron = "0 0 2 * * ?")*/
@Scheduled(cron = "0 */1 * * * ?")
public void dailyCertificateCheck() {
log.info("开始执行每日证书检查任务...");
try {
vetCertificateService.checkAndSendCertificateReminders();
log.info("每日证书检查任务执行完成");
} catch (Exception e) {
log.error("每日证书检查任务执行失败", e);
}
}
/* *//**
* 每小时检查一次可选用于及时更新状态
*//*
@Scheduled(cron = "0 0 * * * ?")
public void hourlyCertificateCheck() {
log.debug("执行每小时证书状态检查...");
try {
// 更新所有证书状态
vetCertificateService.selectVetCertificateList(new VetCertificate())
.forEach(cert -> {
// 这里可以调用更新状态的方法
});
} catch (Exception e) {
log.error("每小时证书检查失败", e);
}
}*/
}

8
chenhai-system/src/main/java/com/chenhai/vet/domain/VetCertificate.java

@ -56,12 +56,12 @@ public class VetCertificate extends BaseEntity
private String certImage;
/** 状态(0正常 1即将过期 2已过期) */
@Excel(name = "状态", readConverterExp = "0=正常,1=即将过期,2=已过期")
@Excel(name = "状态", dictType = "certificate_status")
private String status;
/** 提前提醒天数 */
@Excel(name = "提前提醒天数")
private Long remindDays;
private Integer remindDays;
/** 上次提醒时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@ -168,12 +168,12 @@ public class VetCertificate extends BaseEntity
return status;
}
public void setRemindDays(Long remindDays)
public void setRemindDays(Integer remindDays)
{
this.remindDays = remindDays;
}
public Long getRemindDays()
public Integer getRemindDays()
{
return remindDays;
}

161
chenhai-system/src/main/java/com/chenhai/vet/domain/VetNotification.java

@ -0,0 +1,161 @@
package com.chenhai.vet.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;
/**
* 兽医通知对象 vet_notification
*
* @author ruoyi
* @date 2026-01-04
*/
public class VetNotification extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 接收用户ID */
@Excel(name = "接收用户ID")
private Long userId;
/** 通知标题 */
@Excel(name = "通知标题")
private String title;
/** 通知内容 */
@Excel(name = "通知内容")
private String content;
/** 通知类型 */
@Excel(name = "通知类型")
private String type;
/** 关联ID */
@Excel(name = "关联ID")
private String relatedId;
/** 是否已读 0:未读 1:已读 */
@Excel(name = "是否已读 0:未读 1:已读")
private Integer isRead;
/** 提醒级别 1:低 2:中 3:高 */
@Excel(name = "提醒级别 1:低 2:中 3:高")
private Integer remindLevel;
/** 阅读时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "阅读时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date readTime;
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 setContent(String content)
{
this.content = content;
}
public String getContent()
{
return content;
}
public void setType(String type)
{
this.type = type;
}
public String getType()
{
return type;
}
public void setRelatedId(String relatedId)
{
this.relatedId = relatedId;
}
public String getRelatedId()
{
return relatedId;
}
public void setIsRead(Integer isRead)
{
this.isRead = isRead;
}
public Integer getIsRead()
{
return isRead;
}
public void setRemindLevel(Integer remindLevel)
{
this.remindLevel = remindLevel;
}
public Integer getRemindLevel()
{
return remindLevel;
}
public void setReadTime(Date readTime)
{
this.readTime = readTime;
}
public Date getReadTime()
{
return readTime;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("userId", getUserId())
.append("title", getTitle())
.append("content", getContent())
.append("type", getType())
.append("relatedId", getRelatedId())
.append("isRead", getIsRead())
.append("remindLevel", getRemindLevel())
.append("createTime", getCreateTime())
.append("readTime", getReadTime())
.toString();
}
}

14
chenhai-system/src/main/java/com/chenhai/vet/domain/VetPersonalInfo.java

@ -57,6 +57,9 @@ public class VetPersonalInfo extends BaseEntity
/** 联系地址 */
@Excel(name = "联系地址")
private String address;
/** 联系方式 */
@Excel(name = "联系方式")
private String iphone;
/** 个人简介 */
@Excel(name = "个人简介")
@ -186,6 +189,16 @@ public class VetPersonalInfo extends BaseEntity
return address;
}
public void setIphone(String address)
{
this.iphone = iphone;
}
public String getIphone()
{
return iphone;
}
public void setIntroduction(String introduction)
{
this.introduction = introduction;
@ -209,6 +222,7 @@ public class VetPersonalInfo extends BaseEntity
.append("workExperience", getWorkExperience())
.append("hospital", getHospital())
.append("address", getAddress())
.append("iphone", getIphone())
.append("introduction", getIntroduction())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())

120
chenhai-system/src/main/java/com/chenhai/vet/mapper/VetNotificationMapper.java

@ -0,0 +1,120 @@
// VetNotificationMapper.java
package com.chenhai.vet.mapper;
import com.chenhai.vet.domain.VetNotification;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 兽医通知Mapper接口
*
* @author ruoyi
* @date 2025-12-29
*/
public interface VetNotificationMapper
{
/**
* 查询兽医通知
*
* @param id 兽医通知主键
* @return 兽医通知
*/
public VetNotification selectVetNotificationById(Long id);
/**
* 查询兽医通知列表
*
* @param vetNotification 兽医通知
* @return 兽医通知集合
*/
public List<VetNotification> selectVetNotificationList(VetNotification vetNotification);
/**
* 新增兽医通知
*
* @param vetNotification 兽医通知
* @return 结果
*/
public int insertVetNotification(VetNotification vetNotification);
/**
* 修改兽医通知
*
* @param vetNotification 兽医通知
* @return 结果
*/
public int updateVetNotification(VetNotification vetNotification);
/**
* 删除兽医通知
*
* @param id 兽医通知主键
* @return 结果
*/
public int deleteVetNotificationById(Long id);
/**
* 批量删除兽医通知
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteVetNotificationByIds(Long[] ids);
/**
* 根据用户ID查询未读通知列表
*
* @param userId 用户ID
* @return 通知列表
*/
public List<VetNotification> selectUnreadNotificationsByUserId(Long userId);
/**
* 根据用户ID查询证书相关通知列表
*
* @param userId 用户ID
* @return 通知列表
*/
public List<VetNotification> selectCertificateNotificationsByUserId(Long userId);
/**
* 标记通知为已读
*
* @param id 通知ID
* @return 结果
*/
public int markNotificationAsRead(Long id);
/**
* 标记用户所有通知为已读
*
* @param userId 用户ID
* @return 结果
*/
public int markAllNotificationsAsRead(Long userId);
/**
* 查询用户未读通知数量
*
* @param userId 用户ID
* @return 未读通知数量
*/
public Integer countUnreadNotificationsByUserId(Long userId);
/**
* 批量插入通知
*
* @param notifications 通知列表
* @return 结果
*/
public int batchInsertVetNotifications(@Param("list") List<VetNotification> notifications);
/**
* 清理过期通知
*
* @param days 过期天数
* @return 清理数量
*/
public int cleanExpiredNotifications(@Param("days") Integer days);
}

1
chenhai-system/src/main/java/com/chenhai/vet/mapper/VetPersonalInfoMapper.java

@ -59,5 +59,4 @@ public interface VetPersonalInfoMapper
* @return 结果
*/
public int deleteVetPersonalInfoByIds(Long[] ids);
public List<VetPersonalInfo> selectWithCertificates(VetPersonalInfo vetPersonalInfo);
}

64
chenhai-system/src/main/java/com/chenhai/vet/service/IVetCertificateService.java

@ -1,62 +1,66 @@
package com.chenhai.vet.service;
import com.chenhai.vet.domain.VetCertificate;
import java.util.List;
import java.util.Map;
/**
* 兽医执业证书Service接口
*
* @author ruoyi
* @date 2025-12-29
*/
public interface IVetCertificateService
public interface IVetCertificateService
{
/**
* 查询兽医执业证书
*
* @param id 兽医执业证书主键
* @return 兽医执业证书
*/
public VetCertificate selectVetCertificateById(Long id);
VetCertificate selectVetCertificateById(Long id);
/**
* 查询兽医执业证书列表
*
* @param vetCertificate 兽医执业证书
* @return 兽医执业证书集合
*/
public List<VetCertificate> selectVetCertificateList(VetCertificate vetCertificate);
List<VetCertificate> selectVetCertificateList(VetCertificate vetCertificate);
/**
* 新增兽医执业证书
*
* @param vetCertificate 兽医执业证书
* @return 结果
*/
public int insertVetCertificate(VetCertificate vetCertificate);
int insertVetCertificate(VetCertificate vetCertificate);
/**
* 修改兽医执业证书
*
* @param vetCertificate 兽医执业证书
* @return 结果
*/
public int updateVetCertificate(VetCertificate vetCertificate);
int updateVetCertificate(VetCertificate vetCertificate);
/**
* 批量删除兽医执业证书
*
* @param ids 需要删除的兽医执业证书主键集合
* @return 结果
*/
public int deleteVetCertificateByIds(Long[] ids);
int deleteVetCertificateByIds(Long[] ids);
/**
* 删除兽医执业证书信息
*
* @param id 兽医执业证书主键
* @return 结果
*/
public int deleteVetCertificateById(Long id);
}
int deleteVetCertificateById(Long id);
/**
* 根据用户ID查询证书列表
*/
List<VetCertificate> selectCertificatesByUserId(Long userId);
/**
* 获取即将过期的证书30天内
*/
List<VetCertificate> selectExpiringCertificates(Long userId);
/**
* 检查并发送证书过期提醒
*/
void checkAndSendCertificateReminders();
/**
* 手动触发证书检查
*/
void manualCheckCertificates(Long userId);
/**
* 获取证书统计信息
*/
Map<String, Object> getCertificateStatistics(Long userId);
}

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

@ -60,7 +60,6 @@ public interface IVetPersonalInfoService
* @return 结果
*/
public int deleteVetPersonalInfoById(Long id);
List<VetPersonalInfo> selectVetPersonalInfoWithCertificates(VetPersonalInfo vetPersonalInfo);
/**
* 获取兽医的完整信息包含证书详情

64
chenhai-system/src/main/java/com/chenhai/vet/service/VetNotificationService.java

@ -0,0 +1,64 @@
// VetNotificationService.java
package com.chenhai.vet.service;
import com.chenhai.vet.domain.VetCertificate;
import com.chenhai.vet.domain.VetNotification;
import java.util.List;
public interface VetNotificationService {
/**
* 根据ID查询通知
*/
VetNotification selectVetNotificationById(Long id);
/**
* 查询通知列表
*/
List<VetNotification> selectVetNotificationList(VetNotification vetNotification);
/**
* 新增通知
*/
int insertVetNotification(VetNotification vetNotification);
/**
* 修改通知
*/
int updateVetNotification(VetNotification vetNotification);
/**
* 删除通知
*/
int deleteVetNotificationById(Long id);
/**
* 批量删除通知
*/
int deleteVetNotificationByIds(Long[] ids);
/**
* 获取用户通知列表
*/
List<VetNotification> getNotificationsByUserId(Long userId);
/**
* 获取未读通知数量
*/
int getUnreadCount(Long userId);
/**
* 发送证书过期提醒通知
*/
void sendCertificateExpireRemind(VetCertificate certificate);
/**
* 标记通知为已读
*/
boolean markAsRead(Long notificationId);
/**
* 标记所有通知为已读
*/
boolean markAllAsRead(Long userId);
}

330
chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetCertificateServiceImpl.java

@ -4,94 +4,344 @@ import com.chenhai.common.utils.DateUtils;
import com.chenhai.vet.domain.VetCertificate;
import com.chenhai.vet.mapper.VetCertificateMapper;
import com.chenhai.vet.service.IVetCertificateService;
import com.chenhai.vet.service.VetNotificationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 兽医执业证书Service业务层处理
*
* @author ruoyi
* @date 2025-12-29
*/
@Service
public class VetCertificateServiceImpl implements IVetCertificateService
{
@Autowired
private VetCertificateMapper vetCertificateMapper;
@Autowired
private VetNotificationService vetNotificationService;
/**
* 查询兽医执业证书
*
* @param id 兽医执业证书主键
* @return 兽医执业证书
*/
@Override
public VetCertificate selectVetCertificateById(Long id)
{
return vetCertificateMapper.selectVetCertificateById(id);
}
/**
* 查询兽医执业证书列表
*
* @param vetCertificate 兽医执业证书
* @return 兽医执业证书
*/
@Override
public List<VetCertificate> selectVetCertificateList(VetCertificate vetCertificate)
{
return vetCertificateMapper.selectVetCertificateList(vetCertificate);
}
/**
* 新增兽医执业证书
*
* @param vetCertificate 兽医执业证书
* @return 结果
*/
@Override
public int insertVetCertificate(VetCertificate vetCertificate)
{
vetCertificate.setCreateTime(DateUtils.getNowDate());
// 验证并设置证书状态
updateCertificateStatus(vetCertificate);
// 设置默认提醒天数
if (vetCertificate.getRemindDays() == null) {
vetCertificate.setRemindDays(30);
}
return vetCertificateMapper.insertVetCertificate(vetCertificate);
}
/**
* 修改兽医执业证书
*
* @param vetCertificate 兽医执业证书
* @return 结果
*/
@Override
public int updateVetCertificate(VetCertificate vetCertificate)
{
vetCertificate.setUpdateTime(DateUtils.getNowDate());
// 验证并更新证书状态
updateCertificateStatus(vetCertificate);
return vetCertificateMapper.updateVetCertificate(vetCertificate);
}
/**
* 批量删除兽医执业证书
*
* @param ids 需要删除的兽医执业证书主键
* @return 结果
*/
@Override
public int deleteVetCertificateByIds(Long[] ids)
{
return vetCertificateMapper.deleteVetCertificateByIds(ids);
}
/**
* 删除兽医执业证书信息
*
* @param id 兽医执业证书主键
* @return 结果
*/
@Override
public int deleteVetCertificateById(Long id)
{
return vetCertificateMapper.deleteVetCertificateById(id);
}
}
@Override
public List<VetCertificate> selectCertificatesByUserId(Long userId)
{
VetCertificate query = new VetCertificate();
query.setUserId(userId);
return vetCertificateMapper.selectVetCertificateList(query);
}
@Override
public List<VetCertificate> selectExpiringCertificates(Long userId)
{
Date today = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(today);
calendar.add(Calendar.DAY_OF_YEAR, 30);
Date thirtyDaysLater = calendar.getTime();
// 先获取用户所有证书
List<VetCertificate> allCertificates = selectCertificatesByUserId(userId);
// 过滤出即将过期的证书
return allCertificates.stream()
.filter(cert -> cert.getExpireDate() != null &&
!cert.getExpireDate().before(today) &&
!cert.getExpireDate().after(thirtyDaysLater))
.toList();
}
@Override
public void checkAndSendCertificateReminders()
{
Date today = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(today);
calendar.add(Calendar.DAY_OF_YEAR, 30);
Date thresholdDate = calendar.getTime(); // 30天后过期
calendar.setTime(today);
calendar.add(Calendar.DAY_OF_YEAR, -7);
Date remindDate = calendar.getTime(); // 7天前提醒过的不再提醒
// 查询所有证书
VetCertificate query = new VetCertificate();
List<VetCertificate> allCertificates = vetCertificateMapper.selectVetCertificateList(query);
int successCount = 0;
int failCount = 0;
for (VetCertificate certificate : allCertificates) {
try {
// 检查证书是否需要提醒
if (shouldSendRemind(certificate, today, thresholdDate, remindDate)) {
// 调用通知服务发送站内信
vetNotificationService.sendCertificateExpireRemind(certificate);
// 更新最后提醒时间
certificate.setLastRemindTime(new Date());
vetCertificateMapper.updateVetCertificate(certificate);
successCount++;
// 保留原来的日志输出
System.out.println("发送证书提醒:证书ID=" + certificate.getId() +
", 证书名称=" + certificate.getCertName());
}
} catch (Exception e) {
failCount++;
e.printStackTrace();
}
}
// 添加统计日志
System.out.println("证书提醒任务完成:成功 " + successCount + " 条,失败 " + failCount + " 条");
}
/**
* 判断是否需要发送提醒
*/
private boolean shouldSendRemind(VetCertificate certificate, Date today,
Date thresholdDate, Date remindDate) {
if (certificate.getExpireDate() == null) {
return false;
}
// 检查是否即将过期30天内
if (certificate.getExpireDate().after(thresholdDate)) {
return false;
}
// 检查是否已提醒过7天内
if (certificate.getLastRemindTime() != null) {
Calendar cal = Calendar.getInstance();
cal.setTime(certificate.getLastRemindTime());
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date lastRemindDateOnly = cal.getTime();
if (!lastRemindDateOnly.before(remindDate)) {
return false;
}
}
return true;
}
/**
* 发送证书过期提醒
*//*
private void sendCertificateExpireRemind(VetCertificate certificate) {
Date today = new Date();
long daysBetween = calculateDayDifference(today, certificate.getExpireDate());
String title;
String content;
int remindLevel;
if (daysBetween <= 0) {
title = "证书已过期";
content = String.format("您的《%s》证书已于%s过期!请立即更新证书。",
certificate.getCertName(), formatDate(certificate.getExpireDate()));
remindLevel = 3;
} else if (daysBetween <= 7) {
title = "证书即将过期(7天内)";
content = String.format("您的《%s》证书将在%d天后过期,请尽快更新!",
certificate.getCertName(), daysBetween);
remindLevel = 3;
} else {
title = "证书即将过期(30天内)";
content = String.format("您的《%s》证书将在%d天后过期,请及时更新。",
certificate.getCertName(), daysBetween);
remindLevel = 2;
}
System.out.println("发送证书提醒:" + title + " - " + content);
}
*/
/**
* 计算两个日期之间的天数差自己实现不依赖DateUtils
*/
private long calculateDayDifference(Date startDate, Date endDate) {
if (startDate == null || endDate == null) {
return 0L;
}
// 清除时间部分只比较日期
Calendar startCal = Calendar.getInstance();
startCal.setTime(startDate);
startCal.set(Calendar.HOUR_OF_DAY, 0);
startCal.set(Calendar.MINUTE, 0);
startCal.set(Calendar.SECOND, 0);
startCal.set(Calendar.MILLISECOND, 0);
Calendar endCal = Calendar.getInstance();
endCal.setTime(endDate);
endCal.set(Calendar.HOUR_OF_DAY, 0);
endCal.set(Calendar.MINUTE, 0);
endCal.set(Calendar.SECOND, 0);
endCal.set(Calendar.MILLISECOND, 0);
long diff = endCal.getTimeInMillis() - startCal.getTimeInMillis();
return diff / (1000 * 60 * 60 * 24);
}
/**
* 格式化日期为字符串
*/
private String formatDate(Date date) {
if (date == null) {
return "";
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // 月份从0开始
int day = calendar.get(Calendar.DAY_OF_MONTH);
return String.format("%d-%02d-%02d", year, month, day);
}
/**
* 更新证书状态
*/
private void updateCertificateStatus(VetCertificate certificate) {
if (certificate.getExpireDate() == null) {
certificate.setStatus("0"); // 默认正常
return;}
Date today = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(today);
calendar.add(Calendar.DAY_OF_YEAR, 30);
Date thirtyDaysLater = calendar.getTime();
if (certificate.getExpireDate().before(today)) {
certificate.setStatus("2"); // 已过期
} else if (certificate.getExpireDate().before(thirtyDaysLater)) {
certificate.setStatus("1"); // 即将过期
} else {
certificate.setStatus("0"); // 正常
}
}
@Override
public void manualCheckCertificates(Long userId) {
List<VetCertificate> certificates = selectCertificatesByUserId(userId);
for (VetCertificate certificate : certificates) {
updateCertificateStatus(certificate);
vetCertificateMapper.updateVetCertificate(certificate);
}
}
@Override
public Map<String, Object> getCertificateStatistics(Long userId) {
Map<String, Object> statistics = new HashMap<>();
List<VetCertificate> certificates = selectCertificatesByUserId(userId);
Date today = new Date();
long total = certificates.size();
long expiring = 0; // 30天内过期
long expired = 0; // 已过期
int expiringSoon = 0; // 7天内过期
Calendar calendar = Calendar.getInstance();
calendar.setTime(today);
calendar.add(Calendar.DAY_OF_YEAR, 30);
Date thirtyDaysLater = calendar.getTime();
calendar.setTime(today);
calendar.add(Calendar.DAY_OF_YEAR, 7);
Date sevenDaysLater = calendar.getTime();
for (VetCertificate cert : certificates) {
if (cert.getExpireDate() != null) {
if (cert.getExpireDate().before(today)) {
expired++;
} else if (cert.getExpireDate().before(thirtyDaysLater)) {
expiring++;
// 7天内过期
if (cert.getExpireDate().before(sevenDaysLater)) {
expiringSoon++;
}
}
}
}
long normal = total - expiring - expired;
statistics.put("total", total);
statistics.put("normal", normal);
statistics.put("expiring", expiring);
statistics.put("expired", expired);
statistics.put("expiringSoon", expiringSoon);
// 设置警告级别
if (expired > 0) {
statistics.put("warningLevel", "DANGER");
} else if (expiring > 0 || expiringSoon > 0) {
statistics.put("warningLevel", "WARNING");
} else {
statistics.put("warningLevel", "NORMAL");
}
return statistics;
}
}

178
chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetNotificationServiceImpl.java

@ -0,0 +1,178 @@
// 更简单的版本完全使用 Date
package com.chenhai.vet.service.impl;
import com.chenhai.vet.domain.VetCertificate;
import com.chenhai.vet.domain.VetNotification;
import com.chenhai.vet.mapper.VetNotificationMapper;
import com.chenhai.vet.service.VetNotificationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@Service
@RequiredArgsConstructor
@Slf4j
public class VetNotificationServiceImpl implements VetNotificationService {
private final VetNotificationMapper vetNotificationMapper;
@Override
public VetNotification selectVetNotificationById(Long id) {
return vetNotificationMapper.selectVetNotificationById(id);
}
@Override
public List<VetNotification> selectVetNotificationList(VetNotification vetNotification) {
return vetNotificationMapper.selectVetNotificationList(vetNotification);
}
@Override
public int insertVetNotification(VetNotification vetNotification) {
return vetNotificationMapper.insertVetNotification(vetNotification);
}
@Override
public int updateVetNotification(VetNotification vetNotification) {
return vetNotificationMapper.updateVetNotification(vetNotification);
}
@Override
public int deleteVetNotificationById(Long id) {
return vetNotificationMapper.deleteVetNotificationById(id);
}
@Override
public int deleteVetNotificationByIds(Long[] ids) {
return vetNotificationMapper.deleteVetNotificationByIds(ids);
}
@Override
public List<VetNotification> getNotificationsByUserId(Long userId) {
VetNotification query = new VetNotification();
query.setUserId(userId);
return vetNotificationMapper.selectVetNotificationList(query);
}
@Override
public int getUnreadCount(Long userId) {
VetNotification query = new VetNotification();
query.setUserId(userId);
query.setIsRead(0);
List<VetNotification> list = vetNotificationMapper.selectVetNotificationList(query);
return list != null ? list.size() : 0;
}
@Override
public void sendCertificateExpireRemind(VetCertificate certificate) {
if (certificate == null || certificate.getExpireDate() == null) {
return;
}
Date now = new Date();
Date expireDate = certificate.getExpireDate();
// 计算距离过期的天数
long daysBetween = calculateDayDifference(now, expireDate);
VetNotification notification = new VetNotification();
notification.setUserId(certificate.getUserId());
notification.setRelatedId(certificate.getId().toString());
notification.setType("CERT_EXPIRE_REMIND");
notification.setIsRead(0);
notification.setCreateTime(now); // 直接使用 Date
if (daysBetween <= 0) {
notification.setTitle("🔴 证书已过期");
notification.setContent(String.format("您的《%s》证书已于%s过期!请立即更新证书以继续提供服务。",
certificate.getCertName(), formatDate(expireDate)));
notification.setRemindLevel(3);
} else if (daysBetween <= 7) {
notification.setTitle("⚠️ 证书即将过期(7天内)");
notification.setContent(String.format("您的《%s》证书将在%d天后过期,请尽快完成更新!",
certificate.getCertName(), daysBetween));
notification.setRemindLevel(3);
} else if (daysBetween <= 30) {
notification.setTitle("⚠️ 证书即将过期(30天内)");
notification.setContent(String.format("您的《%s》证书将在%d天后过期,请及时安排更新。",
certificate.getCertName(), daysBetween));
notification.setRemindLevel(2);
} else {
// 超过30天不发送常规提醒
return;
}
vetNotificationMapper.insertVetNotification(notification);
log.info("发送证书提醒通知:用户ID={}, 证书ID={}, 天数={}",
certificate.getUserId(), certificate.getId(), daysBetween);
}
@Override
public boolean markAsRead(Long notificationId) {
VetNotification notification = vetNotificationMapper.selectVetNotificationById(notificationId);
if (notification != null && notification.getIsRead() == 0) {
notification.setIsRead(1);
notification.setReadTime(new Date());
int result = vetNotificationMapper.updateVetNotification(notification);
return result > 0;
}
return false;
}
@Override
public boolean markAllAsRead(Long userId) {
List<VetNotification> unreadNotifications = getNotificationsByUserId(userId);
int count = 0;
for (VetNotification notification : unreadNotifications) {
if (notification.getIsRead() == 0) {
notification.setIsRead(1);
notification.setReadTime(new Date());
vetNotificationMapper.updateVetNotification(notification);
count++;
}
}
log.info("标记所有通知为已读:用户ID={}, 标记数量={}", userId, count);
return count > 0;
}
/**
* 计算两个日期之间的天数差忽略时间部分
*/
private long calculateDayDifference(Date startDate, Date endDate) {
if (startDate == null || endDate == null) {
return 0L;
}
Calendar startCal = Calendar.getInstance();
startCal.setTime(startDate);
startCal.set(Calendar.HOUR_OF_DAY, 0);
startCal.set(Calendar.MINUTE, 0);
startCal.set(Calendar.SECOND, 0);
startCal.set(Calendar.MILLISECOND, 0);
Calendar endCal = Calendar.getInstance();
endCal.setTime(endDate);
endCal.set(Calendar.HOUR_OF_DAY, 0);
endCal.set(Calendar.MINUTE, 0);
endCal.set(Calendar.SECOND, 0);
endCal.set(Calendar.MILLISECOND, 0);
long diff = endCal.getTimeInMillis() - startCal.getTimeInMillis();
return diff / (1000 * 60 * 60 * 24);
}
/**
* 格式化日期为字符串
*/
private String formatDate(Date date) {
if (date == null) {
return "";
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
}

5
chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetPersonalInfoServiceImpl.java

@ -102,11 +102,6 @@ public class VetPersonalInfoServiceImpl implements IVetPersonalInfoService
return vetPersonalInfoMapper.deleteVetPersonalInfoById(id);
}
@Override
public List<VetPersonalInfo> selectVetPersonalInfoWithCertificates(VetPersonalInfo vetPersonalInfo) {
return vetPersonalInfoMapper.selectWithCertificates(vetPersonalInfo);
}
/**
* 获取兽医的完整信息包含证书详情
*/

182
chenhai-system/src/main/resources/mapper/vet/VetNotificationMapper.xml

@ -0,0 +1,182 @@
<?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.VetNotificationMapper">
<resultMap type="VetNotification" id="VetNotificationResult">
<id property="id" column="id"/>
<result property="userId" column="user_id"/>
<result property="title" column="title"/>
<result property="content" column="content"/>
<result property="type" column="type"/>
<result property="relatedId" column="related_id"/>
<result property="isRead" column="is_read"/>
<result property="remindLevel" column="remind_level"/>
<result property="createTime" column="create_time"/>
<result property="readTime" column="read_time"/>
</resultMap>
<sql id="selectVetNotificationVo">
select id, user_id, title, content, type, related_id, is_read, remind_level, create_time, read_time
from vet_notification
</sql>
<select id="selectVetNotificationById" parameterType="Long" resultMap="VetNotificationResult">
<include refid="selectVetNotificationVo"/>
where id = #{id}
<!-- 已删除 is_deleted = 0 -->
</select>
<select id="selectVetNotificationList" parameterType="VetNotification" resultMap="VetNotificationResult">
<include refid="selectVetNotificationVo"/>
<where>
<!-- 已删除 is_deleted = 0 -->
<if test="userId != null"> and user_id = #{userId}</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="type != null and type != ''"> and type = #{type}</if>
<if test="relatedId != null and relatedId != ''"> and related_id = #{relatedId}</if>
<if test="isRead != null"> and is_read = #{isRead}</if>
<if test="remindLevel != null"> and remind_level = #{remindLevel}</if>
<if test="createTime != null"> and create_time = #{createTime}</if>
<if test="readTime != null"> and read_time = #{readTime}</if>
</where>
order by create_time desc
</select>
<select id="selectUnreadNotificationsByUserId" parameterType="Long" resultMap="VetNotificationResult">
<include refid="selectVetNotificationVo"/>
where user_id = #{userId}
and is_read = 0
<!-- 已删除 and is_deleted = 0 -->
order by create_time desc
</select>
<select id="selectCertificateNotificationsByUserId" parameterType="Long" resultMap="VetNotificationResult">
<include refid="selectVetNotificationVo"/>
where user_id = #{userId}
and type like 'CERT%'
<!-- 已删除 and is_deleted = 0 -->
order by create_time desc
</select>
<select id="countUnreadNotificationsByUserId" parameterType="Long" resultType="Integer">
select count(*) from vet_notification
where user_id = #{userId}
and is_read = 0
<!-- 已删除 and is_deleted = 0 -->
</select>
<insert id="insertVetNotification" parameterType="VetNotification" useGeneratedKeys="true" keyProperty="id">
insert into vet_notification
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">user_id,</if>
<if test="title != null and title != ''">title,</if>
<if test="content != null and content != ''">content,</if>
<if test="type != null and type != ''">type,</if>
<if test="relatedId != null and relatedId != ''">related_id,</if>
<if test="isRead != null">is_read,</if>
<if test="remindLevel != null">remind_level,</if>
<if test="createTime != null">create_time,</if>
<if test="readTime != null">read_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
<if test="title != null and title != ''">#{title},</if>
<if test="content != null and content != ''">#{content},</if>
<if test="type != null and type != ''">#{type},</if>
<if test="relatedId != null and relatedId != ''">#{relatedId},</if>
<if test="isRead != null">#{isRead},</if>
<if test="remindLevel != null">#{remindLevel},</if>
<if test="createTime != null">#{createTime},</if>
<if test="readTime != null">#{readTime},</if>
</trim>
</insert>
<insert id="batchInsertVetNotifications" parameterType="java.util.List">
insert into vet_notification (user_id, title, content, type, related_id, is_read, remind_level, create_time)
values
<foreach collection="list" item="item" separator=",">
(
#{item.userId},
#{item.title},
#{item.content},
#{item.type},
#{item.relatedId},
#{item.isRead},
#{item.remindLevel},
#{item.createTime}
)
</foreach>
</insert>
<update id="updateVetNotification" parameterType="VetNotification">
update vet_notification
<trim prefix="SET" suffixOverrides=",">
<if test="userId != null">user_id = #{userId},</if>
<if test="title != null and title != ''">title = #{title},</if>
<if test="content != null and content != ''">content = #{content},</if>
<if test="type != null and type != ''">type = #{type},</if>
<if test="relatedId != null and relatedId != ''">related_id = #{relatedId},</if>
<if test="isRead != null">is_read = #{isRead},</if>
<if test="remindLevel != null">remind_level = #{remindLevel},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="readTime != null">read_time = #{readTime},</if>
</trim>
where id = #{id}
</update>
<update id="markNotificationAsRead" parameterType="Long">
update vet_notification
set is_read = 1,
read_time = now()
where id = #{id}
and is_read = 0
</update>
<update id="markAllNotificationsAsRead" parameterType="Long">
update vet_notification
set is_read = 1,
read_time = now()
where user_id = #{userId}
and is_read = 0
</update>
<!-- 删除这个cleanExpiredNotifications方法,因为没有is_deleted字段 -->
<!-- <update id="cleanExpiredNotifications" parameterType="Integer">
update vet_notification
set is_deleted = 1
where create_time &lt; date_sub(now(), interval #{days} day)
and is_deleted = 0
</update> -->
<!-- 删除这个deleteVetNotificationById方法,因为没有is_deleted字段 -->
<!-- <update id="deleteVetNotificationById" parameterType="Long">
update vet_notification
set is_deleted = 1
where id = #{id}
</update> -->
<!-- 删除这个deleteVetNotificationByIds方法,因为没有is_deleted字段 -->
<!-- <update id="deleteVetNotificationByIds" parameterType="Long">
update vet_notification
set is_deleted = 1
where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</update> -->
<!-- 如果需要硬删除,可以添加以下方法 -->
<delete id="deleteVetNotificationById" parameterType="Long">
delete from vet_notification where id = #{id}
</delete>
<delete id="deleteVetNotificationByIds" parameterType="Long">
delete from vet_notification where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

44
chenhai-system/src/main/resources/mapper/vet/VetPersonalInfoMapper.xml

@ -30,27 +30,6 @@
</sql>
<select id="selectVetPersonalInfoList" parameterType="VetPersonalInfo" resultMap="VetPersonalInfoResult">
<include refid="selectVetPersonalInfoVo"/>
<where>
<if test="userId != null"> and user_id = #{userId}</if>
<if test="realName != null and realName != ''"> and real_name like concat('%', #{realName}, '%')</if>
<if test="gender != null and gender != ''"> and gender = #{gender}</if>
<if test="idCard != null and idCard != ''"> and id_card = #{idCard}</if>
<if test="specialty != null and specialty != ''"> and specialty = #{specialty}</if>
<if test="workExperience != null"> and work_experience = #{workExperience}</if>
<if test="hospital != null and hospital != ''"> and hospital = #{hospital}</if>
<if test="address != null and address != ''"> and address = #{address}</if>
<if test="introduction != null and introduction != ''"> and introduction = #{introduction}</if>
</where>
</select>
<select id="selectVetPersonalInfoById" parameterType="Long" resultMap="VetPersonalInfoResult">
<include refid="selectVetPersonalInfoVo"/>
where id = #{id}
</select>
<!-- 注意:这里有两个selectWithCertificates,删除重复的那个 -->
<select id="selectWithCertificates" parameterType="VetPersonalInfo" resultMap="VetPersonalInfoResult">
SELECT
vpi.id,
vpi.user_id,
@ -67,22 +46,31 @@
vpi.create_time,
vpi.update_by,
vpi.update_time,
<!-- 证书统计信息 -->
COUNT(vc.id) as certCount,
GROUP_CONCAT(vc.cert_name) as certNames
FROM vet_personal_info vpi
LEFT JOIN vet_certificate vc ON vpi.user_id = vc.user_id
<where>
<if test="userId != null">AND vpi.user_id = #{userId}</if>
<if test="realName != null and realName != ''">AND vpi.real_name like concat('%', #{realName}, '%')</if>
<if test="gender != null and gender != ''">AND vpi.gender = #{gender}</if>
<if test="idCard != null and idCard != ''">AND vpi.id_card like concat('%', #{idCard}, '%')</if>
<if test="specialty != null and specialty != ''">AND vpi.specialty like concat('%', #{specialty}, '%')</if>
<if test="hospital != null and hospital != ''">AND vpi.hospital like concat('%', #{hospital}, '%')</if>
<if test="address != null and address != ''">AND vpi.address like concat('%', #{address}, '%')</if>
<if test="userId != null"> and vpi.user_id = #{userId}</if>
<if test="realName != null and realName != ''"> and vpi.real_name like concat('%', #{realName}, '%')</if>
<if test="gender != null and gender != ''"> and vpi.gender = #{gender}</if>
<if test="idCard != null and idCard != ''"> and vpi.id_card = #{idCard}</if>
<if test="specialty != null and specialty != ''"> and vpi.specialty = #{specialty}</if>
<if test="workExperience != null"> and vpi.work_experience = #{workExperience}</if>
<if test="hospital != null and hospital != ''"> and vpi.hospital = #{hospital}</if>
<if test="iphone != null and iphone != ''">and vpi.iphone = #{iphone}</if>
<if test="address != null and address != ''"> and vpi.address = #{address}</if>
<if test="introduction != null and introduction != ''"> and vpi.introduction = #{introduction}</if>
</where>
GROUP BY vpi.id
</select>
<select id="selectVetPersonalInfoById" parameterType="Long" resultMap="VetPersonalInfoResult">
<include refid="selectVetPersonalInfoVo"/>
where id = #{id}
</select>
<insert id="insertVetPersonalInfo" parameterType="VetPersonalInfo" useGeneratedKeys="true" keyProperty="id">
insert into vet_personal_info
<trim prefix="(" suffix=")" suffixOverrides=",">

44
chenhai-ui/src/api/vet/notification.js

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询兽医通知列表
export function listNotification(query) {
return request({
url: '/vet/notification/list',
method: 'get',
params: query
})
}
// 查询兽医通知详细
export function getNotification(id) {
return request({
url: '/vet/notification/' + id,
method: 'get'
})
}
// 新增兽医通知
export function addNotification(data) {
return request({
url: '/vet/notification',
method: 'post',
data: data
})
}
// 修改兽医通知
export function updateNotification(data) {
return request({
url: '/vet/notification',
method: 'put',
data: data
})
}
// 删除兽医通知
export function delNotification(id) {
return request({
url: '/vet/notification/' + id,
method: 'delete'
})
}

4
chenhai-ui/src/views/vet/certificate/index.vue

@ -224,13 +224,13 @@
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</div>-
</el-dialog>
</div>
</template>
<script>
import { listCertificate, getCertificate, delCertificate, addCertificate, updateCertificate } from "@/api/vet/certificate"
import { listCertificate, getCertificate, delCertificate, addCertificate, updateCertificate } from "@/api/system/certificate"
export default {
name: "Certificate",

340
chenhai-ui/src/views/vet/notification/index.vue

@ -0,0 +1,340 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<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="title">
<el-input
v-model="queryParams.title"
placeholder="请输入通知标题"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="关联ID" prop="relatedId">
<el-input
v-model="queryParams.relatedId"
placeholder="请输入关联ID"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="是否已读 0:未读 1:已读" prop="isRead">
<el-input
v-model="queryParams.isRead"
placeholder="请输入是否已读 0:未读 1:已读"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="提醒级别 1:低 2:中 3:高" prop="remindLevel">
<el-input
v-model="queryParams.remindLevel"
placeholder="请输入提醒级别 1:低 2:中 3:高"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="阅读时间" prop="readTime">
<el-date-picker clearable
v-model="queryParams.readTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择阅读时间">
</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>
<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="['vet:notification: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="['vet:notification: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="['vet:notification: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:notification:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="notificationList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键ID" align="center" prop="id" />
<el-table-column label="接收用户ID" align="center" prop="userId" />
<el-table-column label="通知标题" align="center" prop="title" />
<el-table-column label="通知内容" align="center" prop="content" />
<el-table-column label="通知类型" align="center" prop="type" />
<el-table-column label="关联ID" align="center" prop="relatedId" />
<el-table-column label="是否已读 0:未读 1:已读" align="center" prop="isRead" />
<el-table-column label="提醒级别 1:低 2:中 3:高" align="center" prop="remindLevel" />
<el-table-column label="阅读时间" align="center" prop="readTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.readTime, '{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="['vet:notification:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['vet:notification: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="接收用户ID" prop="userId">
<el-input v-model="form.userId" placeholder="请输入接收用户ID" />
</el-form-item>
<el-form-item label="通知标题" prop="title">
<el-input v-model="form.title" placeholder="请输入通知标题" />
</el-form-item>
<el-form-item label="通知内容">
<editor v-model="form.content" :min-height="192"/>
</el-form-item>
<el-form-item label="关联ID" prop="relatedId">
<el-input v-model="form.relatedId" placeholder="请输入关联ID" />
</el-form-item>
<el-form-item label="是否已读 0:未读 1:已读" prop="isRead">
<el-input v-model="form.isRead" placeholder="请输入是否已读 0:未读 1:已读" />
</el-form-item>
<el-form-item label="提醒级别 1:低 2:中 3:高" prop="remindLevel">
<el-input v-model="form.remindLevel" placeholder="请输入提醒级别 1:低 2:中 3:高" />
</el-form-item>
<el-form-item label="阅读时间" prop="readTime">
<el-date-picker clearable
v-model="form.readTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择阅读时间">
</el-date-picker>
</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 { listNotification, getNotification, delNotification, addNotification, updateNotification } from "@/api/vet/notification"
export default {
name: "Notification",
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
notificationList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
userId: null,
title: null,
content: null,
type: null,
relatedId: null,
isRead: null,
remindLevel: null,
readTime: null
},
//
form: {},
//
rules: {
userId: [
{ required: true, message: "接收用户ID不能为空", trigger: "blur" }
],
title: [
{ required: true, message: "通知标题不能为空", trigger: "blur" }
],
}
}
},
created() {
this.getList()
},
methods: {
/** 查询兽医通知列表 */
getList() {
this.loading = true
listNotification(this.queryParams).then(response => {
this.notificationList = response.rows
this.total = response.total
this.loading = false
})
},
//
cancel() {
this.open = false
this.reset()
},
//
reset() {
this.form = {
id: null,
userId: null,
title: null,
content: null,
type: null,
relatedId: null,
isRead: null,
remindLevel: null,
createTime: null,
readTime: 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
getNotification(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) {
updateNotification(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
addNotification(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 delNotification(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('vet/notification/export', {
...this.queryParams
}, `notification_${new Date().getTime()}.xlsx`)
}
}
}
</script>

2
chenhai-ui/vue.config.js

@ -9,7 +9,7 @@ const CompressionPlugin = require('compression-webpack-plugin')
const name = process.env.VUE_APP_TITLE || '"与牧同行"' // 网页标题
const baseUrl = 'http://localhost:8081' // 后端接
const baseUrl = 'http://localhost:8082' // 后端接
const port = process.env.port || process.env.npm_config_port || 80 // 端口
// vue.config.js 配置说明

Loading…
Cancel
Save