自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

【故障現(xiàn)場】將變更收斂在一處,避免散彈式更新

開發(fā) 前端
深入思考,該問題的本質(zhì)就是:對信息沒有進行統(tǒng)一維護,導致同一份數(shù)據(jù)在多個地方進行管理,當發(fā)生變化時只要有一處未及時更新便會出現(xiàn)問題。

1. 問題&分析

使用 code 真香,終于不用擔心枚舉重構(gòu)了,但還是高興的太早了,一個線上bug正在路上….

1.1. 案例

經(jīng)過連續(xù)多天奮戰(zhàn),系統(tǒng)終于上線了訂單手工取消功能,剛剛上線便收到客服部門的反饋:訂單列表中訂單狀態(tài)出現(xiàn)問題,顯示未 undefine。小艾趕緊查看后端日志,沒有發(fā)現(xiàn)任何異常,并緊急給前端負責人虎哥掛了個電話,很快虎哥便定位原因并進行緊急修復(fù)。

事后復(fù)盤,原因是這樣的:

  1. 在訂單列表接口中,后端只返回了枚舉的 name
  2. 前端維護了一個配置文件,key 是 name,value 是顯示名稱,從接口獲取 name 后會基于配置文件進行轉(zhuǎn)換,最終展示為 描述信息
  3. 本次修改,只改了主站的 js 配置,遺漏了客服系統(tǒng)。所以,主站沒有問題,而客服系統(tǒng)由于找不到新加的name,所以展示為 undefine

后端返回結(jié)果如下圖所示:

圖片圖片

默認情況下,枚舉只會返回 Name,非常不利于展示,所以在前端會進行一次翻譯,將 Name 翻譯成展示文案。

在這個接口的基礎(chǔ)上引起的問題如下圖所示:

圖片圖片

由于業(yè)務(wù)發(fā)展,OrderStatus 的枚舉值發(fā)生了變化,但只對主站頁面進行調(diào)整,而客服系統(tǒng)被遺漏。所以:

  1. 主站頁面有最新的全量配置,信息展示準確沒有任何問題
  2. 客服系統(tǒng)由于被遺忘使用的還是之前的配置,導致后端返回的 Name 和 配置信息不一致,由于找不到 Name 而出現(xiàn) undefine 錯誤

1.2. 問題分析

深入思考,該問題的本質(zhì)就是:對信息沒有進行統(tǒng)一維護,導致同一份數(shù)據(jù)在多個地方進行管理,當發(fā)生變化時只要有一處未及時更新便會出現(xiàn)問題。

那解法也就很簡單了,將信息收口到后端進行統(tǒng)一管理!

除這個問題外,還有一個非常類似的問題:前端下拉列表,也需要和后端定義保持一致,一般情況下:

  1. 前端單獨維護,寫死在頁面,當后端發(fā)生變化后,前端跟著一起調(diào)整。這個方案就會出現(xiàn)兩者不一致的問題,不鼓勵使用;
  2. 后端提供一個接口用于獲取數(shù)據(jù),然后在渲染到前端組件。這個是鼓勵的方案,但每個枚舉都需要提供一個接口,增加了后端的開發(fā)負擔;

2. 解決方案

和 code 方案一致,可以使用接口對枚舉進行約束。

2.1. 構(gòu)建統(tǒng)一接口

首先,定義統(tǒng)一的接口,用于提供描述信息:

public interface SelfDescribedEnum {
    default String getName(){
        return name();
    }

    String name();
    /**
    * 獲取描述信息
    */
    String getDescription();
}

2.2. 枚舉實現(xiàn)接口

然后,讓我們的枚舉實現(xiàn) SelfDescribedEnum 接口,具體如下:

public enum SelfDescribedEnumBasedOrderStatus implements SelfDescribedEnum {
    CREATED("待支付"),
    TIMEOUT_CANCELLED("超時取消"),
    MANUAL_CANCELLED("手工取消"),
    PAID("支付成功"),
    FINISHED("已完成");
    private final String description;

    SelfDescribedEnumBasedOrderStatus(String description) {
        this.description = description;
    }

    @Override
    public String getDescription() {
        return description;
    }
}

2.3. 集成 Spring MVC 返回結(jié)果

在完成上述工作后,我們將 OrderVO 中的 status 屬性類型更新為 SelfDescribedEnumBasedOrderStatus,具體如下:

@Data
public class OrderVO {
    private Long id;
    private SelfDescribedEnumBasedOrderStatus status;
}

最后一步也是最關(guān)鍵的一步便是,對 Jackson 序列化器進行定制,核心代碼如下:

@Configuration
public class SelfDescribedEnumJacksonCustomizer {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer commonEnumBuilderCustomizer(){
        return builder ->{
            // 注冊自定義枚舉序列化器
            builder.serializerByType(SelfDescribedEnum.class, new SelfDescribedEnumJsonSerializer());
        };
    }

    static class SelfDescribedEnumJsonSerializer extends JsonSerializer {

        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            SelfDescribedEnum selfDescribedEnum = (SelfDescribedEnum) o;
            SelfDescribedEnumVO selfDescribedEnumVO = SelfDescribedEnumVO.from(selfDescribedEnum);
            jsonGenerator.writeObject(selfDescribedEnumVO);
        }
    }
}

// SelfDescribedEnumVO 為定義的一個 VO,具體如下:
@Data
public class SelfDescribedEnumVO {
    @ApiModelProperty(notes = "Name")
    private final String name;

    @ApiModelProperty(notes = "描述")
    private final String desc;

    public static SelfDescribedEnumVO from(SelfDescribedEnum selfDescribedEnum){
        if (selfDescribedEnum == null){
            return null;
        }
        return new SelfDescribedEnumVO(selfDescribedEnum.getName(), selfDescribedEnum.getDescription());
    }

    public static List<SelfDescribedEnumVO> from(List<SelfDescribedEnum> commonEnums){
        if (CollectionUtils.isEmpty(commonEnums)){
            return Collections.emptyList();
        }
        return commonEnums.stream()
                .filter(Objects::nonNull)
                .map(SelfDescribedEnumVO::from)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }
}

最后,啟動服務(wù)查看新返回值,具體如下:

圖片圖片

可以看,status 字段原本只返回了 name,現(xiàn)在返回的是一個包括 name 和 desc 的對象。前端無需進行轉(zhuǎn)換,只需直接讀取 status.desc 信息即可。

2.4. 提供統(tǒng)一字典服務(wù)

對于下來列表、選擇框的場景,最優(yōu)方案是為前端提供一個統(tǒng)一的字典接口,由該接口來返回所有字典信息。

核心代碼如下:

public class EnumDictController {
    private Map<String, List<SelfDescribedEnum>> enumDict = new HashMap<String, List<SelfDescribedEnum>>();

    public EnumDictController(){
        add("OrderStatus", SelfDescribedEnumBasedOrderStatus.values());
    }

    private void add(String type, SelfDescribedEnumBasedOrderStatus[] values) {
        this.enumDict.put(type, Arrays.asList(values));
    }

    /**
     * 獲取所有字典信息
     * @return
     */
    @GetMapping("all")
    public RestResult<Map<String, List<SelfDescribedEnumVO>>> allEnums(){
        Map<String, List<SelfDescribedEnumVO>> dictVo = Maps.newHashMapWithExpectedSize(enumDict.size());
        for (Map.Entry<String, List<SelfDescribedEnum>> entry : enumDict.entrySet()){
            dictVo.put(entry.getKey(), SelfDescribedEnumVO.from(entry.getValue()));
        }
        return RestResult.success(dictVo);
    }

    /**
     * 獲取支持的全部字典類型
     * @return
     */
    @GetMapping("types")
    public RestResult<List<String>> enumTypes(){
        return RestResult.success(Lists.newArrayList(enumDict.keySet()));
    }

    /**
     * 獲取指定字典的全部值
     * @param type
     * @return
     */
    @GetMapping("/{type}")
    public RestResult<List<SelfDescribedEnumVO>> dictByType(@PathVariable("type") String type){
        List<SelfDescribedEnum> enums = enumDict.get(type);

        return RestResult.success(SelfDescribedEnumVO.from(enums));
    }
}

啟動服務(wù),驗證字典接口。

獲取全部字典信息,返回結(jié)果如下:

圖片圖片

一次性返回全部字典對性能有損耗,那可以返回指定字典,結(jié)果如下:

圖片圖片

此時,前端只需從接口中獲取所需要的數(shù)據(jù),無需在 js 中進行單獨維護。

3. 示例&源碼

代碼倉庫:https://gitee.com/litao851025/learnFromBug

代碼地址:https://gitee.com/litao851025/learnFromBug/tree/master/src/main/java/com/geekhalo/demo/enums/descr


責任編輯:武曉燕 來源: geekhalo
相關(guān)推薦

2025-02-13 00:34:22

Spring對象系統(tǒng)

2009-06-06 09:07:05

微軟蓋茨莊園

2019-10-09 13:39:39

Python編程語言異常錯誤

2014-08-21 14:49:32

MIUI 6

2017-05-11 08:46:35

全閃存數(shù)據(jù)中心容量

2023-08-29 17:40:28

技術(shù)大會

2018-04-27 14:18:01

2024-08-09 08:25:32

Spring流程注解

2025-03-12 08:00:00

單點登錄單設(shè)備登錄程序

2009-06-12 16:55:10

VPN客戶端故障

2010-11-19 17:11:48

RG-SSO組件五位一體銳捷網(wǎng)絡(luò)

2023-12-05 14:10:00

接口可讀性

2019-08-22 14:02:00

Spring BootRestful APIJava

2021-12-02 07:50:30

NFS故障內(nèi)存

2024-01-29 09:22:59

死鎖線程池服務(wù)

2022-01-17 09:19:12

Transformer數(shù)據(jù)人工智能

2020-05-26 13:48:05

后端框架異常

2025-02-05 11:30:00

單點故障MySQL數(shù)據(jù)庫

2021-04-14 17:47:12

聯(lián)想

2020-02-19 08:00:00

微服務(wù)架構(gòu)分布式代碼
點贊
收藏

51CTO技術(shù)棧公眾號