“强大”的MapPPP
写在前面
因为要给用户发送通知提醒,项目中有个短信模板/微信模板/钉钉模板/邮件模板的占位符替换的class。其中一段代码的逻辑是根据入参(model/json)来定义要替换的占位符集合,使用的是Map,其中key是占位符,value是入参对象的属性值。
Map<String, String> replaceDataMap = new HashMap<>();
replaceDataMap.put("【商户名称】", merchant.getMerchantName());
replaceDataMap.put("【企业名称】", merchant.getMerchantName());
replaceDataMap.put("【票号】", order.getDraftNo());
replaceDataMap.put("【订单号】", order.getOrderNo());
replaceDataMap.put("【失败原因】", order.getReason());
replaceDataMap.put("【具体原因】", order.getReason());
replaceDataMap.put("【失败理由】", order.getReason());
replaceDataMap.put("【截止时间】", formatToDateTime(order.getProxyExpiryDate()));
接下来的逻辑是遍历这个Map,去把短信模板/微信模板/钉钉模板/邮件模板中匹配的占位符替换成map的Value。
为什么要MapPPP?
注意到上面往Map放的键,有【商户名称】和【企业名称】,有【失败原因】和【具体原因】和【失败理由】。即,不同的占位符会表示相同的含义。
--你可能会想:不带这样子的ya!
--在我们这个系统它的确存在,因为这些通知模板没有专门的维护功能页,是产品经理给到我们导入进来的。
--当然,你会说:处理一下改成一致不就得了!比如【商户名称】和【企业名称】都统一改成【商户名称】。
--没毛病。可是假如下次产品经理追加模板再发给我们,假如不是给我,是给别人了,那么他在导入到数据库的时候,还要问我或翻代码。
--到这里你也许该不耐烦了:那就这么着把2个/3个都put到Map里不就完事了嘛!你到底要说什么?
没错,我要说的是,咱们兼容产品经理的这种任性,把占位符的各种情况(也就这么二三种情况)穷举定义到这个map里。
我还要说的,就是本文要说的,如果有个连续put的Map,我把相同含义的占位符通过.put.put放到一个代码段里,可能会更可读、可维护。即:
replaceDataMap.put("【商户名称】", merchant.getMerchantName()).put("【企业名称】", merchant.getMerchantName());
replaceDataMap.put("【票号】", order.getDraftNo());
replaceDataMap.put("【订单号】", order.getOrderNo());
replaceDataMap.put("【失败原因】", order.getReason())
.put("【具体原因】", order.getReason())
.put("【失败理由】", order.getReason());
replaceDataMap.put("【截止时间】", formatToDateTime(order.getProxyExpiryDate()));
Map类型本身是不支持连续put的。因此,就有了MapPPP的诞生。
为什么叫MapPPP?
首先,是为了替换Map工具的,所以命名以Map-开头。
道德经:一生三,三生万物。三个P(Put)表示连续Put,所以取名MapPPP。
MapPPP定义
核心方法是put,存放占位符和属性值的键值对。另外,replace方法,是将给定的模板内容里的占位符替换成相应的属性值。
ps:设计replace方法时倒也费了一番周折。因为String本身是值传递,所以方法内部改变其值是带不出来的。java也没有c#语言的ref或out关键字。后来我就用String数组,不过调用就有些费劲。再后来跟同事商量,改用StringBuilder,嗯,是个好办法!
package com.emaxcard.util; import com.google.common.collect.Maps;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Map; /**
* 可以连续put的map工具
*
* @param <K>
* @param <V>
*/
public final class MapPPP<K, V> extends Object implements Cloneable, Serializable {
private Map<K, V> _map; public MapPPP() {
_map = Maps.newHashMap();
} public MapPPP<K, V> put(K key, V value) {
_map.put(key, value);
return this;
}
public void replace(StringBuilder... stringBuilders) {
Iterator<Map.Entry<K, V>> iterator = _map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<K, V> entry = iterator.next();
if (entry.getValue() != null) {
for (int i = 0; i < stringBuilders.length; i++) {
StringBuilder s = stringBuilders[i];
String entryKey = String.valueOf(entry.getKey());
if (s.indexOf(entryKey) >= 0) {
s.replace(s.indexOf(entryKey), s.indexOf(entryKey) + entryKey.length(), String.valueOf(entry.getValue()));
}
}
}
}
} @Override
public String toString() {
StringBuilder stringValue = new StringBuilder();
Iterator<Map.Entry<K, V>> iterator = _map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<K, V> entry = iterator.next();
stringValue.append(entry.getKey()).append(":").append(entry.getValue()).append("\r\n");
}
return stringValue.toString();
}
}
怎么用MapPPP?
MapPPP<String, String> replaceDataMap = new MapPPP<>();
replaceDataMap.put("【商户名称】", merchant.getMerchantName()).put("【企业名称】", merchant.getMerchantName())
.put("【票号】", order.getDraftNo())
.put("【订单号】", order.getOrderNo())
.put("【票面金额】", order.getOrderAmt() !=null ? order.getOrderAmt().divide(BigDecimal.valueOf(10000)) + "万元":"")//**经测试,divide之后会自动去除小数点最后的0
.put("【提醒时间】", AccountOperationReminder.formatToDateTime(new Date()))
//在截止时间内回购
.put("【截止时间】", AccountOperationReminder.formatToDateTime(order.getProxyExpiryDate()))
.put("【失败原因】", order.getRemark()).put("【具体理由】", order.getRemark()).put("【具体原因】", order.getRemark())
.put("【票据数量】", order.getCreateBy()); StringBuilder smsContent = new StringBuilder(tradeRemindTypeEnum.getSmsTemplateContent());
StringBuilder wechatTemplateContent = new StringBuilder(tradeRemindTypeEnum.getWechatTemplateContent());
replaceDataMap.replace(smsContent, wechatTemplateContent); for (WarnType warnType : tradeRemindTypeEnum.getWarnTypes()) {
if (WarnType.WECHAT == warnType) {
... ... Map<String, String> wechatDataMap = AccountOperationReminder.getWechatDataMap(wechatTemplateContent.toString());
SendMessageUtil.sendWechat(jsonObject.getString("openid"), tradeRemindTypeEnum.getWechatTemplateId(), dataMap); } else if (WarnType.SENDMESSAGE == warnType) {
SendMessageUtil.sendSMS(user.getMobile(), smsContent, order.getOrderNo());
}
}
爽!
再说一个强大的StringCheckUtils
在我们的项目里,还有一个很强大的StringCheckUtils。众所周知,org.apache.commons.lang3包里提供了StringUtils,用来对字符串判空、去除空格(trim)、取子串、去头去尾(strip),等等处理。apache之所以提供这个工具包,很容易理解,通过封装基本的操作,让我们只需关注企业应用开发即可。这样,一方面提高了开发效率,另一方面,更重要的,使得程序更易读易维护。这就是它的强大之处,许多的工具和框架也都是基于这样的理念。再来说StringCheckUtils,其实,和MapPPP一样,也是基于这个理念的延伸。
package com.emaxcard.util; import org.apache.commons.lang3.StringUtils;
import java.util.stream.Stream; /**
* 字符串校验工具类.
*/
public abstract class StringCheckUtils { /**
* 有任何一个参数为空则返回true
*
* @param val
* @return
*/
public static boolean isBlank(String... val) {
Stream<String> strStream = Stream.of(val);
return strStream.anyMatch(str -> StringUtils.isBlank(str));
} /**
* 只要有一个参数不为空则返回true
*
* @param val
* @return
*/
public static boolean isNotBlank(String... val) {
Stream<String> strStream = Stream.of(val);
return strStream.anyMatch(str -> StringUtils.isNotBlank(str));
} }
“强大”的MapPPP的更多相关文章
- Postman - 功能强大的 API 接口请求调试和管理工具
Postman 是一款功能强大的的 Chrome 应用,可以便捷的调试接口.前端开发人员在开发或者调试 Web 程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的 Fi ...
- 纯JS打造比QQ空间更强大的图片浏览器-支持拖拽、缩放、过滤、缩略图等
在线演示地址(打开网页后,点击商家图册): http://www.sport7.cn/cc/jiangnan/football5.html 先看一看效果图: 该图片浏览器实现的功能如下: 1. 鼠标滚 ...
- 你从未知道如此强大的ASP.NET MVC DefaultModelBinder
看到很多ASP.NET MVC项目还在从request.querystring或者formContext里面获取数据,这实在是非常落后的做法.也有的项目建了大量的自定义的modelbinder,以为很 ...
- 虚拟机体验之 VirtualBox 篇 —— 性能强大的经典架构
前两篇体验了 QEMU 和经过 KVM 加速的 QEMU,并体验了第三方虚拟机管理工具 virt-manager,让我们见识了开源社区的强大和开源虚拟机软件的高质量和高性能.这一篇,我来剖析一下 Vi ...
- transformjs污染了DOM?是你不了解它的强大
原文链接: https://github.com/AlloyTeam/AlloyTouch/wiki/Powerful-transformjs 写在前面 上星期在React微信群里,有小伙伴觉得tra ...
- 强大的flash头像上传插件(支持旋转、拖拽、剪裁、生成缩略图等)
今天介绍的这款flash上传头像功能非常强大,支持php,asp,jsp,asp.net 调用 头像剪裁,预览组件插件. 本组件需要安装Flash Player后才可使用,请从http://dl.pc ...
- 强大的支持多文件上传的jQuery文件上传插件Uploadify
支持多文件上传的jQuery文件上传插件Uploadify,目前此插件有两种版本即Flash版本和HTML5版本,对于HTML5版本会比较好的支持手机浏览器,避免苹果手机Safari浏览器不支持Fla ...
- 一款强大的Android网络渗透软件dsploit
dSploit是一款基于Android系统的功能十分全面强大的网络渗透工具,可以提供给网络安全工作人员检查网络的安全性.小黑这次主要使用了其中的"简易嗅探""会话劫持&q ...
- 5款强大的Java Web开发工具
1.WebBuilder这是一款开源的可视化Web应用开发和运行平台.基于浏览器的集成开发环境,采用可视化的设计模式,支持控件的拖拽操作,能轻松完成前后台应用开发:高效.稳定和可扩展的特点,适合复杂企 ...
随机推荐
- Android 基于ksoap2的webservice请求的学习
[学习阶段] WebService网络请求? 其实我也是第一次遇到,之所以有这个需要是因为一些与 ERP 相关的业务,需要用到这样的一个请求方式. 开始学习WebService ①当然是百度搜索,这里 ...
- [Codeforces 1244C] The Football Season
思维加枚举 题意 :足球赛,赢平所得到的分数分别为w和d,w>d,分别求赢平输的场数,输出一组即可,即x+y+z=n 且 xw+yd=p的一组解. 可以扩展公约数做,但由于注意到d和w<1 ...
- CefSharp 无法输入中文的问题
在CefSharp75版本,使用了WpfImeKeyboardHandler支持后,无法支持搜狗中文输入法 其中的一个修复方案: 在ChrominumWebBrowser中,添加焦点事件的重写,对In ...
- Java生鲜电商平台-深入理解微服务SpringCloud各个组件的关联与架构
Java生鲜电商平台-深入理解微服务SpringCloud各个组件的关联与架构 概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留 ...
- springboot向elk写日志
springboot里连接elk里的logstash,然后写指定index索引的日志,而之后使用kibana去查询和分析日志,使用elasticsearch去保存日志. 添加引用 implementa ...
- 如何使用 CODING 实践 DevOps 全流程
你好,欢迎使用 CODING!这份最佳实践将帮助你通过 CODING 研发管理系统来更好地实践 DevOps 流程. DevOps 的本质是打破各个部门之间的隔阂,打通企业的前中后台,推进跨部门协作. ...
- Mysql增量备份之Mysqldump&Mylvmbackup
简单介绍 备份类型 备份方式 热备份:备份期间不需要服务停机,业务不受影响: 温备份:备份期间仅允许读的请求: 冷备份:备份期间需要关闭Mysql服务或读写请求都不受影响: 完全备份:full bac ...
- Go语言系列:(2)go get 命令介绍
Go语言的代码被托管于 Github.com 网站,该网站是基于 Git 代码管理工具的,很多有名的项目都在该网站托管代码.其他类似的托管网站还有 code.google.com.bitbucket. ...
- Linux系统学习 二十二、SAMBA服务—Samba基本使用—share权限访问、客户端的使用
share权限访问 配置文件修改 [global]全局设置 workgroup=MYGROUP server string=Samba Server Lamp log file=/var/log/sa ...
- 关于定义序列化器时,read_only和write_only有什么作用
关于序列化和反序列化 在谈论前,先说一下序列化和反序列化,这两个概念最初是在学习json的时候提出来的,回头来看,其实可以用最初的理解就可以了 序列化就是将对象转化方便传输和存储字节序列,例如js ...