GoFramework框架开发指南
服务层规范
java文件分类
文件类型 | 说明 |
---|---|
枚举 | 目前枚举多由工具生成,如果需要进行数据库操作,则需要实现org.go.api.core.enums.BaseEnum接口 |
dto | 目前同时做为请求及响应参数,与数据模型关联密切 |
request | 对应渠道侧的页面级请求,当dto模型不能满足页面请求时,需要新增request类型 |
response | 对应接口的最终返回结果,该类型代表接口的最终结果,一般不能做为集合中的元素返回。 |
item | 对应集合中的元素,当dto模型不能直接做元素使用时,可以使用通过定义item类型来解决 |
facade | 分为直接操作数据库的简单服务,以及进行服务组合或复杂运算的高级服务。高级服务需要在名称中增加Prof关键字来区分。 |
bo | 对应数据库表或视图的实体,在mybatis中做为请求或响应参数来使用。目前要求除非数据返回的是基础类型如Integer,Long,String等,则必须使用bo类来承载返回结果。 |
dto | 定义mybatis接口及xml文件 |
facade实现类 | 含义如同其名。 |
错误码:
1、错误码存放在ResCode类中,由一位字母加四位数字组成,系统的不同模块错误码可以定义不同的错误码前缀。
2、接口全局错误码,针对错误码配置太繁琐的问题,将一个接口定义成一个错误码,并结合1.0.1+的注解进行使用,暂定类名称为R。如下:
public class R {
public static final String gridInfoProfFacade = "R0101";// 网格点高级服务
public static final String unitInfoProfFacade = "R0102";// 单位高级服务
public static final String generatePdfFacade = "R0103";// 单位高级服务
public static final String unitDeviceProfFacade = "R0104";// 单位设备高级服务
....
}
3、要求每个接口都需要指定错误码及信息(可以共用)。为了避免抛出未知异常,接口中必须要做异常处理。如下:
@Override
public List<FormElement> getFormElementList(FormElementListGetRequest request) throws PendingException {
try {
// 对请求参数进行校验
AbstractBaseRequestDto.validateThrow(request);
// 查询缓存用户信息
User user = userCache.get(request.getUserId());
// 查询表单元素列表
....
return formElementList;
} catch (Exception ex) {
// 对异常进行处理
throw transferException(ex, ResCode.formElementListGetFailed);
}
}
如果使用1.0.1框架及以上,也可以使用注解方式,减少代码量
@Override
@RpcMethod("部门下的文书及风险项选项列表查询")
public List<FormElementOptionItem> queryDeptDocRiskOptionList(DepartmentIdRequest request) throws PendingException {
// 对请求参数进行校验
validateThrow(request);
// 从缓存中进行查询
return deptDocRiskOptionCache.get(request.getDepartmentId());
}
上述前提需要给接口所在服务类配置全局错误码,如下
@Service(version = "1.0.0")
@RpcClass(R.departmentInfoProfFacade)
public class DepartmentInfoProfFacadeImpl extends AbstractDubboIntegrationService implements DepartmentInfoProfFacade {
....
}
数据库自定义查询开发流程
结构组成:
request类:数据库查询时所需要的请求参数类。但如果请求参数只有一个基础类型,或者能找到合适的dto模型,则可以省掉本类。
bo类:数据库查询结果的映射类型,如果返回结果是基础类型如Integer|String等,则可以省略。
dto类:最终接口返回的数据模型,与bo类对应。注:本处bo和dto仅做返回结果的映射,请求则交给request类。
dao接口及xml:mybatis的必要组件
facade及实现类:看情况决定使用现有类型还是新增
开发过程:
(1)编写request类。可使用Dto工具生成校验部分的代表,如下图:
(2)编写bo类,与数据库查询返回结果对应。
(3)利用bo类生成dto类及mybatis map的代码片断。如下图:
(4)在dao的xml文件中写查询块。请求参数直接使用request类,响应参数使用bo类的Map。
(5)使用工具生成dao接口、facade及实现类的方法内容并复制回相应类。如下图:
事务开发流程
需要使用到newTransactionTemplate模板类。
(1)1.0.0版本写法
@Override
public void saveInspectRecord(InspectRecordSaveRequest request) throws PendingException {
try {
....
// 事务处理
newTransactionTemplate.execute(status -> {
try {
....
return inspectRecord;
} catch (Exception ex) {
// 对数据库事务进行回滚
status.setRollbackOnly();
// 对异常进行处理
throw new GoRuntimeException(transferException(ex, ResCode.inspectRecordSaveFailed));
}
});
} catch (Exception ex) {
// 对异常进行处理
throw transferException(ex, ResCode.inspectRecordSaveFailed);
}
}
(2)1.0.1+版本写法,去掉了最外层的try catch,及不需要单独定义错误码
@Override
@RpcMethod("新增单位信息")
public void addUnit(UnitAddRequest unitAddRequest) throws PendingException {
...
newTransactionTemplate.execute(
status -> {
try {
....
return true;
} catch (Exception ex) {
// 事务回滚
status.setRollbackOnly();
// 转换成运行时异常
throw toRuntime(ex);
}
}
);
web端接口开发流程
前提:在1.0.0及以上版本框架中,web端可以做SPI实现,并实现请求参数的部分会话字段自动注入及校验的功能。如下:
package com.yc.clouds.web.spi;
import com.google.gson.Gson;
import com.yc.clouds.api.business.constants.QUser;
import com.yc.clouds.api.core.constants.ResCode;
import com.yc.clouds.web.bean.session.SessionUser;
import com.yc.clouds.web.constants.SessionKey;
import org.go.framework.core.exception.PendingException;
import org.go.framework.web.aop.spi.IInjectFieldFetcher;
import javax.servlet.http.HttpServletRequest;
/**
* MVC字段注入的Fetcher实现类
*/
public class InjectFieldFetcherImpl implements IInjectFieldFetcher {
@Override
public Object getValue(HttpServletRequest request, String fieldName) throws PendingException {
// 获取会话用户信息
SessionUser sessionUser = getSessionUser(request);
// 如果会话用户不存在
if(sessionUser == null){
throw ResCode.SESSION_AUTH_FAILED.toException();
}
// 根据属性名字进行注入
switch (fieldName) {
case QUser.appId:
return sessionUser.getAppId();
case QUser.userId:
return sessionUser.getUserId();
case QUser.realName:
return sessionUser.getRealName();
case QUser.phone:
return sessionUser.getPhone();
case QUser.roleType:
return sessionUser.getRoleType();
case QUser.privilegeType:
return sessionUser.getPrivilegeType();
default:
throw ResCode.injectFieldInvalid.toException();
}
}
/**
* 获取用户会话信息
* @param request
* @return
*/
private SessionUser getSessionUser(HttpServletRequest request) {
String jsonStr = (String) request.getSession().getAttribute(SessionKey.SESSION_KEY);
return jsonStr == null ? null : new Gson().fromJson(jsonStr, SessionUser.class);
}
}
(1)在controller类的mvc方法中,尽量使用request类型做请求参数,可以写参数校验逻辑,以及对参数字段做各种注解标识。应避免直接使用工具生成的dto类。
public class BackstageUnitPageRequest extends AbstractBasePageRequestDto {
/**
* 应用id
*/
@MvcInject
private Long appId;
/**
* 一级网格点Id
*/
@MvcOptional
private Long gridId;
/**
* 单位整体资质情况
*/
@MvcIgnore
private UnitQualificationStatus unitQualificationStatus;
....
}
mvc字段注解说明:
MvcInject:字段值自动注入,可以指定会话对象的字段名,如不指定就使用属性名进行匹配。当字段使用该注解后,接口文档中将忽视该字段。
MvcOptional:标记字段为可选,在生成接口文档时会增加可选标记。
MvcIgnore:说明该字段并非由接口传递值,而是后期通过运算生成。接口文档中会忽视该字段。
(2)若需要使用上述注解的自动注入功能。需要在controller的mvc方法中增加org.go.framework.base.annotation.MvcValidate注解。如
@RequestMapping(value = "/registerValidate.do", method = RequestMethod.POST)
@MvcValidate
public ResDto<?> registerValidate(HttpServletRequest request, @RequestBody RegisterValidateRequest registerValidateRequest) throws PendingException {
....
return new ResDto<>();
}
其它SPI实现:会话注入
package com.yc.clouds.web.spi;
import com.yc.clouds.web.constants.SessionKey;
import org.go.framework.web.interceptor.spi.ISessionUserInjector;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* 会话用户注入的实现
* @author 素闲人
*
*/
public class SessionUserInjectorImpl implements ISessionUserInjector {
@Override
public String getSessionUserJson(HttpServletRequest request) {
return (String) getHttpRequest().getSession().getAttribute(SessionKey.SESSION_KEY);
}
private HttpServletRequest getHttpRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}
实现该SPI方法,可以在dubbo调用时,把会话信息通过dubbo的上下文传递到服务侧。即使接口中没有用户参数,也可以通过上下文获得。如下面工具类:
package com.yc.clouds.api.business.utils;
import com.google.gson.Gson;
import com.yc.clouds.api.business.bean.session.RpcSessionUser;
import com.yc.clouds.api.core.constants.ResCode;
import org.go.framework.core.Context;
import org.go.framework.core.exception.GoRuntimeException;
/**
* 会话用户信息上下文
*
* @author 素闲人
*/
public class SessionUserContext {
/**
* 获取会话用户信息
*
* @return
*/
public static RpcSessionUser get() {
// 从context中获取会话信息的json字符串
RpcSessionUser rpcSessionUser = new Gson().fromJson(Context.getRequestInfo().getSessionUser(), RpcSessionUser.class);
// 若用户会话信息不存在则抛出异常
if (Context.getRequestInfo().getSessionUser() == null) {
throw new GoRuntimeException(ResCode.RPC_SESSION_GET_FAILED.toException());
}
return rpcSessionUser;
}
}
在适当的接口中可以使用上述工具类来获取会话用户信息:
@Override
@RpcMethod("APP单位首页信息查询")
public UnitAppHomePageResponse getAppHomePageData(UnitIdRequest unitIdRequest) throws PendingException {
// 对请求参数进行校验
validateThrow(unitIdRequest);
// 查询单位缓存信息
UnitCacheItem unitCacheItem = unitItemCache.get(unitIdRequest.getUnitId());
// 获取会话用户信息
RpcSessionUser sessionUser = SessionUserContext.get();
....