当前位置: 首页 > news >正文

SpringBoot全局Controller返回值格式统一处理

一、Controller返回值格式统一

1、WebResult类

在 Controller对外提供服务的时候,我们都需要统一返回值格式。一般定义一个 WebResult类。

统一返回值(WebResult类)格式如下:

{"success": true,"code": 200000,"message": null,"data": {"pageList": ["张三","ccc"],"paginator": {"currentPage": 1,"pageSize": 2,"total": 3,"pages": 4}}
}

WebResult类信息:

@Data
@ApiModel(value = "Web结果集")
public class WebResult<T> implements Serializable {private static final long serialVersionUID = -4350499690382193884L;/*** 是否成功, 默认false*/@ApiModelProperty(value = "是否成功, 默认false")private Boolean success = false;/*** 返回状态码*/@ApiModelProperty(value = "返回状态码")private Integer code;/*** 返回信息*/@ApiModelProperty(value = "返回信息")private String message;/*** 返回数据*/@ApiModelProperty(value = "返回数据")private T data;public static <T> WebResult<T> ok() {WebResult<T> webResult = new WebResult<>();webResult.setSuccess(true);webResult.setCode(WebHttpCode.SERVICE_SUCCESS);webResult.setMessage("操作成功");return webResult;}public static <T> WebResult<T> ok(T data) {WebResult<T> webResult = new WebResult<>();webResult.setSuccess(true);webResult.setCode(WebHttpCode.SERVICE_SUCCESS);webResult.setMessage("操作成功");webResult.setData(data);return webResult;}public static <T> WebResult<T> error() {WebResult<T> webResult = new WebResult<>();webResult.setSuccess(false);webResult.setCode(WebHttpCode.SERVICE_ERROR);webResult.setMessage("操作失败");return webResult;}public WebResult message(String message) {this.setMessage(message);return this;}public WebResult data(T data) {this.setData(data);return this;}}

如果我们不做全局 Controller统一处理返回时,就出需要业务在每个 Controller类中返回 WebResult类。同时在全局异常中也返回 WebResult类。

有没有一种跟优雅的方式处理呢?当然有

  • 业务不需要对所有 Controller都使用一个返回值(WebResult类),Controller可以需要返回原始值或者自定义的基类,然后处理器统一对返回值(WebResult类)进行封装并输出。
  • 同时也可以添加自定义注解,此注解用于忽略返回值封装,按照 Controller原始值返回。

下面我们使用 HandlerMethodReturnValueHandler接口来实现。

2、相关类说明

2.1 HandlerMethodReturnValueHandler接口

使用不同策略处理从调用处理程序方法的返回值,策略处理顶层接口,自定义返回值格式需要实现此接口。

org.springframework.web.method.support.HandlerMethodReturnValueHandler

  • supportsReturnType:设置支持返回值类型
  • handleReturnValue:处理返回值基础参数

在这里插入图片描述

2.2 RequestMappingHandlerAdapter类

请求映射处理适配,包含了参数、返回值处理器等信息

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

  • HandlerMethodReturnValueHandlerComposite内部维护了HandlerMethodReturnValueHandler列表

在这里插入图片描述

3.3 RequestResponseBodyMethodProcessor类

属于HandlerMethodReturnValueHandler子类。

org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor

  • 主要功能是对请求和响应体的做处理的方法,所有属于RequestResponseBodyMethodProcessor的子类都需要替换为自定义返回值处理

在这里插入图片描述

全局Controller返回值统一处理实现原理就是:在bean初始化的时候,获取到所有处理器数组,然后将所有是 RequestResponseBodyMethodProcessor处理器子类对返回值处理的过程替换为自定义返回值处理器处理。
这样当调用对应返回值处理器时,将会使用到自定义的返回值处理器,也就是所有返回值都会按照规定的进行处理。
同时,我们也可以自定义注解(作用于Controller类或者方法级别忽略返回值封装),然后在自定义返回值处理器中忽略返回值封装。

二、全局Controller返回值统一处理实战

1、自定义注解

自定义注解,作用于Controller类或者方法级别忽略返回值封装。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreResponseBodyWrap {
}

2、创建自定义返回值处理器类

创建自定义处理器类实现 HandlerMethodReturnValueHandler接口,主要用于实现自定义返回值内容,不需要注入容器。

下面代码中的 BaseXXXResult是业务的基类。你可以自定义或者使用你们约定的基类。

/*** Controller 返回值格式统一处理*/
public class ResponseBodyWrapHandler implements HandlerMethodReturnValueHandler {private final HandlerMethodReturnValueHandler delegate;public ResponseBodyWrapHandler(HandlerMethodReturnValueHandler delegate) {this.delegate = delegate;}/*** 设置支持返回值类型** @param returnType* @return*/@Overridepublic boolean supportsReturnType(MethodParameter returnType) {return delegate.supportsReturnType(returnType);}/*** 处理返回值基础参数** @param returnValue  方法的返回值对象* @param returnType   封装方法参数说明的辅助类(方法的返回值类型)* @param mavContainer 用于设置模型和视图的容器* @param webRequest   当前的请求对象* @throws Exception*/@Overridepublic void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 如果类或者方法含有不包装注解则忽略包装IgnoreResponseBodyWrap ignoreResponseBodyWrap = returnType.getDeclaringClass().getAnnotation(IgnoreResponseBodyWrap.class);if (ignoreResponseBodyWrap != null) {delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);return;}ignoreResponseBodyWrap = returnType.getMethodAnnotation(IgnoreResponseBodyWrap.class);if (ignoreResponseBodyWrap != null) {delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);return;}// 返回统一格式Object obj = null;if (returnValue instanceof WebResult) {obj = returnValue;} else if (returnValue instanceof BasePageResult) {BasePageResult basePageResult = (BasePageResult) returnValue;ErrorCode errorCode = basePageResult.getErrorCode();Map<String, Object> pageData = new HashMap<>();pageData.put(CommonConstants.PAGE_LIST, basePageResult.getPageList());pageData.put(CommonConstants.PAGINATOR, basePageResult.getPaginator());obj = WebResult.ok().data(pageData).message(errorCode == null ? null : errorCode.getMessage());} else if (returnValue instanceof BaseOperateResult) {BaseOperateResult baseOperateResult = (BaseOperateResult) returnValue;ErrorCode errorCode = baseOperateResult.getErrorCode();boolean success = baseOperateResult.isSuccess();if (success) {obj = WebResult.ok().data(baseOperateResult.getId()).message(errorCode == null ? null : errorCode.getMessage());} else {obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());}} else if (returnValue instanceof BaseDataResult) {BaseDataResult baseDataResult = (BaseDataResult) returnValue;ErrorCode errorCode = baseDataResult.getErrorCode();boolean success = baseDataResult.isSuccess();if (success) {obj = WebResult.ok().data(baseDataResult.getData()).message(errorCode == null ? null : errorCode.getMessage());} else {obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());}} else if (returnValue instanceof BaseResult) {BaseResult baseResult = (BaseResult) returnValue;ErrorCode errorCode = baseResult.getErrorCode();boolean success = baseResult.isSuccess();if (success) {obj = WebResult.ok().message(errorCode == null ? null : errorCode.getMessage());} else {obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());}} else {obj = returnValue;}delegate.handleReturnValue(obj, returnType, mavContainer, webRequest);}
}

3、注册自定义返回值处理器类

将所有 RequestResponseBodyMethodProcessor返回值处理器替换为自定义的返回值处理器。

@Component
public class ResponseBodyWrapFactoryBean implements InitializingBean {private final RequestMappingHandlerAdapter adapter;@Autowiredpublic ResponseBodyWrapFactoryBean(RequestMappingHandlerAdapter adapter) {this.adapter = adapter;}@Overridepublic void afterPropertiesSet() throws Exception {List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();if (returnValueHandlers.size() > 0) {// 将内置的返回值处理器进行替换List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers);decorateHandlers(handlers);adapter.setReturnValueHandlers(handlers);}}/*** 将所有RequestResponseBodyMethodProcessor返回值处理器替换为自定义的返回值处理器** @author tianxincode@163.com* @since 2020/10/12*/private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {for (HandlerMethodReturnValueHandler handler : handlers) {if (handler instanceof RequestResponseBodyMethodProcessor) {// 替换为自定义返回值处理器ResponseBodyWrapHandler decorator = new ResponseBodyWrapHandler(handler);int index = handlers.indexOf(handler);handlers.set(index, decorator);break;}}}}

4、全局异常处理类

一般项目都会有一个全局异常处理类

@Slf4j
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(value = Exception.class)private WebResult handlerException(Exception e) {log.error("Exception 异常 -> error:", e);WebResult webResult = new WebResult();webResult.setSuccess(false);webResult.setCode(WebHttpCode.SYSTEM_ERROR);webResult.setMessage("系统异常,请联系管理员");return webResult;}@ExceptionHandler(value = RuntimeException.class)private WebResult handlerRuntimeException(RuntimeException e) {log.error("RuntimeException 异常 -> error:", e);WebResult webResult = new WebResult();webResult.setSuccess(false);webResult.setCode(WebHttpCode.SYSTEM_ERROR);webResult.setMessage("系统异常");return webResult;}@ExceptionHandler(value = ServiceException.class)private WebResult handlerServiceException(ServiceException e) {log.error("ServiceException 异常 -> error:", e);WebResult webResult = new WebResult();webResult.setSuccess(false);webResult.setCode(WebHttpCode.SERVICE_ERROR);webResult.setMessage(e.getMessage());return webResult;}}

5、Controller示例

@RestController
@RequestMapping("/project2")
@Slf4j
@Api(value = "Project2Controller", tags = {"Project2相关操作接口"})
public class Project2Controller {@GetMapping("/successPage")@ApiOperation(value = "successPage接口")public BaseResult successPage() {log.info("successPage接口");List list = new ArrayList();list.add("张三");list.add("ccc");BasePageResult basePageResult = new BasePageResult<>();basePageResult.setSuccess(Boolean.TRUE);basePageResult.setPageList(list);basePageResult.setPaginator(new Paginator(1, 2, 3, 4));return basePageResult;}@GetMapping("/get")@ApiOperation(value = "get接口")@IgnoreResponseBodyWrappublic List get() {log.info("get接口");ProjectDO projectDO = new ProjectDO();projectDO.setId(10L);projectDO.setName("项目10");projectDO.setDepartmentId(0L);projectDO.setDescr("");projectDO.setDelFlag(false);projectDO.setCreateTime(new Date());projectDO.setUpdateTime(new Date());List list = new ArrayList();list.add("张三");list.add("ccc");return list;}@GetMapping("/get2")@ApiOperation(value = "get2接口")public BaseDataResult get2() {log.info("get2接口");ProjectDO projectDO = new ProjectDO();projectDO.setId(10L);projectDO.setName("项目10");projectDO.setDepartmentId(0L);projectDO.setDescr("");projectDO.setDelFlag(false);projectDO.setCreateTime(new Date());projectDO.setUpdateTime(new Date());BaseDataResult baseDataResult = new BaseDataResult();baseDataResult.setSuccess(Boolean.TRUE);baseDataResult.setData(projectDO);return baseDataResult;}/*** 文件下载接口*/@GetMapping("/download")@ApiOperation(value = "文件下载接口")public void download(HttpServletResponse response) throws SerialException {try {// 获取要下载的文件File file = new File("D:\\TempFiles\\xxxxxx.xlsx");String fileName = file.getName();response.setContentType("application/octet-stream;charset=UTF-8");response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "iso-8859-1"));IOUtils.copy(new FileInputStream(file), response.getOutputStream());} catch (Exception e) {log.info("文件下载异常 -> e=", e);throw new ServiceException("文件下载异常");}}@GetMapping("/systemError")@ApiOperation(value = "systemError接口")public BaseResult systemError() {log.info("systemError接口");int i = 2 / 0;BaseResult baseResult = new BaseResult();baseResult.setSuccess(Boolean.TRUE);return baseResult;}}

在这里插入图片描述

– 求知若饥,虚心若愚。

相关文章:

SpringBoot全局Controller返回值格式统一处理

一、Controller返回值格式统一 1、WebResult类 在 Controller对外提供服务的时候&#xff0c;我们都需要统一返回值格式。一般定义一个 WebResult类。 统一返回值&#xff08;WebResult类&#xff09;格式如下&#xff1a; {"success": true,"code": 2…...

程序媛的mac修炼手册-- 终端shell的驾驭 zsh vs bash

进入终端(Terminal)为新下载的应用配置环境&#xff0c;是Mac生产力up up的关键一步&#xff0c;更是编程小白装大神的第一步。Fake it till you make it , 硅谷大神标准路径&#xff5e; shell的基本原理 为应用配置环境&#xff0c;相当于在应用和操作系统间架桥。由此&…...

基于PHP的校园代购商城系统

有需要请加文章底部Q哦 可远程调试 基于PHP的校园代购商城系统 一 介绍 此校园代购商城系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。(附带参考设计文档) 技术栈&#xff1a;phpmysqlbootstrapphpstudyvscode 二 功能 …...

感知与认知的碰撞,大模型时代的智能文档处理范式

目录 0 写在前面1 GPT4-V&#xff1a;拓宽文档认知边界2 大语言模型的文档感知缺陷3 大一统文档图像处理范式3.1 像素级OCR任务3.2 OCR大一统模型3.3 长文档理解与应用 4 总结抽奖福利 0 写在前面 由中国图象图形学学会青年工作委员会发起的第十九届中国图象图形学学会青年科学…...

ECMAScript和JavaScript的区别

ECMAScript和JavaScript之间的关系和差异可以从以下几个方面来理解&#xff1a; 定义&#xff1a; ECMAScript&#xff1a;ECMAScript是一种由Ecma国际&#xff08;前身为欧洲计算机制造商协会&#xff0c;英文名称是European Computer Manufacturers Association&#xff09;通…...

[BUG]Datax写入数据到psql报不能序列化特殊字符

1.问题描述 Datax从mongodb写入数据到psql报错如下 org.postgresql.util.PSQLException: ERROR: invalid bytesequence for encoding "UTF8": 0x002.原因分析 此为psql独有的错误&#xff0c;不能对特殊字符’/u0000’,进行序列化&#xff0c;需要将此特殊字符替…...

用数据结构python写大数计算器

下面是一个基于Python的大数计算器的示例代码&#xff1a; class BigNumberCalculator:def __init__(self, num1, num2):self.num1 num1self.num2 num2staticmethoddef add(num1, num2):result carry 0len1, len2 len(num1), len(num2)max_len max(len1, len2)for i in …...

08.哲说建造者模式(Builder Pattern)

“The odds that we’re in ‘base reality’ is one in billions.” —— Elon Musk 这段话出自马斯克在2016年的一次演讲&#xff0c;“人类活在真实世界的几率&#xff0c;可能不到十亿分之一”。此言一出&#xff0c;可谓一石激起千层浪。有人嘲讽马斯克是“语不惊人死不休…...

ubuntu18.04查询实时内存、CPU占用率命令

gnome-system-monitor效果就是下面这样&#xff1a;...

Python计算圆的面积

Python 计算圆的面积 圆的面积公式为 &#xff1a; 公式中 r 为圆的半径。 # 定义一个方法来计算圆的面积 def findArea(r): PI 3.142 return PI * (r*r) # 调用方法 r float( input("请输入圆的半径:") ) print( "圆的面积为 %.3f&qu…...

(Java企业 / 公司项目)Nacos的怎么搭建多环境配置?(含相关面试题)(二)

上一篇讲了一个单体服务中配置&#xff0c;传统的Nacos配置但是在微服务架构当中肯定都是多环境下配置&#xff0c;比如生产环境&#xff0c;dev测试环境等等。 第一种方式模拟开始&#xff1a; 首先展示在生产环境中nacos如何配置&#xff0c;在模块下新建一个配置文件&…...

DolphinScheduler实际应用

前言 最近公司新启动了一个项目&#xff0c;然后领导想用一下新技术&#xff0c;并且为公司提供多个大数据调度解决方案&#xff0c;我呢就根据领导要求调研了下当前的开源调度工具&#xff0c;最终决定采用DolphinScheduler&#xff0c; 因此研究了一下DolphinScheduler &…...

P10 RV1126推流项目——ffmpeg输出参数初始化

前言 从本章开始我们将要学习嵌入式音视频的学习了 &#xff0c;使用的瑞芯微的开发板 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ &#x1f525; 推荐专栏2: 《Linux C应用编程&#xff08;概念类&#xff09;_C…...

正定矩阵在格密码中的应用(知识铺垫)

目录 一. 写在前面 二. 最小值点 三. 二次型结构 四. 正定与非正定讨论 4.1 对参数a的要求 4.2 对参数c的要求 4.3 对参数b的要求 五. 最小值&#xff0c;最大值与奇异值 5.1 正定型&#xff08;positive definite&#xff09; 5.2 负定型&#xff08;negative defin…...

关于使用Selenium获取网页控制台的数据

背景&#xff1a; 需要获取网页的控制台的数据&#xff0c;如下图 在此文章将使用到 Pycharm 和 Selenium4 Pycharm安装 Selenium安装 from selenium import webdriver from selenium.webdriver.common.by import By import time# 创建浏览器对象 browser webdriver.Chro…...

vue2和vue3中的路由使用及传参方式

文章目录 vue2中使用路由Vue3 中使用路由路由传参方式 Vue 2 和 Vue 3 中的路由系统有很多相似之处&#xff0c;但也存在一些重要的区别。下面将分别介绍 Vue 2 和 Vue 3 中的路由使用方式&#xff0c;并了解下它们之间的不同之处。 vue2中使用路由 在 Vue 2 中&#xff0c;通…...

论文管理器

论文管理器 这个论文管理器仍然存在许多漏洞。目前&#xff0c;通过按照一些例行程序操作&#xff0c;它可以正常工作。我将在有时间的时候改进代码&#xff0c;提供详细说明&#xff0c;并添加新功能。当该管理器的代码进行优化后&#xff0c;我会上传到github上。 一个建立…...

postfix配置tls加密

1.编译安装 编译安装openss【卸载原有openssl&#xff0c;然后下载新的安装&#xff0c;因为postfix需要新版本openssl】编译安装postfix,下面这行命令 make -f Makefile.init makefiles CCARGS"-DHAS_MYSQL -I/www/server/mysql/include -DUSE_SASL_AUTH -I/usr/include…...

虚拟专线网络(IP-VPN)

虚拟专线网络(IP-VPN)&#xff0c;因为它的安全性和可靠性。通过亚洲领先的 IP VPN 提供商。享受更高的可管理性和可扩展性&#xff0c;在多个站点之间交付 IP 流量或数据包&#xff0c;拥有亚太地区最大的 IP 骨干网。 1&#xff0c;保证正常运行时间&#xff0c;在网络链路发…...

【Unity动画系统】Unity动画系统Animation详解,参数细节你是否弄清?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…...

亚马逊卖家公开信息数据提取:反爬攻防战与 Python 批量采集实战

摘要&#xff1a; 批量获取亚马逊&#xff08;Amazon&#xff09;第三方卖家的商业名称、信用代码和注册地址等信息&#xff0c;对于跨境 B2B 拓客和供应链分析具有重要意义。然而&#xff0c;亚马逊的 Cloudflare 盾和 Robot 验证码构成了极高的反爬门槛。本文将深度解析亚马逊…...

极致精简,功能强大的PDF编辑工具

这是一款功能全面的PDF编辑工具 你只需要导入一份PDF格式文件 就可以快速的对它进行插入 批注编辑保护转换等各种操作 而且无需登录 也可以直接使用 在插入选项中可以进行插入文字图片 页面页眉页脚页码文档背景水印视频音频等 在批注选项中可以管理批注隐藏批注 高亮显示 文本…...

VMware ESXi 9.1.0.0集成NVME+网卡驱动版发布|新特性+驱动集成+部署升级+FAQ全指南

一、ESXi 9.1.0.0 正式版核心新特性 VMware ESXi 9.1.0.0&#xff08;2026 年 5 月发布&#xff09;是 vSphere 9.1 核心组件&#xff0c;聚焦硬件兼容扩展、性能跃升、安全加固、运维简化四大方向&#xff0c;重点强化 NVMe 存储与网卡生态适配&#xff0c;以下为关键更新&am…...

Windows文件夹共享

目标&#xff1a;同一局域网实现在一台计算机上共享文件夹&#xff0c;在另一台电脑访问一、电脑A 1.点击要共享的文件夹 -> 属性 -> 共享2.添加Everyone用户组3.控制面板中网络共享关闭密码保存&#xff0c;在访问时不用输入账号密码。二、电脑B 1.在文件资源管理器路径…...

艾尔登法环存档迁移终极指南:3分钟解决角色转移难题

艾尔登法环存档迁移终极指南&#xff1a;3分钟解决角色转移难题 【免费下载链接】EldenRingSaveCopier 项目地址: https://gitcode.com/gh_mirrors/el/EldenRingSaveCopier 还在为《艾尔登法环》存档版本不兼容而烦恼吗&#xff1f;EldenRingSaveCopier 是你的终极解决…...

PvZ Toolkit终极指南:三步掌握植物大战僵尸最强修改器

PvZ Toolkit终极指南&#xff1a;三步掌握植物大战僵尸最强修改器 【免费下载链接】pvztoolkit 植物大战僵尸 PC 版综合修改器 项目地址: https://gitcode.com/gh_mirrors/pv/pvztoolkit PvZ Toolkit是一款专为植物大战僵尸PC版设计的综合修改器工具&#xff0c;能够让你…...

Hermes Agent工具如何自定义接入Taotoken提供商

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 Hermes Agent工具如何自定义接入Taotoken提供商 Hermes Agent 是一款功能强大的AI智能体开发框架&#xff0c;它支持通过自定义提供…...

如何让旧款Mac运行最新系统:OpenCore Legacy Patcher完整指南

如何让旧款Mac运行最新系统&#xff1a;OpenCore Legacy Patcher完整指南 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 想让你的老旧Mac设备重新焕发活力&a…...

基于ESP8266与RGBDigit的Wi-Fi网络时钟:硬件设计、物联网集成与DIY实践

1. 项目概述&#xff1a;一个能感知环境的网络时钟如果你和我一样&#xff0c;对复古又带点科技感的显示设备没有抵抗力&#xff0c;同时又是个喜欢动手折腾的极客&#xff0c;那么这个项目绝对能让你在工作室或家里多一个既实用又炫酷的玩意儿。我说的就是这款基于RGBDigit数码…...

DRG存档编辑器终极指南:如何快速解锁《深岩银河》的全部游戏体验

DRG存档编辑器终极指南&#xff1a;如何快速解锁《深岩银河》的全部游戏体验 【免费下载链接】DRG-Save-Editor Rock and stone! 项目地址: https://gitcode.com/gh_mirrors/dr/DRG-Save-Editor 还在为《深岩银河》中无尽的资源收集和等级提升感到疲惫吗&#xff1f;DRG…...