首页
学习
活动
专区
圈层
工具
发布

兄弟,你写的收货地址接口,真垃圾。。

昨天晚上加班那会儿,已经十一点多了,楼下都没什么人了,我正好在那抽烟顺便刷了下群消息,结果公司群里突然小李问我“收货地址接口是不是你刚才提的bug啊?”我说咋了,他说检查代码的时候,发现前端传default_flag=1之后,数据库里怎么会出现两个默认地址?他还挺懵,说他理解是只要前端传了1,就直接给那条设成默认的,其他不用管。你说这不就出事儿了嘛。

其实这事儿特别常见,尤其那种电商项目,用户一个账号最多只能有一个默认地址嘛,你不能让他有俩,对吧?我当时让小李用Postman随便加了几条地址,然后查数据库,果然能查出俩default_flag=1。我说你这个逻辑没加限制吧?他说“不是,前端已经传了参数啊。”我说你是不是只在意前端传啥了,没管数据库实际存了几个默认的?然后他才反应过来,好像确实是。

后来让他改了,几个小时后又给我看一遍代码,基本功能没啥毛病了,就是想让我再看看还能怎么优化优化。其实这类逻辑写多了,大家都知道,关键是你后端得兜底,前端传啥都不保险,你不能让客户端来控制“只有一个默认”的规则,这种必须写死在服务端。

讲讲这个接口咋写的

假设你就是用Spring Boot写的,常规流程:新增或者更新收货地址的时候,判断一下default_flag是不是1,如果是,就把这个用户下面所有地址的default_flag都先清零,然后再把当前这条设成1。

原来小李那代码,大概长这样(当然是有问题的):

if (address.getDefaultFlag() == 1) {

  // 只给当前地址设默认,没管其他

  address.setDefaultFlag(1);

} else {

  address.setDefaultFlag(0);

}

addressRepository.save(address);

你看这样一搞,数据库里可能N条都是默认。

后来我让他改成这样:

if (address.getDefaultFlag() == 1) {

  // 先把当前用户所有地址的 default_flag 都设为0

  addressRepository.resetDefaultFlag(userId);

  // 然后当前这条设为默认

  address.setDefaultFlag(1);

} else {

  address.setDefaultFlag(0);

}

addressRepository.save(address);

resetDefaultFlag其实就是一句SQL:

@Modifying

@Query("update Address a set a.defaultFlag = 0 where a.userId = :userId")

void resetDefaultFlag(@Param("userId") Long userId);

就这么简单,但很多人第一反应还真不是这么写的。

其实你要说代码还有啥提升空间,我觉得可以多考虑下事务安全。像这种批量update再insert/update当前那条,最好包一层事务,Spring直接加个@Transactional就完事了,否则你要是两步操作间服务器宕机,可能又出幺蛾子。

比如这样:

@Transactional

public void saveAddress(Address address, Long userId) {

  if (address.getDefaultFlag() == 1) {

      addressRepository.resetDefaultFlag(userId);

      address.setDefaultFlag(1);

  } else {

      address.setDefaultFlag(0);

  }

  addressRepository.save(address);

}

还有就是你新建地址的时候,用户如果一条都没有,那应该自动设成默认。这个逻辑得加上:

long count = addressRepository.countByUserId(userId);

if (count == 0) {

  address.setDefaultFlag(1);

}

别的也没啥了,唯一要吐槽的就是大家别偷懒,别老想着让前端兜底,数据一致性还是得靠服务端。小李也是年轻,学费交得有点多。

我说你们遇到这种情况会咋处理?还有什么骚操作没?

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OTT2lkSY_nR-F31kp4xbvZtw0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券