分享一个1000行代码的报表接口
1 controller
package cn.cncommdata.report.controller;
import cn.cncommdata.form.vo.BaseVO;
import cn.cncommdata.report.service.IWasteAnalysisService;
import cn.cncommdata.report.service.IXianFengScreenService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author meihongliang
* @since 2020/4/10 10:13 下午
* 险峰大屏展示需要接口
*/
@RestController
public class ScreenController {
/**
* 引入service 接口
*/
@Resource
private IWasteAnalysisService wasteAnalysisService;
/**
* 废品情况分析——hour/kg
* <p>
* 数据来源
* 工票信息:工票号、计量单位、车间完成工时
* 不合格品通知单:工票号、生产项目、废品原因分析(10大原因)、综合回用量、责任回用量
* 不合格品指标管理:废品率-指标、责任废品率-指标、综合回用率-指标、责任回用率-指标
* <p>
* 201测试数据
* tenantId//1189773091425882112
* grantId//1192291334245978112
* formIds//{"工票信息": 1237569872439939104,"不合格品通知单": 1237569872439939100,"不合格品指标管理": 1255764697836294144}
*
* 202测试数据
* tenantId//1189773091425882112
* grantId//1192291334245978112
* formIds//{"工票信息": 1237569872439939104,"不合格品通知单": 1237569872439939100,"不合格品指标管理": 1257869474644758528}
*
* @param tenantId 企业id
* @param grantId 操作员
* @param formIds 所需表单id的json对象字符串(内部研发项目、外部研发项目、生产技术准备、产品安装调试、进度计划维护)
* @return 查询结果
* @author leimin
*/
@ApiOperation("废品情况分析——工时/重量")
@PostMapping("/scm/screen/loss")
public BaseVO<List<Map<String, Object>>> getLossAnalysis(
@ApiParam(value = "租户Id", required = true) @RequestHeader("tenant_id") Long tenantId,
@ApiParam(value = "操作者Id", required = true) @RequestHeader("grant_id") Long grantId,
@ApiParam(value = "表单id的json对象字符串", required = true) @RequestParam("form_ids") String formIds) {
return wasteAnalysisService.getLossAnalysis(tenantId, grantId, formIds);
}
}
2 service
package cn.cncommdata.report.service;
import cn.cncommdata.form.vo.BaseVO;
import java.util.List;
import java.util.Map;
/**
* @author: leimin
* @description: 废品分析接口方法
* @date: 2020-04-28
*/
public interface IWasteAnalysisService {
/**
* 工时损失废品分析——小时(hour)
* <p>
* 数据来源
* 工票信息:工票号、计量单位、车间完成工时
* 不合格品通知单:工票号、生产项目、废品原因分析(10大原因)、综合回用量、责任回用量
* 各种指标:废品率-指标、责任废品率-指标、综合回用率-指标、责任回用率-指标
*
* @param tenantId 企业id
* @param grantId 操作员
* @param formIds 所需表单id的json对象字符串(内部研发项目、外部研发项目、生产技术准备、产品安装调试、进度计划维护)
* @return 查询结果
* @author leimin
*/
BaseVO<List<Map<String, Object>>> getLossAnalysis(Long tenantId, Long grantId, String formIds);
}
3 serviceImpl
package cn.cncommdata.report.service.impl;
import cn.cncommdata.form.vo.BaseVO;
import cn.cncommdata.form.vo.FormDataVO;
import cn.cncommdata.form.vo.fielddata.CommonDataVO;
import cn.cncommdata.form.vo.fielddata.DataBaseVO;
import cn.cncommdata.form.vo.fielddata.SelectDataVO;
import cn.cncommdata.form.vo.fielddata.TableDataVO;
import cn.cncommdata.form.vo.fielddata.TableRowVO;
import cn.cncommdata.report.service.IWasteAnalysisService;
import cn.cncommdata.report.util.ReportUtil;
import cn.cncommdata.report.util.TimeUtil;
import cn.cncommdata.report.util.constant.Const;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* @author: leimin
* @description: 废品分析实现方法
* @date: 2020-04-28
*/
@Slf4j
@Service
public class WasteAnalysisServiceImpl implements IWasteAnalysisService {
/**
* 引入表单工具
*/
@Resource
private ReportUtil reportUtil;
/**
* 工时损失废品分析——小时(hour)
* <p>
* 数据来源
* 工票信息:工票号、计量单位、车间完成工时
* 不合格品通知单:工票号、生产项目、废品原因分析(10大原因)、综合回用量、责任回用量
* 各种指标:废品率-指标、责任废品率-指标、综合回用率-指标、责任回用率-指标
*
* @param tenantId 企业id
* @param grantId 操作员
* @param formIds 所需表单id的json对象字符串(内部研发项目、外部研发项目、生产技术准备、产品安装调试、进度计划维护)
* @return 查询结果
* @author leimin
*/
@Override
public BaseVO<List<Map<String, Object>>> getLossAnalysis(Long tenantId, Long grantId, String formIds) {
// 查询所有,按年分,再按月分
// 1。表单有不合格品通知单、工票信息
// 2。查询所有的不合格品通知单、工票,塞选不匹配的数据(不合格品通知单.工票号 不为空)
// 3。按yyyy-mm-生产项目作为数据的键,然后获取可以直接获取到的数据,同键的数据累加;
// 4。通过计算规则计算其余的字段;
// 1。查询表单数据
List<Object> unqualifiedList, workTicketList, indexList;
JSONObject metadataIds = JSONObject.parseObject(formIds);
if (CollectionUtils.isEmpty(metadataIds)) {
return new BaseVO<>(BaseVO.ILLEGAL_PARAM_CODE, "参数错误!");
}
Long unqualifiedFormId = metadataIds.getLong(Const.NOTICE_OF_UNQUALIFIED_PRODUCT);
Long workTicketFormId = metadataIds.getLong(Const.WORK_ORDER);
if (unqualifiedFormId == null || workTicketFormId == null) {
return new BaseVO<>(BaseVO.ILLEGAL_PARAM_CODE, "参数错误!");
}
unqualifiedList = reportUtil.getDataList(tenantId, grantId, unqualifiedFormId);
if (CollectionUtils.isEmpty(unqualifiedList)) {
return new BaseVO<>(BaseVO.ILLEGAL_PARAM_CODE, "参数错误!");
}
workTicketList = reportUtil.getDataList(tenantId, grantId, workTicketFormId);
if (CollectionUtils.isEmpty(workTicketList)) {
return new BaseVO<>(BaseVO.ILLEGAL_PARAM_CODE, "参数错误!");
}
Long indexFormId = metadataIds.getLong(Const.INDEX_OF_UNQUALIFIED_PRODUCT);
if (indexFormId == null) {
new BaseVO<>(BaseVO.SUCCESS_CODE, "校验成功!");
}
indexList = reportUtil.getDataList(tenantId, grantId, indexFormId);
// 2。筛选数据
List<FormDataVO> defects = new ArrayList<>();
List<FormDataVO> workTickets = new ArrayList<>();
BaseVO baseVo = filterData(unqualifiedList, workTicketList, defects, workTickets);
if (BaseVO.SUCCESS_CODE != baseVo.getCode()) {
return baseVo;
}
// 组合为所需的数据结构
HashMap map = new HashMap(Const.SIXTEEN);
range(defects, workTickets, map);
// 3。按yyyy-mm-生产项目,作为数据的键,然后获取可以直接获取到的数据,同键的数据累加;
Map<String, Map<String, Object>> result = new HashMap<>(Const.SIXTEEN);
middleAnalysis(result, map);
// 4。计算最终的数据
lastAnalysis(result, indexList);
List<Map<String, Object>> list = sortMap(result);
return new BaseVO<>(BaseVO.SUCCESS_CODE, "校验成功!", list);
}
/**
* 按照给出的顺序排序
*
* @param result 排序前
* @return 排序后
*/
private List<Map<String, Object>> sortMap(Map<String, Map<String, Object>> result) {
// 1。找出所有的月分;set
// 2。然后可以先对所有的月份排序,重新封装
// 3。遍历月份,解析项目名称和该条数据,加入同类的list;
// 4。合并
Set<String> sortSet = new TreeSet<String>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);//降序排列
}
});
Set<String> intKeys = getYearMonth(result, "");
sortSet.addAll(intKeys);
List<Map<String, Object>> list = new ArrayList<>();
for (String key : sortSet) {
// 生成key
String yearMonth = key.substring(0, 4) + "-" + key.substring(4);
for (String project : Const.SORTED_LIST) {
String finalKey = yearMonth + "-" + project;
Map<String, Object> map = result.get(finalKey);
if (CollectionUtils.isEmpty(map)) {
continue;
}
map.put("key", finalKey);
list.add(map);
}
}
return list;
}
/**
* 计算最终的数据
* 1。合并行
* 2。注入单元格公式计算
* 3。注入指标
*
* @param middle 半成品数据
*/
private void lastAnalysis(Map<String, Map<String, Object>> middle, List<Object> indexList) {
if (CollectionUtils.isEmpty(middle)) {
return;
}
// 1。合并行
mergeRows(middle);
// 2。注入单元格公式计算
calculateByCell(middle);
// 3。注入指标
setStandard(middle, indexList);
}
/**
* 给数据中的每一行注入指标
*
* @param middle 数据
* @param indexList 指标对象
*/
private void setStandard(Map<String, Map<String, Object>> middle, List<Object> indexList) {
if (CollectionUtils.isEmpty(indexList)) {
return;
}
// 每年的指标必须对应
// 解析指标,每年,的指标
Map<String, Map<String, Object>> allIndex = setAllYearIndex(indexList);
// 遍历每一行:
// 获取每一行对应的指标
// 给每一行注入指标
for (Map.Entry<String, Map<String, Object>> entry : middle.entrySet()) {
Map<String, Object> row = entry.getValue();
if (StringUtils.isEmpty(row)) {
continue;
}
String indexKey = intelligenceMatch(row);
if (StringUtils.isEmpty(indexKey)) {
continue;
}
row.putAll(allIndex.get(indexKey));
}
}
/**
* 只能获取目标指标的key
*
* @param row 当前行数据
* @return 目标指标的key
*/
private String intelligenceMatch(Map<String, Object> row) {
//
String projectName = (String) row.get(Const.PROJECT);
String year = (String) row.get(Const.YEAR);
for (String indexName : Const.PROJECTS) {
if (indexName.substring(0, 2).equals(projectName.substring(0, 2))) {
return year + "-" + indexName;
}
}
return null;
}
/**
* 解析每一年的指标,采用健值对形式存储,key=年+行名称,value =每一行的指标
*
* @param indexList 所有的指标原属数据
* @return 解析后的指标,便于读取
*/
private Map<String, Map<String, Object>> setAllYearIndex(List<Object> indexList) {
Map<String, Map<String, Object>> allIndex = new HashMap<>(Const.SIXTEEN);
for (Object object : indexList) {
if (object == null) {
continue;
}
FormDataVO formDataVO = (FormDataVO) object;
String timestamp = reportUtil.getStringFromData(formDataVO, Const.YEAR_NUMBER);
String yearDate = TimeUtil.getStringYear(timestamp);
//遍历注入指标
for (String firstField : Const.PROJECTS) {
Map<String, Object> row = new HashMap<>(Const.SIXTEEN);
String rowKey = yearDate + "-" + firstField;
TableDataVO tableDataVO = (TableDataVO) formDataVO.getData().get(firstField);
TableRowVO tableRowVO = tableDataVO.getValue().get(0);
for (Map.Entry<String, DataBaseVO> entry : tableRowVO.getValue().entrySet()) {
CommonDataVO commonDataVO = (CommonDataVO) entry.getValue();
String value = commonDataVO.getValue();
row.put(entry.getKey(), stob(value));
}
allIndex.put(rowKey, row);
}
}
return allIndex;
}
/**
* 注入单元格公式计算,更具单元格之间的计算规则,计算未知的单元格的值
*
* @param middle 已知的单于格
*/
private void calculateByCell(Map<String, Map<String, Object>> middle) {
for (Map.Entry<String, Map<String, Object>> entry : middle.entrySet()) {
Map<String, Object> row = entry.getValue();
if (CollectionUtils.isEmpty(row)) {
continue;
}
String projectName = (String) row.get(Const.PROJECT);
boolean isWeight = isWeight(projectName);
// 1。责任废品量(21)
// 2。废品量(4)
// 3。实际工作量(5)
// 4。实际 废品率(7)
setResponseWasteAmount(isWeight, row);
setWasteAmount(isWeight, row);
setActualWorkAmount(row);
setDivideValue(row, Const.ACTUAL_WASTE_RATE, Const.WASTE_NUMBER, Const.ACTUAL_WORK_AMOUNT);
// 5。实际 责任废品率(23)
// 6。实际 综合回用率(26)
// 7。实际 责任回用率(29)
setDivideValue(row, Const.ACTUAL_RESPONSIBLE_WASTE_RATE, Const.RESPONSIBLE_WASTE_AMOUNT, Const.ACTUAL_WORK_AMOUNT);
setDivideValue(row, Const.ACTUAL_COMPREHENSIVE_REUSE_RATE, Const.COMPREHENSIVE_AMOUNT, Const.ACTUAL_WORK_AMOUNT);
setDivideValue(row, Const.ACTUAL_RESPONSIBLE_REUSE_RATE, Const.RESPONSIBLE_AMOUNT, Const.ACTUAL_WORK_AMOUNT);
}
}
/**
* 计算son/mom的结果,再放入row,target健对应的值
*
* @param row map
* @param target 健
* @param son 分子的健
* @param mom 分母的健
*/
private void setDivideValue(Map<String, Object> row, String target, String son, String mom) {
// 计算需要的子数据
// 计算主数据
// 封装主数据
BigDecimal sonValue = getRowValue(row, son);
BigDecimal momValue = getRowValue(row, mom);
if (BigDecimal.ZERO.equals(momValue)) {
if (BigDecimal.ZERO.equals(sonValue)) {
row.put(target, BigDecimal.ZERO);
} else {
row.put(target, BigDecimal.ONE);
}
} else {
row.put(target, sonValue.divide(momValue, 2, RoundingMode.UP));
}
}
/**
* 计算并封装实际工作量
*
* @param row 封装数据对象
*/
private void setActualWorkAmount(Map<String, Object> row) {
// 计算需要的子数据
// 计算主数据
// 封装主数据
BigDecimal workAmount = getRowValue(row, Const.WORKSHOP_TASK);
BigDecimal wasteAmount = getRowValue(row, Const.WASTE_NUMBER);
row.put(Const.ACTUAL_WORK_AMOUNT, workAmount.add(wasteAmount));
}
/**
* 获取健值对的值
*
* @param row map
* @param key 健
* @return 值
*/
private BigDecimal getRowValue(Map<String, Object> row, String key) {
Object o = row.get(key);
if (o == null) {
return BigDecimal.ZERO;
} else {
String valueString = o.toString();
if (StringUtils.isEmpty(valueString)) {
return BigDecimal.ZERO;
} else {
return new BigDecimal(valueString);
}
}
}
/**
* 计算并封装责任废品量
*
* @param isWeight 操作类型
* @param row 封装对象
*/
private void setWasteAmount(boolean isWeight, Map<String, Object> row) {
// 计算需要的子数据
// 计算主数据
// 封装主数据
BigDecimal wasteAmount;
if (isWeight) {
wasteAmount = getRowValue(row, Const.RESPONSIBLE_WASTE_AMOUNT);
} else {
BigDecimal badDesign = getRowValue(row, Const.POOR_DESIGNER);
BigDecimal poorSpecif = getRowValue(row, Const.POOR_SPECIFICATION);
BigDecimal poorFix = getRowValue(row, Const.POOR_FIXTURE);
BigDecimal poorHeatTreat = getRowValue(row, Const.POOR_HEAT_TREATMENT);
BigDecimal improperInspect = getRowValue(row, Const.IMPROPER_INSPECTION);
BigDecimal equipmentFail = getRowValue(row, Const.EQUIPMENT_FAILURE);
BigDecimal poorRaw = getRowValue(row, Const.POOR_RAW_MATERIAL);
BigDecimal poorForge = getRowValue(row, Const.POOR_FORGING_BLANK);
BigDecimal others = getRowValue(row, Const.OTHER_ELSE);
BigDecimal poorCast = getRowValue(row, Const.POOR_CASTING_BLANK);
BigDecimal responseAmount = getRowValue(row, Const.RESPONSIBLE_WASTE_AMOUNT);
wasteAmount = badDesign.add(poorSpecif).add(poorFix).add(poorHeatTreat).add(improperInspect).
add(equipmentFail).add(poorRaw).add(poorForge).add(others).add(poorCast).add(responseAmount);
}
row.put(Const.WASTE_NUMBER, wasteAmount);
}
/**
* 计算并封装责任废品量
*
* @param isWeight 操作类型
* @param row 封装对象
*/
private void setResponseWasteAmount(boolean isWeight, Map<String, Object> row) {
// 计算需要的子数据
// 计算主数据
// 封装主数据
BigDecimal responseWasteAmount;
if (isWeight) {
//内废/外废求和
BigDecimal in = getRowValue(row, Const.INTERNAL_WASTE);
BigDecimal out = getRowValue(row, Const.EXTERNAL_WASTE);
responseWasteAmount = in.add(out);
} else {
BigDecimal careless = getRowValue(row, Const.CARELESS);
BigDecimal orderViolate = getRowValue(row, Const.SPECIFICATION_VIOLATION);
BigDecimal poorLeader = getRowValue(row, Const.POOR_LEADER);
responseWasteAmount = careless.add(orderViolate).add(poorLeader);
}
row.put(Const.RESPONSIBLE_WASTE_AMOUNT, responseWasteAmount);
}
/**
* 获取年月
*
* @param middle 数据源
* @return 结果
*/
private Set<String> getYearMonth(Map<String, Map<String, Object>> middle, String tag) {
Set<String> yearMonth = new HashSet<>(Const.SIXTEEN);
if (CollectionUtils.isEmpty(middle)) {
return yearMonth;
}
for (Map.Entry<String, Map<String, Object>> entry : middle.entrySet()) {
if (CollectionUtils.isEmpty(entry.getValue())) {
continue;
}
Object yObj = entry.getValue().get(Const.YEAR);
Object mObj = entry.getValue().get(Const.MONTH);
if (yObj == null && mObj == null) {
continue;
}
String year = yObj == null ? "0000" : (String) yObj;
String month = mObj == null ? "00" : (String) mObj;
String key = "-".equals(tag) ? year + "-" + month : year + month;
yearMonth.add(key);
}
return yearMonth;
}
/**
* 合并 每个月的金一、金二
*
* @param middle 数据
*/
private void mergeRows(Map<String, Map<String, Object>> middle) {
// 1。先找出所有的月
// 2。找出每个月中的厂,及他们的数据
// 3。合并每个月对应的厂,重新封装
// 4。返回
Set<String> yearMonth = getYearMonth(middle, "-");
for (String key : yearMonth) {
// 生成key
String keyOne = key + "-" + Const.METAL_WORKSHOP_ONE;
String keyTwo = key + "-" + Const.METAL_WORKSHOP_TWO;
// 取出来
Map<String, Object> rowOne = middle.get(keyOne);
Map<String, Object> rowTwo = middle.get(keyTwo);
if (CollectionUtils.isEmpty(rowOne) && CollectionUtils.isEmpty(rowTwo)) {
continue;
}
// 组合
Map<String, Object> rowLast = mergeTwo(rowOne, rowTwo);
// 放回去
middle.put(key + "-" + Const.REJECT_RATE_OF_MECHINING, rowLast);
}
}
/**
* 合并两行数据
*
* @param rowOne 第一行
* @param rowTwo 第二行
* @return 合并后的数据
*/
private Map<String, Object> mergeTwo(Map<String, Object> rowOne, Map<String, Object> rowTwo) {
// 深度复制
if (CollectionUtils.isEmpty(rowOne) && !CollectionUtils.isEmpty(rowTwo)) {
return deepCopy(rowTwo);
}
if (!CollectionUtils.isEmpty(rowOne) && CollectionUtils.isEmpty(rowTwo)) {
return deepCopy(rowOne);
}
// 深度复制
Map<String, Object> rowLast = new HashMap<>(Const.SIXTEEN);
for (Map.Entry<String, Object> entry : rowOne.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (Const.ONCE_LIST.contains(key)) {
String valueString = value == null ? "" : value.toString();
rowLast.put(key, valueString);
} else {
BigDecimal valueOne;
if (value == null) {
valueOne = BigDecimal.ZERO;
} else {
valueOne = new BigDecimal(value.toString());
}
BigDecimal valueTwo;
if (rowTwo.get(key) == null) {
valueTwo = BigDecimal.ZERO;
} else {
valueTwo = new BigDecimal(rowTwo.get(key).toString());
}
if (valueOne.equals(BigDecimal.ZERO) && valueTwo.equals(BigDecimal.ZERO)) {
continue;
}
rowLast.put(key, valueOne.add(valueTwo));
}
}
rowLast.put(Const.PROJECT, Const.REJECT_RATE_OF_MECHINING);
return rowLast;
}
/**
* 深度复制map
*
* @param rowOne 原始的map
* @return 复制的对象
*/
private Map<String, Object> deepCopy(Map<String, Object> rowOne) {
//
Map<String, Object> rowNew = new HashMap<>(Const.SIXTEEN);
for (Map.Entry<String, Object> entry : rowOne.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (Const.ONCE_LIST.contains(key)) {
// new string
String valueString = value == null ? "" : value.toString();
rowNew.put(key, valueString);
} else {
// new BigDecimal
BigDecimal valueOne;
if (value == null) {
valueOne = BigDecimal.ZERO;
} else {
valueOne = new BigDecimal(value.toString());
}
rowNew.put(key, valueOne);
}
}
rowNew.put(Const.PROJECT, Const.REJECT_RATE_OF_MECHINING);
return rowNew;
}
/**
* 初步解析数据
* <p>
* 工时损失废品分析——小时(hour)
* <p>
* 数据来源
* 工票信息:工票号、计量单位、车间完成工时
* 不合格品通知单:工票号、生产项目、废品原因分析(10大原因)、综合回用量、责任回用量
* 各种指标:废品率-指标、责任废品率-指标、综合回用率-指标、责任回用率-指标
*
* @param middle 结果封装
* @param results 工票信息和不合格品数据
*/
private void middleAnalysis(Map<String, Map<String, Object>> middle, Map<String, Map<String, FormDataVO>> results) {
if (CollectionUtils.isEmpty(results)) {
return;
}
// 需要计算的数据
// 1。总工时=准备工时+加工工时*生产数量
// 2。综合回用量(h)=总工时/派工数量*车间总回用数量(四个回用数求和)-责任回用工时(不合格品通知单)
// 综合回用量(kg)=加工工时*车间总回用数量(截图包括的回用数)-责任回用重量
// 3。责任回用量(h)=责任回用工时
// 责任回用重量(kg)=责任回用数*加工工时
// 4。内废/外废(kg) = 报废数量*工票上加工工时
//封装数据类型
for (Map.Entry<String, Map<String, FormDataVO>> entry : results.entrySet()) {
FormDataVO defectData = entry.getValue().get(Const.NOTICE_OF_UNQUALIFIED_PRODUCT);
FormDataVO ticketData = entry.getValue().get(Const.WORK_ORDER);
String projectName = reportUtil.getStringFromData(defectData, Const.PROJECT);
if (StringUtils.isEmpty(projectName)) {
continue;
}
boolean isWeight = isWeight(projectName);
String timeStamp = reportUtil.getStringFromData(defectData, Const.CREATE_DATE);
String year = TimeUtil.getStringYear(timeStamp);
String month = TimeUtil.getStringMonth(timeStamp);
String finalKey = year + "-" + month + "-" + projectName;
Map<String, Object> map = middle.get(finalKey);
boolean isEmpty = false;
if (CollectionUtils.isEmpty(map)) {
isEmpty = true;
map = new HashMap<>(Const.SIXTEEN);
map.put(Const.YEAR, year);
map.put(Const.MONTH, month);
map.put(Const.PROJECT, projectName);
map.put(Const.UNIT, isWeight ? Const.KG : Const.HOUR);
}
// 1。总工时=准备工时+加工工时*派工数量
BigDecimal totalHour = countAndSetWorkHour(ticketData, isEmpty, map);
// 2。设置综合回用量
setComprehensiveAmount(defectData, ticketData, isEmpty, map, isWeight, totalHour);
// 3。责任回用工时;责任回用重量=“责任回用数”*加工工时
setResponseAmount(defectData, ticketData, isEmpty, map, isWeight);
// 4。
if (isWeight) {
//内废/外废(kg) = 报废数量*工票上加工工时
setWeight(defectData, ticketData, isEmpty, map);
} else {
// 十大原因
for (String fieldName : Const.HOUR_WASTE_CASE) {
setSecondDataIntoMap(isEmpty, map, fieldName, defectData);
}
}
middle.put(finalKey, map);
}
}
/**
* 计算并设置责任回用量
*
* @param defectData 不合格品数据
* @param ticketData 工票信息
* @param isEmpty 唯一主键是否有值,true 有值,增量操作,false全量操作
* @param map 数据封装对象
* @param isWeight 重量计算还是工时计算,true 重量
*/
private void setResponseAmount(FormDataVO defectData, FormDataVO ticketData, boolean isEmpty, Map<String, Object> map,
boolean isWeight) {
BigDecimal workDecimal = getBigDecimalFromData(ticketData, Const.MAN_HOUR);
BigDecimal responseAmount;
if (isWeight) {
responseAmount = getResponseReuseWeight(workDecimal, defectData);
} else {
responseAmount = getBigDecimalFromData(defectData, Const.RESPONSIBLE_REUSE_HOUR);
}
setMap(isEmpty, map, Const.RESPONSIBLE_AMOUNT, responseAmount);
}
/**
* 获取数字类型的值
*
* @param formDataVO 所有数据
* @param fieldName 字段名称
* @return 数字类型的值
*/
private BigDecimal getBigDecimalFromData(FormDataVO formDataVO, String fieldName) {
String responseReuseHour = reportUtil.getStringFromData(formDataVO, fieldName);
return stob(responseReuseHour);
}
/**
* 计算并设置综合回用量
*
* @param defectData 不合格品数据
* @param ticketData 工票信息
* @param isEmpty 唯一主键是否有值,true 有值,增量操作,false全量操作
* @param map 数据封装对象
* @param isWeight 重量计算还是工时计算,true 重量
* @param totalHour 当前工票的总工时
*/
private void setComprehensiveAmount(FormDataVO defectData, FormDataVO ticketData, boolean isEmpty, Map<String, Object> map,
boolean isWeight, BigDecimal totalHour) {
// 重量计算时:综合回用量=总工时/派工数量*车间总回用数量(四个回用数求和)-责任回用工时(不合格品通知单)
// 以公斤为单位时,综合回用量=加工工时*车间总回用数量(截图包括的回用数)-责任回用重量
BigDecimal workNum = getBigDecimalFromData(ticketData, Const.TASK_NUMBER);
BigDecimal directReuseNum = getBigDecimalFromData(defectData, Const.DIRECT_REUSE_NUMBER);
BigDecimal applyReuseNum = getBigDecimalFromData(defectData, Const.APPLY_REUSE_NUMBER);
BigDecimal partReuseNum = getBigDecimalFromData(defectData, Const.PART_REUSE_NUMBER);
BigDecimal repairReuseNum = getBigDecimalFromData(defectData, Const.REPAIR_REUSE_NUMBER);
BigDecimal totalReuseNumber = directReuseNum.add(applyReuseNum).add(partReuseNum).add(repairReuseNum);
BigDecimal comprehensiveAmount;
if (isWeight) {
BigDecimal responseNum = getBigDecimalFromData(defectData, Const.RESPONSIBLE_REUSE_HOUR);
comprehensiveAmount = totalHour.divide(workNum, 2, RoundingMode.HALF_UP).multiply(totalReuseNumber).subtract(responseNum);
} else {
BigDecimal workDecimal = getBigDecimalFromData(ticketData, Const.MAN_HOUR);
comprehensiveAmount = workDecimal.multiply(totalReuseNumber).
subtract(getResponseReuseWeight(workDecimal, defectData));
}
setMap(isEmpty, map, Const.COMPREHENSIVE_AMOUNT, comprehensiveAmount);
}
/**
* 获取责任回用重量
*
* @param workDecimal 加工工时
* @param defectData 不合格品通知单
* @return 责任回用重量
*/
private BigDecimal getResponseReuseWeight(BigDecimal workDecimal, FormDataVO defectData) {
BigDecimal responseNum = getBigDecimalFromData(defectData, Const.RESPONSIBLE_REUSE_NUMBER);
return workDecimal.multiply(responseNum);
}
/**
* 计算并新增总工时
*
* @param ticketData 工票数据
* @param isEmpty 之前是否有key相同的数据,true没有
* @param map 封装的数据对象
* @return 当前工票总工时
*/
private BigDecimal countAndSetWorkHour(FormDataVO ticketData, boolean isEmpty, Map<String, Object> map) {
// 总工时=准备工时+加工工时*派工数量
BigDecimal prepareTime = getBigDecimalFromData(ticketData, Const.PREPARE_HOUR);
BigDecimal workTime = getBigDecimalFromData(ticketData, Const.MAN_HOUR);
BigDecimal workNum = getBigDecimalFromData(ticketData, Const.TASK_NUMBER);
BigDecimal totalTime = workTime.multiply(workNum).add(prepareTime);
setMap(isEmpty, map, Const.TOTAL_WORK_HOUR, totalTime);
return totalTime;
}
/**
* 设置内废外废重量
*
* @param defectData 不合格品通知单
* @param ticketData 工票
* @param isEmpty 之前是否有key相同的数据,true没有
* @param map 封装的数据对象
*/
private void setWeight(FormDataVO defectData, FormDataVO ticketData, boolean isEmpty, Map<String, Object> map) {
String occurrenceWorkshop = reportUtil.getStringFromData(defectData, Const.OCCURRENCE_WORKSHOP);
String responsibleWorkshop = reportUtil.getStringFromData(defectData, Const.RESPONSIBLE_WORKSHOP);
BigDecimal number = getBigDecimalFromData(defectData, Const.WASTE_AMOUNT);
BigDecimal singleWeight = getBigDecimalFromData(ticketData, Const.MAN_HOUR);
BigDecimal wasteWeight = singleWeight.multiply(number);
// 外废
if (occurrenceWorkshop.equals(responsibleWorkshop)) {
if (isEmpty) {
// 加入操作
map.put(Const.INTERNAL_WASTE, BigDecimal.ZERO);
map.put(Const.EXTERNAL_WASTE, wasteWeight);
} else {
// 增量操作
BigDecimal out = getRowValue(map, Const.EXTERNAL_WASTE);
map.put(Const.EXTERNAL_WASTE, wasteWeight.add(out));
}
// 内废
} else {
if (isEmpty) {
// 加入操作
map.put(Const.INTERNAL_WASTE, wasteWeight);
map.put(Const.EXTERNAL_WASTE, BigDecimal.ZERO);
} else {
// 增量操作
BigDecimal out = getRowValue(map, Const.INTERNAL_WASTE);
map.put(Const.INTERNAL_WASTE, wasteWeight.add(out));
}
}
}
/**
* 判断操作类型
*
* @param projectName 项目类型
* @return true 按kg计算,false 按小时计算
*/
private Boolean isWeight(String projectName) {
for (String project : Const.WEIGHT_LIST) {
if (project.contains(projectName)) {
return true;
}
}
return false;
}
/**
* 向map中加入数据
*
* @param isEmpty 是否已经存在数据,(指定key的数据)
* @param map 数据对象
* @param key 数据的键
* @param formDataVO 数据对象
*/
private void setSecondDataIntoMap(boolean isEmpty, Map<String, Object> map, String key, FormDataVO formDataVO) {
if (Const.ONCE_LIST.contains(key)) {
return;
}
TableDataVO tableDataVO = (TableDataVO) formDataVO.getData().get(Const.WASTE_REASON_ANALYSIS);
if (tableDataVO == null) {
setMap(isEmpty, map, key, BigDecimal.ZERO);
return;
}
String value = "0";
for (TableRowVO tableRowVO : tableDataVO.getValue()) {
SelectDataVO selectData = (SelectDataVO) tableRowVO.getValue().get(Const.WASTE_REASON);
List<String> oneItemList = selectData.getValue();
if (CollectionUtils.isEmpty(oneItemList)) {
continue;
}
if (key.equals(oneItemList.get(0))) {
CommonDataVO hour = (CommonDataVO) tableRowVO.getValue().get(Const.WORK_HOUR);
value = hour.getValue();
break;
}
}
BigDecimal outValue = stob(value);
setMap(isEmpty, map, key, outValue);
}
/**
* 方法名称:StringToBigDecimal,简写stob
* 字符串转BigDecimal
*
* @param str 字符串
* @return 数字类型的值
*/
private BigDecimal stob(String str) {
if (StringUtils.isEmpty(str)) {
return BigDecimal.ZERO;
} else {
if (str.contains(Const.HOUR)) {
return new BigDecimal(str.substring(0, str.length() - 2));
} else {
return new BigDecimal(str);
}
}
}
/**
* 直接设置数据到map
*
* @param isEmpty 唯一主键是否有值,true 有值,增量操作,false全量操作
* @param map 数据封装map对象
* @param key 健
* @param value 值
*/
private void setMap(boolean isEmpty, Map<String, Object> map, String key, BigDecimal value) {
if (isEmpty) {
map.put(key, value);
} else {
// 增量操作
BigDecimal out = getRowValue(map, key);
map.put(key, value.add(out));
}
}
/**
* 重新组织数据结构
*
* @param defects 不合格品通知单
* @param workTickets 工票信息
* @param results 组织结果
* @return 整理后的数据
*/
private BaseVO<Map<String, Long>> range(List<FormDataVO> defects, List<FormDataVO> workTickets, Map<String, Map<String, FormDataVO>> results) {
if (CollectionUtils.isEmpty(workTickets) || CollectionUtils.isEmpty(defects)) {
return new BaseVO<>(BaseVO.ILLEGAL_PARAM_CODE, "参数错误!");
}
for (FormDataVO formDataVO : defects) {
String temp = reportUtil.getStringFromData(formDataVO, Const.TICKET);
if (StringUtils.isEmpty(temp)) {
continue;
}
Map<String, FormDataVO> defMap = new HashMap<>(Const.FOUR);
for (FormDataVO vo : workTickets) {
String ticket = reportUtil.getStringFromData(vo, Const.TICKET);
if (StringUtils.isEmpty(ticket) || !temp.equals(ticket)) {
continue;
}
defMap.put(Const.NOTICE_OF_UNQUALIFIED_PRODUCT, formDataVO);
defMap.put(Const.WORK_ORDER, vo);
break;
}
if (CollectionUtils.isEmpty(defMap)) {
continue;
}
results.put(temp, defMap);
}
return new BaseVO<>(BaseVO.SUCCESS_CODE, "校验成功!");
}
/**
* 检验参数,并获取原始数据
*
* @param unqualifiedList 所有的不合格品通知单
* @param workTicketList 所有的工票数据
* @param defects 过滤后的不合格品通知单
* @param workTickets 过滤后的工票信息
* @return 校验结果
*/
private BaseVO<String> filterData(List<Object> unqualifiedList, List<Object> workTicketList,
List<FormDataVO> defects, List<FormDataVO> workTickets) {
if (CollectionUtils.isEmpty(unqualifiedList) || CollectionUtils.isEmpty(workTicketList)) {
return new BaseVO<>(BaseVO.ILLEGAL_PARAM_CODE, "参数错误!");
}
Set<String> tickets = new HashSet<>(Const.SIXTEEN);
for (Object object : unqualifiedList) {
FormDataVO formDataVO = (FormDataVO) object;
String temp = reportUtil.getStringFromData(formDataVO, Const.TICKET);
if (StringUtils.isEmpty(temp)) {
continue;
}
tickets.add(temp);
defects.add(formDataVO);
}
if (CollectionUtils.isEmpty(tickets) || CollectionUtils.isEmpty(defects)) {
return new BaseVO<>(BaseVO.ILLEGAL_PARAM_CODE, "参数错误!");
}
for (Object object : workTicketList) {
FormDataVO formDataVO = (FormDataVO) object;
String temp = reportUtil.getStringFromData(formDataVO, Const.TICKET);
if (StringUtils.isEmpty(temp) || !tickets.contains(temp)) {
continue;
}
workTickets.add(formDataVO);
}
if (CollectionUtils.isEmpty(workTickets)) {
return new BaseVO<>(BaseVO.ILLEGAL_PARAM_CODE, "参数错误!");
}
return new BaseVO<>(BaseVO.SUCCESS_CODE, "校验成功!");
}
}
4 util
package cn.cncommdata.report.util;
import cn.cncommdata.FormClient;
import cn.cncommdata.form.vo.FormDataVO;
import cn.cncommdata.form.vo.Page;
import cn.cncommdata.form.vo.fielddata.CommonDataVO;
import cn.cncommdata.form.vo.fielddata.DataBaseVO;
import cn.cncommdata.form.vo.fielddata.QuoteDataVO;
import cn.cncommdata.form.vo.fielddata.SelectDataVO;
import cn.cncommdata.metadata.MetadataClient;
import cn.cncommdata.metadata.vo.FormVO;
import cn.cncommdata.report.util.constant.Const;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 物料统计报表需求form_id
*
* @author weihong.zhu
*/
@Component
public class ReportUtil {
/**
* 引入元数据
*/
@Autowired
private MetadataClient metadataClient;
/**
* 引入FormId的Cache
*/
@Resource
private FormIdCache idCache;
/**
* 导入form客户端
*/
@Resource
private FormClient formClient;
/**
* 获取表单id
*
* @param tenantId 企业id
* @param formName 表单名称
* @param reportFormId 报表的formId
* @return formId
*/
public Long getFormId(Long tenantId, String formName, Long reportFormId) {
Long formId = idCache.get(tenantId, formName, reportFormId);
if (!Objects.isNull(formId)) {
return formId;
}
FormVO formVO = metadataClient.getForm(tenantId, 0L, reportFormId);
if (Objects.isNull(formVO)) {
return null;
}
Map<String, Long> source = formVO.getSource();
if (!CollectionUtils.isEmpty(source)) {
Long sourceFormId = source.get(formName);
// 存入缓存
idCache.put(tenantId, formName, reportFormId, sourceFormId);
return sourceFormId;
}
return null;
}
/**
* 获取数据列表
*
* @param tenantId 企业id
* @param grantId 操作员 id
* @param formId 表单id
* @return 数据列表
*/
public List<Object> getDataList(Long tenantId, Long grantId, Long formId) {
Page<Object> dataList = formClient.getDataList(tenantId, grantId, Const.ALL, formId, null,
null, null, null, null, Const.ZERO, Const.ZERO, null);
List<Object> rowsData = dataList.getRows();
if (StringUtils.isEmpty(rowsData)) {
return null;
}
return rowsData;
}
/**
* 获取数据对象的值
*
* @param formDataVO 数据
* @param fieldName 字段名称
* @return 字段值
*/
public String getStringFromData(FormDataVO formDataVO, String fieldName) {
if (formDataVO.getData().get(fieldName) == null) {
return "";
}
DataBaseVO dataBaseVO = formDataVO.getData().get(fieldName);
String deliveryTime = "交货时间";
if (deliveryTime.equals(fieldName)) {
CommonDataVO commonDataVO = (CommonDataVO) dataBaseVO;
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(Long.parseLong(commonDataVO.getValue()));
int month = calendar.get(Calendar.MONTH) + 1;
String monthStr = month < 10 ? "0" + month : "" + month;
int day = calendar.get(Calendar.DAY_OF_MONTH);
String dayStr = day < 10 ? "0" + day : "" + day;
String dateStr = calendar.get(Calendar.YEAR) + "-" + monthStr + "-" + dayStr;
return dateStr.substring(2);
}
if (Const.SELECT.equals(dataBaseVO.getType())) {
SelectDataVO selectData = (SelectDataVO) dataBaseVO;
List<String> oneItemList = selectData.getValue();
if (!CollectionUtils.isEmpty(oneItemList)) {
return oneItemList.get(0);
}else{
return "";
}
} else if (Const.QUOTE.equals(dataBaseVO.getType())) {
QuoteDataVO quoteData = (QuoteDataVO) dataBaseVO;
return quoteData.getValue();
} else {
CommonDataVO commonDataVO = (CommonDataVO) dataBaseVO;
return commonDataVO.getValue();
}
}
}
5 const
package cn.cncommdata.report.util.constant;
import java.util.Arrays;
import java.util.List;
/**
* @author LiuLuhao
* @since 2019/10/22 18:12
*/
public final class Const {
/**
* 常量0
*/
public static final int ZERO = 0;
/**
* 常量1
*/
public static final int ONE = 1;
/**
* 常量2
*/
public static final int TWO = 2;
/**
* 常量4
*/
public static final int FOUR = 4;
/**
* 常量9
*/
public static final int NINE = 9;
/**
* 常量10
*/
public static final int TEN = 10;
/**
* 常量12
*/
public static final int TWELVE = 12;
/**
* 常量16
*/
public static final int SIXTEEN = 16;
/**
* 工票信息:工票号、计量单位、车间完成工时
*/
public static final String WORK_ORDER = "工票信息";
public static final String TICKET = "工票号";
public static final String UNIT = "工时单位";
public static final String WORKSHOP_TASK = "车间完成任务";
public static final String HOUR = "小时";
public static final String KG = "kg";
/**
* 不合格品指标管理:
*/
public static final String INDEX_OF_UNQUALIFIED_PRODUCT = "不合格品指标管理";
/**
* 不合格品通知单:工票号、生产项目、废品原因分析(10大原因)、综合回用量、责任回用量
*/
public static final String NOTICE_OF_UNQUALIFIED_PRODUCT = "不合格品通知单";
public static final String PROJECT = "生产项目";
public static final String COMPREHENSIVE_AMOUNT = "综合回用量";
public static final String ACTUAL_COMPREHENSIVE_REUSE_RATE = "实际综合回用率";
public static final String ACTUAL_RESPONSIBLE_REUSE_RATE = "实际责任回用率";
public static final String RESPONSIBLE_AMOUNT = "责任回用量";
public static final String RESPONSIBLE_REUSE_NUMBER = "责任回用数";
public static final String WASTE_REASON_ANALYSIS = "废品原因分析";
public static final String WASTE_REASON = "废品原因";
public static final String WORK_HOUR = "工时";
public static final String MAN_HOUR = "加工工时";
public static final String PREPARE_HOUR = "准备工时";
public static final String TOTAL_WORK_HOUR = "总工时";
public static final String RESPONSIBLE_REUSE_HOUR = "责任回用工时";
public static final String TASK_NUMBER = "派工数量";
public static final String DIRECT_REUSE_NUMBER = "直接回用数";
public static final String APPLY_REUSE_NUMBER = "申请回用数";
public static final String PART_REUSE_NUMBER = "配件回用数";
public static final String REPAIR_REUSE_NUMBER = "返修回用数";
public static final String WASTE_AMOUNT = "报废数量";
public static final String WASTE_NUMBER = "废品量";
public static final String ACTUAL_WORK_AMOUNT = "实际工作量";
public static final String ACTUAL_WASTE_RATE = "实际废品率";
public static final String ACTUAL_RESPONSIBLE_WASTE_RATE = "实际责任废品率";
public static final String OCCURRENCE_WORKSHOP = "发生车间";
public static final String RESPONSIBLE_WORKSHOP = "责任车间";
public static final String INTERNAL_WASTE = "内废";
public static final String EXTERNAL_WASTE = "外废";
public static final String REJECT_RATE_OF_MECHINING = "机加工工废品率";
public static final String METAL_WORKSHOP_ONE = "金一";
public static final String METAL_WORKSHOP_TWO = "金二";
public static final String RESPONSIBLE_WASTE_AMOUNT = "责任废品量";
public static final String CARELESS = "粗心大意";
public static final String SPECIFICATION_VIOLATION = "违反工艺规程";
public static final String POOR_LEADER = "不真确的指导工作";
public static final String POOR_DESIGNER = "设计不真确及更改不及时";
public static final String POOR_SPECIFICATION = "工艺规程不真确";
public static final String POOR_FIXTURE = "工夹具不良";
public static final String POOR_HEAT_TREATMENT = "热处理不良";
public static final String IMPROPER_INSPECTION = "前步检查失当";
public static final String EQUIPMENT_FAILURE = "设备故障精度不良";
public static final String POOR_RAW_MATERIAL = "原材料半成品不良";
public static final String POOR_FORGING_BLANK = "锻件毛坯不良";
public static final String OTHER_ELSE = "其他";
public static final String POOR_CASTING_BLANK = "铸件毛坯不良";
public static final String CREATE_DATE = "创建时间";
public static final String YEAR = "year";
public static final String YEAR_NUMBER = "年份";
public static final String MONTH = "month";
public static final List<String> PROJECTS = Arrays.asList("机加工工废指标", "金一指标", "金二指标", "机修指标", "工具指标",
"铆焊指标", "热处理指标", "总铸铁指标", "产铸铁指标", "有色指标", "锻钢指标");
public static final List<String> WEIGHT_LIST = Arrays.asList("热处理废品率", "总铸铁废品率", "产铸铁废品率", "有色废品率", "锻钢件废品率");
public static final List<String> SORTED_LIST = Arrays.asList(
"机加工工废品率", "金一", "金二", "机修废品率", "工具废品率", "铆焊废品率",
"热处理废品率", "总铸铁废品率", "产铸铁废品率", "有色废品率", "锻钢件废品率");
public static final List<String> ONCE_LIST = Arrays.asList(Const.YEAR, Const.MONTH, Const.PROJECT, Const.UNIT);
/**
* 十大原因
*/
public static final List<String> HOUR_WASTE_CASE = Arrays.asList(
Const.CARELESS, Const.SPECIFICATION_VIOLATION, Const.POOR_LEADER, Const.POOR_DESIGNER, Const.POOR_SPECIFICATION,
Const.POOR_FIXTURE, Const.POOR_HEAT_TREATMENT, Const.IMPROPER_INSPECTION, Const.EQUIPMENT_FAILURE, Const.POOR_RAW_MATERIAL,
Const.POOR_FORGING_BLANK, Const.OTHER_ELSE, Const.POOR_CASTING_BLANK);
private Const() {
}
}