From acf191df812427ca18b6643c087b788a735f0bf3 Mon Sep 17 00:00:00 2001 From: wenshijun Date: Tue, 26 Aug 2025 17:01:43 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E7=9B=B8=E5=85=B3=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 8 + .../zg/framework/config/SecurityConfig.java | 1 + .../controller/SysNoticeController.java | 2 +- .../wisdom/controller/RkInfoController.java | 11 +- .../controller/RkStatisticsController.java | 170 ++++++ .../wisdom/domain/dto/RkInfoQueryDTO.java | 18 + .../wisdom/domain/vo/Age3060ResultVO.java | 17 + .../wisdom/domain/vo/AgeSummaryVO.java | 24 + .../wisdom/domain/vo/DayCountyStatVO.java | 32 ++ .../domain/vo/GysJhUndeliveredSummaryVO.java | 18 + .../wisdom/domain/vo/GysJhUndeliveredVO.java | 13 + .../project/wisdom/domain/vo/IOBucketVO.java | 26 + .../wisdom/domain/vo/RkAgeDetailVO.java | 53 ++ .../project/wisdom/domain/vo/RkAgeStatVO.java | 21 + .../zg/project/wisdom/domain/vo/RkInVO.java | 11 + .../domain/vo/RkMonthInOutSummaryVO.java | 12 + .../wisdom/domain/vo/RkMonthSummaryVO.java | 18 + .../zg/project/wisdom/domain/vo/RkOutVO.java | 13 + .../project/wisdom/domain/vo/RkSummaryVO.java | 24 + .../project/wisdom/domain/vo/RkTypePieVO.java | 30 ++ .../domain/vo/RkTypeProjectDetailVO.java | 31 ++ .../domain/vo/RkTypeProjectGoodsCountVO.java | 24 + .../wisdom/domain/vo/RkTypeSummaryVO.java | 44 ++ .../wisdom/domain/vo/SceneUsageVO.java | 16 + .../domain/vo/WarehouseSceneStatsResp.java | 11 + .../wisdom/domain/vo/WarehouseStatVO.java | 20 + .../wisdom/domain/vo/WlTypeStockStatVO.java | 20 + .../wisdom/mapper/RkStatisticsMapper.java | 102 ++++ .../wisdom/mapper/WarehouseStatMapper.java | 20 + .../wisdom/service/RkStatisticsService.java | 83 +++ .../wisdom/service/WarehouseStatService.java | 9 + .../service/impl/DdTaskServiceImpl.java | 4 +- .../service/impl/RkInfoServiceImpl.java | 2 +- .../service/impl/RkStatisticsServiceImpl.java | 391 ++++++++++++++ .../impl/WarehouseStatServiceImpl.java | 80 +++ .../mybatis/wisdom/RkStatisticsMapper.xml | 487 ++++++++++++++++++ .../mybatis/wisdom/WarehouseStatMapper.xml | 50 ++ 37 files changed, 1905 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/zg/project/wisdom/controller/RkStatisticsController.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/dto/RkInfoQueryDTO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/Age3060ResultVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/AgeSummaryVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/DayCountyStatVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/GysJhUndeliveredSummaryVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/GysJhUndeliveredVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/IOBucketVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkAgeDetailVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkAgeStatVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkInVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkMonthInOutSummaryVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkMonthSummaryVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkOutVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkSummaryVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkTypePieVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkTypeProjectDetailVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkTypeProjectGoodsCountVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/RkTypeSummaryVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/SceneUsageVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/WarehouseSceneStatsResp.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/WarehouseStatVO.java create mode 100644 src/main/java/com/zg/project/wisdom/domain/vo/WlTypeStockStatVO.java create mode 100644 src/main/java/com/zg/project/wisdom/mapper/RkStatisticsMapper.java create mode 100644 src/main/java/com/zg/project/wisdom/mapper/WarehouseStatMapper.java create mode 100644 src/main/java/com/zg/project/wisdom/service/RkStatisticsService.java create mode 100644 src/main/java/com/zg/project/wisdom/service/WarehouseStatService.java create mode 100644 src/main/java/com/zg/project/wisdom/service/impl/RkStatisticsServiceImpl.java create mode 100644 src/main/java/com/zg/project/wisdom/service/impl/WarehouseStatServiceImpl.java create mode 100644 src/main/resources/mybatis/wisdom/RkStatisticsMapper.xml create mode 100644 src/main/resources/mybatis/wisdom/WarehouseStatMapper.xml diff --git a/pom.xml b/pom.xml index 0139fc2..a0c10e3 100644 --- a/pom.xml +++ b/pom.xml @@ -307,6 +307,14 @@ com.zg.SmartApplication + + org.apache.maven.plugins + maven-compiler-plugin + + 9 + 9 + + diff --git a/src/main/java/com/zg/framework/config/SecurityConfig.java b/src/main/java/com/zg/framework/config/SecurityConfig.java index 5327891..25c2d35 100644 --- a/src/main/java/com/zg/framework/config/SecurityConfig.java +++ b/src/main/java/com/zg/framework/config/SecurityConfig.java @@ -115,6 +115,7 @@ public class SecurityConfig "/register", "/captchaImage", "/user/**", + "/stat/**", "/query/jh/**", "/wisdom/signature/**", "/system/config/**", diff --git a/src/main/java/com/zg/project/system/controller/SysNoticeController.java b/src/main/java/com/zg/project/system/controller/SysNoticeController.java index 08c002e..dc3d703 100644 --- a/src/main/java/com/zg/project/system/controller/SysNoticeController.java +++ b/src/main/java/com/zg/project/system/controller/SysNoticeController.java @@ -35,7 +35,7 @@ public class SysNoticeController extends BaseController /** * 获取通知公告列表 */ - @PreAuthorize("@ss.hasPermi('system:notice:list')") +// @PreAuthorize("@ss.hasPermi('system:notice:list')") @GetMapping("/list") public TableDataInfo list(SysNotice notice) { diff --git a/src/main/java/com/zg/project/wisdom/controller/RkInfoController.java b/src/main/java/com/zg/project/wisdom/controller/RkInfoController.java index d5d5c38..261e9ba 100644 --- a/src/main/java/com/zg/project/wisdom/controller/RkInfoController.java +++ b/src/main/java/com/zg/project/wisdom/controller/RkInfoController.java @@ -5,10 +5,8 @@ import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletResponse; -import com.zg.project.wisdom.domain.dto.PcRkInfoBatchDTO; -import com.zg.project.wisdom.domain.dto.RefundRequestDTO; -import com.zg.project.wisdom.domain.dto.RkCancelDTO; -import com.zg.project.wisdom.domain.dto.StockOutDTO; +import com.github.pagehelper.PageHelper; +import com.zg.project.wisdom.domain.dto.*; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -40,9 +38,8 @@ public class RkInfoController extends BaseController */ @PreAuthorize("@ss.hasPermi('wisdom:stock:list')") @PostMapping("/list") - public TableDataInfo list(@RequestBody RkInfo rkInfo) - { - startPage(); + public TableDataInfo list(@RequestBody RkInfoQueryDTO rkInfo) { + PageHelper.startPage(rkInfo.getPageNum(), rkInfo.getPageSize()); List list = rkInfoService.selectRkInfoList(rkInfo); return getDataTable(list); } diff --git a/src/main/java/com/zg/project/wisdom/controller/RkStatisticsController.java b/src/main/java/com/zg/project/wisdom/controller/RkStatisticsController.java new file mode 100644 index 0000000..c83b07d --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/controller/RkStatisticsController.java @@ -0,0 +1,170 @@ +package com.zg.project.wisdom.controller; + +import com.zg.framework.web.domain.AjaxResult; +import com.zg.project.wisdom.domain.vo.*; +import com.zg.project.wisdom.service.RkStatisticsService; +import com.zg.project.wisdom.service.WarehouseStatService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +@Api(tags = "库存统计") +@RestController +@RequestMapping("/stat") +public class RkStatisticsController { + + @Resource + private RkStatisticsService rkStatisticsService; + + @Resource + private WarehouseStatService warehouseStatService; + + + /** 库龄统计(未出库:>10 / >20 / >30 天),value 为项目数(按 xm_no 去重) */ + @ApiOperation("库龄统计(未出库:>10 / >20 / >30 天)") + @GetMapping("/age") + public AjaxResult ageStats() { + List> stats = rkStatisticsService.getAgeStatsAsList(); + return AjaxResult.success(stats); + } + + /** + * 仓库-场景库位使用统计(总仓库数、每仓库场景数、每场景库位/使用率/空闲率) + * @return + */ + @GetMapping("/warehouseScene") + public AjaxResult warehouseScene() { + WarehouseSceneStatsResp resp = warehouseStatService.getWarehouseSceneStats(); + return AjaxResult.success(resp); + } + + /** + * 当前库存根据入库类型统计项目数,每个项目的条目数,实际入库总数,金额总和(仅未出库) + */ + @GetMapping("/type/summary") + public AjaxResult typeSummary() { + List list = rkStatisticsService.getTypeSummary(); + return AjaxResult.success(list); + } + + + /** + * 县局汇总(仅未出库,county 非空) + */ + @GetMapping("/county/summary") + public AjaxResult countySummary() { + List list = rkStatisticsService.getCountySummary(); + return AjaxResult.success(list); + } + + /** + * 一个接口:一周天级统计(返回入库+出库)根据供电中心统计 + * - 如果 start/end 都传了,就用传入值; + * - 否则:end=今天,start=本周周一; + */ + @ApiOperation("一周天级统计(入库+出库合并返回)") + @GetMapping("/week/daily") + public AjaxResult weekDaily(@RequestParam(required = false) String start, + @RequestParam(required = false) String end) { + Map data = rkStatisticsService.weekDaily(start, end); + return AjaxResult.success(data); + } + + /** + * 库龄统计(>30天 & >60天):项目数、条目数、总金额 + 明细列表 + */ + @ApiOperation("库龄统计:>30天 & >60天(含汇总与明细)") + @GetMapping("/age/count") + public AjaxResult age3060() { + List> data = rkStatisticsService.getAge3060(); + return AjaxResult.success(data); + } + + @ApiOperation("导出库龄明细(>30天与>60天,合并导出)") + @PostMapping("/age/export") + public void exportAge3060(HttpServletResponse response) { + rkStatisticsService.exportAge3060(response); + } + + /** + * 入库类型饼图数据接口 + * 返回每个入库类型的项目数、条目数、总数量、总金额 + */ + @ApiOperation("入库类型饼图数据") + @GetMapping("/type/pie") + public AjaxResult typePie() { + List list = rkStatisticsService.getTypePie(); + return AjaxResult.success(list); + } + + /** + * 按入库类型统计(带时间范围) + * 统计维度:项目数、条目数、总数量、总金额 + * 时间字段:rk_time(自然日闭区间 [start, end]) + * 参数格式:推荐 yyyy-MM-dd(如 2025-01-01) + */ + @ApiOperation("按入库类型统计(带时间范围:项目数/条目数/总数量/总金额)") + @GetMapping("/type/range") + public AjaxResult typeSummaryRange(@RequestParam(required = false) String start, + @RequestParam(required = false) String end) { + List list = rkStatisticsService.getTypeSummaryByRange(start, end); + return AjaxResult.success(list); + } + + @ApiOperation("按出库类型统计(带时间范围:项目数/条目数/总数量/总金额)") + @GetMapping("/type/out/range") + public AjaxResult outTypeSummaryRange(@RequestParam(required = false) String start, + @RequestParam(required = false) String end) { + List list = rkStatisticsService.getOutTypeSummaryByRange(start, end); + return AjaxResult.success(list); + } + + + @ApiOperation("本月出入库汇总(项目数/条目数/总数量/总金额)") + @GetMapping("/month/summary") + public AjaxResult monthSummary() { + RkMonthInOutSummaryVO vo = rkStatisticsService.getThisMonthInOutSummary(); + return AjaxResult.success(vo); + } + + @ApiOperation("应到未到统计(未到/部分未到)") + @GetMapping("/undelivered") + public AjaxResult undelivered() { + GysJhUndeliveredVO vo = rkStatisticsService.getUndeliveredSummary(); + return AjaxResult.success(vo); + } + + @ApiOperation("当前库存按物资类型统计(项目数/条目数/总数量/总金额)") + @GetMapping("/stock/wlType") + public AjaxResult stockByWlType() { + List list = rkStatisticsService.getCurrentStockByWlType(); + return AjaxResult.success(list); + } + + /** + * 当前库存:入库时间 >30天(项目数 / 条目数 / 总数量 / 总金额) + * 口径:仅统计未删除、未出库(is_chuku=0 或 NULL)、rk_time 非空且 DATEDIFF(CURDATE(), DATE(rk_time)) > 30 的记录 + */ + @ApiOperation("当前库存:入库时间>30天(项目数/条目数/总数量/总金额)") + @GetMapping("/age/gt30") + public AjaxResult ageGt30() { + RkTypePieVO vo = rkStatisticsService.getAgeGt30Summary(); + return AjaxResult.success(vo); + } + + /** + * range: 0=最近一周(7天, 按日);2=最近半年(6个月, 按月) + * 返回:每个时间桶一行(bucket, 入/出库项目数&条目数) + */ + @ApiOperation("出/入库项目数与条目数(按时间桶返回)") + @GetMapping("/io/buckets") + public AjaxResult ioBuckets(@RequestParam Integer range) { + List rows = rkStatisticsService.getIOBuckets(range); + return AjaxResult.success(rows); + } +} \ No newline at end of file diff --git a/src/main/java/com/zg/project/wisdom/domain/dto/RkInfoQueryDTO.java b/src/main/java/com/zg/project/wisdom/domain/dto/RkInfoQueryDTO.java new file mode 100644 index 0000000..42d0eb2 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/dto/RkInfoQueryDTO.java @@ -0,0 +1,18 @@ +package com.zg.project.wisdom.domain.dto; + +import com.zg.project.wisdom.domain.RkInfo; +import lombok.Data; + +/** + * 库存单据分页查询 DTO + * 继承 RkInfo,额外加分页参数 + */ +@Data +public class RkInfoQueryDTO extends RkInfo { + + /** 页码 */ + private Integer pageNum; + + /** 每页条数 */ + private Integer pageSize; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/Age3060ResultVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/Age3060ResultVO.java new file mode 100644 index 0000000..299ca29 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/Age3060ResultVO.java @@ -0,0 +1,17 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.util.List; + +/** 最终返回结构(汇总 + 明细) */ +@Data +public class Age3060ResultVO { + /** >30 天的汇总 */ + private AgeSummaryVO.Slot gt30; + /** >60 天的汇总 */ + private AgeSummaryVO.Slot gt60; + /** >30 天明细 */ + private List gt30List; + /** >60 天明细 */ + private List gt60List; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/AgeSummaryVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/AgeSummaryVO.java new file mode 100644 index 0000000..fdd8d81 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/AgeSummaryVO.java @@ -0,0 +1,24 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; + +/** 汇总 VO(包含两个 Slot:gt30 / gt60) */ +@Data +public class AgeSummaryVO { + + @Data + public static class Slot { + /** 仅用于 resultMap 绑定(无业务含义) */ + private Integer dummyId; + /** 项目数(DISTINCT xm_no) */ + private Integer projectCount; + /** 条目数(COUNT(*)) */ + private Integer goodsCount; + /** 总金额(SUM(ht_dj * real_qty)) */ + private BigDecimal sumAmount; + } + + private Slot gt30; + private Slot gt60; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/DayCountyStatVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/DayCountyStatVO.java new file mode 100644 index 0000000..010eb08 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/DayCountyStatVO.java @@ -0,0 +1,32 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; + +/** 周天级统计(按县局) */ +@Data +public class DayCountyStatVO { + /** + * 日期(如果 SQL 未分日统计,该字段可能为 null) + * 示例:2025-08-19 + */ + private String day; + + /** 县局(若为空或空串会被归类为“未知县局”) */ + private String xj; + + /** 项目数(去重 xm_no 计数) */ + private Integer projectCount; + + /** 条目数(明细记录总数,COUNT(*)) */ + private Integer itemCount; + + /** 数量总和(SUM(real_qty)) */ + private Long totalQty; + + /** 合同金额总和(SUM(ht_dj * real_qty)) */ + private BigDecimal amountHt; + + /** 计划金额总和(仅入库时有值,出库恒为 0) */ + private BigDecimal amountPlan; +} \ No newline at end of file diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/GysJhUndeliveredSummaryVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/GysJhUndeliveredSummaryVO.java new file mode 100644 index 0000000..c4875ce --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/GysJhUndeliveredSummaryVO.java @@ -0,0 +1,18 @@ +// com.zg.project.wisdom.domain.vo.GysJhUndeliveredSummaryVO +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; + +/** 供应计划-应到未到/部分未到 汇总 */ +@Data +public class GysJhUndeliveredSummaryVO { + /** 去重项目数(按 xm_no 去重) */ + private Integer projectCount; + /** 条目数(记录行数) */ + private Integer itemCount; + /** 总数量(jh_qty 合计,表示剩余未到数量) */ + private Long totalQty; + /** 总金额(ht_dj * jh_qty 合计) */ + private BigDecimal amountPlan; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/GysJhUndeliveredVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/GysJhUndeliveredVO.java new file mode 100644 index 0000000..8bfc16c --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/GysJhUndeliveredVO.java @@ -0,0 +1,13 @@ +// com.zg.project.wisdom.domain.vo.GysJhUndeliveredVO +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; + +/** 返回体:未到 + 部分未到 + 合计 */ +@Data +public class GysJhUndeliveredVO { + /** 未到货(status='0') */ + private GysJhUndeliveredSummaryVO undelivered; + /** 部分未到货(status='2') */ + private GysJhUndeliveredSummaryVO partialUndelivered; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/IOBucketVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/IOBucketVO.java new file mode 100644 index 0000000..319f341 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/IOBucketVO.java @@ -0,0 +1,26 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; + +/** + * 出/入库统计结果 VO + * 每个时间桶(按日/按月)一行 + */ +@Data +public class IOBucketVO { + + /** 时间桶: + * range=0(最近一周):返回 MM-dd; + * range=2(最近半年):返回 yyyy-MM + */ + private String bucket; + + /** 入库项目数 */ + private Integer inProjectCount; + /** 入库条目数 */ + private Integer inItemCount; + /** 出库项目数 */ + private Integer outProjectCount; + /** 出库条目数 */ + private Integer outItemCount; +} \ No newline at end of file diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkAgeDetailVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkAgeDetailVO.java new file mode 100644 index 0000000..5dc5db4 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkAgeDetailVO.java @@ -0,0 +1,53 @@ +package com.zg.project.wisdom.domain.vo; + +import com.zg.framework.aspectj.lang.annotation.Excel; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class RkAgeDetailVO { + /* ---------- 仅用于导出分组显示,不落库 ---------- */ + @Excel(name = "库龄分组") // “>30天”/“>60天” + private String ageBucket; + + @Excel(name = "ID") + private Long id; + + /** 项目号 / 项目描述 */ + @Excel(name = "项目号") + private String xmNo; + + @Excel(name = "项目描述") + private String xmMs; + + /** 入库时间 & 库龄天数 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "入库时间", dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date rkTime; + + @Excel(name = "库龄(天)") + private Integer kuLingDays; + + /** 单价、数量、行金额 */ + @Excel(name = "单价") + private BigDecimal htDj; + + @Excel(name = "数量") + private BigDecimal realQty; + + @Excel(name = "行金额") + private BigDecimal amount; + + /** 库位 / 托盘 / 单据号(便于追踪) */ + @Excel(name = "库位") + private String pcode; + + @Excel(name = "托盘") + private String trayCode; + + @Excel(name = "单据号") + private String billNo; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkAgeStatVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkAgeStatVO.java new file mode 100644 index 0000000..31a7884 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkAgeStatVO.java @@ -0,0 +1,21 @@ +// com/zg/project/wisdom/vo/stat/RkAgeStatVO.java +package com.zg.project.wisdom.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel(value = "RkAgeStatVO", description = "库龄统计结果(仅未出库)") +public class RkAgeStatVO { + + @ApiModelProperty("大于10天的数量") + private Integer gt10; + + @ApiModelProperty("大于20天的数量") + private Integer gt20; + + @ApiModelProperty("大于30天的数量") + private Integer gt30; + +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkInVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkInVO.java new file mode 100644 index 0000000..8d4e21e --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkInVO.java @@ -0,0 +1,11 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.time.LocalDateTime; + +/** 入库原始数据(用于代码侧分桶) */ +@Data +public class RkInVO { + private String xmNo; + private LocalDateTime rkTime; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkMonthInOutSummaryVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkMonthInOutSummaryVO.java new file mode 100644 index 0000000..5163668 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkMonthInOutSummaryVO.java @@ -0,0 +1,12 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; + +/** 月度入/出库两段结果 */ +@Data +public class RkMonthInOutSummaryVO { + /** 入库汇总(rk_time) */ + private RkMonthSummaryVO inSummary; + /** 出库汇总(ly_time, is_chuku=1) */ + private RkMonthSummaryVO outSummary; +} \ No newline at end of file diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkMonthSummaryVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkMonthSummaryVO.java new file mode 100644 index 0000000..c37abef --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkMonthSummaryVO.java @@ -0,0 +1,18 @@ +// com.zg.project.wisdom.domain.vo.RkMonthSummaryVO +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; + +/** 月度汇总(项目数/条目数/总数量/总金额) */ +@Data +public class RkMonthSummaryVO { + /** 去重项目数(按 xm_no 去重) */ + private Integer projectCount; + /** 条目数(明细行数) */ + private Integer itemCount; + /** 总数量(real_qty 合计) */ + private Long totalQty; + /** 总金额(ht_dj * real_qty 合计) */ + private BigDecimal amountHt; +} \ No newline at end of file diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkOutVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkOutVO.java new file mode 100644 index 0000000..cc2a01a --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkOutVO.java @@ -0,0 +1,13 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.time.LocalDateTime; + +/** 出库原始数据(用于代码侧分桶) */ +@Data +public class RkOutVO { + private String xmNoCk; + private String xmNo; + private LocalDateTime lyTime; + private LocalDateTime updateTime; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkSummaryVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkSummaryVO.java new file mode 100644 index 0000000..5658f42 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkSummaryVO.java @@ -0,0 +1,24 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; + +/** 县局维度汇总结果 */ +@Data +public class RkSummaryVO { + + /** 分组名称(如县局名、入库类型等) */ + private String groupName; + + /** 项目数(去重 xm_no) */ + private Integer projectCount; + + /** 条目总数(COUNT(*)) */ + private Integer goodsCountTotal; + + /** 总金额 = SUM(ht_dj * real_qty) */ + private BigDecimal sumAmount; + + /** 总数量 = SUM(real_qty) */ + private BigDecimal sumQty; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkTypePieVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkTypePieVO.java new file mode 100644 index 0000000..f112f9f --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkTypePieVO.java @@ -0,0 +1,30 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; + +/** + * 入库类型饼状图统计 VO + * 每个入库类型对应一条记录,用于前端饼状图展示 + */ +@Data +public class RkTypePieVO { + + /** 入库类型编码(stock_in_type.type_code) */ + private String rkTypeCode; + + /** 入库类型名称(stock_in_type.type_name) */ + private String rkTypeName; + + /** 项目数量(去重 xm_no) */ + private Integer projectCount; + + /** 条目总数(COUNT(*)) */ + private Integer goodsCountTotal; + + /** 总数量(SUM real_qty) */ + private BigDecimal sumQty; + + /** 总金额(SUM ht_dj * real_qty) */ + private BigDecimal sumAmount; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkTypeProjectDetailVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkTypeProjectDetailVO.java new file mode 100644 index 0000000..e9580cc --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkTypeProjectDetailVO.java @@ -0,0 +1,31 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; + +/** + * 入库类型 + 项目 明细VO + * 用于统计每种入库类型下的每个项目: + * - 项目信息 + * - 货物数量 + * - 合同单价合计 + * - 实际入库数量合计 + * - 金额合计 + */ +@Data +public class RkTypeProjectDetailVO { + /** 入库类型(rk_type) */ + private Integer rkType; + /** 项目号(xm_no) */ + private String projectNo; + /** 项目名称/描述(xm_ms) */ + private String projectName; + /** 该项目下的货物条数 */ + private Integer goodsCount; + /** 合同单价合计 */ + private BigDecimal sumHtDj; + /** 实际入库数量合计 */ + private BigDecimal sumRealQty; + /** 金额合计 = ht_dj * real_qty */ + private BigDecimal sumAmount; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkTypeProjectGoodsCountVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkTypeProjectGoodsCountVO.java new file mode 100644 index 0000000..0ec6b56 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkTypeProjectGoodsCountVO.java @@ -0,0 +1,24 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; + +/** + * 某入库类型下,单个项目的货物条目数 + */ +@Data +public class RkTypeProjectGoodsCountVO { + /** 入库类型(冗余用于归并) */ + private Integer rkType; + + /** 县局,类型统计时可为null */ + private String county; + + /** 项目号(xm_no) */ + private String projectNo; + + /** 项目名称/描述(xm_ms),取一个示例值 */ + private String projectName; + + /** 该项目的货物条目数(COUNT(*)) */ + private Integer goodsCount; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/RkTypeSummaryVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/RkTypeSummaryVO.java new file mode 100644 index 0000000..76e6701 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/RkTypeSummaryVO.java @@ -0,0 +1,44 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; +import java.util.List; + + +/** + * 入库类型汇总VO(仅统计未出库 is_chuku = 0) + * 每种入库类型下聚合: + * - projectCount:项目数量(去重xm_no) + * - goodsCountTotal:货物条目总数(记录条数) + * - sumHtDj:合同单价合计 + * - sumRealQty:实际入库数量合计 + * - sumAmount:金额合计 = ht_dj * real_qty + * - avgGoodsPerProject:每项目平均条目数 = goodsCountTotal / projectCount(保留2位小数) + * - projectGoodsCounts:每个项目的“条目数清单”(你要的关键补充) + */ +@Data +public class RkTypeSummaryVO { + /** 入库类型(rk_type) */ + private String rkType; + + /** 县局,类型统计时可为null */ + private String county; + + /** 项目数量(去重xm_no) */ + private Integer projectCount; + + /** 货物条目总数(该类型下的记录条数) */ + private Integer goodsCountTotal; + + /** 合同单价合计 */ + private BigDecimal sumHtDj; + + /** 实际入库数量合计 */ + private BigDecimal sumRealQty; + + /** 金额合计 = ht_dj * real_qty */ + private BigDecimal sumAmount; + + /** 每个项目的条目数清单(仅含条目数;如需金额/数量可再扩展) */ + private List projectGoodsCounts; +} \ No newline at end of file diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/SceneUsageVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/SceneUsageVO.java new file mode 100644 index 0000000..088913d --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/SceneUsageVO.java @@ -0,0 +1,16 @@ +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; + +@Data +public class SceneUsageVO { + private String sceneCode; + private String sceneName; + + private Integer totalPositions; // 该场景库位总数 + private Integer usedPositions; // 已使用(未出库) + private Integer freePositions; // 空闲 = total - used + + private String usageRatePercent; // 使用率(xx.xx) + private String freeRatePercent; // 空闲率(xx.xx) +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/WarehouseSceneStatsResp.java b/src/main/java/com/zg/project/wisdom/domain/vo/WarehouseSceneStatsResp.java new file mode 100644 index 0000000..523d2e2 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/WarehouseSceneStatsResp.java @@ -0,0 +1,11 @@ +// src/main/java/com/zg/project/wisdom/domain/vo/stat/WarehouseSceneStatsResp.java +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.util.List; + +@Data +public class WarehouseSceneStatsResp { + private Integer totalWarehouses; // 启用仓库总数 + private List items; +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/WarehouseStatVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/WarehouseStatVO.java new file mode 100644 index 0000000..fcb1d6d --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/WarehouseStatVO.java @@ -0,0 +1,20 @@ +// src/main/java/com/zg/project/wisdom/domain/vo/stat/WarehouseStatVO.java +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.util.List; + +@Data +public class WarehouseStatVO { + private String warehouseCode; + private String warehouseName; + + private Integer sceneCount; // 场景数量 + private Integer totalPositions; // 仓库汇总-总库位 + private Integer usedPositions; // 仓库汇总-已使用 + private Integer freePositions; // 仓库汇总-空闲 + private String usageRatePercent; // 仓库汇总-使用率 + private String freeRatePercent; // 仓库汇总-空闲率 + + private List scenes; // 场景明细 +} diff --git a/src/main/java/com/zg/project/wisdom/domain/vo/WlTypeStockStatVO.java b/src/main/java/com/zg/project/wisdom/domain/vo/WlTypeStockStatVO.java new file mode 100644 index 0000000..e7b955d --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/domain/vo/WlTypeStockStatVO.java @@ -0,0 +1,20 @@ +// com.zg.project.wisdom.domain.vo.WlTypeStockStatVO +package com.zg.project.wisdom.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; + +/** 当前库存-按物资类型统计 */ +@Data +public class WlTypeStockStatVO { + /** 物资类型(wl_type) */ + private String typeName; + /** 去重项目数(按 xm_no) */ + private Integer projectCount; + /** 条目数(明细行数) */ + private Integer itemCount; + /** 总数量(real_qty 合计) */ + private Long totalQty; + /** 总金额(ht_dj * real_qty 合计) */ + private BigDecimal amountHt; +} diff --git a/src/main/java/com/zg/project/wisdom/mapper/RkStatisticsMapper.java b/src/main/java/com/zg/project/wisdom/mapper/RkStatisticsMapper.java new file mode 100644 index 0000000..8260dde --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/mapper/RkStatisticsMapper.java @@ -0,0 +1,102 @@ +package com.zg.project.wisdom.mapper; + +import com.zg.project.wisdom.domain.vo.*; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +@Mapper +public interface RkStatisticsMapper { + + /** 库龄统计(未出库):按项目数计数(去重 xm_no) */ + RkAgeStatVO selectAgeStats(); + + + /** + * 入库类型汇总(仅未出库) + * @return 汇总结果列表 + */ + List selectRkTypeSummary(); + + + /** + * 按入库类型 + 项目 统计【每项目的条目数】 + * (用于回填到类型汇总的 projectGoodsCounts 中) + */ + List selectProjectGoodsCountByType(); + + + /** 入库类型汇总(未出库,rk_type 非空) */ + List selectTypeSummary(); + + /** + * 县局汇总(未出库,县局非空) + */ + List selectCountySummary(); + + /** 入库(每天+县局):按 rk_time */ + List selectWeekDailyIn(@Param("startDate") String startDate, + @Param("endDate") String endDate); + + /** 出库(每天+县局):按 ly_time */ + List selectWeekDailyOut(@Param("startDate") String startDate, + @Param("endDate") String endDate); + + /** 汇总(>30 & >60):项目数、条目数、总金额 */ + AgeSummaryVO selectAge3060Summary(); + + /** 明细列表:返回超过 minDays 天的明细(minDays 取 30 或 60) */ + List selectAgeDetails(@Param("minDays") int minDays); + + /** + * 按入库类型统计饼状图数据 + * @return 每个入库类型的项目数、条目数、总数量、总金额 + */ + List selectTypePie(); + + /** + * 按入库类型统计(联查 stock_in_type,时间范围过滤) + * 仅统计未删除、未出库 + */ + List selectTypeSummaryByRange(@Param("start") String start, + @Param("end") String end); + + /** + * 按出库类型统计(时间范围;仅 is_chuku=1) + * 维度:项目数、条目数、总数量、总金额 + */ + List selectOutTypeSummaryByRange(@Param("start") String start, + @Param("end") String end); + + + /** 本月入库汇总(按 rk_time 过滤) */ + RkMonthSummaryVO selectMonthlyInSummary(@Param("start") String start, + @Param("end") String end); + + /** 本月出库汇总(is_chuku=1 且按 ly_time 过滤) */ + RkMonthSummaryVO selectMonthlyOutSummary(@Param("start") String start, + @Param("end") String end); + + + /** 应到未到(status='0') */ + GysJhUndeliveredSummaryVO selectUndelivered(); + + /** 部分未到(status='2') */ + GysJhUndeliveredSummaryVO selectPartialUndelivered(); + + /** 当前库存:按物资类型统计(过滤 wl_type 空/无) */ + List selectCurrentStockByWlType(); + + /** 当前库存:入库时间 >30 天的四项汇总(SQL 聚合计算) */ + RkTypePieVO selectAgeGt30Summary(); + + /** 入库:在 [startTs, endTs) 时间范围内的原始行(仅最小过滤) */ + List selectInRange(@Param("startTs") String startTs, + @Param("endTs") String endTs); + + /** 出库:在 [startTs, endTs) 时间范围内、is_chuku=1 的原始行(仅最小过滤) */ + List selectOutRange(@Param("startTs") String startTs, + @Param("endTs") String endTs); +} diff --git a/src/main/java/com/zg/project/wisdom/mapper/WarehouseStatMapper.java b/src/main/java/com/zg/project/wisdom/mapper/WarehouseStatMapper.java new file mode 100644 index 0000000..050a2b0 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/mapper/WarehouseStatMapper.java @@ -0,0 +1,20 @@ +// src/main/java/com/zg/project/wisdom/mapper/WarehouseStatMapper.java +package com.zg.project.wisdom.mapper; + +import com.zg.project.wisdom.domain.vo.SceneUsageVO; +import com.zg.project.wisdom.domain.vo.WarehouseStatVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface WarehouseStatMapper { + + /** 启用仓库列表(只取 code、name,用 VO 承载,不引入新类) */ + List selectEnabledWarehouses(); + + /** 指定仓库下的场景库位统计(总库位 & 未出库占用库位) */ + List selectSceneUsageByWarehouse(@Param("warehouseCode") String warehouseCode); + + /** 启用仓库总数(也可用 selectEnabledWarehouses().size()) */ + Integer countEnabledWarehouses(); +} diff --git a/src/main/java/com/zg/project/wisdom/service/RkStatisticsService.java b/src/main/java/com/zg/project/wisdom/service/RkStatisticsService.java new file mode 100644 index 0000000..a17a11d --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/service/RkStatisticsService.java @@ -0,0 +1,83 @@ +package com.zg.project.wisdom.service; + +import com.zg.project.wisdom.domain.vo.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +/** 首页统计服务 */ +public interface RkStatisticsService { + + /** 库龄统计(仅未出库) */ +// RkAgeStatVO getAgeStats(); + + /** 新增:返回 Map */ + List> getAgeStatsAsList(); + + /** 入库类型汇总(未出库) */ + List getTypeSummary(); + + /** + * 县局汇总(未出库) + */ + List getCountySummary(); + + + /** + * 一周天级统计(一个接口返回入库+出库) + * @param start 可空(yyyy-MM-dd) + * @param end 可空(yyyy-MM-dd) + * @return { "in": List, "out": List } + */ + Map weekDaily(String start, String end); + + /** + * 库龄统计(>30天 & >60天):项目数、条目数、总金额 + 两个明细列表 + */ + Age3060ResultVO getAge3060Detail(); + + List> getAge3060(); + + /** 导出库龄明细(>30 与 >60 合并导出) */ + void exportAge3060(HttpServletResponse response); + + /** + * 获取入库类型饼状图数据 + * @return 入库类型饼图统计列表 + */ + List getTypePie(); + + /** + * 按入库类型统计(时间范围) + * @param start 开始日期(建议 yyyy-MM-dd) + * @param end 结束日期(建议 yyyy-MM-dd) + * @return 每个入库类型的项目数、条目数、总数量、总金额 + */ + List getTypeSummaryByRange(String start, String end); + + /** + * 按出库类型统计(时间范围) + * 口径:仅统计已出库(is_chuku = 1),排除删除;出库类型非空 + * 时间字段:ly_time(自然日闭区间 [start, end],参数建议 yyyy-MM-dd) + */ + List getOutTypeSummaryByRange(String start, String end); + + /** 本月入/出库汇总(项目数/条目数/总数量/总金额) */ + RkMonthInOutSummaryVO getThisMonthInOutSummary(); + + /** 供应计划:应到未到统计(未到、部分未到、合计) */ + GysJhUndeliveredVO getUndeliveredSummary(); + + /** 当前库存:按物资类型统计 */ + List getCurrentStockByWlType(); + + /** 当前库存 “入库时间 >30 天”的四项汇总 */ + RkTypePieVO getAgeGt30Summary(); + + /** + * range: 0=最近一周(日);2=最近半年(月) + * 返回每个时间桶的一行数据(固定 7 行或 6 行) + */ + List getIOBuckets(Integer range); +} diff --git a/src/main/java/com/zg/project/wisdom/service/WarehouseStatService.java b/src/main/java/com/zg/project/wisdom/service/WarehouseStatService.java new file mode 100644 index 0000000..5e83d9b --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/service/WarehouseStatService.java @@ -0,0 +1,9 @@ + +package com.zg.project.wisdom.service; + + +import com.zg.project.wisdom.domain.vo.WarehouseSceneStatsResp; + +public interface WarehouseStatService { + WarehouseSceneStatsResp getWarehouseSceneStats(); +} diff --git a/src/main/java/com/zg/project/wisdom/service/impl/DdTaskServiceImpl.java b/src/main/java/com/zg/project/wisdom/service/impl/DdTaskServiceImpl.java index 5995af5..2f0d6d7 100644 --- a/src/main/java/com/zg/project/wisdom/service/impl/DdTaskServiceImpl.java +++ b/src/main/java/com/zg/project/wisdom/service/impl/DdTaskServiceImpl.java @@ -178,10 +178,10 @@ public class DdTaskServiceImpl implements IDdTaskService { wcsParam.put("Quantity", ""); } else { String materialCode = task.getMid(); - String quantity = task.getNum().toString(); +// String quantity = task.getNum().toString(); String specification = mtdMapper.selectWlmsByWlbh(materialCode); wcsParam.put("MaterialCode", materialCode); - wcsParam.put("Quantity", quantity); +// wcsParam.put("Quantity", quantity); wcsParam.put("Specification", specification == null ? "" : specification); } diff --git a/src/main/java/com/zg/project/wisdom/service/impl/RkInfoServiceImpl.java b/src/main/java/com/zg/project/wisdom/service/impl/RkInfoServiceImpl.java index 6330991..d214588 100644 --- a/src/main/java/com/zg/project/wisdom/service/impl/RkInfoServiceImpl.java +++ b/src/main/java/com/zg/project/wisdom/service/impl/RkInfoServiceImpl.java @@ -647,7 +647,7 @@ public class RkInfoServiceImpl implements IRkInfoService update.setBillNoCk(billNo); update.setCkType(dto.getCkType()); update.setTeamCode(dto.getTeamCode()); - update.setLyTime(now); // ❗使用后端生成时间 + update.setLyTime(now); update.setCkLihuoY(dto.getCkLihuoY()); update.setCkRemark(item.getCkRemark()); update.setXmNoCk(dto.getXmNoCk()); diff --git a/src/main/java/com/zg/project/wisdom/service/impl/RkStatisticsServiceImpl.java b/src/main/java/com/zg/project/wisdom/service/impl/RkStatisticsServiceImpl.java new file mode 100644 index 0000000..a15a6bc --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/service/impl/RkStatisticsServiceImpl.java @@ -0,0 +1,391 @@ +package com.zg.project.wisdom.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.zg.common.exception.ServiceException; +import com.zg.common.utils.DateUtils; +import com.zg.common.utils.StringUtils; +import com.zg.common.utils.poi.ExcelUtil; +import com.zg.project.wisdom.domain.vo.*; +import com.zg.project.wisdom.mapper.GysJhMapper; +import com.zg.project.wisdom.mapper.RkStatisticsMapper; +import com.zg.project.wisdom.service.RkStatisticsService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.*; + +@Service +public class RkStatisticsServiceImpl implements RkStatisticsService { + + @Resource + private RkStatisticsMapper rkStatisticsMapper; + + + @Override + public List> getAgeStatsAsList() { + // 这里的 gt10/gt20/gt30 均为“项目数”(COUNT DISTINCT xm_no) + RkAgeStatVO vo = rkStatisticsMapper.selectAgeStats(); + + List> list = new ArrayList<>(3); + list.add(Map.of("name", ">10天", "value", vo.getGt10())); + list.add(Map.of("name", ">20天", "value", vo.getGt20())); + list.add(Map.of("name", ">30天", "value", vo.getGt30())); + return list; + } + /** + * 库存类型统计(仅未出库) + */ + @Override + public List getTypeSummary() { + List list = rkStatisticsMapper.selectTypeSummary(); + return CollectionUtils.isEmpty(list) ? Collections.emptyList() : list; + } + + /** + * 县局统计(仅未出库) + */ + @Override + public List getCountySummary() { + List list = rkStatisticsMapper.selectCountySummary(); + return CollectionUtils.isEmpty(list) ? Collections.emptyList() : list; + } + + private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + @Override + public Map weekDaily(String start, String end) { + // 如果携带了 start 和 end,就直接用;否则:end=今天,start=本周周一 + if (StringUtils.isBlank(start) || StringUtils.isBlank(end)) { + LocalDate today = LocalDate.now(); + LocalDate monday = today.with(DayOfWeek.MONDAY); + start = monday.format(F); + end = today.format(F); + } + + Map ret = new HashMap<>(2); + ret.put("in", rkStatisticsMapper.selectWeekDailyIn(start, end)); + ret.put("out", rkStatisticsMapper.selectWeekDailyOut(start, end)); + return ret; + } + + + @Override + public Age3060ResultVO getAge3060Detail() { + // 1) 汇总(同时返回 >30 / >60 的项目数、条目数、总金额) + AgeSummaryVO summary = rkStatisticsMapper.selectAge3060Summary(); + + // 2) 明细(>30) + List gt30List = rkStatisticsMapper.selectAgeDetails(30); + + // 3) 明细(>60) + List gt60List = rkStatisticsMapper.selectAgeDetails(60); + + // 4) 组装返回 + Age3060ResultVO vo = new Age3060ResultVO(); + vo.setGt30(summary.getGt30()); + vo.setGt60(summary.getGt60()); + vo.setGt30List(gt30List); + vo.setGt60List(gt60List); + return vo; + } + + @Override + public List> getAge3060() { + // 1) 汇总(同时返回 >30 / >60 的项目数、条目数、总金额) + AgeSummaryVO summary = rkStatisticsMapper.selectAge3060Summary(); + + // 2) 用 List 封装成数组结构 + List> list = new ArrayList<>(); + + Map gt30 = new HashMap<>(); + gt30.put("name", ">30天"); + gt30.put("value", summary.getGt30().getGoodsCount()); // 这里取“货物数量”作 value + list.add(gt30); + + Map gt60 = new HashMap<>(); + gt60.put("name", ">60天"); + gt60.put("value", summary.getGt60().getGoodsCount()); + list.add(gt60); + + return list; + } + + + /** + * 导出库龄明细(>30天 与 >60天 合并导出为一张表) + * - 所有业务逻辑均在 Service 层完成 + * - 使用若依 ExcelUtil + @Excel 注解导出 + */ + @Override + public void exportAge3060(HttpServletResponse response) { + // 1) 调用已有统计逻辑 + Age3060ResultVO res = this.getAge3060Detail(); + + // 2) 合并两组明细,加上导出用的分组字段 + List out = new ArrayList<>(); + if (res.getGt30List() != null) { + for (RkAgeDetailVO row : res.getGt30List()) { + normalizeRowForExport(row, ">30天"); + out.add(row); + } + } + if (res.getGt60List() != null) { + for (RkAgeDetailVO row : res.getGt60List()) { + normalizeRowForExport(row, ">60天"); + out.add(row); + } + } + + // 3) 导出 Excel + ExcelUtil util = new ExcelUtil<>(RkAgeDetailVO.class); + util.exportExcel(response, out, "库龄明细"); + } + + private void normalizeRowForExport(RkAgeDetailVO row, String bucket) { + row.setAgeBucket(bucket); + if (row.getAmount() == null) { + BigDecimal dj = row.getHtDj() == null ? BigDecimal.ZERO : row.getHtDj(); + BigDecimal qty = row.getRealQty() == null ? BigDecimal.ZERO : row.getRealQty(); + row.setAmount(dj.multiply(qty)); + } + } + + /** + * 获取入库类型饼状图数据 + */ + @Override + public List getTypePie() { + return rkStatisticsMapper.selectTypePie(); + } + + @Override + public List getTypeSummaryByRange(String start, String end) { + // 基础入参校验(避免全表扫描/人为误操作) + if (StringUtils.isBlank(start) || StringUtils.isBlank(end)) { + throw new ServiceException("start/end 不能为空,建议格式:yyyy-MM-dd"); + } + return rkStatisticsMapper.selectTypeSummaryByRange(start, end); + } + + @Override + public List getOutTypeSummaryByRange(String start, String end) { + if (StringUtils.isBlank(start) || StringUtils.isBlank(end)) { + throw new ServiceException("start/end 不能为空,建议格式:yyyy-MM-dd"); + } + return rkStatisticsMapper.selectOutTypeSummaryByRange(start, end); + } + + private static final DateTimeFormatter DF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + @Override + public RkMonthInOutSummaryVO getThisMonthInOutSummary() { + // 本月起止: [YYYY-MM-01 00:00:00, 下月01 00:00:00) + LocalDate firstDay = LocalDate.now().withDayOfMonth(1); + LocalDate nextMonthFirst = firstDay.plusMonths(1); + + String start = firstDay.atStartOfDay().format(DF); + String end = nextMonthFirst.atStartOfDay().format(DF); + + RkMonthSummaryVO inSummary = rkStatisticsMapper.selectMonthlyInSummary(start, end); + RkMonthSummaryVO outSummary = rkStatisticsMapper.selectMonthlyOutSummary(start, end); + + // null 兜底 + if (inSummary == null) { + inSummary = new RkMonthSummaryVO(); + } + if (outSummary == null) { + outSummary = new RkMonthSummaryVO(); + } + + RkMonthInOutSummaryVO vo = new RkMonthInOutSummaryVO(); + vo.setInSummary(inSummary); + vo.setOutSummary(outSummary); + return vo; + } + + + @Override + public GysJhUndeliveredVO getUndeliveredSummary() { + // 分别查询:未到(status=0)、部分未到(status=2) + GysJhUndeliveredSummaryVO undelivered = rkStatisticsMapper.selectUndelivered(); + GysJhUndeliveredSummaryVO partial = rkStatisticsMapper.selectPartialUndelivered(); + + // 对象兜底 + if (undelivered == null) undelivered = new GysJhUndeliveredSummaryVO(); + if (partial == null) partial = new GysJhUndeliveredSummaryVO(); + + // 字段兜底(防止仍有 null 透出) + if (undelivered.getProjectCount() == null) undelivered.setProjectCount(0); + if (undelivered.getItemCount() == null) undelivered.setItemCount(0); + if (undelivered.getTotalQty() == null) undelivered.setTotalQty(0L); + if (undelivered.getAmountPlan() == null) undelivered.setAmountPlan(BigDecimal.ZERO); + + if (partial.getProjectCount() == null) partial.setProjectCount(0); + if (partial.getItemCount() == null) partial.setItemCount(0); + if (partial.getTotalQty() == null) partial.setTotalQty(0L); + if (partial.getAmountPlan() == null) partial.setAmountPlan(BigDecimal.ZERO); + + // 返回 + GysJhUndeliveredVO vo = new GysJhUndeliveredVO(); + vo.setUndelivered(undelivered); + vo.setPartialUndelivered(partial); + return vo; + } + + @Override + public List getCurrentStockByWlType() { + List list = rkStatisticsMapper.selectCurrentStockByWlType(); + return list == null ? Collections.emptyList() : list; + } + + @Override + public RkTypePieVO getAgeGt30Summary() { + RkTypePieVO vo = rkStatisticsMapper.selectAgeGt30Summary(); + if (vo == null) vo = new RkTypePieVO(); + + // 兜底,避免前端拿到 null + if (vo.getProjectCount() == null) vo.setProjectCount(0); + if (vo.getGoodsCountTotal() == null) vo.setGoodsCountTotal(0); + if (vo.getSumQty() == null) vo.setSumQty(BigDecimal.ZERO); + if (vo.getSumAmount() == null) vo.setSumAmount(BigDecimal.ZERO); + + // 本接口不设置 rkTypeCode / rkTypeName + vo.setRkTypeCode(null); + vo.setRkTypeName(null); + return vo; + } + + + private static final DateTimeFormatter TS_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter DAY_FMT = DateTimeFormatter.ofPattern("MM-dd"); + private static final DateTimeFormatter MON_FMT = DateTimeFormatter.ofPattern("yyyy-MM"); + + @Override + public List getIOBuckets(Integer range) { + if (range == null || (range != 0 && range != 2)) { + throw new IllegalArgumentException("range 仅支持 0(最近一周) 或 2(最近半年)"); + } + + Date now = DateUtils.getNowDate(); + LocalDateTime nowLdt = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault()); + + if (range == 0) { + // ===== 最近一周(按日),输出 MM-dd ===== + LocalDate endDay = nowLdt.toLocalDate(); + LocalDate startDay = endDay.minusDays(6); + LocalDateTime startTs = startDay.atStartOfDay(); + LocalDateTime endTs = endDay.plusDays(1).atStartOfDay(); + + List inRows = rkStatisticsMapper.selectInRange(startTs.format(TS_FMT), endTs.format(TS_FMT)); + List outRows = rkStatisticsMapper.selectOutRange(startTs.format(TS_FMT), endTs.format(TS_FMT)); + + // 改为以 LocalDate 作为桶键,防止键格式与输出格式不一致导致匹配失败 + LinkedHashMap acc = new LinkedHashMap<>(); + for (int i = 0; i < 7; i++) { + LocalDate d = startDay.plusDays(i); + acc.put(d, new BucketAcc()); + } + + // 入库聚合(按 LocalDate) + for (RkInVO r : inRows) { + if (r.getRkTime() == null) continue; + LocalDate d = r.getRkTime().toLocalDate(); + BucketAcc b = acc.get(d); + if (b == null) continue; + b.inItemCount++; + if (StringUtils.isNotBlank(r.getXmNo())) { + b.inProjects.add(r.getXmNo().trim()); + } + } + + // 出库聚合(按 LocalDate;ly_time 优先,否则 update_time) + for (RkOutVO r : outRows) { + LocalDateTime t = (r.getLyTime() != null) ? r.getLyTime() : r.getUpdateTime(); + if (t == null) continue; + LocalDate d = t.toLocalDate(); + BucketAcc b = acc.get(d); + if (b == null) continue; + b.outItemCount++; + String proj = StringUtils.isNotBlank(r.getXmNoCk()) ? r.getXmNoCk().trim() : r.getXmNo(); + if (StringUtils.isNotBlank(proj)) b.outProjects.add(proj); + } + + ArrayList out = new ArrayList<>(7); + for (Map.Entry e : acc.entrySet()) { + IOBucketVO row = new IOBucketVO(); + row.setBucket(e.getKey().format(DAY_FMT)); // 仅返回 MM-dd + row.setInProjectCount(e.getValue().inProjects.size()); + row.setInItemCount(e.getValue().inItemCount); + row.setOutProjectCount(e.getValue().outProjects.size()); + row.setOutItemCount(e.getValue().outItemCount); + out.add(row); + } + return out; + + } else { + // ===== 最近半年(按月),输出 yyyy-MM ===== + YearMonth endYm = YearMonth.from(nowLdt.toLocalDate()); + YearMonth startYm = endYm.minusMonths(5); + LocalDateTime startTs = startYm.atDay(1).atStartOfDay(); + LocalDateTime endTs = endYm.plusMonths(1).atDay(1).atStartOfDay(); + + List inRows = rkStatisticsMapper.selectInRange(startTs.format(TS_FMT), endTs.format(TS_FMT)); + List outRows = rkStatisticsMapper.selectOutRange(startTs.format(TS_FMT), endTs.format(TS_FMT)); + + LinkedHashMap acc = new LinkedHashMap<>(); + for (int i = 0; i < 6; i++) { + YearMonth ym = startYm.plusMonths(i); + acc.put(ym, new BucketAcc()); + } + + // 入库按月 + for (RkInVO r : inRows) { + if (r.getRkTime() == null) continue; + YearMonth ym = YearMonth.from(r.getRkTime().toLocalDate()); + BucketAcc b = acc.get(ym); + if (b == null) continue; + b.inItemCount++; + if (StringUtils.isNotBlank(r.getXmNo())) { + b.inProjects.add(r.getXmNo().trim()); + } + } + + // 出库按月 + for (RkOutVO r : outRows) { + LocalDateTime t = (r.getLyTime() != null) ? r.getLyTime() : r.getUpdateTime(); + if (t == null) continue; + YearMonth ym = YearMonth.from(t.toLocalDate()); + BucketAcc b = acc.get(ym); + if (b == null) continue; + b.outItemCount++; + String proj = StringUtils.isNotBlank(r.getXmNoCk()) ? r.getXmNoCk().trim() : r.getXmNo(); + if (StringUtils.isNotBlank(proj)) b.outProjects.add(proj); + } + + ArrayList out = new ArrayList<>(6); + for (Map.Entry e : acc.entrySet()) { + IOBucketVO row = new IOBucketVO(); + row.setBucket(e.getKey().format(MON_FMT)); // yyyy-MM + row.setInProjectCount(e.getValue().inProjects.size()); + row.setInItemCount(e.getValue().inItemCount); + row.setOutProjectCount(e.getValue().outProjects.size()); + row.setOutItemCount(e.getValue().outItemCount); + out.add(row); + } + return out; + } + } + + /** 桶内累加器 */ + private static class BucketAcc { + int inItemCount = 0; + int outItemCount = 0; + Set inProjects = new HashSet<>(); + Set outProjects = new HashSet<>(); + } +} diff --git a/src/main/java/com/zg/project/wisdom/service/impl/WarehouseStatServiceImpl.java b/src/main/java/com/zg/project/wisdom/service/impl/WarehouseStatServiceImpl.java new file mode 100644 index 0000000..f0ec248 --- /dev/null +++ b/src/main/java/com/zg/project/wisdom/service/impl/WarehouseStatServiceImpl.java @@ -0,0 +1,80 @@ +package com.zg.project.wisdom.service.impl; + +import com.zg.project.wisdom.domain.vo.SceneUsageVO; +import com.zg.project.wisdom.domain.vo.WarehouseSceneStatsResp; +import com.zg.project.wisdom.domain.vo.WarehouseStatVO; +import com.zg.project.wisdom.mapper.WarehouseStatMapper; +import com.zg.project.wisdom.service.WarehouseStatService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; + +@Service +public class WarehouseStatServiceImpl implements WarehouseStatService { + + @Resource + private WarehouseStatMapper warehouseStatMapper; + + @Override + public WarehouseSceneStatsResp getWarehouseSceneStats() { + // 1) 启用仓库列表 & 总数 + List warehouses = warehouseStatMapper.selectEnabledWarehouses(); + int totalWarehouses = warehouses == null ? 0 : warehouses.size(); + + // 2) 逐仓库查询场景统计并计算仓库级汇总与百分比 + if (warehouses != null) { + for (WarehouseStatVO wh : warehouses) { + String code = wh.getWarehouseCode(); + + // 场景列表(每个场景含总库位、已使用) + List scenes = warehouseStatMapper.selectSceneUsageByWarehouse(code); + if (scenes == null) scenes = new ArrayList<>(); + + // 补充每个场景的空闲与百分比 + int whTotal = 0, whUsed = 0, whFree = 0; + for (SceneUsageVO s : scenes) { + int t = nvl(s.getTotalPositions()); + int u = nvl(s.getUsedPositions()); + int f = Math.max(t - u, 0); + + s.setFreePositions(f); + s.setUsageRatePercent(pct(u, t)); + s.setFreeRatePercent(pct(f, t)); + + whTotal += t; + whUsed += u; + whFree += f; + } + + // 仓库级汇总 + wh.setScenes(scenes); + wh.setSceneCount(scenes.size()); + wh.setTotalPositions(whTotal); + wh.setUsedPositions(whUsed); + wh.setFreePositions(whFree); + wh.setUsageRatePercent(pct(whUsed, whTotal)); + wh.setFreeRatePercent(pct(whFree, whTotal)); + } + } + + // 3) 组装响应 + WarehouseSceneStatsResp resp = new WarehouseSceneStatsResp(); + resp.setTotalWarehouses(totalWarehouses); + resp.setItems(warehouses == null ? new ArrayList<>() : warehouses); + return resp; + } + + private static int nvl(Integer v) { return v == null ? 0 : v; } + + private static String pct(int part, int total) { + if (total <= 0 || part <= 0) return "0.00"; + return new BigDecimal(part) + .multiply(new BigDecimal("100")) + .divide(new BigDecimal(total), 2, RoundingMode.HALF_UP) + .toPlainString(); + } +} diff --git a/src/main/resources/mybatis/wisdom/RkStatisticsMapper.xml b/src/main/resources/mybatis/wisdom/RkStatisticsMapper.xml new file mode 100644 index 0000000..d90cd13 --- /dev/null +++ b/src/main/resources/mybatis/wisdom/RkStatisticsMapper.xml @@ -0,0 +1,487 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mybatis/wisdom/WarehouseStatMapper.xml b/src/main/resources/mybatis/wisdom/WarehouseStatMapper.xml new file mode 100644 index 0000000..634f237 --- /dev/null +++ b/src/main/resources/mybatis/wisdom/WarehouseStatMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +