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();
      
        ....        

posted @ 2018-05-28 09:46  无语还真  阅读(3249)  评论(0编辑  收藏  举报