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

上一篇:分位函数(四分位数)概念与pandas中的quantile函数


下一篇:零基础自学Python是看书还是看视频?