itest 開源測試管理項目中封裝的下拉列表小組件:實現下拉列表使用者前後端0行代碼

来源:https://www.cnblogs.com/mypm/archive/2019/04/17/10723039.html
-Advertisement-
Play Games

導讀: 主要從4個方面來闡述,1:背景;2:思路;3:代碼實現;4:使用 一:封裝背景 像easy ui 之類的純前端組件,也有下拉列表組件,但是使用的時候,每個下拉列表,要配一個URL ,以及設置URL反回來的值和 select 的text ,和value 的對應關係 ,這有2個問題:一使用者必須 ...


導讀:

主要從4個方面來闡述,1:背景;2:思路;3:代碼實現;4:使用

一:封裝背景

      像easy ui 之類的純前端組件,也有下拉列表組件,但是使用的時候,每個下拉列表,要配一個URL ,以及設置URL反回來的值和 select 的text ,和value 的對應關係 ,這有2個問題:一使用者必須知道URL ,二,如果頁面有10個下拉表表,要請求後臺10次,肯定影響性能,而我想要的是使用者只要申明用哪個數據字典就行了,其他根本不用操心,另外加上在做itest開測試測試管理項目的時候,有幾個頁面,特別多下拉列表,且是動態數據,到處都有處理下拉表列表,後臺代碼還好,前端到處都要用JS處理,就算是用vue ,或理angular JS 一樣要處理,我這人又很懶, 最怕重覆的代碼,千女散花似的散落在各個角落中,一不做,二不休乾脆不如簡單的寫一個組件(前後端都有的),讓使用者前後端0行代碼。我們先來看看一下,itest 開源測試管理項目中這個界面,下拉列表,多得頭大,處理不好,會很慢。可以在這體驗這個多下拉列表頁面(點測試,然後選擇一個項目,然後點缺陷管理,再點增加),體驗地址:https://itest.work/rsf/site/itest/product/index.html   然後點線上體驗 

 

 二:封裝實現思路

     (1) 後端,第1步,字典對像維護:項目中所有字典放一張表中,定義了一個完整的父類,子類只要通過@DiscriminatorValue 註解標明某個字典,在字典分類欄位上的值就行

    (2) 後端,第2步,寫一個初始化字典的工具類,主要完成的功能,一是緩存字典數據,二提供,把某個list 的對像中的字典屬性轉換為他的名稱,如把性別中的0轉為“男”,1 轉為女,這個轉換主要是為前端 表格組件用,在後臺把轉換了,不用前臺再加format 之類的函數處理

    (3) 後端,第3步,對前端實現rest 介面,返回下拉列表數據,參數:前端下拉表的元素ID用逗號拼成的串,,以及他對應的字典類型和逗號拼成的串,這麼做是實現,批量一次以MAP返回前端所有下拉列表的數據,Map<String,List<BaseDictionary>>,key 為字前端下拉表列元素的ID,value 是一個字典對像的list

    (4) 寫一個公用JS ,描掃頁面中的下拉列表對像,獲取其ID,同時 獲取,下拉表中自定義的用於標識字典類型的html 屬性,把這些按對應的順序拼為(3)中描述的兩個以逗號隔開的字元串

  

三:代碼實現

      (1) BaseDictionary   抽像類定義字典對像的通用的方法

      (2) Dictionary 繼承 BaseDictionary   ,Dictionary是所有字典類對像的實體類父類,採用子類和父類共一張表的策略  ,Dictionary   類上的註解如下

    @Entity
         @Table(name = "t_dictionary")
           @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
           @DiscriminatorColumn(
              name = "type_name",
               discriminatorType = DiscriminatorType.STRING
            )
         @DiscriminatorValue(value = "all")

 

      其他字典類,只要申明一下就行,如下麵的姓別,主要是要用 DiscriminatorValue註解申明t_dictionary表中的type_name 欄位的值為什麼時表示這個子類,下麵示例表示  type_name   為Gender 表示姓別的字典類

@Entity
@DiscriminatorValue("Gender")
public class Gender extends Dictionary{

}

 (3) DictionaryCacheServiceImpl ,實現DictionaryCacheService 接中,同時定義一個init 方法,來載入字典到緩存 通過@PostConstruct 註解告訴spring ,構造這個對像完後成,就執行init 方法

 (4) DictionaryHolderService  ,實現public Map<String, String> getDictionaryHolder() ,方法,一個靜態的MAP, key 是字典類型,也就是具體的字典子類中,@DiscriminatorValue註解的值,value 就是 字典包名的類名,DictionaryCacheServiceImpl,通過這介面,知道有哪些字典類,然後載入到緩存中,後續版本我們通過spi 實現 DictionaryHolderService ,有老項目, 他們直接在 applicationContext.xml 中配置一個MAP ,

 (5) DictionaryRest  ,提供rest 介面供前端調用

 (6) 前端公用JS ,只要引入該JS,他會自動掃描頁面上的下拉表組件,後來我們實現了jquery 版本,easy ui 版,angular 版本

另外,現在公司內部,我們字典,後端做成兩部分,上面描述的我稱作自定議欄位,是項目內部字典,還有一個公共字典,在前端,在自定義HTML 屬性中,除了字典屬性外,還有一個是自定議的,還是公用的;公用的做成一個微服務了,只要POM中引入相關包就行了

   

上面簡單回顧了一個實現思路,下麵就上代碼:

BaseDictionary

public abstract class BaseDictionary {

    public abstract String getDictId();

    public abstract String getDesc();

    public abstract String getValue();

}

Dictionary

/**
 * <p>標題: Dictionary.java</p>
 * <p>業務描述:字典公共父類</p>
 * <p>公司:itest.work</p>
 * <p>版權:itest 2018 </p>
 * @author itest  andy 
 * @date 2018年6月8日
 * @version V1.0 
 */
@Entity
@Table(name = "t_dictionary")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name = "type_name",
    discriminatorType = DiscriminatorType.STRING
)
@DiscriminatorValue(value = "all")
public class Dictionary extends BaseDictionary implements Serializable {
    

    private static final long serialVersionUID = 1L;
    
    private Integer dictId;
    private String desc;
    private String value;
    
    public Dictionary() {
    
    }
    
    public Dictionary(Integer dictId) {
        this.dictId = dictId;
    }
    
    public Dictionary(Integer dictId, String desc, String value) {
        this.dictId = dictId;
        this.desc = desc;
        this.value = value;
    }
    
    /**  
     * @return dictId 
     */
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "ID", unique=true, nullable=false, length=32)
    public Integer getDictId() {
        return dictId;
    }

    /**  
     * @param dictId dictId 
     */
    public void setDictId(Integer dictId) {
        this.dictId = dictId;
    }

    /**  
     * @return desc 
     */
    @Column(name = "lable_text", length = 100)
    public String getDesc() {
        return desc;
    }

    /**  
     * @param desc desc 
     */
    public void setDesc(String desc) {
        this.desc = desc;
    }

    /**  
     * @return value 
     */
    @Column(name = "value", length = 100)
    public String getValue() {
        return value;
    }

    /**  
     * @param value value 
     */
    public void setValue(String value) {
        this.value = value;
    }

}

 

DictionaryCacheServiceImpl

package cn.com.mypm.framework.app.service.dictionary.impl;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import cn.com.mypm.framework.app.dao.common.CommonDao;
import cn.com.mypm.framework.app.entity.dictionary.BaseDictionary;
import cn.com.mypm.framework.app.service.dictionary.DictionaryCacheService;
import cn.com.mypm.framework.app.service.dictionary.DictionaryHolderService;
import cn.com.mypm.framework.common.SpringContextHolder;
import cn.com.mypm.framework.utils.ItestBeanUtils;
@Service("dictionaryCacheService")
@DependsOn("springContextHolder")
public class DictionaryCacheServiceImpl implements DictionaryCacheService {

    private static Log log = LogFactory.getLog(DictionaryCacheServiceImpl.class);

    private static DictionaryHolderService dictionaryHolder;
    /**
     * 
     */
    private static Map<String, List<BaseDictionary>> direcListMap = new HashMap<String, List<BaseDictionary>>();
    /**
     * key 為字典type value 為某類欄位的map 它的key為字典value ,value這字典的名稱
     */
    private static Map<String, BaseDictionary> dictionaryMap = new HashMap<String, BaseDictionary>();

    public DictionaryCacheServiceImpl() {

    }

    @PostConstruct
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void init() {
        try {
            if (SpringContextHolder.getBean("dictionaryHolderService") != null) {
                dictionaryHolder = SpringContextHolder.getBean("dictionaryHolderService");
            }

            Iterator<Map.Entry<String, String>> it = dictionaryHolder.getDictionaryHolder().entrySet().iterator();
            CommonDao commonDao = SpringContextHolder.getBean("commonDao");
            while (it.hasNext()) {
                Map.Entry<String, String> me = (Map.Entry<String, String>) it.next();
                List<BaseDictionary> list = commonDao.findDictionary(me.getValue());
                if (list != null) {
                    String type = me.getKey();
                    direcListMap.put(type, list);
                    for (BaseDictionary dc : list) {
                        dictionaryMap.put(type + "_" + dc.getValue(), dc);
                    }
                }
            }
        } catch (Exception e) {
            log.warn("======PLS confirm if or not configuration dictionaryHolder=====");
            log.warn(e.getMessage());
        }

    }

    /**
     * 
     * @param value
     *            字典值
     * @param type
     *            字典類型
     * @return 字典名稱
     */
    public static String getDictNameByValueType(String value, String type) {
        if (dictionaryMap.get(type + "_" + value) != null) {
            return dictionaryMap.get(type + "_" + value).getDesc();
        }
        return "";

    }

    /**
     * 
     * @param type
     *            字典類型
     * @return 字典列表
     */
    public static List<BaseDictionary> getDictListByType(String type) {

        return direcListMap.get(type) == null ? null : direcListMap.get(type);
    }

    public Map<String, BaseDictionary> getDictionaryMap() {
        return dictionaryMap;
    }

    public Map<String, List<BaseDictionary>> getDictionaryListMap() {
        return direcListMap;
    }
    
        /**
     * 把list中字典表中代碼值轉換為他的名稱
     * 
     * @param list
     * @param praAndValueMap
     *            key為list中object的表示字典表的屬性 (支持通過點來多層次的屬性如 dto.user.id或是無層次的id),
     *            value為他的類型,如學歷,性別
     */
    @Override
    public void dictionaryConvert(List<?> list, Map<String, String> dictMapDesc) {
        if (list == null || list.isEmpty()) {
            return;
        }
        if (dictMapDesc == null || dictMapDesc.isEmpty()) {
            return;
        }

        for (Object currObj : list) {
            this.dictionaryConvert(currObj, dictMapDesc);

        }
    }
    
    public void dictionaryConvert(Object dictObj,
            Map<String, String> dictMapDesc) {
        if (dictObj == null) {
            return;
        }
        if (dictMapDesc == null || dictMapDesc.isEmpty()) {
            return;
        }
        try {
            Iterator<Entry<String, String>> it = dictMapDesc.entrySet()
                    .iterator();
            String[] propertys = null;
            while (it.hasNext()) {
                Entry<String, String> me = it.next();
                propertys = me.getKey().split("\\.");
                Object dictValue = ItestBeanUtils.forceGetProperty(dictObj,
                        propertys[0]);
                if (dictValue == null) {
                    continue;
                }
                if (propertys.length == 1) {
                    ;
                    ItestBeanUtils.forceSetProperty(dictObj, me.getKey(),
                            DictionaryCacheServiceImpl.getDictNameByValueType(
                                    (String) dictValue, me.getValue()));
                } else {
                    Object laseLayerObj = null;
                    for (int i = 1; i < propertys.length; i++) {
                        if (i != propertys.length - 1
                                || (propertys.length == 2 && i == 1)) {
                            laseLayerObj = dictValue;
                        }

                        dictValue = ItestBeanUtils.forceGetProperty(dictValue,
                                propertys[i]);

                        if (dictValue == null) {
                            break;
                        }
                    }
                    if (dictValue != null && laseLayerObj != null) {
                        ItestBeanUtils.forceSetProperty(laseLayerObj,
                                propertys[propertys.length - 1],
                                DictionaryCacheServiceImpl
                                        .getDictNameByValueType(
                                                (String) dictValue,
                                                me.getValue()));
                    }
                }
                dictValue = null;
            }
        } catch (NoSuchFieldException e) {
            logger.error(e.getMessage(), e);
        }
    }


}

 

DictionaryRest

package cn.com.mypm.framework.app.web.rest.dict;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.com.mypm.framework.app.entity.dictionary.BaseDictionary;
import cn.com.mypm.framework.app.entity.vo.dict.BatchLoad;
import cn.com.mypm.framework.app.service.common.BaseDic;
import cn.com.mypm.framework.app.service.dictionary.PubDicInterface;
import cn.com.mypm.framework.app.service.dictionary.impl.DictionaryCacheServiceImpl;
import cn.com.mypm.framework.common.SpringContextHolder;





@RestController
@RequestMapping("/itestAPi/dictService")
public class DictionaryRest {


    private static Log logger = LogFactory.getLog(DictionaryRest.class);

    @GetMapping(value="/find/{type}",consumes="application/json")
    public  List<BaseDictionary> find(@PathVariable("type") String type) {
        
        return DictionaryCacheServiceImpl.getDictListByType(type);
        
    }
    
    //項目內自定義字典
    @PostMapping(value="batchLoad",consumes="application/json")
    public Map<String,List<BaseDictionary>> load(@RequestBody BatchLoad batchLoad){
        if(batchLoad==null){
            return null;
        }
        if(batchLoad.getIds()==null||batchLoad.getIds().trim().equals("")){
            return null;
        }
        if(batchLoad.getDicts()==null||batchLoad.getDicts().trim().equals("")){
            return null;
        }
        String[] idsArr = batchLoad.getIds().split(",");
        String[] dictsArr =  batchLoad.getDicts().split(",");
        Map<String,List<BaseDictionary>>  resultMap = new HashMap<String,List<BaseDictionary>>(idsArr.length);
        int i = 0;
        for(String id :idsArr){
            List<BaseDictionary> currDict = DictionaryCacheServiceImpl.getDictListByType(dictsArr[i]);
            if(currDict!=null&&!currDict.isEmpty()){
                resultMap.put(id, currDict);
            }
            i++;
        }
        return resultMap;
    }
   
//公共字典 @PostMapping(value
="pubBatchLoad",consumes="application/json") public Map<String,List<BaseDic>> pubLoad(@RequestBody BatchLoad batchLoad){ if(batchLoad==null){ return null; } if(batchLoad.getIds()==null||batchLoad.getIds().trim().equals("")){ return null; } if(batchLoad.getDicts()==null||batchLoad.getDicts().trim().equals("")){ return null; } PubDicInterface pubDicInterface = null ; try { pubDicInterface = SpringContextHolder.getBean("pubDicInterface"); } catch (Exception e) { logger.error("pub dic no pubDicInterface implements "+e.getMessage(),e); return null; } return pubDicInterface.batchLoadDic(batchLoad); } }

 

  列舉幾個字典類:

@Entity
@DiscriminatorValue("AccessMode")
public class AccessMode extends Dictionary{

}
@Entity
@DiscriminatorValue("Gender")
public class Gender extends Dictionary{

}
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("CertificateType")
public class CertificateType extends Dictionary{

}

不一一列舉了,總之後端,只要定義,字典類就行了

 

 

載入字典的公共JS ,下麵是eas ui 版本,且作預設執行的JS中要執行的方法,

 

/**
 * 批量獲取頁面字典數據
 */
function batDicts(opts)
{
    // action
    var url = '';
    // type 區分是公共不是項目內自定義字典
    var type = 'public';
    if(!!opts) {
if(!!opts.type) {
            type = opts.type;
        }
    }
    var dicts = [];
    var pageCombos = $(document).data("bsui-combobox")||{};
    $.each(pageCombos, function(i, n){
        if(i && n) {
            var td = i.split('^');
            if(2 === td.length) {
                if(td[0]===type && -1===$.inArray(td[1], dicts)) {
                    dicts.push(td[1]);
                }
            }
        }
    });
    if(!!url && dicts.length > 0) {
        // req params
        var params = '{"ids": "'+dicts.join(",")+'","dicts": "'+dicts.join(",")+'"}';
        // post request
        ajaxReq(url, params, '', '',{
            'type' : 'POST',
            'contentType':'application/json; charset=UTF-8'
        }).done(function(data){
            $.each(dicts, function(i,n){
                if(!!pageCombos[type+'^'+n] 
                    && !pageCombos[type+'^'+n]['getted'] 
                    && !!data[n]) {
                    pageCombos[type+'^'+n]['getted'] = true;
                    pageCombos[type+'^'+n]['data'] = data[n];
                    $.each(pageCombos[type+'^'+n]["list"], function(){
                        // 更新頁面combo
                        $(this).combobox('loadData', data[n]);
                    });
                }
            });
        });
    }
}

/**
 * 一次設置頁面上所有下拉列表
 */
function batCombo()
{
    batDicts({
        type: 'public',
        url: $CommonUI._PUBLIC_DICT_SERVICE_URL
    });
    batDicts({
        type: 'custom',
        url: $CommonUI._CUSTOM_DICT_SERVICE_URL
    });
}

 

四:使用

 在前端,正常使用select 組件的基本上,增加一個自定義屬性 即,可,不用寫任何JS代碼,當然要引用公用JS

 

簡單吧,前端,什麼都不用了,只要定義了用什麼字典及是公共字典,還是自定義的,後端,是通用的代碼,只需要申明字類就 OK ,如 Gender ,有其他的,如學歷等,只要後臺定義一個 他的類,並用 @DiscriminatorValue 申明就行了 , 不再寫任何代碼  ,是不是很省事呀, easy ui ,預設的下拉表表組件,只要寫URL和兩個屬性,但是下拉多,一個下拉請求一次後臺,這很不友好,且需要使用者知道URL,或是實現 load 的JS函數,侵入性我認為太大。

 

另外,前面gird 的數據,通知會包含量字典數據,通知會在前端通過 grid 組年中,定義format 方法,時行轉行,這麻煩,轉換者,還要知道如來轉,所以後臺字典的service 實現中中增加了一個方法,用於把list 中的的對像里的字典屬性轉換為其名稱

 

        /**
     * 把list中字典表中代碼值轉換為他的名稱
     * 
     * @param list
     * @param praAndValueMap
     *            key為list中object的表示字典表的屬性 (支持通過點來多層次的屬性如 dto.user.id或是無層次的id),
     *            value為他的類型,如學歷,性別
     */
    @Override
    public void dictionaryConvert(List<?> list, Map<String, String> dictMapDesc)

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JANA面向對象的三大特性:封裝,繼承,多態。 今天學了繼承,繼承,通俗點說就是子類可以用父類的代碼,或重寫父類的方法、構造方法、屬性 例如我這裡要調用父類的方法: 下邊有兩個測試類,自己分別試一下,自己體驗效果。嘻嘻!!! 這是用父類new一個子類 這是直接new一個子類,這個子類的方法名如果和父 ...
  • CopyOnWriteArraySet是用Map實現的嗎? CopyOnWriteArraySet是有序的嗎? CopyOnWriteArraySet以何種方式保證元素不重覆? 如何比較兩個Set中的元素是否完全一致? ...
  • 一、各Set實現類的性能分析 HashSet和TreeSet是Set的兩個典型實現。HashSet的性能總是比TreeSet好(特別是最常用的添加、查詢元素等操作),因為TreeSet需要額外的紅黑樹演算法來維護集合元素的次序。只有當需要一個排序的Set時,才應該使用TreeSet,否則都應該使用Ha ...
  • 前面介紹了利用文件寫入器和文件讀取器來讀寫文件,因為FileWriter與FileReader讀寫的數據以字元為單位,所以這種讀寫文件的方式被稱作“字元流I/O”,其中字母I代表輸入Input,字母O代表輸出Output。可是FileWriter的讀操作並不高效,緣由在於FileWriter每次調用 ...
  • 索引 一、富文本編輯器 1.1 在Admin中使用 1.2 自定義使用 1.3 顯示 二、全文檢索 2.1 創建引擎及索引 2.2 使用 三、發送郵件 一、富文本編輯器 藉助富文本編輯器,網站的編輯人員能夠像使用offfice一樣編寫出漂亮的、所見即所得的頁面。此處以tinymce為例,其它富文本編 ...
  • 第二周-第02章節-Python3.5-模塊初識 G:\Python3.7.3\python.exe G:/practise/oldboy/day2/sys.py['G:/practise/oldboy/day2/sys.py'] 驅動器 G 中的捲沒有標簽。 捲的序列號是 C038-3181 G: ...
  • 文章大綱 一、Spring介紹二、Spring的IoC實戰三、IoC常見註解總結四、項目源碼及參考資料下載五、參考文章 一、Spring介紹 1. 什麼是Spring Spring是分層的Java SE/EE應用 full-stack輕量級開源框架,以IoC(Inverse Of Control:反 ...
  • 通過源碼,分析了ArrayList類的繼承實現結構,主要對ArrayList動態數組數據結構的具體實現細節進行分析 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...