From 1491d0a67be5b2085a4e7e61aaedb56003265b4d Mon Sep 17 00:00:00 2001 From: wenshijun Date: Fri, 17 Oct 2025 09:26:24 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 92 +++--- .../zg/framework/config/SecurityConfig.java | 4 +- .../controller/MediaUploadController.java | 265 +----------------- .../wisdom/controller/RkInfoController.java | 11 + .../com/zg/project/wisdom/domain/RkInfo.java | 14 + .../wisdom/domain/dto/StockOutDTO.java | 3 + .../project/wisdom/mapper/RkInfoMapper.java | 6 + .../wisdom/service/IRkInfoService.java | 6 + .../service/impl/RkInfoServiceImpl.java | 10 + .../service/impl/RkStatisticsServiceImpl.java | 18 +- src/main/resources/application-druid.yml | 1 + .../resources/mybatis/wisdom/RkInfoMapper.xml | 50 +++- 12 files changed, 166 insertions(+), 314 deletions(-) diff --git a/pom.xml b/pom.xml index a0c10e3..4e254c1 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ smart 3.8.9 jar - + zg http://www.zg.vip @@ -24,6 +24,9 @@ UTF-8 UTF-8 1.8 + 1.8 + 1.8 + 3.1.1 1.4.7 2.0.53 @@ -34,7 +37,8 @@ 2.3.3 3.0.0 4.1.2 - 6.6.5 + + 5.8.5 2.3 9.0.102 @@ -82,11 +86,13 @@ - - - - - + @@ -218,6 +224,7 @@ poi-ooxml ${poi.version} + io.minio @@ -274,21 +281,6 @@ ZMPrinter 4.4.7 - - - - - - - - - - - - - - - org.projectlombok @@ -307,40 +299,44 @@ com.zg.SmartApplication + + org.apache.maven.plugins maven-compiler-plugin + 3.8.1 - 9 - 9 + ${maven.compiler.source} + ${maven.compiler.target} + - - - public - aliyun nexus - https://maven.aliyun.com/repository/public - - true - - - + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + - - - public - aliyun nexus - https://maven.aliyun.com/repository/public - - true - - - false - - - + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + false + + + - \ No newline at end of file + diff --git a/src/main/java/com/zg/framework/config/SecurityConfig.java b/src/main/java/com/zg/framework/config/SecurityConfig.java index 5511de0..cd5729b 100644 --- a/src/main/java/com/zg/framework/config/SecurityConfig.java +++ b/src/main/java/com/zg/framework/config/SecurityConfig.java @@ -116,7 +116,7 @@ public class SecurityConfig "/captchaImage", "/user/**", "/stat/**", - "/query/jh/**", +// "/query/jh/**", "/wisdom/signature/**", "/system/config/**", "/AutoInventory/**", @@ -124,12 +124,10 @@ public class SecurityConfig "/photo/**", "/wisdom/stock/**", "/system/media/**", - "/wisdom/**", "/mock/**", "/information/device/**", "/MatchScan/**", "/wisdom/task/**", -// "/wisdom/stock/**", // "/information/materialtype/**", // "/information/warehousingtype/**", // "/information/warehouseinfo/**", diff --git a/src/main/java/com/zg/project/wisdom/controller/MediaUploadController.java b/src/main/java/com/zg/project/wisdom/controller/MediaUploadController.java index a596f23..eba9a48 100644 --- a/src/main/java/com/zg/project/wisdom/controller/MediaUploadController.java +++ b/src/main/java/com/zg/project/wisdom/controller/MediaUploadController.java @@ -1,252 +1,3 @@ -//package com.zg.project.wisdom.controller; -// -//import com.zg.framework.web.domain.AjaxResult; -//import org.apache.commons.lang3.StringUtils; -//import org.springframework.http.MediaType; -//import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -//import org.springframework.util.unit.DataSize; -//import org.springframework.web.bind.annotation.*; -//import org.springframework.web.multipart.MultipartFile; -// -//import javax.annotation.Resource; -//import java.io.IOException; -//import java.io.InputStream; -//import java.io.OutputStream; -//import java.nio.file.*; -//import java.util.*; -//import java.util.concurrent.CompletableFuture; -//import java.util.concurrent.Semaphore; -// -///** -// * 多媒体上传控制器(支持图片与视频并行落盘) -// */ -//@RestController -//@RequestMapping("/system/media") -//public class MediaUploadController { -// -// /** 根目录(如需配置化,可改为从 application.yml 读取) */ -// private static final Path ROOT_DIR = Paths.get("D:/uploads"); -// -// /** 本次上传总大小限制:100MB */ -// private static final long MAX_TOTAL_BYTES = DataSize.ofMegabytes(100).toBytes(); -// -// /** 图片/视频数量限制 */ -// private static final int MAX_IMAGE_COUNT = 100; -// private static final int MAX_VIDEO_COUNT = 5; -// -// /** 单次请求内的并发上限(建议 2~4;默认 4) */ -// private static final int PER_REQUEST_CONCURRENCY = 4; -// -// /** 注入线程池 Bean:threadPoolTaskExecutor */ -// @Resource(name = "threadPoolTaskExecutor") -// private ThreadPoolTaskExecutor executor; -// -// /** -// * 上传接口 -// */ -// @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) -// public AjaxResult upload( -// @RequestParam("username") String username, -// @RequestParam("nameBase") String nameBase, -// @RequestParam(value = "imageNames", required = false) String imageNames, -// @RequestParam(value = "videoNames", required = false) String videoNames, -// @RequestPart(value = "images", required = false) MultipartFile[] images, -// @RequestPart(value = "videos", required = false) MultipartFile[] videos -// ) { -// try { -// // 1. 基础校验 -// if (StringUtils.isBlank(username)) { -// return AjaxResult.error("username 不能为空"); -// } -// if (StringUtils.isBlank(nameBase)) { -// return AjaxResult.error("nameBase(命名信息)不能为空"); -// } -// -// int imageCount = images == null ? 0 : images.length; -// int videoCount = videos == null ? 0 : videos.length; -// -// if (imageCount == 0 && videoCount == 0) { -// return AjaxResult.error("请至少上传一张图片或一段视频"); -// } -// if (imageCount > MAX_IMAGE_COUNT) { -// return AjaxResult.error("图片最多支持上传 " + MAX_IMAGE_COUNT + " 张"); -// } -// if (videoCount > MAX_VIDEO_COUNT) { -// return AjaxResult.error("视频最多支持上传 " + MAX_VIDEO_COUNT + " 段"); -// } -// -// // 2. 总体积校验 -// long totalBytes = 0L; -// if (images != null) for (MultipartFile f : images) totalBytes += safeSize(f); -// if (videos != null) for (MultipartFile f : videos) totalBytes += safeSize(f); -// if (totalBytes > MAX_TOTAL_BYTES) { -// return AjaxResult.error("本次上传总大小超过 100 MB(约 " + (totalBytes / 1024 / 1024) + " MB)"); -// } -// -// // 3. 目标目录 -// String base = sanitize(nameBase); -// String user = sanitize(username); -// String folderName = base + "_" + user; -// Path targetDir = ROOT_DIR.resolve(folderName).normalize(); -// if (!targetDir.startsWith(ROOT_DIR)) { -// return AjaxResult.error("非法路径"); -// } -// Files.createDirectories(targetDir); -// -// // 4. 解析前端传递的文件名 -// List imageNameList = new ArrayList<>(); -// List videoNameList = new ArrayList<>(); -// -// if (StringUtils.isNotBlank(imageNames)) { -// try { -// imageNameList = Arrays.asList(imageNames.split(",")); -// } catch (Exception e) { -// // 如果解析失败,使用空列表 -// imageNameList = new ArrayList<>(); -// } -// } -// if (StringUtils.isNotBlank(videoNames)) { -// try { -// videoNameList = Arrays.asList(videoNames.split(",")); -// } catch (Exception e) { -// // 如果解析失败,使用空列表 -// videoNameList = new ArrayList<>(); -// } -// } -// -// // 5. 并发落盘任务 -// int seq = 1; -// List>> futures = new ArrayList<>(); -// Semaphore semaphore = new Semaphore(PER_REQUEST_CONCURRENCY); -// -// // 5.1 图片处理 -// if (images != null) { -// for (int i = 0; i < images.length; i++) { -// MultipartFile img = images[i]; -// if (img == null || img.isEmpty()) continue; -// -// // 使用前端传递的文件名,如果没有则从原始文件名获取扩展名 -// String originalName = i < imageNameList.size() ? imageNameList.get(i) : img.getOriginalFilename(); -// String ext = getExtension(originalName).toLowerCase(Locale.ROOT); -// if (StringUtils.isBlank(ext)) { -// ext = "jpg"; // 图片默认扩展名 -// } -// -// final String filename = String.format("%s%s%03d.%s", base, user, seq++, ext); -// final Path dest = targetDir.resolve(filename).normalize(); -// -// futures.add(CompletableFuture.supplyAsync(() -> { -// acquire(semaphore); -// try { return saveOne(img, dest, "image"); } -// finally { semaphore.release(); } -// }, executor)); -// } -// } -// -// // 5.2 视频处理 -// if (videos != null) { -// for (int i = 0; i < videos.length; i++) { -// MultipartFile vid = videos[i]; -// if (vid == null || vid.isEmpty()) continue; -// -// // 使用前端传递的文件名,如果没有则从原始文件名获取扩展名 -// String originalName = i < videoNameList.size() ? videoNameList.get(i) : vid.getOriginalFilename(); -// String ext = getExtension(originalName).toLowerCase(Locale.ROOT); -// if (StringUtils.isBlank(ext)) { -// ext = "mp4"; // 视频默认扩展名 -// } -// -// final String filename = String.format("%s%s%03d.%s", base, user, seq++, ext); -// final Path dest = targetDir.resolve(filename).normalize(); -// -// futures.add(CompletableFuture.supplyAsync(() -> { -// acquire(semaphore); -// try { return saveOne(vid, dest, "video"); } -// finally { semaphore.release(); } -// }, executor)); -// } -// } -// -// // 6. 等待完成并返回 -// List> files = new ArrayList<>(futures.size()); -// for (CompletableFuture> f : futures) { -// files.add(f.join()); -// } -// files.sort(Comparator.comparing(m -> String.valueOf(m.get("path")))); -// -// Map data = new LinkedHashMap<>(); -// data.put("folder", targetDir.toString()); -// data.put("username", username); -// data.put("nameBase", nameBase); -// data.put("totalBytes", totalBytes); -// data.put("count", files.size()); -// data.put("files", files); -// -// return AjaxResult.success("上传成功", data); -// -// } catch (Exception e) { -// return AjaxResult.error("保存文件失败:" + e.getMessage()); -// } -// } -// -// // ========================= 辅助方法 ========================= -// -// /** 安全获取文件大小,避免 NPE */ -// private static long safeSize(MultipartFile f) { -// try { return (f == null) ? 0L : f.getSize(); } -// catch (Exception ignored) { return 0L; } -// } -// -// /** 获取扩展名(无则返回空字符串) */ -// private static String getExtension(String originalName) { -// if (StringUtils.isBlank(originalName)) return ""; -// int i = originalName.lastIndexOf('.'); -// return (i >= 0 && i < originalName.length() - 1) ? originalName.substring(i + 1) : ""; -// } -// -// /** 单文件保存 */ -// private Map saveOne(MultipartFile src, Path dest, String type) { -// try { -// Path parent = dest.getParent(); -// if (parent == null) throw new IOException("父目录为空"); -// if (!dest.normalize().startsWith(parent.normalize())) throw new IOException("非法路径"); -// -// try (InputStream in = src.getInputStream(); -// OutputStream out = Files.newOutputStream(dest, -// StandardOpenOption.CREATE, -// StandardOpenOption.TRUNCATE_EXISTING, -// StandardOpenOption.WRITE)) { -// in.transferTo(out); -// } -// -// Map m = new LinkedHashMap<>(); -// m.put("type", type); -// m.put("path", dest.toString()); -// m.put("sizeBytes", src.getSize()); -// m.put("sizeMB", String.format(Locale.ROOT, "%.2f", src.getSize() / 1024.0 / 1024.0)); -// return m; -// -// } catch (Exception ex) { -// throw new RuntimeException("保存失败:" + dest.getFileName() + ",原因:" + ex.getMessage(), ex); -// } -// } -// -// /** 获取信号量许可证(受控并发) */ -// private static void acquire(Semaphore sem) { -// try { sem.acquire(); } -// catch (InterruptedException e) { -// Thread.currentThread().interrupt(); -// throw new RuntimeException("线程被中断", e); -// } -// } -// -// /** 文件名/目录名清洗 */ -// private static String sanitize(String s) { -// String cleaned = s.replaceAll("[^\\p{L}\\p{N}_-]", "_"); -// return cleaned.length() > 64 ? cleaned.substring(0, 64) : cleaned; -// } -//} - package com.zg.project.wisdom.controller; import com.zg.framework.web.domain.AjaxResult; @@ -260,8 +11,8 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.nio.file.*; +import java.nio.file.StandardCopyOption; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; @@ -345,7 +96,7 @@ public class MediaUploadController { List>> futures = new ArrayList<>(); Semaphore semaphore = new Semaphore(PER_REQUEST_CONCURRENCY); - // 4.1 图片(不做格式校验) + // 4.1 图片 if (images != null) { for (MultipartFile img : images) { if (img == null || img.isEmpty()) continue; @@ -364,7 +115,7 @@ public class MediaUploadController { } } - // 4.2 视频(不做格式校验) + // 4.2 视频 if (videos != null) { for (MultipartFile vid : videos) { if (vid == null || vid.isEmpty()) continue; @@ -420,19 +171,15 @@ public class MediaUploadController { return (i >= 0 && i < originalName.length() - 1) ? originalName.substring(i + 1) : ""; } - /** 单文件保存 */ + /** 单文件保存(JDK8 兼容实现) */ private Map saveOne(MultipartFile src, Path dest, String type) { try { Path parent = dest.getParent(); if (parent == null) throw new IOException("父目录为空"); if (!dest.normalize().startsWith(parent.normalize())) throw new IOException("非法路径"); - try (InputStream in = src.getInputStream(); - OutputStream out = Files.newOutputStream(dest, - StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING, - StandardOpenOption.WRITE)) { - in.transferTo(out); + try (InputStream in = src.getInputStream()) { + Files.copy(in, dest, StandardCopyOption.REPLACE_EXISTING); } Map m = new LinkedHashMap<>(); diff --git a/src/main/java/com/zg/project/wisdom/controller/RkInfoController.java b/src/main/java/com/zg/project/wisdom/controller/RkInfoController.java index c5e9adb..80b55fd 100644 --- a/src/main/java/com/zg/project/wisdom/controller/RkInfoController.java +++ b/src/main/java/com/zg/project/wisdom/controller/RkInfoController.java @@ -221,4 +221,15 @@ public class RkInfoController extends BaseController return toAjax(rkInfoService.refundMaterial(dto)); } + /** + * 配送出库单据查询 + * @param + * @return + */ + @Log(title = "配送出库单据查询", businessType = BusinessType.OTHER) + @GetMapping("/delivery") + public AjaxResult listAllForDelivery(RkInfo query) { + List list = rkInfoService.selectDeliveryAll(query); + return AjaxResult.success(list); + } } diff --git a/src/main/java/com/zg/project/wisdom/domain/RkInfo.java b/src/main/java/com/zg/project/wisdom/domain/RkInfo.java index 5265f5c..d523d06 100644 --- a/src/main/java/com/zg/project/wisdom/domain/RkInfo.java +++ b/src/main/java/com/zg/project/wisdom/domain/RkInfo.java @@ -100,6 +100,10 @@ public class RkInfo extends BaseEntity @Excel(name = "出库单据号") private String billNoCk; + /** 是否需要配送(0否 1是) */ + @Excel(name = "是否需要配送", readConverterExp = "0=否,1=是") + private String isDelivery; + /** 县局 */ @Excel(name = "县局") private String xj; @@ -472,6 +476,15 @@ public class RkInfo extends BaseEntity public String getIsDelete() { return isDelete; } public void setIsDelete(String isDelete) { this.isDelete = isDelete; } + + public String getIsDelivery() { + return isDelivery; + } + + public void setIsDelivery(String isDelivery) { + this.isDelivery = isDelivery; + } + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) @@ -488,6 +501,7 @@ public class RkInfo extends BaseEntity .append("isChuku", getIsChuku()) .append("billNo", getBillNo()) .append("billNoCk", getBillNoCk()) + .append("isDelivery", getBillNoCk()) .append("remark", getRemark()) .append("xj", getXj()) .append("xmNo", getXmNo()) diff --git a/src/main/java/com/zg/project/wisdom/domain/dto/StockOutDTO.java b/src/main/java/com/zg/project/wisdom/domain/dto/StockOutDTO.java index 4d1b184..d1519b7 100644 --- a/src/main/java/com/zg/project/wisdom/domain/dto/StockOutDTO.java +++ b/src/main/java/com/zg/project/wisdom/domain/dto/StockOutDTO.java @@ -20,6 +20,9 @@ public class StockOutDTO { /** 借用时间 */ private Date borrowTime; + /** 是否需要配送(0否 1是) */ + private String isDelivery; + /** 归还时间 */ private Date returnTime; diff --git a/src/main/java/com/zg/project/wisdom/mapper/RkInfoMapper.java b/src/main/java/com/zg/project/wisdom/mapper/RkInfoMapper.java index 589420d..1a177c9 100644 --- a/src/main/java/com/zg/project/wisdom/mapper/RkInfoMapper.java +++ b/src/main/java/com/zg/project/wisdom/mapper/RkInfoMapper.java @@ -222,4 +222,10 @@ public interface RkInfoMapper List listRkInfoByPcode(String pcode); + /** + * 获取需要配送的出库单 + * @param query + * @return + */ + List selectDeliveryAll(RkInfo query); } diff --git a/src/main/java/com/zg/project/wisdom/service/IRkInfoService.java b/src/main/java/com/zg/project/wisdom/service/IRkInfoService.java index 13b3755..e0ce161 100644 --- a/src/main/java/com/zg/project/wisdom/service/IRkInfoService.java +++ b/src/main/java/com/zg/project/wisdom/service/IRkInfoService.java @@ -155,4 +155,10 @@ public interface IRkInfoService */ List listRkInfoByPcode(String pcode); + /** + * 获取需要配送的出库列表 + * @param query + * @return + */ + List selectDeliveryAll(RkInfo query); } diff --git a/src/main/java/com/zg/project/wisdom/service/impl/RkInfoServiceImpl.java b/src/main/java/com/zg/project/wisdom/service/impl/RkInfoServiceImpl.java index adc73aa..ddc37fc 100644 --- a/src/main/java/com/zg/project/wisdom/service/impl/RkInfoServiceImpl.java +++ b/src/main/java/com/zg/project/wisdom/service/impl/RkInfoServiceImpl.java @@ -661,6 +661,7 @@ public class RkInfoServiceImpl implements IRkInfoService RkInfo update = new RkInfo(); update.setId(item.getId()); update.setBillNoCk(billNo); + update.setIsDelivery(dto.getIsDelivery()); update.setCkType(dto.getCkType()); update.setTeamCode(dto.getTeamCode()); update.setLyTime(now); @@ -983,4 +984,13 @@ public class RkInfoServiceImpl implements IRkInfoService return rkInfoMapper.listRkInfoByPcode(pcode); } + /** + * 获取需要配送的出库列表 + * @param query + * @return + */ + @Override + public List selectDeliveryAll(RkInfo query) { + return rkInfoMapper.selectDeliveryAll(query); + } } diff --git a/src/main/java/com/zg/project/wisdom/service/impl/RkStatisticsServiceImpl.java b/src/main/java/com/zg/project/wisdom/service/impl/RkStatisticsServiceImpl.java index f7a0cd2..9fd2eeb 100644 --- a/src/main/java/com/zg/project/wisdom/service/impl/RkStatisticsServiceImpl.java +++ b/src/main/java/com/zg/project/wisdom/service/impl/RkStatisticsServiceImpl.java @@ -33,9 +33,21 @@ public class RkStatisticsServiceImpl implements RkStatisticsService { RkAgeStatVO vo = rkStatisticsMapper.selectAgeStats(); List> list = new ArrayList<>(3); - list.add(Map.of("name", ">10天", "value", vo.getGt10())); - list.add(Map.of("name", ">20天", "value", vo.getGt20())); - list.add(Map.of("name", ">30天", "value", vo.getGt30())); + Map m1 = new HashMap<>(); + m1.put("name", ">10天"); + m1.put("value", vo.getGt10()); + list.add(m1); + + Map m2 = new HashMap<>(); + m2.put("name", ">20天"); + m2.put("value", vo.getGt20()); + list.add(m2); + + Map m3 = new HashMap<>(); + m3.put("name", ">30天"); + m3.put("value", vo.getGt30()); + list.add(m3); + return list; } /** diff --git a/src/main/resources/application-druid.yml b/src/main/resources/application-druid.yml index faa02e2..88d57ed 100644 --- a/src/main/resources/application-druid.yml +++ b/src/main/resources/application-druid.yml @@ -8,6 +8,7 @@ spring: master: # url: jdbc:mysql://101.132.133.142:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 url: jdbc:mysql://192.168.1.20:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 +# url: jdbc:mysql://192.168.1.192:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 # url: jdbc:mysql://192.168.1.251:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 # url: jdbc:mysql://localhost:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root diff --git a/src/main/resources/mybatis/wisdom/RkInfoMapper.xml b/src/main/resources/mybatis/wisdom/RkInfoMapper.xml index 50e24e6..9f65622 100644 --- a/src/main/resources/mybatis/wisdom/RkInfoMapper.xml +++ b/src/main/resources/mybatis/wisdom/RkInfoMapper.xml @@ -57,6 +57,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -67,7 +68,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ri.rk_type, st.type_name AS rk_type_name, ri.wl_type, mt.type_name AS wl_type_name, ri.cangku, wh.warehouse_name AS cangku_name, - ri.rk_time, ri.lihuo_y, ri.is_chuku, ri.is_borrowed, ri.remark, + ri.rk_time, ri.lihuo_y, ri.is_chuku, ri.is_borrowed, ri.is_delivery,ri.remark, ri.ck_lihuo_y, ri.ck_type, sot.type_name AS ck_type_name, ri.team_code, ct.team_name, ri.ck_remark, @@ -818,6 +819,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" is_borrowed = #{isBorrowed}, bill_no_ck = #{billNoCk}, ck_type = #{ckType}, + is_delivery = #{isDelivery}, ck_lihuo_y = #{ckLihuoY}, team_code = #{teamCode}, ck_remark = #{ckRemark}, @@ -897,5 +899,51 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ORDER BY t.rk_time DESC, t.id DESC + + \ No newline at end of file