Explorar o código

Merge branch 'master' of http://121.36.46.135:3000/mes-ljz/mescloud

sunxianglong hai 2 meses
pai
achega
acb4aa240c

+ 147 - 2
src/main/java/com/jeesite/modules/mes/web/MesProductScreenController.java

@@ -3,10 +3,23 @@ package com.jeesite.modules.mes.web;
 import com.jeesite.common.config.Global;
 import com.jeesite.common.entity.Page;
 import com.jeesite.common.lang.StringUtils;
+import com.jeesite.common.mybatis.mapper.query.QueryType;
 import com.jeesite.common.web.BaseController;
+import com.jeesite.modules.mes.entity.MesDevice;
+import com.jeesite.modules.mes.entity.MesDeviceAlarm;
+import com.jeesite.modules.mes.entity.MesLineProcess;
+import com.jeesite.modules.mes.entity.MesProductRecord;
 import com.jeesite.modules.mes.entity.MesProductScreen;
 import com.jeesite.modules.mes.resp.CommonResp;
+import com.jeesite.modules.mes.service.MesDeviceAlarmService;
+import com.jeesite.modules.mes.service.MesDeviceService;
+import com.jeesite.modules.mes.service.MesLineProcessService;
+import com.jeesite.modules.mes.service.MesProductRecordService;
 import com.jeesite.modules.mes.service.MesProductScreenService;
+import com.jeesite.modules.mes.service.MesRepairService;
+import com.jeesite.modules.mes.service.MesScrapService;
+import com.jeesite.modules.sys.entity.EmpUser;
+import com.jeesite.modules.sys.service.EmpUserService;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
@@ -19,11 +32,14 @@ import org.springframework.web.bind.annotation.ResponseBody;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
- * 大屏展示配置Controller
+ * 大屏展示配置Controller(含大屏专用只读接口,无权限注解)
  * @author mes
  * @version 2026-08-12
  */
@@ -33,6 +49,20 @@ public class MesProductScreenController extends BaseController {
 
 	@Autowired
 	private MesProductScreenService mesProductScreenService;
+	@Autowired
+	private MesDeviceAlarmService mesDeviceAlarmService;
+	@Autowired
+	private MesRepairService mesRepairService;
+	@Autowired
+	private MesScrapService mesScrapService;
+	@Autowired
+	private MesLineProcessService mesLineProcessService;
+	@Autowired
+	private MesProductRecordService mesProductRecordService;
+	@Autowired
+	private MesDeviceService mesDeviceService;
+	@Autowired
+	private EmpUserService empUserService;
 
 	@ModelAttribute
 	public MesProductScreen get(String id, boolean isNewRecord) {
@@ -92,6 +122,121 @@ public class MesProductScreenController extends BaseController {
 		return resp;
 	}
 
+	/** 大屏:待处理设备报警列表(替代 mesDeviceAlarm/listData) */
+	@RequestMapping(value = "alertList")
+	@ResponseBody
+	public Page<MesDeviceAlarm> alertList(MesDeviceAlarm mesDeviceAlarm,
+			HttpServletRequest request, HttpServletResponse response) {
+		if (StringUtils.isBlank(mesDeviceAlarm.getState())) {
+			mesDeviceAlarm.setState("0");
+		}
+		mesDeviceAlarm.setPage(new Page<>(request, response));
+		return mesDeviceAlarmService.findPage(mesDeviceAlarm);
+	}
+
+	/** 大屏:当天返修/报废数量(替代 mesRepair/listData + mesScrap/listData 计数) */
+	@RequestMapping(value = "repairScrapCount")
+	@ResponseBody
+	public CommonResp<Map<String, Object>> repairScrapCount() {
+		CommonResp<Map<String, Object>> resp = new CommonResp<>();
+		LocalDate today = LocalDate.now();
+		DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+		String start = today.format(fmt) + " 00:00:00";
+		String end = today.plusDays(1).format(fmt) + " 00:00:00";
+		Map<String, Object> data = new HashMap<>();
+		try {
+			Long repair = mesRepairService.countByDateRange(start, end);
+			Long scrap = mesScrapService.countByDateRange(start, end);
+			data.put("repairCount", repair == null ? 0 : repair);
+			data.put("scrapCount", scrap == null ? 0 : scrap);
+			resp.setData(data);
+			resp.setResult(Global.TRUE);
+		} catch (Exception e) {
+			data.put("repairCount", 0);
+			data.put("scrapCount", 0);
+			resp.setData(data);
+			resp.setResult(Global.FALSE);
+			resp.setMessage(e.getMessage());
+		}
+		return resp;
+	}
+
+	/** 大屏:工序列表(替代 mesLineProcess/listData) */
+	@RequestMapping(value = "lineProcessList")
+	@ResponseBody
+	public Page<MesLineProcess> lineProcessList(MesLineProcess mesLineProcess,
+			HttpServletRequest request, HttpServletResponse response) {
+		mesLineProcess.getSqlMap().getWhere().and("a.pid", QueryType.IS_NULL, "");
+		mesLineProcess.setPage(new Page<>(request, response));
+		return mesLineProcessService.findPage(mesLineProcess);
+	}
+
+	/** 大屏:生产记录分页(替代 mesProductRecord/listData) */
+	@RequestMapping(value = "recordList")
+	@ResponseBody
+	public Page<MesProductRecord> recordList(MesProductRecord mesProductRecord,
+			HttpServletRequest request, HttpServletResponse response) {
+		mesProductRecord.setPage(new Page<>(request, response));
+		mesProductRecord.getSqlMap().getOrder().setOrderBy("a.id DESC");
+		return mesProductRecordService.findPage(mesProductRecord);
+	}
+
+	/** 大屏:生产部人数(替代 sys/empUser/listData) */
+	@RequestMapping(value = "peopleCount")
+	@ResponseBody
+	public CommonResp<Map<String, Object>> peopleCount() {
+		CommonResp<Map<String, Object>> resp = new CommonResp<>();
+		Map<String, Object> data = new HashMap<>();
+		int peopleCount = 0;
+		try {
+			List<EmpUser> users = empUserService.findUserList(new EmpUser());
+			if (users != null) {
+				for (EmpUser u : users) {
+					if (u == null || u.getEmployee() == null || u.getEmployee().getOffice() == null) {
+						continue;
+					}
+					String officeName = u.getEmployee().getOffice().getOfficeName();
+					if (officeName == null) {
+						continue;
+					}
+					String normalized = officeName.replaceAll("\\s", "");
+					if ("生产部".equals(normalized) || normalized.matches(".*[/\\\\>]+生产部$")) {
+						peopleCount++;
+					}
+				}
+			}
+			data.put("peopleCount", peopleCount);
+			resp.setData(data);
+			resp.setResult(Global.TRUE);
+		} catch (Exception e) {
+			data.put("peopleCount", 0);
+			resp.setData(data);
+			resp.setResult(Global.FALSE);
+			resp.setMessage(e.getMessage());
+		}
+		return resp;
+	}
+
+	/** 大屏:设备数量(替代 mesDevice/listData 计数) */
+	@RequestMapping(value = "deviceCount")
+	@ResponseBody
+	public CommonResp<Map<String, Object>> deviceCount() {
+		CommonResp<Map<String, Object>> resp = new CommonResp<>();
+		Map<String, Object> data = new HashMap<>();
+		try {
+			Long count = mesDeviceService.findCount(new MesDevice());
+			data.put("deviceCount", count == null ? 0 : count);
+			resp.setData(data);
+			resp.setResult(Global.TRUE);
+		} catch (Exception e) {
+			data.put("deviceCount", 0);
+			resp.setData(data);
+			resp.setResult(Global.FALSE);
+			resp.setMessage(e.getMessage());
+		}
+		return resp;
+	}
+
 	private int parseIntSafe(String val) {
 		if (StringUtils.isBlank(val)) {
 			return 0;
@@ -102,4 +247,4 @@ public class MesProductScreenController extends BaseController {
 			return 0;
 		}
 	}
-}
+}

+ 12 - 4
src/main/java/com/jeesite/modules/tianHong/entity/MachineAttrHistory.java

@@ -3,13 +3,16 @@ package com.jeesite.modules.tianHong.entity;
 import java.util.Date;
 import java.util.List;
 
+import org.springframework.format.annotation.DateTimeFormat;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.jeesite.common.entity.DataEntity;
 
 /**
  * 设备历史属性Entity
- * 对应 bzh 库 machine_attr_history 表,关联 crutemphistory、alutemphistory、mouldtemphistory、pressurehistory、EleConsumeHistory
+ * 列表/详情由主库 tianhong_backup 按秒展开填充
  * @author ljr
- * @version 2026-07-07
+ * @version 2026-08-15
  */
 public class MachineAttrHistory extends DataEntity<MachineAttrHistory> {
 
@@ -23,7 +26,8 @@ public class MachineAttrHistory extends DataEntity<MachineAttrHistory> {
 	private String prePres;				// 预压压力设定(attr='PrePres')
 	private String holdPressure;		// 保压开始信号(attr='HoldPressure')
 	private String moldFillingPre;		// 充型压力(attr='moldFillingPre')
-	private Date attrEventTime;			// machine_attr_history 最后更新时间
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	private Date attrEventTime;			// 属性更新时间(秒)
 
 	// ===== 关联表:crutemphistory 炉膛温度 =====
 	private String cruTemp;				// 炉膛温度
@@ -47,8 +51,12 @@ public class MachineAttrHistory extends DataEntity<MachineAttrHistory> {
 	private String eleConsume;			// 电量
 	private Date eleConsumeTime;		// 电量时间
 
-	// ===== 查询参数:时间范围 =====
+	// ===== 查询参数:时间范围(映射 tianhong_backup.create_date)=====
+	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
 	private Date beginTime;
+	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
 	private Date endTime;
 
 	// ===== 手工分页(避免拦截器对复杂 SQL 二次 LIMIT)=====

+ 319 - 172
src/main/java/com/jeesite/modules/tianHong/service/MachineAttrHistoryService.java

@@ -1,15 +1,20 @@
 package com.jeesite.modules.tianHong.service;
 
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
+import javax.sql.DataSource;
+
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -21,15 +26,36 @@ import com.jeesite.modules.tianHong.entity.MachineAttrHistory;
 
 /**
  * 设备历史属性Service
- * @author ljr
- * @version 2026-07-07
+ * 读主库 mes_cloud_ljr.tianhong_backup(eqp_id + create_date),
+ * create_date 分钟个位向下取整为 10min 窗口末尾,# 串拆成 600 秒展示。
  */
 @Service
 @Transactional(readOnly = true)
 public class MachineAttrHistoryService extends CrudService<MachineAttrHistoryDao, MachineAttrHistory> {
 
+	private static final int POINT_COUNT = 600;
+	/** 列表查询 create_date 跨度上限(毫秒):30 分钟 */
+	private static final long MAX_RANGE_MS = 30L * 60L * 1000L;
+
+	private static final String SELECT_BACKUP_SQL =
+			"SELECT eqp_id, create_date,"
+			+ " cru_temp, alu_temp, mould_info,"
+			+ " theory_pressure, real_pressure, pressure_level, ele_consume,"
+			+ " alu_val, pressurize_time, cooling_time, pre_pres, hold_pressure, mold_filling_pre,"
+			+ " point_count"
+			+ " FROM tianhong_backup"
+			+ " WHERE eqp_id = ?"
+			+ " AND create_date >= ?"
+			+ " AND create_date <= ?"
+			+ " ORDER BY create_date DESC";
+
+	@Autowired
+	private DataSource dataSource;
+
 	/**
-	 * 分页:必须先选设备;按 event_time 倒序,每页默认 20;关联表按「秒」补齐
+	 * 分页:eqpId + beginTime(上传时间/create_date);秒级正序。
+	 * 无截止时间时自动按上传时间起向后取最长 30 分钟;
+	 * 展示从上传时间减 3 秒起,更早的秒隐藏。
 	 */
 	@Override
 	public Page<MachineAttrHistory> findPage(MachineAttrHistory machineAttrHistory) {
@@ -38,11 +64,13 @@ public class MachineAttrHistoryService extends CrudService<MachineAttrHistoryDao
 			page = new Page<>();
 			machineAttrHistory.setPage(page);
 		}
-		if (StringUtils.isBlank(machineAttrHistory.getEqpId())) {
+		if (StringUtils.isBlank(machineAttrHistory.getEqpId())
+				|| machineAttrHistory.getBeginTime() == null) {
 			page.setList(Collections.emptyList());
 			page.setCount(0);
 			return page;
 		}
+
 		int pageSize = page.getPageSize() > 0 ? page.getPageSize() : 20;
 		int pageNo = page.getPageNo() > 0 ? page.getPageNo() : 1;
 		if (pageSize > 100) {
@@ -51,217 +79,336 @@ public class MachineAttrHistoryService extends CrudService<MachineAttrHistoryDao
 		page.setPageSize(pageSize);
 		page.setPageNo(pageNo);
 
-		machineAttrHistory.setPage(null);
-		Long count = dao.findHistoryCount(machineAttrHistory);
-		page.setCount(count == null ? 0L : count);
+		Date uploadTime = machineAttrHistory.getBeginTime();
+		Date begin = uploadTime;
+		Date end = machineAttrHistory.getEndTime();
+		if (end == null || end.before(begin)) {
+			end = new Date(begin.getTime() + MAX_RANGE_MS);
+		}
+		if (end.getTime() - begin.getTime() > MAX_RANGE_MS) {
+			end = new Date(begin.getTime() + MAX_RANGE_MS);
+		}
 
-		machineAttrHistory.setLimit(pageSize);
-		machineAttrHistory.setOffset((pageNo - 1) * pageSize);
-		List<MachineAttrHistory> list = dao.findList(machineAttrHistory);
-		fillRelatedBySecond(machineAttrHistory.getEqpId(), list);
-		page.setList(list);
+		List<BackupRow> backups;
+		try {
+			backups = loadBackups(machineAttrHistory.getEqpId(), begin, end);
+		} catch (Exception e) {
+			logger.error("load tianhong_backup failed, eqpId={}", machineAttrHistory.getEqpId(), e);
+			page.setList(Collections.emptyList());
+			page.setCount(0);
+			return page;
+		}
+
+		List<MachineAttrHistory> all = expandBackups(backups);
+		// 展示从上传时间减 3 秒开始,前面隐藏
+		final long showFromMs = uploadTime.getTime() - 3L * 1000L;
+		List<MachineAttrHistory> filtered = new ArrayList<>();
+		for (MachineAttrHistory row : all) {
+			if (row.getAttrEventTime() != null && row.getAttrEventTime().getTime() >= showFromMs) {
+				filtered.add(row);
+			}
+		}
+		filtered.sort(Comparator.comparing(MachineAttrHistory::getAttrEventTime,
+				Comparator.nullsLast(Comparator.naturalOrder())));
+
+		page.setCount(filtered.size());
+		int from = (pageNo - 1) * pageSize;
+		if (from >= filtered.size()) {
+			page.setList(Collections.emptyList());
+			return page;
+		}
+		int to = Math.min(from + pageSize, filtered.size());
+		page.setList(filtered.subList(from, to));
 		return page;
 	}
 
-	/**
-	 * 查询列表(必须带设备号)
-	 */
 	@Override
 	public List<MachineAttrHistory> findList(MachineAttrHistory machineAttrHistory) {
-		if (StringUtils.isBlank(machineAttrHistory.getEqpId())) {
+		Page<MachineAttrHistory> page = findPage(machineAttrHistory);
+		return page.getList() == null ? Collections.emptyList() : page.getList();
+	}
+
+	/**
+	 * 属性明细:主库 backup 展开后按秒取槽位拼 attrList
+	 */
+	public List<MachineAttrHistory> findAttrList(MachineAttrHistory machineAttrHistory) {
+		MachineAttrHistory one = resolveOneSecond(machineAttrHistory);
+		if (one == null || one.getAttrList() == null) {
 			return Collections.emptyList();
 		}
-		if (machineAttrHistory.getLimit() == null) {
-			machineAttrHistory.setLimit(20);
-		}
-		if (machineAttrHistory.getOffset() == null) {
-			machineAttrHistory.setOffset(0);
-		}
-		List<MachineAttrHistory> list = dao.findList(machineAttrHistory);
-		fillRelatedBySecond(machineAttrHistory.getEqpId(), list);
-		return list;
+		return one.getAttrList();
 	}
 
 	/**
-	 * 用本页时间点截断到秒,按索引范围补齐温压电量(避免大表按秒 JOIN)
+	 * 单条详情(含 attrList),全部主库
 	 */
-	private void fillRelatedBySecond(String eqpId, List<MachineAttrHistory> list) {
-		if (StringUtils.isBlank(eqpId) || list == null || list.isEmpty()) {
-			return;
-		}
-		Set<Date> secSet = new LinkedHashSet<>();
-		for (MachineAttrHistory row : list) {
-			if (row.getAttrEventTime() != null) {
-				secSet.add(truncateToSecond(row.getAttrEventTime()));
-			}
+	@Override
+	public MachineAttrHistory get(MachineAttrHistory machineAttrHistory) {
+		if (StringUtils.isBlank(machineAttrHistory.getEqpId())) {
+			return null;
 		}
-		if (secSet.isEmpty()) {
-			return;
+		MachineAttrHistory entity = resolveOneSecond(machineAttrHistory);
+		if (entity == null) {
+			entity = new MachineAttrHistory();
+			entity.setEqpId(machineAttrHistory.getEqpId());
+			entity.setAttrList(Collections.emptyList());
 		}
-		List<Date> secList = new ArrayList<>(secSet);
-
-		Map<Long, MachineAttrHistory> cruMap = latestBySec(dao.findCruBySecList(eqpId, secList));
-		Map<Long, MachineAttrHistory> aluMap = latestBySec(dao.findAluBySecList(eqpId, secList));
-		Map<Long, MachineAttrHistory> mouldMap = latestBySec(dao.findMouldBySecList(eqpId, secList));
-		Map<Long, MachineAttrHistory> pressMap = latestBySec(dao.findPressureBySecList(eqpId, secList));
-		Map<Long, MachineAttrHistory> eleMap = latestBySec(dao.findEleBySecList(eqpId, secList));
+		return entity;
+	}
 
-		for (MachineAttrHistory row : list) {
-			if (row.getAttrEventTime() == null) {
-				continue;
-			}
-			Long key = secKey(row.getAttrEventTime());
-			MachineAttrHistory cru = cruMap.get(key);
-			if (cru != null) {
-				row.setCruTemp(cru.getCruTemp());
-				row.setCruTempTime(cru.getCruTempTime());
-			}
-			MachineAttrHistory alu = aluMap.get(key);
-			if (alu != null) {
-				row.setAluTemp(alu.getAluTemp());
-				row.setAluTempTime(alu.getAluTempTime());
-			}
-			MachineAttrHistory mould = mouldMap.get(key);
-			if (mould != null) {
-				row.setMouldInfo(mould.getMouldInfo());
-				row.setMouldTempTime(mould.getMouldTempTime());
+	private MachineAttrHistory resolveOneSecond(MachineAttrHistory query) {
+		if (query == null || StringUtils.isBlank(query.getEqpId())) {
+			return null;
+		}
+		Date target = resolveTargetSecond(query);
+		if (target == null) {
+			return null;
+		}
+		Date windowStart = floorTo10Minutes(target);
+		Date windowEnd = addMinutes(windowStart, 10);
+		Date createFrom = windowEnd;
+		Date createTo = addMinutes(windowEnd, 10);
+		List<BackupRow> backups;
+		try {
+			backups = loadBackups(query.getEqpId(), createFrom, createTo);
+			if (backups.isEmpty()) {
+				backups = loadBackups(query.getEqpId(), addMinutes(windowEnd, -1), createTo);
 			}
-			MachineAttrHistory press = pressMap.get(key);
-			if (press != null) {
-				row.setTheoryPressure(press.getTheoryPressure());
-				row.setRealPressure(press.getRealPressure());
-				row.setPressureLevel(press.getPressureLevel());
-				row.setPressureTime(press.getPressureTime());
+		} catch (Exception e) {
+			logger.error("load tianhong_backup for detail failed, eqpId={}", query.getEqpId(), e);
+			return null;
+		}
+		BackupRow hit = null;
+		for (BackupRow row : backups) {
+			Date we = floorTo10Minutes(row.createDate);
+			Date ws = addMinutes(we, -10);
+			if (!target.before(ws) && target.before(we)) {
+				hit = row;
+				break;
 			}
-			MachineAttrHistory ele = eleMap.get(key);
-			if (ele != null) {
-				row.setEleConsume(ele.getEleConsume());
-				row.setEleConsumeTime(ele.getEleConsumeTime());
+		}
+		if (hit == null && !backups.isEmpty()) {
+			hit = backups.get(0);
+			windowEnd = floorTo10Minutes(hit.createDate);
+			windowStart = addMinutes(windowEnd, -10);
+		} else if (hit != null) {
+			windowEnd = floorTo10Minutes(hit.createDate);
+			windowStart = addMinutes(windowEnd, -10);
+		} else {
+			return null;
+		}
+		int idx = secIndex(target, windowStart);
+		if (idx < 0 || idx >= POINT_COUNT) {
+			return null;
+		}
+		MachineAttrHistory row = buildSecondRow(query.getEqpId(), windowStart, idx, hit);
+		row.setAttrList(buildAttrList(row));
+		return row;
+	}
+
+	private Date resolveTargetSecond(MachineAttrHistory query) {
+		if (StringUtils.isNotBlank(query.getEventTimeKey())) {
+			Date parsed = parseEventTimeKey(query.getEventTimeKey());
+			if (parsed != null) {
+				return truncateToSecond(parsed);
 			}
 		}
+		if (query.getEventTime() != null) {
+			return truncateToSecond(query.getEventTime());
+		}
+		if (query.getAttrEventTime() != null) {
+			return truncateToSecond(query.getAttrEventTime());
+		}
+		if (query.getBeginTime() != null) {
+			return truncateToSecond(query.getBeginTime());
+		}
+		return null;
 	}
 
-	/** 同一秒多条时保留 EventTime 最新的一条 */
-	private Map<Long, MachineAttrHistory> latestBySec(List<MachineAttrHistory> rows) {
-		Map<Long, MachineAttrHistory> map = new HashMap<>();
-		if (rows == null) {
-			return map;
+	/** eventTimeKey: yyyyMMddHHmmss + 微秒6位,共20位 */
+	private Date parseEventTimeKey(String key) {
+		String k = StringUtils.trim(key);
+		if (k == null || k.length() < 14) {
+			return null;
 		}
-		for (MachineAttrHistory row : rows) {
-			Date t = row.getEventTime();
-			if (t == null) {
-				t = row.getCruTempTime();
-			}
-			if (t == null) {
-				t = row.getAluTempTime();
-			}
-			if (t == null) {
-				t = row.getMouldTempTime();
-			}
-			if (t == null) {
-				t = row.getPressureTime();
-			}
-			if (t == null) {
-				t = row.getEleConsumeTime();
-			}
-			if (t == null) {
-				continue;
-			}
-			Long key = secKey(t);
-			MachineAttrHistory old = map.get(key);
-			if (old == null || compareTime(t, resolveTime(old)) > 0) {
-				if (row.getEventTime() == null) {
-					row.setEventTime(t);
+		try {
+			return new SimpleDateFormat("yyyyMMddHHmmss").parse(k.substring(0, 14));
+		} catch (Exception e) {
+			return null;
+		}
+	}
+
+	private List<BackupRow> loadBackups(String eqpId, Date createFrom, Date createTo) throws Exception {
+		List<BackupRow> list = new ArrayList<>();
+		try (Connection conn = dataSource.getConnection();
+				PreparedStatement ps = conn.prepareStatement(SELECT_BACKUP_SQL)) {
+			ps.setString(1, eqpId);
+			ps.setTimestamp(2, new Timestamp(createFrom.getTime()));
+			ps.setTimestamp(3, new Timestamp(createTo.getTime()));
+			try (ResultSet rs = ps.executeQuery()) {
+				while (rs.next()) {
+					BackupRow row = new BackupRow();
+					row.eqpId = rs.getString("eqp_id");
+					Timestamp cd = rs.getTimestamp("create_date");
+					row.createDate = cd == null ? null : new Date(cd.getTime());
+					row.cruTemp = rs.getString("cru_temp");
+					row.aluTemp = rs.getString("alu_temp");
+					row.mouldInfo = rs.getString("mould_info");
+					row.theoryPressure = rs.getString("theory_pressure");
+					row.realPressure = rs.getString("real_pressure");
+					row.pressureLevel = rs.getString("pressure_level");
+					row.eleConsume = rs.getString("ele_consume");
+					row.aluVal = rs.getString("alu_val");
+					row.pressurizeTime = rs.getString("pressurize_time");
+					row.coolingTime = rs.getString("cooling_time");
+					row.prePres = rs.getString("pre_pres");
+					row.holdPressure = rs.getString("hold_pressure");
+					row.moldFillingPre = rs.getString("mold_filling_pre");
+					list.add(row);
 				}
-				map.put(key, row);
 			}
 		}
-		return map;
+		return list;
 	}
 
-	private Date resolveTime(MachineAttrHistory row) {
-		if (row.getEventTime() != null) {
-			return row.getEventTime();
-		}
-		if (row.getCruTempTime() != null) {
-			return row.getCruTempTime();
+	private List<MachineAttrHistory> expandBackups(List<BackupRow> backups) {
+		List<MachineAttrHistory> result = new ArrayList<>();
+		if (backups == null || backups.isEmpty()) {
+			return result;
 		}
-		if (row.getAluTempTime() != null) {
-			return row.getAluTempTime();
+		for (BackupRow backup : backups) {
+			if (backup.createDate == null) {
+				continue;
+			}
+			Date windowEnd = floorTo10Minutes(backup.createDate);
+			Date windowStart = addMinutes(windowEnd, -10);
+			for (int i = 0; i < POINT_COUNT; i++) {
+				result.add(buildSecondRow(backup.eqpId, windowStart, i, backup));
+			}
 		}
-		if (row.getMouldTempTime() != null) {
-			return row.getMouldTempTime();
+		return result;
+	}
+
+	private MachineAttrHistory buildSecondRow(String eqpId, Date windowStart, int idx, BackupRow backup) {
+		Date sec = new Date(windowStart.getTime() + idx * 1000L);
+		MachineAttrHistory row = new MachineAttrHistory();
+		row.setEqpId(eqpId);
+		row.setAttrEventTime(sec);
+		row.setEventTime(sec);
+		row.setId(eqpId + "_" + formatEventTimeKey(sec));
+
+		row.setCruTemp(slot(backup.cruTemp, idx));
+		row.setAluTemp(slot(backup.aluTemp, idx));
+		row.setMouldInfo(slot(backup.mouldInfo, idx));
+		row.setTheoryPressure(slot(backup.theoryPressure, idx));
+		row.setRealPressure(slot(backup.realPressure, idx));
+		row.setPressureLevel(slot(backup.pressureLevel, idx));
+		row.setEleConsume(slot(backup.eleConsume, idx));
+		row.setAluVal(slot(backup.aluVal, idx));
+		row.setPressurizeTime(slot(backup.pressurizeTime, idx));
+		row.setCoolingTime(slot(backup.coolingTime, idx));
+		row.setPrePres(slot(backup.prePres, idx));
+		row.setHoldPressure(slot(backup.holdPressure, idx));
+		row.setMoldFillingPre(slot(backup.moldFillingPre, idx));
+
+		row.setCruTempTime(sec);
+		row.setAluTempTime(sec);
+		row.setMouldTempTime(sec);
+		row.setPressureTime(sec);
+		row.setEleConsumeTime(sec);
+		return row;
+	}
+
+	private List<MachineAttrHistory> buildAttrList(MachineAttrHistory row) {
+		List<MachineAttrHistory> list = new ArrayList<>();
+		addAttr(list, row, "alu_val", row.getAluVal());
+		addAttr(list, row, "pressurizeTime", row.getPressurizeTime());
+		addAttr(list, row, "coolingTime", row.getCoolingTime());
+		addAttr(list, row, "PrePres", row.getPrePres());
+		addAttr(list, row, "HoldPressure", row.getHoldPressure());
+		addAttr(list, row, "moldFillingPre", row.getMoldFillingPre());
+		addAttr(list, row, "cruTemp", row.getCruTemp());
+		addAttr(list, row, "aluTemp", row.getAluTemp());
+		addAttr(list, row, "mouldInfo", row.getMouldInfo());
+		addAttr(list, row, "theoryPressure", row.getTheoryPressure());
+		addAttr(list, row, "realPressure", row.getRealPressure());
+		addAttr(list, row, "pressureLevel", row.getPressureLevel());
+		addAttr(list, row, "eleConsume", row.getEleConsume());
+		return list;
+	}
+
+	private void addAttr(List<MachineAttrHistory> list, MachineAttrHistory src, String attr, String val) {
+		MachineAttrHistory item = new MachineAttrHistory();
+		item.setEqpId(src.getEqpId());
+		item.setAttr(attr);
+		item.setVal(val == null ? "" : val);
+		item.setEventTime(src.getAttrEventTime());
+		list.add(item);
+	}
+
+	private static String slot(String series, int idx) {
+		if (series == null) {
+			return "";
 		}
-		if (row.getPressureTime() != null) {
-			return row.getPressureTime();
+		String[] parts = series.split("#", -1);
+		if (idx < 0 || idx >= parts.length) {
+			return "";
 		}
-		return row.getEleConsumeTime();
+		return parts[idx] == null ? "" : parts[idx];
 	}
 
-	private int compareTime(Date a, Date b) {
-		if (a == null && b == null) {
-			return 0;
-		}
-		if (a == null) {
+	private static int secIndex(Date eventTime, Date windowStart) {
+		long ms = eventTime.getTime() - windowStart.getTime();
+		if (ms < 0) {
 			return -1;
 		}
-		if (b == null) {
-			return 1;
-		}
-		return a.compareTo(b);
+		return (int) (ms / 1000L);
 	}
 
-	private Date truncateToSecond(Date date) {
+	/** 分钟个位向下取整(对 10 取整),秒/毫秒清零 → 10min 窗口锚点/末尾 */
+	private static Date floorTo10Minutes(Date date) {
 		Calendar cal = Calendar.getInstance();
 		cal.setTime(date);
+		cal.set(Calendar.SECOND, 0);
 		cal.set(Calendar.MILLISECOND, 0);
+		int minute = cal.get(Calendar.MINUTE);
+		cal.set(Calendar.MINUTE, minute - (minute % 10));
 		return cal.getTime();
 	}
 
-	/** 用 epoch 秒做 map key,避免 Date 毫秒差异 */
-	private Long secKey(Date date) {
-		return date.getTime() / 1000L;
+	private static Date addMinutes(Date date, int minutes) {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.add(Calendar.MINUTE, minutes);
+		return cal.getTime();
 	}
 
-	/**
-	 * 查询指定设备的所有属性明细(用于详情展示)
-	 */
-	public List<MachineAttrHistory> findAttrList(MachineAttrHistory machineAttrHistory) {
-		if (StringUtils.isBlank(machineAttrHistory.getEqpId())) {
-			return Collections.emptyList();
-		}
-		return dao.findAttrList(machineAttrHistory);
+	private static Date truncateToSecond(Date date) {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.set(Calendar.MILLISECOND, 0);
+		return cal.getTime();
 	}
 
-	/**
-	 * 获取单条数据(按 eqp_id + 快照时间查询属性明细)
-	 * 须带 eventTimeKey / eventTime / beginTime~endTime,禁止无时间条件全量加载
-	 */
-	@Override
-	public MachineAttrHistory get(MachineAttrHistory machineAttrHistory) {
-		if (StringUtils.isBlank(machineAttrHistory.getEqpId())) {
-			return null;
-		}
-		MachineAttrHistory entity = new MachineAttrHistory();
-		entity.setEqpId(machineAttrHistory.getEqpId());
-		entity.setAttrEventTime(machineAttrHistory.getEventTime());
-		entity.setEventTimeKey(machineAttrHistory.getEventTimeKey());
-		boolean hasTime = StringUtils.isNotBlank(machineAttrHistory.getEventTimeKey())
-				|| machineAttrHistory.getEventTime() != null
-				|| machineAttrHistory.getBeginTime() != null
-				|| machineAttrHistory.getEndTime() != null;
-		if (!hasTime) {
-			entity.setAttrList(Collections.emptyList());
-			return entity;
-		}
-		MachineAttrHistory queryParam = new MachineAttrHistory();
-		queryParam.setEqpId(machineAttrHistory.getEqpId());
-		queryParam.setEventTimeKey(machineAttrHistory.getEventTimeKey());
-		queryParam.setEventTime(machineAttrHistory.getEventTime());
-		queryParam.setBeginTime(machineAttrHistory.getBeginTime());
-		queryParam.setEndTime(machineAttrHistory.getEndTime());
-		entity.setAttrList(dao.findAttrList(queryParam));
-		return entity;
+	private static String formatEventTimeKey(Date sec) {
+		return new SimpleDateFormat("yyyyMMddHHmmss").format(sec) + "000000";
 	}
 
+	private static final class BackupRow {
+		String eqpId;
+		Date createDate;
+		String cruTemp;
+		String aluTemp;
+		String mouldInfo;
+		String theoryPressure;
+		String realPressure;
+		String pressureLevel;
+		String eleConsume;
+		String aluVal;
+		String pressurizeTime;
+		String coolingTime;
+		String prePres;
+		String holdPressure;
+		String moldFillingPre;
+	}
 }

+ 9 - 4
src/main/java/com/jeesite/modules/tianHong/web/MachineAttrHistoryController.java

@@ -25,9 +25,10 @@ import com.jeesite.modules.tianHong.service.MachineAttrHistoryService;
 
 /**
  * 设备历史属性Controller
- * 对应 bzh 库 machine_attr_history 表,关联温度/压力/电量等历史表
+ * 列表/详情读主库 mes_cloud_ljr.tianhong_backup(eqp_id + create_date),
+ * 按 create_date 分钟个位取整得到 10min 窗口末尾后拆 # 为秒级数据。
  * @author ljr
- * @version 2026-07-07
+ * @version 2026-08-15
  */
 @Controller
 @RequestMapping(value = "${adminPath}/tianHong/machineAttrHistory")
@@ -86,12 +87,16 @@ public class MachineAttrHistoryController extends BaseController {
 	@RequiresPermissions("tianHong:machineAttrHistory:view")
 	@RequestMapping(value = { "list", "" })
 	public String list(MachineAttrHistory machineAttrHistory, Model model) {
+		// 默认上传时间=当前
+		if (machineAttrHistory.getBeginTime() == null) {
+			machineAttrHistory.setBeginTime(new Date());
+		}
 		model.addAttribute("machineAttrHistory", machineAttrHistory);
 		return "modules/tianHong/machineAttrHistoryList";
 	}
 
 	/**
-	 * 查询列表数据:须传设备号;按 event_time 倒序分页(默认每页 20)
+	 * 查询列表数据:须传设备号 + 上传时间(create_date);秒级正序分页
 	 */
 	@RequiresPermissions("tianHong:machineAttrHistory:view")
 	@RequestMapping(value = "listData")
@@ -103,7 +108,7 @@ public class MachineAttrHistoryController extends BaseController {
 	}
 
 	/**
-	 * 查询指定设备的所有属性明细(用于详情模态框)
+	 * 查询指定设备某一秒的属性明细(主库 backup)
 	 */
 	@RequiresPermissions("tianHong:machineAttrHistory:view")
 	@RequestMapping(value = "attrListData")

+ 21 - 0
src/main/resources/config/application.yml

@@ -684,6 +684,7 @@ shiro:
     ${adminPath}/mes/mesProductRecord/ccdcheck = anon
     ${adminPath}/mes/mesProductRecord/updateOprno = anon
     ${adminPath}/mes/mesProductRecord/checkQuality = anon
+    ${adminPath}/mes/mesProductTj/upload = anon
     ${adminPath}/mes/mesProduct/alarmData = anon
     ${adminPath}/mes/mesProcessAlarm/add = anon
     ${adminPath}/mes/mesProductRecord/checkDJ = anon
@@ -773,6 +774,26 @@ shiro:
     ${adminPath}/sys/empUser/listData= anon
     ${adminPath}/mes/mesProductVoltage/add= anon
     
+    ${adminPath}/mes/mesProduct/screen = anon
+    ${adminPath}/mes/mesProduct/screen3 = anon
+    ${adminPath}/mes/mesProduct/screen5 = anon
+    ${adminPath}/mes/mesProduct/screen6 = anon
+    ${adminPath}/mes/mesProduct/hourlyUpDownOkData = anon
+    ${adminPath}/mes/mesProduct/defectTypeData = anon
+    ${adminPath}/mes/mesProductScreen/latestConfig = anon
+    ${adminPath}/mes/mesProductScreen/alertList = anon
+    ${adminPath}/mes/mesProductScreen/repairScrapCount = anon
+    ${adminPath}/mes/mesProductScreen/lineProcessList = anon
+    ${adminPath}/mes/mesProductScreen/recordList = anon
+    ${adminPath}/mes/mesProductScreen/peopleCount = anon
+    ${adminPath}/mes/mesProductScreen/deviceCount = anon
+    ${adminPath}/mes/mesProductRecord/dayCapacityData = anon
+    ${adminPath}/mes/mesProductRecord/shiftCapacityData = anon
+    ${adminPath}/mes/mesProductRecord/weekQualityData = anon
+    ${adminPath}/mes/mesProductRecord/monthlyProductData = anon
+    ${adminPath}/mes/mesPlan/latestPlanNums = anon
+
+    
     
   
     

+ 53 - 77
src/main/resources/views/modules/mes/mesScreen3.html

@@ -809,6 +809,21 @@
 <script src="${ctxStatic}/jquery-1.11.3.min.js"></script>
 <script>
 (function() {
+	var __screenFail = 0, __screenReloadAt = 0;
+	$(document).ajaxError(function(e, xhr) {
+		if (!xhr) return;
+		var text = xhr.responseText || '';
+		if (xhr.status === 0 || xhr.status === 401 || xhr.status === 403 || xhr.status === 502 || xhr.status === 503
+			|| text.indexOf('login') >= 0 || text.indexOf('登录') >= 0) {
+			__screenFail++;
+			if (__screenFail >= 3 && Date.now() - __screenReloadAt > 60000) {
+				__screenReloadAt = Date.now();
+				location.reload();
+			}
+		}
+	});
+	$(document).ajaxSuccess(function() { __screenFail = 0; });
+
 	function pad(n) { return n < 10 ? '0' + n : '' + n; }
 
 	function updateClock() {
@@ -1473,7 +1488,7 @@
 
 	function updateDeviceAlarmAlertsByApi() {
 		$.ajax({
-			url: '${ctx}/mes/mesDeviceAlarm/listData',
+			url: '${ctx}/mes/mesProductScreen/alertList',
 			type: 'POST',
 			dataType: 'json',
 			data: {
@@ -1602,57 +1617,18 @@
 		return list.length;
 	}
 
-	// 中间卡:返修数量 <- mesRepair/listData;报废数量 <- mesScrap/listData(按当天 updateDate)
+	// 中间卡:返修/报废数量 <- mesProductScreen/repairScrapCount
 	function updateRepairScrapCountByApi() {
-		var dayRange = getCalendarDayUpdateDateRange();
-		var pending = 2;
-		var repairCount = 0;
-		var scrapCount = 0;
-
-		function done() {
-			pending--;
-			if (pending > 0) return;
-			mockData.cards = mockData.cards || {};
-			mockData.cards.repairCount = repairCount;
-			mockData.cards.scrapCount = scrapCount;
-			renderTextData();
-		}
-
 		$.ajax({
-			url: '${ctx}/mes/mesRepair/listData',
+			url: '${ctx}/mes/mesProductScreen/repairScrapCount',
 			type: 'POST',
 			dataType: 'json',
-			data: {
-				pageNo: 1,
-				pageSize: 1,
-				updateDate_gte: dayRange.gte,
-				updateDate_lte: dayRange.lte
-			},
 			success: function(resp) {
-				repairCount = extractPageCount(resp);
-				done();
-			},
-			error: function() {
-				done();
-			}
-		});
-
-		$.ajax({
-			url: '${ctx}/mes/mesScrap/listData',
-			type: 'POST',
-			dataType: 'json',
-			data: {
-				pageNo: 1,
-				pageSize: 1,
-				updateDate_gte: dayRange.gte,
-				updateDate_lte: dayRange.lte
-			},
-			success: function(resp) {
-				scrapCount = extractPageCount(resp);
-				done();
-			},
-			error: function() {
-				done();
+				if (!resp || resp.result !== 'true' || !resp.data) return;
+				mockData.cards = mockData.cards || {};
+				mockData.cards.repairCount = Number(resp.data.repairCount) || 0;
+				mockData.cards.scrapCount = Number(resp.data.scrapCount) || 0;
+				renderTextData();
 			}
 		});
 	}
@@ -1763,39 +1739,39 @@
 
 		function fetchPage() {
 			$.ajax({
-				url: '${ctx}/mes/mesProductRecord/listData',
-				type: 'POST',
-				dataType: 'json',
-				data: {
-					pageNo: pageNo,
-					pageSize: pageSize,
-					oprno: OP_OFFLINE,
-					content: 'OK',
-					updateDate_gte: dayRange.gte,
-					updateDate_lte: dayRange.lte
-				},
-				success: function(resp) {
-					var list = extractList(resp);
-					for (var i = 0; i < list.length; i++) {
-						var r = list[i] || {};
-						if (isOprOfflineFamily(r.oprno) && isRecordContentOk(r.content) && isRecordWithinToday(r, dayRange)) {
-							totalOk++;
-						}
-					}
-					if (list.length === pageSize && pageNo < maxPages) {
-						pageNo++;
-						fetchPage();
-					} else {
-						applyActualProductionCount(totalOk);
+			url: '${ctx}/mes/mesProductScreen/recordList',
+			type: 'POST',
+			dataType: 'json',
+			data: {
+				pageNo: pageNo,
+				pageSize: pageSize,
+				oprno: OP_OFFLINE,
+				content: 'OK',
+				updateDate_gte: dayRange.gte,
+				updateDate_lte: dayRange.lte
+			},
+			success: function(resp) {
+				var list = extractList(resp);
+				for (var i = 0; i < list.length; i++) {
+					var r = list[i] || {};
+					if (isOprOfflineFamily(r.oprno) && isRecordContentOk(r.content) && isRecordWithinToday(r, dayRange)) {
+						totalOk++;
 					}
-				},
-				error: function() {
+				}
+				if (list.length === pageSize && pageNo < maxPages) {
+					pageNo++;
+					fetchPage();
+				} else {
 					applyActualProductionCount(totalOk);
 				}
-			});
-		}
-		fetchPage();
+			},
+			error: function() {
+				applyActualProductionCount(totalOk);
+			}
+		});
 	}
+	fetchPage();
+}
 
 	function matchesOfflineOkRecord(r) {
 		var t = parseRecordDateMs(r.updateDate || r.update_date);
@@ -1835,7 +1811,7 @@
 
 		function fetchPage() {
 			$.ajax({
-				url: '${ctx}/mes/mesProductRecord/listData',
+				url: '${ctx}/mes/mesProductScreen/recordList',
 				type: 'POST',
 				dataType: 'json',
 				data: {

+ 17 - 2
src/main/resources/views/modules/mes/mesScreen4.html

@@ -391,6 +391,21 @@
 <script src="${ctxStatic}/jquery-1.11.3.min.js"></script>
 <script>
 (function() {
+	var __screenFail = 0, __screenReloadAt = 0;
+	$(document).ajaxError(function(e, xhr) {
+		if (!xhr) return;
+		var text = xhr.responseText || '';
+		if (xhr.status === 0 || xhr.status === 401 || xhr.status === 403 || xhr.status === 502 || xhr.status === 503
+			|| text.indexOf('login') >= 0 || text.indexOf('登录') >= 0) {
+			__screenFail++;
+			if (__screenFail >= 3 && Date.now() - __screenReloadAt > 60000) {
+				__screenReloadAt = Date.now();
+				location.reload();
+			}
+		}
+	});
+	$(document).ajaxSuccess(function() { __screenFail = 0; });
+
 	function pad(n) { return n < 10 ? '0' + n : '' + n; }
 
 	function updateClock() {
@@ -867,7 +882,7 @@
 
 	function updateLineProcessTableByApi() {
 		$.ajax({
-			url: '${ctx}/mes/mesLineProcess/listData',
+			url: '${ctx}/mes/mesProductScreen/lineProcessList',
 			type: 'POST',
 			dataType: 'json',
 			data: {
@@ -992,7 +1007,7 @@
 
 	function updateDeviceAlarmAlertsByApi() {
 		$.ajax({
-			url: '${ctx}/mes/mesDeviceAlarm/listData',
+			url: '${ctx}/mes/mesProductScreen/alertList',
 			type: 'POST',
 			dataType: 'json',
 			data: {

+ 17 - 2
src/main/resources/views/modules/mes/mesScreen5.html

@@ -409,6 +409,21 @@
 <script src="${ctxStatic}/jquery-1.11.3.min.js"></script>
 <script>
 (function() {
+	var __screenFail = 0, __screenReloadAt = 0;
+	$(document).ajaxError(function(e, xhr) {
+		if (!xhr) return;
+		var text = xhr.responseText || '';
+		if (xhr.status === 0 || xhr.status === 401 || xhr.status === 403 || xhr.status === 502 || xhr.status === 503
+			|| text.indexOf('login') >= 0 || text.indexOf('登录') >= 0) {
+			__screenFail++;
+			if (__screenFail >= 3 && Date.now() - __screenReloadAt > 60000) {
+				__screenReloadAt = Date.now();
+				location.reload();
+			}
+		}
+	});
+	$(document).ajaxSuccess(function() { __screenFail = 0; });
+
 	function pad(n) { return n < 10 ? '0' + n : '' + n; }
 
 	/** 工位表:每页条数与自动翻页间隔(毫秒) */
@@ -944,7 +959,7 @@
 
 	function updateLineProcessTableByApi() {
 		$.ajax({
-			url: '${ctx}/mes/mesLineProcess/listData',
+			url: '${ctx}/mes/mesProductScreen/lineProcessList',
 			type: 'POST',
 			dataType: 'json',
 			data: {
@@ -1073,7 +1088,7 @@
 
 	function updateDeviceAlarmAlertsByApi() {
 		$.ajax({
-			url: '${ctx}/mes/mesDeviceAlarm/listData',
+			url: '${ctx}/mes/mesProductScreen/alertList',
 			type: 'POST',
 			dataType: 'json',
 			data: {

+ 24 - 35
src/main/resources/views/modules/mes/mesScreen6.html

@@ -398,6 +398,21 @@
 <script src="${ctxStatic}/jquery-1.11.3.min.js"></script>
 <script>
 (function() {
+	var __screenFail = 0, __screenReloadAt = 0;
+	$(document).ajaxError(function(e, xhr) {
+		if (!xhr) return;
+		var text = xhr.responseText || '';
+		if (xhr.status === 0 || xhr.status === 401 || xhr.status === 403 || xhr.status === 502 || xhr.status === 503
+			|| text.indexOf('login') >= 0 || text.indexOf('登录') >= 0) {
+			__screenFail++;
+			if (__screenFail >= 3 && Date.now() - __screenReloadAt > 60000) {
+				__screenReloadAt = Date.now();
+				location.reload();
+			}
+		}
+	});
+	$(document).ajaxSuccess(function() { __screenFail = 0; });
+
 	function pad(n) { return n < 10 ? '0' + n : '' + n; }
 
 	/** 工位表:每页条数与自动翻页间隔(毫秒) */
@@ -968,7 +983,7 @@
 
 	function updateLineProcessTableByApi() {
 		$.ajax({
-			url: '${ctx}/mes/mesLineProcess/listData',
+			url: '${ctx}/mes/mesProductScreen/lineProcessList',
 			type: 'POST',
 			dataType: 'json',
 			data: {
@@ -1166,7 +1181,7 @@
 
 	function updateDeviceAlarmAlertsByApi() {
 		$.ajax({
-			url: '${ctx}/mes/mesDeviceAlarm/listData',
+			url: '${ctx}/mes/mesProductScreen/alertList',
 			type: 'POST',
 			dataType: 'json',
 			data: {
@@ -1195,29 +1210,12 @@
 
 	function updateProductionPeopleByApi() {
 		$.ajax({
-			url: '${ctx}/sys/empUser/listData',
+			url: '${ctx}/mes/mesProductScreen/peopleCount',
 			type: 'POST',
 			dataType: 'json',
-			data: {
-				pageNo: 1,
-				pageSize: 5000,
-				'employee.office.officeName': '生产部'
-			},
 			success: function(resp) {
-				var peopleCount = 0;
-				var users = extractUserList(resp);
-				for (var i = 0; i < users.length; i++) {
-					var u = users[i] || {};
-					var office = (u.employee && u.employee.office) ? u.employee.office : {};
-					var officeName = office.officeName || '';
-					var normalizedOfficeName = (officeName + '').replace(/\s/g, '');
-					if (
-						normalizedOfficeName === '生产部' ||
-						/[/\\>]+生产部$/.test(normalizedOfficeName)
-					) {
-						peopleCount++;
-					}
-				}
+				if (!resp || resp.result !== 'true' || !resp.data) return;
+				var peopleCount = Number(resp.data.peopleCount) || 0;
 				mockData.cards = mockData.cards || {};
 				mockData.cards.productionPeople = peopleCount;
 				var target = document.getElementById('productionPeople');
@@ -1228,21 +1226,12 @@
 
 	function updateDeviceCountByApi() {
 		$.ajax({
-			url: '${ctx}/mes/mesDevice/listData',
+			url: '${ctx}/mes/mesProductScreen/deviceCount',
 			type: 'POST',
 			dataType: 'json',
-			data: {
-				pageNo: 1,
-				pageSize: 1
-			},
 			success: function(resp) {
-				var deviceCount = 0;
-				if (resp && typeof resp.count === 'number') {
-					deviceCount = resp.count;
-				} else {
-					var devices = extractUserList(resp);
-					deviceCount = devices.length;
-				}
+				if (!resp || resp.result !== 'true' || !resp.data) return;
+				var deviceCount = Number(resp.data.deviceCount) || 0;
 				mockData.cards = mockData.cards || {};
 				mockData.cards.deviceCount = deviceCount;
 				var target = document.getElementById('deviceCount');
@@ -1313,7 +1302,7 @@
 		var totalOk = 0;
 		function fetchPage() {
 			$.ajax({
-				url: '${ctx}/mes/mesProductRecord/listData',
+				url: '${ctx}/mes/mesProductScreen/recordList',
 				type: 'POST',
 				dataType: 'json',
 				data: {

+ 8 - 1
src/main/resources/views/modules/tianHong/machineAttrHistoryForm.html

@@ -27,7 +27,14 @@ var attrNameMap = {
 	'coolingTime': '冷却时间',
 	'PrePres': '预压压力设定',
 	'HoldPressure': '保压开始信号',
-	'moldFillingPre': '各段压力(冲型压力)'
+	'moldFillingPre': '各段压力(冲型压力)',
+	'cruTemp': '炉膛温度',
+	'aluTemp': '铝水温度',
+	'mouldInfo': '模具温度',
+	'theoryPressure': '理论压力',
+	'realPressure': '实际压力',
+	'pressureLevel': '压力段位',
+	'eleConsume': '电量总计'
 };
 var attrNameRules = [
 	{re:/^COOL_FLOW_(\d+)$/, name:function(m){ return '冷却流量['+m[1]+']'; }},

+ 116 - 41
src/main/resources/views/modules/tianHong/machineAttrHistoryList.html

@@ -1,4 +1,58 @@
 <% layout('/layouts/default.html', {title: '设备历史属性', libs: ['dataGrid','laydate']}){ %>
+<style>
+	/* 仅本页查询区样式,不影响其他模块 */
+	.mah-search {
+		display: flex;
+		flex-wrap: wrap;
+		align-items: center;
+		gap: 12px 20px;
+		padding: 14px 16px;
+		margin-bottom: 12px;
+		background: #f7f9fc;
+		border: 1px solid #e6ebf2;
+		border-radius: 6px;
+	}
+	.mah-search .mah-item {
+		display: flex;
+		align-items: center;
+		margin: 0;
+	}
+	.mah-search .mah-item > label {
+		margin: 0 8px 0 0;
+		white-space: nowrap;
+		color: #556070;
+		font-weight: 500;
+	}
+	.mah-search .form-control {
+		height: 34px;
+		border-radius: 4px;
+		border-color: #d0d7e2;
+		box-shadow: none;
+	}
+	.mah-search .form-control:focus {
+		border-color: #5b9bd5;
+		box-shadow: 0 0 0 2px rgba(91, 155, 213, 0.15);
+	}
+	.mah-search #eqpId {
+		width: 140px;
+	}
+	.mah-search #beginTime {
+		width: 178px;
+		cursor: pointer;
+		background: #fff;
+	}
+	.mah-search .mah-actions {
+		display: flex;
+		gap: 8px;
+		margin-left: auto;
+	}
+	.mah-search .mah-actions .btn {
+		min-width: 72px;
+		height: 34px;
+		padding: 0 14px;
+		border-radius: 4px;
+	}
+</style>
 <div class="main-content">
 	<div class="box box-main">
 		<div class="box-header">
@@ -14,29 +68,24 @@
 		<div class="box-body">
 			<#form:form id="searchForm" model="${machineAttrHistory}" action="${ctx}/tianHong/machineAttrHistory/listData" method="post" class="form-inline hide"
 					data-page-no="${parameter.pageNo}" data-page-size="20" data-order-by="${parameter.orderBy}">
-				<div class="form-group">
-				<label class="control-label">${text('设备号')}:</label>
-				<div class="control-inline">
-					<#form:input path="eqpId" maxlength="40" class="form-control width-120" required="true"/>
-				</div>
-			</div>
-			<div class="form-group">
-				<label class="control-label">${text('开始时间')}:</label>
-				<div class="control-inline">
-					<#form:input path="beginTime" maxlength="20" class="form-control laydate width-160"
-						dateFmt="yyyy-MM-dd HH:mm:ss" />
-				</div>
-			</div>
-			<div class="form-group">
-				<label class="control-label">${text('结束时间')}:</label>
-				<div class="control-inline">
-					<#form:input path="endTime" maxlength="20" class="form-control laydate width-160"
-						dateFmt="yyyy-MM-dd HH:mm:ss" />
-				</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 class="mah-search">
+					<div class="mah-item">
+						<label>${text('设备号')}</label>
+						<#form:input path="eqpId" maxlength="40" class="form-control" required="true" placeholder="如 D001"/>
+					</div>
+					<div class="mah-item">
+						<label>${text('上传时间')}</label>
+						<input type="text" id="beginTime" name="beginTime" readonly="readonly"
+							maxlength="19" class="form-control laydate required"
+							data-type="datetime" data-format="yyyy-MM-dd HH:mm:ss"
+							data-msg-required="请选择上传时间"
+							placeholder="上传时间"
+							value="${machineAttrHistory.beginTime!, 'yyyy-MM-dd HH:mm:ss'}"/>
+					</div>
+					<div class="mah-actions">
+						<button type="submit" class="btn btn-primary btn-sm"><i class="fa fa-search"></i> ${text('查询')}</button>
+						<button type="reset" class="btn btn-default btn-sm"><i class="fa fa-undo"></i> ${text('重置')}</button>
+					</div>
 				</div>
 			</#form:form>
 			<table id="dataGrid"></table>
@@ -46,32 +95,39 @@
 </div>
 <% } %>
 <script>
-// 初始化DataGrid对象(须先填设备号;每页 20,时间倒序由后端保证)
+// 初始化DataGrid对象(须先填设备号;每页 20,时间正序由后端保证)
 $('#dataGrid').dataGrid({
 	searchForm: $("#searchForm"),
 	pageSize: 20,
+	shrinkToFit: false,
 	columnModel: [
-		{header:'${text("设备号")}', name:'eqpId', index:'t.eqp_id', width:120, align:"center", frozen:true},
-		{header:'${text("炉膛温度(℃)")}', name:'cruTemp', index:'c.Value', width:110, align:"center"},
-		{header:'${text("铝水温度(℃)")}', name:'aluTemp', index:'a.Value', width:110, align:"center"},
-		{header:'${text("模具温度(℃)")}', name:'mouldInfo', index:'mt.mould_info', width:200, align:"center"},
-		{header:'${text("理论压力")}', name:'theoryPressure', index:'p.TheoryValue', width:100, align:"center"},
-		{header:'${text("实际压力")}', name:'realPressure', index:'p.RealValue', width:100, align:"center"},
-		{header:'${text("压力段位")}', name:'pressureLevel', index:'p.LevelID', width:90, align:"center"},
-		{header:'${text("电量总计")}', name:'eleConsume', index:'e.Value', width:110, align:"center"},
-		{header:'${text("剩余模次")}', name:'aluVal', width:90, align:"center"},
-		{header:'${text("保压时间")}', name:'pressurizeTime', width:90, align:"center"},
-		{header:'${text("冷却时间")}', name:'coolingTime', width:90, align:"center"},
-		{header:'${text("预压压力设定")}', name:'prePres', width:110, align:"center"},
-		{header:'${text("保压开始信号")}', name:'holdPressure', width:110, align:"center"},
-		{header:'${text("充型压力")}', name:'moldFillingPre', width:100, align:"center"},
-		{header:'${text("属性更新时间")}', name:'attrEventTime', width:150, align:"center"},
+		{header:'${text("设备号")}', name:'eqpId', index:'t.eqp_id', width:100, align:"center", frozen:true},
+		{header:'${text("炉膛温度(℃)")}', name:'cruTemp', index:'c.Value', width:100, align:"center"},
+		{header:'${text("铝水温度(℃)")}', name:'aluTemp', index:'a.Value', width:100, align:"center"},
+		{header:'${text("模具温度(℃)")}', name:'mouldInfo', index:'mt.mould_info', width:160, align:"center"},
+		{header:'${text("理论压力")}', name:'theoryPressure', index:'p.TheoryValue', width:90, align:"center"},
+		{header:'${text("实际压力")}', name:'realPressure', index:'p.RealValue', width:90, align:"center"},
+		{header:'${text("压力段位")}', name:'pressureLevel', index:'p.LevelID', width:80, align:"center"},
+		{header:'${text("电量总计")}', name:'eleConsume', index:'e.Value', width:90, align:"center"},
+		{header:'${text("剩余模次")}', name:'aluVal', width:80, align:"center"},
+		{header:'${text("保压时间")}', name:'pressurizeTime', width:80, align:"center"},
+		{header:'${text("冷却时间")}', name:'coolingTime', width:80, align:"center"},
+		{header:'${text("预压压力设定")}', name:'prePres', width:100, align:"center"},
+		{header:'${text("保压开始信号")}', name:'holdPressure', width:100, align:"center"},
+		{header:'${text("充型压力")}', name:'moldFillingPre', width:90, align:"center"},
+		{header:'${text("属性更新时间")}', name:'attrEventTime', width:170, align:"center",
+			formatter: function(val){
+				if (!val) return '';
+				if (typeof val === 'string') {
+					return val.length >= 19 ? val.substring(0, 19) : val;
+				}
+				return val;
+			}},
 		{header:'${text("操作")}', name:'actions', width:120, align:"center", formatter: function(val, obj, row, act){
 			var actions = [];
 			var eqpId = js.val(row,'eqpId');
 			var rowId = js.val(row,'id') || '';
 			var href = '${ctx}/tianHong/machineAttrHistory/form?id=' + encodeURIComponent(eqpId || '');
-			// id 形如 D001_20260803172341921000,取末尾 20 位时间键精确查明细
 			if (rowId && /_\d{20}$/.test(rowId)) {
 				href += '&eventTimeKey=' + encodeURIComponent(rowId.slice(-20));
 			} else {
@@ -86,8 +142,27 @@ $('#dataGrid').dataGrid({
 	]
 });
 
-// 刷新按钮
 $('#btnRefresh').click(function(){
 	$('#dataGrid').dataGrid('refresh');
 });
+
+function pad(n){ return n < 10 ? '0' + n : '' + n; }
+function formatDate(d) {
+	return d.getFullYear() + '-' + pad(d.getMonth()+1) + '-' + pad(d.getDate())
+		+ ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
+}
+function formatNow() { return formatDate(new Date()); }
+
+(function ensureDateTime(){
+	var re = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
+	var $b = $('#beginTime');
+	if (!$b.val() || !re.test($b.val())) {
+		$b.val(formatNow());
+	}
+})();
+$('#searchForm').on('reset', function(){
+	setTimeout(function(){
+		$('#beginTime').val(formatNow());
+	}, 0);
+});
 </script>