diff --git a/src/main/java/com/yutou/napcat/NapCatQQ.java b/src/main/java/com/yutou/napcat/NapCatQQ.java index 3cb9ebf..8a2c312 100644 --- a/src/main/java/com/yutou/napcat/NapCatQQ.java +++ b/src/main/java/com/yutou/napcat/NapCatQQ.java @@ -10,6 +10,7 @@ import com.yutou.napcat.http.NapCatApi; import com.yutou.qqbot.QQBotManager; import com.yutou.qqbot.utlis.Base64Tools; import lombok.val; +import okhttp3.Headers; import java.io.File; import java.util.ArrayList; @@ -42,7 +43,7 @@ public class NapCatQQ { .build() ).enqueue(new HttpCallback() { @Override - public void onResponse(int code, String status, SendMessageResponse response, String rawResponse) { + public void onResponse(Headers headers, int code, String status, SendMessageResponse response, String rawResponse) { System.out.println("code = " + code + ", status = " + status + ", response = " + response + ", rawResponse = " + rawResponse); } diff --git a/src/main/java/com/yutou/okhttp/FileBody.java b/src/main/java/com/yutou/okhttp/FileBody.java new file mode 100644 index 0000000..f492250 --- /dev/null +++ b/src/main/java/com/yutou/okhttp/FileBody.java @@ -0,0 +1,11 @@ +package com.yutou.okhttp; + +import lombok.Data; + +import java.io.InputStream; + +@Data +public class FileBody { + InputStream inputStream; + T t; +} diff --git a/src/main/java/com/yutou/okhttp/FileCallback.java b/src/main/java/com/yutou/okhttp/FileCallback.java new file mode 100644 index 0000000..742d8d9 --- /dev/null +++ b/src/main/java/com/yutou/okhttp/FileCallback.java @@ -0,0 +1,112 @@ +package com.yutou.okhttp; + +import com.yutou.qqbot.utlis.Log; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public abstract class FileCallback implements Callback> { + + private static ThreadPoolExecutor executor; + private final T bean; + private String savePath; + + public FileCallback(T bean, String savePath) { + this.bean = bean; + this.savePath = savePath; + if (executor == null) { + executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue(100)); + } + } + + private class DownloadTask implements Runnable { + private T bean; + private Headers headers; + private HttpUrl url; + private InputStream inputStream; + + public DownloadTask(T bean, Headers headers, HttpUrl url, InputStream inputStream) { + this.bean = bean; + + this.headers = headers; + this.url = url; + this.inputStream = inputStream; + } + + @Override + public void run() { + try { + Log.i("开始下载"); + File file = new File(savePath); + onStart(bean); + if (!file.exists()) { + boolean mkdirs = file.getParentFile().mkdirs(); + Log.i("mkdirs = " + mkdirs); + } + FileOutputStream outputStream = new FileOutputStream(file); + int len; + long total = 0; + byte[] bytes = new byte[4096]; + boolean isDownload = true; + long available = inputStream.available(); + while ((len = inputStream.read(bytes)) != -1 && isDownload) { + total += len; + outputStream.write(bytes, 0, len); + outputStream.flush(); + isDownload = onDownload(headers, bean, total, available); + } + Log.i("下载完成"); + outputStream.close(); + } catch (Exception e) { + Log.e(e); + onFailure(bean,e); + } finally { + onFinish(bean); + try { + inputStream.close(); + } catch (IOException e) { + Log.e(e); + } + } + + + } + + } + + public abstract void onStart(T bean); + + public abstract boolean onDownload(Headers headers, T bean, long len, long total); + + public abstract void onFinish(T bean); + + public abstract void onFailure(T bean, Throwable throwable); + + @Override + public void onResponse(Call> call, Response> response) { + try { + executor.execute(new DownloadTask(bean, response.headers(), call.request().url(), response.body().getInputStream())); + } catch (Exception e) { + Log.e(e); + onFailure(bean,e); + call.cancel(); + } + } + + @Override + public void onFailure(Call> call, Throwable throwable) { + onFailure(bean, throwable); + call.cancel(); + } + +} diff --git a/src/main/java/com/yutou/okhttp/GetRequestParams.java b/src/main/java/com/yutou/okhttp/GetRequestParams.java index 4008e27..c574f91 100644 --- a/src/main/java/com/yutou/okhttp/GetRequestParams.java +++ b/src/main/java/com/yutou/okhttp/GetRequestParams.java @@ -1,6 +1,7 @@ package com.yutou.okhttp; +import okhttp3.Headers; import okhttp3.HttpUrl; import okhttp3.Request; @@ -14,12 +15,18 @@ public class GetRequestParams implements IRequestParam { * @return */ @Override - public Request getRequest(HashMap map, Request request) { + public Request getRequest(HashMap headerMap, HashMap map, Request request) { + Headers.Builder headerBuild = request.headers().newBuilder(); + if (!headerMap.isEmpty()) { + for (String key : headerMap.keySet()) { + headerBuild.add(key, headerMap.get(key)); + } + } //添加公共参数 HttpUrl.Builder builder = request.url().newBuilder(); for (String key : map.keySet()) { builder.addQueryParameter(key, String.valueOf(map.get(key))); } - return request.newBuilder().url(builder.build()).build(); + return request.newBuilder().url(builder.build()).headers(headerBuild.build()).build(); } } \ No newline at end of file diff --git a/src/main/java/com/yutou/okhttp/HttpCallback.java b/src/main/java/com/yutou/okhttp/HttpCallback.java index 577bf1c..147a502 100644 --- a/src/main/java/com/yutou/okhttp/HttpCallback.java +++ b/src/main/java/com/yutou/okhttp/HttpCallback.java @@ -1,12 +1,13 @@ package com.yutou.okhttp; +import okhttp3.Headers; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public abstract class HttpCallback implements Callback> { - public abstract void onResponse(int code, String status, T response, String rawResponse); + public abstract void onResponse(Headers headers,int code, String status, T response, String rawResponse); public abstract void onFailure(Throwable throwable); @@ -14,6 +15,7 @@ public abstract class HttpCallback implements Callback> { public void onResponse(Call> call, Response> response) { if (response.body() != null) { onResponse( + response.headers(), response.body().getRetcode(), response.body().getStatus(), response.body().getData(), @@ -22,10 +24,12 @@ public abstract class HttpCallback implements Callback> { } else { onFailure(new NullPointerException("response body is null")); } + call.cancel(); } @Override public void onFailure(Call> call, Throwable throwable) { onFailure(throwable); + call.cancel(); } } diff --git a/src/main/java/com/yutou/okhttp/HttpDownloadUtils.java b/src/main/java/com/yutou/okhttp/HttpDownloadUtils.java new file mode 100644 index 0000000..b55fb55 --- /dev/null +++ b/src/main/java/com/yutou/okhttp/HttpDownloadUtils.java @@ -0,0 +1,145 @@ +package com.yutou.okhttp; + +import com.yutou.qqbot.interfaces.DownloadInterface; +import com.yutou.qqbot.utlis.ConfigTools; +import com.yutou.qqbot.utlis.Log; +import lombok.Data; +import okhttp3.*; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.StringUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +public class HttpDownloadUtils { + public static void download(Builder builder) { + createHttpClient(builder).enqueue(new Callback() { + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + if (builder.downloadInterface != null) { + builder.downloadInterface.onError(e); + } + } + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + InputStream inputStream = Objects.requireNonNull(response.body()).byteStream(); + File target; + if (StringUtils.hasText(builder.fileName)) { + target = new File(builder.path, builder.fileName); + } else { + target = new File(builder.path); + } + FileOutputStream fileOutputStream = new FileOutputStream(target); + if (builder.downloadInterface != null) { + builder.downloadInterface.onDownloadStart(); + } + try { + byte[] buffer = new byte[2048]; + int len; + long soFarBytes = 0; + long totalBytes = inputStream.available(); + while ((len = inputStream.read(buffer)) != -1) { + fileOutputStream.write(buffer, 0, len); + soFarBytes += len; + if (builder.downloadInterface != null) { + if (!builder.downloadInterface.onDownloading(soFarBytes, totalBytes)) { + break; + } + } + } + fileOutputStream.flush(); + } catch (IOException e) { + Log.e(e,"download error:", builder.url); + } finally { + if (builder.downloadInterface != null) { + builder.downloadInterface.onDownload(target); + } + } + + } + }); + } + + public static File downloadSync(Builder builder) { + try { + Response response = createHttpClient(builder).execute(); + InputStream inputStream = Objects.requireNonNull(response.body()).byteStream(); + File target; + if (StringUtils.hasText(builder.fileName)) { + target = new File(builder.path, builder.fileName); + } else { + target = new File(builder.path); + } + try (FileOutputStream fileOutputStream = new FileOutputStream(target)) { + byte[] buffer = new byte[2048]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + fileOutputStream.write(buffer, 0, len); + } + fileOutputStream.flush(); + return target; + } catch (IOException e) { + Log.e(e,"download error:" , builder.url); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return null; + } + + private static Call createHttpClient(Builder builder) { + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .connectTimeout(2, TimeUnit.MINUTES) + .readTimeout(2, TimeUnit.MINUTES) + .build(); + Request.Builder rb = new Request.Builder() + .get() + .addHeader("User-Agent", ConfigTools.getUserAgent()) + .url(builder.url); + if (StringUtils.hasText(builder.cookie)) { + rb.addHeader("Set-Cookie", builder.cookie); + rb.addHeader("Cookie", builder.cookie); + } + Request request = rb.build(); + return okHttpClient.newCall(request); + } + + @Data + public static class Builder { + String url; + String path; + String fileName; + DownloadInterface downloadInterface; + String cookie; + + public Builder setUrl(String url) { + this.url = url; + return this; + } + + public Builder setPath(String path) { + this.path = path; + return this; + } + + public Builder setFileName(String fileName) { + this.fileName = fileName; + return this; + } + + public Builder setDownloadInterface(DownloadInterface downloadInterface) { + this.downloadInterface = downloadInterface; + return this; + } + + public Builder setCookie(String cookie) { + this.cookie = cookie; + return this; + } + } +} diff --git a/src/main/java/com/yutou/okhttp/HttpLoggingInterceptor.java b/src/main/java/com/yutou/okhttp/HttpLoggingInterceptor.java index 2284923..376f082 100644 --- a/src/main/java/com/yutou/okhttp/HttpLoggingInterceptor.java +++ b/src/main/java/com/yutou/okhttp/HttpLoggingInterceptor.java @@ -1,34 +1,25 @@ package com.yutou.okhttp; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - import com.yutou.qqbot.utlis.Log; -import okhttp3.Connection; -import okhttp3.Headers; -import okhttp3.Interceptor; -import okhttp3.MediaType; -import okhttp3.Protocol; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; +import lombok.val; +import okhttp3.*; import okhttp3.internal.http.HttpHeaders; import okio.Buffer; import okio.BufferedSource; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; public class HttpLoggingInterceptor implements Interceptor { private static final String TAG = "HttpLogging"; - private static final Charset UTF8 = Charset.forName("UTF-8"); + private static final Charset UTF8 = StandardCharsets.UTF_8; - private volatile Level printLevel = Level.NONE; + private volatile Level printLevel = Level.BODY; private java.util.logging.Level colorLevel; private Logger logger; @@ -63,7 +54,7 @@ public class HttpLoggingInterceptor implements Interceptor { private void log(String message) { //logger.log(colorLevel, message); if (prLog) { - Log.i(TAG, message); + Log.getDynamicLogger(TAG).info(message); } //Log.e(TAG,message); } @@ -71,8 +62,11 @@ public class HttpLoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); - if (request.body().contentLength() == 0) { - request = chain.call().request(); + if (request.body() != null && request.body().contentLength() == 0) { + val headers = request.headers(); + request = chain.call().request().newBuilder() + .headers(headers) + .build(); } if (printLevel == Level.NONE) { return chain.proceed(request); @@ -109,8 +103,6 @@ public class HttpLoggingInterceptor implements Interceptor { if (logHeaders) { if (hasRequestBody) { - // Request body headers are only present when installed as a network interceptor. Force - // them to be included (when available) so there values are known. if (requestBody.contentType() != null) { log("\tContent-Type: " + requestBody.contentType()); } @@ -121,7 +113,6 @@ public class HttpLoggingInterceptor implements Interceptor { Headers headers = request.headers(); for (int i = 0, count = headers.size(); i < count; i++) { String name = headers.name(i); - // Skip headers from the request body as they are explicitly logged above. if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) { log("\t" + name + ": " + headers.value(i)); } @@ -151,7 +142,7 @@ public class HttpLoggingInterceptor implements Interceptor { boolean logHeaders = (printLevel == Level.BODY || printLevel == Level.HEADERS); try { - log("<-- " + clone.code() + ' ' + clone.message() + ' ' + clone.request().url() + " (" + tookMs + "ms)"); + log("<-- " + clone.code() + ' ' + clone.message() + ' ' + clone.request().url() + " (" + tookMs + "ms)"); if (logHeaders) { Headers headers = clone.headers(); for (int i = 0, count = headers.size(); i < count; i++) { @@ -162,12 +153,13 @@ public class HttpLoggingInterceptor implements Interceptor { if (responseBody == null) return response; if (isPlaintext(responseBody.contentType())) { - byte[] bytes = responseBody.byteStream().readAllBytes(); - MediaType contentType = responseBody.contentType(); - String body = new String(bytes, getCharset(contentType)); + BufferedSource source = responseBody.source(); + source.request(Long.MAX_VALUE); // 请求整个流 + Buffer buffer = source.buffer(); + + Charset charset = getCharset(responseBody.contentType()); + String body = buffer.clone().readString(charset); log("\tbody:" + body); - responseBody = ResponseBody.create(responseBody.contentType(), bytes); - return response.newBuilder().body(responseBody).build(); } else { log("\tbody: maybe [binary body], omitted!"); } @@ -214,6 +206,8 @@ public class HttpLoggingInterceptor implements Interceptor { body.writeTo(buffer); Charset charset = getCharset(body.contentType()); log("\tbody:" + buffer.readString(charset)); + // 重置请求体以确保后续处理不受影响 + buffer.clear(); } catch (Exception e) { logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); } diff --git a/src/main/java/com/yutou/okhttp/IRequestParam.java b/src/main/java/com/yutou/okhttp/IRequestParam.java index 42ec4cc..c1ac1e5 100644 --- a/src/main/java/com/yutou/okhttp/IRequestParam.java +++ b/src/main/java/com/yutou/okhttp/IRequestParam.java @@ -5,5 +5,5 @@ import okhttp3.Request; import java.util.HashMap; public interface IRequestParam { - Request getRequest(HashMap map, Request request); + Request getRequest(HashMap header, HashMap map, Request request); } \ No newline at end of file diff --git a/src/main/java/com/yutou/okhttp/ParamsContext.java b/src/main/java/com/yutou/okhttp/ParamsContext.java index ca43ba4..cb6229d 100644 --- a/src/main/java/com/yutou/okhttp/ParamsContext.java +++ b/src/main/java/com/yutou/okhttp/ParamsContext.java @@ -1,5 +1,6 @@ package com.yutou.okhttp; +import com.yutou.qqbot.utlis.ConfigTools; import okhttp3.Request; import java.util.HashMap; @@ -7,14 +8,25 @@ import java.util.HashMap; public class ParamsContext { private IRequestParam iRequestParam; private Request request; - private HashMap map; + private HashMap map; + private HashMap headerMap; - public ParamsContext(HashMap map,Request request) { - if(map==null){ - map=new HashMap<>(); + public ParamsContext(HashMap map, Request request) { + if (map == null) { + map = new HashMap<>(); } - this.map=map; + this.map = map; this.request = request; + this.headerMap = new HashMap<>(); + } + + public ParamsContext(HashMap headerMap, HashMap map, Request request) { + if (map == null) { + map = new HashMap<>(); + } + this.map = map; + this.request = request; + this.headerMap = headerMap; } public Request getInRequest() { @@ -26,6 +38,7 @@ public class ParamsContext { iRequestParam = new PostRequestParams(); break; } - return iRequestParam.getRequest(map,request); + headerMap.put("User-Agent", ConfigTools.getUserAgent()); + return iRequestParam.getRequest(headerMap, map, request); } } \ No newline at end of file diff --git a/src/main/java/com/yutou/okhttp/PostRequestParams.java b/src/main/java/com/yutou/okhttp/PostRequestParams.java index 8cf9aca..baa37af 100644 --- a/src/main/java/com/yutou/okhttp/PostRequestParams.java +++ b/src/main/java/com/yutou/okhttp/PostRequestParams.java @@ -2,22 +2,26 @@ package com.yutou.okhttp; import com.alibaba.fastjson2.JSONObject; - +import com.yutou.qqbot.utlis.Log; import okhttp3.*; import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.Set; public class PostRequestParams implements IRequestParam { @Override - public Request getRequest(HashMap map, Request request) { + public Request getRequest(HashMap headerMap, HashMap map, Request request) { + Headers.Builder headerBuilder = request.headers().newBuilder(); + if (!headerMap.isEmpty()) { + for (String key : headerMap.keySet()) { + headerBuilder.add(key, headerMap.get(key)); + } + } if (request.body() instanceof FormBody) { FormBody.Builder bodyBuilder = new FormBody.Builder(); FormBody formBody = (FormBody) request.body(); - for (int i = 0; i < formBody.size(); i++) { bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i)); } @@ -25,10 +29,12 @@ public class PostRequestParams implements IRequestParam { bodyBuilder.addEncoded(key, String.valueOf(map.get(key))); } formBody = bodyBuilder.build(); - request = request.newBuilder().post(formBody).build(); + request = request.newBuilder().headers(headerBuilder.build()).post(formBody).build(); } else if (request.body() != null) { - RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),toUrlParams(map)); - request = request.newBuilder().post(request.body()) + RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), toUrlParams(map)); + request = request.newBuilder() + .headers(headerBuilder.build()) + .post(request.body()) .post(requestBody).build(); } return request; @@ -41,9 +47,9 @@ public class PostRequestParams implements IRequestParam { try { string.append("&").append(key).append("=").append(URLEncoder.encode(json.getString(key), "UTF-8")); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); try { - string.append("&").append(URLEncoder.encode(key,"UTF-8")).append("="); + string.append("&").append(URLEncoder.encode(key, "UTF-8")).append("="); // string += "&" + key + "="; } catch (Exception e1) { string.append("&").append(key).append("="); @@ -56,7 +62,7 @@ public class PostRequestParams implements IRequestParam { } public static String toUrlParams(Map map) { - if(map.isEmpty()){ + if (map.isEmpty()) { return ""; } StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/com/yutou/okhttp/api/BaseApi.java b/src/main/java/com/yutou/okhttp/api/BaseApi.java index c45ec46..3535bb0 100644 --- a/src/main/java/com/yutou/okhttp/api/BaseApi.java +++ b/src/main/java/com/yutou/okhttp/api/BaseApi.java @@ -1,16 +1,16 @@ package com.yutou.okhttp.api; +import com.alibaba.fastjson2.JSONObject; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.internal.bind.DateTypeAdapter; +import com.yutou.okhttp.HttpBody; import com.yutou.okhttp.HttpLoggingInterceptor; import com.yutou.okhttp.ParamsContext; import com.yutou.okhttp.converter.JsonCallAdapter; import com.yutou.okhttp.converter.JsonConverterFactory; -import okhttp3.Interceptor; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; +import lombok.val; +import okhttp3.*; import retrofit2.CallAdapter; import retrofit2.Converter; import retrofit2.Retrofit; @@ -22,27 +22,50 @@ import java.util.logging.Level; public class BaseApi { private String URL; - private HashMap params; + private HashMap params = new HashMap<>(); + private HashMap headers = new HashMap<>(); + public BaseApi setURL(String URL) { this.URL = URL; return this; } + public BaseApi setHeaders(HashMap headers) { + this.headers = headers; + return this; + } + + public void addHeader(HashMap headers) { + this.headers.putAll(headers); + } + + public BaseApi setParams(HashMap params) { this.params = params; return this; } + public void useCookie(JSONObject json) { + StringBuilder ck = new StringBuilder(); + json.remove("sql_time"); + json.remove("gourl"); + for (String key : json.keySet()) { + ck.append(key).append("=").append(json.getString(key)).append(";"); + } + headers.put("Cookie", ck.toString()); + setHeaders(headers); + } + /** * 创建一个接口方法 * - * @param okHttpClient okhttp客户端 - * @param converterFactory 处理工厂类 + * @param okHttpClient okhttp客户端 + * @param converterFactory 处理工厂类 * @param callAdapterFactory 请求适配器工厂 - * @param baseUrl 基础地质 - * @param service 接口 - * @param 接口泛型 + * @param baseUrl 基础地质 + * @param service 接口 + * @param 接口泛型 * @return 接口 */ public T create(OkHttpClient okHttpClient, Converter.Factory converterFactory, CallAdapter.Factory callAdapterFactory, String baseUrl, Class service) { @@ -74,7 +97,6 @@ public class BaseApi { loggingInterceptor.setPrintLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient.Builder builder = new OkHttpClient() .newBuilder() - .addInterceptor(initQuery()) .addInterceptor(loggingInterceptor); return create(builder.build(), @@ -83,14 +105,30 @@ public class BaseApi { URL, apiClass); } + public Interceptor initQuery() { Interceptor addQueryParameterInterceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); //配置公共参数 - request = new ParamsContext(params,request).getInRequest(); - return chain.proceed(request); + request = new ParamsContext(headers, params, request).getInRequest(); + val proceed = chain.proceed(request); + if (!proceed.isSuccessful()) { + HttpBody httpBody = new HttpBody<>(); + httpBody.setCode(200); + val parse = JSONObject.parse(proceed.body().string()); + httpBody.setRetcode(parse.getInteger("code")); + httpBody.setMsg(parse.getString("message")); + ResponseBody errorResponseBody = ResponseBody.create( + JSONObject.toJSONString(httpBody), + MediaType.get("application/json; charset=utf-8")); + val newResponse=proceed.newBuilder() + .code(200) + .body(errorResponseBody).build(); + return newResponse; + } + return proceed; } }; return addQueryParameterInterceptor; diff --git a/src/main/java/com/yutou/okhttp/converter/JsonResponseBodyConverter.java b/src/main/java/com/yutou/okhttp/converter/JsonResponseBodyConverter.java index e8c9eac..34e7685 100644 --- a/src/main/java/com/yutou/okhttp/converter/JsonResponseBodyConverter.java +++ b/src/main/java/com/yutou/okhttp/converter/JsonResponseBodyConverter.java @@ -31,12 +31,26 @@ public class JsonResponseBodyConverter implements Converter HttpBody body; try { body = JSONObject.parseObject(string, type); + if(body.getData()==null){ + JSONObject jt=JSONObject.parseObject(JSONObject.toJSONString(new HttpBody<>())); + jt.put("data",JSONObject.parseObject(string)); + HttpBody bt=JSONObject.parseObject(jt.toJSONString(),type); + body.setData(bt.getData()); + } body.setSrc(string); return (T) body; } catch (Exception e) { e.printStackTrace(); - body = new HttpBody(); - body.setSrc(string); + try { + body = new HttpBody<>(); + body.setData(JSONObject.parseObject(string, type)); + body.setSrc(string); + } catch (Exception e2) { + e2.printStackTrace(); + body = new HttpBody<>(); + body.setSrc(string); + } + } return (T) body; diff --git a/src/main/java/com/yutou/qqbot/QQBotManager.java b/src/main/java/com/yutou/qqbot/QQBotManager.java index 5e12b8b..74409d5 100644 --- a/src/main/java/com/yutou/qqbot/QQBotManager.java +++ b/src/main/java/com/yutou/qqbot/QQBotManager.java @@ -15,6 +15,7 @@ import com.yutou.okhttp.HttpCallback; import com.yutou.qqbot.data.MessageChainBuilder; import com.yutou.qqbot.interfaces.ObjectInterface; import com.yutou.qqbot.utlis.*; +import okhttp3.Headers; import retrofit2.Response; @@ -47,7 +48,7 @@ public class QQBotManager { sendMessage(true, 583819556L, "姬妻酱上线拉~☆Daze~ 当前版本:" + QQBotApplication.version); NapCatApi.getGroupApi().getGroupList().enqueue(new HttpCallback>() { @Override - public void onResponse(int code, String status, List response, String rawResponse) { + public void onResponse(Headers headers, int code, String status, List response, String rawResponse) { for (GroupBean groupBean : response) { QQDatabase.addGroup(groupBean.getGroupId(), groupBean); QQNumberManager.getManager().addNumber(groupBean.getGroupId(), true); @@ -61,7 +62,7 @@ public class QQBotManager { }); NapCatApi.getFriendApi().getFriendList().enqueue(new HttpCallback>() { @Override - public void onResponse(int code, String status, List response, String rawResponse) { + public void onResponse(Headers headers, int code, String status, List response, String rawResponse) { for (FriendBean friendBean : response) { QQDatabase.addUser(friendBean.getUserId(), friendBean); QQNumberManager.getManager().addNumber(friendBean.getUserId(), false); @@ -75,7 +76,7 @@ public class QQBotManager { }); NapCatApi.getUtilsApi().getLoginInfo().enqueue(new HttpCallback() { @Override - public void onResponse(int code, String status, FriendBean response, String rawResponse) { + public void onResponse(Headers headers,int code, String status, FriendBean response, String rawResponse) { QQDatabase.setMe(response); } @@ -214,7 +215,7 @@ public class QQBotManager { public void groupBan(long qqGroup, long user, int timer, ObjectInterface objectInterface) { NapCatApi.getGroupApi().groupBan(qqGroup, user, timer).enqueue(new HttpCallback() { @Override - public void onResponse(int code, String status, BaseBean response, String rawResponse) { + public void onResponse(Headers headers,int code, String status, BaseBean response, String rawResponse) { if (objectInterface != null) { objectInterface.out("1"); } @@ -240,7 +241,7 @@ public class QQBotManager { NapCatApi.getGroupApi().setGroupSpecialTitle(group, user, title, duration).enqueue(new HttpCallback() { @Override - public void onResponse(int code, String status, BaseBean response, String rawResponse) { + public void onResponse(Headers headers,int code, String status, BaseBean response, String rawResponse) { } diff --git a/src/main/java/com/yutou/qqbot/data/gpt/OpenAiBean.java b/src/main/java/com/yutou/qqbot/data/gpt/OpenAiBean.java new file mode 100644 index 0000000..9d085b2 --- /dev/null +++ b/src/main/java/com/yutou/qqbot/data/gpt/OpenAiBean.java @@ -0,0 +1,90 @@ +package com.yutou.qqbot.data.gpt; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.Data; + +import java.util.List; + +@Data +public class OpenAiBean { + @JSONField(name = "id") + private String id; + + @JSONField(name = "choices") + private List choices; + + @JSONField(name = "tool_calls") + private List toolCalls; + + @JSONField(name = "usage") + private Usage usage; + + @JSONField(name = "created") + private long created; + + @JSONField(name = "model") + private String model; + + @JSONField(name = "object") + private String object; + + @JSONField(name = "system_fingerprint") + private String systemFingerprint; + + @Data + public static class Choice { + @JSONField(name = "index") + private int index; + + @JSONField(name = "message") + private Message message; + + @JSONField(name = "finish_reason") + private String finishReason; + } + + @Data + public static class Message { + @JSONField(name = "role") + private String role; + + @JSONField(name = "content") + private String content; + + @JSONField(name = "reasoning_content") + private String reasoningContent; + } + + @Data + public static class ToolCall { + @JSONField(name = "id") + private String id; + + @JSONField(name = "type") + private String type; + + @JSONField(name = "function") + private Function function; + } + + @Data + public static class Function { + @JSONField(name = "name") + private String name; + + @JSONField(name = "arguments") + private String arguments; + } + + @Data + public static class Usage { + @JSONField(name = "prompt_tokens") + private int promptTokens; + + @JSONField(name = "completion_tokens") + private int completionTokens; + + @JSONField(name = "total_tokens") + private int totalTokens; + } +} diff --git a/src/main/java/com/yutou/qqbot/gpt/AbsGPTManager.java b/src/main/java/com/yutou/qqbot/gpt/AbsGPTManager.java index 98b9593..cd15311 100644 --- a/src/main/java/com/yutou/qqbot/gpt/AbsGPTManager.java +++ b/src/main/java/com/yutou/qqbot/gpt/AbsGPTManager.java @@ -3,14 +3,35 @@ package com.yutou.qqbot.gpt; import com.yutou.qqbot.data.baidu.Message; import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; public abstract class AbsGPTManager { protected static final AtomicInteger MAX_MESSAGE = new AtomicInteger(20); + protected final ConcurrentHashMap> msgMap= new ConcurrentHashMap<>(); + // 新增锁映射表 + protected final ConcurrentHashMap userLocks = new ConcurrentHashMap<>(); + protected String model ; + + /** * 清除与GPT管理器相关的所有缓存或状态信息。 */ - public abstract void clear(); + /** + * 这里确实是需要清空所有数据 + */ + public synchronized void clear() { // 添加同步 + msgMap.clear(); + for (AtomicBoolean value : userLocks.values()) { + value.set(false); + } + userLocks.forEachValue(1, atomicBoolean -> atomicBoolean.set(false)); + userLocks.clear(); + } /** * 发送消息到指定用户。 @@ -54,6 +75,17 @@ public abstract class AbsGPTManager { */ public abstract int setMaxMessageCount(int count); + public List getMessageList(String user){ + List list = msgMap.computeIfAbsent(user, k -> Collections.synchronizedList(new ArrayList<>())); + // 限制历史消息的最大数量 + synchronized (list) { + if (list.size() >= MAX_MESSAGE.get()) { + int removeCount = list.size() - MAX_MESSAGE.get() + 1; // 腾出空间给新消息 + list.subList(0, removeCount).clear(); + } + } + return list; + } /** * 根据指定的类获取相应的GPT管理器实例。 * @@ -63,6 +95,8 @@ public abstract class AbsGPTManager { public static AbsGPTManager getManager(Class tClass) { if (tClass == BaiduGPTManager.class) { return BaiduGPTManager.getManager(); + }else if(tClass== SiliconGPTManager.class){ + return SiliconGPTManager.getInstance(); } return new AbsGPTManager() { @Override @@ -97,4 +131,8 @@ public abstract class AbsGPTManager { }; } + protected AbsGPTManager setModel(String model) { + this.model=model; + return this; + } } diff --git a/src/main/java/com/yutou/qqbot/gpt/BaiduGPTManager.java b/src/main/java/com/yutou/qqbot/gpt/BaiduGPTManager.java index e0169c5..56c23a8 100644 --- a/src/main/java/com/yutou/qqbot/gpt/BaiduGPTManager.java +++ b/src/main/java/com/yutou/qqbot/gpt/BaiduGPTManager.java @@ -29,16 +29,12 @@ public class BaiduGPTManager extends AbsGPTManager { //ConfigTools.load操作可以确保获取到相关参数,所以无需关心 private static final String AccessKey = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_ACCESS_KEY, String.class); private static final String SecretKey = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_SECRET_KEY, String.class); - private final ConcurrentHashMap> msgMap; private final static String modelFor40 = "ERNIE-4.0-8K"; private final static String modelFor35 = "ERNIE-Speed-128K"; - private String model = modelFor35; - // 新增锁映射表 - private final ConcurrentHashMap userLocks = new ConcurrentHashMap<>(); + private final Qianfan qianfan; private BaiduGPTManager() { - msgMap = new ConcurrentHashMap<>(); qianfan = new Qianfan(AccessKey, SecretKey); String savedVersion = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, String.class); if (StringUtils.isEmpty(savedVersion) || (!"3.5".equals(savedVersion) && !"4.0".equals(savedVersion))) { @@ -77,18 +73,6 @@ public class BaiduGPTManager extends AbsGPTManager { ConfigTools.save(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, "3.5"); } - /** - * 这里确实是需要清空所有数据 - */ - @Override - public synchronized void clear() { // 添加同步 - msgMap.clear(); - for (AtomicBoolean value : userLocks.values()) { - value.set(false); - } - userLocks.forEachValue(1, atomicBoolean -> atomicBoolean.set(false)); - userLocks.clear(); - } // 这个是官方的示例代码,表示连续对话 @@ -115,15 +99,8 @@ public class BaiduGPTManager extends AbsGPTManager { return Message.create("您有请求正在处理中,请稍后再试", true); } try { - List list = msgMap.computeIfAbsent(user, k -> Collections.synchronizedList(new ArrayList<>())); - // 限制历史消息的最大数量 - synchronized (list) { - if (list.size() >= MAX_MESSAGE.get()) { - int removeCount = list.size() - MAX_MESSAGE.get() + 1; // 腾出空间给新消息 - list.subList(0, removeCount).clear(); - } - list.add(Message.create(message)); - } + List list = getMessageList(user); + list.add(Message.create(message)); val builder = qianfan.chatCompletion() .model(model); for (Message msg : list) { @@ -202,7 +179,7 @@ public class BaiduGPTManager extends AbsGPTManager { .execute(); String translationPrompt = "将以下英文内容严格翻译为简体中文,不要解释、不要添加额外内容,保留专业术语和名称(如Star Wars保持英文):\n" + response.getResult(); // 获取API返回的结果 - return sendMessage("bot",translationPrompt).getContent(); + return sendMessage("bot", translationPrompt).getContent(); } catch (Exception e) { // 异常处理:记录错误日志 Log.e(e); diff --git a/src/main/java/com/yutou/qqbot/gpt/SiliconGPTManager.java b/src/main/java/com/yutou/qqbot/gpt/SiliconGPTManager.java new file mode 100644 index 0000000..c41409f --- /dev/null +++ b/src/main/java/com/yutou/qqbot/gpt/SiliconGPTManager.java @@ -0,0 +1,117 @@ +package com.yutou.qqbot.gpt; + +import com.yutou.okhttp.HttpLoggingInterceptor; +import com.yutou.qqbot.data.baidu.Message; +import com.yutou.qqbot.data.gpt.OpenAiBean; +import com.yutou.qqbot.http.GPTApi; +import com.yutou.qqbot.http.GPTBuilder; +import lombok.val; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +public class SiliconGPTManager extends AbsGPTManager { + //生成单例模式 + private volatile static SiliconGPTManager instance = new SiliconGPTManager(); + + private SiliconGPTManager() { + } + + public static SiliconGPTManager getInstance() { + if (instance == null) { + synchronized (SiliconGPTManager.class) { + if (instance == null) { + instance = new SiliconGPTManager(); + } + } + } + return instance; + } + + @Override + public synchronized Message sendMessage(String user, String message) { + // 获取或创建用户锁 + AtomicBoolean lock = userLocks.computeIfAbsent(user, k -> new AtomicBoolean(false)); + try { + GPTApi.setLog(false); + // 尝试加锁(如果已被锁定则立即返回提示) + if (!lock.compareAndSet(false, true)) { + return Message.create("您有请求正在处理中,请稍后再试", true); + } + + val builder = GPTBuilder.create(model); + List list=getMessageList(user); + list.add(Message.create(message)); + for (Message msg : list) { + builder.addMessage(msg.getRole(),msg.getContent()); + } + val response = GPTApi.getApi().completions(builder.build()).execute(); + + if (!response.isSuccessful()) { + return Message.create("API请求失败", true); + } + + val body = response.body(); + if (body == null || body.getData() == null) { + return Message.create("API请求为空", true); + } + if (body.getRetcode() != 0) { + return Message.create(body.getMsg(), true); + } + val choices = body.getData().getChoices(); + if (choices == null || choices.isEmpty()) { + return Message.create("没有对话信息", true); + } + + val choice = choices.get(choices.size() - 1); + val bot = Message.create(choice.getMessage().getContent(), true); + + list.add(bot); + return bot; + + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + lock.set(false); + userLocks.remove(user, lock); + } + + } + + @Override + public File textToImage(String user, String text) { + return null; + } + + @Override + public String imageToText(String user, File file) { + return ""; + } + + @Override + public String getGPTVersion() { + return model; + } + + @Override + public int setMaxMessageCount(int count) { + MAX_MESSAGE.set(count); + return count; + } + + public static void main(String[] args) { + String model="THUDM/glm-4-9b-chat"; + val message = AbsGPTManager.getManager(SiliconGPTManager.class) + .setModel(model) + .sendMessage("user", "宫廷玉液酒的下一句是什么?"); + System.out.println(message); + System.out.println(AbsGPTManager.getManager(SiliconGPTManager.class) + .setModel(model) + .sendMessage("user", "宫廷玉液酒减去大锤等于多少") + ); + } +} diff --git a/src/main/java/com/yutou/qqbot/http/GPTApi.java b/src/main/java/com/yutou/qqbot/http/GPTApi.java new file mode 100644 index 0000000..27ce95b --- /dev/null +++ b/src/main/java/com/yutou/qqbot/http/GPTApi.java @@ -0,0 +1,26 @@ +package com.yutou.qqbot.http; + +import com.yutou.okhttp.HttpLoggingInterceptor; +import com.yutou.okhttp.api.BaseApi; +import com.yutou.qqbot.utlis.ConfigTools; +import lombok.val; + +import java.util.HashMap; + +public class GPTApi extends BaseApi { + + public static void setLog(boolean log) { + HttpLoggingInterceptor.setLog(log); + } + + public static SiliconGPTApi getApi() { + val api = new GPTApi(); + api.setURL("https://api.siliconflow.cn/v1/"); +// api.setURL("http://127.0.0.1:8080/"); + HashMap header = new HashMap<>(); + header.put("Authorization", "Bearer sk-dcmhlbhyitcdnjbjfgflhwimahdmygfrcaopzjjcpgsfzmzo"); + header.put("Content-Type", "application/json"); + api.addHeader(header); + return api.createApi(SiliconGPTApi.class); + } +} diff --git a/src/main/java/com/yutou/qqbot/http/GPTBuilder.java b/src/main/java/com/yutou/qqbot/http/GPTBuilder.java new file mode 100644 index 0000000..7ece3bf --- /dev/null +++ b/src/main/java/com/yutou/qqbot/http/GPTBuilder.java @@ -0,0 +1,127 @@ +package com.yutou.qqbot.http; + + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.Data; +import lombok.val; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +@Data +class Message { + @JSONField(name = "role") + String role; + + @JSONField(name = "content") + String content; + + public Message(String role, String content) { + this.role = role; + this.content = content; + } +} + +@Data +class GPTRequest { + @JSONField(name = "model") + String model; + + @JSONField(name = "messages") + List messages = new ArrayList<>(); + + @JSONField(name = "stream") + boolean stream = false; + + @JSONField(name = "max_tokens") + int maxTokens; + + @JSONField(name = "stop") + List stop = new ArrayList<>(List.of("null")); + + @JSONField(name = "temperature") + float temperature ; + + @JSONField(name = "top_p") + float topP ; + + @JSONField(name = "top_k") + int topK ; + + @JSONField(name = "frequency_penalty") + float frequencyPenalty; +} + +public class GPTBuilder { + private final GPTRequest request; + + private GPTBuilder(String model) { + request = new GPTRequest(); + request.model = model; + } + + public static GPTBuilder create(String model) { + return new GPTBuilder(model); + } + + public GPTBuilder addMessage(String content,boolean isGPT) { + request.messages.add(new Message(isGPT ? "assistant" : "user", content)); + return this; + } + + public GPTBuilder addMessage(String role, String content) { + request.messages.add(new Message(role, content)); + return this; + } + + public GPTBuilder setMaxTokens(int maxTokens) { + request.maxTokens = maxTokens; + return this; + } + + public GPTBuilder setStream(boolean stream) { + request.stream = stream; + return this; + } + + public GPTBuilder setTemperature(float temperature) { + request.temperature = temperature; + return this; + } + + public GPTBuilder setTopP(float topP) { + request.topP = topP; + return this; + } + + public GPTBuilder setTopK(int topK) { + request.topK = topK; + return this; + } + + public GPTBuilder setFrequencyPenalty(float frequencyPenalty) { + request.frequencyPenalty = frequencyPenalty; + return this; + } + + // 可以根据需要添加更多配置方法 + + public JSONObject build() { + val json = JSONObject.parse(JSONObject.toJSONString(request)); + // 创建一个迭代器来遍历JSON对象的键 + Iterator keys = json.keySet().iterator(); + while (keys.hasNext()) { + String key = keys.next(); + Object value = json.get(key); + + // 检查值是否为0或为空字符串 + if (value == null || value.equals(0) || (value instanceof String && ((String) value).isEmpty())) { + keys.remove(); // 移除键值对 + } + } + return json; + } +} \ No newline at end of file diff --git a/src/main/java/com/yutou/qqbot/http/SiliconGPTApi.java b/src/main/java/com/yutou/qqbot/http/SiliconGPTApi.java new file mode 100644 index 0000000..ba37599 --- /dev/null +++ b/src/main/java/com/yutou/qqbot/http/SiliconGPTApi.java @@ -0,0 +1,17 @@ +package com.yutou.qqbot.http; + +import com.alibaba.fastjson2.JSONObject; +import com.yutou.napcat.model.MessageBean; +import com.yutou.okhttp.HttpBody; +import com.yutou.qqbot.data.gpt.OpenAiBean; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.POST; + +public interface SiliconGPTApi { + @POST("/chat/completions") + Call> completions( + @Body + JSONObject message + ); +} diff --git a/src/main/java/com/yutou/qqbot/interfaces/DownloadInterface.java b/src/main/java/com/yutou/qqbot/interfaces/DownloadInterface.java index b99018d..5550a6b 100644 --- a/src/main/java/com/yutou/qqbot/interfaces/DownloadInterface.java +++ b/src/main/java/com/yutou/qqbot/interfaces/DownloadInterface.java @@ -3,7 +3,9 @@ package com.yutou.qqbot.interfaces; import java.io.File; public abstract class DownloadInterface { - public void onDownloading(double soFarBytes, double totalBytes){}; public void onDownload(File file){}; public void onError(Exception e){}; + public void onDownloadStart(){} + public boolean onDownloading(double soFarBytes, double totalBytes){return true;} + } diff --git a/src/main/java/com/yutou/qqbot/models/Commands/QQBean.java b/src/main/java/com/yutou/qqbot/models/Commands/QQBean.java index 094274d..d8a7113 100644 --- a/src/main/java/com/yutou/qqbot/models/Commands/QQBean.java +++ b/src/main/java/com/yutou/qqbot/models/Commands/QQBean.java @@ -12,6 +12,7 @@ import com.yutou.qqbot.QQBotManager; import com.yutou.qqbot.interfaces.ObjectInterface; import com.yutou.qqbot.models.Model; import com.yutou.qqbot.utlis.RedisTools; +import okhttp3.Headers; import java.text.SimpleDateFormat; import java.util.*; @@ -103,7 +104,7 @@ public class QQBean extends Model { void releaseAll(long qq, boolean isRelease) { QQBotManager.getInstance().getShutUpList(qq, new HttpCallback>() { @Override - public void onResponse(int code, String status, List response, String rawResponse) { + public void onResponse(Headers headers, int code, String status, List response, String rawResponse) { List shutList = new ArrayList<>(); for (GroupUserBean bean : response) { if (bean.getShutUpTimestamp() > 60) { @@ -124,7 +125,7 @@ public class QQBean extends Model { for (GroupUserBean bean : shutList) { NapCatApi.getGroupApi().groupBan(qq, bean.getUserId(), 0).enqueue(new HttpCallback() { @Override - public void onResponse(int code, String status, BaseBean response, String rawResponse) { + public void onResponse(Headers headers, int code, String status, BaseBean response, String rawResponse) { } diff --git a/src/main/java/com/yutou/qqbot/utlis/ConfigTools.java b/src/main/java/com/yutou/qqbot/utlis/ConfigTools.java index ed308bb..bd07edf 100644 --- a/src/main/java/com/yutou/qqbot/utlis/ConfigTools.java +++ b/src/main/java/com/yutou/qqbot/utlis/ConfigTools.java @@ -142,4 +142,11 @@ public class ConfigTools { public static String getServerUrl() { return ConfigTools.load(CONFIG, SERVER_URL, String.class); } + public static String getUserAgent() { + String ua=load(CONFIG,"userAgent",String.class); + if(!org.springframework.util.StringUtils.hasText(ua)){ + ua="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"; + } + return ua; + } } diff --git a/src/main/java/com/yutou/qqbot/utlis/DynamicLogFile.java b/src/main/java/com/yutou/qqbot/utlis/DynamicLogFile.java index 39805d9..6b6fbed 100644 --- a/src/main/java/com/yutou/qqbot/utlis/DynamicLogFile.java +++ b/src/main/java/com/yutou/qqbot/utlis/DynamicLogFile.java @@ -8,6 +8,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.appender.ConsoleAppender; import org.apache.logging.log4j.core.appender.RollingFileAppender; import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy; import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy; @@ -80,8 +81,17 @@ public class DynamicLogFile { .withPolicy(triggeringPolicy) .build(); + // 创建控制台Appender + Appender consoleAppender = ConsoleAppender.newBuilder() + .setName(loggerName + "-console") + .setLayout(layout) + .setTarget(ConsoleAppender.Target.SYSTEM_OUT) + .build(); + appender.start(); + consoleAppender.start(); config.addAppender(appender); + config.addAppender(consoleAppender); // 获取Logger对象 org.apache.logging.log4j.core.Logger coreLogger = context.getLogger(loggerName); @@ -91,6 +101,7 @@ public class DynamicLogFile { // 将Appender添加到Logger对象中 coreLogger.addAppender(appender); + coreLogger.addAppender(consoleAppender); coreLogger.setLevel(Level.ALL); coreLogger.setAdditive(false); diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 801c1d5..3e2926a 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -28,7 +28,7 @@ - +