1. 程式人生 > >IDEA專案搭建十三——服務消費端與生產端通訊實現

IDEA專案搭建十三——服務消費端與生產端通訊實現

一、簡介

之前已經完成了EurekaClient的服務生產者和Feign的服務消費者模組的搭建,現在實現統一的通訊約定

(1) 統一Request結構

(2) 統一Response結構

(3) 統一Error通知

二、程式碼

1、建立統一請求物件ServiceRequest<>實際引數就是這個泛型,使用統一的構造進行建立便於對資料進行統一的加密傳輸

import java.util.Date;

/**
 * 客戶端請求內容物件
 */
public class ServiceRequest<T> {

    //region 屬性

    /**
     * 請求唯一ID
     
*/ private String requestID; /** * 請求時間 */ private Date requestTime; /** * 客戶端代號 */ private String clientCode; /** * 請求籤名 */ private String requestSign; /** * 請求引數 */ private T reqData; public String getRequestID() { return
requestID; } public void setRequestID(String requestID) { this.requestID = requestID; } public Date getRequestTime() { return requestTime; } public void setRequestTime(Date requestTime) { this.requestTime = requestTime; } public String getClientCode() {
return clientCode; } public void setClientCode(String clientCode) { this.clientCode = clientCode; } public String getRequestSign() { return requestSign; } public void setRequestSign(String requestSign) { this.requestSign = requestSign; } /** * 禁止使用此方法-不能刪除內部自動取值需要使用 * @return */ @Deprecated public T getReqData() { return reqData; } /** * 禁止使用此方法-不能刪除內部自動賦值需要使用 * @param reqData */ @Deprecated public void setReqData(T reqData) { this.reqData = reqData; } //endregion //設定類的無參構造為私有禁止外部例項化 private ServiceRequest() { } /** * 建立請求物件 * @param data */ public ServiceRequest(T data) { //後期從此處增加加解密程式碼... this.reqData = data; } /** * 獲取請求引數 * @param req * @param <T> * @return */ public static <T> T getRequestData(ServiceRequest<T> req) { //後期從此處增加加解密程式碼... T obj = req.getReqData(); return obj; } }

2、建立統一響應物件ServiceResponse<>實際響應就是這個泛型,使用統一的取值便於有需求時對響應資料進行統一的解密

import com.google.common.base.Strings;
import java.util.Date;

/**
 * 服務端響應結果物件
 */
public class ServiceResponse<T> {

    //region 屬性

    /**
     * 請求唯一ID
     */
    private String requestID;
    /**
     * 響應代號
     * <p>
     * 000000 - 正確
     */
    private ServiceCodeMsgEnum resCodeMsg;
    /**
     * 響應時間
     */
    private Date resTime;
    /**
     * 響應結果
     */
    private T resData;

    public String getRequestID() {
        return requestID;
    }

    public void setRequestID(String requestID) {
        this.requestID = requestID;
    }

    public ServiceCodeMsgEnum getResCodeMsg() {
        return resCodeMsg;
    }

    public void setResCodeMsg(ServiceCodeMsgEnum resCodeMsg) {
        this.resCodeMsg = resCodeMsg;
    }

    public Date getResTime() {
        return resTime;
    }

    public void setResTime(Date resTime) {
        this.resTime = resTime;
    }

    /**
     * 禁止使用此方法-不能刪除內部取值需要使用
     *
     * @return
     */
    @Deprecated
    public T getResData() {
        return resData;
    }

    /**
     * 禁止使用此方法-不能刪除內部賦值需要使用
     *
     * @param resData
     */
    @Deprecated
    public void setResData(T resData) {
        this.resData = resData;
    }

    //endregion

    //設定類的無參構造為私有禁止外部例項化,只能通過下方靜態方法建立
    private ServiceResponse() {
    }

    /**
     * 建立執行正確響應物件
     * @param data
     */
    public ServiceResponse(T data) {
        this.resCodeMsg = ServiceCodeMsgEnum.Success;
        this.resData = data;
    }

    /**
     * 建立執行錯誤響應物件
     * @param codeMsg
     */
    public ServiceResponse(ServiceCodeMsgEnum codeMsg) {
        this.resCodeMsg = codeMsg;
        this.resData = null;
    }

    /**
     * 獲取響應CodeMsg(外部WebApi專用)
     *
     * @param res
     * @param <T>
     * @return ServiceCodeMsgEnum.Success為正確
     */
    private static <T> ServiceCodeMsgEnum getResponseCodeMsg(ServiceResponse<T> res) {
        return res.getResCodeMsg();
    }

    /**
     * 獲取響應Msg(內部站點專用)
     *
     * @param res
     * @param <T>
     * @return null為正確
     */
    public static <T> String getResponseMsg(ServiceResponse<T> res) {
        return Strings.emptyToNull(res.getResCodeMsg().getMsg());
    }

    /**
     * 獲取響應引數
     *
     * @param res
     * @param <T>
     * @return
     */
    public static <T> T getResponseData(ServiceResponse<T> res) {
        return res.getResData();
    }
}

3、建立統一響應結果列舉,這裡可以統一控制響應CodeMsg的對應關係,使用也簡單直觀

/**
 * 服務通訊CodeMsg列舉
 */
public enum ServiceCodeMsgEnum {

    Success("000000", null),
    Error("999999", "系統異常");

    //region

    private String code;
    private String msg;

    ServiceCodeMsgEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    //endregion
}

4、服務生產者這邊,統一入參和返回值都是ServiceRequest和ServiceResponse,又可以根據泛型來識別到底是什麼物件

import com.google.gson.Gson;
import com.ysl.ts.common.serviceModel.ServiceCodeMsgEnum;
import com.ysl.ts.common.serviceModel.ServiceRequest;
import com.ysl.ts.common.serviceModel.ServiceResponse;
import com.ysl.ts.core.model.base.ts_base.SysUserModel;
import com.ysl.ts.core.service.base.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

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

@Controller
@RequestMapping("/api/sysUser")
public class SysUserController extends BaseController {
    @Autowired
    SysUserService service;

    @ResponseBody
    @RequestMapping("/save")
    public ServiceResponse<Integer> save(@RequestBody ServiceRequest<SysUserModel> req) {
        try {
            //使用統一的方法獲取請求Data
            SysUserModel agent = ServiceRequest.getRequestData(req);
            //呼叫Service
            int result = service.save(agent);
            //響應-成功
            return new ServiceResponse<>(result);
        } catch (Exception e) {
            //響應-錯誤
            return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
        }
    }

    @ResponseBody
    @RequestMapping("/delete")
    public ServiceResponse<Integer> delete(@RequestBody ServiceRequest<Integer> req) {
        try {
            //獲取請求Data
            int id = ServiceRequest.getRequestData(req);
            //呼叫Service
            int result = service.delete(id);
            //響應-成功
            return new ServiceResponse<>(result);
        } catch (Exception e) {
            //響應-錯誤
            return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
        }
    }

    @ResponseBody
    @RequestMapping("/get")
    public ServiceResponse<SysUserModel> get(@RequestBody ServiceRequest<Integer> req) {
        try {
            //獲取請求Data
            int id = ServiceRequest.getRequestData(req);
            //呼叫Service
            SysUserModel result = service.get(id);
            //響應-成功
            return new ServiceResponse<>(result);
        } catch (Exception e) {
            //響應-錯誤
            return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
        }
    }

    @ResponseBody
    @RequestMapping("/list")
    public ServiceResponse<List<SysUserModel>> list(@RequestBody ServiceRequest<String> req) {
        try {
            //獲取請求Data
            String search = ServiceRequest.getRequestData(req);
            //呼叫Service
            List<SysUserModel> result = service.list();
            //響應-成功
            return new ServiceResponse<>(result);
        } catch (Exception e) {
            //響應-錯誤
            return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
        }
    }
}

5、服務消費者這邊,使用了Feign所以需要一個介面來實現呼叫,我們直接傳入ServiceRequest<>來做統一的請求物件,返回ServiceResponse<>來做統一的響應物件

import com.ysl.ts.common.serviceModel.ServiceRequest;
import com.ysl.ts.common.serviceModel.ServiceResponse;
import com.ysl.ts.core.model.base.ts_base.SysUserModel;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Service
@FeignClient("YSL-TS-Core-Service-Base")//服務生產者名稱
@RequestMapping("/api/sysUser")//服務路由
public interface SysUserService {

    @RequestMapping("/save")
    ServiceResponse<Integer> save(@RequestBody ServiceRequest<SysUserModel> req);

    @RequestMapping("/delete")
    ServiceResponse<Integer> delete(@RequestBody ServiceRequest<Integer> req);

    @RequestMapping("/get")
    ServiceResponse<SysUserModel> get(@RequestBody ServiceRequest<Integer> req);

    @RequestMapping("/list")
    ServiceResponse<List<SysUserModel>> list(@RequestBody ServiceRequest<String> req);
}

6、獲得基礎資料實體後,在頁面展現之前可能有些欄位需要進行翻譯,比如狀態1:啟用,0:禁用等等,這部分建立一個ModelEx類繼承Model類,把其中需要翻譯的欄位寫在ModelEx中,

用以下轉換類對實體值進行拷貝,然後頁面接收這個ModelEx物件,這樣預設可以使用父類中的屬性,如果要顯示翻譯的就用子類中的屬性即可

import java.util.ArrayList;
import java.util.List;

/**
 * Model 轉換類
 *
 * @param <TModel>   Model型別物件
 * @param <TModelEx> ModelEx型別物件*/
public abstract class AbstractModelConvertor<TModel, TModelEx extends TModel> {

    /**
     * 轉換
     *
     * @param model   Model型別物件
     * @param modelEx ModelEx型別物件
     * @return
     */
    public TModelEx convert(TModel model, Class<TModelEx> modelEx) {
        TModelEx ex = new DeepClone().clone(model, modelEx);
        convertFields(ex);//填充翻譯欄位,需要子類重寫
        return ex;
    }

    /**
     * 列表轉換
     *
     * @param modelList Model型別物件列表
     * @param modelEx   ModelEx型別物件
     * @return
     */
    public List<TModelEx> convert(List<TModel> modelList, Class<TModelEx> modelEx) {
        List<TModelEx> list = new ArrayList<>();

        for (TModel tModel : modelList) {
            list.add(convert(tModel, modelEx));
        }

        return list;
    }

    /**
     * 欄位轉換介面
     *
     * @param modelEx
     */
    protected abstract void convertFields(TModelEx modelEx);
}

實際上就是使用Gson對實體做了一次序列化很簡單

import com.google.gson.Gson;

/**
 * 深入拷貝
 */
public class DeepClone {

    private Gson gson = new Gson();

    /**
     * 深拷貝
     *
     * @param t     源資料
     * @param clazz 目標類
     * @param <T>   源資料型別
     * @param <K>   目標型別
     * @return
     */
    public <T, K> K clone(T t, Class<K> clazz) {
        return gson.fromJson(gson.toJson(t), clazz);
    }
}

 7、每一個實體都繼承抽象基類,這樣就可以直接使用轉換方法了

import com.ysl.ts.common.AbstractModelConvertor;
import com.ysl.ts.core.model.base.ts_base.SysUserModel;
import com.ysl.ts.core.model.base.ts_base.ex.SysUserModelEx;

public class SysUserConvertor extends AbstractModelConvertor<SysUserModel, SysUserModelEx> {
    /**
     * 填充待翻譯欄位
     */
    @Override
    protected void convertFields(SysUserModelEx sysUserModelEx) {

    }
}

8、下面就是頁面Controller的Action呼叫了

import com.ysl.ts.common.serviceModel.ServiceRequest;
import com.ysl.ts.common.serviceModel.ServiceResponse;
import com.ysl.ts.core.model.base.ts_base.SysUserModel;
import com.ysl.ts.core.model.base.ts_base.ex.SysUserModelEx;
import com.ysl.ts.web.base.modelConvertors.ts_base.SysUserConvertor;
import com.ysl.ts.web.base.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.List;

@Controller
@Component("AdminSysUser")
@RequestMapping("/sysUser")
public class SysUserController extends BaseController {
    //自動注入Feign介面物件
    @Autowired
    SysUserService service;

    @RequestMapping("/save")
    public boolean save(SysUserModel agent) {
        boolean result = false;
        //建立ServiceRequest
        ServiceRequest<SysUserModel> req = new ServiceRequest<>(agent);
        //呼叫Service並獲得ServiceResponse
        ServiceResponse<Integer> res = service.save(req);
        //解析獲得Code
        String msg = ServiceResponse.getResponseMsg(res);
        //NULL代表響應正常
        if (null == msg)
        {
            //解析獲得T型別
            result = ServiceResponse.getResponseData(res) > 0;
        }
        return result;
    }

    @RequestMapping("/delete")
    public boolean delete(int id){
        boolean result = false;
        //建立ServiceRequest
        ServiceRequest<Integer> req = new ServiceRequest<>(id);
        //呼叫Service並獲得ServiceResponse
        ServiceResponse<Integer> res = service.delete(req);
        //解析獲得Code
        String msg = ServiceResponse.getResponseMsg(res);
        //NULL代表響應正常
        if (null == msg)
        {
            //解析獲得T型別
            result = ServiceResponse.getResponseData(res) > 0;
        }
        return result;
    }

    @ResponseBody
    @RequestMapping("/get")
    public SysUserModelEx get(Model model, int id) {
        SysUserModelEx result = new SysUserModelEx();
        //建立ServiceRequest
        ServiceRequest<Integer> req = new ServiceRequest<>(id);
        //呼叫Service並獲得ServiceResponse
        ServiceResponse<SysUserModel> res = service.get(req);
        //解析獲得Code
        String msg = ServiceResponse.getResponseMsg(res);
        //NULL代表響應正常
        if (null == msg)
        {
            //解析獲得T型別
            SysUserModel tmp = ServiceResponse.getResponseData(res);
            //翻譯所需欄位
            result = new SysUserConvertor().convert(tmp, SysUserModelEx.class);
        }
        return result;
        //model.addAttribute("model", result);
        //return "/hotel/get";
    }

    //直接返回json不寫頁面了
    @ResponseBody
    @RequestMapping("/list")
    public List<SysUserModelEx> list(String search) {
        List<SysUserModelEx> result = new ArrayList<>();
        //建立ServiceRequest
        ServiceRequest<String> req = new ServiceRequest<>(search);
        //呼叫Service並獲得ServiceResponse
        ServiceResponse<List<SysUserModel>> res = service.list(req);
        //解析獲得Code
        String msg = ServiceResponse.getResponseMsg(res);
        //NULL代表響應正常
        if (null == msg)
        {
            //解析獲得T型別
            List<SysUserModel> tmp = ServiceResponse.getResponseData(res);
            //翻譯所需欄位
            result = new SysUserConvertor().convert(tmp, SysUserModelEx.class);
        }
        return result;
    }
}

111