分享一个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 接口*/@Resourceprivate 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 {/*** 引入表单工具*/@Resourceprivate ReportUtil reportUtil;/*** 工时损失废品分析——小时(hour)* <p>* 数据来源* 工票信息:工票号、计量单位、车间完成工时* 不合格品通知单:工票号、生产项目、废品原因分析(10大原因)、综合回用量、责任回用量* 各种指标:废品率-指标、责任废品率-指标、综合回用率-指标、责任回用率-指标** @param tenantId 企业id* @param grantId  操作员* @param formIds  所需表单id的json对象字符串(内部研发项目、外部研发项目、生产技术准备、产品安装调试、进度计划维护)* @return 查询结果* @author leimin*/@Overridepublic 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>() {@Overridepublic 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) {// 生成keyString 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) {// 生成keyString 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 stringString valueString = value == null ? "" : value.toString();rowNew.put(key, valueString);} else {// new BigDecimalBigDecimal 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 {/*** 引入元数据*/@Autowiredprivate MetadataClient metadataClient;/*** 引入FormId的Cache*/@Resourceprivate FormIdCache idCache;/*** 导入form客户端*/@Resourceprivate 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() {}
}

废品情况分析——报表接口开发相关推荐

  1. 接口开发指的是什么_企业在什么情况下要选择定制开发软件

    软件定制开发是指软件开发商依据我们的需求停止量身定制的开发,软件定制开发相关于单纯产品的施行周期长.本钱高.风险大.假如根据定制开发的工作量或水平来分,我们能够分为完整定制开发和局部定制开发,完整定制 ...

  2. 在线教育_Day02-_项目环境搭建和讲师管理接口开发

    一.数据库设计 1.1 创建数据库 创建数据库:自定义数据库名 1.2 创建数据表 导入资料中的 gl_edu.sql文件,创建表 1.3 数据库设计规约 以下规约只针对本模块,更全面的文档参考< ...

  3. YesApi-超强的API接口开发神器

    YesApi 是一个免费.简单又好用的API低代码开发平台.定位:YesApi = API开发 + API测试 + API文档 + API调用 + API后端,让你用一个账号,就能轻松搞定API接口开 ...

  4. TP5_接口开发之全局异常控制

    前言: 说到异常控制,也许很多会比较陌生,我身边很少人会去写抛异常的代码.但是异常用好了是非常的方便大家开发.首先我们来回顾下哪里可以看到异常,首先我们用框架开发的时候,我们的代码出错或者别的东西.如 ...

  5. SpringBoot+Vue博客系统---后端接口开发

    Java后端接口开发 从零开始搭建一个项目骨架,最好选择合适,熟悉的技术,并且在未来易拓展,适合微服务化体系等.所以一般以Springboot作为我们的框架基础,这是离不开的了. 然后数据层,我们常用 ...

  6. 基于SpringBoot+Vue开发的前后端分离博客项目-Java后端接口开发

    文章目录 1. 前言 2. 新建Springboot项目 3. 整合mybatis plus 第一步:导依赖 第二步:写配置文件 第三步:mapper扫描+分页插件 第四步:代码生成配置 第五步:执行 ...

  7. 南孚电池:如何从0-1建立经营分析报表平台,助力集团转型?

    陆续分享一些企业数据化管理的案例,有制造.零售.金融有侧重业务,有侧重项目,希望给同处在数字化转型路上的企业一些思路与启发!今天分享的是南孚电池的案例. 注:本文为帆软2021数据生产力大赛获奖案例, ...

  8. 基于软件分析的智能化开发新型服务与技术

    摘要:从云服务厂商的角度来给大家介绍一下,当前业界围绕该领域要做哪些事情. 本文分享自华为云社区<基于软件分析的智能化开发新型服务与技术>,作者:敏捷的小智 . 本文以技术文章的方式回顾梁 ...

  9. 微信公众平台卡券API接口开发指南

    文章目录 说明 卡券术语介绍 卡券生命周期流程图 准备 申请开发账号 公众平台开发配置 公众号开发信息配置 填写服务器配置并验证有效性 申请微信认证及开通微信卡券功能 卡券分类 普通卡券 会员卡券 朋 ...

最新文章

  1. java 线程组作用_浅析Java中线程组(ThreadGroup类)
  2. TensorFlow练习24: GANs-生成对抗网络 (生成明星脸)
  3. python练习册 每天一个小程序 第0013题
  4. AFNetworking 下载文件断点续传操作
  5. 注解@CrossOrigin解决跨域问题
  6. 程序员面试题精选100题(34)-数组中只出现一次的数字[算法]
  7. typescript 方法后面加感叹号_TypeScript编译器SDK版本问题
  8. 洛谷 P2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm
  9. 阿里云服务器邮件发送
  10. mysql 目录更改 php,Linux下更改MySQL目录
  11. 爬取了BAT等一线大厂近10000+招聘需求,总结出3-5年+Java开发的高频技术需求
  12. java翻转字符串中的单词
  13. Python中如何打印空行
  14. LayaAir Geolocation 获取地理位置
  15. 除了秀米,微信排版还有什么好用的? ---短网址
  16. 用户研究中的定性研究、定量研究
  17. ebtables日志nflog
  18. 英文版win11怎么变成中文版?英文版win11改中文版教程
  19. 银河英雄传说旗舰名称考证—帝国军
  20. 网站被劫持勒索怎么办

热门文章

  1. 复盘_7月(第2周工作复盘)
  2. android @nonnull的作用,Android Studio错误的含义:未注释的参数会覆盖@NonNull参数
  3. Java八股文重点记录
  4. Delphi XE开发Linux应用(一)
  5. Oracle数据库学习(二):SQL developer客户端下载、安装与连接Oralce 11g数据库
  6. 学徒浅析Android开发:第三讲——Dialog的常用方法介绍(二)
  7. Linux离线与在线安装Docker 详细教程
  8. 得物 API 一站式协作平台探索与落地
  9. 萌猫娘巧克力 的 VR笔记
  10. linux 卸载java jdk1.6_linux下查看已经安装的jdk 并卸载jdk