ReportParseUtils.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. package com.simuwang.daq.components;
  2. import cn.hutool.core.collection.ListUtil;
  3. import cn.hutool.core.map.MapUtil;
  4. import cn.hutool.core.util.StrUtil;
  5. import java.util.Calendar;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Objects;
  9. import java.util.regex.Matcher;
  10. import java.util.regex.Pattern;
  11. import java.util.stream.Collectors;
  12. public final class ReportParseUtils {
  13. /**
  14. * 行业配置的表格列名称
  15. */
  16. public static final List<String> INDUSTRY_COLUMN_NAMES = ListUtil.list(false);
  17. /**
  18. * 份额变动的表格列名称
  19. */
  20. public static final List<String> SHARE_CHANGE_COLUMN_NAMES = ListUtil.list(false);
  21. /**
  22. * 主要财务指标识别列名称
  23. */
  24. public static final List<String> FINANCIAL_INDICATORS_COLUMN_NAMES = ListUtil.list(false);
  25. /**
  26. * 资产配置明细和大类关系映射
  27. */
  28. public static final Map<String, String> ASSET_ALLOCATION_TYPE_MAPPER = MapUtil.newHashMap(32, true);
  29. static {
  30. // 财务指标
  31. FINANCIAL_INDICATORS_COLUMN_NAMES.add("期末基金净资产");
  32. FINANCIAL_INDICATORS_COLUMN_NAMES.add("报告期期末单位净值");
  33. FINANCIAL_INDICATORS_COLUMN_NAMES.add("本期利润");
  34. FINANCIAL_INDICATORS_COLUMN_NAMES.add("本期已实现收益");
  35. FINANCIAL_INDICATORS_COLUMN_NAMES.add("期末可供分配利润");
  36. FINANCIAL_INDICATORS_COLUMN_NAMES.add("期末可供分配基金份额利润");
  37. FINANCIAL_INDICATORS_COLUMN_NAMES.add("基金份额累计净值增长率");
  38. // 中国证监会行业标准
  39. INDUSTRY_COLUMN_NAMES.add("农、林、牧、渔业");
  40. INDUSTRY_COLUMN_NAMES.add("采矿业");
  41. INDUSTRY_COLUMN_NAMES.add("制造业");
  42. INDUSTRY_COLUMN_NAMES.add("电力、热力、燃气及水生产和供应业");
  43. INDUSTRY_COLUMN_NAMES.add("建筑业");
  44. INDUSTRY_COLUMN_NAMES.add("批发和零售业");
  45. INDUSTRY_COLUMN_NAMES.add("交通运输、仓储和邮政业");
  46. INDUSTRY_COLUMN_NAMES.add("住宿和餐饮业");
  47. INDUSTRY_COLUMN_NAMES.add("信息传输、软件和信息技术服务业");
  48. INDUSTRY_COLUMN_NAMES.add("金融业");
  49. INDUSTRY_COLUMN_NAMES.add("房地产业");
  50. INDUSTRY_COLUMN_NAMES.add("租赁和商务服务业");
  51. INDUSTRY_COLUMN_NAMES.add("科学研究和技术服务业");
  52. INDUSTRY_COLUMN_NAMES.add("水利、环境和公共设施管理业");
  53. INDUSTRY_COLUMN_NAMES.add("居民服务、修理和其他服务业");
  54. INDUSTRY_COLUMN_NAMES.add("教育");
  55. INDUSTRY_COLUMN_NAMES.add("卫生和社会工作");
  56. INDUSTRY_COLUMN_NAMES.add("文化、体育和娱乐业");
  57. INDUSTRY_COLUMN_NAMES.add("综合");
  58. INDUSTRY_COLUMN_NAMES.add("港股通");
  59. // 以下为国际标准
  60. INDUSTRY_COLUMN_NAMES.add("能源");
  61. INDUSTRY_COLUMN_NAMES.add("原材料");
  62. INDUSTRY_COLUMN_NAMES.add("工业");
  63. INDUSTRY_COLUMN_NAMES.add("非日常生活消费品");
  64. INDUSTRY_COLUMN_NAMES.add("日常消费品");
  65. INDUSTRY_COLUMN_NAMES.add("医疗保健");
  66. INDUSTRY_COLUMN_NAMES.add("金融");
  67. INDUSTRY_COLUMN_NAMES.add("信息技术");
  68. INDUSTRY_COLUMN_NAMES.add("通讯服务");
  69. INDUSTRY_COLUMN_NAMES.add("公用事业");
  70. INDUSTRY_COLUMN_NAMES.add("房地产");
  71. // 份额变动表格识别列
  72. SHARE_CHANGE_COLUMN_NAMES.add("报告期期初基金份额总额");
  73. SHARE_CHANGE_COLUMN_NAMES.add("减:报告期期间基金总赎回份额");
  74. SHARE_CHANGE_COLUMN_NAMES.add("期末基金总份额/期末基金实缴总额");
  75. SHARE_CHANGE_COLUMN_NAMES.add("报告期期间基金拆分变动份额");
  76. SHARE_CHANGE_COLUMN_NAMES.add("报告期期间基金总申购份额");
  77. // 资产配置
  78. ASSET_ALLOCATION_TYPE_MAPPER.put("银行存款", "现金类资产");
  79. // 境内未上市、未挂牌公司股权投资
  80. ASSET_ALLOCATION_TYPE_MAPPER.put("股权投资", "境内未上市、未挂牌公司股权投资");
  81. ASSET_ALLOCATION_TYPE_MAPPER.put("其中:优先股", "境内未上市、未挂牌公司股权投资");
  82. ASSET_ALLOCATION_TYPE_MAPPER.put("其他股权类投资", "境内未上市、未挂牌公司股权投资");
  83. // 上市公司定向增发投资
  84. ASSET_ALLOCATION_TYPE_MAPPER.put("上市公司定向增发投资", "上市公司定向增发投资");
  85. // 新三板投资
  86. ASSET_ALLOCATION_TYPE_MAPPER.put("新三板挂牌企业投资", "新三板投资");
  87. // 境内证券投资规模
  88. ASSET_ALLOCATION_TYPE_MAPPER.put("结算备付金", "境内证券投资规模");
  89. ASSET_ALLOCATION_TYPE_MAPPER.put("存出保证金", "境内证券投资规模");
  90. ASSET_ALLOCATION_TYPE_MAPPER.put("股票投资", "境内证券投资规模");
  91. ASSET_ALLOCATION_TYPE_MAPPER.put("债券投资", "境内证券投资规模");
  92. ASSET_ALLOCATION_TYPE_MAPPER.put("其中:银行间市场债券", "境内证券投资规模");
  93. ASSET_ALLOCATION_TYPE_MAPPER.put("其中:利率债", "境内证券投资规模");
  94. ASSET_ALLOCATION_TYPE_MAPPER.put("其中:信用债", "境内证券投资规模");
  95. ASSET_ALLOCATION_TYPE_MAPPER.put("资产支持证券", "境内证券投资规模");
  96. ASSET_ALLOCATION_TYPE_MAPPER.put("基金投资(公募基金)", "境内证券投资规模");
  97. ASSET_ALLOCATION_TYPE_MAPPER.put("其中:货币基金", "境内证券投资规模");
  98. ASSET_ALLOCATION_TYPE_MAPPER.put("期货及衍生品交易保证金", "境内证券投资规模");
  99. ASSET_ALLOCATION_TYPE_MAPPER.put("买入返售金融资产", "境内证券投资规模");
  100. ASSET_ALLOCATION_TYPE_MAPPER.put("其他证券类标的", "境内证券投资规模");
  101. // 资管计划投资
  102. ASSET_ALLOCATION_TYPE_MAPPER.put("商业银行理财产品投资", "资管计划投资");
  103. ASSET_ALLOCATION_TYPE_MAPPER.put("信托计划投资", "资管计划投资");
  104. ASSET_ALLOCATION_TYPE_MAPPER.put("基金公司及其子公司资产管理计划投资", "资管计划投资");
  105. ASSET_ALLOCATION_TYPE_MAPPER.put("保险资产管理计划投资", "资管计划投资");
  106. ASSET_ALLOCATION_TYPE_MAPPER.put("证券公司及其子公司资产管理计划投资", "资管计划投资");
  107. ASSET_ALLOCATION_TYPE_MAPPER.put("期货公司及其子公司资产管理计划投资", "资管计划投资");
  108. ASSET_ALLOCATION_TYPE_MAPPER.put("私募基金产品投资", "资管计划投资");
  109. ASSET_ALLOCATION_TYPE_MAPPER.put("未在协会备案的合伙企业份额", "资管计划投资");
  110. // 另类投资
  111. ASSET_ALLOCATION_TYPE_MAPPER.put("另类投资", "另类投资");
  112. // 境内债权类投资
  113. ASSET_ALLOCATION_TYPE_MAPPER.put("银行委托贷款规模", "境内债权类投资");
  114. ASSET_ALLOCATION_TYPE_MAPPER.put("信托贷款", "境内债权类投资");
  115. ASSET_ALLOCATION_TYPE_MAPPER.put("应收账款投资", "境内债权类投资");
  116. ASSET_ALLOCATION_TYPE_MAPPER.put("各类受(收)益权投资", "境内债权类投资");
  117. ASSET_ALLOCATION_TYPE_MAPPER.put("票据(承兑汇票等)投资", "境内债权类投资");
  118. ASSET_ALLOCATION_TYPE_MAPPER.put("其他债权投资", "境内债权类投资");
  119. // 境外投资
  120. ASSET_ALLOCATION_TYPE_MAPPER.put("境外投资", "境外投资");
  121. // 其他资产
  122. ASSET_ALLOCATION_TYPE_MAPPER.put("其他资产", "其他资产");
  123. // 基金负债情况
  124. ASSET_ALLOCATION_TYPE_MAPPER.put("债券回购总额", "基金负债情况");
  125. ASSET_ALLOCATION_TYPE_MAPPER.put("融资、融券总额", "基金负债情况");
  126. ASSET_ALLOCATION_TYPE_MAPPER.put("其中:融券总额", "基金负债情况");
  127. ASSET_ALLOCATION_TYPE_MAPPER.put("银行借款总额", "基金负债情况");
  128. ASSET_ALLOCATION_TYPE_MAPPER.put("其他融资总额", "基金负债情况");
  129. }
  130. /**
  131. * 数据清洗,替换圆括号,包含中文或英文的圆括号
  132. *
  133. * @param value /
  134. * @return /
  135. */
  136. public static String cleaningValue(Object value) {
  137. return cleaningValue(value, true);
  138. }
  139. /**
  140. * 数据简单清洗,并全部转为字符串类型
  141. *
  142. * @param value 待清洗的数据
  143. * @param replaceParentheses 是否替换圆括号
  144. * @return /
  145. */
  146. public static String cleaningValue(Object value, boolean replaceParentheses) {
  147. String fieldValue = StrUtil.toStringOrNull(value);
  148. if (!StrUtil.isNullOrUndefined(fieldValue)) {
  149. // 特殊字符替换,空格替换为空字符
  150. fieldValue = fieldValue
  151. .replace("\r", StrUtil.EMPTY)
  152. .replace(";", ";")
  153. .replaceAll(" ", StrUtil.EMPTY);
  154. if (replaceParentheses) {
  155. // 正则表达式匹配中文括号及其内容,并替换为空字符串
  156. fieldValue = Pattern.compile("[(|(][^)]*[)|)]").matcher(fieldValue).replaceAll(StrUtil.EMPTY);
  157. }
  158. }
  159. // 如果仅有 “-” 该字段值为null
  160. if (Objects.equals("-", fieldValue)) {
  161. fieldValue = null;
  162. }
  163. return StrUtil.isBlank(fieldValue) ? null : fieldValue;
  164. }
  165. /**
  166. * 匹配分级基金名称(并且把母基金追加到第一行)
  167. *
  168. * @param text 文本内容
  169. * @return /
  170. */
  171. public static List<String> matchTieredFund(String text) {
  172. List<String> matches = ListUtil.list(false);
  173. if (StrUtil.isBlank(text)) {
  174. return matches;
  175. }
  176. // 使用正则表达式查找匹配项
  177. Pattern pattern = Pattern.compile("[A-F]级|基金[A-F]");
  178. Matcher matcher = pattern.matcher(text);
  179. // 收集所有匹配项
  180. while (matcher.find()) {
  181. matches.add(matcher.group());
  182. }
  183. // 提取字母并按字母顺序排序
  184. List<String> levels = matches.stream()
  185. .map(s -> s.replaceAll("[^A-F]", ""))
  186. .distinct()
  187. .sorted()
  188. .map(letter -> letter + "级")
  189. .collect(Collectors.toList());
  190. levels.add(0, "母基金");
  191. return levels;
  192. }
  193. /**
  194. * 匹配报告日期
  195. *
  196. * @param string 文本内容
  197. * @return 报告日期
  198. */
  199. public static String matchReportDate(String string) {
  200. if (string == null) {
  201. return null;
  202. }
  203. // 编译正则表达式模式
  204. Pattern pat1 = Pattern.compile("(2\\d{3}).*([一二三四1234])季度"); // 2023年XXX3季度
  205. Pattern pat2 = Pattern.compile("\\d{4}-\\d{2}-\\d{2}"); // 2023-12-31
  206. Pattern pat3 = Pattern.compile("(2\\d{3})年年度"); // 2023年年度
  207. Pattern pat4 = Pattern.compile("(\\d{4})年(\\d{1,2})月"); // 2023年12月
  208. Pattern pat5 = Pattern.compile("\\d{4}\\d{2}\\d{2}"); // 20231231
  209. Pattern pat6 = Pattern.compile("(2\\d{3})年度"); // 2023年度
  210. // 创建Matcher对象
  211. Matcher matcher1 = pat1.matcher(string);
  212. Matcher matcher2 = pat2.matcher(string);
  213. Matcher matcher3 = pat3.matcher(string);
  214. Matcher matcher4 = pat4.matcher(string);
  215. Matcher matcher5 = pat5.matcher(string);
  216. Matcher matcher6 = pat6.matcher(string);
  217. // 尝试匹配
  218. if (matcher1.find()) {
  219. String year = matcher1.group(1);
  220. String quarter = matcher1.group(2);
  221. return switch (quarter) {
  222. case "一", "1" -> year + "-03-31";
  223. case "二", "2" -> year + "-06-30";
  224. case "三", "3" -> year + "-09-30";
  225. case "四", "4" -> year + "-12-31";
  226. default -> null;
  227. };
  228. } else if (matcher2.find()) {
  229. return matcher2.group();
  230. } else if (matcher5.find()) {
  231. return matcher5.group();
  232. } else if (matcher3.find()) {
  233. return matcher3.group(1) + "-12-31";
  234. } else if (matcher6.find()) {
  235. return matcher6.group(1) + "-12-31";
  236. } else if (matcher4.find()) {
  237. String year = matcher4.group(1);
  238. String month = matcher4.group(2);
  239. int lastDayOfMonth = getLastDayOfMonth(Integer.parseInt(year), Integer.parseInt(month));
  240. return year + "-" + padZero(month) + "-" + padZero(lastDayOfMonth + "");
  241. } else {
  242. return null;
  243. }
  244. }
  245. /**
  246. * 匹配报告类型,如“季度”、“年度”
  247. *
  248. * @param string 输入字符串
  249. * @return 匹配到的报告类型子字符串,如果没有匹配到则返回null
  250. */
  251. public static String matchReportType(String string) {
  252. if (string == null) {
  253. return null;
  254. }
  255. // 所有报告的正则识别方式
  256. String patterns = "年度|年报|季度|季报|季|月度|月报|月|年";
  257. // 编译正则表达式模式
  258. Pattern pattern = Pattern.compile(patterns);
  259. // 创建Matcher对象
  260. Matcher matcher = pattern.matcher(string);
  261. // 尝试匹配
  262. if (matcher.find()) {
  263. return matcher.group();
  264. } else {
  265. return null;
  266. }
  267. }
  268. private static int getLastDayOfMonth(int year, int month) {
  269. Calendar calendar = Calendar.getInstance();
  270. calendar.set(Calendar.YEAR, year);
  271. calendar.set(Calendar.MONTH, month - 1); // Calendar.MONTH 是从0开始的
  272. return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
  273. }
  274. private static String padZero(String number) {
  275. return String.format("%02d", Integer.parseInt(number));
  276. }
  277. }