一、简介
之前已经完成了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