瀏覽代碼

密度测量优化,报废导出

sunxianglong 3 月之前
父節點
當前提交
8339aa1aac

+ 8 - 8
src/main/java/com/jeesite/modules/mes/entity/MesScrap.java

@@ -19,7 +19,7 @@ import java.util.List;
 		@Column(name="id", attrName="id", label="id", isPK=true),
 		@Column(name="sn", attrName="sn", label="工件码"),
 		@Column(name="reason", attrName="reason", label="报废原因"),
-		@Column(name="product_cate", attrName="productCate", label="产品类型"),
+		@Column(name="product_cate", attrName="productCate", label="报废类型"),
 		@Column(name="status", attrName="status", label="状态", isInsert=false, isUpdate=false, isQuery=false),
 		@Column(name="create_by", attrName="createBy", label="create_by", isUpdate=false, isQuery=false),
 		@Column(name="create_date", attrName="createDate", label="create_date", isUpdate=false, isQuery=false, isUpdateForce=true),
@@ -28,30 +28,30 @@ import java.util.List;
 	}, orderBy="a.update_date DESC"
 )
 public class MesScrap extends DataEntity<MesScrap> {
-	
+
 	private static final long serialVersionUID = 1L;
 	private String sn;		// 工件码
 	private String reason;		// 报废原因
 	private List img = ListUtils.newArrayList();
-	private String productCate;
+	private String productCate;  //报废类型
 
 	@ExcelFields({
 			@ExcelField(title="工件码", attrName="sn", align= ExcelField.Align.CENTER, sort=20),
 //			@ExcelField(title="工位号", attrName="oprno", align= ExcelField.Align.CENTER, sort=30),
 			@ExcelField(title="报废原因", attrName="reason", align= ExcelField.Align.CENTER, sort=40),
 			@ExcelField(title="操作人", attrName="createBy", align= ExcelField.Align.CENTER, sort=50),
-			@ExcelField(title="产品类型", attrName="productCate", align= ExcelField.Align.CENTER, sort=60),
+			@ExcelField(title="报废类型", attrName="productCate", align= ExcelField.Align.CENTER, sort=60),
 			@ExcelField(title="操作日期", attrName="updateDate", align= ExcelField.Align.CENTER, sort=90, dataFormat="yyyy-MM-dd hh:mm"),
 	})
 
 	public MesScrap() {
 		this(null);
 	}
-	
+
 	public MesScrap(String id){
 		super(id);
 	}
-	
+
 	@Size(min=0, max=50, message="工件码长度不能超过 50 个字符")
 	public String getSn() {
 		return sn;
@@ -60,7 +60,7 @@ public class MesScrap extends DataEntity<MesScrap> {
 	public void setSn(String sn) {
 		this.sn = sn;
 	}
-	
+
 	@Size(min=0, max=255, message="报废原因长度不能超过 255 个字符")
 	public String getReason() {
 		return reason;
@@ -84,4 +84,4 @@ public class MesScrap extends DataEntity<MesScrap> {
 	public void setProductCate(String productCate) {
 		this.productCate = productCate;
 	}
-}
+}

+ 249 - 8
src/main/java/com/jeesite/modules/mes/service/MesScrapService.java

@@ -1,15 +1,36 @@
 package com.jeesite.modules.mes.service;
 
 import com.jeesite.common.entity.Page;
+import com.jeesite.common.lang.StringUtils;
 import com.jeesite.common.mybatis.mapper.query.QueryType;
 import com.jeesite.common.service.CrudService;
+import com.jeesite.common.web.CookieUtils;
 import com.jeesite.modules.file.utils.FileUploadUtils;
 import com.jeesite.modules.mes.dao.MesScrapDao;
+import com.jeesite.modules.mes.entity.MesProduct;
 import com.jeesite.modules.mes.entity.MesScrap;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.ClassPathResource;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * 报废记录Service
@@ -18,7 +39,21 @@ import java.util.List;
  */
 @Service
 public class MesScrapService extends CrudService<MesScrapDao, MesScrap> {
-	
+
+	/** 生产报废数据库模板列数 */
+	private static final int COL_COUNT = 53;
+	/** 缺陷列(不含报废数/比例/汇总列) */
+	private static final int[] DEFECT_COLS = {
+			9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+			21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+			41, 42, 43, 44, 45, 46
+	};
+	/** 未匹配原因时默认归入精加工-其它 */
+	private static final int DEFAULT_OTHER_COL = 38;
+
+	@Autowired
+	private MesProductService mesProductService;
+
 	/**
 	 * 获取单条数据
 	 * @param mesScrap
@@ -28,7 +63,7 @@ public class MesScrapService extends CrudService<MesScrapDao, MesScrap> {
 	public MesScrap get(MesScrap mesScrap) {
 		return super.get(mesScrap);
 	}
-	
+
 	/**
 	 * 查询分页数据
 	 * @param mesScrap 查询条件
@@ -39,7 +74,7 @@ public class MesScrapService extends CrudService<MesScrapDao, MesScrap> {
 	public Page<MesScrap> findPage(MesScrap mesScrap) {
 		return super.findPage(mesScrap);
 	}
-	
+
 	/**
 	 * 查询列表数据
 	 * @param mesScrap
@@ -49,7 +84,7 @@ public class MesScrapService extends CrudService<MesScrapDao, MesScrap> {
 	public List<MesScrap> findList(MesScrap mesScrap) {
 		return super.findList(mesScrap);
 	}
-	
+
 	/**
 	 * 保存数据(插入或更新)
 	 * @param mesScrap
@@ -63,7 +98,7 @@ public class MesScrapService extends CrudService<MesScrapDao, MesScrap> {
 		// 保存上传附件
 		FileUploadUtils.saveFileUpload(mesScrap, mesScrap.getId(), "mesScrap_file");
 	}
-	
+
 	/**
 	 * 更新状态
 	 * @param mesScrap
@@ -73,7 +108,7 @@ public class MesScrapService extends CrudService<MesScrapDao, MesScrap> {
 	public void updateStatus(MesScrap mesScrap) {
 		super.updateStatus(mesScrap);
 	}
-	
+
 	/**
 	 * 删除数据
 	 * @param mesScrap
@@ -94,5 +129,211 @@ public class MesScrapService extends CrudService<MesScrapDao, MesScrap> {
 				.and("create_date", QueryType.LT, edate);
 		return super.findCount(q);
 	}
-	
-}
+
+	/**
+	 * 按「生产报废数据库」模板导出报废统计
+	 */
+	public void exportScrapDatabase(List<MesScrap> list, HttpServletResponse response, String fileName) {
+		try (InputStream in = new ClassPathResource("templates/mes/scrap_export.xls").getInputStream();
+			 Workbook workbook = WorkbookFactory.create(in)) {
+			Sheet sheet = workbook.getSheetAt(0);
+			Row headerRow = sheet.getRow(1);
+			String[] headers = new String[COL_COUNT];
+			for (int c = 0; c < COL_COUNT; c++) {
+				headers[c] = getCellString(headerRow, c);
+			}
+
+			// 清除模板示例数据,保留前两行表头
+			for (int r = sheet.getLastRowNum(); r >= 2; r--) {
+				Row row = sheet.getRow(r);
+				if (row != null) {
+					sheet.removeRow(row);
+				}
+			}
+
+			Map<String, MesProduct> productBySn = loadProducts(list);
+			Map<String, double[]> rows = new LinkedHashMap<>();
+
+			for (MesScrap scrap : list) {
+				String productName = resolveProductName(scrap, productBySn);
+				double[] values = rows.computeIfAbsent(productName, k -> new double[COL_COUNT]);
+				int col = matchDefectColumn(scrap.getReason(), headers);
+				if (col < 0) {
+					col = DEFAULT_OTHER_COL;
+				}
+				values[col] += 1;
+			}
+
+			int rowIndex = 2;
+			for (Map.Entry<String, double[]> entry : rows.entrySet()) {
+				double[] values = entry.getValue();
+				// 各车间报废数
+				values[19] = sumRange(values, 9, 18);
+				values[39] = sumRange(values, 21, 38);
+				values[47] = sumRange(values, 41, 46);
+				// 成品报废总数
+				values[4] = values[19] + values[39] + values[47];
+				// 毛坯报废总数按压铸合计
+				values[7] = values[19];
+				// 料工报废:工=精加工+装配,料=压铸
+				values[49] = values[39] + values[47];
+				values[51] = values[19];
+
+				Row row = sheet.createRow(rowIndex++);
+				setCellString(row, 1, entry.getKey());
+				setCellString(row, 2, "/");
+				for (int c = 0; c < COL_COUNT; c++) {
+					if (c == 1 || c == 2) {
+						continue;
+					}
+					if (c == 0) {
+						setCellString(row, 0, "");
+						continue;
+					}
+					setCellNumber(row, c, values[c]);
+				}
+			}
+
+			response.reset();
+			response.setContentType("application/vnd.ms-excel; charset=utf-8");
+			String encodedName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replace("+", "%20");
+			response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedName);
+			CookieUtils.setCookie(response, "fileDownload", "true");
+			try (OutputStream out = response.getOutputStream()) {
+				workbook.write(out);
+				out.flush();
+			}
+		} catch (Exception e) {
+			throw new RuntimeException("导出生产报废数据库失败:" + e.getMessage(), e);
+		}
+	}
+
+	private Map<String, MesProduct> loadProducts(List<MesScrap> list) {
+		Map<String, MesProduct> map = new HashMap<>();
+		if (list == null || list.isEmpty()) {
+			return map;
+		}
+		Set<String> sns = new HashSet<>();
+		for (MesScrap scrap : list) {
+			if (StringUtils.isNotBlank(scrap.getSn())) {
+				sns.add(scrap.getSn().trim());
+			}
+		}
+		if (sns.isEmpty()) {
+			return map;
+		}
+		MesProduct query = new MesProduct();
+		query.getSqlMap().getWhere().and("sn", QueryType.IN, new ArrayList<>(sns));
+		List<MesProduct> products = mesProductService.findList(query);
+		if (products != null) {
+			for (MesProduct product : products) {
+				if (product != null && StringUtils.isNotBlank(product.getSn())) {
+					map.put(product.getSn(), product);
+				}
+			}
+		}
+		return map;
+	}
+
+	private String resolveProductName(MesScrap scrap, Map<String, MesProduct> productBySn) {
+		if (scrap != null && StringUtils.isNotBlank(scrap.getSn())) {
+			MesProduct product = productBySn.get(scrap.getSn().trim());
+			if (product != null && StringUtils.isNotBlank(product.getTitle())) {
+				return product.getTitle().trim();
+			}
+		}
+		if (scrap != null && StringUtils.isNotBlank(scrap.getProductCate())) {
+			return scrap.getProductCate().trim();
+		}
+		if (scrap != null && StringUtils.isNotBlank(scrap.getSn())) {
+			return scrap.getSn().trim();
+		}
+		return "未知产品";
+	}
+
+	/**
+	 * 按报废原因匹配模板缺陷列:精确匹配优先,其次包含匹配;同名列按精加工 > 压铸 > 装配顺序。
+	 */
+	private int matchDefectColumn(String reason, String[] headers) {
+		if (StringUtils.isBlank(reason)) {
+			return -1;
+		}
+		String target = reason.trim();
+		List<Integer> exact = new ArrayList<>();
+		List<Integer> contains = new ArrayList<>();
+		for (int col : DEFECT_COLS) {
+			String header = headers[col];
+			if (StringUtils.isBlank(header)) {
+				continue;
+			}
+			if (header.equals(target)) {
+				exact.add(col);
+			} else if (header.contains(target) || target.contains(header)) {
+				contains.add(col);
+			}
+		}
+		if (!exact.isEmpty()) {
+			return preferColumn(exact);
+		}
+		if (!contains.isEmpty()) {
+			return preferColumn(contains);
+		}
+		return -1;
+	}
+
+	private int preferColumn(List<Integer> cols) {
+		Integer best = null;
+		int bestScore = -1;
+		for (Integer col : cols) {
+			int score;
+			if (col >= 21 && col <= 38) {
+				score = 3; // 精加工
+			} else if (col >= 9 && col <= 18) {
+				score = 2; // 压铸
+			} else {
+				score = 1; // 装配
+			}
+			if (score > bestScore) {
+				bestScore = score;
+				best = col;
+			}
+		}
+		return best == null ? cols.get(0) : best;
+	}
+
+	private double sumRange(double[] values, int from, int to) {
+		double sum = 0;
+		for (int i = from; i <= to; i++) {
+			sum += values[i];
+		}
+		return sum;
+	}
+
+	private String getCellString(Row row, int col) {
+		if (row == null) {
+			return "";
+		}
+		Cell cell = row.getCell(col);
+		if (cell == null) {
+			return "";
+		}
+		return StringUtils.trimToEmpty(cell.toString());
+	}
+
+	private void setCellString(Row row, int col, String value) {
+		Cell cell = row.getCell(col);
+		if (cell == null) {
+			cell = row.createCell(col);
+		}
+		cell.setCellValue(value == null ? "" : value);
+	}
+
+	private void setCellNumber(Row row, int col, double value) {
+		Cell cell = row.getCell(col);
+		if (cell == null) {
+			cell = row.createCell(col);
+		}
+		cell.setCellValue(value);
+	}
+
+}

+ 10 - 0
src/main/java/com/jeesite/modules/mes/web/MesMdclController.java

@@ -74,6 +74,16 @@ public class MesMdclController extends BaseController {
 	}
 
 	/**
+	 * 查看详情
+	 */
+	@RequiresPermissions("mes:mesMdcl:view")
+	@RequestMapping(value = "info")
+	public String info(MesMdcl mesMdcl, Model model) {
+		model.addAttribute("mesMdcl", mesMdcl);
+		return "modules/mes/mesMdclInfo";
+	}
+
+	/**
 	 * 查看编辑表单
 	 */
 	@RequiresPermissions("mes:mesMdcl:view")

+ 6 - 2
src/main/java/com/jeesite/modules/mes/web/MesProductSaleController.java

@@ -11,9 +11,13 @@ import org.springframework.ui.Model;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.jeesite.common.config.Global;
 import com.jeesite.common.entity.Page;
 import com.jeesite.common.lang.StringUtils;
@@ -97,8 +101,8 @@ public class MesProductSaleController extends BaseController {
 
 	/**
 	 * 接收erp销售订单数据
-	 * @param mesProductSale
-	 * @return
+	 * 入参格式示例:["{\"saleOrder\":\"...\",\"docLineNo\":\"10\",...}"]
+	 * 先去掉外层 [""] 包装,再解析为 MesProductSale
 	 */
 	@RequestMapping(value = "getSaleOrder")
 	@ResponseBody

+ 3 - 6
src/main/java/com/jeesite/modules/mes/web/MesScrapController.java

@@ -7,7 +7,6 @@ import com.jeesite.common.config.Global;
 import com.jeesite.common.entity.Page;
 import com.jeesite.common.lang.DateUtils;
 import com.jeesite.common.lang.ObjectUtils;
-import com.jeesite.common.utils.excel.ExcelExport;
 import com.jeesite.common.web.BaseController;
 import com.jeesite.modules.file.entity.FileUpload;
 import com.jeesite.modules.file.service.FileUploadService;
@@ -119,16 +118,14 @@ public class MesScrapController extends BaseController {
 	}
 
 	/**
-	 * 导出数据
+	 * 导出数据(按生产报废数据库模板)
 	 */
 	@RequiresPermissions("mes:mesScrap:view")
 	@RequestMapping(value = "exportData")
 	public void exportData(MesScrap mesScrap, HttpServletResponse response) {
 		List<MesScrap> list = mesScrapService.findList(mesScrap);
-		String fileName = "报废记录" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx";
-		try(ExcelExport ee = new ExcelExport("报废记录", MesScrap.class)){
-			ee.setDataList(list).write(response, fileName);
-		}
+		String fileName = "生产报废数据库" + DateUtils.getDate("yyyyMMddHHmmss") + ".xls";
+		mesScrapService.exportScrapDatabase(list, response, fileName);
 	}
 
 	@RequiresPermissions("mes:mesScrap:api")

二進制
src/main/resources/templates/mes/scrap_export.xls


+ 83 - 0
src/main/resources/views/modules/mes/mesMdclInfo.html

@@ -0,0 +1,83 @@
+<% layout('/layouts/default.html', {title: '密度测量表详情', libs: ['fileupload']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header with-border">
+			<div class="box-title">
+				<i class="fa icon-note"></i> ${text('密度测量表详情')}
+			</div>
+			<div class="box-tools pull-right">
+				<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+			</div>
+		</div>
+		<div class="box-body">
+			<div class="form-unit">${text('基本信息')}</div>
+			<div class="row">
+				<div class="col-xs-12">
+					<table class="table table-bordered">
+						<tr>
+							<th class="mpc-header" style="width:120px;">${text('炉号')}</th>
+							<td>${mesMdcl.luCode}</td>
+						</tr>
+						<tr>
+							<th class="mpc-header">${text('包号')}</th>
+							<td>${mesMdcl.baoCode}</td>
+						</tr>
+						<tr>
+							<th class="mpc-header">${text('密度标准')}</th>
+							<td>≥2.6</td>
+						</tr>
+						<tr>
+							<th class="mpc-header">${text('单位')}</th>
+							<td>g/cm3</td>
+						</tr>
+						<tr>
+							<th class="mpc-header">${text('密度值')}</th>
+							<td>${mesMdcl.mdValue}</td>
+						</tr>
+						<tr>
+							<th class="mpc-header">${text('DI标准')}</th>
+							<td>≤0.8</td>
+						</tr>
+						<tr>
+							<th class="mpc-header">${text('DI值')}</th>
+							<td>${mesMdcl.diValue}</td>
+						</tr>
+						<tr>
+							<th class="mpc-header">${text('判定结果')}</th>
+							<td>
+								<% if('合格' == mesMdcl.result){ %>
+									<span style="color:green;font-weight:bold;">合格</span>
+								<% } else if(isNotEmpty(mesMdcl.result)){ %>
+									<span style="color:red;font-weight:bold;">${mesMdcl.result}</span>
+								<% } %>
+							</td>
+						</tr>
+						<tr>
+							<th class="mpc-header">${text('类型')}</th>
+							<td>熔炼密度检测</td>
+						</tr>
+						<tr>
+							<th class="mpc-header">${text('图片')}</th>
+							<td>
+								<#form:fileupload id="uploadImage" bizKey="${mesMdcl.id}" bizType="mesMdcl_image"
+									uploadType="image" class="" readonly="true" preview="true"/>
+							</td>
+						</tr>
+						<tr>
+							<th class="mpc-header">${text('更新时间')}</th>
+							<td>${@DateUtils.formatDateTime(mesMdcl.updateDate)}</td>
+						</tr>
+					</table>
+				</div>
+			</div>
+		</div>
+		<div class="box-footer">
+			<div class="row">
+				<div class="col-sm-offset-2 col-sm-10">
+					<button type="button" class="btn btn-sm btn-default" id="btnCancel" onclick="js.closeCurrentTabPage()"><i class="fa fa-reply-all"></i> ${text('关 闭')}</button>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
+<% } %>

+ 1 - 1
src/main/resources/views/modules/mes/mesMdclList.html

@@ -57,7 +57,7 @@ $('#dataGrid').dataGrid({
 	searchForm: $("#searchForm"),
 	columnModel: [
 		{header:'${text("炉号")}', name:'luCode', index:'a.lu_code', width:150, align:"center", frozen:true, formatter: function(val, obj, row, act){
-			return '<a href="${ctx}/mes/mesMdcl/form?id='+row.id+'" class="btnList" data-title="${text("编辑密度测量表")}">'+(val||row.id)+'</a>';
+			return '<a href="${ctx}/mes/mesMdcl/info?id='+row.id+'" class="btnList" data-title="${text("密度测量表详情")}">'+(val||row.id)+'</a>';
 		}},
 		{header:'${text("包号")}', name:'baoCode', index:'a.bao_code', width:150, align:"center"},
 		{header:'${text("密度标准")}', name:'mdStandard', width:100, align:"center", sortable:false, formatter: function(){

+ 3 - 3
src/main/resources/views/modules/mes/mesScrapList.html

@@ -50,9 +50,9 @@ $('#dataGrid').dataGrid({
 			//
 			return val||row.id;
 		}},
+		{header:'${text("报废类型")}', name:'productCate', index:'a.product_cate', width:150, align:"center"},
 		{header:'${text("报废原因")}', name:'reason', index:'a.reason', width:150, align:"center"},
 		{header:'${text("操作人")}', name:'createBy', index:'a.create_by', width:150, align:"center"},
-		{header:'${text("产品类型")}', name:'productCate', index:'a.product_cate', width:150, align:"center"},
 		{header:'${text("日期")}', name:'updateDate', index:'a.update_date', width:150, align:"center"},
 		{header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){
 			var actions = [];
@@ -68,7 +68,7 @@ $('#dataGrid').dataGrid({
 	],
 	// 加载成功后执行事件
 	ajaxSuccess: function(data){
-		
+
 	}
 });
 
@@ -78,4 +78,4 @@ $('#btnExport').click(function(){
 		downloadFile:true
 	});
 });
-</script>
+</script>