diff --git a/.gitignore b/.gitignore index 0153c2e..cabe0a6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ mvnw.cmd maven-wrapper.properties **/logs/ +/logs/ diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/EquipmentInfoController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/EquipmentInfoController.java new file mode 100644 index 0000000..37480d1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/EquipmentInfoController.java @@ -0,0 +1,71 @@ +package com.ruoyi.system.controller; + +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.validate.AddGroup; +import com.ruoyi.common.core.validate.EditGroup; +import com.ruoyi.system.domain.bo.EquipmentInfoBo; +import com.ruoyi.system.domain.vo.EquipmentInfoVo; +import com.ruoyi.system.service.IEquipmentInfoService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/equipment") +public class EquipmentInfoController extends BaseController { + + private final IEquipmentInfoService equipmentInfoService; + + @GetMapping("/list") + public TableDataInfo list(EquipmentInfoBo bo, PageQuery pageQuery) { + return equipmentInfoService.queryPageList(bo, pageQuery); + } + + @GetMapping("/options") + public R> options(@RequestParam(required = false) String workCenterName, + @RequestParam(required = false) String keyword) { + return R.ok(equipmentInfoService.queryOptions(workCenterName, keyword)); + } + + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(equipmentInfoService.queryById(id)); + } + + @RepeatSubmit + @PostMapping + public R add(@Validated(AddGroup.class) @RequestBody EquipmentInfoBo bo) { + return toAjax(equipmentInfoService.insertByBo(bo)); + } + + @RepeatSubmit + @PutMapping + public R edit(@Validated(EditGroup.class) @RequestBody EquipmentInfoBo bo) { + return toAjax(equipmentInfoService.updateByBo(bo)); + } + + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(equipmentInfoService.deleteWithValidByIds(Arrays.asList(ids), true)); + } +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/WorkbenchController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/WorkbenchController.java new file mode 100644 index 0000000..f8e7876 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/WorkbenchController.java @@ -0,0 +1,100 @@ +package com.ruoyi.system.controller; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.system.domain.dto.workbench.WorkbenchOverviewRespDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchProcessDefectRankRespDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchWorkOrderBarRespDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchWorkOrderProgressRespDto; +import com.ruoyi.system.service.IWorkbenchService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 工作台(看板)数据接口。 + *

+ * 说明: + *

    + *
  • 时间参数格式:yyyy-MM-dd HH:mm:ss
  • + *
  • workCenterName:按工作中心过滤(来自工序计划明细的 work_center_name)
  • + *
+ */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/system/workbench") +public class WorkbenchController { + + private final IWorkbenchService workbenchService; + + /** + * 顶部概览卡片(一次返回 4 张卡,保证口径一致)。 + * + *

rangeType 仅在未传 startTime/endTime 时生效。

+ * + * @param rangeType 时间范围类型:day|week|month|year(默认 month) + * @param startTime 自定义开始时间(可选,格式 yyyy-MM-dd HH:mm:ss) + * @param endTime 自定义结束时间(可选,格式 yyyy-MM-dd HH:mm:ss) + * @param workCenterName 工作中心名称(可选) + */ + @GetMapping("/overview") + public R overview(@RequestParam(required = false) String rangeType, + @RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime, + @RequestParam(required = false) String workCenterName) { + return R.ok(workbenchService.overview(rangeType, startTime, endTime, workCenterName)); + } + + /** + * 工单数量柱状图。 + * + *

当未传 startTime/endTime 时,默认统计当年 1~12 月。

+ * + * @param startTime 统计开始时间(可选,格式 yyyy-MM-dd HH:mm:ss) + * @param endTime 统计结束时间(可选,格式 yyyy-MM-dd HH:mm:ss) + * @param groupBy 分组粒度:month|day(默认 month) + * @param workCenterName 工作中心名称(可选) + */ + @GetMapping("/workOrderBar") + public R workOrderBar(@RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime, + @RequestParam(required = false) String groupBy, + @RequestParam(required = false) String workCenterName) { + return R.ok(workbenchService.workOrderBar(startTime, endTime, groupBy, workCenterName)); + } + + /** + * 工单实时进度。 + * + * @param startTime 统计开始时间(可选,格式 yyyy-MM-dd HH:mm:ss;不传则按默认 month 范围) + * @param endTime 统计结束时间(可选,格式 yyyy-MM-dd HH:mm:ss;不传则按默认 month 范围) + * @param topN 返回条数(默认 5) + * @param workCenterName 工作中心名称(可选) + */ + @GetMapping("/workOrderProgress") + public R workOrderProgress(@RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime, + @RequestParam(required = false) Integer topN, + @RequestParam(required = false) String workCenterName) { + return R.ok(workbenchService.workOrderProgress(startTime, endTime, topN, workCenterName)); + } + + /** + * 工序不良率 TopN 排名。 + * + *

不良率 = 不良数 / (合格数 + 不良数)。

+ * + * @param startTime 统计开始时间(可选,格式 yyyy-MM-dd HH:mm:ss;不传则按默认 month 范围) + * @param endTime 统计结束时间(可选,格式 yyyy-MM-dd HH:mm:ss;不传则按默认 month 范围) + * @param topN 返回条数(默认 10) + * @param workCenterName 工作中心名称(可选) + */ + @GetMapping("/processDefectRank") + public R processDefectRank(@RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime, + @RequestParam(required = false) Integer topN, + @RequestParam(required = false) String workCenterName) { + return R.ok(workbenchService.processDefectRank(startTime, endTime, topN, workCenterName)); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/EquipmentInfo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/EquipmentInfo.java new file mode 100644 index 0000000..96f9d13 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/EquipmentInfo.java @@ -0,0 +1,28 @@ +package com.ruoyi.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("equipment_info") +public class EquipmentInfo extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + private String equipmentName; + + private String equipmentCode; + + private String operator; + + private String workCenterName; +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/EquipmentInfoBo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/EquipmentInfoBo.java new file mode 100644 index 0000000..451ff26 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/EquipmentInfoBo.java @@ -0,0 +1,31 @@ +package com.ruoyi.system.domain.bo; + +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.core.validate.AddGroup; +import com.ruoyi.common.core.validate.EditGroup; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Data +@EqualsAndHashCode(callSuper = true) +public class EquipmentInfoBo extends BaseEntity { + + @NotNull(message = "id不能为空", groups = {EditGroup.class}) + private Long id; + + @NotBlank(message = "设备名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String equipmentName; + + @NotBlank(message = "设备编号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String equipmentCode; + + @NotBlank(message = "作业人员不能为空", groups = {AddGroup.class, EditGroup.class}) + private String operator; + + @NotBlank(message = "所属工段不能为空", groups = {AddGroup.class, EditGroup.class}) + private String workCenterName; +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SfcOperationPlanningMainBo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SfcOperationPlanningMainBo.java index 4c2f8fe..3b8f61a 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SfcOperationPlanningMainBo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SfcOperationPlanningMainBo.java @@ -81,5 +81,7 @@ public class SfcOperationPlanningMainBo extends BaseEntity { @NotNull(message = "计划结束时间不能为空", groups = { AddGroup.class, EditGroup.class }) private Date planFinishTime; + private String workCenterName; + } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchOverviewAggDto.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchOverviewAggDto.java new file mode 100644 index 0000000..c9a6f5a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchOverviewAggDto.java @@ -0,0 +1,25 @@ +package com.ruoyi.system.domain.dto.workbench; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class WorkbenchOverviewAggDto { + /** + * 工单数(工序计划主表记录数)。 + */ + private Long workOrderCount; + /** + * 完工工单数(当前实现口径见 WorkbenchMapper.selectOverviewAgg 的说明)。 + */ + private Long finishedWorkOrderCount; + /** + * 在制品数量(当前实现口径:计划数 - 完工数,小于 0 按 0 计)。 + */ + private BigDecimal wipCount; + /** + * 不良品数(不良数量汇总)。 + */ + private BigDecimal ngCount; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchOverviewRespDto.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchOverviewRespDto.java new file mode 100644 index 0000000..8a7c72f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchOverviewRespDto.java @@ -0,0 +1,44 @@ +package com.ruoyi.system.domain.dto.workbench; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +public class WorkbenchOverviewRespDto { + /** + * 数据统计时点(服务端当前时间)。 + */ + private String asOf; + /** + * 4 张卡片(固定顺序:完工工单数/生产工单数/在制品/不良品数)。 + */ + private List cards; + + @Data + public static class Card { + /** + * 唯一标识(前端用于识别卡片):finishedWorkOrderCount/workOrderCount/wipCount/ngCount。 + */ + private String key; + /** + * 卡片标题。 + */ + private String title; + /** + * 主数值(整数)。 + */ + private Long value; + /** + * 环比增长率: (本期-上期)/上期。 + *

例如 0.68 表示 +68%。

+ */ + private BigDecimal mom; + /** + * 同比增长率: (本期-去年同期)/去年同期。 + *

例如 -0.05 表示 -5%。

+ */ + private BigDecimal yoy; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchProcessDefectRankRespDto.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchProcessDefectRankRespDto.java new file mode 100644 index 0000000..eec2b8a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchProcessDefectRankRespDto.java @@ -0,0 +1,39 @@ +package com.ruoyi.system.domain.dto.workbench; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +public class WorkbenchProcessDefectRankRespDto { + /** + * 排名列表。 + */ + private List items; + + @Data + public static class Item { + /** + * 名次(从 1 开始)。 + */ + private Integer rank; + /** + * 工序/作业名称。 + */ + private String processName; + /** + * 不良率:不良数 / (合格数 + 不良数)。 + *

例如 0.40 表示 40%。

+ */ + private BigDecimal defectRate; + /** + * 不良数量。 + */ + private Long defectQty; + /** + * 送检数量(合格数 + 不良数)。 + */ + private Long inspectedQty; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchProcessDefectRankRowDto.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchProcessDefectRankRowDto.java new file mode 100644 index 0000000..27e4d34 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchProcessDefectRankRowDto.java @@ -0,0 +1,25 @@ +package com.ruoyi.system.domain.dto.workbench; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class WorkbenchProcessDefectRankRowDto { + /** + * 工序/作业名称(来自明细 process_name)。 + */ + private String processName; + /** + * 不良数量(unqualified_qty 汇总)。 + */ + private BigDecimal defectQty; + /** + * 送检数量(qualified_qty + unqualified_qty 汇总)。 + */ + private BigDecimal inspectedQty; + /** + * 不良率(defectQty / inspectedQty)。 + */ + private BigDecimal defectRate; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderBarPointDto.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderBarPointDto.java new file mode 100644 index 0000000..9017f7c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderBarPointDto.java @@ -0,0 +1,19 @@ +package com.ruoyi.system.domain.dto.workbench; + +import lombok.Data; + +@Data +public class WorkbenchWorkOrderBarPointDto { + /** + * 分组桶标识: + *
    + *
  • groupBy=day:yyyy-MM-dd
  • + *
  • groupBy=month:数字月份(1~12)
  • + *
+ */ + private String bucket; + /** + * 分组内工单数(count distinct 主表 id)。 + */ + private Long cnt; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderBarRespDto.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderBarRespDto.java new file mode 100644 index 0000000..f6aa880 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderBarRespDto.java @@ -0,0 +1,33 @@ +package com.ruoyi.system.domain.dto.workbench; + +import lombok.Data; + +import java.util.List; + +@Data +public class WorkbenchWorkOrderBarRespDto { + /** + * X 轴分组名称: + *
    + *
  • groupBy=month:固定为 1月~12月
  • + *
  • groupBy=day:为 yyyy-MM-dd
  • + *
+ */ + private List xAxis; + /** + * 系列数据(当前只有 1 个系列:工单数)。 + */ + private List series; + + @Data + public static class Series { + /** + * 系列名称(例如:工单数)。 + */ + private String name; + /** + * 与 xAxis 一一对应的数据点。 + */ + private List data; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderProgressRespDto.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderProgressRespDto.java new file mode 100644 index 0000000..8139071 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderProgressRespDto.java @@ -0,0 +1,19 @@ +package com.ruoyi.system.domain.dto.workbench; + +import lombok.Data; + +import java.util.List; + +@Data +public class WorkbenchWorkOrderProgressRespDto { + private List items; + + @Data + public static class Item { + private String workOrderNo; + private String status; + private Long progress; + private Long totalQty; + } +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderProgressRowDto.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderProgressRowDto.java new file mode 100644 index 0000000..56c3c58 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/workbench/WorkbenchWorkOrderProgressRowDto.java @@ -0,0 +1,14 @@ +package com.ruoyi.system.domain.dto.workbench; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class WorkbenchWorkOrderProgressRowDto { + private String workOrderNo; + private String status; + private BigDecimal progress; + private BigDecimal totalQty; +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EquipmentInfoVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EquipmentInfoVo.java new file mode 100644 index 0000000..9fd7d4f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EquipmentInfoVo.java @@ -0,0 +1,31 @@ +package com.ruoyi.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.util.Date; + +@Data +@ExcelIgnoreUnannotated +public class EquipmentInfoVo { + + @ExcelProperty("id") + private Long id; + + @ExcelProperty("设备名称") + private String equipmentName; + + @ExcelProperty("设备编号") + private String equipmentCode; + + @ExcelProperty("作业人员") + private String operator; + + @ExcelProperty("所属工段") + private String workCenterName; + + @ExcelProperty("创建时间") + private Date createTime; +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/EquipmentInfoMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/EquipmentInfoMapper.java new file mode 100644 index 0000000..c1a9222 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/EquipmentInfoMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.system.mapper; + +import com.ruoyi.common.core.mapper.BaseMapperPlus; +import com.ruoyi.system.domain.EquipmentInfo; +import com.ruoyi.system.domain.vo.EquipmentInfoVo; + +public interface EquipmentInfoMapper extends BaseMapperPlus { +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/WorkbenchMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/WorkbenchMapper.java new file mode 100644 index 0000000..5b03cec --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/WorkbenchMapper.java @@ -0,0 +1,79 @@ +package com.ruoyi.system.mapper; + +import com.ruoyi.system.domain.dto.workbench.WorkbenchOverviewAggDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchProcessDefectRankRowDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchWorkOrderBarPointDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchWorkOrderProgressRowDto; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +/** + * 工作台(看板)统计查询。 + * + *

这里的查询只负责“按口径聚合出结果”,不做时间范围推导、补点、环比/同比等计算。

+ */ +public interface WorkbenchMapper { + + /** + * 概览卡片所需的聚合汇总。 + * + *
    + *
  • workOrderCount:工单数(主表记录数)
  • + *
  • finishedWorkOrderCount:完工工单数(当前实现口径:取每个工单“最大工序号”的状态=4 认为完工)
  • + *
  • wipCount:在制品(当前实现口径:计划数 - 完工数,小于 0 按 0 计)
  • + *
  • ngCount:不良品数(明细不良数汇总)
  • + *
+ * + * @param startTime 统计开始时间 + * @param endTime 统计结束时间 + * @param workCenterName 工作中心过滤(可为空) + */ + WorkbenchOverviewAggDto selectOverviewAgg(@Param("startTime") Date startTime, + @Param("endTime") Date endTime, + @Param("workCenterName") String workCenterName); + + /** + * 工单数量柱状图的分组统计点。 + * + * @param startTime 统计开始时间 + * @param endTime 统计结束时间 + * @param groupBy day|month + * @param workCenterName 工作中心过滤(可为空) + */ + List selectWorkOrderBar(@Param("startTime") Date startTime, + @Param("endTime") Date endTime, + @Param("groupBy") String groupBy, + @Param("workCenterName") String workCenterName); + + /** + * 工单实时进度列表。 + * + *

进度=合格数汇总/计划数汇总*100。

+ * + * @param startTime 统计开始时间 + * @param endTime 统计结束时间 + * @param topN 返回条数 + * @param workCenterName 工作中心过滤(可为空) + */ + List selectWorkOrderProgress(@Param("startTime") Date startTime, + @Param("endTime") Date endTime, + @Param("topN") Integer topN, + @Param("workCenterName") String workCenterName); + + /** + * 工序不良率 TopN。 + * + *

defectRate = defectQty / inspectedQty,inspectedQty = okQty + ngQty。

+ * + * @param startTime 统计开始时间 + * @param endTime 统计结束时间 + * @param topN 返回条数 + * @param workCenterName 工作中心过滤(可为空) + */ + List selectProcessDefectRank(@Param("startTime") Date startTime, + @Param("endTime") Date endTime, + @Param("topN") Integer topN, + @Param("workCenterName") String workCenterName); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IEquipmentInfoService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IEquipmentInfoService.java new file mode 100644 index 0000000..6ff3675 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IEquipmentInfoService.java @@ -0,0 +1,27 @@ +package com.ruoyi.system.service; + +import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.system.domain.bo.EquipmentInfoBo; +import com.ruoyi.system.domain.vo.EquipmentInfoVo; + +import java.util.Collection; +import java.util.List; + +public interface IEquipmentInfoService { + + EquipmentInfoVo queryById(Long id); + + TableDataInfo queryPageList(EquipmentInfoBo bo, PageQuery pageQuery); + + List queryList(EquipmentInfoBo bo); + + List queryOptions(String workCenterName, String keyword); + + Boolean insertByBo(EquipmentInfoBo bo); + + Boolean updateByBo(EquipmentInfoBo bo); + + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IWorkbenchService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IWorkbenchService.java new file mode 100644 index 0000000..4eafd8e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IWorkbenchService.java @@ -0,0 +1,57 @@ +package com.ruoyi.system.service; + +import com.ruoyi.system.domain.dto.workbench.WorkbenchOverviewRespDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchWorkOrderProgressRespDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchProcessDefectRankRespDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchWorkOrderBarRespDto; + +/** + * 工作台(看板)统计服务。 + *

+ * 统一约定:时间字符串入参格式为 yyyy-MM-dd HH:mm:ss。 + *

+ */ +public interface IWorkbenchService { + + /** + * 顶部 4 张概览卡片。 + * + *

mom/yoy 为“增长率”,不是百分数:例如 0.68 表示 +68%。

+ * + * @param rangeType day|week|month|year(默认 month,仅在未传 startTime/endTime 时生效) + * @param startTime 自定义开始时间(可选) + * @param endTime 自定义结束时间(可选) + * @param workCenterName 工作中心名称(可选) + */ + WorkbenchOverviewRespDto overview(String rangeType, String startTime, String endTime, String workCenterName); + + /** + * 工单数量柱状图。 + * + * @param startTime 开始时间(可选,不传则默认当年 1~12 月) + * @param endTime 结束时间(可选,不传则默认当年 1~12 月) + * @param groupBy month|day(默认 month) + * @param workCenterName 工作中心名称(可选) + */ + WorkbenchWorkOrderBarRespDto workOrderBar(String startTime, String endTime, String groupBy, String workCenterName); + + /** + * 工单实时进度列表。 + * + * @param startTime 开始时间(可选,不传则默认 month 范围) + * @param endTime 结束时间(可选,不传则默认 month 范围) + * @param topN 返回条数(默认 5) + * @param workCenterName 工作中心名称(可选) + */ + WorkbenchWorkOrderProgressRespDto workOrderProgress(String startTime, String endTime, Integer topN, String workCenterName); + + /** + * 工序不良率 TopN。 + * + * @param startTime 开始时间(可选,不传则默认 month 范围) + * @param endTime 结束时间(可选,不传则默认 month 范围) + * @param topN TopN(默认 10) + * @param workCenterName 工作中心名称(可选) + */ + WorkbenchProcessDefectRankRespDto processDefectRank(String startTime, String endTime, Integer topN, String workCenterName); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/EquipmentInfoServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/EquipmentInfoServiceImpl.java new file mode 100644 index 0000000..25bcaef --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/EquipmentInfoServiceImpl.java @@ -0,0 +1,87 @@ +package com.ruoyi.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.EquipmentInfo; +import com.ruoyi.system.domain.bo.EquipmentInfoBo; +import com.ruoyi.system.domain.vo.EquipmentInfoVo; +import com.ruoyi.system.mapper.EquipmentInfoMapper; +import com.ruoyi.system.service.IEquipmentInfoService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class EquipmentInfoServiceImpl implements IEquipmentInfoService { + + private final EquipmentInfoMapper baseMapper; + + @Override + public EquipmentInfoVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + @Override + public TableDataInfo queryPageList(EquipmentInfoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public List queryList(EquipmentInfoBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public List queryOptions(String workCenterName, String keyword) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(workCenterName), EquipmentInfo::getWorkCenterName, workCenterName); + if (StringUtils.isNotBlank(keyword)) { + String kw = keyword.trim(); + lqw.and(w -> w.like(EquipmentInfo::getEquipmentName, kw).or().like(EquipmentInfo::getEquipmentCode, kw)); + } + lqw.orderByAsc(EquipmentInfo::getWorkCenterName) + .orderByAsc(EquipmentInfo::getEquipmentName) + .orderByAsc(EquipmentInfo::getEquipmentCode); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(EquipmentInfoBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getEquipmentCode()), EquipmentInfo::getEquipmentCode, bo.getEquipmentCode()); + lqw.like(StringUtils.isNotBlank(bo.getEquipmentName()), EquipmentInfo::getEquipmentName, bo.getEquipmentName()); + lqw.eq(StringUtils.isNotBlank(bo.getOperator()), EquipmentInfo::getOperator, bo.getOperator()); + lqw.eq(StringUtils.isNotBlank(bo.getWorkCenterName()), EquipmentInfo::getWorkCenterName, bo.getWorkCenterName()); + return lqw; + } + + @Override + public Boolean insertByBo(EquipmentInfoBo bo) { + EquipmentInfo add = BeanUtil.toBean(bo, EquipmentInfo.class); + return baseMapper.insert(add) > 0; + } + + @Override + public Boolean updateByBo(EquipmentInfoBo bo) { + EquipmentInfo update = BeanUtil.toBean(bo, EquipmentInfo.class); + return baseMapper.updateById(update) > 0; + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + return baseMapper.deleteBatchIds(ids) > 0; + } +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SfcOperationPlanningMainServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SfcOperationPlanningMainServiceImpl.java index cdb1d97..95c68f9 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SfcOperationPlanningMainServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SfcOperationPlanningMainServiceImpl.java @@ -2,15 +2,14 @@ package com.ruoyi.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.toolkit.StringUtils; -import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.ruoyi.common.helper.LoginHelper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import lombok.var; import org.springframework.stereotype.Service; import com.ruoyi.system.domain.bo.SfcOperationPlanningMainBo; @@ -384,38 +383,12 @@ public class SfcOperationPlanningMainServiceImpl implements ISfcOperationPlannin */ @Override public TableDataInfo queryCascadePageList(SfcOperationPlanningMainBo bo, PageQuery pageQuery) { - // 1. 获取当前登录用户信息与工段 - boolean isAdmin = false; - String currentUserWorkCenter = ""; - try { - LoginUser loginUser = LoginHelper.getLoginUser(); - if (loginUser != null && loginUser.getDeptName() != null) { - isAdmin = com.ruoyi.common.helper.LoginHelper.isAdmin(); - if (loginUser.getDeptName() != null) { - currentUserWorkCenter = loginUser.getDeptName(); - } - } - } catch (Exception e) { - log.warn("获取当前登录用户失败,可能未登录或在定时任务中执行"); - } - - // 2. 构建主表查询条件 + // 1. 构建主表查询条件 LambdaQueryWrapper lqw = buildQueryWrapper(bo); // 过滤生产令号包含“暂停”的记录 lqw.notLike(SfcOperationPlanningMain::getHbytSclh, "暂停"); - // 3. 如果不是管理员,且有所属工段,且部门名称是指定的具体工段之一,则只查询明细表中包含该工段的订单主表 ID - boolean isSpecificWorkCenter = false; - if (!isAdmin && StringUtils.isNotBlank(currentUserWorkCenter)) { - List targetWorkCenters = java.util.Arrays.asList("机一工段", "装二工段", "装一工段", "铆焊工段", "机二工段", "委外中心"); - if (targetWorkCenters.contains(currentUserWorkCenter)) { - isSpecificWorkCenter = true; - lqw.inSql(SfcOperationPlanningMain::getId, - "SELECT main_id FROM sfc_operation_planning_detail WHERE work_center_name = '" + currentUserWorkCenter + "'"); - } - } - - // 4. 分页查询主表数据 + // 2. 分页查询主表数据 Page page = baseMapper.selectPage(pageQuery.build(), lqw); List mainList = page.getRecords(); @@ -423,19 +396,22 @@ public class SfcOperationPlanningMainServiceImpl implements ISfcOperationPlannin return TableDataInfo.build(new ArrayList<>()); } - // 5. 提取当前页所有主表的 ID + // 3. 提取当前页所有主表的 ID List mainIds = mainList.stream().map(SfcOperationPlanningMain::getId).collect(Collectors.toList()); - // 6. 根据主表 ID 批量查询子表数据,并按工序号升序排序 (这里查询所有的明细用于计算外层的总进度) - List detailList = detailMapper.selectList(Wrappers.lambdaQuery() - .in(SfcOperationPlanningDetail::getMainId, mainIds) - .orderByAsc(SfcOperationPlanningDetail::getOperNumber)); + // 4. 根据主表 ID 批量查询子表数据,并按工序号升序排序 (这里查询所有的明细用于计算外层的总进度) + var detailQuery = Wrappers.lambdaQuery() + .in(SfcOperationPlanningDetail::getMainId, mainIds); + if (StringUtils.isNotBlank(bo.getWorkCenterName())) { + detailQuery.eq(SfcOperationPlanningDetail::getWorkCenterName, bo.getWorkCenterName().trim()); + } + List detailList = detailMapper.selectList(detailQuery.orderByAsc(SfcOperationPlanningDetail::getOperNumber)); - // 7. 按主表 ID 对子表数据进行分组 + // 5. 按主表 ID 对子表数据进行分组 Map> detailMap = detailList.stream() .collect(Collectors.groupingBy(SfcOperationPlanningDetail::getMainId)); - // 8. 组装返回给前端的层级结构 + // 6. 组装返回给前端的层级结构 List resultList = new ArrayList<>(); for (SfcOperationPlanningMain main : mainList) { OperationPlanResultDTO resultDTO = new OperationPlanResultDTO(); @@ -472,13 +448,7 @@ public class SfcOperationPlanningMainServiceImpl implements ISfcOperationPlannin } } - // 构建明细列表,并在构建时进行权限过滤(只有指定的几个工段用户才只能看到自己的数据,其他非管理员都能看所有) for (SfcOperationPlanningDetail detail : relatedDetails) { - // 权限过滤:如果当前用户属于这几个特定的工段,则只显示自己工段的明细 - if (isSpecificWorkCenter && !currentUserWorkCenter.equals(detail.getWorkCenterName())) { - continue; - } - ProcessDetailDTO detailDTO = new ProcessDetailDTO(); detailDTO.setOperNo(detail.getOperNumber() != null ? detail.getOperNumber().intValue() : null); detailDTO.setProcessName(detail.getProcessName()); @@ -593,8 +563,23 @@ public class SfcOperationPlanningMainServiceImpl implements ISfcOperationPlannin lqw.eq(StringUtils.isNotBlank(bo.getProductCode()), SfcOperationPlanningMain::getProductCode, bo.getProductCode()); lqw.like(StringUtils.isNotBlank(bo.getProductName()), SfcOperationPlanningMain::getProductName, bo.getProductName()); lqw.eq(StringUtils.isNotBlank(bo.getProSpecification()), SfcOperationPlanningMain::getProSpecification, bo.getProSpecification()); - lqw.eq(bo.getPlanStartTime() != null, SfcOperationPlanningMain::getPlanStartTime, bo.getPlanStartTime()); - lqw.eq(bo.getPlanFinishTime() != null, SfcOperationPlanningMain::getPlanFinishTime, bo.getPlanFinishTime()); + lqw.ge(bo.getPlanStartTime() != null, SfcOperationPlanningMain::getPlanStartTime, bo.getPlanStartTime()); + lqw.le(bo.getPlanFinishTime() != null, SfcOperationPlanningMain::getPlanFinishTime, bo.getPlanFinishTime()); + if (StringUtils.isNotBlank(bo.getWorkCenterName())) { + String workCenterName = bo.getWorkCenterName().trim(); + List filteredMainIds = detailMapper.selectList(Wrappers.lambdaQuery() + .select(SfcOperationPlanningDetail::getMainId) + .eq(SfcOperationPlanningDetail::getWorkCenterName, workCenterName)) + .stream() + .map(SfcOperationPlanningDetail::getMainId) + .distinct() + .collect(Collectors.toList()); + if (filteredMainIds.isEmpty()) { + lqw.eq(SfcOperationPlanningMain::getId, -1L); + } else { + lqw.in(SfcOperationPlanningMain::getId, filteredMainIds); + } + } return lqw; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WorkbenchServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WorkbenchServiceImpl.java new file mode 100644 index 0000000..5ae4343 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WorkbenchServiceImpl.java @@ -0,0 +1,285 @@ +package com.ruoyi.system.service.impl; + +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateUtil; +import com.ruoyi.system.domain.dto.workbench.WorkbenchOverviewAggDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchOverviewRespDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchProcessDefectRankRespDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchProcessDefectRankRowDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchWorkOrderBarPointDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchWorkOrderBarRespDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchWorkOrderProgressRespDto; +import com.ruoyi.system.domain.dto.workbench.WorkbenchWorkOrderProgressRowDto; +import com.ruoyi.system.mapper.WorkbenchMapper; +import com.ruoyi.system.service.IWorkbenchService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class WorkbenchServiceImpl implements IWorkbenchService { + + private final WorkbenchMapper workbenchMapper; + + @Override + public WorkbenchOverviewRespDto overview(String rangeType, String startTime, String endTime, String workCenterName) { + Date asOf = new Date(); + TimeRange current = resolveRange(rangeType, startTime, endTime, asOf); + TimeRange prev = previousRange(current); + TimeRange yoy = yearOverYearRange(current); + + WorkbenchOverviewAggDto curAgg = defaultAgg(workbenchMapper.selectOverviewAgg(current.startTime, current.endTime, trimToNull(workCenterName))); + WorkbenchOverviewAggDto prevAgg = defaultAgg(workbenchMapper.selectOverviewAgg(prev.startTime, prev.endTime, trimToNull(workCenterName))); + WorkbenchOverviewAggDto yoyAgg = defaultAgg(workbenchMapper.selectOverviewAgg(yoy.startTime, yoy.endTime, trimToNull(workCenterName))); + + WorkbenchOverviewRespDto resp = new WorkbenchOverviewRespDto(); + resp.setAsOf(DateUtil.formatDateTime(asOf)); + + List cards = new ArrayList<>(); + cards.add(buildCard("finishedWorkOrderCount", "完工工单数", + nvl(curAgg.getFinishedWorkOrderCount()), nvl(prevAgg.getFinishedWorkOrderCount()), nvl(yoyAgg.getFinishedWorkOrderCount()))); + cards.add(buildCard("workOrderCount", "生产工单数", + nvl(curAgg.getWorkOrderCount()), nvl(prevAgg.getWorkOrderCount()), nvl(yoyAgg.getWorkOrderCount()))); + cards.add(buildCard("wipCount", "在制品", + toLong(curAgg.getWipCount()), toLong(prevAgg.getWipCount()), toLong(yoyAgg.getWipCount()))); + cards.add(buildCard("ngCount", "不良品数", + toLong(curAgg.getNgCount()), toLong(prevAgg.getNgCount()), toLong(yoyAgg.getNgCount()))); + resp.setCards(cards); + return resp; + } + + @Override + public WorkbenchWorkOrderBarRespDto workOrderBar(String startTime, String endTime, String groupBy, String workCenterName) { + Date asOf = new Date(); + TimeRange range = resolveBarRange(startTime, endTime, asOf); + String gb = normalizeGroupBy(groupBy); + + List points = workbenchMapper.selectWorkOrderBar(range.startTime, range.endTime, gb, trimToNull(workCenterName)); + Map bucketToCnt = new HashMap<>(); + if (points != null) { + for (WorkbenchWorkOrderBarPointDto p : points) { + if (p != null && StringUtils.isNotBlank(p.getBucket())) { + bucketToCnt.put(p.getBucket(), nvl(p.getCnt())); + } + } + } + + WorkbenchWorkOrderBarRespDto resp = new WorkbenchWorkOrderBarRespDto(); + WorkbenchWorkOrderBarRespDto.Series series = new WorkbenchWorkOrderBarRespDto.Series(); + series.setName("工单数"); + + if ("day".equals(gb)) { + List xAxis = new ArrayList<>(); + List data = new ArrayList<>(); + + Date cursor = DateUtil.beginOfDay(range.startTime); + Date endDay = DateUtil.beginOfDay(range.endTime); + while (!cursor.after(endDay)) { + String bucket = DateUtil.format(cursor, "yyyy-MM-dd"); + xAxis.add(bucket); + data.add(bucketToCnt.getOrDefault(bucket, 0L)); + cursor = DateUtil.offsetDay(cursor, 1); + } + resp.setXAxis(xAxis); + series.setData(data); + } else { + List xAxis = new ArrayList<>(); + List data = new ArrayList<>(); + for (int m = 1; m <= 12; m++) { + xAxis.add(m + "月"); + data.add(bucketToCnt.getOrDefault(String.valueOf(m), 0L)); + } + resp.setXAxis(xAxis); + series.setData(data); + } + + List seriesList = new ArrayList<>(); + seriesList.add(series); + resp.setSeries(seriesList); + return resp; + } + + @Override + public WorkbenchWorkOrderProgressRespDto workOrderProgress(String startTime, String endTime, Integer topN, String workCenterName) { + Date asOf = new Date(); + TimeRange range = resolveRange("month", startTime, endTime, asOf); + int limit = (topN == null || topN <= 0) ? 5 : topN; + + List rows = workbenchMapper.selectWorkOrderProgress(range.startTime, range.endTime, limit, trimToNull(workCenterName)); + List items = new ArrayList<>(); + if (rows != null) { + for (WorkbenchWorkOrderProgressRowDto r : rows) { + WorkbenchWorkOrderProgressRespDto.Item item = new WorkbenchWorkOrderProgressRespDto.Item(); + item.setWorkOrderNo(r.getWorkOrderNo()); + item.setStatus(r.getStatus()); + item.setProgress(toLong(r.getProgress())); + item.setTotalQty(toLong(r.getTotalQty())); + items.add(item); + } + } + + WorkbenchWorkOrderProgressRespDto resp = new WorkbenchWorkOrderProgressRespDto(); + resp.setItems(items); + return resp; + } + + @Override + public WorkbenchProcessDefectRankRespDto processDefectRank(String startTime, String endTime, Integer topN, String workCenterName) { + Date asOf = new Date(); + TimeRange range = resolveRange("month", startTime, endTime, asOf); + int limit = (topN == null || topN <= 0) ? 10 : topN; + + List rows = workbenchMapper.selectProcessDefectRank(range.startTime, range.endTime, limit, trimToNull(workCenterName)); + List items = new ArrayList<>(); + if (rows != null) { + int rank = 1; + for (WorkbenchProcessDefectRankRowDto r : rows) { + WorkbenchProcessDefectRankRespDto.Item item = new WorkbenchProcessDefectRankRespDto.Item(); + item.setRank(rank++); + item.setProcessName(r.getProcessName()); + item.setDefectRate(scale(r.getDefectRate(), 4)); + item.setDefectQty(toLong(r.getDefectQty())); + item.setInspectedQty(toLong(r.getInspectedQty())); + items.add(item); + } + } + + WorkbenchProcessDefectRankRespDto resp = new WorkbenchProcessDefectRankRespDto(); + resp.setItems(items); + return resp; + } + + private WorkbenchOverviewRespDto.Card buildCard(String key, String title, long cur, long prev, long yoy) { + WorkbenchOverviewRespDto.Card card = new WorkbenchOverviewRespDto.Card(); + card.setKey(key); + card.setTitle(title); + card.setValue(cur); + card.setMom(calcRate(cur, prev)); + card.setYoy(calcRate(cur, yoy)); + return card; + } + + private BigDecimal calcRate(long current, long base) { + if (base == 0L) { + return current == 0L ? BigDecimal.ZERO : BigDecimal.ONE; + } + return BigDecimal.valueOf(current) + .subtract(BigDecimal.valueOf(base)) + .divide(BigDecimal.valueOf(base), 4, RoundingMode.HALF_UP); + } + + private BigDecimal scale(BigDecimal val, int scale) { + if (val == null) { + return BigDecimal.ZERO; + } + return val.setScale(scale, RoundingMode.HALF_UP); + } + + private WorkbenchOverviewAggDto defaultAgg(WorkbenchOverviewAggDto agg) { + if (agg != null) { + if (agg.getWorkOrderCount() == null) agg.setWorkOrderCount(0L); + if (agg.getFinishedWorkOrderCount() == null) agg.setFinishedWorkOrderCount(0L); + if (agg.getWipCount() == null) agg.setWipCount(BigDecimal.ZERO); + if (agg.getNgCount() == null) agg.setNgCount(BigDecimal.ZERO); + return agg; + } + WorkbenchOverviewAggDto a = new WorkbenchOverviewAggDto(); + a.setWorkOrderCount(0L); + a.setFinishedWorkOrderCount(0L); + a.setWipCount(BigDecimal.ZERO); + a.setNgCount(BigDecimal.ZERO); + return a; + } + + private long nvl(Long v) { + return v == null ? 0L : v; + } + + private long toLong(BigDecimal v) { + return v == null ? 0L : v.setScale(0, RoundingMode.HALF_UP).longValue(); + } + + private String trimToNull(String s) { + if (StringUtils.isBlank(s)) { + return null; + } + String t = s.trim(); + return t.isEmpty() ? null : t; + } + + private String normalizeGroupBy(String groupBy) { + if ("day".equalsIgnoreCase(groupBy)) { + return "day"; + } + return "month"; + } + + private TimeRange resolveBarRange(String startTime, String endTime, Date asOf) { + if (StringUtils.isBlank(startTime) || StringUtils.isBlank(endTime)) { + Date start = DateUtil.beginOfYear(asOf); + Date end = DateUtil.endOfYear(asOf); + return new TimeRange(start, end); + } + return new TimeRange(parseDateTime(startTime), parseDateTime(endTime)); + } + + private TimeRange resolveRange(String rangeType, String startTime, String endTime, Date asOf) { + if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) { + return new TimeRange(parseDateTime(startTime), parseDateTime(endTime)); + } + + String rt = StringUtils.defaultIfBlank(rangeType, "month").toLowerCase(); + Date start; + if ("day".equals(rt)) { + start = DateUtil.beginOfDay(asOf); + } else if ("week".equals(rt)) { + start = DateUtil.beginOfWeek(asOf); + } else if ("year".equals(rt)) { + start = DateUtil.beginOfYear(asOf); + } else { + start = DateUtil.beginOfMonth(asOf); + } + return new TimeRange(start, asOf); + } + + private Date parseDateTime(String s) { + return DateUtil.parse(s.trim(), "yyyy-MM-dd HH:mm:ss"); + } + + private TimeRange previousRange(TimeRange current) { + long durationMs = current.endTime.getTime() - current.startTime.getTime(); + Date prevEnd = new Date(current.startTime.getTime() - 1); + Date prevStart = new Date(prevEnd.getTime() - durationMs); + return new TimeRange(prevStart, prevEnd); + } + + private TimeRange yearOverYearRange(TimeRange current) { + Date start = DateUtil.offset(current.startTime, DateField.YEAR, -1); + Date end = DateUtil.offset(current.endTime, DateField.YEAR, -1); + return new TimeRange(start, end); + } + + private static class TimeRange { + private final Date startTime; + private final Date endTime; + + private TimeRange(Date startTime, Date endTime) { + if (startTime != null && endTime != null && startTime.after(endTime)) { + this.startTime = endTime; + this.endTime = startTime; + } else { + this.startTime = startTime; + this.endTime = endTime; + } + } + } +} diff --git a/ruoyi-system/src/main/resources/mapper/system/EquipmentInfoMapper.xml b/ruoyi-system/src/main/resources/mapper/system/EquipmentInfoMapper.xml new file mode 100644 index 0000000..21dcaa8 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/EquipmentInfoMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/WorkbenchMapper.xml b/ruoyi-system/src/main/resources/mapper/system/WorkbenchMapper.xml new file mode 100644 index 0000000..190deca --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/WorkbenchMapper.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + +