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. 60
      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. 328
      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 org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 兽医执业证书Controller * 兽医执业证书Controller
@ -22,7 +23,7 @@ import java.util.List;
* @date 2025-12-29 * @date 2025-12-29
*/ */
@RestController @RestController
@RequestMapping("/system/certificate")
@RequestMapping("/vet/certificate")
public class VetCertificateController extends BaseController public class VetCertificateController extends BaseController
{ {
@Autowired @Autowired
@ -31,7 +32,7 @@ public class VetCertificateController extends BaseController
/** /**
* 查询兽医执业证书列表 * 查询兽医执业证书列表
*/ */
@PreAuthorize("@ss.hasPermi('system:certificate:list')")
@PreAuthorize("@ss.hasPermi('vet:certificate:list')")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo list(VetCertificate vetCertificate) 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) @Log(title = "兽医执业证书", businessType = BusinessType.EXPORT)
@PostMapping("/export") @PostMapping("/export")
public void export(HttpServletResponse response, VetCertificate vetCertificate) 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}") @GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long 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) @Log(title = "兽医执业证书", businessType = BusinessType.INSERT)
@PostMapping @PostMapping
public AjaxResult add(@RequestBody VetCertificate vetCertificate) 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) @Log(title = "兽医执业证书", businessType = BusinessType.UPDATE)
@PutMapping @PutMapping
public AjaxResult edit(@RequestBody VetCertificate vetCertificate) 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) @Log(title = "兽医执业证书", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}") @DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids) public AjaxResult remove(@PathVariable Long[] ids)
{ {
return toAjax(vetCertificateService.deleteVetCertificateByIds(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); 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: server:
# 服务器的HTTP端口,默认为8080 # 服务器的HTTP端口,默认为8080
port: 8081
port: 8082
servlet: servlet:
# 应用的访问路径 # 应用的访问路径
context-path: / 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; private String certImage;
/** 状态(0正常 1即将过期 2已过期) */ /** 状态(0正常 1即将过期 2已过期) */
@Excel(name = "状态", readConverterExp = "0=正常,1=即将过期,2=已过期")
@Excel(name = "状态", dictType = "certificate_status")
private String status; private String status;
/** 提前提醒天数 */ /** 提前提醒天数 */
@Excel(name = "提前提醒天数") @Excel(name = "提前提醒天数")
private Long remindDays;
private Integer remindDays;
/** 上次提醒时间 */ /** 上次提醒时间 */
@JsonFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd")
@ -168,12 +168,12 @@ public class VetCertificate extends BaseEntity
return status; return status;
} }
public void setRemindDays(Long remindDays)
public void setRemindDays(Integer remindDays)
{ {
this.remindDays = remindDays; this.remindDays = remindDays;
} }
public Long getRemindDays()
public Integer getRemindDays()
{ {
return remindDays; 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 = "联系地址") @Excel(name = "联系地址")
private String address; private String address;
/** 联系方式 */
@Excel(name = "联系方式")
private String iphone;
/** 个人简介 */ /** 个人简介 */
@Excel(name = "个人简介") @Excel(name = "个人简介")
@ -186,6 +189,16 @@ public class VetPersonalInfo extends BaseEntity
return address; return address;
} }
public void setIphone(String address)
{
this.iphone = iphone;
}
public String getIphone()
{
return iphone;
}
public void setIntroduction(String introduction) public void setIntroduction(String introduction)
{ {
this.introduction = introduction; this.introduction = introduction;
@ -209,6 +222,7 @@ public class VetPersonalInfo extends BaseEntity
.append("workExperience", getWorkExperience()) .append("workExperience", getWorkExperience())
.append("hospital", getHospital()) .append("hospital", getHospital())
.append("address", getAddress()) .append("address", getAddress())
.append("iphone", getIphone())
.append("introduction", getIntroduction()) .append("introduction", getIntroduction())
.append("createBy", getCreateBy()) .append("createBy", getCreateBy())
.append("createTime", getCreateTime()) .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 结果 * @return 结果
*/ */
public int deleteVetPersonalInfoByIds(Long[] ids); public int deleteVetPersonalInfoByIds(Long[] ids);
public List<VetPersonalInfo> selectWithCertificates(VetPersonalInfo vetPersonalInfo);
} }

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

@ -1,62 +1,66 @@
package com.chenhai.vet.service; package com.chenhai.vet.service;
import com.chenhai.vet.domain.VetCertificate; import com.chenhai.vet.domain.VetCertificate;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 兽医执业证书Service接口 * 兽医执业证书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 结果 * @return 结果
*/ */
public int deleteVetPersonalInfoById(Long id); 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);
}

328
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.domain.VetCertificate;
import com.chenhai.vet.mapper.VetCertificateMapper; import com.chenhai.vet.mapper.VetCertificateMapper;
import com.chenhai.vet.service.IVetCertificateService; import com.chenhai.vet.service.IVetCertificateService;
import com.chenhai.vet.service.VetNotificationService;
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 java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 兽医执业证书Service业务层处理 * 兽医执业证书Service业务层处理
*
* @author ruoyi
* @date 2025-12-29
*/ */
@Service @Service
public class VetCertificateServiceImpl implements IVetCertificateService public class VetCertificateServiceImpl implements IVetCertificateService
{ {
@Autowired @Autowired
private VetCertificateMapper vetCertificateMapper; private VetCertificateMapper vetCertificateMapper;
@Autowired
private VetNotificationService vetNotificationService;
/**
* 查询兽医执业证书
*
* @param id 兽医执业证书主键
* @return 兽医执业证书
*/
@Override @Override
public VetCertificate selectVetCertificateById(Long id) public VetCertificate selectVetCertificateById(Long id)
{ {
return vetCertificateMapper.selectVetCertificateById(id); return vetCertificateMapper.selectVetCertificateById(id);
} }
/**
* 查询兽医执业证书列表
*
* @param vetCertificate 兽医执业证书
* @return 兽医执业证书
*/
@Override @Override
public List<VetCertificate> selectVetCertificateList(VetCertificate vetCertificate) public List<VetCertificate> selectVetCertificateList(VetCertificate vetCertificate)
{ {
return vetCertificateMapper.selectVetCertificateList(vetCertificate); return vetCertificateMapper.selectVetCertificateList(vetCertificate);
} }
/**
* 新增兽医执业证书
*
* @param vetCertificate 兽医执业证书
* @return 结果
*/
@Override @Override
public int insertVetCertificate(VetCertificate vetCertificate) public int insertVetCertificate(VetCertificate vetCertificate)
{ {
vetCertificate.setCreateTime(DateUtils.getNowDate()); vetCertificate.setCreateTime(DateUtils.getNowDate());
// 验证并设置证书状态
updateCertificateStatus(vetCertificate);
// 设置默认提醒天数
if (vetCertificate.getRemindDays() == null) {
vetCertificate.setRemindDays(30);
}
return vetCertificateMapper.insertVetCertificate(vetCertificate); return vetCertificateMapper.insertVetCertificate(vetCertificate);
} }
/**
* 修改兽医执业证书
*
* @param vetCertificate 兽医执业证书
* @return 结果
*/
@Override @Override
public int updateVetCertificate(VetCertificate vetCertificate) public int updateVetCertificate(VetCertificate vetCertificate)
{ {
vetCertificate.setUpdateTime(DateUtils.getNowDate()); vetCertificate.setUpdateTime(DateUtils.getNowDate());
// 验证并更新证书状态
updateCertificateStatus(vetCertificate);
return vetCertificateMapper.updateVetCertificate(vetCertificate); return vetCertificateMapper.updateVetCertificate(vetCertificate);
} }
/**
* 批量删除兽医执业证书
*
* @param ids 需要删除的兽医执业证书主键
* @return 结果
*/
@Override @Override
public int deleteVetCertificateByIds(Long[] ids) public int deleteVetCertificateByIds(Long[] ids)
{ {
return vetCertificateMapper.deleteVetCertificateByIds(ids); return vetCertificateMapper.deleteVetCertificateByIds(ids);
} }
/**
* 删除兽医执业证书信息
*
* @param id 兽医执业证书主键
* @return 结果
*/
@Override @Override
public int deleteVetCertificateById(Long id) public int deleteVetCertificateById(Long id)
{ {
return vetCertificateMapper.deleteVetCertificateById(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); 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> </sql>
<select id="selectVetPersonalInfoList" parameterType="VetPersonalInfo" resultMap="VetPersonalInfoResult"> <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 SELECT
vpi.id, vpi.id,
vpi.user_id, vpi.user_id,
@ -67,22 +46,31 @@
vpi.create_time, vpi.create_time,
vpi.update_by, vpi.update_by,
vpi.update_time, vpi.update_time,
<!-- 证书统计信息 -->
COUNT(vc.id) as certCount, COUNT(vc.id) as certCount,
GROUP_CONCAT(vc.cert_name) as certNames GROUP_CONCAT(vc.cert_name) as certNames
FROM vet_personal_info vpi FROM vet_personal_info vpi
LEFT JOIN vet_certificate vc ON vpi.user_id = vc.user_id LEFT JOIN vet_certificate vc ON vpi.user_id = vc.user_id
<where> <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> </where>
GROUP BY vpi.id GROUP BY vpi.id
</select> </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 id="insertVetPersonalInfo" parameterType="VetPersonalInfo" useGeneratedKeys="true" keyProperty="id">
insert into vet_personal_info insert into vet_personal_info
<trim prefix="(" suffix=")" suffixOverrides=","> <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"> <div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button> <el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button> <el-button @click="cancel"> </el-button>
</div>
</div>-
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { listCertificate, getCertificate, delCertificate, addCertificate, updateCertificate } from "@/api/vet/certificate"
import { listCertificate, getCertificate, delCertificate, addCertificate, updateCertificate } from "@/api/system/certificate"
export default { export default {
name: "Certificate", 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 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 // 端口 const port = process.env.port || process.env.npm_config_port || 80 // 端口
// vue.config.js 配置说明 // vue.config.js 配置说明

Loading…
Cancel
Save