| New file |
| | |
| | | package com.zy.asrs.controller; |
| | | |
| | | import com.core.annotations.ManagerAuth; |
| | | import com.core.common.Cools; |
| | | import com.core.common.R; |
| | | import com.zy.common.web.BaseController; |
| | | import com.zy.core.enums.SlaveType; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @RestController |
| | | public class DeviceLogController extends BaseController { |
| | | |
| | | @Value("${deviceLogStorage.loggingPath}") |
| | | private String loggingPath; |
| | | |
| | | private static class ProgressInfo { |
| | | long totalRaw; |
| | | long processedRaw; |
| | | int totalCount; |
| | | int processedCount; |
| | | boolean finished; |
| | | } |
| | | |
| | | private static final Map<String, ProgressInfo> DOWNLOAD_PROGRESS = new ConcurrentHashMap<>(); |
| | | |
| | | @RequestMapping(value = "/deviceLog/dates/auth") |
| | | @ManagerAuth |
| | | public R dates() { |
| | | try { |
| | | Path baseDir = Paths.get(loggingPath); |
| | | if (!Files.exists(baseDir)) { |
| | | return R.ok(new ArrayList<>()); |
| | | } |
| | | List<String> days = Files.list(baseDir) |
| | | .filter(Files::isDirectory) |
| | | .map(p -> p.getFileName().toString()) |
| | | .filter(name -> name.length() == 8 && name.chars().allMatch(Character::isDigit)) |
| | | .sorted() |
| | | .collect(Collectors.toList()); |
| | | Map<String, Map<String, List<String>>> grouped = new LinkedHashMap<>(); |
| | | for (String day : days) { |
| | | String year = day.substring(0, 4); |
| | | String month = day.substring(4, 6); |
| | | grouped.computeIfAbsent(year, k -> new LinkedHashMap<>()) |
| | | .computeIfAbsent(month, k -> new ArrayList<>()) |
| | | .add(day); |
| | | } |
| | | List<Map<String, Object>> tree = new ArrayList<>(); |
| | | for (Map.Entry<String, Map<String, List<String>>> yEntry : grouped.entrySet()) { |
| | | Map<String, Object> yNode = new HashMap<>(); |
| | | yNode.put("title", yEntry.getKey()); |
| | | yNode.put("id", yEntry.getKey()); |
| | | List<Map<String, Object>> mChildren = new ArrayList<>(); |
| | | for (Map.Entry<String, List<String>> mEntry : yEntry.getValue().entrySet()) { |
| | | Map<String, Object> mNode = new HashMap<>(); |
| | | mNode.put("title", mEntry.getKey()); |
| | | mNode.put("id", yEntry.getKey() + "-" + mEntry.getKey()); |
| | | List<Map<String, Object>> dChildren = new ArrayList<>(); |
| | | for (String d : mEntry.getValue()) { |
| | | Map<String, Object> dNode = new HashMap<>(); |
| | | dNode.put("title", d.substring(6, 8)); |
| | | dNode.put("id", d); |
| | | dNode.put("day", d); |
| | | dChildren.add(dNode); |
| | | } |
| | | mNode.put("children", dChildren); |
| | | mChildren.add(mNode); |
| | | } |
| | | yNode.put("children", mChildren); |
| | | tree.add(yNode); |
| | | } |
| | | return R.ok(tree); |
| | | } catch (Exception e) { |
| | | return R.error("读取日期失败"); |
| | | } |
| | | } |
| | | |
| | | @RequestMapping(value = "/deviceLog/day/{day}/devices/auth") |
| | | @ManagerAuth |
| | | public R devices(@PathVariable("day") String day) { |
| | | try { |
| | | if (day == null || day.length() != 8 || !day.chars().allMatch(Character::isDigit)) { |
| | | return R.error("日期格式错误"); |
| | | } |
| | | Path dayDir = Paths.get(loggingPath, day); |
| | | if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) { |
| | | return R.ok(new ArrayList<>()); |
| | | } |
| | | List<Path> files = Files.list(dayDir) |
| | | .filter(p -> !Files.isDirectory(p) && p.getFileName().toString().endsWith(".log")) |
| | | .collect(Collectors.toList()); |
| | | Map<String, Map<String, Object>> deviceMap = new HashMap<>(); |
| | | for (Path p : files) { |
| | | String name = p.getFileName().toString(); |
| | | String[] parts = name.split("_"); |
| | | if (parts.length < 4) { |
| | | continue; |
| | | } |
| | | String deviceNo = parts[1]; |
| | | String type = parts[0]; |
| | | Map<String, Object> info = deviceMap.computeIfAbsent(deviceNo, k -> { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("deviceNo", deviceNo); |
| | | map.put("types", new HashSet<String>()); |
| | | map.put("fileCount", 0); |
| | | return map; |
| | | }); |
| | | ((Set<String>) info.get("types")).add(type); |
| | | info.put("fileCount", ((Integer) info.get("fileCount")) + 1); |
| | | } |
| | | List<Map<String, Object>> res = deviceMap.values().stream().map(m -> { |
| | | Map<String, Object> x = new HashMap<>(); |
| | | x.put("deviceNo", m.get("deviceNo")); |
| | | x.put("types", ((Set<String>) m.get("types")).stream().collect(Collectors.toList())); |
| | | x.put("fileCount", m.get("fileCount")); |
| | | return x; |
| | | }).collect(Collectors.toList()); |
| | | return R.ok(res); |
| | | } catch (Exception e) { |
| | | return R.error("读取设备列表失败"); |
| | | } |
| | | } |
| | | |
| | | @RequestMapping(value = "/deviceLog/day/{day}/download/auth") |
| | | @ManagerAuth |
| | | public void download(@PathVariable("day") String day, |
| | | @RequestParam("type") String type, |
| | | @RequestParam("deviceNo") String deviceNo, |
| | | @RequestParam(value = "offset", required = false) Integer offset, |
| | | @RequestParam(value = "limit", required = false) Integer limit, |
| | | @RequestParam(value = "progressId", required = false) String progressId, |
| | | HttpServletResponse response) { |
| | | try { |
| | | String dayClean = day == null ? null : day.replaceAll("\\D", ""); |
| | | if (dayClean == null || dayClean.length() != 8 || !dayClean.chars().allMatch(Character::isDigit)) { |
| | | response.setStatus(400); |
| | | return; |
| | | } |
| | | if (type == null || SlaveType.findInstance(type) == null) { |
| | | response.setStatus(400); |
| | | return; |
| | | } |
| | | if (deviceNo == null || !deviceNo.chars().allMatch(Character::isDigit)) { |
| | | response.setStatus(400); |
| | | return; |
| | | } |
| | | Path dayDir = Paths.get(loggingPath, dayClean); |
| | | if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) { |
| | | response.setStatus(404); |
| | | return; |
| | | } |
| | | List<Path> files = Files.list(dayDir) |
| | | .filter(p -> { |
| | | String name = p.getFileName().toString(); |
| | | String prefix = type + "_" + deviceNo + "_" + dayClean + "_"; |
| | | return name.endsWith(".log") && name.startsWith(prefix); |
| | | }).collect(Collectors.toList()); |
| | | // 排序(按文件中的索引号递增) |
| | | String prefix = type + "_" + deviceNo + "_" + dayClean + "_"; |
| | | files.sort(Comparator.comparingInt(p -> { |
| | | String n = p.getFileName().toString(); |
| | | try { |
| | | String suf = n.substring(prefix.length(), n.length() - 4); |
| | | return Integer.parseInt(suf); |
| | | } catch (Exception e) { |
| | | return Integer.MAX_VALUE; |
| | | } |
| | | })); |
| | | int from = offset == null || offset < 0 ? 0 : offset; |
| | | int max = limit == null || limit <= 0 ? 200 : limit; |
| | | int to = Math.min(files.size(), from + max); |
| | | if (from >= files.size()) { |
| | | response.setStatus(404); |
| | | return; |
| | | } |
| | | files = files.subList(from, to); |
| | | if (files.isEmpty()) { |
| | | response.setStatus(404); |
| | | return; |
| | | } |
| | | ProgressInfo info; |
| | | String id = progressId; |
| | | if (Cools.isEmpty(id)) { |
| | | id = UUID.randomUUID().toString(); |
| | | } |
| | | List<Path> finalFiles = files; |
| | | info = DOWNLOAD_PROGRESS.computeIfAbsent(id, k -> { |
| | | ProgressInfo x = new ProgressInfo(); |
| | | x.totalCount = finalFiles.size(); |
| | | long sum = 0L; |
| | | for (Path f : finalFiles) { |
| | | try { sum += Files.size(f); } catch (Exception ignored) {} |
| | | } |
| | | x.totalRaw = sum; |
| | | x.processedRaw = 0L; |
| | | x.processedCount = 0; |
| | | x.finished = false; |
| | | return x; |
| | | }); |
| | | response.reset(); |
| | | response.setContentType("application/zip"); |
| | | String filename = type + "_" + deviceNo + "_" + dayClean + ".zip"; |
| | | response.setHeader("Content-Disposition", "attachment; filename=" + filename); |
| | | long totalRawSize = 0L; |
| | | for (Path f : files) { |
| | | try { totalRawSize += Files.size(f); } catch (Exception ignored) {} |
| | | } |
| | | response.setHeader("X-Total-Size", String.valueOf(totalRawSize)); |
| | | response.setHeader("X-File-Count", String.valueOf(files.size())); |
| | | response.setHeader("X-Progress-Id", id); |
| | | try (java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(response.getOutputStream())) { |
| | | for (Path f : files) { |
| | | java.util.zip.ZipEntry entry = new java.util.zip.ZipEntry(f.getFileName().toString()); |
| | | zos.putNextEntry(entry); |
| | | Files.copy(f, zos); |
| | | zos.closeEntry(); |
| | | try { |
| | | info.processedRaw += Files.size(f); |
| | | } catch (Exception ignored) {} |
| | | info.processedCount += 1; |
| | | } |
| | | zos.finish(); |
| | | info.finished = true; |
| | | } |
| | | } catch (Exception e) { |
| | | try { response.setStatus(500); } catch (Exception ignore) {} |
| | | } |
| | | } |
| | | |
| | | @RequestMapping(value = "/deviceLog/download/init/auth") |
| | | @ManagerAuth |
| | | public R init(@org.springframework.web.bind.annotation.RequestBody com.alibaba.fastjson.JSONObject param) { |
| | | try { |
| | | String day = param.getString("day"); |
| | | String type = param.getString("type"); |
| | | String deviceNo = param.getString("deviceNo"); |
| | | Integer offset = param.getInteger("offset"); |
| | | Integer limit = param.getInteger("limit"); |
| | | String dayClean = Cools.isEmpty(day) ? null : day.replaceAll("\\D", ""); |
| | | if (Cools.isEmpty(dayClean) || dayClean.length() != 8 || !dayClean.chars().allMatch(Character::isDigit)) { |
| | | return R.error("日期格式错误"); |
| | | } |
| | | if (Cools.isEmpty(type) || SlaveType.findInstance(type) == null) { |
| | | return R.error("设备类型错误"); |
| | | } |
| | | if (Cools.isEmpty(deviceNo) || !deviceNo.chars().allMatch(Character::isDigit)) { |
| | | return R.error("设备编号错误"); |
| | | } |
| | | Path dayDir = Paths.get(loggingPath, dayClean); |
| | | if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) { |
| | | return R.error("当日目录不存在"); |
| | | } |
| | | List<Path> files = Files.list(dayDir) |
| | | .filter(p -> { |
| | | String name = p.getFileName().toString(); |
| | | String prefix = type + "_" + deviceNo + "_" + dayClean + "_"; |
| | | return name.endsWith(".log") && name.startsWith(prefix); |
| | | }).collect(Collectors.toList()); |
| | | String prefix = type + "_" + deviceNo + "_" + dayClean + "_"; |
| | | files.sort(Comparator.comparingInt(p -> { |
| | | String n = p.getFileName().toString(); |
| | | try { |
| | | String suf = n.substring(prefix.length(), n.length() - 4); |
| | | return Integer.parseInt(suf); |
| | | } catch (Exception e) { |
| | | return Integer.MAX_VALUE; |
| | | } |
| | | })); |
| | | int from = offset == null || offset < 0 ? 0 : offset; |
| | | int max = limit == null || limit <= 0 ? 200 : limit; |
| | | int to = Math.min(files.size(), from + max); |
| | | if (from >= files.size()) { |
| | | return R.error("起始序号超出范围"); |
| | | } |
| | | files = files.subList(from, to); |
| | | String id = UUID.randomUUID().toString(); |
| | | ProgressInfo info = new ProgressInfo(); |
| | | info.totalCount = files.size(); |
| | | long sum = 0L; |
| | | for (Path f : files) { |
| | | try { sum += Files.size(f); } catch (Exception ignored) {} |
| | | } |
| | | info.totalRaw = sum; |
| | | info.processedRaw = 0L; |
| | | info.processedCount = 0; |
| | | info.finished = false; |
| | | DOWNLOAD_PROGRESS.put(id, info); |
| | | Map<String, Object> res = new HashMap<>(); |
| | | res.put("progressId", id); |
| | | res.put("totalSize", info.totalRaw); |
| | | res.put("fileCount", info.totalCount); |
| | | return R.ok(res); |
| | | } catch (Exception e) { |
| | | return R.error("初始化失败"); |
| | | } |
| | | } |
| | | |
| | | @RequestMapping(value = "/deviceLog/download/progress/auth") |
| | | @ManagerAuth |
| | | public R progress(String id) { |
| | | ProgressInfo info = DOWNLOAD_PROGRESS.get(id); |
| | | if (info == null) { |
| | | return R.error("无效进度"); |
| | | } |
| | | long total = info.totalRaw; |
| | | long done = info.processedRaw; |
| | | int percent; |
| | | if (info.finished) { |
| | | percent = 100; |
| | | } else if (total > 0) { |
| | | percent = (int) Math.min(99, (done * 100L) / total); |
| | | } else if (info.totalCount > 0) { |
| | | percent = (int) Math.min(99, (info.processedCount * 100L) / info.totalCount); |
| | | } else { |
| | | percent = 0; |
| | | } |
| | | Map<String, Object> res = new HashMap<>(); |
| | | res.put("percent", percent); |
| | | res.put("processedSize", done); |
| | | res.put("totalSize", total); |
| | | res.put("processedCount", info.processedCount); |
| | | res.put("totalCount", info.totalCount); |
| | | res.put("finished", info.finished); |
| | | return R.ok(res); |
| | | } |
| | | } |