文章目录
- 前言
- 一、项目分层
- 二、模块职责简介
- 1. API层
- 2. Service层
- 2.1 操作执行服务
- 2.2 操作器
- 2.3 操作执行器
- 2.4 参数校验
- 2.5 操作器实现
- 3. Domain层
- 4. Infrastructure层
- 4.1 Dal层
- 三、对应类图
- 四、开发内容
- 3.1 约定请求报文格式
- 3.2 新增交易码与操作器映射枚举类
- 3.3 配置参数校验Yml
- 3.4 编写操作器(核心工作内容)
- 3.5 编写仓储和实体
- 总结
前言
在开始一个新项目建设,进入开发环节之前,容易忽略一项非常重要的环节统一开发框架
。对于代码的编写,是开发人员各展拳脚还是制定约束规范,前置的结果是项目个子系统代码风格各异,开发人员个人能力直接体现到了项目代码中,能力稍微差的项目代码惨不忍睹,后期的维护扩展令人担忧,这种结果是我们都不愿看到的。
对于后者书面的约定规范真的起作用么,也不尽然,就像马路中间树立着禁止穿越马路警示,很多人还是当做没看到,所以最直接的办法就是加栅栏,强制限制,当然翻越栅栏的也不再少数,但是我们已经阻止了多数违规。
所以,在进入开发阶段,我们对新建的项目的各个子系统的应用框架进行统一化,即在系统新建初期就定下项目的分层,以及依赖的包版本,对同一业务场景的统一处理方案,以及最基本的非业务层面的代码逻辑等等,通过层层限制,来规范统一化代码风格,让开发人员集中精力完成系统的业务编写。
本篇将讲述自己在工作中所使用的开发框架以供参考(融入DDD思想)。
一、项目分层
按照DDD分层将系统分为四层,基础设施层包含多个模块,其余三层各对应一个模块:
各模块之间的依赖关系如下:
案例项目实际模块分层如下:
二、模块职责简介
下面以后台配置接口(路由规则条件配置)为例简要介绍下每个模块所包含的包以及各模块职责,对应的后台管理系统页面如下:
输入配置信息,点击提交,请求到后台的报文如下:
{
"header": {
"channelCode": "",
"channelDateTime": "",
"channelNo": "",
"transCode": "ruleConfig",
"transType": "add"
},
"body": {
"ruleScene": "routerRule",
"configKey": "produce_code",
"configName": "产品码",
"configType": "INPUT",
"isScript": "N"
}
}
注:
transCode:交易码(业务类型)对应到后台代码枚举如下:
public enum ManagerTransCodeEnum {
// 后台操作
ruleConfig("ruleConfig", "ruleConfigOperator", "规则配置"),
// 运行时...
}
transType:交易类型(增删改查操作)对应到后台代码枚举如下:
public enum ManagerTransTypeEnum {
ADD("add","addExecutor" ,"新增"),
MODIFY("modify","modifyExecutor" ,"修改"),
DELETE("delete","deleteExecutor" , "删除"),
QUERY("query", "queryExecutor" ,"单笔查询"),
LIST("list", "listExecutor" ,"批量查询"),
IMPORT("import","importExecutor" , "导入"),
EXPORT("export","exportExecutor" , "导出");
// ... ...
}
1. API层
一个系统对外提供的接口可以归为两类,提供于后台管理系统进行配置信息配置的接口
,运行时对外提供服务的接口
,我们对这两类接口进行收敛,即就只定义两个接口(理想情况下,具体实践还是要依据实际情况)。
接口定义如下:
控制层抽象类 AbstractController 定义通用处理方法 handle 以及业务方法模板 handleRequestInternal 如下:
/**
* @author Kkk
* @Describe: controller抽象模板
*/
public abstract class AbstractController implements ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(AbstractController.class);
private ApplicationContext applicationContext;
/**
* 请求处理
* @param request
* @param response
* @throws Exception
*/
public void handle(HttpServletRequest request, HttpServletResponse response) throws Exception {
//1、前置处理
PayCoreContext context = preHandle(request, response);
//2、业务处理;
handleRequestInternal(context);
//3、后置处理;
postHandle(request, response, context);
}
/**
* 前置处理
* @param request
* @param response
* @return
* @throws Exception
*/
public PayCoreContext preHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
//1.创建context
PayCoreContext context = createContext(request, response);
//2.填充context
fillContext(request, response, context);
return context;
}
/**
* 后置处理
* @param request
* @param response
* @param context
* @throws Exception
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, PayCoreContext context) throws Exception {
//1.构造model
Map<String, ?> model = buildModel(request, response, context);
//2.解析视图
String viewName = resolveView();
View view = getView(viewName);
//3.渲染视图
view.render(model, request, response);
}
... ...
}
请求处理核心方法:preHandle、handleRequestInternal、postHandle
//1、前置处理
PayCoreContext context = preHandle(request, response);
//2、业务处理;
handleRequestInternal(context);
//3、后置处理;
postHandle(request, response, context);
preHandle:
前置处理,构建请求的上下文,并解析上送报文填充到上下文中;
handleRequestInternal:
具体业务处理由子类(BackstageController、RuntimeController)实现;
postHandle:
后置处理,构造model,解析视图,渲染视图;
后台管理控制类实现 BackstageController 如下:
/**
* @author Kkk
* @Describe: 后台管理系统入口
*/
@RequestMapping(path="/backstage",produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@Controller
public class BackstageController extends AbstractController {
/**
* 后台服务操作器
*/
@Resource(name = "backstageOperateExecutorService")
private OperateExecutorService backstageOperateExecutorService;
@Override
@RequestMapping("/service")
public void handle(HttpServletRequest request, HttpServletResponse response) throws Exception {
super.handle(request, response);
}
@Override
protected void handleRequestInternal(PayCoreContext context) throws Exception {
this.backstageOperateExecutorService.execute(context);
}
/**
* 视图解析->后台接口独立视图
* @return
*/
@Override
protected String resolveView() {
return PayCoreConstant.PAYCORE_JSON_VIEW;
}
}
2. Service层
2.1 操作执行服务
定义Service层操作执行服务接口如下:
/**
* @author Kkk
* @Description: 操作执行服务,将操作抽象为几类:
* 1、增加;
* 2、删除;
* 3、修改;
* 4、查询;
* 5、。。。
* 每一类操作有一个执行器负责执行,本接口只负责分发;
*/
public interface OperateExecutorService {
/**
* 操作执行
* @param context
* @throws Exception
*/
void execute(PayCoreContext context) throws Exception;
}
后台操作执行服务接口实现 BackstageOperateExecutorServiceImpl 如下:
/**
* @author Kkk
* @Description: 操作执行服务,将操作抽象为几类:
* 1、增加;
* 2、删除;
* 3、修改;
* 4、查询;
* 5、待扩展。。。
* 每一类操作有一个执行器负责执行,本接口只负责分发;
*/
@Service("backstageOperateExecutorService")
public class BackstageOperateExecutorServiceImpl implements OperateExecutorService, ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(BackstageOperateExecutorServiceImpl.class);
/**
* spring 上下文
*/
private ApplicationContext applicationContext;
/**
* 字段验证器
*/
@Autowired
private FieldValidateExecutor fieldValidateExecutor;
@Override
public void execute(PayCoreContext context) throws Exception {
MessageEntity<Map<String, Object>> requestEntity = context.getRequestEntity();
String transCode = requestEntity.getHeader().getTransCode();//交易编码
String transType = requestEntity.getHeader().getTransType();//交易类型
AssertUtils.isNotBlank(transCode, SystemErrorCode.TRANS_CODE_UNDEFINED, new Object[]{transCode});
AssertUtils.isNotBlank(transType, SystemErrorCode.TRANS_TYPE_UNDEFINED, new Object[]{transType});
//1.交易码校验
ManagerTransCodeEnum managerTransCodeEnum = ManagerTransCodeEnum.getByCode(transCode);
AssertUtils.isNotNull(managerTransCodeEnum, SystemErrorCode.TRANS_CODE_UNDEFINED, new Object[]{transCode});
BackstageOperator backstageOperator = this.applicationContext.getBean(managerTransCodeEnum.getOperator(), BackstageOperator.class);
AssertUtils.isNotNull(backstageOperator,SystemErrorCode.TRANS_CODE_UNDEFINED, new Object[]{transCode});
//2.交易类型校验
ManagerTransTypeEnum managerTransTypeEnum = ManagerTransTypeEnum.getByCode(transType);
AssertUtils.isNotNull(managerTransTypeEnum,SystemErrorCode.TRANS_CODE_UNDEFINED, new Object[]{transType});
OperateExecutor operateExecutor = this.applicationContext.getBean(managerTransTypeEnum.getExecutor(), OperateExecutor.class);
AssertUtils.isNotNull(operateExecutor,SystemErrorCode.TRANS_CODE_UNDEFINED, new Object[]{transType});
//3.参数格式校验
this.fieldValidateExecutor.validate(transCode + transType, context.getRequestEntity().getBody());
//4.交易业务处理
operateExecutor.execute(backstageOperator, context);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
2.2 操作器
后台管理系统基础操作器
BackstageOperator 如下:
/**
* @author kkk
* @Description: 后台管理系统基础操作器
*/
public interface BackstageOperator {
/**
* 增加数据对象
* @param context
* @throws Exception
*/
void add(PayCoreContext context) throws Exception;
/**
* 增加检测
* @param context
* @return
*/
boolean addCheck(PayCoreContext context);
/**
* 删除数据对象
* @param context
* @throws Exception
*/
void delete(PayCoreContext context) throws Exception;
/**
* 删除检测
* @param context
* @return
*/
boolean deleteCheck(PayCoreContext context);
/**
* 修改数据对象
* @param context
* @throws Exception
*/
void modify(PayCoreContext context) throws Exception;
/**
* 修改检测
* @param context
* @return
*/
boolean modifyCheck(PayCoreContext context);
/**
* 查询
* @param context
*/
void query(PayCoreContext context);
/**
* 查询检测
* @param context
* @return
*/
boolean queryCheck(PayCoreContext context);
/**
* 列表查询
* @param context
* @throws Exception
*/
void list(PayCoreContext context) throws Exception;
/**
* 列表查询检测
* @param context
* @return
*/
boolean listCheck(PayCoreContext context);
}
操作器定义了增删改查方法以及对应的校验方法。
2.3 操作执行器
定义操作执行器
OperateExecutor 如下:
/**
* @author Kkk
* @Description: 操作执行器
*/
public interface OperateExecutor {
/**
* @Description 操作执行
* @Params
* @Return void
* @Exceptions
*/
void execute(BackstageOperator operator, PayCoreContext requestParam) throws Exception;
}
操作执行器实现类图如下:
定义增加操作执行器 AddExecutor 如下:
/**
* @author kkk
* @Description: 增加执行器
*/
@Component("addExecutor")
public class AddExecutor implements OperateExecutor {
@Override
public void execute(BackstageOperator operator, PayCoreContext requestParam) throws Exception {
operator.addCheck(requestParam);
operator.add(requestParam);
}
}
即在 BackstageOperateExecutorServiceImpl 中根据交易码获取到基础操作器,根据交易类型获取到操作执行器
,进行业务处理,
//4.交易业务处理
operateExecutor.execute(backstageOperator, context);
2.4 参数校验
同时在 BackstageOperateExecutorServiceImpl 还有很重要的一部分,参数校验,因为在定义接口的时候没有在方法中定于特定的实体类,使用一些框架注解进行参数校验,所以在入口处没有进行参数校验,这里使用基于YML文件配置方式进行参数校验。
//3.参数格式校验
this.fieldValidateExecutor.validate(transCode + transType, context.getRequestEntity().getBody());
在main层资源配置包下配置参数校验文件:
如下为路由规则配置新增校验配置:
#路由规则配置新增
ruleConfigadd:
- field: ruleScene
fieldName: 规则场景
enums: RuleSceneEnum
option: true
style: codeStyle
- field: configType
fieldName: 配置类型
enums: ConfigTypeEnum
option: false
style: codeStyle
- field: configKey
fieldName: 配置属性
option: false
style: codeStyle
- field: configName
fieldName: 配置名称
option: false
length: 1,32
- field: isScript
fieldName: 是否脚本
option: false
enums: BooleanEnum
regex: ^\w{1,4}$
- field: scriptContent
fieldName: 脚本内容
length: 1,65536
- field: remark
fieldName: 备注
style: descStyle
#路由规则配置删除
ruleConfigdelete:
- field: id
fieldName: id
ints: int
option: false
style: idStyle
#路由规则配置修改
ruleConfigmodify:
- field: ruleScene
fieldName: 规则场景
enums: RuleSceneEnum
option: true
style: codeStyle
- field: id
fieldName: 规则配置ID
ints: int
option: false
- field: configType
fieldName: 配置类型
enums: ConfigTypeEnum
option: false
style: codeStyle
- field: configKey
fieldName: 配置属性
option: false
style: codeStyle
- field: configName
fieldName: 配置名称
option: false
length: 1,32
- field: isScript
fieldName: 是否脚本
option: false
enums: BooleanEnum
regex: ^\w{1,4}$
- field: scriptContent
fieldName: 脚本内容
length: 1,65536
- field: remark
fieldName: 备注
style: descStyle
#路由规则配置单笔查询
ruleConfigquery:
- field: id
fieldName: id
option: true
ints: int
style: idStyle
#路由规则配置列表查询
ruleConfiglist:
- field: ruleScene
fieldName: 规则场景
enums: RuleSceneEnum
option: true
style: codeStyle
- field: id
fieldName: id
ints: int
style: idStyle
- field: configKey
fieldName: 配置编码
style: codeStyle
- field: configType
fieldName: 配置属性
style: codeStyle
- field: configName
fieldName: 配置名称
length: 1,32
- field: isScript
fieldName: 是否脚本
regex: ^\w{1,4}$
- field: pageNumber
fieldName: 页码
style: pageStyle
- field: pageSize
fieldName: 页记录数
style: pageStyle
#路由规则配置新增:ruleConfigadd
#路由规则配置删除:ruleConfigdelete
#路由规则配置修改:ruleConfigmodify
#路由规则配置单笔查询:ruleConfigquery
#路由规则配置列表查询:ruleConfiglist
本案例中报文上送header中对应字段如下
“transCode”: “ruleConfig”,
“transType”: “add”
transCode+transType"=ruleConfigadd,所以对应于Yml中的 #路由规则配置新增:ruleConfigadd
。
为了降低参数校验配置的重复工作,我们抽象出style,如果在某项配置新增、修改中有相同的参数,并且校验规则是相同的,则抽取出来,在对应的校验配置文件中只需要引入style中的对应配置即可。
参数/字段验证器接口实现类图,已经涵盖了日常开发所涉及的参数校验
关于此处使用Yml方式对接口参数进行校验的方式此处不在展开了,后期有机会单独开文章介绍。
2.5 操作器实现
后台管理系统规则条件配置操作器具体实现如下:
/**
* @author Kkk
* @Describe: 后台管理系统规则条件配置操作器
*/
@Service("ruleConfigOperator")
public class RuleConfigOperator extends AbstractBackstageOperator {
@Autowired
private RuleConfigRepository ruleConfigRepository;
@Autowired
private RouterRuleDomainRepository routerRuleDomainRepository;
@Override
public void add(PayCoreContext context) throws Exception {
RuleConfig ruleConfig = new RuleConfig();
Map<String, Object> requestBodyMap = context.getRequestEntity().getBody();
BeanUtils.populate(ruleConfig, requestBodyMap);
Date currentDateTime = DateUtil.getCurrentDate();
ruleConfig.setCreateTime(currentDateTime);
ruleConfig.setUpdateTime(currentDateTime);
ruleConfig.setIsDelete(DeleteEnum.NO.getCode());
ruleConfigRepository.saveAndFlush(ruleConfig);
}
@Override
public boolean addCheck(PayCoreContext context) {
Map<String, Object> requestBodyMap = context.getRequestEntity().getBody();
RuleConfig queryRuleConfig = new RuleConfig();
queryRuleConfig.setRuleScene(String.valueOf(requestBodyMap.get(PayCoreConstant.RULE_SCENE)));
queryRuleConfig.setConfigKey(String.valueOf(requestBodyMap.get(PayCoreConstant.CONFIG_KEY)));
Specification<RuleConfig> specification = buildQueryCondition(queryRuleConfig);
RuleConfig ruleConfig = ruleConfigRepository.findOne(specification);
if(ruleConfig != null) {
throw new PayCoreException(SystemErrorCode.BIZ_CHECK_ERROR,new Object[]{"已存在该规则属性,不能新增"});
}
String isScript = requestBodyMap.get(PayCoreConstant.IS_SCRIPT)==null?"":requestBodyMap.get(PayCoreConstant.IS_SCRIPT).toString();
String scriptContent = requestBodyMap.get(PayCoreConstant.SCRIPT_CONTENT)==null?"":requestBodyMap.get(PayCoreConstant.SCRIPT_CONTENT).toString();
if(BooleanEnum.YES.getCode().equals(isScript) && StringUtils.isEmpty(scriptContent)) {
throw new PayCoreException(SystemErrorCode.BIZ_CHECK_ERROR,new Object[]{"需要填写脚本内容,不能新增"});
}
return false;
}
@Override
public void delete(PayCoreContext context) throws Exception {
Map<String, Object> requestBodyMap = context.getRequestEntity().getBody();
RuleConfig queryRuleConfig = new RuleConfig();
queryRuleConfig.setId(Long.valueOf(String.valueOf(requestBodyMap.get(PayCoreConstant.ID))));
Specification<RuleConfig> specification = buildQueryCondition(queryRuleConfig);
RuleConfig ruleConfig = ruleConfigRepository.findOne(specification);
if(ruleConfig != null) {
deleteCheck(ruleConfig);
ruleConfig.setIsDelete(DeleteEnum.YES.getCode());
}else {
throw new PayCoreException(SystemErrorCode.BIZ_CHECK_ERROR,new Object[]{"要修改的数据不存在"});
}
ruleConfigRepository.saveAndFlush(ruleConfig);
}
@Override
public boolean deleteCheck(PayCoreContext context) {
return false;
}
private void deleteCheck(RuleConfig ruleConfig) throws Exception{
RuleCondition ruleCondition = new RuleCondition();
ruleCondition.setRuleScene(ruleConfig.getRuleScene());
ruleCondition.setConfigKey(ruleConfig.getConfigKey());
List<RuleCondition> ruleCondtionList= routerRuleDomainRepository.findAllRuleCondition(ruleCondition);
if(ruleCondtionList != null && ruleCondtionList.size() > 0) {
throw new PayCoreException(SystemErrorCode.BIZ_CHECK_ERROR,new Object[]{"已被规则条件引用,不能删除"});
}
}
@Override
public void modify(PayCoreContext context) throws Exception {
Map<String, Object> requestBodyMap = context.getRequestEntity().getBody();
RuleConfig queryRuleConfig = new RuleConfig();
queryRuleConfig.setId(Long.valueOf(String.valueOf(requestBodyMap.get(PayCoreConstant.ID))));
Specification<RuleConfig> specification = buildQueryCondition(queryRuleConfig);
RuleConfig ruleConfig = ruleConfigRepository.findOne(specification);
if(ruleConfig != null) {
BeanUtils.populate(ruleConfig, requestBodyMap);
}else {
throw new PayCoreException(SystemErrorCode.BIZ_CHECK_ERROR,new Object[]{"要修改的数据不存在"});
}
ruleConfigRepository.saveAndFlush(ruleConfig);
}
@Override
public boolean modifyCheck(PayCoreContext context) {
Map<String, Object> requestBodyMap = context.getRequestEntity().getBody();
String ruleScene = StringUtils.valueOf(requestBodyMap.get(PayCoreConstant.RULE_SCENE));
String configKey = String.valueOf(requestBodyMap.get(PayCoreConstant.CONFIG_KEY));
RuleConfig queryRuleConfig = new RuleConfig();
queryRuleConfig.setRuleScene(ruleScene);
queryRuleConfig.setConfigKey(configKey);
Specification<RuleConfig> specification = buildQueryCondition(queryRuleConfig);
List<RuleConfig> ruleConfigs = ruleConfigRepository.findAll(specification);
if(ruleConfigs != null) {
for(RuleConfig ruleConfig: ruleConfigs) {
if(ruleConfig.getId().compareTo(Long.valueOf(StringUtils.valueOf(requestBodyMap.get(PayCoreConstant.ID)))) != 0) {
throw new PayCoreException(SystemErrorCode.BIZ_CHECK_ERROR,new Object[]{"已存在该规则属性,不能修改"});
}
}
}
String isScript = requestBodyMap.get(PayCoreConstant.IS_SCRIPT)==null?"":requestBodyMap.get(PayCoreConstant.IS_SCRIPT).toString();
String scriptContent = requestBodyMap.get(PayCoreConstant.SCRIPT_CONTENT)==null?"":requestBodyMap.get(PayCoreConstant.SCRIPT_CONTENT).toString();
if(BooleanEnum.YES.getCode().equals(isScript) && StringUtils.isEmpty(scriptContent)) {
throw new PayCoreException(SystemErrorCode.BIZ_CHECK_ERROR,new Object[]{"需要填写脚本内容,不能修改"});
}
return false;
}
@Override
public void query(PayCoreContext context) {
}
@Override
public boolean queryCheck(PayCoreContext context) {
return false;
}
@Override
public void list(PayCoreContext context) throws Exception {
PageRequest pageRequest = new MessageEntityWrap(context.getRequestEntity()).getPageRequestFromRequestBody();
Map<String, Object> requestBodyMap = context.getRequestEntity().getBody();
RuleConfig ruleConfig = new RuleConfig();
BeanUtils.populate(ruleConfig, requestBodyMap);
Specification<RuleConfig> specification = buildQueryCondition(ruleConfig);
if(pageRequest == null) {
List<RuleConfig> ruleList= ruleConfigRepository.findAll(specification);
buildResponseEntity(context, MapUtils.buildMap(PayCoreConstant.PAGE_CONTENT, ruleList));
} else {
Page<RuleConfig> ruleList =ruleConfigRepository.findAll(specification, pageRequest);
buildResponseEntity(context, ruleList);
}
}
@Override
public boolean listCheck(PayCoreContext context) {
return false;
}
public Specification buildQueryCondition(final RuleConfig ruleConfig){
return new Specification<RuleConfig>() {
@Override
public Predicate toPredicate(Root<RuleConfig> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<Predicate>();
Predicate deletePredicate = cb.equal(root.get(PayCoreConstant.IS_DELETE).as(String.class), DeleteEnum.NO.getCode());
list.add(deletePredicate);
if(ruleConfig.getId() != null) {
Predicate predicate = cb.equal(root.get(PayCoreConstant.ID).as(Long.class), Long.valueOf(ruleConfig.getId()));
list.add(predicate);
}
if(ruleConfig.getRuleScene() != null) {
Predicate predicate = cb.equal(root.get(PayCoreConstant.RULE_SCENE).as(String.class), ruleConfig.getRuleScene());
list.add(predicate);
}
if(ruleConfig.getConfigKey() != null) {
Predicate predicate = cb.equal(root.get(PayCoreConstant.CONFIG_KEY).as(String.class), ruleConfig.getConfigKey());
list.add(predicate);
}
if(ruleConfig.getConfigName() != null) {
Predicate predicate = cb.equal(root.get(PayCoreConstant.CONFIG_NAME).as(String.class), ruleConfig.getConfigName());
list.add(predicate);
}
Predicate[] predicates = new Predicate[list.size()];
query.where(cb.and(list.toArray(predicates)));
return query.getRestriction();
}
};
}
}
3. Domain层
domain层主要包括领域模型工厂、实体类、聚合、领域模型仓储、领域服务等包。
在上面RuleConfigOperator中,我们可以看到对于持久化引入了领域模型仓储、仓储两个类:
@Autowired
private RuleConfigRepository ruleConfigRepository;
@Autowired
private RouterRuleDomainRepository routerRuleDomainRepository;
RuleConfigRepository 规则配置仓储,用于持久化配置信息;RouterRuleDomainRepository 路由领域模型仓储用于校验我们对配置的删除操作是否允许,及是否有路由引用此规则。
注意:领域模型存储仓储定义于domain层仓储定义于dal层,领域模型存储仓储包含多个仓储,如下,路由配置领域模型领域仓储
包含规则仓储
和规则条件仓储
。
/**
* @author Kkk
* @Describe: 路由配置领域模型领域仓储
*/
@Repository
public class RouterRuleDomainRepositoryImpl implements RouterRuleDomainRepository {
@Autowired
private RouterRuleRepository ruleRepository;
@Autowired
private RuleConditionRepository ruleConditionRepository;
//... ...
}
4. Infrastructure层
4.1 Dal层
对于后台管理接口在Infrastructure层主要使用到了Dal层进行数据持久化操作。
dal层主要包含和数据库对应的实体,以及持久化的repository类。
三、对应类图
四、开发内容
如上介绍了那么多内容,此时我们后台管理系统又增加了如下一项页面配置,那么开发需要做什么?
3.1 约定请求报文格式
{
"header": {
"channelCode": "",
"channelDateTime": "",
"channelNo": "",
"transCode": "baseConfig",
"transType": "add"
},
"body": {
"configKey": "scene_code",
"code": "001",
"name": "场景码",
"value": "对应值"
}
}
3.2 新增交易码与操作器映射枚举类
/**
* @author Kkk
* @Description: 交易码与操作器映射枚举类
*/
public enum ManagerTransCodeEnum {
baseConfig("baseConfig", "baseConfigOperator", "基础信息管理");
}
3.3 配置参数校验Yml
#基本配置新增
baseConfigadd:
- field: configKey
fieldName: 类型
option: false
length: 1,256
- field: code
fieldName: 编码
option: false
style: codeStyle
- field: name
fieldName: 名称
option: false
regex: ^[\w\-\.\u00b7\u4E00-\u9FA5]{1,256}$
- field: value
fieldName: 值
length: 1,256
- field: remark
fieldName: 备注
length: 1,256
3.4 编写操作器(核心工作内容)
/**
* @author Kkk
* @Describe: 后台管理系统基本配置操作器
*/
@Service("baseConfigOperator")
public class BaseConfigOperator extends AbstractBackstageOperator {
@Autowired
private BaseConfigRepository baseConfigRepository;
@Override
public void add(PayCoreContext context) throws Exception {
BaseConfig baseConfig = new BaseConfig();
Map<String, Object> requestBodyMap = context.getRequestEntity().getBody();
BeanUtils.populate(baseConfig, requestBodyMap);
Date currentDateTime = DateUtil.getCurrentDate();
baseConfig.setCreateTime(currentDateTime);
baseConfig.setUpdateTime(currentDateTime);
baseConfig.setIsDelete(DeleteEnum.NO.getCode());
getIsExistAdd(baseConfig.getConfigKey(), baseConfig.getCode(), baseConfig.getId());
baseConfigRepository.saveAndFlush(baseConfig);
}
@Override
public boolean addCheck(PayCoreContext context) {
Map<String, Object> requestBodyMap = context.getRequestEntity().getBody();
BaseConfig queryBaseConfig = new BaseConfig();
queryBaseConfig.setCode(String.valueOf(requestBodyMap.get("code")));
queryBaseConfig.setConfigKey(String.valueOf(requestBodyMap.get("configKey")));
Specification<BaseConfig> specification = buildQueryCondition(queryBaseConfig);
BaseConfig BaseConfig = baseConfigRepository.findOne(specification);
if (BaseConfig != null) {
throw new PayCoreException(SystemErrorCode.BIZ_CHECK_ERROR, new Object[]{"已存在该编码,不能新增"});
}
return false;
}
// ... ...
}
3.5 编写仓储和实体
/**
* @author Kkk
* @Describe: 基础配置表仓储接口实现
*/
@Repository
public class BaseConfigRepositoryImpl extends GeneralRepositoryImpl<BaseConfig, Long> {
}
通过如上5步骤完成了,后台页面的新增配置的功能,虽然代码量并没有减少很多,但是我们的项目整洁性以及后期迁移性得到了极大的保证。
总结
本篇主要以后台管理配置接口简要的介绍了下工作中所使用的统一开发框架结构。