支付系统设计二:统一开发框架

news/2025/2/12 21:01:55/

文章目录

  • 前言
  • 一、项目分层
  • 二、模块职责简介
    • 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.创建contextPayCoreContext context = createContext(request, response);//2.填充contextfillContext(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.构造modelMap<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);}@Overrideprotected void handleRequestInternal(PayCoreContext context) throws Exception {this.backstageOperateExecutorService.execute(context);}/*** 视图解析->后台接口独立视图* @return*/@Overrideprotected 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;/*** 字段验证器*/@Autowiredprivate FieldValidateExecutor fieldValidateExecutor;@Overridepublic 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);}@Overridepublic 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 {@Overridepublic 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: ruleScenefieldName: 规则场景enums: RuleSceneEnumoption: truestyle: codeStyle- field: configTypefieldName: 配置类型enums: ConfigTypeEnumoption: falsestyle: codeStyle- field: configKeyfieldName: 配置属性option: falsestyle: codeStyle- field: configNamefieldName: 配置名称option: falselength: 1,32- field: isScriptfieldName: 是否脚本option: falseenums: BooleanEnumregex: ^\w{1,4}$- field: scriptContentfieldName: 脚本内容length: 1,65536- field: remarkfieldName: 备注style: descStyle
#路由规则配置删除
ruleConfigdelete:- field: idfieldName: idints: intoption: falsestyle: idStyle
#路由规则配置修改
ruleConfigmodify:- field: ruleScenefieldName: 规则场景enums: RuleSceneEnumoption: truestyle: codeStyle- field: idfieldName: 规则配置IDints: intoption: false- field: configTypefieldName: 配置类型enums: ConfigTypeEnumoption: falsestyle: codeStyle- field: configKeyfieldName: 配置属性option: falsestyle: codeStyle- field: configNamefieldName: 配置名称option: falselength: 1,32- field: isScriptfieldName: 是否脚本option: falseenums: BooleanEnumregex: ^\w{1,4}$- field: scriptContentfieldName: 脚本内容length: 1,65536- field: remarkfieldName: 备注style: descStyle
#路由规则配置单笔查询
ruleConfigquery:- field: idfieldName: idoption: trueints: intstyle: idStyle
#路由规则配置列表查询
ruleConfiglist:- field: ruleScenefieldName: 规则场景enums: RuleSceneEnumoption: truestyle: codeStyle- field: idfieldName: idints: intstyle: idStyle- field: configKeyfieldName: 配置编码style: codeStyle- field: configTypefieldName: 配置属性style: codeStyle- field: configNamefieldName: 配置名称length: 1,32- field: isScriptfieldName: 是否脚本regex: ^\w{1,4}$- field: pageNumberfieldName: 页码style: pageStyle- field: pageSizefieldName: 页记录数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 {@Autowiredprivate RuleConfigRepository ruleConfigRepository;@Autowiredprivate RouterRuleDomainRepository routerRuleDomainRepository;@Overridepublic 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);}@Overridepublic 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;}@Overridepublic 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);}@Overridepublic 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[]{"已被规则条件引用,不能删除"});}}@Overridepublic 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);}@Overridepublic 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;}@Overridepublic void query(PayCoreContext context) {}@Overridepublic boolean queryCheck(PayCoreContext context) {return false;}@Overridepublic 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);}}@Overridepublic boolean listCheck(PayCoreContext context) {return false;}public Specification buildQueryCondition(final RuleConfig ruleConfig){return new Specification<RuleConfig>() {@Overridepublic 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中,我们可以看到对于持久化引入了领域模型仓储、仓储两个类:

    @Autowiredprivate RuleConfigRepository ruleConfigRepository;@Autowiredprivate RouterRuleDomainRepository routerRuleDomainRepository;

RuleConfigRepository 规则配置仓储,用于持久化配置信息;RouterRuleDomainRepository 路由领域模型仓储用于校验我们对配置的删除操作是否允许,及是否有路由引用此规则。

注意:领域模型存储仓储定义于domain层仓储定义于dal层,领域模型存储仓储包含多个仓储,如下,路由配置领域模型领域仓储包含规则仓储规则条件仓储

/*** @author Kkk* @Describe: 路由配置领域模型领域仓储*/
@Repository
public class RouterRuleDomainRepositoryImpl implements RouterRuleDomainRepository {@Autowiredprivate RouterRuleRepository ruleRepository;@Autowiredprivate 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: configKeyfieldName: 类型option: falselength: 1,256- field: codefieldName: 编码option: falsestyle: codeStyle- field: namefieldName: 名称option: falseregex: ^[\w\-\.\u00b7\u4E00-\u9FA5]{1,256}$- field: valuefieldName:length: 1,256- field: remarkfieldName: 备注length: 1,256

3.4 编写操作器(核心工作内容)

/*** @author Kkk* @Describe: 后台管理系统基本配置操作器*/
@Service("baseConfigOperator")
public class BaseConfigOperator extends AbstractBackstageOperator {@Autowiredprivate BaseConfigRepository baseConfigRepository;@Overridepublic 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);}@Overridepublic 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步骤完成了,后台页面的新增配置的功能,虽然代码量并没有减少很多,但是我们的项目整洁性以及后期迁移性得到了极大的保证。


总结

本篇主要以后台管理配置接口简要的介绍了下工作中所使用的统一开发框架结构。


http://www.ppmy.cn/news/67276.html

相关文章

浅尝Kubernetes

第一节 内容编排与Kubernetes 为什么要用k8s 集群环境容器部署的困境&#xff0c;假设我们有数十台服务器。分别部署Nginx&#xff0c;redis&#xff0c;mysql&#xff0c;业务服务。如何合理的分配这些资源。这里就需要用到容器编排 容器编排 在实际集群环境下&#xff0…

3.Docker实用技术

Docker实用篇 0.学习目标 1.初识Docker 1.1.什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。在数百上千台服务中重复部署…

平衡二叉树理论详解

文章目录 基本概念平衡二叉树插入结点LL&#xff08;左单旋&#xff09;RR&#xff08;右单旋&#xff09;LR&#xff08;左右旋&#xff09;RL&#xff08;右左旋&#xff09; 示例插入推导过程 基本概念 平衡二叉树是一棵空树或它的左右两个子树的高度差的绝对值不超过1&…

Caused by: java.io.EOFException: SSL peer shut down incorrectly

Android Studio新建项目提示 Caused by: java.io.EOFException: SSL peer shut down incorrectly 环境 1、Android Studio版本&#xff1a;4.1 Version 4.1 (AI-201.8743.12.41.6858069) 2、项目的仓库信息&构建工具版本 buildscript {ext.kotlin_version "1.4.…

2023年河北沃克高位承重货架最新中标项目|中国沈阳某大型集团高位重型横梁式货架项目建设初期

【项目名称】高位重型横梁式货架项目 【承建单位】河北沃克金属制品有限公司 【合作客户】中国沈阳某大型集团 【建设时间】2023年5月上旬 【建设地域】中国沈阳地区 【项目客户需求】 本次沈阳高位重型横梁式货架项目合作的沈阳某大型集团中国变压器行业规模最大的制造企…

前端框架比较:Vue.js、React、AngularJS三者的优缺点和应用场景

章节一&#xff1a;引言 在当前的互联网开发中&#xff0c;前端框架已经成为了不可或缺的一部分。然而&#xff0c;前端框架如此之多&#xff0c;该如何选择呢&#xff1f;Vue.js、React和AngularJS是目前比较受欢迎的三个前端框架&#xff0c;它们各自有着不同的优缺点和应用…

杂记 2023.5.11

目录 come across(as).. 与异性对话经验和理论、策略 单词记忆 机器学习 come across(as).. 这个用法在口语里超级高频&#xff0c;表示「给人.印象&#xff0c;让人觉得..」&#xff0c;s后面可跟名词、形容词、 being形容词。 我们再来看几个例子&#xff1a; ◆He comes ac…

EFDC建模方法及在地表水环境评价、水源地划分、排污口论证中的应用

为了定量地描述地表水环境质量与污染排放之间的动态关系&#xff0c;EFDC、MIKE、Delft3D、Qual2K等数值模型被广泛应用在环境、水务、海洋等多个领域。Environmental Fluid Dynamics Code&#xff08;EFDC&#xff09;是一款用于模拟江河&#xff0c;湖泊&#xff0c;河口&…