• “强大”的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("
    ");
            }
            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));
        }
    
    }
  • 相关阅读:
    [转]Release版程序调试排错技巧
    关于获得MFC窗口其它类指针的方法(CSDN)
    MFC .DLL指南(二)
    对“仅通过崩溃地址找出源代码的出错行”一文的补充与改进,转自vckbase,记录一下
    [转]调试Release版本应用程序,不知道转自哪里
    MFC的DLL 概述
    从今天开始每天写C++或其他学习的知识的笔记,以激励自己
    近日发现vs2005安装的一个问题
    VC中处理C1010错误的两种方法
    [转]VC调试篇不知道转自何处
  • 原文地址:https://www.cnblogs.com/buguge/p/11157806.html
Copyright © 2020-2023  润新知