让同事高血压的8个Bug操作集锦

2023-12-16

7baa7f2e0c874b5bb7987fdf04e6e9d6.jpeg 来源:juejin.cn/post/7222676935147651132

  • 前言

  • 案例一

  • 案例二

  • 案例三

  • 案例四

  • 案例五

  • 案例六

  • 案例七

  • 案例八


前言

本文的选题来源于一周前我的一次崩溃,当时各种压力和工作把我整的不行了,让我在短时间内改另一个同事写的代码。本来以为是一次简单的叠叠乐,没想到随便看了眼之前的代码,发现历史需求没写完,而且逻辑还有错,当时直接蚌埠住了。我的 IDEA 比较新,平时看着报错我都会去手动改一下,有时候我看着同事的代码一片黄,总是忍不住要改。久而久之也烦了,不明白为什么这么明显的 BUG 还能放在上面,于是产生了来一期盘点的想法。排名不分先后,上榜各凭本事,客官们,上菜咯!

案例一

用不明白的三元表达式能不能别用了啊,求求啦。当时看到这个地方的时候,我一眼就觉得很怪,因为我觉得他写错了,但同时我又能了解开发者的意图,这种感觉就很奇妙,我觉得我融入这个 BUG 了。

a5ccdbb5fea3bb3946191fc2aa27cd09.jpeg

图片

错误示例,这里我单独写了个例子来证明他真的写错了,和我想得一样,就是执行顺序的问题。

7b22d5f5cac33ade3f7bc39513ce4d56.jpeg

图片

正确示例,加括号,注意执行顺序,这样就对了嘛。

57546c95dcfd00e0eecd5039978526dc.jpeg

图片

案例二

参数校验真的是非常常见的场景了,有用 AOP 的。

ba8d8baad68a9f1ee44ff4243a49dd3b.jpeg

图片

还有方法里 if 判断的。

f7b66d3b9d79ca1f38238227aab77eb3.jpeg

图片

有简单的法子,他是真不用啊,注解难道不是又快又简洁吗?这还是我开发文档里写了的,我麻了,真就不看文档呗。

befc132d360c290e6100e06e26fcedc5.jpeg

图片

案例三

log 日志老是玩不对的,我也是见太多了,像这种占位符写不明白的,不是很理解。

8da157c1b943eb43929584d2a4d45f53.jpeg

图片

莫名其妙的,我发现好多人喜欢这么写,不看 API 吗,这时候想起占位符了。

bce02bdcaa4bb436301f2419f6fd6daf.jpeg

图片

传世经典之异常不打印堆栈信息,我觉得这个倒是小问题,或许有别的想法呢,但是我推荐还是记录一下比较好。

533b9d64ca34dee0fc7740f9843623f9.jpeg

图片

d1edcc8094e64afbd9bc20b4f5b1b048.jpeg

图片

案例四

意义不明的代码,要么删除要么打上注释,不要放在那里影响判断。这个问题在我去接手或者修改别人代码的时候最严重,依托答辩放在那里,没有引用也没有后续的使用,那到底是干啥的呢?我是真的猜不透啊,不敢动了。

fd45a3baa9a694ef9e6a0e3f389b52d8.jpeg

图片

案例五

严禁在方法内部自由输出错误码,同时也禁止在 service 层输出结果类,这样 controller 层将没有意义,切点在 controller 层的切面会失效。

778e01e5df610efe65a087225b56f59f.jpeg

图片

异常需使用指定异常或者继承了指定异常的异常类抛出,这样才能抓到错误码,否则将被全局监听覆盖错误码和错误信息

1e9f1b3b10f2acf673a69a73f5b79412.jpeg

图片

同时也不建议在有框架保底的情况下在 controller 层写过多的代码,这种 try catch 能用全局异常监听能处理的就不要自己写,用好异常和错误码即可。

2550bd7fd537a81e7ebba77cd120e744.jpeg

图片

正常像这样简洁方便的写就好了,在 service 层处理的时候抛出自定义的异常即可。这个写法是两年前我做的一次优化,本以为不会出现类似问题了,结果最近的代码中又发现了,真是不改嗷。

09a79d7d52ca82dc4770ad8d7df2e04e.jpeg

图片

案例六

经典自调用切面失效场景,这个其实注意一下就可以避免的,但是很多时候就是写着写着就忘了,非得报错才知道写错了。

4ea272c95415c3f36c092f2ea7b75a22.jpeg

图片

最便捷的方法就是注入本体,再调用。

07b1a4ba29bfda1dcd4d4a5454f2b56b.jpeg

图片

案例七

在使用 Redis 等中间件相关的操作框架比如 Redisson 时,要特别注意不要让中间件宕机时影响程序的正常运行。最近 Redis 宕机了,猛然发现这个问题,想了下确实也不能怪框架,毕竟没有数据和报错确实是两码事,这个只能开发者自己控制。

@Autowired
private RedissonClient redissonClient;
//原始版本
public BigDecimal getIntervalQty(int itemId, Date startDate, Date endDate) {
    String cacheKey = "dashboard:intervalQty:" + itemId + "-" + startDate + "-" + endDate;
    RBucket<BigDecimal> bucket = redissonClient.getBucket(cacheKey);
    BigDecimal cacheValue = bucket.get();
    if (cacheValue != null) {
        return cacheValue;
    } else {
        BigDecimal intervalQty = erpInfoMapper.getIntervalQty(itemId, startDate, endDate);
        BigDecimal res = Optional.ofNullable(intervalQty).orElse(BigDecimal.valueOf(0)).setScale(2,
                RoundingMode.HALF_UP);
        bucket.set(res, 16, TimeUnit.HOURS);
        return res;
    }
}
//更新避免Redis报错版本
public String getProductLine(String itemNo) {
        String cacheKey = "order:getProductLine:" + itemNo;
        String cacheValue = null;
        RBucket<String> bucket = redissonClient.getBucket(cacheKey);
        try {
            cacheValue = bucket.get();
        } catch (Exception e) {
            log.error("redis连接异常", e);
        }
        if (cacheValue != null) {
            return cacheValue;
        } else {
            String res = ptmErpMapper.getProductLine(itemNo);
            bucket.set(res, 16, TimeUnit.HOURS);
            return res;
        }
 }

案例八

这是我写的业务组件的一个 BUG,是一个上传文件导入时会报文件找不到的问题,正常我是让大家这么写 Excel 文件上传。

通用Controller层
@LimitMethod
@PostMapping("/import")
public RemoteResult<String> importAdd(@RequestParam("file") MultipartFile multipartFile)

1. 第一步生成单号或者标识,userId之类的最好也取出来,做好传递的准备

identifier标识可使用LuaTool生成
String generateOrder = luaTool.generateOrder("SMB-PRODUCT-");
生成局部变量方便线程间数据传递
RequestContext.getCurrentContext()或者使用CurrentUserUtil工具类(sso-zero提供)

2. 第二步调用commonImportExcel方法读取并传递文件到PTM2.0(这一步必须放在外面,是对excel的基本校验,有错误及时推送前端,不能异步)

该方法包含对excel的基本校验,并且自带上传文件服务器以及传递PTM
long ptmFileId = excelTool.commonImportExcel(file, generateOrder);

3. 开启异步,使用readFile或者readMultipartFile解析文件,并进行业务处理
如果此部分需要事务,请另起一个事务类,使用@Transactional(rollbackFor = Exception.class)或者在当前代码区域手动开启事务或者自注入再调用方法。

CompletableFuture.runAsync(() -> {
        读取文件,readMultipartFile方法会调用Easy Excel解析读取excel,如果有读取错误会抛出错误,方法入参中有表头校验,选择true会校验表头是否正确,不正确会抛异常
        CustomizedExcelListener excelListener = new CustomizedExcelListener();
        EasyExcel.read(finalInputStream, MaterialChipImportDTO.class, excelListener).sheet().doRead();
        ExcelAnalyzeResDTO analyzeRes = excelListener.getExcelData();       
        List<MaterialChipImportDTO> judgeMaterialList = (List<MaterialChipImportDTO>) analyzeRes.getExcelDataList();
        //业务处理,这里MaterialChipImportDTO导入类需要冗余一个异常信息字段errMsg,业务处理的时候把错误信息塞进去
        List<MaterialChipImportDTO> afterList = judgeChipImport(judgeMaterialList, beforeAllList, isAdd);
        //判断errMsg字段是否有值,有值说明这一行有业务逻辑错误
        MaterialChipImportDTO orElse = judgeMaterialList.stream()
                .filter(ma -> StringUtils.isNotBlank(ma.getErrMsg())).findAny().orElse(null);
        //可选,异常文件导出
        if (orElse != null) {
            String fileName = "错误提示文件-" + finalGenerateOrder;
            excelTool.synchronizeExportExcel(judgeMaterialList, MaterialChipImportDTO.class, fileName, fileName,userId, userName);
            throw new PtmException("文件校验有错误项,请下载错误提示文件");
        } else {
            //继续业务操作
        }
    }
})

4. handle处理部分,调用finishFileStatus方法回传PTM2.0状态,注意这里的ptmFileId是指PTM文件列表的id
.handle((res, e) -> {
    if (e != null) {
        log.error("物料风险地图芯片导入异步处理数据失败,流程单号:{},异常信息:", generateOrder, e);
        excelTool.finishFileStatus(ptmFileId, null, null, ExcelFieldConstant.TYPE_IMPORT,
ExcelFieldConstant.IMPORT_FAILED, e.getMessage());
    } else {
        excelTool.finishFileStatus(ptmFileId, null, null, ExcelFieldConstant.TYPE_IMPORT,ExcelFieldConstant.IMPORT_SUCCESS, null);
    }
    return null;
});

通过以上伪代码可以做到 Excel 上传校验解析以及与文件服务器和主站的数据交互。

在给同事使用的过程中会偶发文件找不到的问题,经过定位后发现是异步开启后主线程处理完毕关闭 MultipartFile的 IO 流,使得上传的临时文件删除的问题。解决方案很直接,将上传的 multipartFile 转换成一个本地临时文件 flie,用这个临时文件在异步线程中解析,避免文件丢失,在使用完文件之后再主动删除。

/**
 * @param mFile      上传文件
 * @param identifier PTM2.0文件列表查询参数:唯一标识
 * @Author WangZY
 * @Date 2021/12/14 14:25
 * @Description 导入文件--文件下载链接10天有效期
 * @return 文件列表ID
 **/
 public ExcelUploadResDTO commonImportExcel(MultipartFile mFile, String identifier) {
        if (mFile.isEmpty()) {
            throw new PtmException("上传excel文件不能为空");
        } else {
            String fileName = mFile.getOriginalFilename();
            if (StringUtils.isEmpty(fileName)) {
                throw new PtmException("excel名称不能为空");
            } else {
                if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
                    throw new PtmException("excel格式不正确");
                } else {
                    //组装Token和文件服务器权限信息发送请求
                    String token = getToken(true, 10);
                    if (mFile.getSize() <= 0) {
                        throw new PtmException("上传文件为空");
                    } else {
                        // 先判断文件夹是否存在,避免不存在时报错
                        String fileDir = commonProperties.getFileDir();
                        File filePathExist = new File(fileDir);
                        if (!filePathExist.exists()) {
                            boolean mkdir = filePathExist.mkdirs();
                            if (!mkdir) {
                                return null;
                            }
                        }
                        //将multipartFile转换为临时文件file,避免异步时子线程找不到文件实例
                        File file = new File(fileDir + mFile.getOriginalFilename());
                        try (BufferedInputStream bis = new BufferedInputStream(mFile.getInputStream());
                             BufferedOutputStream bos =
                                     new BufferedOutputStream(Files.newOutputStream(file.toPath()))) {
                            int bytesRead = 0;
                            byte[] buffer = new byte[8192];
                            while ((bytesRead = bis.read(buffer, 0, 8192)) != -1) {
                                bos.write(buffer, 0, bytesRead);
                            }
                        } catch (Exception e) {
                            log.error("multipartFileToFile失败", e);
                        }
                        String fileId = ExternalApi.uploadFileServer(token, file, splicingFileServerUrl("upload"));
                        //组装文件信息,在PTM文件列表创建一条处理中的记录
                        long ptmFileId = createPtmFile(fileId, fileName, identifier, true);
                        return new ExcelUploadResDTO(ptmFileId, file);
                    }
                }
            }
        }
 }
 /**
 * @param file  文件
 * @param clazz 类
 * @Author WangZY
 * @Date 2020/12/12 14:24
 * @Description 读文件-文件使用后删除
 **/
public List<?> readFile(File file, Class<?> clazz, boolean headCheck) {
    ExcelListener excelListener = new ExcelListener();
    EasyExcel.read(file, clazz, excelListener).sheet().doRead();
    ExcelAnalyzeResDTO excelData = excelListener.getExcelData();
    if (headCheck) {
        checkHeadRight(clazz, excelData);
    }
    String dateError = excelData.getDateError();
    log.info("完成文件解析,删除文件名={},临时文件结果={}", file.getName(), file.delete());
    if (!StringUtils.isEmpty(dateError)) {
        throw new PtmException(dateError);
    } else {
        return excelData.getExcelDataList();
    }
}

这个 Excel 业务模块目前已经使用了两年时间,前后更新了十余次吧,非常稳定的一个组件。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

让同事高血压的8个Bug操作集锦 的相关文章

随机推荐

  • 【UE 材质】角色触碰空气墙效果

    效果 步骤 1 新建一个工程 创建一个Basic关卡 添加一个第三人称游戏资源到内容浏览器 2 新建一个材质参数集 这里命名为 MPC Vector 打开 MPC Vector 添加一个向量参数 3 新建一个材质 这里命名为 M Wall
  • 【UE5.1 MetaHuman】使用mixamo_converter把Mixamo的动画重定向给MetaHuman使用

    目录 前言 效果 步骤 一 下载mixamo converter软件 二 Mixamo动画重定向 三 导入UE 四 动画重定向 五 使用重定向后的动画 前言 上一篇 UE5 初识MetaHuman 创建虚拟角色 中我们已经制作了一个Meta
  • 【UE】制作熔岩星球材质

    效果 步骤 1 新建一个工程 创建一个Basic关卡 添加第三人称游戏和初学者内容包资源到内容浏览器 2 新建一个材质 这里命名为 M Sun 打开 M Sun 添加两个Texture节点 纹理分别为 T Rock Basalt N 和 T
  • 【UE5】监控摄像头效果(上)

    目录 效果 步骤 一 视角切换 二 摄像头画面后期处理 三 在场景中显示摄像头画面 效果 步骤 一 视角切换 1 新建一个Basic关卡 添加第三人称游戏资源到项目浏览器 2 新建一个Actor蓝图 这里命名为 BP SecurityCam
  • pico示波器使用

    文章目录 Pico示波器保存波形 Pico示波器录制数据 Pico示波器解析CAN报文 Pico示波器保存波形 Pico示波器可以通过以下步骤保存波形 在示波器上选择要保存的波形 连接示波器到计算机上 可以使用USB或者Ethernet连接
  • 头歌——HBase 开发:使用Java操作HBase

    第1关 创建表 题目 任务描述 本关任务 使用 Java 代码在 HBase 中创建表 相关知识 为了完成本关任务 你需要掌握 1 如何使用 Java 连接 HBase 数据库 2 如何使用 Java 代码在 HBase 中创建表 如何使用
  • 【UE5】瞬移+马赛克过渡效果

    效果 步骤 1 新建一个工程 创建一个Basic关卡 2 添加第三人称游戏资源到内容浏览器 3 新建一个材质 这里命名为 M Pixel 打开 M Pixel 设置材质域为 后期处理 在材质图表中添加如下节点 此时效果如下 已经有马赛克的效
  • 【3DsMax】制作简单的骨骼动画

    效果 步骤 首先准备4个板子模型展开放置好 添加一个4段的骨骼 选中其中的一块板子添加蒙皮命令 在蒙皮的参数面板中 设置每块板子对应哪块骨骼 设置好后你可以发现此时就已经可以通过骨骼来控制模型了 接下来就可以制作动画 点击左下角 时间配置
  • 【UE】制作地月全息投影

    效果 步骤 1 在必应国际版上搜索 purlin noise 下载如下所示图片 再搜索 Earth Map 下载如下所示图片 再搜索 Moon 360 下载如下所示图片 这三张图片的资源链接如下 链接 https pan baidu com
  • python在车载电子测试方面的应用笔记【1】

    文章目录 在DataFrame中某列插入数据 并根据另一列查找是否存在某个字符串完全一样 在另一列插入对应数据的功能 删除DataFrame某列数据长度大于6的数据 使用 PyInstaller 打包成一个独立的 exe 文件 通过检索空格
  • 通过kubeadm方式安装k8s

    虚拟机最少是 2 core master内存最小3G node内存最小2G 要求的Docker版本是18 03 如果不是安装的docker ce 版本是过旧的 可以选择删除后重新安装 也可以重新创建一个虚拟机执行以下命令 简单方法 使用ma
  • Docker build 无法解析域名

    报错 Docker build 无法解析域名 报错 ERROR 2 12 RUN curl o etc yum repos d CentOS Base repo https mirrors aliyun com repo Centos 7
  • 安装 运行 gemmini 和chipyard

    安装gemmini 和chipyard过程 安装版本 chipyard 版本是1 8 1 gemmini版本0 7 0 tip 如果在base里安装conda lock觉得缓慢 可以新建新的环境时就指定安装conda lock conda
  • GoLong的学习之路,进阶,微服务之序列化协议,Protocol Buffers V3

    这章是接上一章 使用 RPC包 序列化中没有详细去讲 因为这一块需要看的和学习的地方很多 并且这一块是RPC中可以说是最重要的一块 也是性能的重要影响因子 今天这篇主要会讲其使用方式 文章目录 Protocol Buffers V3 背景以
  • GoLong的学习之路,进阶,微服务之使用,RPC包(包括源码分析)

    今天这篇是接上上篇RPC原理之后这篇是讲如何使用go本身自带的标准库RPC 这篇篇幅会比较短 重点在于上一章对的补充 文章目录 RPC包的概念 使用RPC包 服务器代码分析 如何实现的 总结 Server还提供了两个注册服务的方法
  • 车载以太网笔记

    文章目录 以太网协议分层 协议 中间设备 子网掩码 物理层 测试 内容比较杂 后续会整理 以太网协议分层 协议 中间设备
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)

    WebAppDbTest 项目准备 项目准备 1 net cli 创建项目 2 nuget 包引用和项目结构 2 1 项目添加相关 nuget 包 2 2 WebAppDbTest 项目结构 3 项目代码说明
  • ETL.NET 助力海量数据轻松处理

    ETL NET 助力海量数据轻松处理 什么是 ETL EtlT About ETL About EtlT 谈谈 ETL 作用 ETL 对企业的作用 ETL 对个人职业发展的作用 ETL NET 介绍
  • 一篇能让你彻底搞明白前复权、后复权、不复权的好文

    当我们打开炒股软件看K线走势图时 点击鼠标右键会看到有前复权 后复权 不复权三种模式形态 有的股票三种模式下形态差别很大 选错模式 甚至有的股票用不复权处在底部 用前复权处在顶部 股票位置的高低都分不清 又怎么能选好股票呢 今天用最容易理解
  • 让同事高血压的8个Bug操作集锦

    来源 juejin cn post 7222676935147651132 前言 案例一 案例二 案例三 案例四 案例五 案例六 案例七 案例八 前言 本文的选题来源于一周前我的一次崩溃 当时各种压