周报/朋友圈简单@功能实现(java)
1. 项目背景
最近公司在开发周报系统,周报中需要像朋友圈一样具有@ 人的功能,在周保中可以输入@XXX,发布后需要给XXX发送消息提醒(xxx在某周周报中@ 了您,请点击链接查看)
2. 设计思路
使用jsoup获取前端页面自定义标签属性
前端在监控到需要输入“@” 符号并输入内容的时候发起suggest,查询可能需要@的人,查询到目标人后,前端在此处添加约定的标签属性,编辑发布完周报后,后端解析前端的区域内容(富文本,为保证编辑的格式),解析所有的内容,过滤需要的标签属性,获取该周报中的所有@记录,判断该份周报中是否为首次@,是发送消息提醒
标签解析弊端:
1. 前端富文本中如果输入了你需要匹配的自定义标签,并赋值,也会被解析工具解析,在一个完整的富文本标签里,该解析工具有可能无法区别,导致业务代码错误处理
3. 代码实现
- 前端代码展示片段展示
<p data-uuid="6d568da9-9142-44b3-8b5f-59bf82ff4453">这是我的测试区域
<span class="slate-editor-ait" data-ait-text="李伟" data-ait-id="bb420577-8dab-4a3b-889a-343233d763f1" data-ait-value="00111">@李伟</span>,看到效果了吧
</p>
- 后端代码实现
<!-- maven依赖 -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
标签解析
/**
* 处理json中包含特殊需要处理的标签,并收集对用的value
*
* @param object
*
* @return
*/
private List<AteLabelData> parseHtmlLabel(Object object, String areaCode) {
if (ObjectUtils.isEmpty(object)) {
return new ArrayList<>();
}
String html = JSONObject.toJSONString(object);
// 将转移后的 json 字符串 反转义解析(ps: 去除 \" )
html = StringEscapeUtils.unescapeJava(html);
log.debug("区域{},周报解析转义后的数据:{}", areaCode, html);
Document doc = Jsoup.parse(html);
Elements s = doc.getElementsByAttribute(BizConstants.SpecialLabel.DATA_ATE_ID);
List<AteLabelData> list = new ArrayList<>();
for (Element e : s) {
log.debug("-----------------------------");
String ateFlag = e.attr(BizConstants.SpecialLabel.DATA_ATE_ID);
log.debug("data-ate-id 标签的值:{}", ateFlag);
String userId = e.attr(BizConstants.SpecialLabel.DATA_ATE_USER_ID);
log.debug("data-ate-user-id 标签的值:{}", userId);
log.debug("-----------------------------");
if (StringUtils.isBlank(ateFlag) || StringUtils.isBlank(userId)) {
continue;
}
AteLabelData labelData = new AteLabelData();
labelData.setAteFlag(ateFlag);
labelData.setUserId(Long.parseLong(userId));
labelData.setAreaCode(areaCode);
list.add(labelData);
}
return list;
}
业务数据处理
@Override
public void selectAteRecord(ReportViewVO reportViewVO, ReportMain reportMain, HcmUser hcmUser) {
if (reportMain == null || reportViewVO == null) {
return;
}
log.debug("{}的周报,mainId:{},vo:{}", reportMain.getOwnerId(), reportMain.getMainId(), reportViewVO);
List<AteLabelData> dataList = new ArrayList<>();
/**
* .......其他区域处理
*/
// planReport
if (reportViewVO.getPlanReportInfoVO() != null) {
List<AteLabelData> labelList = parseHtmlLabel(reportViewVO.getPlanReportInfoVO(),
BizConstants.ReportTemplateAreaCode.PLAN_REPORT);
if (CollectionUtils.isNotEmpty(labelList)) {
dataList.addAll(labelList);
}
}
if (CollectionUtils.isEmpty(dataList)) {
return;
}
// 处理ate表数据
dealAteDataFormLabel(dataList, reportMain, hcmUser, reportViewVO);
}
/**
* 处理ate数据
*
* @param dataList
* @param reportViewVO
* @param hcmUser
* @param reportMain
*/
private void dealAteDataFormLabel(List<AteLabelData> dataList, ReportMain reportMain,
HcmUser hcmUser, ReportViewVO reportViewVO) {
ReportAteExample ateExample = new ReportAteExample();
ateExample.createCriteria().andMainIdEqualTo(reportMain.getMainId())
.andEnabledEqualTo(BizConstants.YesOrNot.YES);
List<ReportAte> reportAteList = selectByExample(ateExample);
if (CollectionUtils.isEmpty(reportAteList)) {
dealFirstAteData(dataList, reportMain, hcmUser, reportViewVO);
} else {
dealHaveAteData(dataList, reportAteList, reportMain, hcmUser, reportViewVO);
}
}
/**
* 处理本次周报提交前没有ate过
*
* @param dataList
* @param reportMain
* @param hcmUser
*/
private void dealFirstAteData(List<AteLabelData> dataList, CuxOkrReportMain reportMain,
HcmUser hcmUser, OkrReportViewVO reportViewVO) {
// 具体的业务逻辑
}
/**
* 处理本次周报提交前已有ate过数据的
*
* @param dataList
* @param reportMain
* @param hcmUser
*/
private void dealHaveAteData(List<AteLabelData> dataList, List<CuxOkrReportAte> reportAteList,
CuxOkrReportMain reportMain, HcmUser hcmUser, OkrReportViewVO reportViewVO) {
// 具体的业务逻辑
}
参考文档
- https://blog.csdn.net/mashuai720/article/details/80563848
- https://www.jianshu.com/p/fd5caaaa950d
- https://www.open-open.com/jsoup/