废话不多说直接进入主题将思路
难点
需要判断当前语言是否有填写内容,如果没有填写则需要查询主语言
一般解决思路
先查一遍判断是否有内容,没有则查询主语言再覆盖,这种方式简单明了,但会造成多余的查询
解决思路(不一定最优解欢迎讨论)
利用sql FIELD函数对语言进行排序,先判断当前切换的语言是否为主语言如果为主语言则不需要进行排序操作,如果不是则进行排序
首先在多语言表的实体类中给语言字段加上自定义注解
利用Mybatis-Plus InnerInterceptor拦截查询请求,如果查询的实体类有自定义注解则进行数据过滤和排序
代码
部分代码基于diboot有需求可以自行实现或引入相关jar包
实体类
/**
* 主键id
*/
@TableId(type = IdType.ASSIGN_UUID)
private String id;
/**
* 关联主表id
*/
private String mainId;
/**
* 语言code
*/
@DataAccessCheckpoint(type = CheckpointType.POSITION)
@ApiModelProperty(value = "语言code")
private String languageCode;
/**
* 名称
*/
@ApiModelProperty(value = "名称")
private String name;
InnerInterceptor拦截器
public class DataAccessControlInteceptor implements InnerInterceptor {
private static Logger log = LoggerFactory.getLogger(DataAccessControlInteceptor.class);
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
// 替换SQL
PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
String originSql = mpBoundSql.sql();
PlainSelect selectBody = parseSelectBody(originSql);
if (selectBody.getFromItem() instanceof Table) {
Table mainTable = (Table) selectBody.getFromItem();
String tableName = mainTable.getName().replace("`", "");
Class<?> entityClass = BindingCacheManager.getEntityClassByTable(tableName);
// 判断是否有注解有注解则进行筛选排序
if (entityClass != null && DataAccessAnnoCache.hasDataAccessCheckpoint(entityClass)) {
appendDataAccessCondition(selectBody, mainTable, entityClass);
}
String newSql = selectBody.toString();
mpBoundSql.sql(newSql);
// 打印修改后的SQL
if (log.isTraceEnabled() && V.notEquals(originSql, newSql)) {
log.trace("DataAccess Inteceptor SQL : {}", newSql);
}
}
}
/**
* 附加数据访问权限控制SQL条件
*
* @param entityClass
* @return
*/
private void appendDataAccessCondition(PlainSelect selectBody, Table mainTable, Class<?> entityClass) {
Expression dataAccessExpression = buildDataAccessExpression(mainTable, entityClass, selectBody);
// 主表需要数据权限检查
if (dataAccessExpression != null) {
String whereStmt = selectBody.getWhere() == null ? null : selectBody.getWhere().toString();
if (selectBody.getWhere() == null) {
selectBody.setWhere(dataAccessExpression);
} else {
AndExpression andExpression = new AndExpression(selectBody.getWhere(), dataAccessExpression);
selectBody.setWhere(andExpression);
}
log.debug("DataAccess Inteceptor Where: {} => {}", whereStmt, selectBody.getWhere().toString());
}
if (V.notEmpty(selectBody.getJoins())) {
for (Join join : selectBody.getJoins()) {
Table joinTable = (Table) join.getRightItem();
Class<?> joinEntityClass = BindingCacheManager.getEntityClassByTable(joinTable.getName());
// 无权限检查点注解,不处理
if (joinEntityClass == null || !DataAccessAnnoCache.hasDataAccessCheckpoint(joinEntityClass)) {
continue;
}
Expression joinDataAccessExpression = buildDataAccessExpression(joinTable, joinEntityClass, selectBody);
// 主表需要数据权限检查
if (joinDataAccessExpression != null) {
String joinOnStmt = join.getOnExpression() == null ? null : join.getOnExpression().toString();
if (join.getOnExpression() == null) {
join.setOnExpression(joinDataAccessExpression);
} else {
AndExpression andExpression = new AndExpression(join.getOnExpression(), joinDataAccessExpression);
join.setOnExpression(andExpression);
}
log.debug("DataAccess Inteceptor Join: {} => {}", joinOnStmt, join.getOnExpression().toString());
}
}
}
}
/**
* 构建数据权限检查条件
*
* @param mainTable
* @param entityClass
* @return
*/
private Expression buildDataAccessExpression(Table mainTable, Class<?> entityClass, PlainSelect selectBody) {
Expression dataAccessExpression = null;
DataAccessInterface checkImpl = ContextHelper.getBean(DataAccessInterface.class);
for (CheckpointType type : CheckpointType.values()) {
String idCol = DataAccessAnnoCache.getDataPermissionColumn(entityClass, type);
if (V.isEmpty(idCol)) {
continue;
}
List<String> idValues = checkImpl.getAccessibleIds(type);
if (V.isEmpty(idValues)) {
continue;
}
if (idValues.size() == 1) {
EqualsTo equalsTo = new EqualsTo();
if (mainTable.getAlias() != null) {
idCol = mainTable.getAlias().getName() + "." + idCol;
}
equalsTo.setLeftExpression(new Column(idCol));
equalsTo.setRightExpression(new StringValue(idValues.get(0)));
dataAccessExpression = equalsTo;
} else {
if (mainTable.getAlias() != null) {
idCol = mainTable.getAlias().getName() + "." + idCol;
}
if (V.isEmpty(selectBody.getGroupBy())
&& V.isEmpty(selectBody.getOrderByElements())
&& idCol.equals("language_code")) {
List<OrderByElement> elements = new ArrayList<>();
OrderByElement element = new OrderByElement();
Column column = new Column();
column.setColumnName("FIELD(language_code,'" + S.join(idValues, "','") + "')");
element.setExpression(column);
elements.add(element);
selectBody.setOrderByElements(elements);
}
String conditionExpr = idCol + " IN ('" + S.join(idValues, "','") + "')";
Expression valuesExpression = null;
try {
valuesExpression = CCJSqlParserUtil.parseCondExpression(conditionExpr);
} catch (JSQLParserException e) {
log.warn("解析condition异常: " + conditionExpr, e);
}
dataAccessExpression = valuesExpression;
}
// 数据权限只有一个
break;
}
return dataAccessExpression;
}
/**
* 解析SelectBody
*
* @param sql
* @return
*/
private PlainSelect parseSelectBody(String sql) {
Select select = null;
try {
select = (Select) CCJSqlParserUtil.parse(sql);
} catch (JSQLParserException e) {
log.warn("解析SQL异常: " + sql, e);
}
return select != null ? (PlainSelect) select.getSelectBody() : null;
}
}
代码部分因混合其他关键代码无法全部公开。
这部分代码可能非常混乱但大概思路就是利用好sql的FIELD函数对语言进行排序没有就会自动让主语言顶替, 如果代码部分缺少关键信息可以查看diboot源码,关键代码只修改了buildDataAccessExpression方法
至此语言缺失的情况完美解决,晚安😴
评论区