首页统计接口以及导入表格中数据格式错误的问题
This commit is contained in:
@@ -1122,6 +1122,7 @@ public class ExcelUtil<T>
|
||||
/**
|
||||
* 添加单元格
|
||||
*/
|
||||
private final Map<String, CellStyle> bdStyleCache = new HashMap<>();
|
||||
public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
|
||||
{
|
||||
Cell cell = null;
|
||||
@@ -1152,6 +1153,8 @@ public class ExcelUtil<T>
|
||||
// =========================
|
||||
// ✅ BigDecimal:按“实际几位显示几位”,去掉末尾0(核心修改点)
|
||||
// =========================
|
||||
// 类里加一个缓存(避免重复创建 style)
|
||||
|
||||
if (value instanceof BigDecimal)
|
||||
{
|
||||
BigDecimal bd = (BigDecimal) value;
|
||||
@@ -1161,16 +1164,51 @@ public class ExcelUtil<T>
|
||||
bd = bd.setScale(attr.scale(), attr.roundingMode());
|
||||
}
|
||||
|
||||
// 去掉末尾0:1.000000 -> 1,1.2300 -> 1.23
|
||||
// 有后缀只能当文本
|
||||
if (StringUtils.isNotEmpty(attr.suffix()))
|
||||
{
|
||||
String text = bd.stripTrailingZeros().toPlainString();
|
||||
|
||||
// 强制按字符串写入,避免 Excel 数值格式导致显示成固定小数位
|
||||
cell.setCellType(CellType.STRING);
|
||||
cell.setCellValue(StringUtils.isEmpty(text) ? attr.defaultValue() : text + attr.suffix());
|
||||
|
||||
// 统计
|
||||
addStatisticsData(column, text, attr);
|
||||
return cell;
|
||||
}
|
||||
|
||||
BigDecimal stripped = bd.stripTrailingZeros();
|
||||
String text = stripped.toPlainString();
|
||||
|
||||
// ✅ 判断是否整数:strip 后 scale <= 0 就是整数
|
||||
boolean isInt = stripped.scale() <= 0;
|
||||
|
||||
// ✅ 写入数值:整数写 long,小数写 double(避免显示 1.)
|
||||
if (isInt)
|
||||
{
|
||||
try {
|
||||
cell.setCellValue(stripped.longValueExact());
|
||||
} catch (ArithmeticException ex) {
|
||||
// 超大整数兜底
|
||||
cell.setCellValue(stripped.doubleValue());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.setCellValue(stripped.doubleValue());
|
||||
}
|
||||
|
||||
// ✅ clone + cache 样式,整数用 0,小数用 0.############
|
||||
String fmt = isInt ? "0" : "0.############";
|
||||
String styleKey = "bd_" + fmt + "_" + attr.align() + "_" + attr.color() + "_" + attr.backgroundColor() + "_" + attr.wrapText();
|
||||
|
||||
CellStyle numStyle = bdStyleCache.get(styleKey);
|
||||
if (numStyle == null)
|
||||
{
|
||||
numStyle = wb.createCellStyle();
|
||||
numStyle.cloneStyleFrom(cell.getCellStyle());
|
||||
numStyle.setDataFormat(wb.createDataFormat().getFormat(fmt));
|
||||
bdStyleCache.put(styleKey, numStyle);
|
||||
}
|
||||
cell.setCellStyle(numStyle);
|
||||
|
||||
addStatisticsData(column, text, attr);
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
package com.zg.project.wisdom.controller;
|
||||
|
||||
import com.zg.common.utils.poi.ExcelUtil;
|
||||
import com.zg.framework.web.domain.AjaxResult;
|
||||
import com.zg.project.wisdom.domain.dto.HomeStatQueryDTO;
|
||||
import com.zg.project.wisdom.domain.vo.StockAgeExportVO;
|
||||
import com.zg.project.wisdom.domain.vo.StockAgeStatVO;
|
||||
import com.zg.project.wisdom.domain.vo.TodoStatVO;
|
||||
import com.zg.project.wisdom.domain.vo.WarehouseSlotStatVO;
|
||||
import com.zg.project.wisdom.service.RkStatisticsService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
@Api(tags = "库存统计")
|
||||
@RestController
|
||||
@RequestMapping("/stat")
|
||||
@@ -25,5 +33,39 @@ public class RkStatisticsController {
|
||||
public AjaxResult home(@RequestBody(required = false) HomeStatQueryDTO query) {
|
||||
return AjaxResult.success(rkStatisticsService.getHomeStatistics(query));
|
||||
}
|
||||
/**
|
||||
* 首页统计,库龄统计
|
||||
* */
|
||||
|
||||
@GetMapping("/stockAge/stat")
|
||||
public AjaxResult stockAgeStat() {
|
||||
List<StockAgeStatVO> stockAgeStatVOList = rkStatisticsService.selectStockAgeStat();
|
||||
return AjaxResult.success(stockAgeStatVOList);
|
||||
}
|
||||
/**
|
||||
* 首页统计,待办事项
|
||||
* */
|
||||
@GetMapping("/todo")
|
||||
public AjaxResult todoStat() {
|
||||
List<TodoStatVO> list = rkStatisticsService.selectTodoStat();
|
||||
return AjaxResult.success(list);
|
||||
}
|
||||
/**
|
||||
* 小仓库位使用情况统计(总库位/已使用/未使用)
|
||||
*/
|
||||
@GetMapping("/warehouse/slot")
|
||||
public AjaxResult warehouseSlotStat() {
|
||||
List<WarehouseSlotStatVO> list = rkStatisticsService.selectWarehouseSlotStat();
|
||||
return AjaxResult.success(list);
|
||||
}
|
||||
/**
|
||||
* 库龄>=30天明细导出(分组:>30天 / >60天)
|
||||
*/
|
||||
@ApiOperation("库龄>=30天明细导出")
|
||||
@PostMapping("/stockAge/export30")
|
||||
public void exportStockAge30(HttpServletResponse response) {
|
||||
List<StockAgeExportVO> list = rkStatisticsService.selectStockAgeExport30();
|
||||
ExcelUtil<StockAgeExportVO> util = new ExcelUtil<>(StockAgeExportVO.class);
|
||||
util.exportExcel(response, list, "库龄>=30天明细");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.zg.project.wisdom.domain.vo;
|
||||
|
||||
import com.zg.framework.aspectj.lang.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
public class StockAgeExportVO {
|
||||
|
||||
@Excel(name = "库龄分组")
|
||||
private String stockAgeGroup;
|
||||
|
||||
@Excel(name = "ID")
|
||||
private Long id;
|
||||
|
||||
@Excel(name = "项目号")
|
||||
private String projectNo;
|
||||
|
||||
@Excel(name = "项目描述")
|
||||
private String projectDesc;
|
||||
|
||||
@Excel(name = "入库时间", dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inTime;
|
||||
|
||||
@Excel(name = "库龄(天)")
|
||||
private Integer stockAgeDays;
|
||||
|
||||
|
||||
@Excel(name = "合同单价", cellType = Excel.ColumnType.NUMERIC, scale = 3, roundingMode = BigDecimal.ROUND_HALF_UP)
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
@Excel(name = "数量", cellType = Excel.ColumnType.NUMERIC, scale = 3, roundingMode = BigDecimal.ROUND_HALF_UP)
|
||||
private BigDecimal qty;
|
||||
|
||||
@Excel(name = "金额", cellType = Excel.ColumnType.NUMERIC, scale = 3, roundingMode = BigDecimal.ROUND_HALF_UP)
|
||||
private BigDecimal lineAmount;
|
||||
|
||||
@Excel(name = "库位")
|
||||
private String slotCode;
|
||||
|
||||
@Excel(name = "托盘")
|
||||
private String trayCode;
|
||||
|
||||
@Excel(name = "单据号")
|
||||
private String billNo;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.zg.project.wisdom.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class StockAgeStatVO {
|
||||
private String name;
|
||||
private Integer value;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.zg.project.wisdom.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@Data
|
||||
public class TodoStatVO {
|
||||
|
||||
/** 类型:应到未到[部分未到] / 应到未到[全部未到] / 应出未出[全部未出] */
|
||||
private String type;
|
||||
|
||||
/** 项目号(xm_no 去重) */
|
||||
private Integer projectCnt;
|
||||
|
||||
/** 总数量(保留 3 位小数) */
|
||||
private BigDecimal totalQty;
|
||||
|
||||
/** 总金额(保留 3 位小数) */
|
||||
private BigDecimal totalAmt;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.zg.project.wisdom.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class WarehouseSlotStatVO {
|
||||
/** 小仓编码(pcde_detail.warehouse_code) */
|
||||
private String cangkuCode;
|
||||
|
||||
/** 小仓名称(pcde_detail.warehouse_name) */
|
||||
private String cangkuName;
|
||||
|
||||
/** 总库位 */
|
||||
private Integer totalSlot;
|
||||
|
||||
/** 已使用库位 */
|
||||
private Integer usedSlot;
|
||||
|
||||
/** 未使用库位 */
|
||||
private Integer unusedSlot;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.zg.project.wisdom.mapper;
|
||||
|
||||
import com.zg.project.wisdom.domain.vo.HomeKpiVO;
|
||||
import com.zg.project.wisdom.domain.vo.StockStatisticGroupVO;
|
||||
import com.zg.project.wisdom.domain.vo.*;
|
||||
import io.lettuce.core.dynamic.annotation.Param;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@@ -32,4 +31,23 @@ public interface RkStatisticsMapper {
|
||||
|
||||
/** 首页 KPI */
|
||||
HomeKpiVO statHomeKpi();
|
||||
/**
|
||||
* 首页库龄统计接口
|
||||
* */
|
||||
List<StockAgeStatVO> selectStockAgeStat();
|
||||
/**
|
||||
* 首页待办事项接口
|
||||
* */
|
||||
List<TodoStatVO> selectTodoStat();
|
||||
|
||||
|
||||
/**
|
||||
* 小仓库位使用情况统计(总库位/已使用/未使用)
|
||||
*/
|
||||
List<WarehouseSlotStatVO> selectWarehouseSlotStat();
|
||||
/**
|
||||
* 库龄>=30天明细(用于导出)
|
||||
*/
|
||||
List<StockAgeExportVO> selectStockAgeExport30();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.zg.project.wisdom.service;
|
||||
|
||||
import com.zg.project.wisdom.domain.dto.HomeStatQueryDTO;
|
||||
import com.zg.project.wisdom.domain.vo.HomeStatVO;
|
||||
import com.zg.project.wisdom.domain.vo.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** 首页统计服务 */
|
||||
public interface RkStatisticsService {
|
||||
@@ -12,4 +14,21 @@ public interface RkStatisticsService {
|
||||
*/
|
||||
HomeStatVO getHomeStatistics(HomeStatQueryDTO query);
|
||||
|
||||
/**
|
||||
* 首页库龄统计接口
|
||||
* */
|
||||
List<StockAgeStatVO> selectStockAgeStat();
|
||||
|
||||
/**
|
||||
* 首页待办事项接口
|
||||
* */
|
||||
List<TodoStatVO> selectTodoStat();
|
||||
/**
|
||||
* 小仓库位使用情况统计
|
||||
*/
|
||||
List<WarehouseSlotStatVO> selectWarehouseSlotStat();
|
||||
/**
|
||||
* 库龄>=30天明细(用于导出)
|
||||
*/
|
||||
List<StockAgeExportVO> selectStockAgeExport30();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package com.zg.project.wisdom.service.impl;
|
||||
|
||||
import com.zg.project.wisdom.domain.dto.HomeStatQueryDTO;
|
||||
import com.zg.project.wisdom.domain.vo.HomeStatVO;
|
||||
import com.zg.project.wisdom.domain.vo.*;
|
||||
import com.zg.project.wisdom.mapper.RkStatisticsMapper;
|
||||
import com.zg.project.wisdom.service.RkStatisticsService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class RkStatisticsServiceImpl implements RkStatisticsService {
|
||||
@@ -36,5 +37,33 @@ public class RkStatisticsServiceImpl implements RkStatisticsService {
|
||||
|
||||
return vo;
|
||||
}
|
||||
/**
|
||||
* 首页库龄统计接口
|
||||
* */
|
||||
@Override
|
||||
public List<StockAgeStatVO> selectStockAgeStat() {
|
||||
return rkStatisticsMapper.selectStockAgeStat();
|
||||
}
|
||||
/**
|
||||
* 首页待办事项接口
|
||||
* */
|
||||
@Override
|
||||
public List<TodoStatVO> selectTodoStat() {
|
||||
return rkStatisticsMapper.selectTodoStat();
|
||||
}
|
||||
/**
|
||||
* 首页统计仓库使用情况
|
||||
* */
|
||||
@Override
|
||||
public List<WarehouseSlotStatVO> selectWarehouseSlotStat() {
|
||||
return rkStatisticsMapper.selectWarehouseSlotStat();
|
||||
}
|
||||
|
||||
/**
|
||||
* 库龄>=30天明细(用于导出)
|
||||
*/
|
||||
@Override
|
||||
public List<StockAgeExportVO> selectStockAgeExport30() {
|
||||
return rkStatisticsMapper.selectStockAgeExport30();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,184 @@
|
||||
WHERE ri.exec_status = 1
|
||||
AND ri.is_delete = 0
|
||||
</select>
|
||||
<select id="selectStockAgeStat" resultType="com.zg.project.wisdom.domain.vo.StockAgeStatVO">
|
||||
<![CDATA[
|
||||
SELECT '10-20天' AS name, COUNT(*) AS value
|
||||
FROM rk_info ri
|
||||
WHERE ri.is_delete = '0'
|
||||
AND ri.is_chuku = '0'
|
||||
AND ri.operation_time IS NOT NULL
|
||||
AND TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) > 10
|
||||
AND TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) <= 20
|
||||
|
||||
UNION ALL
|
||||
SELECT '20-30天' AS name, COUNT(*) AS value
|
||||
FROM rk_info ri
|
||||
WHERE ri.is_delete = '0'
|
||||
AND ri.is_chuku = '0'
|
||||
AND ri.operation_time IS NOT NULL
|
||||
AND TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) > 20
|
||||
AND TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) <= 30
|
||||
|
||||
UNION ALL
|
||||
SELECT '30天以上' AS name, COUNT(*) AS value
|
||||
FROM rk_info ri
|
||||
WHERE ri.is_delete = '0'
|
||||
AND ri.is_chuku = '0'
|
||||
AND ri.operation_time IS NOT NULL
|
||||
AND TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) > 30
|
||||
|
||||
UNION ALL
|
||||
SELECT '30-60天' AS name, COUNT(*) AS value
|
||||
FROM rk_info ri
|
||||
WHERE ri.is_delete = '0'
|
||||
AND ri.is_chuku = '0'
|
||||
AND ri.operation_time IS NOT NULL
|
||||
AND TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) > 30
|
||||
AND TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) <= 60
|
||||
UNION ALL
|
||||
SELECT '60天以上' AS name, COUNT(*) AS value
|
||||
FROM rk_info ri
|
||||
WHERE ri.is_delete = '0'
|
||||
AND ri.is_chuku = '0'
|
||||
AND ri.operation_time IS NOT NULL
|
||||
AND TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) > 60
|
||||
]]>
|
||||
</select>
|
||||
<select id="selectTodoStat" resultType="com.zg.project.wisdom.domain.vo.TodoStatVO">
|
||||
<![CDATA[
|
||||
SELECT
|
||||
'应到未到[部分未到]' AS type,
|
||||
COUNT(DISTINCT g.xm_no) AS projectCnt,
|
||||
ROUND(
|
||||
COALESCE(
|
||||
SUM(GREATEST(g.jh_qty - g.real_qty, 0)),
|
||||
0
|
||||
),
|
||||
3
|
||||
) AS totalQty,
|
||||
ROUND(
|
||||
COALESCE(
|
||||
SUM(GREATEST(g.jh_qty - g.real_qty, 0) * g.ht_dj),
|
||||
0
|
||||
),
|
||||
3
|
||||
) AS totalAmt
|
||||
FROM gys_jh g
|
||||
WHERE g.is_delete = '0'
|
||||
AND g.status = '2'
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'应到未到[全部未到]' AS type,
|
||||
COUNT(DISTINCT g.xm_no) AS projectCnt,
|
||||
ROUND(
|
||||
COALESCE(
|
||||
SUM(g.jh_qty),
|
||||
0
|
||||
),
|
||||
3
|
||||
) AS totalQty,
|
||||
ROUND(
|
||||
COALESCE(
|
||||
SUM(g.jh_qty * g.ht_dj),
|
||||
0
|
||||
),
|
||||
3
|
||||
) AS totalAmt
|
||||
FROM gys_jh g
|
||||
WHERE g.is_delete = '0'
|
||||
AND g.status = '0'
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'应出未出[全部未出]' AS type,
|
||||
COUNT(DISTINCT g.xm_no) AS projectCnt,
|
||||
ROUND(
|
||||
COALESCE(
|
||||
SUM(g.real_qty),
|
||||
0
|
||||
),
|
||||
3
|
||||
) AS totalQty,
|
||||
ROUND(
|
||||
COALESCE(
|
||||
SUM(g.real_qty * g.ht_dj),
|
||||
0
|
||||
),
|
||||
3
|
||||
) AS totalAmt
|
||||
FROM gys_jh g
|
||||
WHERE g.is_delete = '0'
|
||||
AND g.status = '1';
|
||||
|
||||
]]>
|
||||
</select>
|
||||
<select id="selectWarehouseSlotStat"
|
||||
resultType="com.zg.project.wisdom.domain.vo.WarehouseSlotStatVO">
|
||||
<![CDATA[
|
||||
SELECT
|
||||
p.warehouse_code AS cangkuCode,
|
||||
p.warehouse_name AS cangkuName,
|
||||
|
||||
COUNT(DISTINCT p.pcode) AS totalSlot,
|
||||
|
||||
COUNT(DISTINCT CASE WHEN r.id IS NOT NULL THEN p.pcode END) AS usedSlot,
|
||||
|
||||
COUNT(DISTINCT p.pcode)
|
||||
- COUNT(DISTINCT CASE WHEN r.id IS NOT NULL THEN p.pcode END) AS unusedSlot
|
||||
|
||||
FROM pcde_detail p
|
||||
LEFT JOIN rk_info r
|
||||
ON r.pcode = p.pcode
|
||||
AND r.cangku = p.warehouse_code
|
||||
AND r.is_delete = '0'
|
||||
AND r.is_chuku = '0'
|
||||
|
||||
WHERE p.is_delete = '0'
|
||||
AND p.warehouse_name IS NOT NULL
|
||||
AND TRIM(p.warehouse_name) <> ''
|
||||
|
||||
GROUP BY p.warehouse_code, p.warehouse_name
|
||||
ORDER BY p.warehouse_code
|
||||
]]>
|
||||
</select>
|
||||
<select id="selectStockAgeExport30"
|
||||
resultType="com.zg.project.wisdom.domain.vo.StockAgeExportVO">
|
||||
<![CDATA[
|
||||
SELECT
|
||||
CASE
|
||||
WHEN TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) >= 60 THEN '>60天'
|
||||
ELSE '>30天'
|
||||
END AS stockAgeGroup,
|
||||
|
||||
ri.id AS id,
|
||||
ri.xm_no AS projectNo,
|
||||
ri.xm_ms AS projectDesc,
|
||||
ri.operation_time AS inTime,
|
||||
|
||||
TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) AS stockAgeDays,
|
||||
|
||||
ri.ht_dj AS unitPrice,
|
||||
ri.real_qty AS qty,
|
||||
ROUND(COALESCE(ri.ht_dj, 0) * COALESCE(ri.real_qty, 0), 3) AS lineAmount,
|
||||
|
||||
ri.pcode AS slotCode,
|
||||
ri.tray_code AS trayCode,
|
||||
ri.bill_no AS billNo
|
||||
|
||||
FROM rk_info ri
|
||||
WHERE ri.is_delete = '0'
|
||||
AND ri.is_chuku = '0'
|
||||
AND ri.operation_time IS NOT NULL
|
||||
AND TIMESTAMPDIFF(DAY, ri.operation_time, NOW()) >= 30
|
||||
|
||||
ORDER BY
|
||||
stockAgeGroup DESC,
|
||||
stockAgeDays DESC,
|
||||
ri.operation_time ASC
|
||||
]]>
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user