
核心分析点一期需求分析议题评审 ● 背景梳理清楚 ● 有没有人在做 ● 定时任务对应代码逻辑是否有对其他的检查是否要把代码下掉需求核心1、背景因为消息发送太多为减少干扰当系统检查到TMC中的外汇对冲交易和期权交易缺少BG / Product / Region code三个字段时停止在“财经云-我的消息”和“WeLink-财经One应用号”推送提醒消息。我的消息应用号消息样式1、检查TMC中所有外汇对冲交易是否按要求补充上BG/Product/Region code2、检查TMC中所有期权交易是否按要求补充上BG/Product/Region code涉及范围●交易类型外汇对冲交易、期权交易。●校验字段BG code、Product code、Region code。●触达渠道财经云站内信我的消息、WeLink 应用号财经One。收益提升用户体验、减少干扰并聚焦关键信息。现状25年7月这两个任务已被蒋婷停止现在负责这项工作的人李光银实现方案该提醒由两个定时任务触发可直接停用LTS定时任务。LTS平台项目名TMC任务组名SIT_DEALjob名称 BgProReCodeAlarmTimeScheduler、CoDealBgProReCodeTimeSchedulersit环境、perf环境、B7环境在25年6、7月已被蒋婷停掉了这三个环境的提示信息已经不会提示了只有tmc和A1环境还在运行达到的效果●定时任务停止前三个字段为空 → 触发定时任务并发送 2 条催办消息通知。●定时任务下掉后BG code、Product code、Region code三个字段为空时 →不再发送任何站内信和应用号通知。这两个定时任务调用的代码所在模块dealLTS平台对应的配置信息BgProReCodeAlarmTimeScheduler● 对应beannameBgProReCodeAlarmTimeScheduler● 对应URL${tmcLtsUrlSit}/fin/tmc/deal/publicservices/timertask/lts/task?beanNamebgProReCodeAlarmTimeSchedulerisAsync1CoDealBgProReCodeTimeScheduler● 对应Beanname CoDealBgProReCodeTimeScheduler● 对应URL${tmcLtsUrlSit}/fin/tmc/deal/publicservices/timertask/lts/task?beanNamecoDealBgProReCodeTimeSchedulerisAsync1代码逻辑及影响分析类名BgProReCodeAlarmTimeScheduler类名CoDealBgProReCodeTimeScheduler两个类均实现 ITimeScheduler 接口来自 com.huawei.it.tmc.common.lighttasklock.interfaces.facade该接口定义了定时任务的标准契约// 文件: src/main/java/com/huawei/it/tmc/tmcdeal/application/service/lts/BgProReCodeAlarmTimeScheduler.java:50 public class BgProReCodeAlarmTimeScheduler implements ITimeScheduler, IBGDataHelper { // 文件: src/main/java/com/huawei/it/tmc/tmcdeal/application/service/lts/CoDealBgProReCodeTimeScheduler.java:38 public class CoDealBgProReCodeTimeScheduler implements ITimeScheduler {触发方式为实现executeOnTime(MapString, String map)方法由调度框架在指定时间调用。两个类使用Named注解注入为Spring Bean在tmc.deal.services.xml中配置为JAX-RS服务Bean!-- 文件: src/main/resources/config/tmc.deal.services.xml:27 -- ref beanbgProReCodeAlarmTimeScheduler/二、调度执行流程2.1 BgProReCodeAlarmTimeScheduler 执行流程┌─────────────────────────────────────────────────────────────────┐ │ bgProReCodeAlarmTimeScheduler │ │ executeOnTime() │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 1. getEtrConfig() │ │ 调用restClientService.doSecurityGet() │ │ 从Treasury系统获取BG/Product/Region级联配置 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────┐ │ 配置为空检查 │ └────────┬────────┘ │ ┌──────────────┴──────────────┐ │ 是 │ 否 ▼ ▼ ┌───────────────┐ ┌─────────────────────────────┐ │ 打印日志直接 │ │ 2. findDealForAlarm(alarmPeriod) │ │ 返回 │ │ 从数据库查询最近N天的FX交易 │ └───────────────┘ └─────────────┬───────────────┘ │ ┌─────────────────┴─────────────────┐ │ 交易列表为空检查 │ └───────────────┬───────────────────┘ │ ┌───────────────┴───────────────┐ │ 是 │ 否 ▼ ▼ ┌───────────────┐ ┌─────────────────────────┐ │ 打印日志直接 │ │ 3. secondFilter() │ │ 返回 │ │ 二次筛选 │ └───────────────┘ │ - 比较币种是否在配置中 │ │ - 校验BG/Product/Region │ └─────────────┬─────────────┘ │ ┌───────────────┴───────────────┐ │ alarmDealSet.size() 0 │ └───────────────┬───────────────┘ │ ┌───────────────┴───────────────┐ │ 是 │ 否 ▼ ▼ ┌───────────────┐ ┌─────────────────────────┐ │ 打印日志直接 │ │ 4. changeLine() │ │ 返回 │ │ 构建消息内容和超链接 │ └───────────────┘ └─────────────┬─────────────┘ │ ▼ ┌─────────────────────────┐ │ 5. executeOnTimeSplit() │ │ messageNoticeService │ │ .sendAlarmEsMsg() │ └─────────────────────────┘┌──────────────────────────────────────────────────────────────────────────────────────┐ │ bgProReCodeAlarmTimeScheduler.executeOnTime() │ ├──────────────────────────────────────────────────────────────────────────────────────┤ │ 输入参数: MapString, String map │ │ 依赖注入: IRestClientService, IFxDealDomainService, IMessageNoticeService │ │ 配置属性: fxHedgeCurrencyMapArray (from url_treasury) │ └──────────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────────────────────────────┐ │ 【步骤1】获取ET级联配置: getEtrConfig() │ │ ────────────────────────────────────────────────────────────────────────────────── │ │ 代码位置: BgProReCodeAlarmTimeScheduler.java:221-248 │ │ │ │ 1.1 检查配置URL是否为空 │ │ if (StringUtil.isNullOrEmpty(fxHedgeCurrencyMapArray)) │ │ → LOGGER.error(source Lookup url has not been configured...) │ │ → return null │ │ │ │ 1.2 调用Treasury系统API │ │ RestResponse restResponse restClientService.doSecurityGet( │ │ fxHedgeCurrencyMapArray, null) │ │ │ │ 1.3 JSON解析响应内容 │ │ JSONObject jsonObject JSON.parseObject(restResponse.getResponseText()) │ │ ListETreasuryBusinessDtlDto eTreasuryBusinessDts │ │ JSONArray.parseArray(jsonObject.getString(dltList), │ │ ETreasuryBusinessDtlDto.class) │ │ │ │ 1.4 实体转换 │ │ for (ETreasuryBusinessDtlDto eTreasuryBusinessDt : eTreasuryBusinessDts) { │ │ HedgeCurrencyEntity entity new HedgeCurrencyEntity(eTreasuryBusinessDt); │ │ list.add(entity); │ │ } │ │ │ │ 1.5 过滤无效数据 │ │ list list.stream() │ │ .filter(fxDealModel - fxDealModel.getStatus().equals(1)) │ │ .collect(Collectors.toList()) │ │ │ │ 输出: ListHedgeCurrencyEntity etLookupList │ └──────────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────────────────────────────┐ │ 【条件判断1】etLookupList null || etLookupList.size() 0 │ │ ────────────────────────────────────────────────────────────────────────────────── │ │ │ │ │ │ 是 否 │ │ ▼ ▼ │ │ ┌────────────────────────┐ ┌────────────────────────────────┐ │ │ │ LOGGER.info(There is │ │ 【步骤2】获取FX交易列表 │ │ │ │ no data from source │ │ findDealForAlarm(alarmPeriod) │ │ │ │ config) │ │ ──────────────────────────── │ │ │ │ return (方法结束) │ │ 代码位置: FxDealDomainService │ │ │ └────────────────────────┘ │ .java:625-627 │ │ │ │ │ │ │ │ alarmPeriod Integer.parseInt │ │ │ │ ApplicationConfigProperties │ │ │ │ .getContextProperty( │ │ │ │ Tmc.Deal.BgProReCodeAlarmPeriod,│ │ │ │ 7) │ │ │ │ │ │ │ │ ListFxDealEntity │ │ │ │ fxDealEntities │ │ │ │ fxDealDomainService │ │ │ │ .findDealForAlarm(alarmPeriod)│ │ │ │ │ │ │ │ SQL: 查询最近alarmPeriod天内的 │ │ │ │ 所有FX交易记录 │ │ │ │ │ │ │ │ 输出: ListFxDealEntity │ │ │ │ fxDealEntities │ │ │ └────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────────────────────────────┐ │ 【条件判断2】fxDealEntities null || fxDealEntities.size() 0 │ │ ────────────────────────────────────────────────────────────────────────────────── │ │ │ │ │ │ 是 否 │ │ ▼ ▼ │ │ ┌────────────────────────┐ ┌────────────────────────────────┐ │ │ │ LOGGER.info(There is │ │ 【步骤3】二次筛选: secondFilter │ │ │ │ no conforming fx deal)│ │ ──────────────────────────── │ │ │ │ return (方法结束) │ │ 代码位置: BgProReCodeAlarm │ │ │ └────────────────────────┘ │ TimeScheduler.java:113-158 │ │ │ │ │ │ │ │ 输入: │ │ │ │ - ListFxDealEntity │ │ │ │ fxDealEntities │ │ │ │ - ListHedgeCurrencyEntity │ │ │ │ etLookupList │ │ │ │ - SetLong alarmDealSet │ │ │ │ │ │ │ │ 处理流程: │ │ │ └────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────────────────────────────┐ │ 【步骤3详情】secondFilter() 完整流程 │ │ ────────────────────────────────────────────────────────────────────────────────── │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ FOR循环: for (int index 0; index fxDealEntities.size(); index) │ │ │ │ ───────────────────────────────────────────────────────────────────────── │ │ │ │ │ │ │ │ 【3.1】实体转换 │ │ │ │ FxDealEntity entity fxDealEntities.get(index) │ │ │ │ FxDealDTO fxDealDTO FxDealAssembler.convertToDto(entity) │ │ │ │ │ │ │ │ 【3.2】币种过滤 - 买侧 │ │ │ │ ListHedgeCurrencyEntity buyCcyList etLookupList.stream() │ │ │ │ .filter(etLookup - etLookup.getEtCurrencyCode() │ │ │ │ .equals(fxDealDTO.getCurrencyCode())) │ │ │ │ .collect(Collectors.toList()) │ │ │ │ │ │ │ │ 【3.3】币种过滤 - 卖侧 │ │ │ │ ListHedgeCurrencyEntity sellCcyList etLookupList.stream() │ │ │ │ .filter(etLookup - etLookup.getEtCurrencyCode() │ │ │ │ .equals(fxDealDTO.getCurrencyCode2())) │ │ │ │ .collect(Collectors.toList()) │ │ │ │ │ │ │ │ 【条件判断3】buyCcyList和sellCcyList都为空 │ │ │ │ (null buyCcyList || buyCcyList.size() 0) │ │ │ │ (null sellCcyList || sellCcyList.size() 0) │ │ │ │ │ │ │ │ ├──────────────────────────── 是 ─────────────────────────────────────────│ │ │ │ │ │ │ │ │ │ 【条件判断3.1】BG/Product/Region任一不为空 │ │ │ │ │ !StringUtil.isNullOrEmpty(fxDealDTO.getBgCode()) || │ │ │ │ │ !StringUtil.isNullOrEmpty(fxDealDTO.getCoaGeoPcCode()) || │ │ │ │ │ !StringUtil.isNullOrEmpty(fxDealDTO.getCoaProductCode()) │ │ │ │ │ │ │ │ │ │ ├────────── 是 → alarmDealSet.add(dealNumber), continue │ │ │ │ │ └────────── 否 → continue (不预警) │ │ │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────────────────│ │ │ │ │ │ │ │ └──────────────────────────── 否 ─────────────────────────────────────────│ │ │ │ │ │ │ │ 【3.4】BG代码验证 │ │ │ │ ListLookupItemVO bgCodes itemVOFilterData(null, fxDealDTO.getBgCode()) │ │ │ │ │ │ │ │ 【条件判断4】BG代码无效 │ │ │ │ null fxDealDTO.getBgCode() || null bgCodes || bgCodes.size() 0 │ │ │ │ │ │ │ │ ├────────── 是 → alarmDealSet.add(dealNumber), continue │ │ │ │ └────────── 否 → fxDealDTO.setBgCode(bgCodes.get(0).getItemName()) │ │ │ │ │ │ │ │ 【3.5】合并满足条件的配置列表 │ │ │ │ if (null ! buyCcyList buyCcyList.size() ! 0) │ │ │ │ filterList.addAll(buyCcyList) │ │ │ │ if (null ! sellCcyList sellCcyList.size() ! 0) │ │ │ │ filterList.addAll(sellCcyList) │ │ │ │ │ │ │ │ 【3.6】BG/Product/Region组合匹配验证 │ │ │ │ ListHedgeCurrencyEntity filterResult filterList.stream() │ │ │ │ .filter(etLookup - │ │ │ │ etLookup.getBgCode().equals(fxDealDTO.getBgCode()) │ │ │ │ etLookup.getRegionCode().equals(fxDealDTO.getCoaGeoPcCode()) │ │ │ │ etLookup.getProductCode().equals(fxDealDTO.getCoaProductCode())) │ │ │ │ .collect(Collectors.toList()) │ │ │ │ │ │ │ │ 【条件判断5】组合不匹配 │ │ │ │ null filterResult || filterResult.size() 0 │ │ │ │ │ │ │ │ └────────── 是 → alarmDealSet.add(dealNumber) │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ 输出: SetLong alarmDealSet (待预警的交易号集合) │ └──────────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────────────────────────────┐ │ 【步骤4】构建消息内容 │ │ ────────────────────────────────────────────────────────────────────────────────── │ │ │ │ 4.1 初始化StringJoiner和StringBuilder │ │ StringJoiner stringJoiner new StringJoiner(,) │ │ StringBuilder eSpaceDigestsBuilder new StringBuilder( │ │ There is inconsistency of BG, region code, product code for below TMC │ │ deals, pls check System.lineSeparator() │ │ TMC deal no: ) │ │ │ │ 4.2 换行格式化: changeLine() │ │ 代码位置: BgProReCodeAlarmTimeScheduler.java:178-199 │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ FOR循环: for (Long dealNumber : alarmDealSet) │ │ │ │ │ │ │ │ eSpaceDigestsBuilder.append(dealNumber).append(,) │ │ │ │ │ │ │ │ 构建超链接: │ │ │ │ String replace StringUtils.replace( │ │ │ │ buildUrl(dealNumber, LABEL_URL), // LABEL_URL /deal/fx-detail? │ │ │ │ dealNumber交易号 │ │ │ │ 交易号, dealNumber.toString()) │ │ │ │ │ │ │ │ 每15条交易换行: │ │ │ │ if (count % 15 0) { │ │ │ │ replace System.lineSeparator(); │ │ │ │ } │ │ │ │ │ │ │ │ stringJoiner.add(replace) │ │ │ │ count │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ 4.3 buildUrl()方法 │ │ 代码位置: BgProReCodeAlarmTimeScheduler.java:201-207 │ │ StringBuilder sb new StringBuilder(LABEL_START) // a href │ │ sb.append(HtmlParser.addSaasParamsOfUrl(url, LiveAppidEnum.DEAL.getValue(), │ │ HtmlParser.TABLE_TITLE, dealNumber.toString())) │ │ sb.append(LABEL_END) // 交易号/a │ │ │ │ 输出: │ │ - StringJoiner stringJoiner: 逗号分隔的超链接列表 │ │ - String digests: TMC deal no: 交易号1,交易号2,交易号3... │ └──────────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────────────────────────────┐ │ 【条件判断6】alarmDealSet.size() 0 │ │ ────────────────────────────────────────────────────────────────────────────────── │ │ │ │ │ │ 是 否 │ │ ▼ ▼ │ │ ┌────────────────────────┐ ┌────────────────────────────────┐ │ │ │ LOGGER.info(There is │ │ 【步骤5】发送预警消息 │ │ │ │ no satisfied) │ │ executeOnTimeSplit() │ │ │ │ return (方法结束) │ │ ──────────────────────────── │ │ │ └────────────────────────┘ │ 代码位置: BgProReCodeAlarm │ │ │ │ TimeScheduler.java:209-213 │ │ │ │ │ │ │ │ messageNoticeService │ │ │ │ .sendAlarmEsMsg( │ │ │ │ SUBJECT_LABEL, │ │ │ │ // tmc.bgProReCodeAlarm │ │ │ │ // TimeScheduler.label │ │ │ │ stringJoiner.toString(), │ │ │ │ digests │ │ │ │ ) │ │ │ │ │ │ │ │ LOGGER.info(send esMessage │ │ │ │ completed) │ │ │ │ │ │ │ │ LOGGER.info(bgProReCodeAlarm │ │ │ │ TimeScheduler end) │ │ │ └────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────────────┘executeOnTime() │ ├── 1. getEtrConfig() 获取ET配置 │ └── 调用restClientService.doSecurityGet()访问Treasury配置 │ ├── 2. fxDealDomainService.findDealForAlarm(alarmPeriod) 一次筛选 │ └── 从数据库查询最近N天的FX交易默认7天 │ ├── 3. secondFilter() 二次筛选 │ ├── 与ET配置比较BG/Product/Region Code │ └── 筛选出不匹配的dealNumber存入alarmDealSet │ ├── 4. changeLine() 格式化消息内容 │ └── 5. executeOnTimeSplit() 发送预警消息 └── messageNoticeService.sendAlarmEsMsg()