diff --git a/src/main/java/com/weaver/seconddev/portal/action/entry/EmailAccountGenerateAction.java b/src/main/java/com/weaver/seconddev/portal/action/entry/EmailAccountGenerateAction.java new file mode 100644 index 0000000..240da77 --- /dev/null +++ b/src/main/java/com/weaver/seconddev/portal/action/entry/EmailAccountGenerateAction.java @@ -0,0 +1,89 @@ +package com.weaver.seconddev.portal.action.entry; + +import cn.hutool.core.convert.Convert; +import com.weaver.common.base.entity.result.WeaResult; +import com.weaver.esb.api.rpc.EsbServerlessRpcRemoteInterface; +import com.weaver.seconddev.portal.entity.param.BaseParam; +import com.weaver.seconddev.portal.mapper.action.entry.EmailAccountGenerateMapper; +import com.weaver.seconddev.portal.util.ChineseNameToPinyin; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author:dxfeng + * @createTime: 2025/07/28 + * @version: 1.0 + */ +@Slf4j +@Service("emailAccountGenerateAction") +public class EmailAccountGenerateAction implements EsbServerlessRpcRemoteInterface { + private static final String EMAIL_DOMAIN = "@chabaidao.com"; + + @Autowired + EmailAccountGenerateMapper emailAccountGenerateMapper; + + BaseParam baseParam = new BaseParam(); + + @Override + public WeaResult> execute(Map params) { + // 获取参数 + String xm = Convert.toStr(params.get("xm"), ""); + Long requestId = Convert.toLong(params.get("requestId")); + + if (StringUtils.isBlank(xm)) { + return WeaResult.fail("姓名不能为空", true); + } + + // 生成基础邮箱账号 + String pinyinName = ChineseNameToPinyin.convertChineseNameToPinyin(xm); + + // 检查并生成唯一邮箱账号 + String uniqueEmail = generateUniqueEmail(pinyinName, requestId); + + Map dataMap = new HashMap<>(); + // 将生成的邮箱账号放入返回结果中 + dataMap.put("email", uniqueEmail); + return WeaResult.success(dataMap); + } + + /** + * 生成唯一的邮箱账号 + * + * @param pinyinName 拼音姓名 + * @return 唯一邮箱账号 + */ + private String generateUniqueEmail(String pinyinName, Long requestId) { + String baseEmail = pinyinName + EMAIL_DOMAIN; + + // 首先检查基础邮箱是否已存在 + if (!isEmailExists(baseEmail,requestId)) { + return baseEmail; + } + + // 如果基础邮箱已存在,则尝试添加数字后缀 + int suffix = 1; + String emailWithSuffix; + do { + emailWithSuffix = pinyinName + suffix + EMAIL_DOMAIN; + suffix++; + } while (isEmailExists(emailWithSuffix, requestId)); + + return emailWithSuffix; + } + + /** + * 检查邮箱是否已存在 + * + * @param email 邮箱账号 + * @return 是否存在 + */ + private boolean isEmailExists(String email, Long requestId) { + Integer checkCount = emailAccountGenerateMapper.checkSameEmailAccount(baseParam, email, requestId); + return checkCount != null && checkCount > 0; + } +} diff --git a/src/main/java/com/weaver/seconddev/portal/mapper/action/entry/EmailAccountGenerateMapper.java b/src/main/java/com/weaver/seconddev/portal/mapper/action/entry/EmailAccountGenerateMapper.java new file mode 100644 index 0000000..ed03d26 --- /dev/null +++ b/src/main/java/com/weaver/seconddev/portal/mapper/action/entry/EmailAccountGenerateMapper.java @@ -0,0 +1,16 @@ +package com.weaver.seconddev.portal.mapper.action.entry; + +import com.weaver.seconddev.portal.entity.param.BaseParam; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * @author:dxfeng + * @createTime: 2025/07/28 + * @version: 1.0 + */ +@Mapper +public interface EmailAccountGenerateMapper { + + Integer checkSameEmailAccount(@Param("param") BaseParam param, @Param("email") String email, @Param("requestId") Long requestId); +} diff --git a/src/main/java/com/weaver/seconddev/portal/util/ChineseNameToPinyin.java b/src/main/java/com/weaver/seconddev/portal/util/ChineseNameToPinyin.java new file mode 100644 index 0000000..e38bb0a --- /dev/null +++ b/src/main/java/com/weaver/seconddev/portal/util/ChineseNameToPinyin.java @@ -0,0 +1,349 @@ +package com.weaver.seconddev.portal.util; + +import net.sourceforge.pinyin4j.PinyinHelper; +import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; +import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; +import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; +import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 中文姓名转拼音工具类 + * 支持多音字识别、复姓处理、姓名结构分析等 + */ +public class ChineseNameToPinyin { + + // 拼音输出格式 + private static final HanyuPinyinOutputFormat FORMAT = new HanyuPinyinOutputFormat(); + + // 姓氏多音字映射(姓氏位置读音) + private static final Map SURNAME_POLYPHONE_MAP = new HashMap<>(); + + // 非姓氏多音字映射(词中/词尾读音) + private static final Map NON_SURNAME_POLYPHONE_MAP = new HashMap<>(); + + // 常见复姓 + private static final Set COMPOUND_SURNAMES = new HashSet<>(); + + // 常见单姓 + private static final Set SINGLE_SURNAMES = new HashSet<>(); + + // 常用汉字读音修正表(用于处理常见错误拼音) + private static final Map COMMON_CORRECTION_MAP = new HashMap<>(); + + // 缓存已处理过的姓名,提高性能 + private static final Map NAME_CACHE = new ConcurrentHashMap<>(); + private static final int MAX_CACHE_SIZE = 10000; + + static { + FORMAT.setCaseType(HanyuPinyinCaseType.LOWERCASE); + FORMAT.setToneType(HanyuPinyinToneType.WITHOUT_TONE); + + // 初始化姓氏多音字映射 + initSurnamePolyphoneMap(); + + // 初始化非姓氏多音字映射 + initNonSurnamePolyphoneMap(); + + // 初始化复姓列表 + initCompoundSurnames(); + + // 初始化单姓列表 + initSingleSurnames(); + + // 初始化常用汉字读音修正表 + initCommonCorrectionMap(); + } + + /** + * 初始化姓氏多音字映射 + */ + private static void initSurnamePolyphoneMap() { + // 姓氏中的多音字(在姓氏位置的特殊读音) + SURNAME_POLYPHONE_MAP.put("区", "ou"); + SURNAME_POLYPHONE_MAP.put("仇", "qiu"); + SURNAME_POLYPHONE_MAP.put("单", "shan"); + SURNAME_POLYPHONE_MAP.put("解", "xie"); + SURNAME_POLYPHONE_MAP.put("华", "hua"); + SURNAME_POLYPHONE_MAP.put("朴", "piao"); + SURNAME_POLYPHONE_MAP.put("曾", "zeng"); + SURNAME_POLYPHONE_MAP.put("查", "zha"); + SURNAME_POLYPHONE_MAP.put("翟", "zhai"); + SURNAME_POLYPHONE_MAP.put("尉", "yu"); + SURNAME_POLYPHONE_MAP.put("盖", "ge"); + SURNAME_POLYPHONE_MAP.put("缪", "miao"); + SURNAME_POLYPHONE_MAP.put("覃", "qin"); + SURNAME_POLYPHONE_MAP.put("宓", "mi"); + SURNAME_POLYPHONE_MAP.put("折", "she"); + SURNAME_POLYPHONE_MAP.put("繁", "po"); + SURNAME_POLYPHONE_MAP.put("员", "yun"); + SURNAME_POLYPHONE_MAP.put("召", "shao"); + SURNAME_POLYPHONE_MAP.put("隗", "kui"); + SURNAME_POLYPHONE_MAP.put("覃", "qin"); + SURNAME_POLYPHONE_MAP.put("冼", "xian"); + SURNAME_POLYPHONE_MAP.put("覃", "tan"); + } + + /** + * 初始化非姓氏多音字映射 + */ + private static void initNonSurnamePolyphoneMap() { + // 非姓氏位置的多音字读音 + NON_SURNAME_POLYPHONE_MAP.put("区", "qu"); + NON_SURNAME_POLYPHONE_MAP.put("仇", "chou"); + NON_SURNAME_POLYPHONE_MAP.put("单", "dan"); + NON_SURNAME_POLYPHONE_MAP.put("解", "jie"); + NON_SURNAME_POLYPHONE_MAP.put("华", "hua"); + NON_SURNAME_POLYPHONE_MAP.put("朴", "pu"); + NON_SURNAME_POLYPHONE_MAP.put("曾", "ceng"); + NON_SURNAME_POLYPHONE_MAP.put("查", "cha"); + NON_SURNAME_POLYPHONE_MAP.put("翟", "di"); + NON_SURNAME_POLYPHONE_MAP.put("尉", "wei"); + NON_SURNAME_POLYPHONE_MAP.put("盖", "gai"); + NON_SURNAME_POLYPHONE_MAP.put("缪", "miu"); + NON_SURNAME_POLYPHONE_MAP.put("覃", "tan"); + NON_SURNAME_POLYPHONE_MAP.put("宓", "fu"); + NON_SURNAME_POLYPHONE_MAP.put("折", "zhe"); + NON_SURNAME_POLYPHONE_MAP.put("繁", "fan"); + NON_SURNAME_POLYPHONE_MAP.put("员", "yuan"); + NON_SURNAME_POLYPHONE_MAP.put("召", "zhao"); + NON_SURNAME_POLYPHONE_MAP.put("隗", "wei"); + NON_SURNAME_POLYPHONE_MAP.put("冼", "xiǎn"); + NON_SURNAME_POLYPHONE_MAP.put("覃", "qín"); + } + + /** + * 初始化复姓列表 + */ + private static void initCompoundSurnames() { + // 常见复姓(按长度排序,长的在前) + String[] compoundSurnames = { + "欧阳", "太史", "端木", "上官", "司马", "东方", "独孤", "南宫", "万俟", "闻人", + "夏侯", "诸葛", "尉迟", "公羊", "赫连", "澹台", "皇甫", "宗政", "濮阳", "公冶", + "太叔", "申屠", "公孙", "慕容", "仲孙", "钟离", "长孙", "宇文", "司徒", "鲜于", + "司空", "闾丘", "子车", "亓官", "司寇", "巫马", "公西", "颛孙", "壤驷", "公良", + "漆雕", "乐正", "宰父", "谷梁", "拓跋", "夹谷", "轩辕", "令狐", "段干", "百里", + "呼延", "东郭", "南门", "羊舌", "微生", "岳父", "缑亢", "况后", "有琴", "梁丘", + "左丘", "东门", "西门" + }; + COMPOUND_SURNAMES.addAll(Arrays.asList(compoundSurnames)); + } + + /** + * 初始化单姓列表 + */ + private static void initSingleSurnames() { + // 常见单姓(按频率排序) + String[] singleSurnames = { + "王", "李", "张", "刘", "陈", "杨", "赵", "黄", "周", "吴", "徐", "孙", "胡", "朱", + "高", "林", "何", "郭", "马", "罗", "梁", "宋", "郑", "谢", "韩", "唐", "冯", "于", + "董", "萧", "程", "曹", "袁", "邓", "许", "傅", "沉", "曾", "彭", "吕", "苏", "卢", + "蒋", "蔡", "贾", "丁", "魏", "薛", "叶", "阎", "余", "潘", "杜", "戴", "夏", "钟", + "汪", "田", "任", "姜", "范", "方", "石", "姚", "谭", "廖", "邹", "熊", "金", "陆", + "郝", "孔", "白", "崔", "康", "毛", "邱", "秦", "江", "尹", "薛", "闫", "段", "雷", + "侯", "龙", "史", "陶", "黎", "贺", "顾", "孟", "黄", "万", "段", "雷", "钱", "汤", + "尹", "黎", "易", "常", "武", "乔", "贺", "赖", "龚", "文" + }; + SINGLE_SURNAMES.addAll(Arrays.asList(singleSurnames)); + } + + /** + * 初始化常用汉字读音修正表 + */ + private static void initCommonCorrectionMap() { + // 常见汉字读音修正 + COMMON_CORRECTION_MAP.put("茜", "qian"); // 作为名字时通常读作qian + COMMON_CORRECTION_MAP.put("芃", "peng"); // 人名常用字 + COMMON_CORRECTION_MAP.put("喆", "zhe"); // 人名常用字 + } + + /** + * 将中文姓名转换为拼音(不带声调) + * + * @param chineseName 中文姓名 + * @return 拼音全称 + */ + public static String convertChineseNameToPinyin(String chineseName) { + if (chineseName == null || chineseName.isEmpty()) { + return ""; + } + + // 先从缓存中查找 + String cachedResult = NAME_CACHE.get(chineseName); + if (cachedResult != null) { + return cachedResult; + } + + String result = doConvertChineseNameToPinyin(chineseName); + + // 缓存结果(控制缓存大小) + if (NAME_CACHE.size() < MAX_CACHE_SIZE) { + NAME_CACHE.put(chineseName, result); + } + + return result; + } + + /** + * 实际转换逻辑 + */ + private static String doConvertChineseNameToPinyin(String chineseName) { + StringBuilder pinyinBuilder = new StringBuilder(); + int i = 0; + + // 首先尝试匹配复姓 + boolean surnameProcessed = false; + if (chineseName.length() >= 2) { + // 优先匹配3字复姓 + if (chineseName.length() >= 3) { + String threeCharSurname = chineseName.substring(0, 3); + if (COMPOUND_SURNAMES.contains(threeCharSurname)) { + processSurname(pinyinBuilder, threeCharSurname); + i = 3; + surnameProcessed = true; + } + } + + // 如果不是3字复姓,再匹配2字复姓 + if (!surnameProcessed) { + String twoCharSurname = chineseName.substring(0, 2); + if (COMPOUND_SURNAMES.contains(twoCharSurname)) { + processSurname(pinyinBuilder, twoCharSurname); + i = 2; + surnameProcessed = true; + } + } + } + + // 如果不是复姓,处理单姓 + if (!surnameProcessed && chineseName.length() >= 1) { + String firstChar = String.valueOf(chineseName.charAt(0)); + if (SINGLE_SURNAMES.contains(firstChar) || SURNAME_POLYPHONE_MAP.containsKey(firstChar)) { + processSurname(pinyinBuilder, firstChar); + i = 1; + } + } + + // 处理剩余部分(名字) + while (i < chineseName.length()) { + char ch = chineseName.charAt(i); + processNonSurname(pinyinBuilder, ch, i, chineseName); + i++; + } + + return pinyinBuilder.toString(); + } + + /** + * 处理姓氏部分 + */ + private static void processSurname(StringBuilder pinyinBuilder, String surname) { + for (int j = 0; j < surname.length(); j++) { + char ch = surname.charAt(j); + String pinyin = getPinyinWithPolyphone(ch, true, j, surname); + pinyinBuilder.append(pinyin); + } + } + + /** + * 处理非姓氏部分 + */ + private static void processNonSurname(StringBuilder pinyinBuilder, char ch, int position, String fullName) { + // 处理空格等非中文字符 + if (ch == ' ') { + pinyinBuilder.append(" "); + return; + } + + // 处理英文字母 + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { + pinyinBuilder.append(Character.toLowerCase(ch)); + return; + } + + // 处理数字 + if (ch >= '0' && ch <= '9') { + pinyinBuilder.append(ch); + return; + } + + // 处理中文字符 + String pinyin = getPinyinWithPolyphone(ch, false, position, fullName); + pinyinBuilder.append(pinyin); + } + + /** + * 获取字符的拼音(考虑多音字) + */ + private static String getPinyinWithPolyphone(char ch, boolean isSurname, int position, String context) { + try { + // 首先检查常用修正表 + String correction = COMMON_CORRECTION_MAP.get(String.valueOf(ch)); + if (correction != null) { + return correction; + } + + // 检查多音字映射 + if (isSurname) { + String surnamePinyin = SURNAME_POLYPHONE_MAP.get(String.valueOf(ch)); + if (surnamePinyin != null) { + return surnamePinyin; + } + } else { + String nonSurnamePinyin = NON_SURNAME_POLYPHONE_MAP.get(String.valueOf(ch)); + if (nonSurnamePinyin != null) { + return nonSurnamePinyin; + } + } + + // 使用pinyin4j获取拼音 + String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, FORMAT); + if (pinyinArray != null && pinyinArray.length > 0) { + return pinyinArray[0]; // 默认返回第一个拼音 + } else { + return String.valueOf(ch); // 非中文字符返回原字符 + } + } catch (BadHanyuPinyinOutputFormatCombination e) { + return String.valueOf(ch); // 异常情况下返回原字符 + } + } + + /** + * 带分隔符的拼音转换 + * + * @param chineseName 中文姓名 + * @param separator 分隔符 + * @return 带分隔符的拼音 + */ + public static String convertChineseNameToPinyinWithSeparator(String chineseName, String separator) { + String pinyin = convertChineseNameToPinyin(chineseName); + if (pinyin.isEmpty()) { + return ""; + } + + StringBuilder result = new StringBuilder(); + for (int i = 0; i < pinyin.length(); i++) { + if (i > 0) { + result.append(separator); + } + result.append(pinyin.charAt(i)); + } + return result.toString(); + } + + /** + * 清空缓存 + */ + public static void clearCache() { + NAME_CACHE.clear(); + } + + /** + * 获取缓存大小 + */ + public static int getCacheSize() { + return NAME_CACHE.size(); + } +} diff --git a/src/main/resources/mapper/action/entry/EmailAccountGenerateMapper.xml b/src/main/resources/mapper/action/entry/EmailAccountGenerateMapper.xml new file mode 100644 index 0000000..e995e0f --- /dev/null +++ b/src/main/resources/mapper/action/entry/EmailAccountGenerateMapper.xml @@ -0,0 +1,14 @@ + + + + + +