Prechádzať zdrojové kódy

光谱仪数据修改,完工报告接口

sunxianglong 4 mesiacov pred
rodič
commit
375154f349

+ 1 - 1
src/main/java/com/jeesite/modules/mes/dao/MesProductReportDao.java

@@ -7,7 +7,7 @@ import com.jeesite.modules.mes.entity.MesProductReport;
 /**
  * 完工报告DAO接口
  * @author sxl
- * @version 2026-06-28
+ * @version 2026-07-03
  */
 @MyBatisDao
 public interface MesProductReportDao extends CrudDao<MesProductReport> {

+ 23 - 1
src/main/java/com/jeesite/modules/mes/entity/MesProductReport.java

@@ -14,7 +14,7 @@ import com.jeesite.common.mybatis.mapper.query.QueryType;
 /**
  * 完工报告Entity
  * @author sxl
- * @version 2026-06-28
+ * @version 2026-07-03
  */
 @Table(name="mes_product_report", alias="a", label="完工报告信息", columns={
 		@Column(name="id", attrName="id", label="id", isPK=true),
@@ -33,6 +33,8 @@ import com.jeesite.common.mybatis.mapper.query.QueryType;
 		@Column(name="create_by", attrName="createBy", label="创建人", isUpdate=false, isQuery=false),
 		@Column(name="update_date", attrName="updateDate", label="更新时间", isQuery=false),
 		@Column(name="update_by", attrName="updateBy", label="更新人", isQuery=false),
+		@Column(name="scrap_reason", attrName="scrapReason", label="报废类型"),
+		@Column(name="lot_code", attrName="lotCode", label="入库批号"),
 	}, orderBy="a.update_date DESC"
 )
 public class MesProductReport extends DataEntity<MesProductReport> {
@@ -49,6 +51,8 @@ public class MesProductReport extends DataEntity<MesProductReport> {
 	private String eligibleQty;		// 合格数量
 	private String scrapQty;		// 报废数量
 	private String remark;		// 备注
+	private String scrapReason;		// 报废类型
+	private String lotCode;		// 入库批号
 
 	public MesProductReport() {
 		this(null);
@@ -157,4 +161,22 @@ public class MesProductReport extends DataEntity<MesProductReport> {
 		this.remark = remark;
 	}
 	
+	@Size(min=0, max=64, message="报废类型长度不能超过 64 个字符")
+	public String getScrapReason() {
+		return scrapReason;
+	}
+
+	public void setScrapReason(String scrapReason) {
+		this.scrapReason = scrapReason;
+	}
+	
+	@Size(min=0, max=64, message="入库批号长度不能超过 64 个字符")
+	public String getLotCode() {
+		return lotCode;
+	}
+
+	public void setLotCode(String lotCode) {
+		this.lotCode = lotCode;
+	}
+	
 }

+ 1 - 1
src/main/java/com/jeesite/modules/mes/service/MesProductReportService.java

@@ -13,7 +13,7 @@ import com.jeesite.modules.file.utils.FileUploadUtils;
 /**
  * 完工报告Service
  * @author sxl
- * @version 2026-06-28
+ * @version 2026-07-03
  */
 @Service
 public class MesProductReportService extends CrudService<MesProductReportDao, MesProductReport> {

+ 80 - 1
src/main/java/com/jeesite/modules/mes/util/U9cAuthUtil.java

@@ -1,5 +1,6 @@
 package com.jeesite.modules.mes.util;
 
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.jeesite.common.lang.StringUtils;
 import org.slf4j.Logger;
@@ -29,7 +30,7 @@ public class U9cAuthUtil {
 
 	private static final String CREATE_RCV_URL = "http://10.0.10.11/u9c/webapi/CustLJRMESOpenAPI/CreateRCV";
 
-	private static final String CREATE_COMPLETE_DOC_URL = "http://10.0.10.11/u9c/webapi/CustLJRMESOpenAP/CreateCompleteDoc";
+	private static final String CREATE_COMPLETE_DOC_URL = "http://10.0.10.11/u9c/webapi/CustLJRMESOpenAPI/CreateCompleteDoc";
 
 	private static final String CREATE_ISSUE_DOC_URL = "http://10.0.10.11/u9c/webapi/CustLJRMESOpenAPI/CreateIssueDoc";
 
@@ -157,6 +158,84 @@ public class U9cAuthUtil {
 	}
 
 	/**
+	 * U9C 响应 Data 可能是 JSONArray,也可能是单个 JSONObject
+	 */
+	public static JSONArray getErpDataArray(JSONObject response) {
+		if (response == null) {
+			return null;
+		}
+		Object data = response.get("Data");
+		if (data == null) {
+			return null;
+		}
+		if (data instanceof JSONArray) {
+			return (JSONArray) data;
+		}
+		if (data instanceof JSONObject) {
+			JSONArray array = new JSONArray();
+			array.add(data);
+			return array;
+		}
+		return null;
+	}
+
+	public static JSONObject getErpDataFirst(JSONObject response) {
+		JSONArray dataList = getErpDataArray(response);
+		if (dataList == null || dataList.isEmpty()) {
+			return null;
+		}
+		return dataList.getJSONObject(0);
+	}
+
+	/**
+	 * 从 U9C 接口响应中提取错误信息。
+	 * 优先级:Data[].ErrorMsg &gt; ResMsg &gt; Exception
+	 */
+	public static String getErpErrorMessage(JSONObject response) {
+		if (response == null) {
+			return null;
+		}
+		JSONArray dataList = getErpDataArray(response);
+		if (dataList != null && !dataList.isEmpty()) {
+			for (int i = 0; i < dataList.size(); i++) {
+				JSONObject data = dataList.getJSONObject(i);
+				if (data == null) {
+					continue;
+				}
+				if (!Boolean.TRUE.equals(data.getBoolean("IsSucceed"))) {
+					String errorMsg = data.getString("ErrorMsg");
+					if (StringUtils.isNotBlank(errorMsg)) {
+						return errorMsg;
+					}
+				}
+			}
+		}
+		String resMsg = response.getString("ResMsg");
+		if (StringUtils.isNotBlank(resMsg)) {
+			return resMsg;
+		}
+		return getExceptionText(response.get("Exception"));
+	}
+
+	private static String getExceptionText(Object exception) {
+		if (exception == null) {
+			return null;
+		}
+		if (exception instanceof JSONObject) {
+			JSONObject ex = (JSONObject) exception;
+			String message = ex.getString("Message");
+			if (StringUtils.isBlank(message)) {
+				message = ex.getString("message");
+			}
+			if (StringUtils.isNotBlank(message)) {
+				return message;
+			}
+		}
+		String text = String.valueOf(exception);
+		return "null".equalsIgnoreCase(text) ? null : text;
+	}
+
+	/**
 	 * 调用 U9C OAuth2 登录接口,返回完整响应
 	 */
 	public static JSONObject authLogin() {

+ 38 - 1
src/main/java/com/jeesite/modules/mes/web/MesGpyController.java

@@ -1,9 +1,14 @@
 package com.jeesite.modules.mes.web;
 
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.jeesite.common.mybatis.mapper.query.QueryType;
+
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
@@ -31,6 +36,8 @@ import com.jeesite.modules.mes.service.MesGpyService;
 @RequestMapping(value = "${adminPath}/mes/mesGpy")
 public class MesGpyController extends BaseController {
 
+	private static final String[] MEAS_ELEMENT_NAMES = {"Si", "Fe", "Cu", "Mn", "Mg", "Zn", "Ti", "Sr", "OR"};
+
 	@Autowired
 	private MesGpyService mesGpyService;
 
@@ -86,7 +93,37 @@ public class MesGpyController extends BaseController {
 	public List<MesGpyMeas> measListData(String mesGpyId) {
 		MesGpyMeas query = new MesGpyMeas();
 		query.setMesGpyId(mesGpyId);
-		return mesGpyMeasService.findList(query);
+		query.getSqlMap().getWhere().and("element_name", QueryType.IN, MEAS_ELEMENT_NAMES);
+		List<MesGpyMeas> list = mesGpyMeasService.findList(query);
+		Map<String, Integer> orderMap = new HashMap<>();
+		for (int i = 0; i < MEAS_ELEMENT_NAMES.length; i++) {
+			orderMap.put(MEAS_ELEMENT_NAMES[i], i);
+		}
+		list.sort(Comparator.comparingInt(item -> orderMap.getOrDefault(item.getElementName(), Integer.MAX_VALUE)));
+		for (MesGpyMeas item : list) {
+			item.setReplicate0(formatMeasurementValue(item.getReplicate0()));
+			item.setReplicate1(formatMeasurementValue(item.getReplicate1()));
+			item.setMean(formatMeasurementValue(item.getMean()));
+		}
+		return list;
+	}
+
+	private String formatMeasurementValue(String value) {
+		if (value == null || value.isEmpty()) {
+			return "";
+		}
+		String prefix = "";
+		String number = value;
+		if (value.startsWith("<") || value.startsWith(">")) {
+			prefix = value.substring(0, 1);
+			number = value.substring(1);
+		}
+		try {
+			java.math.BigDecimal bd = new java.math.BigDecimal(number);
+			return prefix + bd.setScale(5, java.math.RoundingMode.HALF_UP).toPlainString();
+		} catch (NumberFormatException e) {
+			return value;
+		}
 	}
 
 	/**

+ 24 - 23
src/main/java/com/jeesite/modules/mes/web/MesMaterialReportController.java

@@ -167,16 +167,18 @@ public class MesMaterialReportController extends BaseController {
 			return resp;
 
 		}
-		String[] parts = materialCode.split("_",-1);
-		if(parts.length < 3){
+		materialCode = materialCode.trim();
+		String[] parts = materialCode.split(",", -1);
+		if (parts.length != 7) {
 			resp.setResult(Global.FALSE);
 			resp.setMessage("二维码规则不正确!");
 			return resp;
 		}
-		String materialId = parts[0];
-		String faDate = parts[1];
-		String nums = parts[2];
-		if (parts.length!= 3||StringUtils.isEmpty(materialId)||StringUtils.isEmpty(faDate)||StringUtils.isEmpty(nums)){
+		// 供应商代码,采购订单号,采购订单行号,erp编码,生产日期,生产批次,批次数量
+		String erpSn = parts[3].trim();
+		String faDate = parts[4].trim();
+		String nums = parts[6].trim();
+		if (StringUtils.isEmpty(erpSn) || StringUtils.isEmpty(faDate) || StringUtils.isEmpty(nums)) {
 			resp.setResult(Global.FALSE);
 			resp.setMessage("二维码规则不正确!");
 			return resp;
@@ -190,7 +192,7 @@ public class MesMaterialReportController extends BaseController {
 			return resp;
 		}
 		MesMaterial mesMaterial = new MesMaterial();
-		mesMaterial.setErpSn(materialId);
+		mesMaterial.setErpSn(erpSn);
 		MesMaterial material = mesMaterialService.findInfo(mesMaterial);
 		if(material==null){
 			resp.setResult(Global.FALSE);
@@ -217,34 +219,33 @@ public class MesMaterialReportController extends BaseController {
 	@ResponseBody
 	public CommonResp submit(HttpServletRequest req) {
 		CommonResp<Map<String, Object>> resp = new CommonResp<>();
-		String materialCode = req.getParameter("materialCode").trim();
+		String materialCode = req.getParameter("materialCode");
 		String nums = req.getParameter("nums");
 		String remark = req.getParameter("remark");
-		String[] parts = materialCode.split("_",-1);
-		if(parts.length < 3){
+		if (StringUtils.isEmpty(materialCode)) {
 			resp.setResult(Global.FALSE);
-			resp.setMessage("二维码规则不正确!");
+			resp.setMessage("来料码不能为空!");
 			return resp;
 		}
-		String materialId = parts[0];
-		String faDate = parts[1];
-		String faNums = parts[2];
-		if (parts.length!= 3||StringUtils.isEmpty(materialId)||StringUtils.isEmpty(faDate)||StringUtils.isEmpty(nums)){
+		materialCode = materialCode.trim();
+		String[] parts = materialCode.split(",", -1);
+		if (parts.length != 7) {
 			resp.setResult(Global.FALSE);
 			resp.setMessage("二维码规则不正确!");
 			return resp;
 		}
-		String faDate1=null;
-        try {
-             faDate1 = DateLocalUtils.getCurrentDate2(faDate);
-        } catch (ParseException e) {
+		// 供应商代码,采购订单号,采购订单行号,erp编码,生产日期,生产批次,批次数量
+		String erpSn = parts[3].trim();
+		String faDate = parts[4].trim();
+		String faNums = parts[6].trim();
+		if (StringUtils.isEmpty(erpSn) || StringUtils.isEmpty(faDate) || StringUtils.isEmpty(faNums)
+				|| StringUtils.isEmpty(nums)) {
 			resp.setResult(Global.FALSE);
 			resp.setMessage("二维码规则不正确!");
 			return resp;
-
-        }
+		}
         MesMaterial mesMaterial = new MesMaterial();
-		mesMaterial.setErpSn(materialId);
+		mesMaterial.setErpSn(erpSn);
 		MesMaterial material = mesMaterialService.findInfo(mesMaterial);
 		if(material==null){
 			resp.setResult(Global.FALSE);
@@ -266,7 +267,7 @@ public class MesMaterialReportController extends BaseController {
 		MesMaterialReport mesMaterialReport1 = new MesMaterialReport();
 		mesMaterialReport1.setSn(materialCode);
 		mesMaterialReport1.setMaterialId(material.getId());
-		mesMaterialReport1.setFaDate(faDate1);
+		mesMaterialReport1.setFaDate(DateUtils.getDate("yyyy-MM-dd HH:mm:ss"));
 		mesMaterialReport1.setFaNums(Long.valueOf(faNums));
 		mesMaterialReport1.setState("0");
 		mesMaterialReport1.setStatus("0");

+ 1 - 1
src/main/java/com/jeesite/modules/mes/web/MesMaterialRkController.java

@@ -230,7 +230,7 @@ public class MesMaterialRkController extends BaseController {
 	@RequestMapping(value = "mesToErp")
 	@ResponseBody
 	public String mesToErp(MesMaterialRk mesMaterialRk) {
-		MesMaterialRk erpCode=null;
+		MesMaterialRk erpCode=new MesMaterialRk();
 		erpCode.setLotCode(mesMaterialRk.getLotCode());
 		List<MesMaterialRk> list = mesMaterialRkService.findList(erpCode);
 		if (list.isEmpty()){

+ 117 - 27
src/main/java/com/jeesite/modules/mes/web/MesProductReportController.java

@@ -1,15 +1,18 @@
 package com.jeesite.modules.mes.web;
 
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.jeesite.common.lang.DateUtils;
-import com.jeesite.common.lang.StringUtils;
 import com.jeesite.modules.mes.util.U9cAuthUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
@@ -17,7 +20,9 @@ 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.RequestMethod;
 import org.springframework.web.bind.annotation.ResponseBody;
 
 import com.jeesite.common.config.Global;
@@ -29,7 +34,7 @@ import com.jeesite.modules.mes.service.MesProductReportService;
 /**
  * 完工报告Controller
  * @author sxl
- * @version 2026-06-28
+ * @version 2026-07-03
  */
 @Controller
 @RequestMapping(value = "${adminPath}/mes/mesProductReport")
@@ -37,7 +42,7 @@ public class MesProductReportController extends BaseController {
 
 	@Autowired
 	private MesProductReportService mesProductReportService;
-	
+
 	/**
 	 * 获取数据
 	 */
@@ -45,7 +50,7 @@ public class MesProductReportController extends BaseController {
 	public MesProductReport get(String id, boolean isNewRecord) {
 		return mesProductReportService.get(id, isNewRecord);
 	}
-	
+
 	/**
 	 * 查询列表
 	 */
@@ -55,7 +60,7 @@ public class MesProductReportController extends BaseController {
 		model.addAttribute("mesProductReport", mesProductReport);
 		return "modules/mes/mesProductReportList";
 	}
-	
+
 	/**
 	 * 查询列表数据
 	 */
@@ -88,7 +93,7 @@ public class MesProductReportController extends BaseController {
 		mesProductReportService.save(mesProductReport);
 		return renderResult(Global.TRUE, text("保存完工报告成功!"));
 	}
-	
+
 	/**
 	 * 删除数据
 	 */
@@ -99,22 +104,19 @@ public class MesProductReportController extends BaseController {
 		mesProductReportService.delete(mesProductReport);
 		return renderResult(Global.TRUE, text("删除完工报告成功!"));
 	}
-
 	/**
 	 * 同步数据到 ERP
 	 */
 	@RequiresPermissions("mes:mesProductReport:edit")
-	@RequestMapping(value = "mesToErp")
+	@RequestMapping(value = "mesToErp", method = {RequestMethod.GET, RequestMethod.POST})
 	@ResponseBody
-	public String mesToErp(MesProductReport mesProductReport) {
-		if (StringUtils.isNotBlank(mesProductReport.getId())) {
-			MesProductReport exist = mesProductReportService.get(mesProductReport);
-			if (exist != null && !exist.getIsNewRecord()) {
-				mesProductReport = exist;
-			}
+	public String mesToErp(@RequestBody(required = false) String body, HttpServletRequest request) {
+		MesProductReport mesProductReport = resolveMesProductReport(body, request.getParameter("id"));
+		if (mesProductReport == null) {
+			return renderResult(Global.FALSE, text("参数错误!"));
 		}
-		if (mesProductReport == null || mesProductReport.getIsNewRecord()) {
-			return renderResult(Global.FALSE, text("完工报告不存在!"));
+		if (mesProductReport.getIsNewRecord()) {
+			mesProductReportService.save(mesProductReport);
 		}
 
 		String requestJson = buildCreateCompleteDocJson(mesProductReport);
@@ -123,26 +125,32 @@ public class MesProductReportController extends BaseController {
 			return renderResult(Global.FALSE, text("同步ERP失败!"));
 		}
 		if (!Boolean.TRUE.equals(response.getBoolean("Success"))) {
-			String msg = response.getString("ResMsg");
+			String msg = U9cAuthUtil.getErpErrorMessage(response);
 			return renderResult(Global.FALSE, text(StringUtils.isNotBlank(msg) ? msg : "同步ERP失败!"));
 		}
 
-		JSONArray dataList = response.getJSONArray("Data");
+		JSONArray dataList = U9cAuthUtil.getErpDataArray(response);
 		if (dataList == null || dataList.isEmpty()) {
-			return renderResult(Global.FALSE, text("同步ERP失败:返回数据为空!"));
+			String msg = U9cAuthUtil.getErpErrorMessage(response);
+			return renderResult(Global.FALSE, text(StringUtils.isNotBlank(msg) ? msg : "同步ERP失败:返回数据为空!"));
 		}
 		JSONObject data = dataList.getJSONObject(0);
 		if (data == null || !Boolean.TRUE.equals(data.getBoolean("IsSucceed"))) {
-			String errorMsg = data != null ? data.getString("ErrorMsg") : null;
+			String errorMsg = U9cAuthUtil.getErpErrorMessage(response);
 			return renderResult(Global.FALSE, text(StringUtils.isNotBlank(errorMsg) ? errorMsg : "同步ERP失败!"));
 		}
 
 		String code = data.getString("Code");
+		if (StringUtils.isBlank(code)) {
+			String errorMsg = U9cAuthUtil.getErpErrorMessage(response);
+			return renderResult(Global.FALSE, text(StringUtils.isNotBlank(errorMsg) ? errorMsg : "同步ERP失败:未返回单号!"));
+		}
+		mesProductReport.setDocNo(code);
+		mesProductReportService.update(mesProductReport);
+
 		String docId = data.getString("ID");
 		StringBuilder successMsg = new StringBuilder(text("同步ERP成功!"));
-		if (StringUtils.isNotBlank(code)) {
-			successMsg.append(" ").append(code);
-		}
+		successMsg.append(" ").append(code);
 		if (StringUtils.isNotBlank(docId)) {
 			successMsg.append(" (").append(docId).append(")");
 		}
@@ -160,10 +168,11 @@ public class MesProductReportController extends BaseController {
 		item.put("DocNo", StringUtils.defaultIfBlank(mesProductReport.getDocNo(), ""));
 		item.put("MODocNo", StringUtils.defaultIfBlank(mesProductReport.getMoDocNo(), ""));
 		item.put("CompleteDate", formatReportDate(mesProductReport.getCompleteDate()));
-		item.put("ActualRcvTime", formatReportDate(mesProductReport.getActualRcvTime()));
 		item.put("CompleteQty", StringUtils.defaultIfBlank(mesProductReport.getCompleteQty(), ""));
 		item.put("EligibleQty", StringUtils.defaultIfBlank(mesProductReport.getEligibleQty(), ""));
 		item.put("ScrapQty", StringUtils.defaultIfBlank(mesProductReport.getScrapQty(), ""));
+		item.put("ScrapReason", StringUtils.defaultIfBlank(mesProductReport.getScrapReason(), ""));
+		item.put("LotCode", StringUtils.defaultIfBlank(mesProductReport.getLotCode(), ""));
 
 		JSONArray requestArray = new JSONArray();
 		requestArray.add(item);
@@ -174,7 +183,88 @@ public class MesProductReportController extends BaseController {
 		if (date == null) {
 			return "";
 		}
-		return DateUtils.formatDate(date, "yyyy-MM-dd HH:mm:ss");
+		return DateUtils.formatDate(date, "yyyy-MM-dd");
 	}
-	
-}
+
+	private MesProductReport resolveMesProductReport(String body, String idParam) {
+		if (StringUtils.isNotBlank(body)) {
+			try {
+				return parseMesProductReportBody(body);
+			} catch (Exception e) {
+				logger.error("解析完工报告参数失败", e);
+				return null;
+			}
+		}
+		if (StringUtils.isNotBlank(idParam)) {
+			MesProductReport exist = mesProductReportService.get(new MesProductReport(idParam));
+			if (exist != null && !exist.getIsNewRecord()) {
+				return exist;
+			}
+		}
+		return null;
+	}
+
+	private MesProductReport parseMesProductReportBody(String body) throws ParseException {
+		String json = body.trim();
+		if (json.startsWith("\"") && json.endsWith("\"") && json.length() >= 2) {
+			json = json.substring(1, json.length() - 1);
+		}
+		JSONObject jsonObj = JSON.parseObject(json);
+		if (jsonObj == null) {
+			return null;
+		}
+		MesProductReport mesProductReport;
+		String id = jsonObj.getString("id");
+		if (StringUtils.isNotBlank(id)) {
+			mesProductReport = mesProductReportService.get(new MesProductReport(id));
+			if (mesProductReport == null || mesProductReport.getIsNewRecord()) {
+				return null;
+			}
+		} else {
+			mesProductReport = new MesProductReport();
+		}
+		if (jsonObj.containsKey("otherId")) {
+			mesProductReport.setOtherId(jsonObj.getString("otherId"));
+		}
+		if (jsonObj.containsKey("docTypeCode")) {
+			mesProductReport.setDocTypeCode(jsonObj.getString("docTypeCode"));
+		}
+		if (jsonObj.containsKey("docNo")) {
+			mesProductReport.setDocNo(jsonObj.getString("docNo"));
+		}
+		if (jsonObj.containsKey("moDocNo")) {
+			mesProductReport.setMoDocNo(jsonObj.getString("moDocNo"));
+		}
+		if (jsonObj.containsKey("completeQty")) {
+			mesProductReport.setCompleteQty(jsonObj.getString("completeQty"));
+		}
+		if (jsonObj.containsKey("eligibleQty")) {
+			mesProductReport.setEligibleQty(jsonObj.getString("eligibleQty"));
+		}
+		if (jsonObj.containsKey("scrapQty")) {
+			mesProductReport.setScrapQty(jsonObj.getString("scrapQty"));
+		}
+		if (jsonObj.containsKey("scrapReason")) {
+			mesProductReport.setScrapReason(jsonObj.getString("scrapReason"));
+		}
+		if (jsonObj.containsKey("lotCode")) {
+			mesProductReport.setLotCode(jsonObj.getString("lotCode"));
+		}
+		if (jsonObj.containsKey("remark")) {
+			mesProductReport.setRemark(jsonObj.getString("remark"));
+		}
+		parseReportDate(jsonObj.getString("businessDate"), mesProductReport::setBusinessDate);
+		parseReportDate(jsonObj.getString("completeDate"), mesProductReport::setCompleteDate);
+		parseReportDate(jsonObj.getString("actualRcvTime"), mesProductReport::setActualRcvTime);
+		return mesProductReport;
+	}
+
+	private void parseReportDate(String dateStr, java.util.function.Consumer<Date> setter) throws ParseException {
+		if (StringUtils.isBlank(dateStr)) {
+			return;
+		}
+		String pattern = dateStr.length() > 10 ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd";
+		setter.accept(new SimpleDateFormat(pattern).parse(dateStr));
+	}
+
+}

+ 86 - 68
src/main/java/com/jeesite/modules/scheduling/GpyXmlScheduling.java

@@ -58,6 +58,7 @@ public class GpyXmlScheduling {
      */
     @Scheduled(fixedRate = 120000)
     private void scanAndImport() {
+        logger.info("触发光谱仪扫描");
         try {
             File dir = new File(XML_DIR);
             if (!dir.exists() || !dir.isDirectory()) {
@@ -253,11 +254,8 @@ public class GpyXmlScheduling {
                 for (int e = 0; e < elements.getLength(); e++) {
                     Element elem = (Element) elements.item(e);
                     String elemName = elem.getAttribute("ElementName");
-                    String elemType = elem.getAttribute("Type");
-                    // 跳过伪元素(如 OR)
-                    if ("Pseudo".equals(elemType)) continue;
 
-                    // 取 StatType="Reported" 且 Type="Conc" 的 ResultValue
+                    // 取 StatType="Reported" 的 ResultValue(ConcTypeCorr / Other,含 OR 伪元素)
                     String reportedValue = getReportedValue(elem);
 
                     replicateValues.computeIfAbsent(elemName, k -> new ArrayList<>());
@@ -286,10 +284,8 @@ public class GpyXmlScheduling {
                     for (int e = 0; e < elements.getLength(); e++) {
                         Element elem = (Element) elements.item(e);
                         String elemName = elem.getAttribute("ElementName");
-                        String elemType = elem.getAttribute("Type");
-                        if ("Pseudo".equals(elemType)) continue;
 
-                        // 取 Mean 值(StatType="Mean", Type="Conc")
+                        // 取 Mean 值(ConcTypeCorr / Other)
                         String meanValue = getMeanValue(elem);
                         meanMap.put(elemName, meanValue);
 
@@ -311,9 +307,9 @@ public class GpyXmlScheduling {
                 meas.setIsNewRecord(true);
                 meas.setMesGpyId(gpyId);
                 meas.setElementName(elemName);
-                meas.setReplicate0(reps.size() > 0 ? reps.get(0) : "");
-                meas.setReplicate1(reps.size() > 1 ? reps.get(1) : "");
-                meas.setMean(meanMap.getOrDefault(elemName, ""));
+                meas.setReplicate0(formatMeasurementValue(reps.size() > 0 ? reps.get(0) : ""));
+                meas.setReplicate1(formatMeasurementValue(reps.size() > 1 ? reps.get(1) : ""));
+                meas.setMean(formatMeasurementValue(meanMap.getOrDefault(elemName, "")));
                 meas.setLowerAcceptanceLimit(lowerLimitMap.getOrDefault(elemName, ""));
                 meas.setUpperAcceptanceLimit(upperLimitMap.getOrDefault(elemName, ""));
 
@@ -327,28 +323,22 @@ public class GpyXmlScheduling {
     }
 
     /**
-     * 从 Element 节点中获取 Type="Conc" + StatType="Reported" 的 ResultValue
-     * 根据 CalibrationStatus 添加 < 或 > 前缀:
-     *   UnderRange → "<值"
-     *   OverRange  → ">值"
-     *   InRange    → "值"
+     * 从 Element 节点中获取 StatType="Reported" 的 ResultValue(ConcTypeCorr / Other)
      */
     private String getReportedValue(Element elem) {
-        NodeList results = elem.getElementsByTagName("ElementResult");
-        for (int i = 0; i < results.getLength(); i++) {
-            Element result = (Element) results.item(i);
-            if ("Conc".equals(result.getAttribute("Type"))
-                    && "Reported".equals(result.getAttribute("StatType"))) {
-                String value = getTextContent(result, "ResultValue");
-                String calibStatus = result.getAttribute("CalibrationStatus");
-                return addRangePrefix(value, calibStatus);
-            }
+        String value = findResultValue(elem, "Reported");
+        if (!value.isEmpty()) {
+            return value;
         }
-        // 如果没有 Reported,取 StatType="None" 且 Type="Conc"
+        return findResultValue(elem, "None");
+    }
+
+    private String findResultValue(Element elem, String statType) {
+        NodeList results = elem.getElementsByTagName("ElementResult");
         for (int i = 0; i < results.getLength(); i++) {
             Element result = (Element) results.item(i);
-            if ("Conc".equals(result.getAttribute("Type"))
-                    && "None".equals(result.getAttribute("StatType"))) {
+            if (isSupportedResultType(result.getAttribute("Type"), elem)
+                    && statType.equals(result.getAttribute("StatType"))) {
                 String value = getTextContent(result, "ResultValue");
                 String calibStatus = result.getAttribute("CalibrationStatus");
                 return addRangePrefix(value, calibStatus);
@@ -357,6 +347,13 @@ public class GpyXmlScheduling {
         return "";
     }
 
+    private boolean isSupportedResultType(String type, Element elem) {
+        if ("Other".equals(type)) {
+            return "Pseudo".equals(elem.getAttribute("Type"));
+        }
+        return "ConcTypeCorr".equals(type);
+    }
+
     /**
      * 根据 CalibrationStatus 添加范围前缀,并将科学计数法转为普通小数
      */
@@ -375,6 +372,27 @@ public class GpyXmlScheduling {
     }
 
     /**
+     * 测量值格式化为 5 位小数(保留 &lt; / &gt; 前缀)
+     */
+    private String formatMeasurementValue(String value) {
+        if (value == null || value.isEmpty()) {
+            return "";
+        }
+        String prefix = "";
+        String number = value;
+        if (value.startsWith("<") || value.startsWith(">")) {
+            prefix = value.substring(0, 1);
+            number = value.substring(1);
+        }
+        try {
+            java.math.BigDecimal bd = new java.math.BigDecimal(number);
+            return prefix + bd.setScale(5, java.math.RoundingMode.HALF_UP).toPlainString();
+        } catch (NumberFormatException e) {
+            return value;
+        }
+    }
+
+    /**
      * 将科学计数法字符串转为普通小数字符串
      * 例如: "5E-05" → "0.00005", "1.2E-03" → "0.0012"
      * 非科学计数法的直接原样返回
@@ -401,61 +419,61 @@ public class GpyXmlScheduling {
      * 优先取 StatType="Reported"(修剪后的报告值),与 replicate 取值逻辑一致
      */
     private String getMeanValue(Element elem) {
-        NodeList results = elem.getElementsByTagName("ElementResult");
-        // 优先取 Reported
-        for (int i = 0; i < results.getLength(); i++) {
-            Element result = (Element) results.item(i);
-            if ("Conc".equals(result.getAttribute("Type"))
-                    && "Reported".equals(result.getAttribute("StatType"))) {
-                String value = getTextContent(result, "ResultValue");
-                String calibStatus = result.getAttribute("CalibrationStatus");
-                return addRangePrefix(value, calibStatus);
-            }
-        }
-        // 没有 Reported 则取 Mean
-        for (int i = 0; i < results.getLength(); i++) {
-            Element result = (Element) results.item(i);
-            if ("Conc".equals(result.getAttribute("Type"))
-                    && "Mean".equals(result.getAttribute("StatType"))) {
-                String value = getTextContent(result, "ResultValue");
-                String calibStatus = result.getAttribute("CalibrationStatus");
-                return addRangePrefix(value, calibStatus);
-            }
+        String value = findResultValue(elem, "Reported");
+        if (!value.isEmpty()) {
+            return value;
         }
-        return "";
+        return findResultValue(elem, "Mean");
     }
 
     /**
      * 从 Element 节点中获取 AcceptanceLimit
-     * 优先从 StatType="Reported" 的 ResultValueLimits 中取
+     * 按优先级从多种 ElementResult 类型中提取可接受上下限
      * @return [lowerAcceptanceLimit, upperAcceptanceLimit]
      */
     private String[] getAcceptanceLimits(Element elem) {
-        String lower = "";
-        String upper = "";
-
         NodeList results = elem.getElementsByTagName("ElementResult");
+        String[][] typeStatPairs = {
+                {"ConcTypeCorr", "Reported"},
+                {"ConcTypeCorr", "Mean"},
+        };
+        for (String[] pair : typeStatPairs) {
+            String[] limits = findAcceptanceLimits(results, pair[0], pair[1]);
+            if (!limits[0].isEmpty() || !limits[1].isEmpty()) {
+                return limits;
+            }
+        }
+        return new String[]{"", ""};
+    }
+
+    private String[] findAcceptanceLimits(NodeList results, String type, String statType) {
         for (int i = 0; i < results.getLength(); i++) {
             Element result = (Element) results.item(i);
-            if ("Conc".equals(result.getAttribute("Type"))
-                    && "Reported".equals(result.getAttribute("StatType"))) {
-                NodeList limits = result.getElementsByTagName("ResultValueLimit");
-                for (int j = 0; j < limits.getLength(); j++) {
-                    Element limit = (Element) limits.item(j);
-                    String limitType = limit.getAttribute("Type");
-                    String limitValue = convertScientificToDecimal(limit.getTextContent().trim());
-                    if ("LowerAcceptanceLimit".equals(limitType)) {
-                        lower = limitValue;
-                    } else if ("UpperAcceptanceLimit".equals(limitType)) {
-                        upper = limitValue;
-                    }
-                }
-                // 找到 Reported 就不再继续
-                if (!lower.isEmpty() || !upper.isEmpty()) {
-                    break;
+            if (type.equals(result.getAttribute("Type"))
+                    && statType.equals(result.getAttribute("StatType"))) {
+                String[] limits = extractAcceptanceLimitsFromResult(result);
+                if (!limits[0].isEmpty() || !limits[1].isEmpty()) {
+                    return limits;
                 }
             }
         }
+        return new String[]{"", ""};
+    }
+
+    private String[] extractAcceptanceLimitsFromResult(Element result) {
+        String lower = "";
+        String upper = "";
+        NodeList limits = result.getElementsByTagName("ResultValueLimit");
+        for (int j = 0; j < limits.getLength(); j++) {
+            Element limit = (Element) limits.item(j);
+            String limitType = limit.getAttribute("Type");
+            String limitValue = convertScientificToDecimal(limit.getTextContent().trim());
+            if ("LowerAcceptanceLimit".equals(limitType)) {
+                lower = limitValue;
+            } else if ("UpperAcceptanceLimit".equals(limitType)) {
+                upper = limitValue;
+            }
+        }
         return new String[]{lower, upper};
     }
 

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

@@ -139,6 +139,19 @@ $("#inputForm").validate({
 });
 
 // 元素测量数据表格
+function formatMeas5(val) {
+	if (val == null || val === '') return '';
+	var prefix = '';
+	var num = val;
+	if (val.charAt(0) === '<' || val.charAt(0) === '>') {
+		prefix = val.charAt(0);
+		num = val.substring(1);
+	}
+	var n = parseFloat(num);
+	if (isNaN(n)) return val;
+	return prefix + n.toFixed(5);
+}
+
 $('#measDataGrid').dataGrid({
 	url: "${ctx}/mes/mesGpy/measListData?mesGpyId=${mesGpy.id}",
 	method: "get",
@@ -147,9 +160,9 @@ $('#measDataGrid').dataGrid({
 	rownumbers: true,
 	columnModel: [
 		{header:'${text("元素名称")}', name:'elementName', width:100, align:"center", frozen:true},
-		{header:'${text("第一次测量")}', name:'replicate0', width:180, align:"right"},
-		{header:'${text("第二次测量")}', name:'replicate1', width:180, align:"right"},
-		{header:'${text("平均值")}', name:'mean', width:180, align:"right"},
+		{header:'${text("第一次测量")}', name:'replicate0', width:180, align:"right", formatter: function(val){ return formatMeas5(val); }},
+		{header:'${text("第二次测量")}', name:'replicate1', width:180, align:"right", formatter: function(val){ return formatMeas5(val); }},
+		{header:'${text("平均值")}', name:'mean', width:180, align:"right", formatter: function(val){ return formatMeas5(val); }},
 		{header:'${text("可接受最小值")}', name:'lowerAcceptanceLimit', width:120, align:"right"},
 		{header:'${text("可接受最大值")}', name:'upperAcceptanceLimit', width:120, align:"right"}
 	]

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

@@ -70,6 +70,18 @@
 </div>
 <% } %>
 <script>
+function formatMeas5(val) {
+	if (val == null || val === '') return '';
+	var prefix = '';
+	var num = val;
+	if (val.charAt(0) === '<' || val.charAt(0) === '>') {
+		prefix = val.charAt(0);
+		num = val.substring(1);
+	}
+	var n = parseFloat(num);
+	if (isNaN(n)) return val;
+	return prefix + n.toFixed(5);
+}
 // 初始化DataGrid对象
 $('#dataGrid').dataGrid({
 	searchForm: $("#searchForm"),
@@ -78,9 +90,9 @@ $('#dataGrid').dataGrid({
 			return '<a href="${ctx}/mes/mesGpyMeas/form?id='+row.id+'" class="btnList" data-title="${text("编辑光谱仪元素表")}">'+(val||row.id)+'</a>';
 		}},
 		{header:'${text("元素名称")}', name:'elementName', index:'a.element_name', width:150, align:"left"},
-		{header:'${text("第一次测量结果")}', name:'replicate0', index:'a.replicate_0', width:150, align:"left"},
-		{header:'${text("第二次测量结果")}', name:'replicate1', index:'a.replicate_1', width:150, align:"left"},
-		{header:'${text("平均值")}', name:'mean', index:'a.mean', width:150, align:"left"},
+		{header:'${text("第一次测量结果")}', name:'replicate0', index:'a.replicate_0', width:150, align:"right", formatter: function(val){ return formatMeas5(val); }},
+		{header:'${text("第二次测量结果")}', name:'replicate1', index:'a.replicate_1', width:150, align:"right", formatter: function(val){ return formatMeas5(val); }},
+		{header:'${text("平均值")}', name:'mean', index:'a.mean', width:150, align:"right", formatter: function(val){ return formatMeas5(val); }},
 		{header:'${text("可接受最大值")}', name:'lowerAcceptanceLimit', index:'a.lower_acceptance_limit', width:150, align:"left"},
 		{header:'${text("可接受最小值")}', name:'upperAcceptanceLimit', index:'a.upper_acceptance_limit', width:150, align:"left"},
 		{header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){

+ 18 - 0
src/main/resources/views/modules/mes/mesProductReportForm.html

@@ -116,6 +116,24 @@
 							</div>
 						</div>
 					</div>
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4" title="">
+								<span class="required hide">*</span> ${text('报废类型')}:<i class="fa icon-question hide"></i></label>
+							<div class="col-sm-8">
+								<#form:input path="scrapReason" maxlength="64" class="form-control"/>
+							</div>
+						</div>
+					</div>
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4" title="">
+								<span class="required hide">*</span> ${text('入库批号')}:<i class="fa icon-question hide"></i></label>
+							<div class="col-sm-8">
+								<#form:input path="lotCode" maxlength="64" class="form-control"/>
+							</div>
+						</div>
+					</div>
 				</div>
 				<div class="row">
 					<div class="col-xs-12">

+ 15 - 2
src/main/resources/views/modules/mes/mesProductReportList.html

@@ -86,6 +86,18 @@
 					</div>
 				</div>
 				<div class="form-group">
+					<label class="control-label">${text('报废类型')}:</label>
+					<div class="control-inline">
+						<#form:input path="scrapReason" maxlength="64" class="form-control width-120"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('入库批号')}:</label>
+					<div class="control-inline">
+						<#form:input path="lotCode" maxlength="64" class="form-control width-120"/>
+					</div>
+				</div>
+				<div class="form-group">
 					<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
 					<button type="reset" class="btn btn-default btn-sm">${text('重置')}</button>
 				</div>
@@ -115,11 +127,12 @@ $('#dataGrid').dataGrid({
 		{header:'${text("报废数量")}', name:'scrapQty', index:'a.scrap_qty', width:150, align:"left"},
 		{header:'${text("备注")}', name:'remark', index:'a.remark', width:150, align:"left"},
 		{header:'${text("更新时间")}', name:'updateDate', index:'a.update_date', width:150, align:"center"},
-		{header:'${text("操作")}', name:'actions', width:150, formatter: function(val, obj, row, act){
+		{header:'${text("报废类型")}', name:'scrapReason', index:'a.scrap_reason', width:150, align:"left"},
+		{header:'${text("入库批号")}', name:'lotCode', index:'a.lot_code', width:150, align:"left"},
+		{header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){
 			var actions = [];
 			//<% if(hasPermi('mes:mesProductReport:edit')){ %>
 				actions.push('<a href="${ctx}/mes/mesProductReport/form?id='+row.id+'" class="btnList" title="${text("编辑完工报告")}"><i class="fa fa-pencil"></i></a>&nbsp;');
-				actions.push('<a href="${ctx}/mes/mesProductReport/mesToErp?id='+row.id+'" class="btnList" title="${text("同步ERP")}" data-confirm="${text("确认要同步该完工报告到ERP吗?")}"><i class="fa fa-cloud-upload"></i></a>&nbsp;');
 				actions.push('<a href="${ctx}/mes/mesProductReport/delete?id='+row.id+'" class="btnList" title="${text("删除完工报告")}" data-confirm="${text("确认要删除该完工报告吗?")}"><i class="fa fa-trash-o"></i></a>&nbsp;');
 			//<% } %>
 			return actions.join('');