写在前面

因为要给用户发送通知提醒,项目中有个短信模板/微信模板/钉钉模板/邮件模板的占位符替换的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的更多相关文章

  1. Postman - 功能强大的 API 接口请求调试和管理工具

    Postman 是一款功能强大的的 Chrome 应用,可以便捷的调试接口.前端开发人员在开发或者调试 Web 程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的 Fi ...

  2. 纯JS打造比QQ空间更强大的图片浏览器-支持拖拽、缩放、过滤、缩略图等

    在线演示地址(打开网页后,点击商家图册): http://www.sport7.cn/cc/jiangnan/football5.html 先看一看效果图: 该图片浏览器实现的功能如下: 1. 鼠标滚 ...

  3. 你从未知道如此强大的ASP.NET MVC DefaultModelBinder

    看到很多ASP.NET MVC项目还在从request.querystring或者formContext里面获取数据,这实在是非常落后的做法.也有的项目建了大量的自定义的modelbinder,以为很 ...

  4. 虚拟机体验之 VirtualBox 篇 —— 性能强大的经典架构

    前两篇体验了 QEMU 和经过 KVM 加速的 QEMU,并体验了第三方虚拟机管理工具 virt-manager,让我们见识了开源社区的强大和开源虚拟机软件的高质量和高性能.这一篇,我来剖析一下 Vi ...

  5. transformjs污染了DOM?是你不了解它的强大

    原文链接: https://github.com/AlloyTeam/AlloyTouch/wiki/Powerful-transformjs 写在前面 上星期在React微信群里,有小伙伴觉得tra ...

  6. 强大的flash头像上传插件(支持旋转、拖拽、剪裁、生成缩略图等)

    今天介绍的这款flash上传头像功能非常强大,支持php,asp,jsp,asp.net 调用 头像剪裁,预览组件插件. 本组件需要安装Flash Player后才可使用,请从http://dl.pc ...

  7. 强大的支持多文件上传的jQuery文件上传插件Uploadify

    支持多文件上传的jQuery文件上传插件Uploadify,目前此插件有两种版本即Flash版本和HTML5版本,对于HTML5版本会比较好的支持手机浏览器,避免苹果手机Safari浏览器不支持Fla ...

  8. 一款强大的Android网络渗透软件dsploit

    dSploit是一款基于Android系统的功能十分全面强大的网络渗透工具,可以提供给网络安全工作人员检查网络的安全性.小黑这次主要使用了其中的"简易嗅探""会话劫持&q ...

  9. 5款强大的Java Web开发工具

    1.WebBuilder这是一款开源的可视化Web应用开发和运行平台.基于浏览器的集成开发环境,采用可视化的设计模式,支持控件的拖拽操作,能轻松完成前后台应用开发:高效.稳定和可扩展的特点,适合复杂企 ...

随机推荐

  1. Client error attempting to change layout margins of a private view

    从 iOS 11 开始,UINavigationBar 使用了自动布局,左右两边的按钮到屏幕之间会有 16 或 20 的边距. 为了避免点击到间距的空白处没有响应,通常做法是:定义一个 UINavig ...

  2. mysql安装过程及无法启动mysql的办法

    下载并解压MySQL 下载mysql-8.0.17-win64 \https://dev.mysql.com/downloads/mysql/8.0.html        // 这里提供的是8.0以 ...

  3. 记一次ES查询数据突然变为空的问题

    基本环境 elasticsearch版本:6.3.1 客户端环境:kibana 6.3.4.Java8应用程序模块. 其中kibana主要用于数据查询诊断和查阅日志,Java8为主要的客户端,数据插入 ...

  4. 松软科技web课堂:随机Math.random()

    Math.random() 返回 0(包括) 至 1(不包括) 之间的随机数: 实例 Math.random(); // 返回随机数 JavaScript 随机整数 Math.random() 与 M ...

  5. vuejs中拖动改变元素宽度实现宽度自适应大小

    需求效果: 原理:拖动效果的实现基本都是dom操作来实现的,通过拖动分隔线,计算分隔线与浏览器边框的距离(left),来实现拖动之后的不同宽度的计算:当拖动分隔线1时,计算元素框left和mid:当拖 ...

  6. python yield关键词使用总结

    python yield关键词使用总结 by:授客 QQ:1033553122 测试环境 win10 python 3.5 yield功能简介 简单来说,yield 的作用就是把一个函数变成一个 ge ...

  7. JS---DOM---元素相关的操作方法

    1. 追加子元素 my$("dv").appendChild(obj); 2. 把新的子元素插入到第一个子元素的前面 my$("dv").insertBefor ...

  8. 如何用web3部署智能合约

    合约示例 pragma solidity ^0.4.18; contract CallMeChallenge { bool public isComplete = false; function ca ...

  9. August 11th, 2019. Week 33rd, Sunday

    Worry does not empty tomorrow of its sorrow. It empties today of its strength. 忧虑不会消除明天的痛苦,它只会削弱今天的力 ...

  10. 基于django的个人博客网站建立(七)

    基于django的个人博客网站建立(七) 前言 网站效果可点击这里访问 这次在原来的基础上添加或修改一些小功能 具体内容 1.代码高亮 在原来的blog-details.html页面添加下面的代码: ...