浏览代码

样本送检增加宠物食品检测

陈长荣 3 周之前
父节点
当前提交
7e5da02973
共有 21 个文件被更改,包括 775 次插入232 次删除
  1. 89 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/entity/SampleFood.java
  2. 56 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/entity/SampleFoodDetail.java
  3. 21 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/entity/SampleInfo.java
  4. 5 1
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/enums/SampleTypeEnum.java
  5. 9 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/mapper/SampleFoodDetailMapper.java
  6. 9 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/mapper/SampleFoodMapper.java
  7. 10 1
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/SampleEditService.java
  8. 7 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/SampleFoodDetailService.java
  9. 15 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/SampleFoodService.java
  10. 153 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/biz/CommonSampleEditServiceImpl.java
  11. 12 43
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/biz/SampleAnimalImagingServiceImpl.java
  12. 11 41
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/biz/SampleAnimalServiceImpl.java
  13. 140 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/biz/SampleFoodServiceImpl.java
  14. 12 43
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/biz/SamplePathologicalServiceImpl.java
  15. 11 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/impl/SampleFoodDetailServiceImpl.java
  16. 11 83
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/impl/SampleInfoServiceImpl.java
  17. 46 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/vo/SampleFoodDetailVo.java
  18. 90 0
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/vo/SampleFoodVo.java
  19. 4 2
      jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/vo/SampleSubmitVo.java
  20. 57 0
      jfcloud-gene-biz/src/main/resources/sql/ddl-20250509.sql
  21. 7 18
      jfcloud-gene-common/src/main/java/com/github/jfcloud/gene/common/util/UserUtil.java

+ 89 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/entity/SampleFood.java

@@ -0,0 +1,89 @@
+package com.github.jfcloud.gene.sample.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.github.jfcloud.gene.common.entity.BaseEntity;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Schema(description = "宠物食品检测表")
+public class SampleFood extends BaseEntity {
+
+    @TableId(type = IdType.INPUT)
+    @Schema(description = "检测单ID")
+    private Long id;
+
+    @Schema(description = "样本ID")
+    private Long sampleId;
+
+    @Schema(description = "送检目的")
+    private String purpose;
+
+    @Schema(description = "送检方名称")
+    private String senderName;
+
+    @Schema(description = "送检方地址")
+    private String senderAddress;
+
+    @Schema(description = "送检方联系人")
+    private Long senderContactId;
+
+    private String senderContactName;
+
+    @Schema(description = "送检方联系电话")
+    private String senderPhone;
+
+    @Schema(description = "送检方电子邮箱")
+    private String senderEmail;
+
+    @Schema(description = "样品类型")
+    private String sampleType;
+
+    @Schema(description = "样品照片")
+    private String samplePhoto;
+
+    @Schema(description = "样品储存要求")
+    private String storageRequirements;
+
+    @Schema(description = "样品储存要求-其他")
+    private String storageRequirementsOther;
+
+    @Schema(description = "样品保留期限")
+    private String retentionPeriod;
+
+    @Schema(description = "是否危险性样品 0否 1是")
+    private String hazard;
+
+    @Schema(description = "样品危害描述")
+    private String hazardDescription;
+
+    @Schema(description = "样品危害描述-其他")
+    private String hazardDescriptionOther;
+
+    @Schema(description = "报告方式")
+    private String reportMethod;
+
+    @Schema(description = "报告类型")
+    private String reportType;
+
+    @Schema(description = "是否评判结果 0否 1是")
+    private String hasEvaluation;
+
+    @Schema(description = "检测结果是否以干基计 0否 1是")
+    private String dryBasis;
+
+    @Schema(description = "其他要求")
+    private String otherRequirements;
+
+    @Schema(description = "服务时限")
+    private String serviceDeadline;
+
+    @Schema(description = "收样人")
+    private Long receiverId;
+
+    @Schema(description = "收样人姓名")
+    private String receiverName;
+}

+ 56 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/entity/SampleFoodDetail.java

@@ -0,0 +1,56 @@
+package com.github.jfcloud.gene.sample.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Schema(description = "宠物食品检测明细表")
+public class SampleFoodDetail extends Model {
+
+    @TableId(type = IdType.AUTO)
+    @Schema(description = "样品ID")
+    private Long id;
+
+    @Schema(description = "关联的宠物食品检测表ID")
+    private Long sampleFoodId;
+
+    @Schema(description = "样品名称")
+    private String sampleName;
+
+    @Schema(description = "生产商")
+    private String manufacturer;
+
+    @Schema(description = "生产日期")
+    private Date productionDate;
+
+    @Schema(description = "批号")
+    private String batchNumber;
+
+    @Schema(description = "数量")
+    private String quantity;
+
+    @Schema(description = "样品状态")
+    private String sampleStatus;
+
+    @Schema(description = "样品状态-其他")
+    private String sampleStatusOther;
+
+    @Schema(description = "包装方式")
+    private String packaging;
+
+    @Schema(description = "包装方式-其他")
+    private String packagingOther;
+
+    @Schema(description = "检测项目")
+    private String detectionItems;
+
+    @Schema(description = "其他补充信息")
+    private String otherInfo;
+}

+ 21 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/entity/SampleInfo.java

@@ -1,12 +1,15 @@
 package com.github.jfcloud.gene.sample.entity;
 
+import cn.hutool.core.util.StrUtil;
 import com.github.jfcloud.gene.common.entity.BaseEntity;
+import com.github.jfcloud.gene.sample.enums.SampleTypeEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
 
 import java.time.LocalDateTime;
+import java.util.Objects;
 
 @Accessors(chain = true)
 @EqualsAndHashCode(callSuper = true)
@@ -80,4 +83,22 @@ public class SampleInfo extends BaseEntity {
 
     @Schema(description = "提交版本号")
     private Integer version;
+
+    /**
+     * 消息通知 项目编号
+     * @return
+     */
+    public String projectType() {
+        String projectType = "样本送检/" + Objects.requireNonNull(SampleTypeEnum.resolve(getType())).getLabel();
+        String proNo = StrUtil.emptyToDefault(getProjectNo(), getApprovalNo());
+        if (StrUtil.isNotBlank(proNo)) {
+            projectType += "/" + proNo;
+        }
+        if (StrUtil.isNotBlank(getProjectName())) {
+            projectType += "/" + getProjectName();
+        }
+
+        return projectType;
+    }
+
 }

+ 5 - 1
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/enums/SampleTypeEnum.java

@@ -19,7 +19,11 @@ public enum SampleTypeEnum {
     /**
      * 动物影像
      */
-    ANIMAL_IMAGE("animalImage", "动物影像检测");
+    ANIMAL_IMAGE("animalImage", "动物影像检测"),
+    /**
+     * 宠物食品检测
+     */
+    ANIMAL_FOOD("animalFood", "宠物食品检测");
 
     private final String type;
 

+ 9 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/mapper/SampleFoodDetailMapper.java

@@ -0,0 +1,9 @@
+package com.github.jfcloud.gene.sample.mapper;
+
+import com.github.jfcloud.common.data.datascope.JfcloudBaseMapper;
+import com.github.jfcloud.gene.sample.entity.SampleFoodDetail;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SampleFoodDetailMapper extends JfcloudBaseMapper<SampleFoodDetail> {
+}

+ 9 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/mapper/SampleFoodMapper.java

@@ -0,0 +1,9 @@
+package com.github.jfcloud.gene.sample.mapper;
+
+import com.github.jfcloud.common.data.datascope.JfcloudBaseMapper;
+import com.github.jfcloud.gene.sample.entity.SampleFood;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SampleFoodMapper extends JfcloudBaseMapper<SampleFood> {
+}

+ 10 - 1
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/SampleEditService.java

@@ -25,12 +25,21 @@ public interface SampleEditService {
      */
     void generate(Long sampleId);
 
+    /**
+     * 审核完成后置操作
+     *
+     * @param sampleId
+     */
+    default void afterAudit(Long sampleId) {
+        generate(sampleId);
+    }
+
     /**
      * 执行后置操作
      *
      * @param sampleId
      */
-    void afterExecute(Long sampleId);
+    default void afterExecute(Long sampleId) {}
 }
 
 

+ 7 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/SampleFoodDetailService.java

@@ -0,0 +1,7 @@
+package com.github.jfcloud.gene.sample.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.github.jfcloud.gene.sample.entity.SampleFoodDetail;
+
+public interface SampleFoodDetailService extends IService<SampleFoodDetail> {
+}

+ 15 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/SampleFoodService.java

@@ -0,0 +1,15 @@
+package com.github.jfcloud.gene.sample.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.github.jfcloud.gene.sample.entity.SampleFood;
+import com.github.jfcloud.gene.sample.vo.SampleFoodVo;
+
+public interface SampleFoodService extends IService<SampleFood> {
+
+    /**
+     * 获取详情
+     * @param id
+     * @return
+     */
+    SampleFoodVo getDetail(Long id);
+}

+ 153 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/biz/CommonSampleEditServiceImpl.java

@@ -0,0 +1,153 @@
+package com.github.jfcloud.gene.sample.service.biz;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.github.jfcloud.admin.api.sys.dto.message.MessageUserDTO;
+import com.github.jfcloud.gene.cache.UserIdNameCache;
+import com.github.jfcloud.gene.common.util.UserUtil;
+import com.github.jfcloud.gene.constants.GeneStatusEnum;
+import com.github.jfcloud.gene.flow.entity.FlowAudit;
+import com.github.jfcloud.gene.flow.service.FlowAuditService;
+import com.github.jfcloud.gene.flow.service.NotifyService;
+import com.github.jfcloud.gene.sample.entity.SampleInfo;
+import com.github.jfcloud.gene.sample.mapper.SampleInfoMapper;
+import com.github.jfcloud.gene.sample.service.SampleEditService;
+import com.github.jfcloud.gene.sample.vo.SampleSubmitVo;
+import com.github.jfcloud.gene.sys.service.DBSystemPropertiesService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class CommonSampleEditServiceImpl implements SampleEditService {
+
+    private final SampleInfoMapper sampleInfoMapper;
+    private final FlowAuditService flowAuditService;
+    private final DBSystemPropertiesService systemPropertiesService;
+    private final NotifyService notifyService;
+    private final UserIdNameCache userIdNameCache;
+
+    @Override
+    public void removeBySampleId(Long sampleId) {
+
+    }
+
+    @Override
+    public void save(Long sampleId, SampleSubmitVo vo) {
+
+    }
+
+    @Override
+    public void generate(Long sampleId) {
+
+    }
+
+    @Override
+    public void afterAudit(Long sampleId) {
+        SampleInfo sampleInfo = sampleInfoMapper.selectById(sampleId);
+        if (sampleInfo == null) {
+            return;
+        }
+
+        //查询流程前一个状态
+        List<FlowAudit> flowAudits = flowAuditService.auditList(sampleId, "sample." + sampleInfo.getType(), false);
+        FlowAudit previousAudit = null;
+        String previousStatus = "";
+        String previousUser = "";
+        if (CollUtil.isNotEmpty(flowAudits)) {
+            previousAudit = flowAudits.get(0);
+            GeneStatusEnum statusEnum = GeneStatusEnum.getByStatus(previousAudit.getFlowStatus());
+            previousStatus = statusEnum != null ? statusEnum.getDescription() : "";
+            previousUser = userIdNameCache.getNicknameByUserId(previousAudit.getCreateBy());
+        }
+
+        String projectType = sampleInfo.projectType();
+        String now = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_TIME_PATTERN);
+
+        Long userId;
+        String dingMsg;
+        GeneStatusEnum statusEnum = Objects.requireNonNull(GeneStatusEnum.getByStatus(sampleInfo.getStatus()));
+        switch (statusEnum) {
+            case REJECTED:
+                //驳回通知申请人
+                userId = Long.valueOf(sampleInfo.getCreateBy());
+                dingMsg = String.format("【%s】【%s/%s】%s不同意,请修正后再提交", projectType, previousStatus, previousUser, now);
+                break;
+            case DEPART_LEADER:
+                userId = sampleInfo.getDepartLeaderId();
+                dingMsg = String.format("【%s】【%s】%s提交,请进行审查", projectType, userIdNameCache.getNicknameByUserId(sampleInfo.getCreateBy()), now);
+                break;
+            case PROJECT_MANAGEMENT:
+                userId = sampleInfo.getProjectManageId();
+                dingMsg = String.format("【%s】【%s/%s】%s同意,请进行审查", projectType, previousStatus, previousUser, now);
+                break;
+            case COMPLETED:
+                //执行
+                userId = Long.valueOf(sampleInfo.getCreateBy());
+                dingMsg = String.format("【%s】【%s/%s】%s同意,请执行项目", projectType, previousStatus, previousUser, now);
+                break;
+            default:
+                log.error("流程项目 ({}) 状态异常 {}", sampleInfo.getProjectName(), statusEnum.getDescription());
+                return;
+        }
+
+        if (sampleInfo.getVersion() > 1) {
+            dingMsg += "(第" + sampleInfo.getVersion() + "次申请)";
+        }
+
+        //查询是否需要钉钉通知
+        String key = String.format("sample.%s.dingding.enable", sampleInfo.getStatus());
+        if (systemPropertiesService.getBooleanValue(key)) {
+            MessageUserDTO messageUserDTO = new MessageUserDTO();
+            messageUserDTO.setUserId(userId);
+            notifyService.sendDingding("样本送检", dingMsg, Collections.singletonList(messageUserDTO));
+        }
+    }
+
+    /**
+     * 项目执行完成通知,添加申请人和检测人员
+     *
+     * @param sampleId 样本送检Id
+     * @param userIds  通知用户Id
+     */
+    public void afterExecuteNotify(Long sampleId, Set<Long> userIds) {
+        //钉钉消息通知申请人、检测人员
+        if (!systemPropertiesService.getBooleanValue("sample.afterExecute.dingding.enable")) {
+            return;
+        }
+
+        SampleInfo sampleInfo = sampleInfoMapper.selectById(sampleId);
+        String now = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_TIME_PATTERN);
+        String dingMsg = String.format("【%s】【%s】%s执行项目", sampleInfo.projectType(), UserUtil.getNickName(), now);
+
+        //申请人
+        userIds.add(sampleInfo.getApplicantId());
+        //检测人员
+        if (StrUtil.isNotBlank(sampleInfo.getInspector())) {
+            JSON.parseArray(sampleInfo.getInspector()).forEach(item -> {
+                JSONObject obj = (JSONObject) item;
+                if (obj.containsKey("id")) {
+                    userIds.add(obj.getLong("id"));
+                }
+            });
+        }
+
+        List<MessageUserDTO> users = userIds.stream()
+                .map(x -> {
+                    MessageUserDTO messageUserDTO = new MessageUserDTO();
+                    messageUserDTO.setUserId(x);
+                    return messageUserDTO;
+                })
+                .collect(Collectors.toList());
+        notifyService.sendDingding("样本送检", dingMsg, users);
+    }
+}

+ 12 - 43
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/biz/SampleAnimalImagingServiceImpl.java

@@ -11,19 +11,16 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.github.jfcloud.admin.api.sys.dto.message.MessageUserDTO;
 import com.github.jfcloud.gene.cache.UserIdNameCache;
 import com.github.jfcloud.gene.common.constant.StrConstant;
 import com.github.jfcloud.gene.common.constant.WhetherEnum;
 import com.github.jfcloud.gene.common.util.CustomIdGenerator;
-import com.github.jfcloud.gene.common.util.UserUtil;
 import com.github.jfcloud.gene.constants.GeneStatusEnum;
 import com.github.jfcloud.gene.file.service.FileInfoService;
 import com.github.jfcloud.gene.flow.entity.FlowAudit;
 import com.github.jfcloud.gene.flow.service.FlowAuditService;
 import com.github.jfcloud.gene.flow.service.NotifyService;
 import com.github.jfcloud.gene.sample.entity.*;
-import com.github.jfcloud.gene.sample.enums.SampleTypeEnum;
 import com.github.jfcloud.gene.sample.mapper.SampleAnimalImagingMapper;
 import com.github.jfcloud.gene.sample.mapper.SampleInfoMapper;
 import com.github.jfcloud.gene.sample.service.*;
@@ -38,7 +35,10 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
-import java.util.*;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -56,6 +56,7 @@ public class SampleAnimalImagingServiceImpl extends ServiceImpl<SampleAnimalImag
     private final FileInfoService fileInfoService;
     private final DBSystemPropertiesService systemPropertiesService;
     private final NotifyService notifyService;
+    private final CommonSampleEditServiceImpl commonSampleEditService;
 
     @Override
     public void removeBySampleId(Long sampleId) {
@@ -244,46 +245,14 @@ public class SampleAnimalImagingServiceImpl extends ServiceImpl<SampleAnimalImag
     }
 
     @Override
-    public void afterExecute(Long sampleId) {
-        //钉钉消息通知申请人、检测人员
-        if (!systemPropertiesService.getBooleanValue("sample.afterExecute.dingding.enable")) {
-            return;
-        }
-
-        SampleInfo sampleInfo = sampleInfoMapper.selectById(sampleId);
-
-        String projectType = "样本送检/" + Objects.requireNonNull(SampleTypeEnum.resolve(sampleInfo.getType())).getLabel();
-        String proNo = StrUtil.emptyToDefault(sampleInfo.getProjectNo(), sampleInfo.getApprovalNo());
-        if (StrUtil.isNotBlank(proNo)) {
-            projectType += "/" + proNo;
-        }
-        if (StrUtil.isNotBlank(sampleInfo.getProjectName())) {
-            projectType += "/" + sampleInfo.getProjectName();
-        }
-        String now = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_TIME_PATTERN);
-        String dingMsg = String.format("【%s】【%s】%s执行项目", projectType, UserUtil.getNickName(), now);
-
-        Set<Long> userIds = new HashSet<>();
-        //申请人
-        userIds.add(sampleInfo.getApplicantId());
-        //检测人员
-        if (StrUtil.isNotBlank(sampleInfo.getInspector())) {
-            JSON.parseArray(sampleInfo.getInspector()).forEach(item -> {
-                JSONObject obj = (JSONObject) item;
-                if (obj.containsKey("id")) {
-                    userIds.add(obj.getLong("id"));
-                }
-            });
-        }
+    public void afterAudit(Long sampleId) {
+        SampleEditService.super.afterAudit(sampleId);
+        commonSampleEditService.afterAudit(sampleId);
+    }
 
-        List<MessageUserDTO> users = userIds.stream()
-                .map(x -> {
-                    MessageUserDTO messageUserDTO = new MessageUserDTO();
-                    messageUserDTO.setUserId(x);
-                    return messageUserDTO;
-                })
-                .collect(Collectors.toList());
-        notifyService.sendDingding("样本送检", dingMsg, users);
+    @Override
+    public void afterExecute(Long sampleId) {
+        commonSampleEditService.afterExecuteNotify(sampleId, new HashSet<>());
     }
 
     @Override

+ 11 - 41
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/biz/SampleAnimalServiceImpl.java

@@ -14,12 +14,10 @@ import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.serializer.SerializerFeature;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.github.jfcloud.admin.api.sys.dto.message.MessageUserDTO;
 import com.github.jfcloud.gene.cache.UserIdNameCache;
 import com.github.jfcloud.gene.common.constant.StrConstant;
 import com.github.jfcloud.gene.common.constant.WhetherEnum;
 import com.github.jfcloud.gene.common.util.CustomIdGenerator;
-import com.github.jfcloud.gene.common.util.UserUtil;
 import com.github.jfcloud.gene.constants.GeneStatusEnum;
 import com.github.jfcloud.gene.file.service.FileInfoService;
 import com.github.jfcloud.gene.flow.entity.FlowAudit;
@@ -27,7 +25,6 @@ import com.github.jfcloud.gene.flow.service.FlowAuditService;
 import com.github.jfcloud.gene.flow.service.NotifyService;
 import com.github.jfcloud.gene.sample.entity.*;
 import com.github.jfcloud.gene.sample.enums.PathogenSampleTypeEnum;
-import com.github.jfcloud.gene.sample.enums.SampleTypeEnum;
 import com.github.jfcloud.gene.sample.enums.ScientificSampleTypeEnum;
 import com.github.jfcloud.gene.sample.mapper.InspectionCommissionMapper;
 import com.github.jfcloud.gene.sample.mapper.SampleAnimalMapper;
@@ -51,7 +48,9 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.lang.reflect.Field;
-import java.util.*;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -70,6 +69,7 @@ public class SampleAnimalServiceImpl extends ServiceImpl<SampleAnimalMapper, Sam
     private final FlowAuditService flowAuditService;
     private final DBSystemPropertiesService systemPropertiesService;
     private final NotifyService notifyService;
+    private final CommonSampleEditServiceImpl commonSampleEditService;
 
     @Override
     public void removeBySampleId(Long sampleId) {
@@ -275,38 +275,15 @@ public class SampleAnimalServiceImpl extends ServiceImpl<SampleAnimalMapper, Sam
     }
 
     @Override
-    public void afterExecute(Long sampleId) {
-        //钉钉消息通知申请人、检测人员
-        if (!systemPropertiesService.getBooleanValue("sample.afterExecute.dingding.enable")) {
-            return;
-        }
-
-        SampleInfo sampleInfo = sampleInfoMapper.selectById(sampleId);
-
-        String projectType = "样本送检/" + Objects.requireNonNull(SampleTypeEnum.resolve(sampleInfo.getType())).getLabel();
-        String proNo = StrUtil.emptyToDefault(sampleInfo.getProjectNo(), sampleInfo.getApprovalNo());
-        if (StrUtil.isNotBlank(proNo)) {
-            projectType += "/" + proNo;
-        }
-        if (StrUtil.isNotBlank(sampleInfo.getProjectName())) {
-            projectType += "/" + sampleInfo.getProjectName();
-        }
-        String now = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_TIME_PATTERN);
-        String dingMsg = String.format("【%s】【%s】%s执行项目", projectType, UserUtil.getNickName(), now);
+    public void afterAudit(Long sampleId) {
+        SampleEditService.super.afterAudit(sampleId);
+        commonSampleEditService.afterAudit(sampleId);
+    }
 
+    @Override
+    public void afterExecute(Long sampleId) {
         Set<Long> userIds = new HashSet<>();
 
-        //申请人
-        userIds.add(sampleInfo.getApplicantId());
-        //检测人员
-        if (StrUtil.isNotBlank(sampleInfo.getInspector())) {
-            JSON.parseArray(sampleInfo.getInspector()).forEach(item -> {
-                JSONObject obj = (JSONObject) item;
-                if (obj.containsKey("id")) {
-                    userIds.add(obj.getLong("id"));
-                }
-            });
-        }
         //委托单接收人
         List<CommissionVo> commissionList = inspectionCommissionMapper.getCommissionList(sampleId);
         if (CollUtil.isNotEmpty(commissionList)) {
@@ -329,14 +306,7 @@ public class SampleAnimalServiceImpl extends ServiceImpl<SampleAnimalMapper, Sam
             });
         }
 
-        List<MessageUserDTO> users = userIds.stream()
-                .map(x -> {
-                    MessageUserDTO messageUserDTO = new MessageUserDTO();
-                    messageUserDTO.setUserId(x);
-                    return messageUserDTO;
-                })
-                .collect(Collectors.toList());
-        notifyService.sendDingding("样本送检", dingMsg, users);
+        commonSampleEditService.afterExecuteNotify(sampleId, userIds);
     }
 
     @Override

+ 140 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/biz/SampleFoodServiceImpl.java

@@ -0,0 +1,140 @@
+package com.github.jfcloud.gene.sample.service.biz;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.Assert;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.github.jfcloud.admin.api.sys.dto.message.MessageUserDTO;
+import com.github.jfcloud.gene.common.util.CustomIdGenerator;
+import com.github.jfcloud.gene.common.util.UserUtil;
+import com.github.jfcloud.gene.constants.GeneStatusEnum;
+import com.github.jfcloud.gene.flow.service.NotifyService;
+import com.github.jfcloud.gene.sample.entity.SampleFood;
+import com.github.jfcloud.gene.sample.entity.SampleFoodDetail;
+import com.github.jfcloud.gene.sample.entity.SampleInfo;
+import com.github.jfcloud.gene.sample.mapper.SampleFoodMapper;
+import com.github.jfcloud.gene.sample.mapper.SampleInfoMapper;
+import com.github.jfcloud.gene.sample.service.SampleEditService;
+import com.github.jfcloud.gene.sample.service.SampleFoodDetailService;
+import com.github.jfcloud.gene.sample.service.SampleFoodService;
+import com.github.jfcloud.gene.sample.vo.SampleFoodDetailVo;
+import com.github.jfcloud.gene.sample.vo.SampleFoodVo;
+import com.github.jfcloud.gene.sample.vo.SampleSubmitVo;
+import com.github.jfcloud.gene.sys.service.DBSystemPropertiesService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SampleFoodServiceImpl extends ServiceImpl<SampleFoodMapper, SampleFood> implements SampleFoodService, SampleEditService {
+
+    private final SampleInfoMapper sampleInfoMapper;
+    private final SampleFoodDetailService sampleFoodDetailService;
+    private final CommonSampleEditServiceImpl commonSampleEditService;
+    private final DBSystemPropertiesService systemPropertiesService;
+    private final NotifyService notifyService;
+
+    @Override
+    public void removeBySampleId(Long sampleId) {
+        List<SampleFood> sampleFoods = list(new LambdaQueryWrapper<>(SampleFood.class)
+                .select(SampleFood::getId)
+                .eq(SampleFood::getSampleId, sampleId));
+        if (sampleFoods.isEmpty()) {
+            return;
+        }
+
+        Set<Long> foodIdSet = sampleFoods.stream().map(SampleFood::getId).collect(Collectors.toSet());
+        removeBatchByIds(foodIdSet);
+
+        //删除详情
+        sampleFoodDetailService.remove(new LambdaQueryWrapper<>(SampleFoodDetail.class)
+                .in(SampleFoodDetail::getSampleFoodId, foodIdSet));
+    }
+
+    @Override
+    public void save(Long sampleId, SampleSubmitVo vo) {
+        Assert.notNull(vo.getAnimalFood(), "宠物食品送检不能为空");
+
+        //先删除之前的记录
+        removeBySampleId(sampleId);
+
+        //保存动物食品送检信息
+        SampleFood sampleFood = BeanUtil.copyProperties(vo.getAnimalFood(), SampleFood.class);
+        sampleFood.setId(CustomIdGenerator.nextId());
+        sampleFood.setSampleId(sampleId);
+        sampleFood.insert();
+
+        //保存详情
+        List<SampleFoodDetailVo> detailList = vo.getAnimalFood().getDetailList();
+        if (!detailList.isEmpty()) {
+            List<SampleFoodDetail> sampleFoodDetails = BeanUtil.copyToList(detailList, SampleFoodDetail.class);
+            sampleFoodDetails.forEach(detail -> detail.setSampleFoodId(sampleFood.getId()));
+            sampleFoodDetailService.saveBatch(sampleFoodDetails);
+        }
+
+        generate(sampleId);
+    }
+
+    @Override
+    public void generate(Long sampleId) {
+
+    }
+
+    @Override
+    public void afterAudit(Long sampleId) {
+        SampleInfo sampleInfo = sampleInfoMapper.selectById(sampleId);
+        if (sampleInfo == null) {
+            return;
+        }
+
+        SampleEditService.super.afterAudit(sampleId);
+
+        //如果审核完成,提醒送检人和收样人
+        if (GeneStatusEnum.COMPLETED.getStatus().equals(sampleInfo.getStatus())) {
+            String now = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_TIME_PATTERN);
+            String dingMsg = String.format("【%s】【%s/%s】%s同意,请执行项目", sampleInfo.projectType(),
+                    GeneStatusEnum.PROJECT_MANAGEMENT.getDescription(), UserUtil.getNickName(), now);
+
+            //查询是否需要钉钉通知
+            String key = String.format("sample.%s.dingding.enable", sampleInfo.getStatus());
+            if (systemPropertiesService.getBooleanValue(key)) {
+                SampleFoodVo detail = getDetail(sampleId);
+
+                List<MessageUserDTO> messageUsers = new ArrayList<>();
+                MessageUserDTO user1 = new MessageUserDTO();
+                user1.setUserId(detail.getSenderContactId());
+                messageUsers.add(user1);
+                MessageUserDTO user2 = new MessageUserDTO();
+                user2.setUserId(detail.getReceiverId());
+                messageUsers.add(user2);
+                notifyService.sendDingding("样本送检", dingMsg, messageUsers);
+            }
+            return;
+        }
+
+        commonSampleEditService.afterAudit(sampleId);
+    }
+
+    @Override
+    public SampleFoodVo getDetail(Long id) {
+        SampleFood sampleFood = getById(id);
+        if (sampleFood == null) {
+            return null;
+        }
+        List<SampleFoodDetail> detailList = sampleFoodDetailService.list(new LambdaQueryWrapper<>(SampleFoodDetail.class).eq(SampleFoodDetail::getSampleFoodId, id));
+
+        SampleFoodVo sampleFoodVo = BeanUtil.copyProperties(sampleFood, SampleFoodVo.class);
+        sampleFoodVo.setDetailList(BeanUtil.copyToList(detailList, SampleFoodDetailVo.class));
+        return sampleFoodVo;
+    }
+}

+ 12 - 43
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/biz/SamplePathologicalServiceImpl.java

@@ -13,12 +13,10 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.github.jfcloud.admin.api.sys.dto.message.MessageUserDTO;
 import com.github.jfcloud.gene.cache.UserIdNameCache;
 import com.github.jfcloud.gene.common.constant.StrConstant;
 import com.github.jfcloud.gene.common.constant.WhetherEnum;
 import com.github.jfcloud.gene.common.util.CustomIdGenerator;
-import com.github.jfcloud.gene.common.util.UserUtil;
 import com.github.jfcloud.gene.constants.GeneStatusEnum;
 import com.github.jfcloud.gene.file.entity.FileInfo;
 import com.github.jfcloud.gene.file.service.FileInfoService;
@@ -29,7 +27,6 @@ import com.github.jfcloud.gene.flow.service.NotifyService;
 import com.github.jfcloud.gene.sample.entity.PathologicalPreExperiment;
 import com.github.jfcloud.gene.sample.entity.SampleInfo;
 import com.github.jfcloud.gene.sample.entity.SamplePathological;
-import com.github.jfcloud.gene.sample.enums.SampleTypeEnum;
 import com.github.jfcloud.gene.sample.mapper.SampleInfoMapper;
 import com.github.jfcloud.gene.sample.mapper.SamplePathologicalMapper;
 import com.github.jfcloud.gene.sample.service.PathologicalPreExperimentService;
@@ -47,7 +44,10 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
@@ -73,6 +73,7 @@ public class SamplePathologicalServiceImpl extends ServiceImpl<SamplePathologica
     private final FlowAuditService flowAuditService;
     private final DBSystemPropertiesService systemPropertiesService;
     private final NotifyService notifyService;
+    private final CommonSampleEditServiceImpl commonSampleEditService;
 
     @Override
     public void removeBySampleId(Long sampleId) {
@@ -274,46 +275,14 @@ public class SamplePathologicalServiceImpl extends ServiceImpl<SamplePathologica
     }
 
     @Override
-    public void afterExecute(Long sampleId) {
-        //钉钉消息通知申请人、检测人员
-        if (!systemPropertiesService.getBooleanValue("sample.afterExecute.dingding.enable")) {
-            return;
-        }
-
-        SampleInfo sampleInfo = sampleInfoMapper.selectById(sampleId);
-
-        String projectType = "样本送检/" + Objects.requireNonNull(SampleTypeEnum.resolve(sampleInfo.getType())).getLabel();
-        String proNo = StrUtil.emptyToDefault(sampleInfo.getProjectNo(), sampleInfo.getApprovalNo());
-        if (StrUtil.isNotBlank(proNo)) {
-            projectType += "/" + proNo;
-        }
-        if (StrUtil.isNotBlank(sampleInfo.getProjectName())) {
-            projectType += "/" + sampleInfo.getProjectName();
-        }
-        String now = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_TIME_PATTERN);
-        String dingMsg = String.format("【%s】【%s】%s执行项目", projectType, UserUtil.getNickName(), now);
-
-        Set<Long> userIds = new HashSet<>();
-        //申请人
-        userIds.add(sampleInfo.getApplicantId());
-        //检测人员
-        if (StrUtil.isNotBlank(sampleInfo.getInspector())) {
-            JSON.parseArray(sampleInfo.getInspector()).forEach(item -> {
-                JSONObject obj = (JSONObject) item;
-                if (obj.containsKey("id")) {
-                    userIds.add(obj.getLong("id"));
-                }
-            });
-        }
+    public void afterAudit(Long sampleId) {
+        SampleEditService.super.afterAudit(sampleId);
+        commonSampleEditService.afterAudit(sampleId);
+    }
 
-        List<MessageUserDTO> users = userIds.stream()
-                .map(x -> {
-                    MessageUserDTO messageUserDTO = new MessageUserDTO();
-                    messageUserDTO.setUserId(x);
-                    return messageUserDTO;
-                })
-                .collect(Collectors.toList());
-        notifyService.sendDingding("样本送检", dingMsg, users);
+    @Override
+    public void afterExecute(Long sampleId) {
+        commonSampleEditService.afterExecuteNotify(sampleId, new HashSet<>());
     }
 
     @Override

+ 11 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/impl/SampleFoodDetailServiceImpl.java

@@ -0,0 +1,11 @@
+package com.github.jfcloud.gene.sample.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.github.jfcloud.gene.sample.entity.SampleFoodDetail;
+import com.github.jfcloud.gene.sample.mapper.SampleFoodDetailMapper;
+import com.github.jfcloud.gene.sample.service.SampleFoodDetailService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SampleFoodDetailServiceImpl extends ServiceImpl<SampleFoodDetailMapper, SampleFoodDetail> implements SampleFoodDetailService {
+}

+ 11 - 83
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/service/impl/SampleInfoServiceImpl.java

@@ -1,9 +1,6 @@
 package com.github.jfcloud.gene.sample.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.date.DatePattern;
-import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
@@ -13,7 +10,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.github.jfcloud.admin.api.sys.dto.message.MessageUserDTO;
 import com.github.jfcloud.common.core.util.R;
 import com.github.jfcloud.common.data.datascope.DataScope;
 import com.github.jfcloud.gene.cache.UserIdNameCache;
@@ -26,7 +22,6 @@ import com.github.jfcloud.gene.file.vo.FileVo;
 import com.github.jfcloud.gene.flow.entity.FlowAudit;
 import com.github.jfcloud.gene.flow.service.FlowAuditService;
 import com.github.jfcloud.gene.flow.service.FlowFileVersionService;
-import com.github.jfcloud.gene.flow.service.NotifyService;
 import com.github.jfcloud.gene.flow.vo.SynProjectVo;
 import com.github.jfcloud.gene.sample.dto.SampleAuditDto;
 import com.github.jfcloud.gene.sample.dto.SampleDetailDto;
@@ -38,6 +33,7 @@ import com.github.jfcloud.gene.sample.service.SampleEditService;
 import com.github.jfcloud.gene.sample.service.SampleInfoService;
 import com.github.jfcloud.gene.sample.service.biz.SampleAnimalImagingServiceImpl;
 import com.github.jfcloud.gene.sample.service.biz.SampleAnimalServiceImpl;
+import com.github.jfcloud.gene.sample.service.biz.SampleFoodServiceImpl;
 import com.github.jfcloud.gene.sample.service.biz.SamplePathologicalServiceImpl;
 import com.github.jfcloud.gene.sample.vo.SampleAuditVo;
 import com.github.jfcloud.gene.sample.vo.SamplePageVo;
@@ -63,10 +59,10 @@ public class SampleInfoServiceImpl extends ServiceImpl<SampleInfoMapper, SampleI
     private final SampleAnimalServiceImpl sampleAnimalService;
     private final SamplePathologicalServiceImpl samplePathologicalService;
     private final SampleAnimalImagingServiceImpl sampleAnimalImagingService;
+    private final SampleFoodServiceImpl sampleFoodService;
     private final FlowFileVersionService fileVersionService;
     private final UserIdNameCache userIdNameCache;
     private final FlowAuditService flowAuditService;
-    private final NotifyService notifyService;
     private final DBSystemPropertiesService systemPropertiesService;
     private final RemoteProjectService remoteProjectService;
     private final SubProjectFeign subProjectFeign;
@@ -85,6 +81,8 @@ public class SampleInfoServiceImpl extends ServiceImpl<SampleInfoMapper, SampleI
                 return sampleAnimalService;
             case ANIMAL_IMAGE:
                 return sampleAnimalImagingService;
+            case ANIMAL_FOOD:
+                return sampleFoodService;
         }
 
         log.error("样本检测类型参数错误 type={}", type);
@@ -167,7 +165,7 @@ public class SampleInfoServiceImpl extends ServiceImpl<SampleInfoMapper, SampleI
                     .setCreateTime(new Date());
         }
         updateInfo.updateById();
-        notify(id);
+        getServiceByType(sampleInfo.getType()).afterAudit(id);
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -237,6 +235,9 @@ public class SampleInfoServiceImpl extends ServiceImpl<SampleInfoMapper, SampleI
             case ANIMAL_IMAGE:
                 detailDto.setAnimalImage(sampleAnimalImagingService.getDetail(id));
                 break;
+            case ANIMAL_FOOD:
+                detailDto.setAnimalFood(sampleFoodService.getDetail(id));
+                break;
         }
 
         return detailDto;
@@ -247,6 +248,7 @@ public class SampleInfoServiceImpl extends ServiceImpl<SampleInfoMapper, SampleI
         SampleInfo sampleInfo = getById(id);
         Assert.notNull(sampleInfo, "样本检测信息不存在");
         Assert.isFalse(GeneStatusEnum.SUBMIT_STATUS.contains(sampleInfo.getStatus()), "样本检测流程状态错误");
+        SampleEditService service = getServiceByType(sampleInfo.getType());
 
         String nextStatus = "";
         GeneStatusEnum statusEnum = Objects.requireNonNull(GeneStatusEnum.getByStatus(sampleInfo.getStatus()));
@@ -287,8 +289,7 @@ public class SampleInfoServiceImpl extends ServiceImpl<SampleInfoMapper, SampleI
                     .setStatus(GeneStatusEnum.REJECTED.getStatus())
                     .updateById();
 
-            generate(id);
-            notify(id);
+            service.afterAudit(id);
             return;
         }
 
@@ -311,8 +312,7 @@ public class SampleInfoServiceImpl extends ServiceImpl<SampleInfoMapper, SampleI
                 .setStatus(nextStatus)
                 .updateById();
 
-        generate(id);
-        notify(id);
+        service.afterAudit(id);
     }
 
     @Override
@@ -410,78 +410,6 @@ public class SampleInfoServiceImpl extends ServiceImpl<SampleInfoMapper, SampleI
         getServiceByType(sampleInfo.getType()).afterExecute(id);
     }
 
-    private void notify(Long sampleId) {
-        SampleInfo sampleInfo = getById(sampleId);
-        if (sampleInfo == null) {
-            return;
-        }
-
-        //查询流程前一个状态
-        List<FlowAudit> flowAudits = flowAuditService.auditList(sampleId, "sample." + sampleInfo.getType(), false);
-        FlowAudit previousAudit = null;
-        String previousStatus = "";
-        String previousUser = "";
-        if (CollUtil.isNotEmpty(flowAudits)) {
-            previousAudit = flowAudits.get(0);
-            GeneStatusEnum statusEnum = GeneStatusEnum.getByStatus(previousAudit.getFlowStatus());
-            previousStatus = statusEnum != null ? statusEnum.getDescription() : "";
-            previousUser = userIdNameCache.getNicknameByUserId(previousAudit.getCreateBy());
-        }
-
-        String projectType = "样本送检/" + Objects.requireNonNull(SampleTypeEnum.resolve(sampleInfo.getType())).getLabel();
-        String proNo = StrUtil.emptyToDefault(sampleInfo.getProjectNo(), sampleInfo.getApprovalNo());
-        if (StrUtil.isNotBlank(proNo)) {
-            projectType += "/" + proNo;
-        }
-        String projectName = sampleInfo.getProjectName();
-        if (StrUtil.isBlank(projectName) && StrUtil.isNotBlank(proNo)) {
-            projectName = Objects.requireNonNull(SampleTypeEnum.resolve(sampleInfo.getType())).getLabel() + "-" + proNo;
-        }
-        if (StrUtil.isNotBlank(projectName)) {
-            projectType += "/" + projectName;
-        }
-
-        String now = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_TIME_PATTERN);
-
-        Long userId;
-        String dingMsg;
-        GeneStatusEnum statusEnum = Objects.requireNonNull(GeneStatusEnum.getByStatus(sampleInfo.getStatus()));
-        switch (statusEnum) {
-            case REJECTED:
-                //驳回通知申请人
-                userId = Long.valueOf(sampleInfo.getCreateBy());
-                dingMsg = String.format("【%s】【%s/%s】%s不同意,请修正后再提交", projectType, previousStatus, previousUser, now);
-                break;
-            case DEPART_LEADER:
-                userId = sampleInfo.getDepartLeaderId();
-                dingMsg = String.format("【%s】【%s】%s提交,请进行审查", projectType, userIdNameCache.getNicknameByUserId(sampleInfo.getCreateBy()), now);
-                break;
-            case PROJECT_MANAGEMENT:
-                userId = sampleInfo.getProjectManageId();
-                dingMsg = String.format("【%s】【%s/%s】%s同意,请进行审查", projectType, previousStatus, previousUser, now);
-                break;
-            case COMPLETED:
-                //执行
-                userId = Long.valueOf(sampleInfo.getCreateBy());
-                dingMsg = String.format("【%s】【%s/%s】%s同意,请执行项目", projectType, previousStatus, previousUser, now);
-                break;
-            default:
-                log.error("流程项目 ({}) 状态异常 {}", projectName, statusEnum.getDescription());
-                return;
-        }
-
-        if (sampleInfo.getVersion() > 1) {
-            dingMsg += "(第" + sampleInfo.getVersion() + "次申请)";
-        }
-
-        //查询是否需要钉钉通知
-        String key = String.format("sample.%s.dingding.enable", sampleInfo.getStatus());
-        if (systemPropertiesService.getBooleanValue(key)) {
-            MessageUserDTO messageUserDTO = new MessageUserDTO();
-            messageUserDTO.setUserId(userId);
-            notifyService.sendDingding("样本送检", dingMsg, Collections.singletonList(messageUserDTO));
-        }
-    }
 }
 
 

+ 46 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/vo/SampleFoodDetailVo.java

@@ -0,0 +1,46 @@
+package com.github.jfcloud.gene.sample.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class SampleFoodDetailVo {
+
+    @Schema(description = "样品名称")
+    private String sampleName;
+
+    @Schema(description = "生产商")
+    private String manufacturer;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Schema(description = "生产日期")
+    private Date productionDate;
+
+    @Schema(description = "批号")
+    private String batchNumber;
+
+    @Schema(description = "数量")
+    private String quantity;
+
+    @Schema(description = "样品状态")
+    private String sampleStatus;
+
+    @Schema(description = "样品状态-其他")
+    private String sampleStatusOther;
+
+    @Schema(description = "包装方式")
+    private String packaging;
+
+    @Schema(description = "包装方式-其他")
+    private String packagingOther;
+
+    @Schema(description = "检测项目")
+    private String detectionItems;
+
+    @Schema(description = "其他补充信息")
+    private String otherInfo;
+
+}

+ 90 - 0
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/vo/SampleFoodVo.java

@@ -0,0 +1,90 @@
+package com.github.jfcloud.gene.sample.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class SampleFoodVo {
+
+    @Schema(description = "送检目的")
+    private String purpose;
+
+    @NotEmpty(message = "送检方名称不能为空")
+    @Schema(description = "送检方名称")
+    private String senderName;
+
+    @Schema(description = "送检方地址")
+    private String senderAddress;
+
+    @NotEmpty(message = "送检方联系人不能为空")
+    @Schema(description = "送检方联系人id")
+    private Long senderContactId;
+
+    @Schema(description = "送检方联系人名称")
+    private String senderContactName;
+
+    @NotEmpty(message = "送检方联系电话不能为空")
+    @Schema(description = "送检方联系电话")
+    private String senderPhone;
+
+    @Schema(description = "送检方电子邮箱")
+    private String senderEmail;
+
+    @Schema(description = "样品类型")
+    private String sampleType;
+
+    @Schema(description = "样品照片")
+    private String samplePhoto;
+
+    @Schema(description = "样品照片-其他")
+    private String samplePhotoOther;
+
+    @Schema(description = "样品储存要求")
+    private String storageRequirements;
+
+    @Schema(description = "样品储存要求-其他")
+    private String storageRequirementsOther;
+
+    @Schema(description = "样品保留期限")
+    private String retentionPeriod;
+
+    @Schema(description = "是否危险性样品 0否 1是")
+    private String hazard;
+
+    @Schema(description = "样品危害描述")
+    private String hazardDescription;
+
+    @Schema(description = "样品危害描述-其他")
+    private String hazardDescriptionOther;
+
+    @Schema(description = "报告方式")
+    private String reportMethod;
+
+    @Schema(description = "报告类型")
+    private String reportType;
+
+    @Schema(description = "是否评判结果 0否 1是")
+    private String hasEvaluation;
+
+    @Schema(description = "检测结果是否以干基计 0否 1是")
+    private String dryBasis;
+
+    @Schema(description = "其他要求")
+    private String otherRequirements;
+
+    @Schema(description = "服务时限")
+    private String serviceDeadline;
+
+    @Schema(description = "收样人")
+    private Long receiverId;
+
+    @Schema(description = "收样人姓名")
+    private String receiverName;
+
+    private List<SampleFoodDetailVo> detailList = new ArrayList<>();
+
+}

+ 4 - 2
jfcloud-gene-biz/src/main/java/com/github/jfcloud/gene/sample/vo/SampleSubmitVo.java

@@ -22,9 +22,9 @@ public class SampleSubmitVo {
     @Size(max = 255, message = "项目编号长度不能超过255个字符")
     private String projectNo;
 
-    @Schema(description = "类型", example = "病理pathological 动物样本检测animal 动物影像检测animalImage")
+    @Schema(description = "类型", example = "病理pathological 动物样本检测animal 动物影像检测animalImage 宠物食品检测animalFood")
     @NotEmpty(message = "类型不能为空")
-    @Pattern(regexp = "pathological|animal|animalImage", message = "未知类型 type")
+    @Pattern(regexp = "pathological|animal|animalImage|animalFood", message = "未知类型 type")
     private String type;
 
     @Schema(description = "申请人id")
@@ -70,4 +70,6 @@ public class SampleSubmitVo {
     private SamplePathologicalVo pathological;
 
     private SampleAnimalImagingVo animalImage;
+
+    private SampleFoodVo animalFood;
 }

+ 57 - 0
jfcloud-gene-biz/src/main/resources/sql/ddl-20250509.sql

@@ -0,0 +1,57 @@
+-- 送检单主表
+CREATE TABLE sample_food
+(
+    id                         BIGINT PRIMARY KEY COMMENT '检测单ID',
+    sample_id                  BIGINT       not null COMMENT '样本ID',
+    purpose                    VARCHAR(255) COMMENT '送检目的',
+    sender_name                VARCHAR(255) NOT NULL COMMENT '送检方名称',
+    sender_address             VARCHAR(255) NOT NULL COMMENT '送检方地址',
+    sender_contact_id          BIGINT       NOT NULL COMMENT '送检方联系人id',
+    sender_contact_name        VARCHAR(255) NOT NULL COMMENT '送检方联系人名称',
+    sender_phone               VARCHAR(30)  NOT NULL COMMENT '送检方联系电话',
+    sender_email               VARCHAR(100) COMMENT '送检方电子邮箱',
+    sample_type                VARCHAR(255) COMMENT '样品类型',
+    sample_photo               VARCHAR(255) COMMENT '样品照片',
+    sample_photo_other         VARCHAR(255) COMMENT '样品照片-其他',
+    storage_requirements       VARCHAR(255) NOT NULL COMMENT '样品储存要求',
+    storage_requirements_other VARCHAR(255) NULL COMMENT '样品储存要求-其他',
+    retention_period           VARCHAR(255) COMMENT '样品保留期限',
+    hazard                     CHAR(1) COMMENT '是否危险性样品 0否 1是',
+    hazard_description         VARCHAR(255) COMMENT '样品危害描述',
+    hazard_description_other   VARCHAR(255) COMMENT '样品危害描述-其他',
+    report_method              VARCHAR(100) COMMENT '报告方式',
+    report_type                VARCHAR(50)  NOT NULL COMMENT '报告类型',
+    has_evaluation             CHAR(1)      NOT NULL COMMENT '是否评判结果 0否 1是',
+    dry_basis                  CHAR(1)      NOT NULL COMMENT '检测结果是否以干基计 0否 1是',
+    other_requirements         VARCHAR(255) COMMENT '其他要求',
+    service_deadline           VARCHAR(255) NOT NULL COMMENT '服务时限',
+
+    receiver_id                BIGINT       NULL COMMENT '收样人',
+    receiver_name              varchar(255) NULL COMMENT '收样人姓名',
+
+    deleted                    char(1)      NOT null default '0' COMMENT '是否被删除',
+    create_by                  varchar(255) COMMENT '创建人',
+    create_time                DATETIME     NULL COMMENT '创建时间',
+    update_by                  varchar(255) COMMENT '更新人',
+    update_time                DATETIME     NULL COMMENT '更新时间',
+    tenant_id                  bigint COMMENT '租户ID',
+    dept_id                    bigint COMMENT '部门ID'
+) COMMENT '宠物食品检测表';
+
+-- 样品信息表
+CREATE TABLE sample_food_detail
+(
+    id                  BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '样品ID',
+    sample_food_id      INT COMMENT '关联的宠物食品检测表ID',
+    sample_name         VARCHAR(255) NOT NULL COMMENT '样品名称',
+    manufacturer        VARCHAR(255) COMMENT '生产商',
+    production_date     DATE COMMENT '生产日期',
+    batch_number        VARCHAR(50) COMMENT '批号',
+    quantity            VARCHAR(10)  NOT NULL COMMENT '数量',
+    sample_status       VARCHAR(50) COMMENT '样品状态',
+    sample_status_other VARCHAR(255) COMMENT '样品状态-其他',
+    packaging           VARCHAR(50) COMMENT '包装方式',
+    packaging_other     VARCHAR(255) COMMENT '包装方式-其他',
+    detection_items     TEXT         NOT NULL COMMENT '检测项目',
+    other_info          TEXT COMMENT '其他补充信息'
+) COMMENT '宠物食品检测明细表';

+ 7 - 18
jfcloud-gene-common/src/main/java/com/github/jfcloud/gene/common/util/UserUtil.java

@@ -7,7 +7,6 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.stereotype.Component;
 
-import java.util.Collections;
 import java.util.List;
 
 @Component
@@ -15,14 +14,12 @@ import java.util.List;
 @RequiredArgsConstructor
 public class UserUtil {
 
-    private static JfcloudUser getCurrentUser() {
+    private static JfcloudUserInfo getCurrentUser() {
         JfcloudUser user = SecurityUtils.getUser();
-        if (user == null) {
-            //获取当前用户失败,避免空指针
-            user = new JfcloudUser("null", "null", false, false, false, false,
-                    Collections.emptyList(), new JfcloudUserInfo());
+        if (user != null) {
+            return user.getJfcloudUserInfo();
         }
-        return user;
+        return new JfcloudUserInfo();
     }
 
     public static Long getUserId() {
@@ -34,23 +31,15 @@ public class UserUtil {
     }
 
     public static String getNickName() {
-        return getCurrentUser().getJfcloudUserInfo().getNickname();
-    }
-
-    public static String getEmail() {
-        return getCurrentUser().getJfcloudUserInfo().getEmail();
+        return getCurrentUser().getNickname();
     }
 
     public static Long getDeptId() {
-        return getCurrentUser().getJfcloudUserInfo().getDeptId();
+        return getCurrentUser().getDeptId();
     }
 
     public static Long getTenantId() {
-        return getCurrentUser().getJfcloudUserInfo().getTenantId();
-    }
-
-    public static JfcloudUser getUser() {
-        return getCurrentUser();
+        return getCurrentUser().getTenantId();
     }
 
     public static List<Long> getRoles() {