Kaynağa Gözat

拉铆工序联动打印机

wangxichen 1 hafta önce
ebeveyn
işleme
3f943ac464

+ 18 - 7
db/ddl-piecework-date.sql

@@ -1,28 +1,33 @@
+-- ============================================
+-- T09 计件统计修复 - DDL
 -- 目标库:mes_cloud_t09
+-- 说明:Online DDL 加字段不锁表,UPDATE 回填建议低峰执行
+-- 历史数据范围:只处理 2026-06-01 至今
+-- ============================================
 
+USE mes_cloud_t09;
+
+-- 步骤1:新增 piecework_date 字段(Online DDL,不锁表)
 ALTER TABLE mes_product_record
 ADD COLUMN piecework_date DATETIME NULL
 COMMENT '计件时间(首次加工完成时间)'
 AFTER update_date;
 
-CREATE INDEX idx_piecework_oprno_date
-ON mes_product_record(oprno, piecework_date, craft);
-
+-- 步骤2:历史数据回填(只处理 2026-06-01 至今)
+-- 用 update_date 作为近似首次完成时间;早于 2026-06-01 的旧数据不回填
 UPDATE mes_product_record
 SET piecework_date = update_date
 WHERE craft = '100000'
   AND content NOT IN ('UDF', '')
-  AND piecework_date IS NULL;
+  AND piecework_date IS NULL
+  AND update_date >= '2026-06-01';
 
+-- 步骤3:验证
 SELECT
-    COUNT(*) as total,
-    SUM(CASE WHEN piecework_date IS NOT NULL THEN 1 ELSE 0 END) as has_piecework_date,
-    SUM(CASE WHEN content = 'UDF' THEN 1 ELSE 0 END) as udf_count
+    COUNT(*) AS total_100000,
+    SUM(CASE WHEN piecework_date IS NOT NULL THEN 1 ELSE 0 END) AS has_piecework_date,
+    SUM(CASE WHEN content = 'UDF' THEN 1 ELSE 0 END) AS udf_count,
+    SUM(CASE WHEN content = 'OK'  THEN 1 ELSE 0 END) AS ok_count,
+    SUM(CASE WHEN content = 'NG'  THEN 1 ELSE 0 END) AS ng_count
 FROM mes_product_record
 WHERE craft = '100000';

+ 39 - 77
src/main/java/com/jeesite/modules/mes/entity/MesProductLaser.java

@@ -13,6 +13,12 @@ import java.util.Date;
 
 /**
  * 钢印码和客户码对应关系Entity
+ *
+ * 打印状态字段说明:
+ *   cmtPrintStatus:   CMT 打印状态,     0=待打印, 1=已打印
+ *   rivetPrintStatus: 拉铆打印状态,     0=无需, 1=待打印, 2=已打印, 3=打印失败
+ *   printMsg:         打印失败原因(CMT/拉铆共用)
+ *
  * @author hzd
  * @version 2026-07-08
  */
@@ -23,8 +29,10 @@ import java.util.Date;
 		@Column(name="steel_sn", attrName="steelSn", label="钢印码", queryType=QueryType.LIKE),
 		@Column(name="oprno", attrName="oprno", label="工位号", queryType=QueryType.LIKE),
 		@Column(name="line_sn", attrName="lineSn", label="产线编号", queryType=QueryType.LIKE),
-		@Column(name="print_status", attrName="printStatus", label="打印状态", queryType=QueryType.EQ),
-		@Column(name="print_time", attrName="printTime", label="打印完成时间"),
+		@Column(name="cmt_print_status", attrName="cmtPrintStatus", label="CMT打印状态", queryType=QueryType.EQ),
+		@Column(name="rivet_print_status", attrName="rivetPrintStatus", label="拉铆打印状态", queryType=QueryType.EQ),
+		@Column(name="cmt_print_time", attrName="cmtPrintTime", label="CMT打印完成时间"),
+		@Column(name="rivet_print_time", attrName="rivetPrintTime", label="拉铆打印完成时间"),
 		@Column(name="print_msg", attrName="printMsg", label="打印失败原因"),
 		@Column(name="create_by", attrName="createBy", label="创建人", isUpdate=false, isQuery=false),
 		@Column(name="create_date", attrName="createDate", label="创建时间", isUpdate=false, isQuery=false, isUpdateForce=true),
@@ -40,8 +48,10 @@ public class MesProductLaser extends DataEntity<MesProductLaser> {
 	private String steelSn;
 	private String oprno;
 	private String lineSn;
-	private Integer printStatus;
-	private Date printTime;
+	private Integer cmtPrintStatus;
+	private Integer rivetPrintStatus;
+	private Date cmtPrintTime;
+	private Date rivetPrintTime;
 	private String printMsg;
 	private Date createDate;
 	private Date updateDate;
@@ -64,126 +74,78 @@ public class MesProductLaser extends DataEntity<MesProductLaser> {
 	}
 
 	@Size(min=0, max=20, message="产品标识长度不能超过 20 个字符")
-	public String getProdCode() {
-		return prodCode;
-	}
-
-	public void setProdCode(String prodCode) {
-		this.prodCode = prodCode;
-	}
+	public String getProdCode() { return prodCode; }
+	public void setProdCode(String prodCode) { this.prodCode = prodCode; }
 
 	@Size(min=0, max=64, message="客户码长度不能超过 64 个字符")
-	public String getCustomerSn() {
-		return customerSn;
-	}
-
-	public void setCustomerSn(String customerSn) {
-		this.customerSn = customerSn;
-	}
+	public String getCustomerSn() { return customerSn; }
+	public void setCustomerSn(String customerSn) { this.customerSn = customerSn; }
 
 	@Size(min=0, max=64, message="钢印码长度不能超过 64 个字符")
-	public String getSteelSn() {
-		return steelSn;
-	}
-
-	public void setSteelSn(String steelSn) {
-		this.steelSn = steelSn;
-	}
+	public String getSteelSn() { return steelSn; }
+	public void setSteelSn(String steelSn) { this.steelSn = steelSn; }
 
 	@Size(min=0, max=20, message="工位号长度不能超过 20 个字符")
-	public String getOprno() {
-		return oprno;
-	}
-
-	public void setOprno(String oprno) {
-		this.oprno = oprno;
-	}
+	public String getOprno() { return oprno; }
+	public void setOprno(String oprno) { this.oprno = oprno; }
 
 	@Size(min=0, max=50, message="产线编号长度不能超过 50 个字符")
-	public String getLineSn() {
-		return lineSn;
-	}
+	public String getLineSn() { return lineSn; }
+	public void setLineSn(String lineSn) { this.lineSn = lineSn; }
 
-	public void setLineSn(String lineSn) {
-		this.lineSn = lineSn;
-	}
-
-	public Integer getPrintStatus() {
-		return printStatus;
-	}
+	public Integer getCmtPrintStatus() { return cmtPrintStatus; }
+	public void setCmtPrintStatus(Integer cmtPrintStatus) { this.cmtPrintStatus = cmtPrintStatus; }
 
-	public void setPrintStatus(Integer printStatus) {
-		this.printStatus = printStatus;
-	}
+	public Integer getRivetPrintStatus() { return rivetPrintStatus; }
+	public void setRivetPrintStatus(Integer rivetPrintStatus) { this.rivetPrintStatus = rivetPrintStatus; }
 
 	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-	public Date getPrintTime() {
-		return printTime;
-	}
+	public Date getCmtPrintTime() { return cmtPrintTime; }
+	public void setCmtPrintTime(Date cmtPrintTime) { this.cmtPrintTime = cmtPrintTime; }
 
-	public void setPrintTime(Date printTime) {
-		this.printTime = printTime;
-	}
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	public Date getRivetPrintTime() { return rivetPrintTime; }
+	public void setRivetPrintTime(Date rivetPrintTime) { this.rivetPrintTime = rivetPrintTime; }
 
 	@Size(min=0, max=200, message="打印失败原因长度不能超过 200 个字符")
-	public String getPrintMsg() {
-		return printMsg;
-	}
-
-	public void setPrintMsg(String printMsg) {
-		this.printMsg = printMsg;
-	}
+	public String getPrintMsg() { return printMsg; }
+	public void setPrintMsg(String printMsg) { this.printMsg = printMsg; }
 
 	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
 	@Override
-	public Date getCreateDate() {
-		return createDate;
-	}
+	public Date getCreateDate() { return createDate; }
 
 	@Override
-	public void setCreateDate(Date createDate) {
-		this.createDate = createDate;
-	}
+	public void setCreateDate(Date createDate) { this.createDate = createDate; }
 
 	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
 	@Override
-	public Date getUpdateDate() {
-		return updateDate;
-	}
+	public Date getUpdateDate() { return updateDate; }
 
 	@Override
-	public void setUpdateDate(Date updateDate) {
-		this.updateDate = updateDate;
-	}
+	public void setUpdateDate(Date updateDate) { this.updateDate = updateDate; }
 
 	public Date getCreateDate_gte() {
 		return sqlMap.getWhere().getValue("create_date", QueryType.GTE);
 	}
-
 	public void setCreateDate_gte(Date createDate) {
 		sqlMap.getWhere().and("create_date", QueryType.GTE, createDate);
 	}
-
 	public Date getCreateDate_lte() {
 		return sqlMap.getWhere().getValue("create_date", QueryType.LTE);
 	}
-
 	public void setCreateDate_lte(Date createDate) {
 		sqlMap.getWhere().and("create_date", QueryType.LTE, createDate);
 	}
-
 	public Date getUpdateDate_gte() {
 		return sqlMap.getWhere().getValue("update_date", QueryType.GTE);
 	}
-
 	public void setUpdateDate_gte(Date updateDate) {
 		sqlMap.getWhere().and("update_date", QueryType.GTE, updateDate);
 	}
-
 	public Date getUpdateDate_lte() {
 		return sqlMap.getWhere().getValue("update_date", QueryType.LTE);
 	}
-
 	public void setUpdateDate_lte(Date updateDate) {
 		sqlMap.getWhere().and("update_date", QueryType.LTE, updateDate);
 	}

+ 33 - 16
src/main/java/com/jeesite/modules/mes/service/MesLaserService.java

@@ -74,10 +74,10 @@ public class MesLaserService {
 
 		String customerSn = genCustomerSn(prodCode, prefix);
 
-		// 绑定保存,同时入待打印队列 print_status=0
+		// 绑定保存。cmt_print_status / rivet_print_status 走表的 DEFAULT 0
 		jdbcTemplate.update(
-				"INSERT INTO mes_product_laser(id,prod_code,customer_sn,steel_sn,oprno,line_sn,print_status,create_by,create_date,update_by,update_date) " +
-						"VALUES (?,?,?,?,?,?,0,?,now(),?,now())",
+				"INSERT INTO mes_product_laser(id,prod_code,customer_sn,steel_sn,oprno,line_sn,create_by,create_date,update_by,update_date) " +
+						"VALUES (?,?,?,?,?,?,?,now(),?,now())",
 				UUID.randomUUID().toString().replace("-", ""), prodCode, customerSn, steelSn, oprno, lineSn, userCode, userCode);
 
 		return customerSn;
@@ -94,16 +94,32 @@ public class MesLaserService {
 	}
 
 	/**
-	 * 拉取指定产线的待打印记录(print_status=0),按创建时间升序 FIFO。
+	 * PDA 扫钢印打流转卡接口:把钢印码对应的记录标记为拉铆待打印(rivet_print_status=1)
+	 * @return UPDATE 影响行数,0 表示 steel_sn 不存在
 	 */
-	public List<Map<String, Object>> listPending(String lineSn, int limit) {
+	public int markRvtPrint(String steelSn) {
+		return jdbcTemplate.update(
+				"UPDATE mes_product_laser SET rivet_print_status=1, print_msg=NULL, update_date=now() "
+						+ "WHERE steel_sn=? AND rivet_print_status IN (0, 3)",
+				steelSn);
+	}
+
+	/**
+	 * 拉取拉铆待处理记录 —— 包含待打印(=1)和已失败(=3),按创建时间升序 FIFO。
+	 * 客户端区分处理:
+	 *   status=1 → 自动打印
+	 *   status=3 → 只在 UI 显示,等待人工点重打(重打时经由 PrintHelper 直接打,走 ackPrint 更新回 2)
+	 */
+	public List<Map<String, Object>> listPending(int limit) {
 		if (limit <= 0 || limit > 100) {
 			limit = 10;
 		}
-		String sql = "SELECT customer_sn, steel_sn, prod_code, oprno, line_sn, create_date "
-				+ "FROM mes_product_laser WHERE print_status = 0 AND line_sn = ? "
-				+ "ORDER BY create_date ASC LIMIT " + limit;
-		List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, lineSn);
+		// pending(=1) 优先,失败(=3) 排后;同状态内按 create_date FIFO
+		String sql = "SELECT customer_sn, steel_sn, prod_code, oprno, line_sn, "
+				+ "rivet_print_status, print_msg, create_date "
+				+ "FROM mes_product_laser WHERE rivet_print_status IN (1, 3) "
+				+ "ORDER BY rivet_print_status ASC, create_date ASC LIMIT " + limit;
+		List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql);
 		if (rows == null) {
 			return new ArrayList<>();
 		}
@@ -111,20 +127,21 @@ public class MesLaserService {
 	}
 
 	/**
-	 * 客户端上报打印结果。
-	 * result=OK:print_status 0→1,写入 print_time;
-	 * result=NG:保留 0,记录 print_msg,下次轮询会再拉到,允许重试
+	 * 客户端上报拉铆打印结果。
+	 * result=OK:rivet_print_status 1→2,写入 rivet_print_time;
+	 * result=NG:rivet_print_status 1→3,记录 print_msg,需要人工介入
 	 */
 	public int ackPrint(String customerSn, String result, String msg) {
 		if ("OK".equalsIgnoreCase(result)) {
 			return jdbcTemplate.update(
-					"UPDATE mes_product_laser SET print_status=1, print_time=now(), print_msg=NULL, update_date=now() "
-							+ "WHERE customer_sn=? AND print_status=0",
+					"UPDATE mes_product_laser SET rivet_print_status=2, rivet_print_time=now(), print_msg=NULL, update_date=now() "
+							+ "WHERE customer_sn=? AND rivet_print_status IN (1, 3)",
 					customerSn);
 		}
-		// NG 不改状态,只更新失败原因
+		// NG:标记为失败态并记录原因,兼容首次失败和重打失败
 		return jdbcTemplate.update(
-				"UPDATE mes_product_laser SET print_msg=?, update_date=now() WHERE customer_sn=?",
+				"UPDATE mes_product_laser SET rivet_print_status=3, print_msg=?, update_date=now() "
+						+ "WHERE customer_sn=? AND rivet_print_status IN (1, 3)",
 				msg == null ? "" : msg, customerSn);
 	}
 

+ 40 - 11
src/main/java/com/jeesite/modules/mes/web/MesLaserController.java

@@ -151,29 +151,57 @@ public class MesLaserController extends BaseController {
 	}
 
 	/**
-	 * OP40 客户端拉取待打印列表(FIFO,按创建时间升序)。
-	 * @param lineSn 产线编号,例 T9
+	 * PDA 扫钢印打流转卡:把 steelSn 对应记录标记为拉铆待打印。
+	 * 请求参数(form):steelSn
+	 * 返回 data.rows = UPDATE 影响行数,0 表示 steelSn 不存在
+	 */
+	@PostMapping(value = "markRvtPrint")
+	@ResponseBody
+	public CommonResp<Map<String, Object>> markRvtPrint(HttpServletRequest request) {
+		CommonResp<Map<String, Object>> resp = new CommonResp<>();
+		String steelSn = request.getParameter("steelSn");
+		if (StringUtils.isEmpty(steelSn)) {
+			resp.setResult(Global.FALSE);
+			resp.setMessage("steelSn 不能为空");
+			return resp;
+		}
+		try {
+			int rows = mesLaserService.markRvtPrint(steelSn);
+			Map<String, Object> data = new HashMap<>();
+			data.put("rows", rows);
+			resp.setData(data);
+			if (rows > 0) {
+				resp.setResult(Global.TRUE);
+				resp.setMessage("已标记为拉铆待打印");
+			} else {
+				resp.setResult(Global.FALSE);
+				resp.setMessage("未找到该钢印码或状态不允许重复标记:" + steelSn);
+			}
+		} catch (Exception e) {
+			logger.error("markRvtPrint 失败:steelSn=" + steelSn, e);
+			resp.setResult(Global.FALSE);
+			resp.setMessage("标记失败:" + e.getMessage());
+		}
+		return resp;
+	}
+
+	/**
+	 * 拉铆打印代理客户端拉取待打印列表(FIFO,按创建时间升序)。
 	 * @param limit 单次条数上限,默认 10
 	 * @return data 列表元素含 customerSn / steelSn / prodCode / oprno / createDate
 	 */
 	@RequestMapping(value = "pendingPrint")
 	@ResponseBody
 	public CommonResp<List<Map<String, Object>>> pendingPrint(
-			@RequestParam("lineSn") String lineSn,
 			@RequestParam(value = "limit", required = false, defaultValue = "10") int limit) {
 		CommonResp<List<Map<String, Object>>> resp = new CommonResp<>();
-		if (StringUtils.isEmpty(lineSn)) {
-			resp.setResult(Global.FALSE);
-			resp.setMessage("lineSn 不能为空");
-			return resp;
-		}
 		try {
-			List<Map<String, Object>> list = mesLaserService.listPending(lineSn, limit);
+			List<Map<String, Object>> list = mesLaserService.listPending(limit);
 			resp.setData(list);
 			resp.setResult(Global.TRUE);
 			resp.setMessage("OK");
 		} catch (Exception e) {
-			logger.error("拉取待打印列表失败:lineSn=" + lineSn, e);
+			logger.error("拉取待打印列表失败", e);
 			resp.setResult(Global.FALSE);
 			resp.setMessage("拉取失败:" + e.getMessage());
 		}
@@ -181,8 +209,9 @@ public class MesLaserController extends BaseController {
 	}
 
 	/**
-	 * OP40 客户端上报打印结果。
+	 * 拉铆打印代理上报打印结果。
 	 * 请求参数(form):customerSn / result=OK|NG / msg(失败原因,可选)
+	 * OK → rivet_print_status 1→2;NG → rivet_print_status 1→3。
 	 */
 	@PostMapping(value = "printAck")
 	@ResponseBody

+ 51 - 6
src/main/resources/views/modules/mes/mesProductLaserList.html

@@ -32,6 +32,28 @@
 					</div>
 				</div>
 				<div class="form-group">
+					<label class="control-label">${text('CMT状态')}:</label>
+					<div class="control-inline">
+						<#form:select path="cmtPrintStatus" class="form-control width-120">
+							<option value="">${text('全部')}</option>
+							<option value="0">${text('待打印')}</option>
+							<option value="1">${text('已打印')}</option>
+						</#form:select>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('拉铆状态')}:</label>
+					<div class="control-inline">
+						<#form:select path="rivetPrintStatus" class="form-control width-120">
+							<option value="">${text('全部')}</option>
+							<option value="0">${text('无需打印')}</option>
+							<option value="1">${text('待打印')}</option>
+							<option value="2">${text('已打印')}</option>
+							<option value="3">${text('打印失败')}</option>
+						</#form:select>
+					</div>
+				</div>
+				<div class="form-group">
 					<label class="control-label">${text('更新时间')}:</label>
 					<div class="control-inline">
 						<#form:input path="updateDate_gte" readonly="true" maxlength="20" class="form-control laydate width-datetime"
@@ -53,15 +75,38 @@
 </div>
 <% } %>
 <script>
+// CMT 打印状态渲染
+function fmtCmtStatus(v){
+	if (v == null || v === '') return '';
+	if (v == 0 || v === '0') return '<span class="label label-default">待打印</span>';
+	if (v == 1 || v === '1') return '<span class="label label-success">已打印</span>';
+	return v;
+}
+// 拉铆打印状态渲染
+function fmtRivetStatus(v){
+	if (v == null || v === '') return '';
+	if (v == 0 || v === '0') return '<span class="label label-default">无需</span>';
+	if (v == 1 || v === '1') return '<span class="label label-warning">待打印</span>';
+	if (v == 2 || v === '2') return '<span class="label label-success">已打印</span>';
+	if (v == 3 || v === '3') return '<span class="label label-danger">打印失败</span>';
+	return v;
+}
 $('#dataGrid').dataGrid({
 	searchForm: $("#searchForm"),
 	columnModel: [
-		{header:'${text("产品标识")}', name:'prodCode', index:'a.prod_code', width:120, align:"center"},
-		{header:'${text("钢印码")}', name:'steelSn', index:'a.steel_sn', width:180, align:"center"},
-		{header:'${text("客户码")}', name:'customerSn', index:'a.customer_sn', width:220, align:"center"},
-		{header:'${text("产线编号")}', name:'lineSn', index:'a.line_sn', width:120, align:"center"},
-		{header:'${text("创建时间")}', name:'createDate', index:'a.create_date', width:160, align:"center"},
-		{header:'${text("更新时间")}', name:'updateDate', index:'a.update_date', width:160, align:"center"}
+		{header:'${text("产品标识")}', name:'prodCode', index:'a.prod_code', width:80, align:"center"},
+		{header:'${text("钢印码")}', name:'steelSn', index:'a.steel_sn', width:160, align:"center"},
+		{header:'${text("客户码")}', name:'customerSn', index:'a.customer_sn', width:200, align:"center"},
+		{header:'${text("产线")}', name:'lineSn', index:'a.line_sn', width:70, align:"center"},
+		{header:'${text("CMT")}', name:'cmtPrintStatus', index:'a.cmt_print_status', width:80, align:"center",
+			formatter: function(v){ return fmtCmtStatus(v); }},
+		{header:'${text("拉铆")}', name:'rivetPrintStatus', index:'a.rivet_print_status', width:80, align:"center",
+			formatter: function(v){ return fmtRivetStatus(v); }},
+		{header:'${text("CMT打印时间")}', name:'cmtPrintTime', index:'a.cmt_print_time', width:140, align:"center"},
+		{header:'${text("拉铆打印时间")}', name:'rivetPrintTime', index:'a.rivet_print_time', width:140, align:"center"},
+		{header:'${text("失败原因")}', name:'printMsg', index:'a.print_msg', width:180, align:"left"},
+		{header:'${text("创建时间")}', name:'createDate', index:'a.create_date', width:140, align:"center"},
+		{header:'${text("更新时间")}', name:'updateDate', index:'a.update_date', width:140, align:"center"}
 	],
 	ajaxSuccess: function(data){
 	}