(fixed & feat) 修复索引删除路径错误的问题, 修复事务导致的索引滞后问题。 新增支持spel表达式的自定义注解,用于索引维护,减少对业务代码侵入同时增加拓展性

This commit is contained in:
Andecheal
2023-12-29 10:47:35 +08:00
parent 824f2f291e
commit 3cd75e1a78
5 changed files with 176 additions and 45 deletions

View File

@@ -0,0 +1,29 @@
package com.blossom.backend.base.search;
import com.blossom.backend.base.search.message.IndexMsgTypeEnum;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EnableIndex {
/**
* 索引操作类型, 默认值为追加
* @return
*/
IndexMsgTypeEnum type();
/**
* id字段表达式
* @return
*/
String id();
}

View File

@@ -0,0 +1,139 @@
package com.blossom.backend.base.search;
import com.blossom.backend.base.auth.AuthContext;
import com.blossom.backend.base.search.message.ArticleIndexMsg;
import com.blossom.backend.base.search.message.IndexMsgTypeEnum;
import com.blossom.backend.base.search.queue.IndexMsgQueue;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
@Aspect
@Component
@Slf4j
public class IndexAspect {
/**
* 用于spl解析
*/
private static final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
@Pointcut("@annotation(com.blossom.backend.base.search.EnableIndex)")
private void cutMethod(){
}
/**
* 成功返回后调用该方法,维护索引。 使用after防止事务未提交导致的数据滞后
*/
@AfterReturning("cutMethod()")
public void afterReturning(JoinPoint joinPoint) {
// 此处进行索引处理, 降低索引维护代码侵入
// 获取注解对象
EnableIndex annotation = getAnnotation(joinPoint);
if (annotation == null){
// 记录问题, 并结束逻辑
log.error("索引切面获取注解失败!");
return;
}
IndexMsgTypeEnum indexMsgTypeEnum = annotation.type();
if (indexMsgTypeEnum == null){
log.error("获取索引消息操作类型失败");
return;
}
String idSpEL = annotation.id();
if (!StringUtils.hasText(idSpEL)){
log.error("获取id表达式失败");
return;
}
Long customerId = parse(idSpEL, joinPoint, Long.class);
if (customerId == null){
return;
}
ArticleIndexMsg indexMsg = new ArticleIndexMsg(indexMsgTypeEnum, customerId, AuthContext.getUserId());
try {
IndexMsgQueue.add(indexMsg);
} catch (InterruptedException e) {
// 不抛出, 暂时先记录
log.error("索引操作失败" + e.getMessage());
}
}
/**
* 获取method
* @return method
*/
private Method getTargetMethod(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
Method agentMethod = methodSignature.getMethod();
try {
return joinPoint.getTarget().getClass().getMethod(agentMethod.getName(),agentMethod.getParameterTypes());
}catch (Exception e){
// 只记录异常
log.error("获取目标方法失败");
}
return null;
}
/**
* 获取注解声明对象
* @param joinPoint
* @return
*/
private EnableIndex getAnnotation(JoinPoint joinPoint){
// 获取方法上的注解
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Method method = sign.getMethod();
return method.getAnnotation(EnableIndex.class);
}
/**
* 解析SpEL表达式 提供后续拓展的灵活性
* @param spel
* @param joinPoint
* @param clazz
* @return
* @param <T>
*/
private <T> T parse(String spel, JoinPoint joinPoint, Class<T> clazz){
ExpressionParser parser = new SpelExpressionParser();
Method method = getTargetMethod(joinPoint);
if (method == null){
return null;
}
String[] params = discoverer.getParameterNames(method);
if (params == null || params.length == 0){
log.error("获取参数列表失败");
return null;
}
Object[] args = joinPoint.getArgs();
EvaluationContext context = new StandardEvaluationContext();
for (int len = 0; len < params.length; len++) {
context.setVariable(params[len], args[len]);
}
Expression expression = parser.parseExpression(spel);
return expression.getValue(context, clazz);
}
}

View File

@@ -21,7 +21,6 @@ import org.apache.lucene.store.FSDirectory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.File;
import java.util.concurrent.Executors;
/**
@@ -88,7 +87,7 @@ public class IndexMsgConsumer {
}
} else if (IndexMsgTypeEnum.DELETE.equals(indexMsg.getType())) {
// 删除索引
try (Directory directory = FSDirectory.open(new File(searchProperties.getPath()).toPath());
try (Directory directory = FSDirectory.open(searchProperties.getUserIndexDirectory(userId));
IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig(new StandardAnalyzer()));
) {

View File

@@ -4,10 +4,8 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blossom.backend.base.auth.AuthContext;
import com.blossom.backend.base.search.message.ArticleIndexMsg;
import com.blossom.backend.base.search.EnableIndex;
import com.blossom.backend.base.search.message.IndexMsgTypeEnum;
import com.blossom.backend.base.search.queue.IndexMsgQueue;
import com.blossom.backend.server.article.TagEnum;
import com.blossom.backend.server.article.draft.pojo.ArticleEntity;
import com.blossom.backend.server.article.draft.pojo.ArticleQueryReq;
@@ -151,16 +149,10 @@ public class ArticleService extends ServiceImpl<ArticleMapper, ArticleEntity> {
/**
* 新增
*/
@EnableIndex(type = IndexMsgTypeEnum.ADD, id = "#req.id")
@Transactional(rollbackFor = Exception.class)
public ArticleEntity insert(ArticleEntity req) {
baseMapper.insert(req);
ArticleIndexMsg articleIndexMsg = new ArticleIndexMsg(IndexMsgTypeEnum.ADD, req.getId(), AuthContext.getUserId());
try {
IndexMsgQueue.add(articleIndexMsg);
} catch (InterruptedException e) {
// 不抛出, 暂时先记录
log.error("索引更新失败" + e.getMessage());
}
return req;
}
@@ -168,17 +160,11 @@ public class ArticleService extends ServiceImpl<ArticleMapper, ArticleEntity> {
* 修改
* <p>该接口只能修改文章的基本信息, 正文及版本修改请使用 {@link ArticleService#updateContentById(ArticleEntity)}
*/
@EnableIndex(type = IndexMsgTypeEnum.ADD, id = "#req.id")
@Transactional(rollbackFor = Exception.class)
public Long update(ArticleEntity req) {
XzException404.throwBy(req.getId() == null, "ID不得为空");
baseMapper.updById(req);
ArticleIndexMsg articleIndexMsg = new ArticleIndexMsg(IndexMsgTypeEnum.ADD, req.getId(),AuthContext.getUserId());
try {
IndexMsgQueue.add(articleIndexMsg);
} catch (InterruptedException e) {
// 不抛出, 暂时先记录
log.error("索引更新失败" + e.getMessage());
}
return req.getId();
}
@@ -187,6 +173,7 @@ public class ArticleService extends ServiceImpl<ArticleMapper, ArticleEntity> {
*
* @return 返回文章字数
*/
@EnableIndex(type = IndexMsgTypeEnum.ADD, id = "#req.id")
@Transactional(rollbackFor = Exception.class)
public Integer updateContentById(ArticleEntity req) {
XzException404.throwBy(req.getId() == null, "ID不得为空");
@@ -199,14 +186,6 @@ public class ArticleService extends ServiceImpl<ArticleMapper, ArticleEntity> {
baseMapper.updContentById(req);
referenceService.bind(req.getUserId(), req.getId(), req.getName(), req.getReferences());
logService.insert(req.getId(), 0, req.getMarkdown());
// 更新索引
ArticleIndexMsg articleIndexMsg = new ArticleIndexMsg(IndexMsgTypeEnum.ADD, req.getId(), AuthContext.getUserId());
try {
IndexMsgQueue.add(articleIndexMsg);
} catch (InterruptedException e) {
// 不抛出, 暂时先记录
log.error("索引更新失败" + e.getMessage());
}
return req.getWords();
}
@@ -218,6 +197,7 @@ public class ArticleService extends ServiceImpl<ArticleMapper, ArticleEntity> {
* @param id 文章ID
* @since 1.10.0 删除时不校验文章内容, 被删除的文章会进入回收站, 可在回收站中查询
*/
@EnableIndex(type = IndexMsgTypeEnum.DELETE, id = "#id")
@Transactional(rollbackFor = Exception.class)
public void delete(Long id) {
ArticleEntity article = selectById(id, false, true, true);
@@ -235,14 +215,6 @@ public class ArticleService extends ServiceImpl<ArticleMapper, ArticleEntity> {
referenceService.delete(id);
// 删除访问记录
viewService.delete(id);
// 删除索引
ArticleIndexMsg articleIndexMsg = new ArticleIndexMsg(IndexMsgTypeEnum.DELETE, id, AuthContext.getUserId());
try {
IndexMsgQueue.add(articleIndexMsg);
} catch (InterruptedException e) {
// 不抛出, 暂时先记录
log.error("索引更新失败" + e.getMessage());
}
}
/**

View File

@@ -3,13 +3,11 @@ package com.blossom.backend.server.article.recycle;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blossom.backend.base.auth.AuthContext;
import com.blossom.backend.base.param.ParamEnum;
import com.blossom.backend.base.param.ParamService;
import com.blossom.backend.base.param.pojo.ParamEntity;
import com.blossom.backend.base.search.message.ArticleIndexMsg;
import com.blossom.backend.base.search.EnableIndex;
import com.blossom.backend.base.search.message.IndexMsgTypeEnum;
import com.blossom.backend.base.search.queue.IndexMsgQueue;
import com.blossom.backend.server.article.recycle.pojo.ArticleRecycleEntity;
import com.blossom.backend.server.folder.FolderService;
import com.blossom.backend.server.folder.pojo.FolderEntity;
@@ -52,6 +50,7 @@ public class ArticleRecycleService extends ServiceImpl<ArticleRecycleMapper, Art
*
* @param id 文章ID
*/
@EnableIndex(type = IndexMsgTypeEnum.ADD, id = "#id")
@Transactional(rollbackFor = Exception.class)
public void restore(Long id) {
ArticleRecycleEntity article = baseMapper.selectById(id);
@@ -62,13 +61,6 @@ public class ArticleRecycleService extends ServiceImpl<ArticleRecycleMapper, Art
baseMapper.restore(id, folder.getId());
}
baseMapper.deleteById(id);
ArticleIndexMsg articleIndexMsg = new ArticleIndexMsg(IndexMsgTypeEnum.ADD, article.getId(), AuthContext.getUserId());
try {
IndexMsgQueue.add(articleIndexMsg);
} catch (InterruptedException e) {
// 不抛出, 暂时先记录
log.error("索引更新失败" + e.getMessage());
}
}
/**