gitweixin
  • 首页
  • 小程序代码
    • 资讯读书
    • 工具类
    • O2O
    • 地图定位
    • 社交
    • 行业软件
    • 电商类
    • 互联网类
    • 企业类
    • UI控件
  • 大数据开发
    • Hadoop
    • Spark
    • Hbase
    • Elasticsearch
    • Kafka
    • Flink
    • 数据仓库
    • 数据挖掘
    • flume
    • Kafka
    • Hive
    • shardingsphere
    • solr
  • 开发博客
    • Android
    • php
    • python
    • 运维
    • 技术架构
    • 数据库
  • 程序员网赚
  • bug清单
  • 量化投资
  • 在线查询工具
    • 去行号
    • 在线时间戳转换工具
    • 免费图片批量修改尺寸在线工具
    • SVG转JPG在线工具

月度归档12月 2020

精品微信小程序开发门户,代码全部亲测可用

  • 首页   /  2020   /  
  • 12月
  • ( 页面3 )
solr 12月 7,2020

Solr封装的Java客户端工具类




import com.finest.ms.address.util.AddrMatchConstant;
import com.finest.ms.address.util.ConfigUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.HttpClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.*;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;


public class SolrClient {


private Logger log = LoggerFactory.getLogger(SolrClient.class);

/** ID唯一标识 */
public static final String ID = "ID";

/**
* solr服务哈希表,key为(格式为“http://ip:端口/core”)路径名称,value为HttpSolrServer对象
*/
private HashMap<String, SolrServer> solrServers = new HashMap<String, SolrServer>();

/** UTF-8编码 */
public static final String UTF8_CHARSET_NAME = "UTF-8";

/** 拆分词组URL,用于拆分词组 */
public static final String URL_SPLIT_WORDS = "splitwords";

/** 代码词组的URL,用于根据代码得到标准词组 */
public static final String URL_CODE_WORDS = "codewords";

/** 字段通用名称--冒号 */
public static final String FIELD_COLON = ":";

/** 字段通用名称--双引号 */
public static final String FIELD_DOUBLE_QUOTATION_MARKS = "\"";

/** 注解字段常量--otherFields */
public static final String ANNOTATION_OTHERFIELDS = "otherFields";

/** solr的URL地址,目前只有两种,标准地址的solrurl地址和业务数据的solrurl地址,在config.properties配置 */
private String solrUrl;

private HttpClient client;

/** solr的循环次数集合,key为solr的url地址, value为循环次数 */
private Map<String, Integer> solrLoopNumMap = new HashMap<String, Integer>();

/** solr网络重连次数,默认为5 */
private int pingLoopNum;

/** solr网络重连睡眠时间,以毫秒为单位,默认为1000毫秒 */
private long pingSleepTime;

// /** debug模式下 - 统计solr访问次数 ,多线程下此值会存在不准确情况 */public static long total = 0;

/**
* 构造函数
*
*
@param solrUrl solr的URL,目前只有两种,标准地址的solrurl地址和业务数据的solrurl地址,在config.properties配置
*/
public SolrClient(String solrUrl) {
this.solrUrl = solrUrl;
}

public SolrClient(String solrUrl, HttpClient client){
this.solrUrl = solrUrl;
this.client = client;
}

/**
* 根据core名称得到SolrServer对象,如果不存在,就创建
*
*
@param core solr的core名称
*
@return
*/
public SolrServer getSolrServer(String core) throws Exception {
// 对solrUrl和core为空判断,当为空时,直接返回SolrHttpException异常
if (StringUtils.isEmpty(solrUrl) || StringUtils.isEmpty(core)) {
String error = "solr工具类-根据core名称得到SolrServer对象方法出现异常!solrUrl或core名称为空!";
log.warn(error);
throw new SolrHttpException(error); // 异常自定义的异常,可以很清楚知道该异常是http连接出现问题导致
}
SolrServer solrServer = null;
// 根据格式为“http://ip:端口/core”组成的key,存放至solr服务哈希表。
String solrCoreUrl = solrUrl + "/" + core;
boolean isNeedAuth = "true".equalsIgnoreCase(ConfigUtil.getPropsValueByKey("ac.solr.isNeedAuth"));
if (solrServers.containsKey(solrCoreUrl)&&!isNeedAuth) {
solrServer = solrServers.get(solrCoreUrl);
} else {
// 判断是否需要验证用户名密码
if (isNeedAuth) {
String solrUser = ConfigUtil.getPropsValueByKey("ac.solr.username");
String solrPass = ConfigUtil.getPropsValueByKey("ac.solr.password");
ModifiableSolrParams params = new ModifiableSolrParams();
params.set(HttpClientUtil.PROP_MAX_CONNECTIONS, 128);
params.set(HttpClientUtil.PROP_MAX_CONNECTIONS_PER_HOST, 32);
params.set(HttpClientUtil.PROP_FOLLOW_REDIRECTS, false);
params.set(HttpClientUtil.PROP_BASIC_AUTH_USER, solrUser);
params.set(HttpClientUtil.PROP_BASIC_AUTH_PASS, solrPass);
HttpClient httpClient = HttpClientUtil.createClient(params);
solrServer = new HttpSolrExServer(solrCoreUrl, httpClient);
} else {
solrServer = new HttpSolrExServer(solrCoreUrl);
}
// solrServer的ping调用
pingSolrServer(solrServer);
//solrServers.put(solrCoreUrl, solrServer);
}
return solrServer;
}

/**
* 获取得到SolrDocument的数据列表
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@return
*/
public List<SolrDocument> getDocs(String criteria, String coreName, int rows, int start, String sortParams)
throws Exception {
return getDocs(criteria, coreName, rows, start, sortParams, null, null, null);
}

/**
* 获取得到SolrDocument的数据列表<br>
* 使用场景:<br>
* 1.当没有过滤显示字段数组,直接返回所有solr原始字段的SolrDocument数据。<br>
* 2.当有过滤显示字段数组并且没有过滤显示字段别名数组,返回过滤字段后的solr原始字段的SolrDocument数据。<br>
* 3.当有过滤显示字段数组并且有过滤显示字段别名数组,长度顺序一致,返回过滤字段后的以别名为key的SolrDocument数据。<br>
* 4.当有过滤显示字段数组并且有过滤显示字段别名数组,顺序一致,但长度不一致,即有部分设置别名,有部分没有设置别名,返回过滤字段后,有别名的以别名为key、没有别名的以字段名为key的SolrDocument数据。<br>
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*
@param coreUrls core Url数组,用于跨core查询
*
@return
*/
public List<SolrDocument> getDocs(String criteria, String coreName, int rows, int start, String sortParams,
String[] fields, String[] aliasFields, String[] coreUrls) throws Exception {
List<SolrDocument> docs = new ArrayList<SolrDocument>();
if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)) {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, rows, start, sortParams, fields, aliasFields,
null, null, coreUrls);

if (response.getResults() != null) {
docs.addAll(response.getResults());
}
}
return docs;
}

/**
* 得到分组统计后的文档列表<br>
* 与数据库group by分组返回结果集一致,支持多个字段的分组<br>
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param facetParams 分组参数字符串,格式以逗号隔开,如name,age
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与facetParams分组参数字符串拆分后得到数组的顺序一一对应,长度可不相同,<br>
* 可短于facetParams分组参数字符串拆分后得到数组,也可长于facetParams分组参数字符串拆分后得到数组。<br>
* 当短时,那些没有设置别名的,将取回facetParams分组对应的字段名;当长时,除了全部用别名之外,还将对统计别名的设置。
*
@return
* @throws Exception
*/
public List<SolrDocument> getFacetDocs(String criteria, String coreName, String facetParams, String[] aliasFields)
throws Exception {
// 当查询条件,或者core名称,或者分组参数为空时,直接返回空列表
if (StringUtils.isEmpty(criteria) || StringUtils.isEmpty(coreName) || StringUtils.isEmpty(facetParams)) {
return Collections.emptyList();
}

// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, 0, 0, null, null, aliasFields, facetParams, null,
null);

List<PivotField> pivotFieldList = null;
// 根据分组结果,设置返回值
NamedList<List<PivotField>> facetPivot = response.getFacetPivot();
if (facetPivot != null) {
pivotFieldList = facetPivot.get(facetParams);
}
// 调用得到分组的solr文档对象列表方法,返回结果
return getFacetSolrDocumentList(pivotFieldList, aliasFields);
}

/**
* 获取得到对象化的数据列表
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param clazz 要转换对象的Class
*
@return
*/
public <T extends Object> List<T> getBeans(String criteria, String coreName, int rows, int start, String sortParams,
Class<T> clazz) throws Exception {
return getBeans(criteria, coreName, rows, start, sortParams, clazz, null);
}

/**
* 获取得到对象化的数据列表<br>
* 使用场景:<br>
* 1.当没有过滤显示字段数组,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据列表。<br>
* 2.当有过滤显示字段数组并且没有过滤显示字段别名数组,过滤字段后,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据列表。<br>
* 3.当有过滤显示字段数组并且有过滤显示字段别名数组,不需要转换对象的Class注解有@Field字段:<br>
* 1)长度顺序一致,即都设置有别名情况下,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值。<br>
* 2)顺序一致,但长度不一致,即有部分设置别名,有部分没有设置别名,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值,找不到相同的属性名则不做赋值处理。<br>
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param clazz 要转换对象的Class
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名,并且返回对象的属性名与别名一致。
*
@param coreUrls core Url数组,用于跨core查询
*
@return
*/
public <T extends Object> List<T> getBeans(String criteria, String coreName, int rows, int start, String sortParams,
Class<T> clazz, String[] fields, String[] aliasFields, String[] coreUrls) throws Exception {
if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)) {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, rows, start, sortParams, fields, aliasFields,
null, null, coreUrls);

if (response.getResults() != null) {
// 如果设置有别名,根据转换对象的Class,通过反射设置得到对应的转换对象列表,并返回
// 如果没有设置别名,返回solr原始getBeans方法得到的对象列表值。该getBeans方法是通过注解的方式设置映射关系
if (fields != null && fields.length > 0 && aliasFields != null && aliasFields.length > 0) {
return this.getBeansByDocsAndClazz(response.getResults(), clazz);
} else {
try {
return response.getBeans(clazz);
} catch (Exception e) {
String error = "获取得到对象化的数据列表方法,在执行QueryResponse的getBeans时出现异常!";
log.error(error, e);
throw new SolrBeanBindingException(error);
}
}
}
}
return Collections.emptyList();
}

/**
* 获取得到对象化的数据列表<br>
* 使用场景:<br>
* 1.当fieldsMap字段Map为空,以转换对象的Class注解有@Field关联字段值进行映射,返回对象数据列表。<br>
* 2.当fieldsMap字段Map不为空情况下,不需要转换对象的Class注解有@Field字段:<br>
* 1)所有键与值都有值,即都设置有别名情况下,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值。<br>
* 2)转换对象的Class注解有@Field("otherFields")的Map属性,所有找不到别名名称相同的转换对象Class的属性名,把那些没有设置到属性里的值,全部加入到注解有@Field(
* "otherFields")的Map对象中<br>
* 3)如果没有找到注解有@Field("otherFields")的Map,所有找不到别名名称相同的转换对象Class的属性名,把那些没有设置到属性里的值全部丢弃掉<br>
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param clazz 要转换对象的Class
*
@param fieldsMap 字段Map,key为字段名,value为字段别名,别名可以为空,取回key对应的字段名为别名
*
@return
*/
public <T extends Object> List<T> getBeans(String criteria, String coreName, int rows, int start, String sortParams,
Class<T> clazz, Map<String, String> fieldsMap) throws Exception {
// 过滤显示字段数组
String[] fields = null;
// 过滤显示字段别名数组
String[] aliasFields = null;

// 通过字段map,得到字段名数组和别名数组,并设置
if (fieldsMap != null) {
// 字段名拼接字符串,字段名之间以逗号隔开
StringBuilder fieldsSb = new StringBuilder();
// 字段别名拼接字符串,字段别名之间以逗号隔开
StringBuilder aliasFieldsSb = new StringBuilder();

for (Entry<String, String> entry : fieldsMap.entrySet()) {
fieldsSb.append(entry.getKey()).append(",");
if (StringUtils.isNotEmpty(entry.getValue())) {
aliasFieldsSb.append(entry.getValue().trim()).append(",");
} else {
aliasFieldsSb.append(",");
}
}
if (fieldsSb.length() > 0) {
fieldsSb.delete(fieldsSb.length() - 1, fieldsSb.length());
}
if (aliasFieldsSb.length() > 0) {
aliasFieldsSb.delete(aliasFieldsSb.length() - 1, aliasFieldsSb.length());
}

fields = fieldsSb.toString().split(" *, *");
aliasFields = aliasFieldsSb.toString().split(" *, *");
}

return getBeans(criteria, coreName, rows, start, sortParams, clazz, fields, aliasFields, null);
}

/**
* 获取得到对象化的数据对象<br>
* 使用场景:<br>
* 1.当没有过滤显示字段数组,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据。<br>
* 2.当有过滤显示字段数组并且没有过滤显示字段别名数组,过滤字段后,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据。<br>
* 3.当有过滤显示字段数组并且有过滤显示字段别名数组,不需要转换对象的Class注解有@Field字段:<br>
* 1)长度顺序一致,即都设置有别名情况下,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值。<br>
* 2)顺序一致,但长度不一致,即有部分设置别名,有部分没有设置别名,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值,找不到相同的属性名则不做赋值处理。<br>
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param clazz 要转换对象的Class
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名,并且返回对象的属性名与别名一致。
*
@param coreUrls core Url数组,用于跨core查询
*
@return
*/
public <T extends Object> T getBean(String criteria, String coreName, String sortParams, Class<T> clazz,
String[] fields, String[] aliasFields, String[] coreUrls) throws Exception {
List<T> list = getBeans(criteria, coreName, 1, 0, sortParams, clazz, fields, aliasFields, coreUrls);
if (CollectionUtils.isNotEmpty(list)) {
return list.get(0);
}
return null;
}

/**
* 获取得到对象化的数据对象
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param clazz 要转换对象的Class
*
@return
*/
public <T extends Object> T getBean(String criteria, String coreName, String sortParams, Class<T> clazz)
throws Exception {
return getBean(criteria, coreName, sortParams, clazz, null, null, null);
}

/**
* 根据ID获取得到对象化的数据对象
*
*
@param id ID唯一标识
*
@param coreName core的名称
*
@param clazz 要转换对象的Class
*
@return
*/
public <T extends Object> T getBeanById(String id, String coreName, Class<T> clazz) throws Exception {
StringBuilder querySb = new StringBuilder();
querySb.append("ID:").append(id);
return getBean(querySb.toString(), coreName, null, clazz);
}

/**
* 获取得到对象化的数据Solr文档
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*
@return
*/
public SolrDocument getDoc(String criteria, String coreName, String[] fields, String[] aliasFields)
throws Exception {
List<SolrDocument> list = getDocs(criteria, coreName, 1, 0, null, fields, aliasFields, null);
if (CollectionUtils.isNotEmpty(list)) {
return list.get(0);
}
return null;
}

/**
* 获取得到对象化的数据Solr文档
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@return
*/
public SolrDocument getDoc(String criteria, String coreName) throws Exception {
return getDoc(criteria, coreName, null, null);
}

/**
* 获取总条数
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@return
*/
public long getNumberFound(String criteria, String coreName) throws Exception {
if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)) {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, 0, 0, null, null, null, null, null, null);
return response.getResults().getNumFound();
}
return 0;
}

/**
* 添加文档
*
*
@param document solr的输入文档对象
*
@param coreName core的名称
*/
public void addDoc(SolrInputDocument document, String coreName) throws Exception {
addDocumentIntoSolr(document, coreName, false);
}

/**
* 添加文档并马上提交
*
*
@param document solr的输入文档对象
*
@param coreName core的名称
*/
public void addDocByHardCommit(SolrInputDocument document, String coreName) throws Exception {
addDocumentIntoSolr(document, coreName, true);
}

/**
* 添加Bean
*
*
@param document Bean对象
*
@param coreName core的名称
*/
public void addBean(Object document, String coreName) throws Exception {
addDocumentIntoSolr(document, coreName, false);
}

/**
* 添加Bean并马上提交
*
*
@param document Bean对象
*
@param coreName core的名称
*/
public void addBeanByHardCommit(Object document, String coreName) throws Exception {
addDocumentIntoSolr(document, coreName, true);
}

/**
* 添加文档至solr
*
*
@param object object对象,可能是SolrInputDocument、也可能是bean对象
*
@param coreName core名称
*
@param isHardCommit 是否马上提交
*
@throws Exception
*/
private void addDocumentIntoSolr(Object object, String coreName, boolean isHardCommit) throws Exception {
if (object != null && StringUtils.isNotEmpty(coreName)) {
SolrServer solrServer = getSolrServer(coreName);
try {
if (object instanceof SolrInputDocument) {
solrServer.add((SolrInputDocument) object);
} else {
solrServer.addBean(object);
}
if (isHardCommit) {
solrServer.commit();
}
} catch (SolrServerException | IOException e) {
String error = "solr工具类-添加文档至solr方法出现异常!";
log.error(error, e);
throw new SolrServerIoException(error);
}
}
}

/**
* 添加bean列表
*
*
@param documents
*
@param coreName
*/
@SuppressWarnings("rawtypes")
public void addBeans(List documents, String coreName) throws Exception {
addDocumentsIntoSolr(documents, coreName, false);
}

/**
* 添加bean列表并马上提交
*
*
@param documents
*
@param coreName
*/
@SuppressWarnings("rawtypes")
public void addBeansByHardCommit(List documents, String coreName) throws Exception {
addDocumentsIntoSolr(documents, coreName, true);
}

/**
* 添加文档列表
*
*
@param documents
*
@param coreName
*/
public void addDocs(List<SolrInputDocument> documents, String coreName) throws Exception {
addDocumentsIntoSolr(documents, coreName, false);
}

/**
* 添加文档列表并马上提交
*
*
@param documents
*
@param coreName
*/
public void addDocsByHardCommit(List<SolrInputDocument> documents, String coreName) throws Exception {
addDocumentsIntoSolr(documents, coreName, true);
}

/**
* 添加文档(对象)列表到solr
*
*
@param documents
*
@param coreName
*
@param isHardCommit
*
@throws Exception
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private void addDocumentsIntoSolr(List documents, String coreName, boolean isHardCommit) throws Exception {
if (documents != null && documents.size() > 0 && StringUtils.isNotEmpty(coreName)) {
try {
SolrServer solrServer = getSolrServer(coreName);
if (documents.get(0) instanceof SolrInputDocument) {
solrServer.add(documents);
} else {
solrServer.addBeans(documents);
}
if (isHardCommit) {
solrServer.commit();
}
} catch (SolrServerException | IOException e) {
String error = "solr工具类-添加文档至solr方法出现异常!";
log.error(error, e);
throw new SolrServerIoException(error);
}
}
}

/**
* 根据ID更新solr字段值
*
*
@param inputDoc
*
@param id
*
@param fields
*
@param values
*
@return
*/
public SolrInputDocument updateSolrFieldsWithId(SolrInputDocument inputDoc, Object id, List<String> fields,
Object[] values) {
inputDoc = this.updateSolrFields(inputDoc, fields, values);
inputDoc.setField(ID, id);
return inputDoc;
}

/**
* 更新solr字段值
*
*
@param inputDoc
*
@param fields
*
@param values
*
@return
*/
public SolrInputDocument updateSolrFields(SolrInputDocument inputDoc, List<String> fields, Object[] values) {
if (fields != null && values != null && fields.size() <= values.length) {
for (int i = 0; i < fields.size(); i++) {
Map<String, Object> updater = new HashMap<String, Object>();
updater.put("set", values[i]);
inputDoc.setField(fields.get(i), updater);
}
}

return inputDoc;
}

/**
* 更新solr字段值
*
*
@param inputDoc
*
@param fields
*
@param values
*
@return
*/
public SolrInputDocument updateSolrFields(SolrInputDocument inputDoc, List<String> fields, SolrDocument values) {
if (fields != null && values != null) {
for (int i = 0; i < fields.size(); i++) {
Map<String, Object> updater = new HashMap<String, Object>();
updater.put("set", values.getFieldValue(fields.get(i)));
inputDoc.setField(fields.get(i), updater);
}
}

return inputDoc;
}

/**
* 根据查询条件删除solr数据
*
*
@param query 查询条件
*
@param coreName core名称
*/
public void deleteByQuery(String query, String coreName) throws Exception {
try {
SolrServer solrServer = getSolrServer(coreName);
solrServer.deleteByQuery(query);
solrServer.commit();
} catch (SolrServerException | IOException e) {
String error = "solr工具类-根据查询条件删除solr数据方法出现异常!";
log.error(error, e);
throw new SolrServerIoException(error);
}
}

/**
*
*
@param coreName
*
@param termsField
*
@param prefix
*
@param fetchRows
*
@return
*/
public List<TermsResponse.Term> suggest(String coreName, String termsField, String prefix, int fetchRows)
throws Exception {
SolrQuery solrQuery = new SolrQuery();
solrQuery.addTermsField(termsField);
solrQuery.setTerms(true);
solrQuery.setTermsLimit(fetchRows);
solrQuery.setTermsSortString("index");
solrQuery.setTermsPrefix(prefix);
solrQuery.setRequestHandler("/terms");
QueryResponse queryResponse = getSolrServer(coreName).query(solrQuery);
return queryResponse.getTermsResponse().getTerms(termsField);
}

/**
* 拼写
*
*
@param coreName
*
@param queryString
*
@param fetchRows
*
@param fetchStartAt
*
@param sortParams
*
@param params
*
@return
*/
public <T extends Object> SolrDocumentList spell(String coreName, String queryString, int fetchRows,
int fetchStartAt, String sortParams, String params) throws Exception {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(queryString, coreName, fetchRows, fetchStartAt, sortParams, null,
null, null, "spell", null);

SolrDocumentList results = response.getResults();
if (null == results && null != response.getGroupResponse()) {
results = new SolrDocumentList();
for (GroupCommand groupCommand : response.getGroupResponse().getValues()) {
for (Group group : groupCommand.getValues()) {
results.addAll(group.getResult());
}
}
}
return results;
}

/**
* 拆分词组,返回匹配到的词组
*
*
@param criteria 查询条件
*
@param coreName core名称
*
@param requestHandler 请求句柄
*
@param rows 条数,可用于分页
*
@param start 开始查询位置,可用于分页
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@return
*/
public <T extends Object> List<T> splitwWordsBeans(String criteria, String coreName, String requestHandler,
int rows, int start, String sortParams, Class<T> clazz) throws Exception {
if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)
&& StringUtils.isNotEmpty(requestHandler)) {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, rows, start, sortParams, null, null, null,
requestHandler, null);

if (response.getResults() != null) {
try {
return response.getBeans(clazz);
} catch (Exception e) {
String error = "拆分词组返回匹配到的词组方法,在执行QueryResponse的getBeans操作时出现异常!";
log.error(error, e);
throw new SolrBeanBindingException(error);
}
}
}
return Collections.emptyList();
}

/**
* 拆分词组,返回匹配到SolrDocument的词组
*
*
@param criteria 查询条件
*
@param coreName core名称
*
@param requestHandler 请求句柄
*
@param rows 条数,可用于分页
*
@param start 开始查询位置,可用于分页
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@return
*/
public List<SolrDocument> splitwWordsDocs(String criteria, String coreName, String requestHandler, int rows,
int start, String sortParams) throws Exception {
List<SolrDocument> docs = new ArrayList<SolrDocument>();
if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)
&& StringUtils.isNotEmpty(requestHandler)) {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, rows, start, sortParams, null, null, null,
requestHandler, null);

if (response.getResults() != null) {
docs.addAll(response.getResults());
}
}
return docs;
}

/**
* 反向匹配,返回转换后的结果对象
*
*
@param coreName core名称
*
@param lon 经度
*
@param lat 纬度
*
@param radius 搜索半径,单位km
*
@param returnDistanceName 返回时距离的名称,与tClass距离属性名称要一致,如不填,tClass要有distance距离属性,不然将无法得到距离值
*
@param clazz 要转换对象的Class
*
@return
*/
public <T extends Object> T reverseMatchBean(String coreName, double lon, double lat, double radius, Class<T> clazz,
String returnDistanceName) throws Exception {
List<T> list = this.reverseMatchBeans(coreName, lon, lat, radius, 1, 0, clazz, returnDistanceName);
if (CollectionUtils.isNotEmpty(list)) {
return list.get(0);
}
return null;
}

/**
* 反向匹配,返回转换后的结果对象列表
*
*
@param coreName core名称
*
@param lon 经度
*
@param lat 纬度
*
@param radius 搜索半径,单位km
*
@param start 开始位置
*
@param rows 查询的行数
*
@param returnDistanceName 返回时距离的名称,与tClass距离属性名称要一致,如不填,tClass要有distance距离属性,不然将无法得到距离值
*
@param clazz 要转换对象的Class
*
@return
*/
public <T extends Object> List<T> reverseMatchBeans(String coreName, double lon, double lat, double radius,
int rows, int start, Class<T> clazz, String returnDistanceName) throws Exception {
if (StringUtils.isNotEmpty(coreName)) {
// 得到反向solr查询对象
SolrQuery solrQuery = getReverseSolrQuery(coreName, lon, lat, radius, rows, start, returnDistanceName);
// 得到solr的查询返回结果对象
if(!coreName.equals(AddrMatchConstant.CORE_NAME_STANDARDADDRESS)) {
coreName = AddrMatchConstant.CORE_NAME_STANDARDADDRESS;
}
QueryResponse response = getQueryResponse(coreName, solrQuery);

if (response.getResults() != null) {
try {
return response.getBeans(clazz);
} catch (Exception e) {
String error = "反向匹配返回转换后的结果对象列表方法,在执行QueryResponse的getBeans时出现异常!";
log.error(error, e);
throw new SolrBeanBindingException(error);
}
}
}
return Collections.emptyList();
}

/**
* 反向匹配,返回SolrDocument列表
*
*
@param coreName core名称
*
@param lon 经度
*
@param lat 纬度
*
@param radius 搜索半径,单位km
*
@param start 开始位置
*
@param rows 查询的行数
*
@param returnDistanceName 返回时距离的名称
*
@return
*/
public List<SolrDocument> reverseMatchDocs(String coreName, double lon, double lat, double radius, int rows,
int start, String returnDistanceName) throws Exception {
List<SolrDocument> docs = new ArrayList<SolrDocument>();
if (StringUtils.isNotEmpty(coreName)) {
// 得到反向solr查询对象
SolrQuery solrQuery = getReverseSolrQuery(coreName, lon, lat, radius, rows, start, returnDistanceName);
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(coreName, solrQuery);

if (response.getResults() != null) {
docs.addAll(response.getResults());
}
}
return docs;
}

/**
* 得到反向solr查询对象
*
*
@param coreName core名称
*
@param lon 经度
*
@param lat 纬度
*
@param radius 搜索半径,单位km
*
@param start 开始位置
*
@param rows 查询的行数
*
@param returnDistanceName 返回时距离的名称
*
@return
*/
private SolrQuery getReverseSolrQuery(String coreName, double lon, double lat, double radius, int rows, int start,
String returnDistanceName) {
SolrQuery solrQuery = new SolrQuery();
if(coreName.equals(AddrMatchConstant.CORE_NAME_INFOPOINT)) {
System.out.println("信息点请求");
solrQuery.set("q", "YSLXDM:12");
}else if(coreName.equals(AddrMatchConstant.CORE_NAME_AEINCLUSIONRELATION)) {
System.out.println("街路巷查询");
solrQuery.set("q", "YSLXDM:6");
}else {
solrQuery.set("q", "*:*");
}
solrQuery.set("fq", "{!geofilt}"); // 距离过滤函数
solrQuery.set("pt", lat + "," + lon); // 当前坐标
solrQuery.set("sfield", "GEO"); // 经纬度字段,默认约定geo名称
solrQuery.set("d", "" + radius); // 搜索半径,单位km
solrQuery.set("sort", "geodist() asc"); // 根据距离排序:由近到远
solrQuery.set("start", start); // 记录开始位置
solrQuery.set("rows", rows); // 查询的行数
if (StringUtils.isEmpty(returnDistanceName)) {
returnDistanceName = "distance";
}
solrQuery.set("fl", "*," + returnDistanceName + ":geodist()"); // 查询的结果中添加距离
return solrQuery;
}


/**
* 得到街路巷查询对象
*
*
@return
*/
private SolrQuery getStreetSolrQuery(String coreName, int rows, int start,
String ssqx) {
String q = "(SJDM:"+ssqx+" OR SJMC:"+ssqx+" OR SJQHDM:"+ssqx+" OR SJQHMC:"+ssqx+" OR QXDM:"+ssqx+" OR QXMC:"+ssqx+") AND " +
"(YSLXMC:"+coreName+" OR JLXDM:"+coreName+")";

SolrQuery solrQuery = new SolrQuery();
solrQuery.set("q", q);
// solrQuery.set("sort", "geodist() asc"); // 根据距离排序:由近到远
solrQuery.set("start", start); // 记录开始位置
solrQuery.set("rows", rows); // 查询的行数

return solrQuery;
}

/**
* solr导入数据操作,返回solr的查询返回结果对象<br>
* 根据参数不同,该方法不一定就是进行solr同步数据。 比如获取solr导入数据状态<br>
*
*
@param coreName core的名称
*
@param paramsDataMap 参数数据Map
*
@return
*/
private QueryResponse importData(String coreName, Map<String, String> paramsDataMap) throws Exception {
// 得到反向solr查询对象
SolrQuery solrQuery = new SolrQuery();
solrQuery.setRequestHandler("/dataimport");
if (paramsDataMap != null) { // 设置相关参数
for (Map.Entry<String, String> entry : paramsDataMap.entrySet()) {
solrQuery.setParam(entry.getKey(), entry.getValue());
}
}
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(coreName, solrQuery);
return response;
}

/**
* 压缩
*
*
@param content
*
@param name
*
@return
* @throws IOException
*/
public static byte[] zip(String content, String name) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = null;
DataOutputStream dataOutputStream = null;
try {
zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
zipOutputStream.putNextEntry(new ZipEntry(name));
dataOutputStream = new DataOutputStream(zipOutputStream);
dataOutputStream.write(content.getBytes(UTF8_CHARSET_NAME));
dataOutputStream.flush();
zipOutputStream.flush();
zipOutputStream.finish();
} finally {
dataOutputStream.close();
zipOutputStream.close();
}
return byteArrayOutputStream.toByteArray();
}

/**
* 解压
*
*
@param value
*
@return
*/
public String unZip(byte[] value) throws Exception {
if (null == value) {
return null;
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(value);
ZipInputStream zipInputStream = null;
String sourceText = null;
try {
zipInputStream = new ZipInputStream(byteArrayInputStream);
zipInputStream.getNextEntry();
final int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int n;
while ((n = zipInputStream.read(buffer, 0, bufferSize)) != -1) {
byteArrayOutputStream.write(buffer, 0, n);
}
sourceText = byteArrayOutputStream.toString("UTF-8");
} catch (IOException e) {
String error = "solr工具类-解压方法出现异常!";
log.error(error, e);
throw new SolrServerIoException(error);
} finally {
try {
if (null != zipInputStream) {
zipInputStream.close();
zipInputStream = null;
}
if (null != byteArrayInputStream) {
byteArrayInputStream.close();
byteArrayInputStream = null;
}
if (null != byteArrayOutputStream) {
byteArrayOutputStream.close();
byteArrayOutputStream = null;
}
} catch (IOException e) {
String error = "solr工具类-解压方法出现异常!";
log.error(error, e);
throw new SolrServerIoException(error);
}
}
return sourceText;
}

/**
* 设置排序参数,并返回SolrQuery对象
*
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param solrQuery solr查询对象
*
@return
*/
private SolrQuery setSortParams(String sortParams, SolrQuery solrQuery) {
SolrQuery returnSolrQuery = solrQuery;
if (StringUtils.isNotEmpty(sortParams)) {
String[] sortFields = sortParams.trim().split(" *, *");
for (String sortField : sortFields) {
String[] sortFieldAndOrder = sortField.split(" +");
if (2 == sortFieldAndOrder.length) {
returnSolrQuery.addSort(sortFieldAndOrder[0],
SolrQuery.ORDER.valueOf(sortFieldAndOrder[1].toLowerCase()));
} else if (1 == sortFieldAndOrder.length) {
returnSolrQuery.addSort(sortFieldAndOrder[0], SolrQuery.ORDER.asc);
}
}
}
return returnSolrQuery;
}

/**
* 得到设置过虑字段参数,如果有别名,则设置别名
*
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*/
private String[] getAliasFieldsArray(String[] fields, String[] aliasFields) {
if (fields == null || fields.length <= 0) {
return null;
}

// 判断字段别名数组不为空且长度与字段数组相等
if (aliasFields != null && aliasFields.length > 0) {
StringBuilder fieldSb = new StringBuilder();
// 别名的格式是: 别名:真实名
for (int i = 0; i < fields.length; i++) {
fieldSb.delete(0, fieldSb.length());
if (aliasFields.length - 1 >= i) {
if (StringUtils.isEmpty(aliasFields[i])) {
aliasFields[i] = fields[i];
}
fieldSb.append(aliasFields[i]).append(":").append(fields[i]);
} else {
fieldSb.append(fields[i]).append(":").append(fields[i]);
}
fields[i] = fieldSb.toString();
}
}
return fields;
}

/**
* 设置分组参数,并返回SolrQuery对象<br>
* 主要用于数据统计,对应数据库中的group by
*
*
@param facetParams 分组参数字符串,格式以逗号隔开,如name,age
*
@param solrQuery solr查询类
*/
private SolrQuery setFacetParams(String facetParams, SolrQuery solrQuery) {
SolrQuery returnSolrQuery = solrQuery;
if (StringUtils.isNotEmpty(facetParams)) {
returnSolrQuery.setFacet(true);
returnSolrQuery.addFacetPivotField(facetParams);
returnSolrQuery.setFacetMissing(false); // 不统计null的值
}
return returnSolrQuery;
}

/**
* 根据solr文档列表和转换对象的Class,通过反射设置得到对应的转换对象列表<br>
* 1.主要用于当已经设置过滤返回对象和别名后得到的docs文档列表,根据转换对象的Class,设置与文档key一致的属性值<br>
* 2.当转换对象的Class注解有@Field("otherFields"),把那些没有设置到属性里的值,全部加入到注解有@Field("otherFields")的Map对象中<br>
* 3.如果没有找到注解有@Field("otherFields")的Map,那些没有设置到属性里的值全部丢弃掉<br>
*
*
@param docs solr文档对象列表
*
@param clazz 要转换对象的Class
*
@return
*/
private <T extends Object> List<T> getBeansByDocsAndClazz(List<SolrDocument> docs, Class<T> clazz) {
// solr文档对象列表为空,直接返回空列表
if (docs == null || docs.size() <= 0) {
return Collections.emptyList();
}

// 得到所有属性列表
Field[] declaredFields = clazz.getDeclaredFields();
// 对象实例
T obj = null;
// 其他字段值map
Map<String, String> otherFieldValueMap = null;
// solr字段Object对象变量
Object fieldValueObj = null;
// solr字段字符串变量
String fieldValueStr = null;
// 返回列表
List<T> rtnList = new ArrayList<T>();
// 是否有相同的字段名称
boolean hasSameFieldName = false;
// 日期格式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

for (SolrDocument doc : docs) {
// fieldValueMap = doc.getFieldValueMap();
try {
hasSameFieldName = false;
otherFieldValueMap = new HashMap<String, String>();
// 创建实例
obj = clazz.newInstance();
// 循环反射得到的字段列表,比较字段名是否一致,一致的话则赋值给对象。
for (Entry<String, Object> entry : doc.entrySet()) {
fieldValueObj = entry.getValue();

for (Field field : declaredFields) {
// 字段名一致
if (field.getName().equals(entry.getKey())) {
field.setAccessible(true);

// 类型转换,如果是solr文档对象是日期类型,并且与clazz属性类型不一致,则做日期格式转换
if (fieldValueObj instanceof Date) {
if (field.getType() == Date.class) {
field.set(obj, fieldValueObj);
} else {
field.set(obj, dateFormat.format(fieldValueObj));
}
} else {
// 除了日期类型之外,其他类型solr对象与bean对象属性类型一致,按原类型设置值
if (fieldValueObj.getClass() == field.getType()) {
field.set(obj, fieldValueObj);
} else {
field.set(obj, fieldValueObj.toString());
}
}
hasSameFieldName = true;
break;
}
}

if (!hasSameFieldName) {
// 那些没有找到相同属性名的值,全部加入Map对象中
if (fieldValueObj instanceof Date) {
fieldValueStr = dateFormat.format(fieldValueObj);
} else {
// 除了日期类型之外,其他类型按字符串类型设置值
fieldValueStr = fieldValueObj.toString();
}
otherFieldValueMap.put(entry.getKey(), fieldValueStr);
}

} // end-for (Entry<String, Object> entry : doc.entrySet())

// 通过反射,设置其他字段值map到对象实例
setOtherFieldValueMap(declaredFields, obj, otherFieldValueMap);

rtnList.add(obj);
} catch (InstantiationException | IllegalAccessException e) {
// 出现异常,记录日志,不直接抛出中断流程
log.error("通过转换得到对应的转换对象列表方法时,出现异常!", e);
}
}
return rtnList;
}

/**
* 通过反射,设置其他字段值map到对象实例
*
*
@param declaredFields 所有属性字段的列表
*
@param obj 要转换对象Class的对象实例
*
@param otherFieldValueMap 其他字段值map
*
@return
*/
private <T extends Object> T setOtherFieldValueMap(Field[] declaredFields, T obj,
Map<String, String> otherFieldValueMap) {

for (Field field : declaredFields) {
if (field.isAnnotationPresent(org.apache.solr.client.solrj.beans.Field.class)
&& field.getType() == Map.class) {

org.apache.solr.client.solrj.beans.Field annotationField = field
.getAnnotation(org.apache.solr.client.solrj.beans.Field.class);

// 注解的字段名是否为otherFields,则把除了有设置别名之外的需要返回的字段值,赋值给该字段值上
if (ANNOTATION_OTHERFIELDS.equals(annotationField.value())) {
try {
field.setAccessible(true);
field.set(obj, otherFieldValueMap);
} catch (IllegalArgumentException | IllegalAccessException e) {
// 出现异常,记录日志,不直接抛出中断流程
log.error("通过反射设置其他字段值map到对象实例方法时,出现异常!", e);
}
break;
}
}
}
return obj;
}

/**
* 触发批量更新,针对批量<br>
* 全库导入,同步的速度是最快,增量导入,需要逐条比对,比较慢。<br>
*
*
@param coreName core的名称
*
@param hasFullImport 是否全库导入,第一次抽取匹配时设置为true,增量抽取匹配时设置为false
*
@return Long 更新条数
*/
@SuppressWarnings("unchecked")
public Long triggerBatchUpdate(String coreName, Boolean hasFullImport) throws Exception {
// 命令参数
String commandParam = (hasFullImport != null && hasFullImport) ? "full-import" : "delta-import";
// 同步参数设置
Map<String, String> paramsDataMap = new HashMap<String, String>();
paramsDataMap.put("command", commandParam);
paramsDataMap.put("clean", "false");
paramsDataMap.put("commit", "true");

// 根据参数执行solr导入同步数据操作
importData(coreName, paramsDataMap);

// 状态参数设置
Map<String, String> statusParamsDataMap = new HashMap<String, String>();
statusParamsDataMap.put("command", "status");

int num = 0;
QueryResponse response; // 查询结果对象
NamedList<Object> resMap; // 查询结果Map
String status; // 状态值,有两种,idle和busy。
LinkedHashMap<String, String> statusMessages; // 状态消息对象

while (true) {
// solr导入数据操作
response = importData(coreName, statusParamsDataMap);

// 获取得到更新条数。
resMap = response.getResponse();
status = resMap.get("status") == null ? "" : resMap.get("status").toString();
log.debug("~~~~~获取数据同步至solr状态!状态为:" + status);

if ("idle".equalsIgnoreCase(status)) { // 状态已完成
if (resMap.get("statusMessages") != null) { // 更新条数是在statusMessages对象里
try {
statusMessages = (LinkedHashMap<String, String>) resMap.get("statusMessages");
num = Integer.parseInt(statusMessages.get("Total Documents Processed"));
} catch (Exception e1) {
num = 0;
}
}
break;
}
TimeUnit.MILLISECONDS.sleep(1000); // 睡眠1秒
}

return (long) num;
}

/**
* 得到分组的solr文档对象列表
*
*
@param pivotFieldList 枢轴字段对象列表
*
@param aliasFields 过滤显示字段别名数组
*
@return
*/
private List<SolrDocument> getFacetSolrDocumentList(List<PivotField> pivotFieldList, String[] aliasFields) {
List<Map<String, Object>> results = new ArrayList<Map<String, Object>>();
List<SolrDocument> returnSolrDocList = new ArrayList<SolrDocument>();
// 调用递归枢轴字段方法
recursiveFacetPivotField(pivotFieldList, null, results);
if (CollectionUtils.isEmpty(results)) {
return returnSolrDocList;
}

// 将List<Map<String, Object>> 转换为List<SolrDocument>结果
SolrDocument solrDoc = null;
int aliasFieldIndex;
String pivotFieldKey;
for (Map<String, Object> pivotFieldAndValueMap : results) {
solrDoc = new SolrDocument();
aliasFieldIndex = 0;
for (Entry<String, Object> entry : pivotFieldAndValueMap.entrySet()) {
// 别名处理
if (aliasFields != null && aliasFields.length > 0 && aliasFieldIndex < aliasFields.length) {
pivotFieldKey = aliasFields[aliasFieldIndex];
aliasFieldIndex++;
} else {
pivotFieldKey = entry.getKey();
}
solrDoc.setField(pivotFieldKey, entry.getValue());
}
returnSolrDocList.add(solrDoc);
}
return returnSolrDocList;
}

/**
* 递归分组的枢轴字段方法<br>
* PivotField为solr特殊类型<br>
*
*
@param pivotFieldList 枢轴字段对象列表
*
@param pivotFieldAndValueMap 枢轴字段和值的Map对象
*
@param results 结果集,类似于数据库group by后,得到的结果集
*/
private void recursiveFacetPivotField(List<PivotField> pivotFieldList, Map<String, Object> pivotFieldAndValueMap,
List<Map<String, Object>> results) {
if (pivotFieldList == null || pivotFieldList.size() <= 0) {
return;
}
if (pivotFieldAndValueMap == null) {
pivotFieldAndValueMap = new LinkedHashMap<String, Object>();
}

for (PivotField pivotField : pivotFieldList) {
pivotFieldAndValueMap.put(pivotField.getField(), pivotField.getValue());
if (pivotField.getPivot() == null || pivotField.getPivot().size() <= 0) {
// TODO 设定统计值key和值,key暂时未设置为常量
pivotFieldAndValueMap.put("count", pivotField.getCount());
results.add(new LinkedHashMap<String, Object>(pivotFieldAndValueMap));
} else {
recursiveFacetPivotField(pivotField.getPivot(), pivotFieldAndValueMap, results);
}
}
}

/**
* 得到solr的查询返回结果对象
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*
@param facetParams 分组参数字符串,格式以逗号隔开,如name,age
*
@param requestHandler 请求句柄,如分词为splitwords
*
@param coreUrls core Url数组,用于跨core查询
*
@return
* @throws Exception
*/
private QueryResponse getQueryResponse(String criteria, String coreName, Integer rows, Integer start,
String sortParams, String[] fields, String[] aliasFields, String facetParams, String requestHandler,
String[] coreUrls) throws Exception {
// 跨core查询
if (coreUrls != null && coreUrls.length > 1) {
return getMoreCoreQueryResponse(coreUrls, criteria, coreName, rows, start, sortParams, fields, aliasFields);
}

SolrQuery solrQuery = new SolrQuery().setQuery(criteria).setRows(rows).setStart(start);
solrQuery = setSortParams(sortParams, solrQuery);
solrQuery = setFacetParams(facetParams, solrQuery);
fields = this.getAliasFieldsArray(fields, aliasFields);
solrQuery.setFields(fields);
if (StringUtils.isNotEmpty(requestHandler)) {
solrQuery.setRequestHandler("/" + requestHandler);
}

return this.getQueryResponse(coreName, solrQuery);
}

/**
* 得到solr的查询返回结果对象
*
*
@param coreName core名称
*
@param solrQuery solr查询对象
*
@return
* @throws Exception
*/
public QueryResponse getQueryResponse(String coreName, SolrQuery solrQuery) throws Exception {
SolrServer solrServer = getSolrServer(coreName);
// 设置分组参数
QueryRequest queryRequest = new QueryRequest(solrQuery);
queryRequest.setMethod(SolrRequest.METHOD.POST);
try {
// if (log.isDebugEnabled()) {total++;}
QueryResponse response = queryRequest.process(solrServer);
return response;
} catch (SolrServerException | HttpSolrServer.RemoteSolrException e) {
String error = "solr工具类-执行得到solr的查询返回结果对象方法出现异常!";
Throwable throwable = e.getCause();
if (throwable instanceof SocketException || throwable instanceof IOException) {
try {
// solrServer的ping调用
pingSolrServer(solrServer);
} catch (Exception ex) {
throw ex;
}
return getQueryResponse(coreName, solrQuery);
}
log.error(error, e);
throw new SolrServerIoException(error);
}
}

/**
* 得到跨core查询返回的结果对象
*
*
@param coreUrls core Url数组,用于跨core查询
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*
@return
* @throws Exception
*/
private QueryResponse getMoreCoreQueryResponse(String[] coreUrls, String criteria, String coreName, int rows,
int start, String sortParams, String[] fields, String[] aliasFields) throws Exception {
SolrServer solrServer = getSolrServer(coreName);
// 获取多core查询参数
ModifiableSolrParams solrParams = getMoreCoreModifiableSolrParams(coreUrls, criteria, rows, start, sortParams,
fields, aliasFields);
QueryRequest queryRequest = new QueryRequest(solrParams);
queryRequest.setMethod(SolrRequest.METHOD.POST);
try {
// if (log.isDebugEnabled()) {total++;}
QueryResponse response = queryRequest.process(solrServer);
return response;
} catch (SolrServerException | HttpSolrServer.RemoteSolrException e) {
String error = "solr工具类-执行得到solr的查询返回结果对象方法出现异常!";
Throwable throwable = e.getCause();
if (throwable instanceof SocketException || throwable instanceof IOException) {
try {
// solrServer的ping调用
pingSolrServer(solrServer);
} catch (Exception ex) {
throw ex;
}
return getMoreCoreQueryResponse(coreUrls, criteria, coreName, rows, start, sortParams, fields,
aliasFields);
}
log.error(error, e);
throw new SolrServerIoException(error);
}
}

/**
* 获取多core查询参数
*
*
@param coreUrls core Url数组,用于跨core查询
*
@param criteria 查询条件
*
@param rows 查询的行数
*
@param start 开始位置
*
@param sortParams 排序参数
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*
@return
*/
private ModifiableSolrParams getMoreCoreModifiableSolrParams(String[] coreUrls, String criteria, int rows,
int start, String sortParams, String[] fields, String[] aliasFields) {
ModifiableSolrParams solrParams = new ModifiableSolrParams();
solrParams.set("q", criteria);
solrParams.set("sort", sortParams); // 排序
solrParams.set("start", start); // 记录开始位置
solrParams.set("rows", rows); // 查询的行数
solrParams.set("shards.info", true);
solrParams.set("shards", StringUtils.join(coreUrls, ","));
fields = this.getAliasFieldsArray(fields, aliasFields);
if (fields != null && fields.length > 0) {
solrParams.set("fl", StringUtils.join(fields, ",")); // 设置过滤字段
}

return solrParams;
}

/**
* 得到solrClient的solrUrl
*
*
@return
*/
public String getSolrUrl() {
return solrUrl;
}

/**
* solrServer的ping调用<br>
* 网络断开连接不上时,调用ping方法检查网络情况。在设置的次数范围之内能ping通网络,则继续进行往下执行地址匹配流程,否则抛出异常。
*
*
@param solrServer solr服务对象
*
@return
* @throws Exception
*/
private synchronized SolrServer pingSolrServer(SolrServer solrServer) throws Exception {
try {
solrServer.ping();
} catch (SolrServerException | IOException | HttpSolrServer.RemoteSolrException e) {
// 循环次数,网络出现异常情况下,循环设置的次数回调ping方法。超过设置的次数,将直接抛出异常。
int loopNum = 0;
if (solrLoopNumMap.containsKey(solrUrl)) {
loopNum = solrLoopNumMap.get(solrUrl) + 1;
} else {
loopNum++;
}
solrLoopNumMap.put(solrUrl, loopNum);

// TODO 由于类代码长度限制1500行,目前暂时以简单方式读取配置值,后期再优化
// solr的ping循环次数,默认为5
if (pingLoopNum <= 0) {
try {
pingLoopNum = Integer.parseInt(ConfigUtil.getPropsValueByKey("ac.httpSolr.pingLoopNum"));
//pingLoopNum = Integer.parseInt(CommonString.pingLoopNum);
pingLoopNum = pingLoopNum <= 0 ? 5 : pingLoopNum;
} catch (NumberFormatException ex) {
ex.printStackTrace();
pingLoopNum = 5;
}
}
// solr的ping睡眠时间,以毫秒为单位,默认为10秒
if (pingSleepTime <= 0) {
try {
pingSleepTime = Long.parseLong(ConfigUtil.getPropsValueByKey("ac.httpSolr.pingSleepTime"));
//pingSleepTime = Long.parseLong(CommonString.pingSleepTime);
pingSleepTime = pingSleepTime <= 0 ? 10000 : pingSleepTime;
} catch (NumberFormatException ex) {
ex.printStackTrace();
pingSleepTime = 10000;
}
}
// sol的ping循环次数超过设置的值,抛出SolrHttpException异常
if (loopNum > pingLoopNum) {
log.warn("执行solrServer的ping方法超过" + pingLoopNum + "次仍然出现网络异常!调用URL为:" + solrUrl);
String error = String.format("solr工具类-执行solrServer的ping方法出现异常!原因是solr【%s】没有启动或网络不通", solrUrl + "/");
log.error(error, e);
throw new SolrHttpException(error);
}
// 睡眠,单位为毫秒
TimeUnit.MILLISECONDS.sleep(pingSleepTime);

log.info("网络出现异常!再次执行solrServer的ping方法,调用次数为:" + loopNum + ",调用URL为:" + solrUrl);
return pingSolrServer(solrServer);
}
solrLoopNumMap.put(solrUrl, 0);
return solrServer;
}

/**
* 空间检索
*
@param coreName
*
@param solrQuery
*
@return
* @throws Exception
*/
public QueryResponse spaceSearch(String coreName, SolrQuery solrQuery) throws Exception {
if (StringUtils.isNotEmpty(coreName)) {
try {
QueryResponse response = getQueryResponse(coreName, solrQuery);
return response;
} catch (SolrServerException | HttpSolrServer.RemoteSolrException e) {
String error = "solr工具类-执行得到solr的查询返回结果对象方法出现异常!";
Throwable throwable = e.getCause();
if (throwable instanceof SocketException || throwable instanceof IOException) {
try {
SolrServer solrServer = getSolrServer(coreName);
// solrServer的ping调用
pingSolrServer(solrServer);
} catch (Exception ex) {
throw ex;
}
return spaceSearch(coreName, solrQuery);
}
log.error(error, e);
throw new SolrServerIoException(error);
}
}
return null;
}




}
作者 east
Java 12月 7,2020

非spring容器下的配置工具类

1、按照spring boot的约定,配置文件名称默认是classpath根路径下的application.properties,并按其约定加载多个配置文件
2、按指定配置文件的路径及名称读取配置,一般是兼容旧工程没有按spring boot约定命名的配置文件 <br>
3、新增读取yml配置文件的功能,默认先读取properties文件,如果不存在再读取yml文件

public final class ConfigUtil {

    private static Logger log = LoggerFactory.getLogger(DataBaseUtil.class);

    /**
     * spring boot默认的配置文件名称
     */
    private static final String CONFIG_FILENAME = "application";

    private static final String DEFAULT_SUFFIX = ".properties";

    private static final String YML_SUFFIX = ".yml";

    /**
     * spring boot通过application.properties配置文件中的spring.profiles.active配置项值指定生效的配置文件名称<br>
     * 如spring.profiles.active=business,表示application-business.properties配置文件生效
     */
    private static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";

    /**
     * spring boot约定命名格式"application-{profile}.properties"来定义多个配置文件,其中{profile}是可变部分
     */
    private static final String SPRING_BOOT_PROFILE_TEMPLATE = "application-{profile}";

    /**
     * 属性对象集合,注意这里是线程安全的
     */
    private static Map<String, Properties> props = new ConcurrentHashMap<>();

    /** 私有的构造函数 */
    private ConfigUtil() {

    }

    /**
     * 加载指定配置文件中的配置
     * 
     * @param fileName
     * @return
     */
    private static Properties loadPropFromFile(String fileName) {
        Properties prop = new Properties();

        try {
            prop.load(ConfigUtil.class.getClassLoader().getResourceAsStream(fileName));
        } catch (IOException e) {
            e.printStackTrace();
            String error = "配置文件工具类加载配置文件" + fileName + "出现异常!";
            log.error(e.getMessage()+":===:"+error);
            throw new RuntimeException(error);
        }
        return prop;
    }

    /**
     * 将yml格式文件加载到Properties中
     * 
     * @param fileName 文件名
     * @return
     */
    private static Properties loadYmlFromFile(String fileName) {
        Properties prop = new Properties();
        LoaderOptions options = new LoaderOptions();
        // 是否允许读取重复key值,默认为true
        options.setAllowDuplicateKeys(false);
        Yaml yaml = new Yaml(options);

        // 加载yml文件流
        InputStream inStream = ConfigUtil.class.getClassLoader().getResourceAsStream(fileName);
        // 按照文件编码读取流
        Reader reader = new UnicodeReader(inStream);
        Iterable<Object> it = yaml.loadAll(reader);
        Map<String, Object> result = new LinkedHashMap<>();

        for (Object object : it) {
            // 构建map类型数据
            buildFlattenedMap(result, asMap(object), null);
        }
        prop.putAll(result);
        return prop;
    }

    /**
     * 递归转换map源数据,将递归的key值用"."拼接成最终目标map的key值,最终的value为目标map的value值
     * 
     * @param result
     * @param source
     * @param path
     */
    @SuppressWarnings("unchecked")
    private static void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, String path) {
        source.forEach((key, value) -> {

            if (hasText(path)) {
                if (key.startsWith("[")) {
                    key = path + key;
                } else {
                    // 分层读取key值,使用"."拼接
                    key = path + '.' + key;
                }
            }

            if (value instanceof String) {
                result.put(key, value);
            } else if (value instanceof Map) {
                // Need a compound key
                Map<String, Object> map = (Map<String, Object>) value;
                // 递归构造map
                buildFlattenedMap(result, map, key);
            } else if (value instanceof Collection) {
                // Need a compound key
                Collection<Object> collection = (Collection<Object>) value;

                if (collection.isEmpty()) {
                    result.put(key, "");
                } else {
                    int count = 0;
                    for (Object object : collection) {
                        buildFlattenedMap(result, Collections.singletonMap("[" + (count++) + "]", object), key);
                    }
                }
            } else {
                result.put(key, (value != null ? value : ""));
            }
        });
    }

    /**
     * 将object数据转换为Map
     * 
     * @param object
     * @return
     */
    @SuppressWarnings("unchecked")
    private static Map<String, Object> asMap(Object object) {
        // YAML can have numbers as keys
        Map<String, Object> result = new LinkedHashMap<>();

        if (!(object instanceof Map)) {
            // A document can be a text literal
            result.put("document", object);
            return result;
        }
        Map<Object, Object> map = (Map<Object, Object>) object;

        map.forEach((key, value) -> {

            if (value instanceof Map) {
                value = asMap(value);
            }

            if (key instanceof CharSequence) {
                result.put(key.toString(), value);
            } else {
                // It has to be a map key in this case
                result.put("[" + key.toString() + "]", value);
            }
        });
        return result;
    }

    // 判断字符串是否有值(不为空、不为null、必须包含字符串)
    private static boolean hasText(@Nullable String str) {
        return (str != null && !str.isEmpty() && containsText(str));
    }

    // 是否包含字符
    private static boolean containsText(CharSequence str) {
        int strLen = str.length();

        for (int i = 0; i < strLen; i++) {

            if (!Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 根据属性key得到对应的属性值<br>
     * 配置文件按spring boot的约定的配置文件命名规则进行加载<br>
     * <font color=red>注意: 暂不支持yml文件属性读取</font>
     * 
     * @param key 键名称
     * @return
     */
    public static String getPropsValueByKey(String key) {
        // TODO 暂时不支持读取yml格式文件,yml文件支持map和list格式数据,需要另写方法支持
        if (!props.containsKey(CONFIG_FILENAME)) {
            Properties prop = new Properties();
            prop = getPropertiesByFileName(CONFIG_FILENAME);

            if (prop.get(SPRING_PROFILES_ACTIVE) != null) {
                // 依次读取指定的配置文件
                for (String partName : prop.get(SPRING_PROFILES_ACTIVE).toString().split(",")) {
                    prop.putAll(getPropertiesByFileName(SPRING_BOOT_PROFILE_TEMPLATE.replace("{profile}", partName)));
                }
            }
            props.put(CONFIG_FILENAME, prop);
        }
        Object obj = props.get(CONFIG_FILENAME).get(key);
        if (obj == null) {
            return null;
        } else {
            return obj.toString();
        }
    }

    /**
     * 根据配置文件名称和属性key得到对应的属性值.<br>
     * 一般用于非spring boot约定的配置文件名称,比如集成其它系统的功能时,有固定名称的配置文件,或兼容旧系统<br>
     * <font color=red>注意: 暂不支持yml文件属性读取</font>
     * 
     * @param configFileName 配置文件名称
     * @param key 属性key值
     * @return
     * 
     */
    public static String getPropsValueByKey(String configFileName, String key) {
        // TODO 暂时不支持读取yml格式文件,yml文件支持map和list格式数据,需要另写方法支持
        if (!props.containsKey(configFileName)) {
            props.put(configFileName, loadPropFromFile(configFileName));
        }
        return props.get(configFileName).getProperty(key);
    }

    /**
     * 根据文件名称获取Properties
     * 
     * @param fileName 文件名
     * @return
     */
    private static Properties getPropertiesByFileName(String fileName) {
        String fileFullName = fileName + DEFAULT_SUFFIX;
        InputStream inputStream = ConfigUtil.class.getClassLoader().getResourceAsStream(fileFullName);

        if (inputStream == null) {
            fileFullName = fileName + YML_SUFFIX;
            // 读取yml配置文件
            inputStream = ConfigUtil.class.getClassLoader().getResourceAsStream(fileFullName);
            return loadYmlFromFile(fileFullName);
        } else {
            // 读取properties文件
            return loadPropFromFile(fileFullName);
        }
    }

    public static String getBody(String bodyName,JSONObject jsonObject) {
        String body = (String) jsonObject.get(bodyName);
        String reqBody = body;
        String pattern = "\\$\\{.*?\\}";
        Pattern r = Pattern.compile(pattern);
        Matcher matcher = r.matcher(body);
        while (matcher.find()) {
            String substring = body.substring(matcher.start(), matcher.end());
            String s = getPropsValueByKey(substring.substring(2, substring.length() - 1));
            reqBody = reqBody.replace(substring, s);
        }
        return reqBody;
    }

}
作者 east
Android 12月 7,2020

Android绘制饼图例子

public class MeFragment extends Fragment {
private View view;
private PieChartView pieChart;
PieChartData pieChardata;
List<SliceValue> values = new ArrayList<SliceValue>();
private int[] data = {21, 20, 9, 2, 8};
private int[] colorData = {Color.parseColor("#ec063d"),
Color.parseColor("#f1c704"),
Color.parseColor("#c9c9c9"),
Color.parseColor("#2bc208"),
Color.parseColor("#333333")};
private String[] stateChar = {"提示", "故障", "离线", "正常", "未激活"};

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_me, null);
setPieChartData();
initPieChart();
pieChart.setOnValueTouchListener(selectListener);//设置点击事件监听
return view;
}

public void initPieChart() {
pieChart = (PieChartView) view.findViewById(R.id.chart);
pieChardata = new PieChartData();
pieChardata.setHasLabels(false);//显示表情
pieChardata.setHasLabelsOnlyForSelected(false);//不用点击显示占的百分比
pieChardata.setHasLabelsOutside(false);//占的百分比是否显示在饼图外面
pieChardata.setHasCenterCircle(true);//是否是环形显示
pieChardata.setValues(values);//填充数据
pieChardata.setCenterCircleColor(Color.WHITE);//设置环形中间的颜色
pieChardata.setCenterCircleScale(0.7f);//设置环形的大小级别
pieChart.setPieChartData(pieChardata);
pieChart.setValueSelectionEnabled(true);//选择饼图某一块变大
pieChart.setAlpha(0.9f);//设置透明度
pieChart.setCircleFillRatio(1f);//设置饼图大小
}

private void setPieChartData() {
for (int i = 0; i < data.length; ++i) {
SliceValue sliceValue = new SliceValue((float) data[i], colorData[i]);
values.add(sliceValue);
}
}

private PieChartOnValueSelectListener selectListener = new PieChartOnValueSelectListener() {

@Override
public void onValueDeselected() {
// TODO Auto-generated method stub

}

@Override
public void onValueSelected(int arg0, SliceValue value) {
//选择对应图形后,在中间部分显示相应信息
pieChardata.setCenterText1(stateChar[arg0]);
pieChardata.setCenterText1Color(colorData[arg0]);
pieChardata.setCenterText1FontSize(10);
pieChardata.setCenterText2(value.getValue() + "(" + calPercent(arg0) + ")");
pieChardata.setCenterText2Color(colorData[arg0]);
pieChardata.setCenterText2FontSize(12);
Toast.makeText(getContext(), stateChar[arg0] + ":" + value.getValue(), Toast.LENGTH_SHORT).show();
}
};

private String calPercent(int i) {
String result = "";
int sum = 0;
for (int i1 = 0; i1 < data.length; i1++) {
sum += data[i1];
}
result = String.format("%.2f", (float) data[i] * 100 / sum) + "%";
return result;
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/base"
android:gravity="center"
android:padding="10dp">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/me_title"
android:textColor="@color/white"
android:textSize="@dimen/title" />
</LinearLayout>

<LinearLayout
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<lecho.lib.hellocharts.view.PieChartView
android:id="@+id/chart"
android:layout_width="200dp"
android:layout_height="200dp" />
</LinearLayout>

</LinearLayout>
作者 east
Android 12月 7,2020

Android获取用户的地理位置工具类

public class GPSUtils {

private static GPSUtils instance;
private Context mContext;
private LocationManager locationManager;

private GPSUtils(Context context) {
this.mContext = context;
}

public static GPSUtils getInstance(Context context) {
if (instance == null) {
instance = new GPSUtils(context);
}
return instance;
}

/**
* 获取经纬度
*
* @return
*/
public String getLngAndLat(OnLocationResultListener onLocationResultListener) {
double latitude = 0.0;
double longitude = 0.0;

mOnLocationListener = onLocationResultListener;

String locationProvider = null;
locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
//获取所有可用的位置提供器
List<String> providers = locationManager.getProviders(true);

if (providers.contains(LocationManager.GPS_PROVIDER)) {
//如果是GPS
locationProvider = LocationManager.GPS_PROVIDER;
} else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
//如果是Network
locationProvider = LocationManager.NETWORK_PROVIDER;
} else {
Intent i = new Intent();
i.setAction(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(i);
return null;
}

//获取Location
Location location = locationManager.getLastKnownLocation(locationProvider);
if (location != null) {
//不为空,显示地理位置经纬度
if (mOnLocationListener != null) {
mOnLocationListener.onLocationResult(location);
}

}
//监视地理位置变化
locationManager.requestLocationUpdates(locationProvider, 3000, 1, locationListener);
return null;
}


public LocationListener locationListener = new LocationListener() {

// Provider的状态在可用、暂时不可用和无服务三个状态直接切换时触发此函数
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {

}

// Provider被enable时触发此函数,比如GPS被打开
@Override
public void onProviderEnabled(String provider) {

}

// Provider被disable时触发此函数,比如GPS被关闭
@Override
public void onProviderDisabled(String provider) {

}

//当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发
@Override
public void onLocationChanged(Location location) {
if (mOnLocationListener != null) {
mOnLocationListener.OnLocationChange(location);
}
}
};

public void removeListener() {
locationManager.removeUpdates(locationListener);
}

private OnLocationResultListener mOnLocationListener;

public interface OnLocationResultListener {
void onLocationResult(Location location);

void OnLocationChange(Location location);
}
}
作者 east
Android 12月 7,2020

Android下载工具类

如果要下载在外部存储空间,要看外部存储空间是否可用。

public class DownloadUtil {
private static DownloadUtil downloadUtil;
private final OkHttpClient okHttpClient;

public static DownloadUtil get() {
if (downloadUtil == null) {
downloadUtil = new DownloadUtil();
}
return downloadUtil;
}

private DownloadUtil() {
okHttpClient = new OkHttpClient();
}

/**
* @param url 下载连接
* @param destFileDir 下载的文件储存目录
* @param destFileName 下载文件名称
* @param listener 下载监听
*/
public void download(final String url, final String destFileDir, final String destFileName, final OnDownloadListener listener) {
Request request = new Request.Builder().url(url).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 下载失败监听回调
listener.onDownloadFailed(e);
}

@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
// 储存下载文件的目录
File dir = new File(getSDPath(destFileDir));
if (!dir.exists()) {
boolean b= dir.mkdirs();
}
File file = new File(dir, destFileName);
try {
is = response.body().byteStream();
long total = response.body().contentLength();
fos = new FileOutputStream(file);
long sum = 0;
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
sum += len;
int progress = (int) (sum * 1.0f / total * 100);
// 下载中更新进度条
listener.onDownloading(progress);
}
fos.flush();
// 下载完成
listener.onDownloadSuccess(file);
} catch (Exception e) {
listener.onDownloadFailed(e);
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
try {
if (fos != null)
fos.close();
} catch (IOException e) {
}
}
}
});
}

public interface OnDownloadListener {
/**
* @param file 下载成功后的文件
*/
void onDownloadSuccess(File file);

/**
* @param progress 下载进度
*/
void onDownloading(int progress);

/**
* @param e 下载异常信息
*/
void onDownloadFailed(Exception e);
}

public String getSDPath(String dirName) {
File sdDir = null;
boolean sdCardExist = Environment.getExternalStorageState()
.equals(android.os.Environment.MEDIA_MOUNTED);
if (sdCardExist) {
sdDir = Environment.getExternalStorageDirectory();//获取根目录
Log.e("Test", "外部存储可用..." + sdDir.toString());
}
return sdDir.toString()+"/"+dirName;
}
}
作者 east
Java 12月 7,2020

Excel快速导入导出工具(仅提供excel2007以及更高版本的支持)

public class ExcelWriter {

/** 日志 */
private static Logger LOG = LoggerFactory.getLogger(ExcelWriter.class);

/** 原生HttpServletResponse对象 */
private HttpServletResponse response = null;

/**
* 用于导入excel的构造函数
*/
private ExcelWriter() {

}

/**
* 用于导出excel的构造函数 <br>
* List(Bean)格式
*
* @param clazz
* @param response
*/
private ExcelWriter(Class<?> clazz, HttpServletResponse response) {
// this.clazz == clazz;
this.response = response;
}

/**
* 用于导出excel的构造函数 <br>
* List(String[]) 格式
*
* @param response
*/
private ExcelWriter(HttpServletResponse response) {
this.response = response;
}

/**
* 用于浏览器导出excel的实例化
*
* @param clazz 实体Class对象
* @param response 原生HttpServletResponse对象
* @return ExcelUtils
*/
public static ExcelWriter exportInit(HttpServletResponse response) {
return new ExcelWriter(response);
}

/**
* 用于浏览器导出excel的实例化
*
* @param clazz 实体Class对象
* @param response 原生HttpServletResponse对象
* @return ExcelUtils
*/
public static ExcelWriter exportInit(Class<?> clazz, HttpServletResponse response) {
return new ExcelWriter(clazz, response);
}

/**
* 导出Excel,响应到浏览器下载(List<String[]>格式)
*
* @param headers 表头
* @param data 数据
* @param fileName 文件名称
* @param sheetName 工作表名
* @return
*/
public boolean writeExcel(List<String> headers, List<String[]> datas, String fileName, String sheetName) {
if (response == null) {
throw new IllegalArgumentException("请先使用exportInit(HttpServletResponse)初始化参数。");
}
long begin = System.currentTimeMillis();

if (ListUtils.isEmpty(datas)) {
LOG.error("没有检测到数据,不执行导出操作。");
return false;
}
int dataSize = datas.size();
LOG.info(String.format("即将导出excel数据 : %s条,请稍后...", dataSize));
try {
// 输出流
OutputStream outputStream = response.getOutputStream();
// 创建新的工作薄
SXSSFWorkbook wb = PoiUtils.newSXSSFWorkbook();
// 表头的样式
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
// 对齐
cellStyle.setAlignment(CellStyle.ALIGN_LEFT);
font.setBoldweight(Font.BOLDWEIGHT_NORMAL);
// 字体大小
font.setFontHeightInPoints((short) 12);
font.setFontName("宋体");
// 应用标题字体到标题样式
cellStyle.setFont(font);
// 设置单元格文本形式
DataFormat dataFormat = wb.createDataFormat();
cellStyle.setDataFormat(dataFormat.getFormat("@"));

// 表头的值
String headerValue = null;
// 单元格的值
String cellValue = null;
// 创建sheet
SXSSFSheet sheet = PoiUtils.newSXSSFSheet(wb, sheetName);
// 创建行表头
SXSSFRow headerRow = PoiUtils.newSXSSFRow(sheet, 0);
for (int i = 0, size = headers.size(); i < size; i++) {
headerValue = headers.get(i);
SXSSFCell cell = PoiUtils.newSXSSFCell(headerRow, i);
cell.setCellValue(headerValue);
cell.setCellStyle(cellStyle);
}

// 明细行数组
String[] bodys = null;
for (int i = 0; i < dataSize; i++) {
// 创建行数据
SXSSFRow bodyRow = PoiUtils.newSXSSFRow(sheet, i + 1);
bodys = datas.get(i);
for (int j = 0, len = bodys.length; j < len; j++) {
cellValue = bodys[j];
if (StringUtils.isBlank(cellValue)) {
// FIXME: 当值为""时,当前index的cell会失效,这里设置为null
cellValue = null;
}

// 创建列
SXSSFCell cell = PoiUtils.newSXSSFCell(bodyRow, j);
cell.setCellValue(cellValue);
cell.setCellType(SXSSFCell.CELL_TYPE_STRING);
}
}

writeByBrowser(response, fileName, wb, outputStream);
LOG.info(String.format("excel处理完成,共生成数据 : %s行,耗时 : %s seconds.", dataSize,
(System.currentTimeMillis() - begin) / 1000.0));
} catch (Exception e) {
LOG.error("导出excel失败 : " + e.getMessage(), e);
}
return true;
}

/**
* 响应到浏览器下载
*
* @param response
* @param fileName
* @param wb
* @param out
* @throws Exception
*/
private static void writeByBrowser(HttpServletResponse response, String fileName, SXSSFWorkbook wb,
OutputStream out) throws Exception {
// response对象不为空,响应到浏览器下载
if (response != null) {
response.setContentType(ConstantUtils.CONTENT_TYPE);
response.setCharacterEncoding("UTF-8");
fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1") + ConstantUtils.XLSX_SUFFIX;
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
if (out == null) {
out = response.getOutputStream();
}
}
wb.write(out);
out.flush();
out.close();
}

/**
* 保存文件到本地
*
* @param headers 表头
* @param datas 转换好的数据
* @param filePath 路径
* @param sheetName 工作表名
* @param type 类型 0-转换成功 1-转换失败
*/
public static void saveByLocal(List<String> headers, List<String[]> datas, String filePath, int type) {
long begin = System.currentTimeMillis();

if (ListUtils.isEmpty(datas)) {
LOG.error("没有检测到数据,不执行导出操作。");
return;
}
int dataSize = datas.size();
LOG.info(String.format("即将保存excel数据 : %s条,请稍后...", dataSize));
// 输出流
FileOutputStream fileOutputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
fileOutputStream = new FileOutputStream(filePath);
bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
String sheetName = "";
if (type == 0) {
sheetName = PropertyUtils.getProperty("successConversion");
} else if (type == 1) {
sheetName = PropertyUtils.getProperty("failConversion");
}
// 创建新的工作薄
SXSSFWorkbook wb = PoiUtils.newSXSSFWorkbook();
// 表头的样式
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
// 对齐
cellStyle.setAlignment(CellStyle.ALIGN_LEFT);
font.setBoldweight(Font.BOLDWEIGHT_NORMAL);
// 字体大小
font.setFontHeightInPoints((short) 12);
font.setFontName("宋体");
// 应用标题字体到标题样式
cellStyle.setFont(font);
// 设置单元格文本形式
DataFormat dataFormat = wb.createDataFormat();
cellStyle.setDataFormat(dataFormat.getFormat("@"));

// 表头的值
String headerValue = null;
// 单元格的值
String cellValue = null;
// 创建sheet
SXSSFSheet sheet = PoiUtils.newSXSSFSheet(wb, sheetName);
// 创建行表头
SXSSFRow headerRow = PoiUtils.newSXSSFRow(sheet, 0);
for (int i = 0, size = headers.size(); i < size; i++) {
headerValue = headers.get(i);
SXSSFCell cell = PoiUtils.newSXSSFCell(headerRow, i);
cell.setCellValue(headerValue);
cell.setCellStyle(cellStyle);
}

// 明细行数组
String[] bodys = null;
for (int i = 0; i < dataSize; i++) {
// 创建行数据
SXSSFRow bodyRow = PoiUtils.newSXSSFRow(sheet, i + 1);
bodys = datas.get(i);
for (int j = 0, len = bodys.length; j < len; j++) {
cellValue = bodys[j];
if (StringUtils.isBlank(cellValue)) {
// FIXME: 当值为""时,当前index的cell会失效,这里设置为null
cellValue = null;
}

// 创建列
SXSSFCell cell = PoiUtils.newSXSSFCell(bodyRow, j);
cell.setCellValue(cellValue);
cell.setCellType(SXSSFCell.CELL_TYPE_STRING);
}
}

wb.write(bufferedOutputStream);
LOG.info(String.format("excel处理完成,共生成数据 : %s行,耗时 : %s seconds.", dataSize,
(System.currentTimeMillis() - begin) / 1000.0));
} catch (Exception e) {
LOG.error("保存excel失败 : " + e.getMessage(), e);
} finally {
if (bufferedOutputStream != null) {
try {
bufferedOutputStream.close();
} catch (IOException e) {
LOG.error("文件流关闭异常");
}
}
}
}

}
作者 east
Java 12月 7,2020

GAT1400视图库没有视图库账户对接遇到的坑

没有视图库账号对接视图库,把DeviceId没有写自己的视图库账号,错误写成要对接的视图库账号,一直返回“用户名错误”。后来才知道,只要按照GAT1400标准,20位的视图库账号可以自己按照标准进行编写一个,然后让要对接的视图库厂商后台配置一下。

作者 east
Java 12月 7,2020

用AOP写的日志管理工具

 使用AOP,将日志记录,异常报告等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。 
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

@Component
@Aspect
public class LogManageUtil {
private static Logger logger = LoggerFactory.getLogger(LogManageUtil.class);
private static final String serverName = "cloudsearch";
private static final Integer showRows = 5;

@Pointcut("execution(* com.funny.services.web..*.*(..))")
public void controllerLogProxy() {

}

@Before("controllerLogProxy()")
public void beforeLogInfo(JoinPoint joinPoint) {
Object[] params = joinPoint.getArgs();
String paramStr = "";
for (int i = 0; i < params.length; i++) {
Object param = params[i];
if (i == 0){
logger.info(param.getClass().getSimpleName());
if(param.getClass().getSimpleName().equals("SessionRepositoryRequestWrapper")) {
paramStr = "该接口无参数";
}else{
paramStr += JSONObject.toJSONString(param);
}
}

else
paramStr += "," + JSONObject.toJSONString(param);
}
logger.info("service:[" + serverName + "] -- API:[" + getApiUrl() + "] 请求参数:" + paramStr);
}

@AfterReturning(pointcut = "controllerLogProxy()", returning = "result")
public void afterLogInfo(Object result) {
if (result instanceof ResultData) {
ResultData tmpRs = (ResultData) result;
ResultData rs = new ResultData();
BeanUtils.copyProperties(tmpRs, rs);
if (rs.getCode()==ResultStatus.SUCCESS_CODE) {
if (rs.getData() instanceof List) {
List<Object> list = (List) rs.getData();
List<Object> tmpList = new ArrayList<>();
if (list != null && (list.size() > showRows)) {
for (int i = 0; i < showRows; i++) {
tmpList.add(list.get(i));
}
Object more = "more...";
tmpList.add(more);
rs.setData(tmpList);
}
}
}
logger.info("service:[" + serverName + "] -- API:[" + getApiUrl() + "] 返回结果:" + JSONObject.toJSONString(rs));
}
}

public static String getApiUrl() {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
String url = request.getRequestURL().toString();
int startIndex = url.indexOf(serverName);
String ApiUrl = url.substring(startIndex + serverName.length(), url.length());
return ApiUrl;
}

public static void serviceErrorPrint(Exception e) {
logger.error("service:[" + serverName + "] -- API:[" + getApiUrl() + "] 错误信息:" + e.getMessage());
}


}
作者 east

上一 1 2 3

关注公众号“大模型全栈程序员”回复“小程序”获取1000个小程序打包源码。回复”chatgpt”获取免注册可用chatgpt。回复“大数据”获取多本大数据电子书

标签

AIGC AI创作 bert chatgpt github GPT-3 gpt3 GTP-3 hive mysql O2O tensorflow UI控件 不含后台 交流 共享经济 出行 图像 地图定位 外卖 多媒体 娱乐 小程序 布局 带后台完整项目 开源项目 搜索 支付 效率 教育 日历 机器学习 深度学习 物流 用户系统 电商 画图 画布(canvas) 社交 签到 联网 读书 资讯 阅读 预订

官方QQ群

小程序开发群:74052405

大数据开发群: 952493060

近期文章

  • 详解Python当中的pip常用命令
  • AUTOSAR如何在多个供应商交付的配置中避免ARXML不兼容?
  • C++thread pool(线程池)设计应关注哪些扩展性问题?
  • 各类MCAL(Microcontroller Abstraction Layer)如何与AUTOSAR工具链解耦?
  • 如何设计AUTOSAR中的“域控制器”以支持未来扩展?
  • C++ 中避免悬挂引用的企业策略有哪些?
  • 嵌入式电机:如何在低速和高负载状态下保持FOC(Field-Oriented Control)算法的电流控制稳定?
  • C++如何在插件式架构中使用反射实现模块隔离?
  • C++如何追踪内存泄漏(valgrind/ASan等)并定位到业务代码?
  • C++大型系统中如何组织头文件和依赖树?

文章归档

  • 2025年6月
  • 2025年5月
  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年1月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年7月
  • 2018年6月

分类目录

  • Android (73)
  • bug清单 (79)
  • C++ (34)
  • Fuchsia (15)
  • php (4)
  • python (43)
  • sklearn (1)
  • 云计算 (20)
  • 人工智能 (61)
    • chatgpt (21)
      • 提示词 (6)
    • Keras (1)
    • Tensorflow (3)
    • 大模型 (1)
    • 智能体 (4)
    • 深度学习 (14)
  • 储能 (44)
  • 前端 (4)
  • 大数据开发 (488)
    • CDH (6)
    • datax (4)
    • doris (30)
    • Elasticsearch (15)
    • Flink (78)
    • flume (7)
    • Hadoop (19)
    • Hbase (23)
    • Hive (40)
    • Impala (2)
    • Java (71)
    • Kafka (10)
    • neo4j (5)
    • shardingsphere (6)
    • solr (5)
    • Spark (99)
    • spring (11)
    • 数据仓库 (9)
    • 数据挖掘 (7)
    • 海豚调度器 (10)
    • 运维 (34)
      • Docker (3)
  • 小游戏代码 (1)
  • 小程序代码 (139)
    • O2O (16)
    • UI控件 (5)
    • 互联网类 (23)
    • 企业类 (6)
    • 地图定位 (9)
    • 多媒体 (6)
    • 工具类 (25)
    • 电商类 (22)
    • 社交 (7)
    • 行业软件 (7)
    • 资讯读书 (11)
  • 嵌入式 (70)
    • autosar (63)
    • RTOS (1)
    • 总线 (1)
  • 开发博客 (16)
    • Harmony (9)
  • 技术架构 (6)
  • 数据库 (32)
    • mongodb (1)
    • mysql (13)
    • pgsql (2)
    • redis (1)
    • tdengine (4)
  • 未分类 (6)
  • 程序员网赚 (20)
    • 广告联盟 (3)
    • 私域流量 (5)
    • 自媒体 (5)
  • 量化投资 (4)
  • 面试 (14)

功能

  • 登录
  • 文章RSS
  • 评论RSS
  • WordPress.org

All Rights Reserved by Gitweixin.本站收集网友上传代码, 如有侵犯版权,请发邮件联系yiyuyos@gmail.com删除.