package org.jeecg.modules.system.aspect; import cn.hutool.core.util.ArrayUtil; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import cn.hutool.core.util.ReflectUtil; import lombok.extern.slf4j.Slf4j; import org.jeecg.common.aspect.annotation.Dict; import org.jeecg.common.aspect.annotation.DictExt; import org.jeecg.common.aspect.annotation.DictList; import org.jeecg.common.aspect.annotation.FieldQuery; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.util.oConvertUtils; import org.jeecg.modules.system.service.ISysDictService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.lang.reflect.Field; import java.text.SimpleDateFormat; import java.util.*; /** * 字典字段翻译服务 * 支持嵌套翻译 * @since 2020/5/27 */ @Service @Slf4j public class DicAspectService { @Autowired private ISysDictService dictService; /** * 本方法针对列表数据进行动态字典注入 * 字典注入实现 通过对实体类添加注解@dict 来标识需要的字典内容,字典分为单字典code即可 ,table字典 code table text配合使用与原来jeecg的用法相同 * 示例为SysUser 字段为sex 添加了注解@Dict(dicCode = "sex") 会在字典服务立马查出来对应的text 然后在请求list的时候将这个字典text,已字段名称加_dictText形式返回到前端 * 例输入当前返回值的就会多出一个sex_dictText字段 * { * sex:1, * sex_dictText:"男" * } * 前端直接取值sext_dictText在table里面无需再进行前端的字典转换了 * customRender:function (text) { * if(text==1){ * return "男"; * }else if(text==2){ * return "女"; * }else{ * return text; * } * } * 目前vue是这么进行字典渲染到table上的多了就很麻烦了 这个直接在服务端渲染完成前端可以直接用 * @param records * @return */ public List detailDict(List records){ List items = new ArrayList<>(); FieldBatchQuery batchQuery = new FieldBatchQuery(); boolean hasDict = false; for (Object record : records) { JSONObject item = parseDictItem(record); Field[] fieldsArray = oConvertUtils.getAllFields(record); for (int i = 0; i < fieldsArray.length; i++) { Field field = fieldsArray[i]; if (field.getAnnotation(Dict.class) != null && records.size()<=500) { hasDict = true; String code = field.getAnnotation(Dict.class).dicCode(); String text = field.getAnnotation(Dict.class).dicText(); String table = field.getAnnotation(Dict.class).dictTable(); String key = String.valueOf(item.get(field.getName())); //翻译字典值对应的txt String textValue = translateDictValue(code, text, table, key); item.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue); } // 字典翻译扩展功能,支持字典多字段匹配 else if(field.getAnnotation(DictExt.class) != null && records.size() <= 500){ hasDict = true; String[] code = field.getAnnotation(DictExt.class).dicCode(); String text = field.getAnnotation(DictExt.class).dicText(); String table = field.getAnnotation(DictExt.class).dicTable(); String[] params = field.getAnnotation(DictExt.class).dicParams(); String[] paramValues = this.getParamValues(item, params); String textValue = this.translateDictValue(code, text, table, paramValues); item.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue); item.put(field.getName(), ArrayUtil.join(paramValues, ",")); } else if(field.getAnnotation(FieldQuery.class) != null && records.size() <= 500){ FieldQuery ann = field.getAnnotation(FieldQuery.class); batchQuery.put(ann.dicTable(), ann.dicText(), ann.dicCode(), this.getParamValues(item, ann.dicParams())); } // 深度翻译list元素 else if(field.getAnnotation(DictList.class) != null){ Object fieldValue = ReflectUtil.getFieldValue(record, field); if(fieldValue == null){ // no-op } else if(null != fieldValue && fieldValue instanceof List){ try{ List listDetail = this.detailDict((List)fieldValue); item.put(field.getName(), listDetail); }catch (Exception e){ log.error("字段翻译错误,index=" + i + ",fieldName=" + field.getName() + ",fieldValue=" + fieldValue + ",record=" + record); } }else{ log.error("深度字典翻译只支持List类型,当前类型为" + fieldValue.getClass()); } } //date类型默认转换string格式化日期 if (field.getType().getName().equals("java.util.Date")&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){ SimpleDateFormat aDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName())))); } } if(records.size()>500 && hasDict){ log.error("使用 @Dict注解超过500条记录请使用分页查询"); } items.add(item); } // 批量查询关联数据 if(batchQuery.size() > 0){ Map> batchResult = new HashMap<>(); batchQuery.forEach((queryKey, queryParam) -> { // 批量查询 try { queryParam.setQueryResult(this.batchQueryFieldValue(queryParam.getQueryTable(), queryParam.getColumns(), queryParam.getParamName(), queryParam.getParamValues())); } catch (Exception e) { throw new RuntimeException(e); } }); for (int i = 0; i < records.size(); i++) { Object record = records.get(i); JSONObject item = items.get(i); try { this.fillItemValue(record, item, batchQuery); } catch (Exception e) { e.printStackTrace(); log.info("批量查询数据填充出错:{}", e.getMessage()); } } } return items; } /** * 单表批量查询 * * @param queryTable * @param columns * @param paramName * @param paramValues * @return */ private List batchQueryFieldValue(String queryTable, String[] columns, String paramName, String[] paramValues) throws Exception { try{ return this.dictService.queryTableFieldByParams(queryTable, columns, paramName, paramValues); }catch (Exception e){ log.error("FieldQuery查询错误", e); throw new Exception("FieldQuery查询错误"); } } /** * 填充item批量查询数据 * @param record * @param item * @param batchQuery */ private void fillItemValue(Object record, JSONObject item, FieldBatchQuery batchQuery) throws Exception{ for (Field field : oConvertUtils.getAllFields(record)) { if(field.getAnnotation(FieldQuery.class) != null){ FieldQuery fieldQuery = field.getAnnotation(FieldQuery.class); String[] paramValues = this.getParamValues(item, fieldQuery.dicParams()); Object textValue = batchQuery.findResult(fieldQuery.dicTable(), fieldQuery.dicCode(), paramValues, fieldQuery.dicText()); item.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue); item.put(field.getName(), ArrayUtil.join(paramValues, ",")); } } } /** * 解析字典Item * @param record * @return */ private JSONObject parseDictItem(Object record) { ObjectMapper mapper = new ObjectMapper(); String json="{}"; try { //解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat json = mapper.writeValueAsString(record); } catch (JsonProcessingException e) { log.error("json解析失败"+e.getMessage(),e); } return JSONObject.parseObject(json); } /** * 获取参数数值 * @param item * @param paramNames * @return */ private String[] getParamValues(JSONObject item, String[] paramNames) { // 获取实际参数 String[] values = new String[paramNames.length]; for (int i = 0; i < paramNames.length; i++) { String param = paramNames[i]; if(param.startsWith("$")){ values[i] = (String) item.get(param.substring(1)); }else{ values[i] = param; } if(values[i] == null) return null; if(values[i].indexOf("&&") > -1){ log.error(">>>参数["+values[i]+"]包含非法字符','"); return null; } } return values; } /** * 翻译字典文本 * * @param code * @param text * @param table * @param paramValues * @return */ private String translateDictValue(String[] code, String text, String table, String[] paramValues) { if(paramValues == null || paramValues.length <=0 || oConvertUtils.isEmpty(table)){ return null; } try{ return this.dictService.queryTableDictByParams(table,text, ArrayUtil.join(code, "&&"), ArrayUtil.join(paramValues, "&&")); }catch (Exception e){ log.error("字典翻译异常table={},text={},code={},paramValues={}", table, text, ArrayUtil.join(code, "&&"),ArrayUtil.join(paramValues, "&&") ); throw e; } } /** * 翻译字典文本 * @param code * @param text * @param table * @param key * @return */ private String translateDictValue(String code, String text, String table, String key) { if(oConvertUtils.isEmpty(key)) { return null; } StringBuffer textValue=new StringBuffer(); String[] keys = key.split(","); for (String k : keys) { String tmpValue = null; if (k.trim().length() == 0) { continue; //跳过循环 } if (!StringUtils.isEmpty(table)){ tmpValue= dictService.queryTableDictTextByKey(table,text,code,k.trim()); }else { tmpValue = dictService.queryDictTextByKey(code, k.trim()); } if (tmpValue != null) { if (!"".equals(textValue.toString())) { textValue.append(","); } textValue.append(tmpValue); } } return textValue.toString(); } }