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

pms 专业公司工作量计划代码初始化

zhangcl преди 1 седмица
родител
ревизия
cd1ee4d387
променени са 9 файла, в които са добавени 725 реда и са изтрити 1 реда
  1. 2 1
      yudao-module-pms/yudao-module-pms-api/src/main/java/cn/iocoder/yudao/module/pms/enums/ErrorCodeConstant.java
  2. 102 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttaskplan/IotProjectTaskPlanController.java
  3. 78 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttaskplan/vo/IotProjectTaskPlanPageReqVO.java
  4. 84 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttaskplan/vo/IotProjectTaskPlanRespVO.java
  5. 61 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttaskplan/vo/IotProjectTaskPlanSaveReqVO.java
  6. 93 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotprojecttaskplan/IotProjectTaskPlanDO.java
  7. 92 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotprojecttaskplan/IotProjectTaskPlanMapper.java
  8. 64 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprojecttaskplan/IotProjectTaskPlanService.java
  9. 149 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprojecttaskplan/IotProjectTaskPlanServiceImpl.java

+ 2 - 1
yudao-module-pms/yudao-module-pms-api/src/main/java/cn/iocoder/yudao/module/pms/enums/ErrorCodeConstant.java

@@ -118,7 +118,8 @@ public interface ErrorCodeConstant{
     ErrorCode IOT_RY_HI_DAILY_REPORT_NOT_EXISTS = new ErrorCode(400, "瑞鹰历史日报不存在");
     ErrorCode IOT_RY_IMPROVE_DAILY_REPORT_DETAIL_NOT_EXISTS = new ErrorCode(500, "瑞鹰设备整改日报明细(生产动态拆分)不存在");
     ErrorCode IOT_FIVE_DAILY_REPORT_NOT_EXISTS = new ErrorCode(600, "5#国日报不存在");
-
+    ErrorCode IOT_PROJECT_TASK_PLAN_NOT_EXISTS = new ErrorCode(700, "日报任务计划(工作量)不存在");
+    ErrorCode IOT_PROJECT_TASK_PLAN_EXISTS = new ErrorCode(700, "部门当前月份的工作量计划已经存在");
     /***
      * 数字油藏
      */

+ 102 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttaskplan/IotProjectTaskPlanController.java

@@ -0,0 +1,102 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan;
+
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanRespVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskplan.IotProjectTaskPlanDO;
+import cn.iocoder.yudao.module.pms.service.iotprojecttaskplan.IotProjectTaskPlanService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 日报任务计划(工作量)")
+@RestController
+@RequestMapping("/pms/iot-project-task-plan")
+@Validated
+public class IotProjectTaskPlanController {
+
+    @Resource
+    private IotProjectTaskPlanService iotProjectTaskPlanService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建日报任务计划(工作量)")
+    @PreAuthorize("@ss.hasPermission('pms:iot-project-task-plan:create')")
+    public CommonResult<Long> createIotProjectTaskPlan(@Valid @RequestBody IotProjectTaskPlanSaveReqVO createReqVO) {
+        return success(iotProjectTaskPlanService.createIotProjectTaskPlan(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新日报任务计划(工作量)")
+    @PreAuthorize("@ss.hasPermission('pms:iot-project-task-plan:update')")
+    public CommonResult<Boolean> updateIotProjectTaskPlan(@Valid @RequestBody IotProjectTaskPlanSaveReqVO updateReqVO) {
+        iotProjectTaskPlanService.updateIotProjectTaskPlan(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除日报任务计划(工作量)")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('pms:iot-project-task-plan:delete')")
+    public CommonResult<Boolean> deleteIotProjectTaskPlan(@RequestParam("id") Long id) {
+        iotProjectTaskPlanService.deleteIotProjectTaskPlan(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得日报任务计划(工作量)")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('pms:iot-project-task-plan:query')")
+    public CommonResult<IotProjectTaskPlanRespVO> getIotProjectTaskPlan(@RequestParam("id") Long id) {
+        IotProjectTaskPlanDO iotProjectTaskPlan = iotProjectTaskPlanService.getIotProjectTaskPlan(id);
+        return success(BeanUtils.toBean(iotProjectTaskPlan, IotProjectTaskPlanRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得日报任务计划(工作量)分页")
+    @PreAuthorize("@ss.hasPermission('pms:iot-project-task-plan:query')")
+    public CommonResult<PageResult<IotProjectTaskPlanRespVO>> getIotProjectTaskPlanPage(@Valid IotProjectTaskPlanPageReqVO pageReqVO) {
+        PageResult<IotProjectTaskPlanDO> pageResult = iotProjectTaskPlanService.getIotProjectTaskPlanPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, IotProjectTaskPlanRespVO.class));
+    }
+
+    @GetMapping("/planExtProperties")
+    @Operation(summary = "获得项目任务计划对应的工作量属性列表")
+    @PreAuthorize("@ss.hasPermission('pms:iot-project-task-plan:query')")
+    public CommonResult<List<IotTaskAttrModelProperty>> planExtProperties(@Valid IotProjectTaskPlanPageReqVO pageReqVO) {
+        List<IotTaskAttrModelProperty> taskPlanExtProperties = iotProjectTaskPlanService.planExtProperties(pageReqVO);
+        return success(taskPlanExtProperties);
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出日报任务计划(工作量) Excel")
+    @PreAuthorize("@ss.hasPermission('pms:iot-project-task-plan:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportIotProjectTaskPlanExcel(@Valid IotProjectTaskPlanPageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<IotProjectTaskPlanDO> list = iotProjectTaskPlanService.getIotProjectTaskPlanPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "日报任务计划(工作量).xls", "数据", IotProjectTaskPlanRespVO.class,
+                        BeanUtils.toBean(list, IotProjectTaskPlanRespVO.class));
+    }
+
+}

+ 78 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttaskplan/vo/IotProjectTaskPlanPageReqVO.java

@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Collection;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 日报任务计划(工作量)分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class IotProjectTaskPlanPageReqVO extends PageParam {
+
+    @Schema(description = "部门id", example = "29794")
+    private Long deptId;
+
+    @Schema(description = "公司id", example = "13586")
+    private Long companyId;
+
+    @Schema(description = "项目id", example = "3992")
+    private Long projectId;
+
+    @Schema(description = "任务id", example = "15952")
+    private Long taskId;
+
+    @Schema(description = "项目业务(注气/钻井/修井/压裂/连油/井下)")
+    private String business;
+
+    @Schema(description = "施工状态(工作量开始/动迁/准备/施工)", example = "2")
+    private String status;
+
+    @Schema(description = "施工阶段描述", example = "随便")
+    private String description;
+
+    @Schema(description = "状态起始时间(计划)")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] startTime;
+
+    @Schema(description = "状态结束时间(计划)")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] endTime;
+
+    @Schema(description = "计划注气量(万方)")
+    private BigDecimal planGasInjection;
+
+    @Schema(description = "计划进尺(m)")
+    private BigDecimal planFootage;
+
+    @Schema(description = "计划井次(井)")
+    private BigDecimal planWellTrips;
+
+    @Schema(description = "计划层数(层)")
+    private BigDecimal planLayers;
+
+    @Schema(description = "工作量单位(井/层/方/m)")
+    private String workloadUnit;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+    /**
+     * 扩展属性
+     */
+    @Schema(description = "部门id集合", example = "15952")
+    private Collection<Long> deptIds;
+}

+ 84 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttaskplan/vo/IotProjectTaskPlanRespVO.java

@@ -0,0 +1,84 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 日报任务计划(工作量) Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class IotProjectTaskPlanRespVO {
+
+    @Schema(description = "主键id", requiredMode = Schema.RequiredMode.REQUIRED, example = "6285")
+    @ExcelProperty("主键id")
+    private Long id;
+
+    @Schema(description = "部门id", example = "29794")
+    @ExcelProperty("部门id")
+    private Long deptId;
+
+    @Schema(description = "公司id", example = "13586")
+    @ExcelProperty("公司id")
+    private Long companyId;
+
+    @Schema(description = "项目id", example = "3992")
+    @ExcelProperty("项目id")
+    private Long projectId;
+
+    @Schema(description = "任务id", example = "15952")
+    @ExcelProperty("任务id")
+    private Long taskId;
+
+    @Schema(description = "项目业务(注气/钻井/修井/压裂/连油/井下)")
+    @ExcelProperty("项目业务(注气/钻井/修井/压裂/连油/井下)")
+    private String business;
+
+    @Schema(description = "施工状态(工作量开始/动迁/准备/施工)", example = "2")
+    @ExcelProperty("施工状态(工作量开始/动迁/准备/施工)")
+    private String status;
+
+    @Schema(description = "施工阶段描述", example = "随便")
+    @ExcelProperty("施工阶段描述")
+    private String description;
+
+    @Schema(description = "状态起始时间(计划)")
+    @ExcelProperty("状态起始时间(计划)")
+    private LocalDateTime startTime;
+
+    @Schema(description = "状态结束时间(计划)")
+    @ExcelProperty("状态结束时间(计划)")
+    private LocalDateTime endTime;
+
+    @Schema(description = "计划注气量(万方)")
+    @ExcelProperty("计划注气量(万方)")
+    private BigDecimal planGasInjection;
+
+    @Schema(description = "计划进尺(m)")
+    @ExcelProperty("计划进尺(m)")
+    private BigDecimal planFootage;
+
+    @Schema(description = "计划井次(井)")
+    @ExcelProperty("计划井次(井)")
+    private BigDecimal planWellTrips;
+
+    @Schema(description = "计划层数(层)")
+    @ExcelProperty("计划层数(层)")
+    private BigDecimal planLayers;
+
+    @Schema(description = "工作量单位(井/层/方/m)")
+    @ExcelProperty("工作量单位(井/层/方/m)")
+    private String workloadUnit;
+
+    @Schema(description = "备注", example = "你说的对")
+    @ExcelProperty("备注")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 61 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttaskplan/vo/IotProjectTaskPlanSaveReqVO.java

@@ -0,0 +1,61 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 日报任务计划(工作量)新增/修改 Request VO")
+@Data
+public class IotProjectTaskPlanSaveReqVO {
+
+    @Schema(description = "主键id", requiredMode = Schema.RequiredMode.REQUIRED, example = "6285")
+    private Long id;
+
+    @Schema(description = "部门id", example = "29794")
+    private Long deptId;
+
+    @Schema(description = "公司id", example = "13586")
+    private Long companyId;
+
+    @Schema(description = "项目id", example = "3992")
+    private Long projectId;
+
+    @Schema(description = "任务id", example = "15952")
+    private Long taskId;
+
+    @Schema(description = "项目业务(注气/钻井/修井/压裂/连油/井下)")
+    private String business;
+
+    @Schema(description = "施工状态(工作量开始/动迁/准备/施工)", example = "2")
+    private String status;
+
+    @Schema(description = "施工阶段描述", example = "随便")
+    private String description;
+
+    @Schema(description = "状态起始时间(计划)")
+    private LocalDateTime startTime;
+
+    @Schema(description = "状态结束时间(计划)")
+    private LocalDateTime endTime;
+
+    @Schema(description = "计划注气量(万方)")
+    private BigDecimal planGasInjection;
+
+    @Schema(description = "计划进尺(m)")
+    private BigDecimal planFootage;
+
+    @Schema(description = "计划井次(井)")
+    private BigDecimal planWellTrips;
+
+    @Schema(description = "计划层数(层)")
+    private BigDecimal planLayers;
+
+    @Schema(description = "工作量单位(井/层/方/m)")
+    private String workloadUnit;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+}

+ 93 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotprojecttaskplan/IotProjectTaskPlanDO.java

@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskplan;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 日报任务计划(工作量) DO
+ *
+ * @author ruqi
+ */
+@TableName("rq_iot_project_task_plan")
+@KeySequence("rq_iot_project_task_plan_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class IotProjectTaskPlanDO extends BaseDO {
+
+    /**
+     * 主键id
+     */
+    @TableId
+    private Long id;
+    /**
+     * 部门id
+     */
+    private Long deptId;
+    /**
+     * 公司id
+     */
+    private Long companyId;
+    /**
+     * 项目id
+     */
+    private Long projectId;
+    /**
+     * 任务id
+     */
+    private Long taskId;
+    /**
+     * 项目业务(注气/钻井/修井/压裂/连油/井下)
+     */
+    private String business;
+    /**
+     * 施工状态(工作量开始/动迁/准备/施工)
+     */
+    private String status;
+    /**
+     * 施工阶段描述
+     */
+    private String description;
+    /**
+     * 状态起始时间(计划)
+     */
+    private LocalDateTime startTime;
+    /**
+     * 状态结束时间(计划)
+     */
+    private LocalDateTime endTime;
+    /**
+     * 计划注气量(万方)
+     */
+    private BigDecimal planGasInjection;
+    /**
+     * 计划进尺(m)
+     */
+    private BigDecimal planFootage;
+    /**
+     * 计划井次(井)
+     */
+    private BigDecimal planWellTrips;
+    /**
+     * 计划层数(层)
+     */
+    private BigDecimal planLayers;
+    /**
+     * 工作量单位(井/层/方/m)
+     */
+    private String workloadUnit;
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 92 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotprojecttaskplan/IotProjectTaskPlanMapper.java

@@ -0,0 +1,92 @@
+package cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttaskplan;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanPageReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskplan.IotProjectTaskPlanDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 日报任务计划(工作量) Mapper
+ *
+ * @author ruqi
+ */
+@Mapper
+public interface IotProjectTaskPlanMapper extends BaseMapperX<IotProjectTaskPlanDO> {
+
+    default PageResult<IotProjectTaskPlanDO> selectPage(IotProjectTaskPlanPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<IotProjectTaskPlanDO>()
+                .eqIfPresent(IotProjectTaskPlanDO::getDeptId, reqVO.getDeptId())
+                .eqIfPresent(IotProjectTaskPlanDO::getCompanyId, reqVO.getCompanyId())
+                .eqIfPresent(IotProjectTaskPlanDO::getProjectId, reqVO.getProjectId())
+                .eqIfPresent(IotProjectTaskPlanDO::getTaskId, reqVO.getTaskId())
+                .eqIfPresent(IotProjectTaskPlanDO::getBusiness, reqVO.getBusiness())
+                .eqIfPresent(IotProjectTaskPlanDO::getStatus, reqVO.getStatus())
+                .eqIfPresent(IotProjectTaskPlanDO::getDescription, reqVO.getDescription())
+                .betweenIfPresent(IotProjectTaskPlanDO::getStartTime, reqVO.getStartTime())
+                .betweenIfPresent(IotProjectTaskPlanDO::getEndTime, reqVO.getEndTime())
+                .eqIfPresent(IotProjectTaskPlanDO::getPlanGasInjection, reqVO.getPlanGasInjection())
+                .eqIfPresent(IotProjectTaskPlanDO::getPlanFootage, reqVO.getPlanFootage())
+                .eqIfPresent(IotProjectTaskPlanDO::getPlanWellTrips, reqVO.getPlanWellTrips())
+                .eqIfPresent(IotProjectTaskPlanDO::getPlanLayers, reqVO.getPlanLayers())
+                .eqIfPresent(IotProjectTaskPlanDO::getWorkloadUnit, reqVO.getWorkloadUnit())
+                .eqIfPresent(IotProjectTaskPlanDO::getRemark, reqVO.getRemark())
+                .betweenIfPresent(IotProjectTaskPlanDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(IotProjectTaskPlanDO::getId));
+    }
+
+    /**
+     * 判断指定部门在指定月份(基于 startTime 的年月)是否存在记录
+     * @param deptId    部门ID
+     * @param startTime 用于提取年月的起始时间
+     * @return true-存在,false-不存在
+     */
+    default boolean existsByDeptIdAndMonth(Long deptId, LocalDateTime startTime) {
+        if (deptId == null || startTime == null) {
+            return false; // 可根据业务决定是否跳过校验(例如,必填字段不允许为空)
+        }
+        // 计算该月起始和结束时间(包含整月,精确到秒)
+        LocalDate date = startTime.toLocalDate();
+        LocalDateTime startOfMonth = date.withDayOfMonth(1).atStartOfDay();
+        LocalDateTime endOfMonth = date.withDayOfMonth(date.lengthOfMonth()).atTime(23, 59, 59);
+
+        return selectCount(new LambdaQueryWrapperX<IotProjectTaskPlanDO>()
+                .eq(IotProjectTaskPlanDO::getDeptId, deptId)
+                .between(IotProjectTaskPlanDO::getStartTime, startOfMonth, endOfMonth)
+        ) > 0;
+    }
+
+    /**
+     * 查询指定部门在指定月份(基于 startTime 的年月)是否存在记录,可排除指定ID
+     * @param deptId    部门ID
+     * @param startTime 用于提取年月的起始时间
+     * @param excludeId 需要排除的记录ID(更新时传入自身ID,新增时传 null)
+     * @return 匹配到的第一条记录,若无则返回 null
+     */
+    default IotProjectTaskPlanDO selectOneByDeptIdAndMonth(Long deptId, LocalDateTime startTime, Long excludeId) {
+        if (deptId == null || startTime == null) {
+            return null;
+        }
+        // 计算该月起始和结束时间
+        LocalDate date = startTime.toLocalDate();
+        LocalDateTime startOfMonth = date.withDayOfMonth(1).atStartOfDay();
+        LocalDateTime endOfMonth = date.withDayOfMonth(date.lengthOfMonth()).atTime(23, 59, 59);
+
+        // 显式声明为 LambdaQueryWrapperX 类型,避免类型推断问题
+        LambdaQueryWrapperX<IotProjectTaskPlanDO> wrapper = new LambdaQueryWrapperX<>();
+        wrapper.eq(IotProjectTaskPlanDO::getDeptId, deptId)
+                .between(IotProjectTaskPlanDO::getStartTime, startOfMonth, endOfMonth);
+
+        // 排除指定ID(若不为null)
+        if (excludeId != null) {
+            wrapper.ne(IotProjectTaskPlanDO::getId, excludeId);
+        }
+
+        // 只取第一条(如果有唯一约束,只会有一条,取第一条即可)
+        return selectOne(wrapper);
+    }
+}

+ 64 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprojecttaskplan/IotProjectTaskPlanService.java

@@ -0,0 +1,64 @@
+package cn.iocoder.yudao.module.pms.service.iotprojecttaskplan;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskplan.IotProjectTaskPlanDO;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 日报任务计划(工作量) Service 接口
+ *
+ * @author ruqi
+ */
+public interface IotProjectTaskPlanService {
+
+    /**
+     * 创建日报任务计划(工作量)
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createIotProjectTaskPlan(@Valid IotProjectTaskPlanSaveReqVO createReqVO);
+
+    /**
+     * 更新日报任务计划(工作量)
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateIotProjectTaskPlan(@Valid IotProjectTaskPlanSaveReqVO updateReqVO);
+
+    /**
+     * 删除日报任务计划(工作量)
+     *
+     * @param id 编号
+     */
+    void deleteIotProjectTaskPlan(Long id);
+
+    /**
+     * 获得日报任务计划(工作量)
+     *
+     * @param id 编号
+     * @return 日报任务计划(工作量)
+     */
+    IotProjectTaskPlanDO getIotProjectTaskPlan(Long id);
+
+    /**
+     * 获得日报任务计划(工作量)分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 日报任务计划(工作量)分页
+     */
+    PageResult<IotProjectTaskPlanDO> getIotProjectTaskPlanPage(IotProjectTaskPlanPageReqVO pageReqVO);
+
+    /**
+     * 专业公司工作量计划扩展属性
+     *
+     * @param pageReqVO 分页查询
+     * @return 工作量计划扩展属性列表
+     */
+    List<IotTaskAttrModelProperty> planExtProperties(IotProjectTaskPlanPageReqVO pageReqVO);
+}

+ 149 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprojecttaskplan/IotProjectTaskPlanServiceImpl.java

@@ -0,0 +1,149 @@
+package cn.iocoder.yudao.module.pms.service.iotprojecttaskplan;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingattrs.vo.IotOperationMeetingAttrsPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeetingattrs.IotOperationMeetingAttrsDO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskplan.IotProjectTaskPlanDO;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotoperationmeetingattrs.IotOperationMeetingAttrsMapper;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttaskplan.IotProjectTaskPlanMapper;
+import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
+import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_PROJECT_TASK_PLAN_EXISTS;
+import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_PROJECT_TASK_PLAN_NOT_EXISTS;
+
+/**
+ * 日报任务计划(工作量) Service 实现类
+ *
+ * @author ruqi
+ */
+@Service
+@Validated
+public class IotProjectTaskPlanServiceImpl implements IotProjectTaskPlanService {
+
+    @Resource
+    private IotProjectTaskPlanMapper iotProjectTaskPlanMapper;
+
+    @Resource
+    private IotOperationMeetingAttrsMapper iotOperationMeetingAttrsMapper;
+
+    @Resource
+    private DeptMapper deptMapper;
+
+    @Override
+    public Long createIotProjectTaskPlan(IotProjectTaskPlanSaveReqVO createReqVO) {
+        // 插入
+        IotProjectTaskPlanDO iotProjectTaskPlan = BeanUtils.toBean(createReqVO, IotProjectTaskPlanDO.class);
+        if (ObjUtil.isEmpty(createReqVO.getDeptId()) || ObjUtil.isEmpty(createReqVO.getStartTime())) {
+            throw exception(IOT_PROJECT_TASK_PLAN_NOT_EXISTS);
+        }
+        // 校验当前队伍部门 指定时间所属月份 是否已经存在 工作量计划记录
+        IotProjectTaskPlanDO existPlan = iotProjectTaskPlanMapper.selectOneByDeptIdAndMonth(createReqVO.getDeptId(), createReqVO.getStartTime(), null);
+        if (ObjUtil.isNotEmpty(existPlan)) {
+            throw exception(IOT_PROJECT_TASK_PLAN_EXISTS);
+        }
+        iotProjectTaskPlanMapper.insert(iotProjectTaskPlan);
+        // 返回
+        return iotProjectTaskPlan.getId();
+    }
+
+    @Override
+    public void updateIotProjectTaskPlan(IotProjectTaskPlanSaveReqVO updateReqVO) {
+        // 校验存在
+        validateIotProjectTaskPlanExists(updateReqVO.getId());
+        // 校验当前队伍部门 指定时间所属月份 是否已经存在 工作量计划记录
+        IotProjectTaskPlanDO existPlan = iotProjectTaskPlanMapper.selectOneByDeptIdAndMonth(updateReqVO.getDeptId(), updateReqVO.getStartTime(), updateReqVO.getId());
+        if (ObjUtil.isNotEmpty(existPlan)) {
+            throw exception(IOT_PROJECT_TASK_PLAN_EXISTS);
+        }
+        // 更新
+        IotProjectTaskPlanDO updateObj = BeanUtils.toBean(updateReqVO, IotProjectTaskPlanDO.class);
+        iotProjectTaskPlanMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteIotProjectTaskPlan(Long id) {
+        // 校验存在
+        validateIotProjectTaskPlanExists(id);
+        // 删除
+        iotProjectTaskPlanMapper.deleteById(id);
+    }
+
+    private void validateIotProjectTaskPlanExists(Long id) {
+        if (iotProjectTaskPlanMapper.selectById(id) == null) {
+            throw exception(IOT_PROJECT_TASK_PLAN_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public IotProjectTaskPlanDO getIotProjectTaskPlan(Long id) {
+        return iotProjectTaskPlanMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<IotProjectTaskPlanDO> getIotProjectTaskPlanPage(IotProjectTaskPlanPageReqVO pageReqVO) {
+        return iotProjectTaskPlanMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<IotTaskAttrModelProperty> planExtProperties(IotProjectTaskPlanPageReqVO pageReqVO) {
+        // deptId 需要传递公司级的部门id
+        Long deptId = pageReqVO.getDeptId();
+        if (ObjUtil.isEmpty(deptId)) {
+            return Collections.emptyList();
+        }
+        // 如果 deptId 是集团级部门 科瑞石油技术 则查询所有公司的工作量扩展属性列表
+        DeptDO currentDept = deptMapper.selectById(deptId);
+        boolean groupFlag = false;
+        if ("0".equals(currentDept.getType())) {
+            groupFlag = true;
+        }
+        // 根据任务井所属的 公司id 查询工作量扩展属性
+        IotOperationMeetingAttrsPageReqVO reqVO = new IotOperationMeetingAttrsPageReqVO();
+        if (!groupFlag) {
+            reqVO.setDeptId(deptId);
+        }
+        reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        PageResult<IotOperationMeetingAttrsDO> pageAttrs = iotOperationMeetingAttrsMapper.selectPage(reqVO);
+        List<IotOperationMeetingAttrsDO> workloadAttrs = pageAttrs.getList();
+        if (CollUtil.isEmpty(workloadAttrs)) {
+            return Collections.emptyList();
+        }
+
+        // 4. Java8 Stream 一站式处理:过滤符合条件属性 → 按name去重 → 按sort升序排序
+        Map<String, IotTaskAttrModelProperty> uniqueAttrMap = workloadAttrs.stream()
+                // 提取扩展属性,过滤空对象
+                .map(IotOperationMeetingAttrsDO::getExtProperty)
+                .filter(ObjUtil::isNotEmpty)
+                // 过滤条件:名称包含“计划”、默认值为空
+                .filter(property -> property.getName().contains("计划") && StrUtil.isBlank(property.getDefaultValue()))
+                // 按name分组,同名只保留第一条数据
+                .collect(Collectors.toMap(
+                        IotTaskAttrModelProperty::getName,
+                        property -> property,
+                        (existing, replacement) -> existing // 冲突时保留先出现的第一条
+                ));
+
+        // map转list,按sort升序排序(保证前端展示顺序统一)
+        List<IotTaskAttrModelProperty> result = new ArrayList<>(uniqueAttrMap.values());
+        result.sort(Comparator.comparing(IotTaskAttrModelProperty::getSort));
+
+        return result;
+    }
+
+}