Преглед на файлове

Merge remote-tracking branch 'origin/master'

lipenghui преди 3 дни
родител
ревизия
72fa040792

+ 30 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/annotation/AntiDuplicateSubmit.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.pms.annotation;
+
+/**
+ * @author yc
+ * @version 1.0
+ * @className AntiDuplicateSubmit
+ * @date 2026/1/8 9:19
+ * @description
+ */
+
+import java.lang.annotation.*;
+
+/**
+ * 接口防重复提交/防刷注解
+ * 作用:标记需要防刷的接口,限制指定时间内重复提交
+ */
+@Target(ElementType.METHOD)       // 注解仅作用于方法
+@Retention(RetentionPolicy.RUNTIME) // 运行时生效(AOP能读取到)
+@Documented                      // 生成文档时包含该注解
+public @interface AntiDuplicateSubmit {
+    /**
+     * 防刷时间窗口,单位:秒(默认5秒)
+     */
+    long timeout() default 5;
+
+    /**
+     * 重复提交时的提示信息
+     */
+    String message() default "5秒内不允许重复提交,请稍后再试";
+}

+ 89 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/aspect/AntiDuplicateSubmitAspect.java

@@ -0,0 +1,89 @@
+package cn.iocoder.yudao.module.pms.aspect;
+
+import cn.hutool.core.lang.hash.MurmurHash;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.pms.annotation.AntiDuplicateSubmit;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author yc
+ * @version 1.0
+ * @className AntiDuplicateSubmitAspect
+ * @date 2026/1/8 9:21
+ * @description
+ */
+@Slf4j
+@Aspect
+@Component
+@RequiredArgsConstructor
+public class AntiDuplicateSubmitAspect {
+    private final StringRedisTemplate redisTemplate;
+    private final ObjectMapper objectMapper;
+
+    // 切入点:标注了AntiDuplicateSubmit注解的方法
+    @Pointcut("@annotation(cn.iocoder.yudao.module.pms.annotation.AntiDuplicateSubmit)")
+    public void antiDuplicateSubmitPointcut() {}
+
+    @Around("antiDuplicateSubmitPointcut()")
+    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+        // 1. 获取注解信息
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+        AntiDuplicateSubmit annotation = method.getAnnotation(AntiDuplicateSubmit.class);
+        long timeout = annotation.timeout();
+        String message = annotation.message();
+
+        // 2. 获取请求信息
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        HttpServletRequest request = attributes.getRequest();
+        String requestURI = request.getRequestURI();
+
+        // 3. 生成请求唯一标识(请求URI + 参数哈希)
+        String requestBody = "";
+        Object[] args = joinPoint.getArgs();
+        if (args != null && args.length > 0) {
+            try {
+                requestBody = objectMapper.writeValueAsString(args);
+            } catch (Exception e) {
+                log.warn("序列化请求参数失败", e);
+                requestBody = args.toString();
+            }
+        }
+
+        // 使用MurmurHash生成哈希值,避免参数过长
+        int hash = MurmurHash.hash32(requestBody.getBytes());
+        String redisKey = "anti_duplicate:".concat(requestURI).concat(":").concat(String.valueOf(hash));
+
+        // 4. 检查Redis中是否存在该请求
+        Boolean exists = redisTemplate.opsForValue().setIfAbsent(redisKey, "1", timeout, TimeUnit.SECONDS);
+        if (Boolean.FALSE.equals(exists)) {
+            // 5秒内重复请求,直接返回错误
+            log.warn("接口防刷触发:{},请求参数:{}", requestURI, requestBody);
+            return CommonResult.error(2,message); // 替换为你项目中实际的返回格式
+        }
+
+        // 5. 正常执行方法
+        try {
+            return joinPoint.proceed();
+        } catch (Exception e) {
+            // 如果方法执行异常,删除Redis中的标记(可选,根据业务需求)
+            redisTemplate.delete(redisKey);
+            throw e;
+        }
+    }
+}

+ 87 - 22
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotopeationfill/IotOpeationFillController.java

@@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
+import cn.iocoder.yudao.module.pms.annotation.AntiDuplicateSubmit;
 import cn.iocoder.yudao.module.pms.controller.admin.iotmodeltemplateattrs.vo.IotModelTemplateAttrsRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.*;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrhdailyreport.vo.IotRhDailyReportSaveReqVO;
@@ -304,6 +305,8 @@ public class IotOpeationFillController {
     @Transactional(rollbackFor = Exception.class)
     @PostMapping("/insertDataList")
     @Operation(summary = "创建运行记录工单设备集填写信息")
+    // 添加防刷注解,5秒内不允许重复请求
+    @AntiDuplicateSubmit(timeout = 5, message = "5秒内不允许重复提交,请稍后再试")
     public CommonResult<Integer> insertDataList(@Valid @RequestBody List<IotOperationSaveInfoVO> createReqVO) {
 
         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
@@ -1343,11 +1346,18 @@ public class IotOpeationFillController {
                 if (deviceVO != null) {
                     // 数采设备逻辑
                     double diff = Double.parseDouble(deviceVO.getLatestData()) - Double.parseDouble(deviceVO.getEarliestData());
-                    attrsDO.setFillContent(String.valueOf(diff));
-                    attrsDO.setTotalRunTime(BigDecimal.valueOf(Double.parseDouble(deviceVO.getLatestData())));
-                    attrsDO.setIsCollection(1);
+                    //如果当日差值为0,则证明该设备虽然是数采,但累计有问题,执行非数采逻辑
+                    if(diff==0.0){
+                        unCollectionDev(logInfoMap, maxLogMap, attrsDO, attrName);
+                    }else{
+                        attrsDO.setFillContent(String.valueOf(diff));
+                        attrsDO.setTotalRunTime(BigDecimal.valueOf(Double.parseDouble(deviceVO.getLatestData())));
+                        attrsDO.setIsCollection(1);
+                    }
                 }else{
-                    IotDeviceRunLogDO logInfo = logInfoMap.get(attrName);
+                    // 非数采设备逻辑
+                    unCollectionDev(logInfoMap, maxLogMap, attrsDO, attrName);
+                    /*IotDeviceRunLogDO logInfo = logInfoMap.get(attrName);
                     IotDeviceRunLogDO maxLog = maxLogMap.get(attrName);
 
                     if (logInfo != null) {
@@ -1357,11 +1367,12 @@ public class IotOpeationFillController {
                         attrsDO.setFillContent("");
                         attrsDO.setTotalRunTime(maxLog != null ? maxLog.getTotalRunTime() : BigDecimal.ZERO);
                     }
-                    attrsDO.setIsCollection(0);
+                    attrsDO.setIsCollection(0);*/
                 }
             }else{
                 // 非数采设备逻辑
-                IotDeviceRunLogDO logInfo = logInfoMap.get(attrName);
+                unCollectionDev(logInfoMap, maxLogMap, attrsDO, attrName);
+                /*IotDeviceRunLogDO logInfo = logInfoMap.get(attrName);
                 IotDeviceRunLogDO maxLog = maxLogMap.get(attrName);
 
                 if (logInfo != null) {
@@ -1371,13 +1382,26 @@ public class IotOpeationFillController {
                     attrsDO.setFillContent("");
                     attrsDO.setTotalRunTime(maxLog != null ? maxLog.getTotalRunTime() : BigDecimal.ZERO);
                 }
-                attrsDO.setIsCollection(0);
+                attrsDO.setIsCollection(0);*/
             }
 
 
         }
     }
 
+    //非数采设备处理逻辑
+    private void unCollectionDev(Map<String, IotDeviceRunLogDO> logInfoMap, Map<String, IotDeviceRunLogDO> maxLogMap, IotModelTemplateAttrsDO attrsDO, String attrName) {
+        IotDeviceRunLogDO logInfo = logInfoMap.get(attrName);
+        IotDeviceRunLogDO maxLog = maxLogMap.get(attrName);
+        if (logInfo != null) {
+            attrsDO.setFillContent(logInfo.getFillContent());
+            attrsDO.setTotalRunTime(logInfo.getTotalRunTime() != null ? logInfo.getTotalRunTime() : BigDecimal.ZERO);
+        } else {
+            attrsDO.setFillContent("");
+            attrsDO.setTotalRunTime(maxLog != null ? maxLog.getTotalRunTime() : BigDecimal.ZERO);
+        }
+        attrsDO.setIsCollection(0);
+    }
 
 
     //**************
@@ -1615,16 +1639,27 @@ public class IotOpeationFillController {
 
                     DeviceVO deviceVO = iDeviceService.getYesInfo(dv);
 
-                    if(!StringUtils.isEmpty(deviceVO) && !deviceVO.getEarliestData().equals("0.0")){
-                        attrsDO.setFillContent(
+                    if(!StringUtils.isEmpty(deviceVO)){
+
+                        double diff = Double.parseDouble(deviceVO.getLatestData()) - Double.parseDouble(deviceVO.getEarliestData());
+
+                        if(diff==0.0){
+                            extracted(logDO1, attrsDO);
+                        }else{
+                            attrsDO.setFillContent(String.valueOf(diff));
+                            attrsDO.setTotalRunTime(BigDecimal.valueOf(Double.parseDouble(deviceVO.getLatestData())));
+                            /**
+                             * 设置为数采
+                             */
+                            attrsDO.setIsCollection(1);
+                        }
+
+                        /*attrsDO.setFillContent(
                                 String.valueOf(Double.parseDouble(deviceVO.getLatestData())-Double.parseDouble(deviceVO.getEarliestData())));
-                        attrsDO.setTotalRunTime(BigDecimal.valueOf(Double.parseDouble(deviceVO.getLatestData())));
-                        /**
-                         * 设置为数采
-                         */
-                        attrsDO.setIsCollection(1);
+                        attrsDO.setTotalRunTime(BigDecimal.valueOf(Double.parseDouble(deviceVO.getLatestData())));*/
+
                     }else{
-                        logDO1.setPointName(attrsDO.getName());
+                        /*logDO1.setPointName(attrsDO.getName());
                         IotDeviceRunLogDO logInfo = iotOpeationFillService.getLogInfo(logDO1);
                         IotDeviceRunLogDO maxLog = iotOpeationFillService.getMaxFillInfo(logDO1);
                         if(!StringUtils.isEmpty(logInfo)){
@@ -1636,12 +1671,10 @@ public class IotOpeationFillController {
                             }else{
                                 attrsDO.setTotalRunTime(maxLog.getTotalRunTime());
                             }
-                            /**
+                            *//**
                              * 设置为非数采
-                             */
+                             *//*
                             attrsDO.setIsCollection(0);
-
-
                         }else{
                             attrsDO.setFillContent("");
                             if(StringUtils.isEmpty(maxLog)){
@@ -1649,11 +1682,12 @@ public class IotOpeationFillController {
                             }else{
                                 attrsDO.setTotalRunTime(maxLog.getTotalRunTime());
                             }
-                            /**
+                            *//**
                              * 设置为非数采
-                             */
+                             *//*
                             attrsDO.setIsCollection(0);
-                        }
+                        }*/
+                        extracted(logDO1, attrsDO);
                     }
                 }
             }else{
@@ -1711,6 +1745,37 @@ public class IotOpeationFillController {
         return success(BeanUtils.toBean(resut,IotModelTemplateAttrsDO1.class));
     }
 
+    private void extracted(IotDeviceRunLogDO logDO1, IotModelTemplateAttrsDO attrsDO) {
+        logDO1.setPointName(attrsDO.getName());
+        IotDeviceRunLogDO logInfo = iotOpeationFillService.getLogInfo(logDO1);
+        IotDeviceRunLogDO maxLog = iotOpeationFillService.getMaxFillInfo(logDO1);
+        if(!StringUtils.isEmpty(logInfo)){
+
+            attrsDO.setFillContent(logInfo.getFillContent());
+
+            if(StringUtils.isEmpty(maxLog)){
+                attrsDO.setTotalRunTime(BigDecimal.valueOf(0));
+            }else{
+                attrsDO.setTotalRunTime(maxLog.getTotalRunTime());
+            }
+            /**
+             * 设置为非数采
+             */
+            attrsDO.setIsCollection(0);
+        }else{
+            attrsDO.setFillContent("");
+            if(StringUtils.isEmpty(maxLog)){
+                attrsDO.setTotalRunTime(BigDecimal.valueOf(0));
+            }else{
+                attrsDO.setTotalRunTime(maxLog.getTotalRunTime());
+            }
+            /**
+             * 设置为非数采
+             */
+            attrsDO.setIsCollection(0);
+        }
+    }
+
     // 工具方法:将字符串转换为整数,空字符串视为0
     private double safeParseInt(String str) {
         if (str == null || str.trim().isEmpty()) {

+ 3 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttask/vo/IotProjectTaskPageReqVO.java

@@ -106,4 +106,7 @@ public class IotProjectTaskPageReqVO extends PageParam {
     @Schema(description = "平台井标识 Y N")
     private String platformFlag;
 
+    @Schema(description = "部门名称 模糊搜索")
+    private String deptName;
+
 }

+ 1 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotprojecttask/IotProjectTaskMapper.java

@@ -107,6 +107,7 @@ public interface IotProjectTaskMapper extends BaseMapperX<IotProjectTaskDO> {
                                      @Param("wellName") String wellName,
                                      @Param("createTime") LocalDateTime[] createTime,
                                      @Param("deptIds") Collection<Long> deptIds,
+                                     @Param("searchDeptIds") Collection<Long> searchDeptIds,
                                      @Param("projectIds") Collection<Long> projectIds,
                                      @Param("platformFlag") String platformFlag
     );

+ 19 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprojecttask/IotProjectTaskServiceImpl.java

@@ -15,7 +15,9 @@ import cn.iocoder.yudao.module.pms.dal.mysql.iotprojectinfo.IotProjectInfoMapper
 import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttask.IotProjectTaskMapper;
 import cn.iocoder.yudao.module.pms.service.iotprojectinfo.IotProjectInfoService;
 import cn.iocoder.yudao.module.supplier.service.product.SupplierService;
+import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
+import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.springframework.stereotype.Service;
@@ -28,6 +30,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_PROJECT_TASK_NOT_EXISTS;
 
 /**
@@ -48,6 +51,8 @@ public class IotProjectTaskServiceImpl implements IotProjectTaskService {
     private SupplierService iotSupplierService;
     @Resource
     private IotProjectInfoMapper iotProjectInfoMapper;
+    @Resource
+    private DeptMapper deptMapper;
 
     @Override
     public Long createIotProjectTask(IotProjectTaskSaveReqVO createReqVO) {
@@ -253,6 +258,19 @@ public class IotProjectTaskServiceImpl implements IotProjectTaskService {
             }
         }
 
+        List<Long> qualifiedDeptIds = new ArrayList<>();
+        // 根据部门名称 模糊搜索
+        if (StrUtil.isNotBlank(vo.getDeptName())) {
+            // 根据名称查询部门id
+            DeptListReqVO reqVO = new DeptListReqVO();
+            reqVO.setName(vo.getDeptName());
+            List<DeptDO> qualifiedDepts = deptMapper.selectList(reqVO);
+            qualifiedDeptIds = convertList(qualifiedDepts, DeptDO::getId);
+            if (CollUtil.isEmpty(qualifiedDeptIds)) {
+                return new PageResult<>(new ArrayList<>(), 0l);
+            }
+        }
+
         IPage<IotProjectTaskDO> taskDOIPage = iotProjectTaskMapper.taskList(Page.of(vo.getPageNo(), vo.getPageSize()),
                 vo.getCompanyId(),
                 vo.getManufactureName(),
@@ -261,6 +279,7 @@ public class IotProjectTaskServiceImpl implements IotProjectTaskService {
                 vo.getWellName(),
                 vo.getCreateTime(),
                 departmentIds,
+                qualifiedDeptIds,
                 projectIds,
                 vo.getPlatformFlag());
         return new PageResult<>(taskDOIPage.getRecords(), taskDOIPage.getTotal());

+ 21 - 14
yudao-module-pms/yudao-module-pms-biz/src/main/resources/mapper/static/iotprojecttask/IotProjectTaskMapper.xml

@@ -19,7 +19,7 @@
     </resultMap>
 
     <select id="taskList" resultMap="IotProjectTaskResultMap">
-        select
+        SELECT
         a.id,
         a.project_id,
         a.well_name,
@@ -33,34 +33,41 @@
         b.manufacture_name,
         b.contract_name,
         b.contract_code
-        from
+        FROM
         rq_iot_project_task a,
         rq_iot_project_info b
-        where 1=1
-        and a.project_id = b.id
-        and a.deleted = 0
-        and b.deleted = 0
-        and (a.platform_well = 0 or a.platform_well = 1)
+        WHERE 1=1
+        AND a.project_id = b.id
+        AND a.deleted = 0
+        AND b.deleted = 0
+        AND (a.platform_well = 0 or a.platform_well = 1)
+        <if test="searchDeptIds != null and !searchDeptIds.isEmpty()">
+            AND (
+                <foreach collection="searchDeptIds" item="deptId" separator=" OR ">
+                    a.dept_ids LIKE CONCAT('%', #{deptId}, '%')
+                </foreach>
+            )
+        </if>
         <if test="companyId != null and companyId != ''">
-        and a.dept_id = #{companyId}
+        AND a.dept_id = #{companyId}
         </if>
         <if test='platformFlag != null and platformFlag == "Y"'>
-            and a.platform_well = 1
+        AND a.platform_well = 1
         </if>
         <if test='platformFlag != null and platformFlag == "N"'>
-            and a.platform_well = 0
+        AND a.platform_well = 0
         </if>
         <if test="manufactureName != null  and manufactureName != ''">
-        and b.manufacture_name like concat('%', #{manufactureName}, '%')
+        AND b.manufacture_name LIKE concat('%', #{manufactureName}, '%')
         </if>
         <if test="contractName != null  and contractName != ''">
-            and b.contract_name like concat('%', #{contractName}, '%')
+            AND b.contract_name LIKE concat('%', #{contractName}, '%')
         </if>
         <if test="contractCode != null  and contractCode != ''">
-            and b.contract_code like concat('%', #{contractCode}, '%')
+            AND b.contract_code LIKE concat('%', #{contractCode}, '%')
         </if>
         <if test="wellName != null  and wellName != ''">
-            and a.well_name like concat('%', #{wellName}, '%')
+            AND a.well_name LIKE concat('%', #{wellName}, '%')
         </if>
         <if test="createTime != null and createTime.length > 0">
             <choose>