Content-Type为null报 'mediatype' must not be empty错

本文详细介绍了Spring框架中一个关于Content-Type为null时报错的bug,该问题已在Spring 3.2.12版本中得到修复。文章提供了两种解决方案:一是给Content-Type赋值或设置为空字符串;二是升级Spring到最新版本。

see

https://jira.spring.io/browse/SPR-12173

spring bug

Content-Type为null时会报错,为空不会报错

spring自3.2.12已修复该bug

解决办法:

1/Content-Type 赋值或者给空字符串

2/升级spring

转载于:https://www.cnblogs.com/haoyunfeng/p/9551914.html

package jnpf.util.wxutil; import com.alibaba.fastjson.JSONObject; import com.google.common.collect.ImmutableMap; import jnpf.util.*; import jnpf.util.context.SpringContext; import lombok.Cleanup; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import javax.net.ssl.SSLContext; import java.io.*; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * @author JNPF开发平台组 * @version V3.1.0 * @copyright 引迈信息技术有限公司 * @date 2021/3/16 8:49 */ @Slf4j public class HttpUtil { private HttpUtil() { throw new IllegalAccessError("工具类不能实例化"); } private static PoolingHttpClientConnectionManager connectionManager = null; private static RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(5000) .setConnectTimeout(5000) .setConnectionRequestTimeout(3000) .build(); static { SSLContext sslcontext = SSLContexts.createSystemDefault(); Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", new SSLConnectionSocketFactory(sslcontext)).build(); connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); connectionManager.setMaxTotal(1000); // 每个路由最大的请求数量 connectionManager.setDefaultMaxPerRoute(200); } public static CloseableHttpClient getHttpClient() { return getHttpClientBuilder().build(); } public static CloseableHttpClient getHttpClient(SSLContext sslContext) { return getHttpClientBuilder(sslContext).build(); } public static HttpClientBuilder getHttpClientBuilder() { return HttpClients.custom().setConnectionManager(connectionManager).setDefaultRequestConfig(requestConfig); } public static HttpClientBuilder getHttpClientBuilder(SSLContext sslContext) { if (sslContext != null) { return getHttpClientBuilder().setSSLContext(sslContext); } else { return getHttpClientBuilder(); } } /** * post 请求 * * @param httpUrl 请求地址 * @param sslContext ssl证书信息 * @return */ public static String sendHttpPost(String httpUrl, SSLContext sslContext) { // 创建httpPost HttpPost httpPost = new HttpPost(httpUrl); return sendHttpPost(httpPost, sslContext); } /** * 发送 post请求 * * @param httpUrl 地址 */ public static String sendHttpPost(String httpUrl) { // 创建httpPost HttpPost httpPost = new HttpPost(httpUrl); return sendHttpPost(httpPost, null); } /** * 发送 post请求 * * @param httpUrl 地址 * @param params 参数(格式:key1=value1&key2=value2) */ public static String sendHttpPost(String httpUrl, String params) { return sendHttpPost(httpUrl, params, null); } /** * 发送 post请求 * * @param httpUrl 地址 * @param params 参数(格式:key1=value1&key2=value2) * @param sslContext ssl证书信息 */ public static String sendHttpPost(String httpUrl, String params, SSLContext sslContext) { // 创建httpPost HttpPost httpPost = new HttpPost(httpUrl); try { // 设置参数 StringEntity stringEntity = new StringEntity(params, Constants.UTF8); stringEntity.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE); httpPost.setEntity(stringEntity); } catch (Exception e) { log.error(e.getMessage(), e); } return sendHttpPost(httpPost, sslContext); } /** * 发送 post请求 * * @param httpUrl 地址 * @param maps 参数 */ public static String sendHttpPost(String httpUrl, Map<String, String> maps) { return sendHttpPost(httpUrl, maps, null); } /** * 发送 post请求 * * @param httpUrl 地址 * @param maps 参数 * @param sslContext ssl证书信息 */ public static String sendHttpPost(String httpUrl, Map<String, String> maps, SSLContext sslContext) { HttpPost httpPost = wrapHttpPost(httpUrl, maps); return sendHttpPost(httpPost, null); } /** * 封装获取HttpPost方法 * * @param httpUrl * @param maps * @return */ public static HttpPost wrapHttpPost(String httpUrl, Map<String, String> maps) { // 创建httpPost HttpPost httpPost = new HttpPost(httpUrl); // 创建参数队列 List<NameValuePair> nameValuePairs = new ArrayList<>(); for (Map.Entry<String, String> m : maps.entrySet()) { nameValuePairs.add(new BasicNameValuePair(m.getKey(), m.getValue())); } try { httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, Constants.UTF8)); } catch (Exception e) { log.error(e.getMessage(), e); } return httpPost; } /** * 发送 post请求(带文件) * * @param httpUrl 地址 * @param file 附件,名称和File对应 */ public static String sendHttpPost(String httpUrl, File file) { return sendHttpPost(httpUrl, ImmutableMap.of("media", file), null, null); } /** * 发送 post请求(带文件) * * @param httpUrl 地址 * @param file 附件,名称和File对应 * @param maps 参数 */ public static String sendHttpPost(String httpUrl, File file, Map<String, String> maps) { return sendHttpPost(httpUrl, ImmutableMap.of("media", file), maps, null); } /** * 发送 post请求(带文件),默认 files 名称数组. * * @param httpUrl 地址 * @param fileLists 附件 * @param maps 参数 */ public static String sendHttpPost(String httpUrl, List<File> fileLists, Map<String, String> maps) { return sendHttpPost(httpUrl, fileLists, maps, null); } /** * 发送 post请求(带文件) * * @param httpUrl 地址 * @param fileMap 附件,名称和File对应 * @param maps 参数 */ public static String sendHttpPost(String httpUrl, Map<String, File> fileMap, Map<String, String> maps) { return sendHttpPost(httpUrl, fileMap, maps, null); } /** * 发送 post请求(带文件),默认 files 名称数组. * * @param httpUrl 地址 * @param fileLists 附件 * @param maps 参数 * @param sslContext ssl证书信息 */ public static String sendHttpPost(String httpUrl, List<File> fileLists, Map<String, String> maps, SSLContext sslContext) { Map<String, File> fileMap = new HashMap<>(16); if (fileLists == null || fileLists.isEmpty()) { for (File file : fileLists) { fileMap.put("media", file); } } return sendHttpPost(httpUrl, fileMap, maps, sslContext); } /** * 发送 post请求(带文件) * * @param httpUrl 地址 * @param fileMap 附件,名称和File对应 * @param maps 参数 * @param sslContext ssl证书信息 */ public static String sendHttpPost(String httpUrl, Map<String, File> fileMap, Map<String, String> maps, SSLContext sslContext) { // 创建httpPost HttpPost httpPost = new HttpPost(httpUrl); MultipartEntityBuilder meBuilder = MultipartEntityBuilder.create(); if (null != maps) { for (Map.Entry<String, String> m : maps.entrySet()) { meBuilder.addPart(m.getKey(), new StringBody(m.getValue(), ContentType.TEXT_PLAIN)); } } if (null != fileMap) { for (Map.Entry<String, File> m : fileMap.entrySet()) { FileBody fileBody = new FileBody(m.getValue()); meBuilder.addPart(m.getKey(), fileBody); } } HttpEntity reqEntity = meBuilder.build(); httpPost.setEntity(reqEntity); return sendHttpPost(httpPost, sslContext); } /** * 发送Post请求 * * @param httpPost * @return */ public static String sendHttpPost(HttpPost httpPost) { return sendHttpPost(httpPost, null); } /** * 发送Post请求 * * @param httpPost * @param sslConext ssl证书信息 * @return */ public static String sendHttpPost(HttpPost httpPost, SSLContext sslConext) { CloseableHttpClient httpClient = getHttpClient(sslConext); CloseableHttpResponse response = null; HttpEntity entity = null; String responseContent = null; try { // 执行请求 response = httpClient.execute(httpPost); entity = response.getEntity(); responseContent = EntityUtils.toString(entity, Constants.UTF8); } catch (Exception e) { log.error(e.getMessage(), e); } finally { try { // 关闭连接,释放资源 if (entity != null) { // 会自动释放连接 EntityUtils.consumeQuietly(entity); } if (response != null) { response.close(); } } catch (Exception e) { log.error(e.getMessage(), e); } } return responseContent; } /** * 发送 get请求 * * @param httpUrl */ public static String sendHttpGet(String httpUrl) { return sendHttpGet(httpUrl, null); } /** * 发送 get请求 * * @param httpUrl * @param sslConext ssl证书信息 */ public static String sendHttpGet(String httpUrl, SSLContext sslConext) { // 创建get请求 HttpGet httpGet = new HttpGet(httpUrl); return sendHttpGet(httpGet, sslConext); } /** * 发送Get请求 * * @param httpGet * @return */ public static String sendHttpGet(HttpGet httpGet) { return sendHttpGet(httpGet, null); } /** * 发送Get请求 * * @param httpGet * @param sslConext ssl证书信息 * @return */ public static String sendHttpGet(HttpGet httpGet, SSLContext sslConext) { CloseableHttpClient httpClient = getHttpClient(sslConext); CloseableHttpResponse response = null; HttpEntity entity = null; String responseContent = null; try { // 执行请求 response = httpClient.execute(httpGet); entity = response.getEntity(); responseContent = EntityUtils.toString(entity, Constants.UTF8); } catch (Exception e) { log.error(e.getMessage(), e); } finally { try { // 关闭连接,释放资源 if (entity != null) { // 会自动释放连接 EntityUtils.consumeQuietly(entity); } if (response != null) { response.close(); } } catch (Exception e) { log.error(e.getMessage(), e); } } return responseContent; } /** * 发送 get请求 * * @param httpUrl 请求路径 * @param headers 请求头参数 * @return */ public static String sendHttpHeaderGet(String httpUrl, Map<String, String> headers) { // 创建get请求 HttpGet httpGet = new HttpGet(httpUrl); for (Map.Entry<String, String> entry : headers.entrySet()) { String key = entry.getKey().toString(); String value = entry.getValue().toString(); httpGet.setHeader(key, value); } return sendHttpGet(httpGet, null); } /** * Get 下载文件 * * @param httpUrl * @param file * @return */ public static File sendHttpGetFile(String httpUrl, File file) { if (file == null) { return null; } HttpGet httpGet = new HttpGet(httpUrl); CloseableHttpClient httpClient = getHttpClient(); CloseableHttpResponse response = null; HttpEntity entity = null; InputStream inputStream = null; FileOutputStream fileOutputStream = null; try { // 执行请求 response = httpClient.execute(httpGet); entity = response.getEntity(); inputStream = entity.getContent(); fileOutputStream = new FileOutputStream(file); int len = 0; byte[] buf = new byte[1024]; while ((len = inputStream.read(buf, 0, 1024)) != -1) { fileOutputStream.write(buf, 0, len); } } catch (Exception e) { log.error(e.getMessage(), e); } finally { try { if (fileOutputStream != null) { fileOutputStream.close(); } if (inputStream != null) { inputStream.close(); } // 关闭连接,释放资源 if (entity != null) { // 会自动释放连接 EntityUtils.consumeQuietly(entity); } if (response != null) { response.close(); } } catch (Exception e) { log.error(e.getMessage(), e); } } return file; } /** * Post 下载文件 * * @param httpUrl * @param maps * @param file * @return */ public static File sendHttpPostFile(String httpUrl, Map<String, String> maps, File file) { if (file == null) { return null; } HttpPost httpPost = wrapHttpPost(httpUrl, maps); CloseableHttpClient httpClient = getHttpClient(); CloseableHttpResponse response = null; HttpEntity entity = null; InputStream inputStream = null; FileOutputStream fileOutputStream = null; try { // 执行请求 response = httpClient.execute(httpPost); entity = response.getEntity(); inputStream = entity.getContent(); fileOutputStream = new FileOutputStream(file); int len = 0; byte[] buf = new byte[1024]; while ((len = inputStream.read(buf, 0, 1024)) != -1) { fileOutputStream.write(buf, 0, len); } } catch (Exception e) { log.error(e.getMessage(), e); } finally { try { if (fileOutputStream != null) { fileOutputStream.close(); } if (inputStream != null) { inputStream.close(); } // 关闭连接,释放资源 if (entity != null) { // 会自动释放连接 EntityUtils.consumeQuietly(entity); } if (response != null) { response.close(); } } catch (Exception e) { log.error(e.getMessage(), e); } } return file; } /** * 判断是否微信返回误 * * @param jsonObject * @return */ public static boolean isWxError(JSONObject jsonObject) { if (null == jsonObject || jsonObject.getIntValue("errcode") != 0) { return true; } return false; } /** * http请求 * * @param requestUrl url * @param requestMethod GET/POST * @param outputStr 参数 * @return */ public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) { return httpRequest(requestUrl,requestMethod,outputStr,null); } /** * http请求 * * @param requestUrl url * @param requestMethod GET/POST * @param outputStr 参数 * @return */ public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr, String... token) { JSONObject jsonObject = null; try { URL url = new URL(requestUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setRequestMethod(requestMethod); conn.setConnectTimeout(50000); conn.setReadTimeout(60000); if (token != null && token.length > 2 && token[2] != null) { if ("1".equals(token[2])) { conn.setRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE); } else if ("2".equals(token[2])) { conn.setRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); } else if ("3".equals(token[2])) { conn.setRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); } else if ("4".equals(token[2])) { conn.setRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE); } else { conn.setRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); } } else { conn.setRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); } if (token != null && token.length > 0 && StringUtil.isNotEmpty(token[0])) { conn.setRequestProperty(Constants.AUTHORIZATION, XSSEscape.escape(token[0])); } // 处理请求头参数 if (token != null && token.length > 1 && StringUtil.isNotEmpty(token[1])) { Map<String, Object> requestHeader = JsonUtil.stringToMap(token[1]); for (String field : requestHeader.keySet()) { conn.setRequestProperty(field, requestHeader.get(field) + ""); } } String agent = ServletUtil.getUserAgent(); if (StringUtil.isNotEmpty(agent)) { conn.setRequestProperty(Constants.USER_AGENT, agent); } if (StringUtil.isNotEmpty(outputStr)) { @Cleanup OutputStream outputStream = conn.getOutputStream(); outputStream.write(outputStr.getBytes(Constants.UTF8)); outputStream.close(); } @Cleanup InputStream inputStream = conn.getInputStream(); @Cleanup InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Constants.UTF8); @Cleanup BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); conn.disconnect(); jsonObject = JSONObject.parseObject(buffer.toString()); if (jsonObject == null) { return new JSONObject(); } } catch (Exception e) { log.error(e.getMessage()); } return jsonObject; } /** * http请求 * * @param requestUrl url * @param requestMethod GET/POST * @param outputStr 参数 * @return */ public static boolean httpCronRequest(String requestUrl, String requestMethod, String outputStr, String token) { boolean falg = false; try { URL url = new URL(requestUrl); final HttpURLConnection[] conn = {null}; Callable<String> task = new Callable<String>() { @Override public String call() throws Exception { //执行耗时代码 try { conn[0] = (HttpURLConnection) url.openConnection(); } catch (Exception e) { log.error(e.getMessage()); } conn[0].setDoOutput(true); conn[0].setDoInput(true); conn[0].setUseCaches(false); conn[0].setRequestMethod(requestMethod); conn[0].setRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); if (StringUtil.isNotEmpty(token)) { conn[0].setRequestProperty(Constants.AUTHORIZATION, token); } if (null != outputStr) { @Cleanup OutputStream outputStream = conn[0].getOutputStream(); outputStream.write(outputStr.getBytes(Constants.UTF8)); outputStream.close(); } @Cleanup InputStream inputStream = conn[0].getInputStream(); @Cleanup InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Constants.UTF8); @Cleanup BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); conn[0].disconnect(); return "url连接ok"; } }; Future<String> future = ThreadPoolExecutorUtil.getExecutor().submit(task); try { //设置超时时间 String rst = future.get(3, TimeUnit.SECONDS); if ("url连接ok".equals(rst)) { falg = true; } } catch (TimeoutException e) { log.error("连接url超时"); } catch (Exception e) { log.error("获取异常," + e.getMessage()); } } catch (MalformedURLException e) { e.printStackTrace(); } return falg; } } // // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.http; import java.io.Serializable; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.InvalidMimeTypeException; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; import org.springframework.util.StringUtils; public class MediaType extends MimeType implements Serializable { private static final long serialVersionUID = 2069937152339670231L; public static final MediaType ALL = new MediaType("*", "*"); public static final String ALL_VALUE = "*/*"; public static final MediaType APPLICATION_ATOM_XML = new MediaType("application", "atom+xml"); public static final String APPLICATION_ATOM_XML_VALUE = "application/atom+xml"; public static final MediaType APPLICATION_CBOR = new MediaType("application", "cbor"); public static final String APPLICATION_CBOR_VALUE = "application/cbor"; public static final MediaType APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded"); public static final String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded"; /** @deprecated */ @Deprecated( since = "6.0.3", forRemoval = true ) public static final MediaType APPLICATION_GRAPHQL = new MediaType("application", "graphql+json"); /** @deprecated */ @Deprecated( since = "6.0.3", forRemoval = true ) public static final String APPLICATION_GRAPHQL_VALUE = "application/graphql+json"; public static final MediaType APPLICATION_GRAPHQL_RESPONSE = new MediaType("application", "graphql-response+json"); public static final String APPLICATION_GRAPHQL_RESPONSE_VALUE = "application/graphql-response+json"; public static final MediaType APPLICATION_JSON = new MediaType("application", "json"); public static final String APPLICATION_JSON_VALUE = "application/json"; /** @deprecated */ @Deprecated public static final MediaType APPLICATION_JSON_UTF8; /** @deprecated */ @Deprecated public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8"; public static final MediaType APPLICATION_OCTET_STREAM; public static final String APPLICATION_OCTET_STREAM_VALUE = "application/octet-stream"; public static final MediaType APPLICATION_PDF; public static final String APPLICATION_PDF_VALUE = "application/pdf"; public static final MediaType APPLICATION_PROBLEM_JSON; public static final String APPLICATION_PROBLEM_JSON_VALUE = "application/problem+json"; /** @deprecated */ @Deprecated public static final MediaType APPLICATION_PROBLEM_JSON_UTF8; /** @deprecated */ @Deprecated public static final String APPLICATION_PROBLEM_JSON_UTF8_VALUE = "application/problem+json;charset=UTF-8"; public static final MediaType APPLICATION_PROBLEM_XML; public static final String APPLICATION_PROBLEM_XML_VALUE = "application/problem+xml"; public static final MediaType APPLICATION_PROTOBUF; public static final String APPLICATION_PROTOBUF_VALUE = "application/x-protobuf"; public static final MediaType APPLICATION_RSS_XML; public static final String APPLICATION_RSS_XML_VALUE = "application/rss+xml"; public static final MediaType APPLICATION_NDJSON; public static final String APPLICATION_NDJSON_VALUE = "application/x-ndjson"; /** @deprecated */ @Deprecated public static final MediaType APPLICATION_STREAM_JSON; /** @deprecated */ @Deprecated public static final String APPLICATION_STREAM_JSON_VALUE = "application/stream+json"; public static final MediaType APPLICATION_XHTML_XML; public static final String APPLICATION_XHTML_XML_VALUE = "application/xhtml+xml"; public static final MediaType APPLICATION_XML; public static final String APPLICATION_XML_VALUE = "application/xml"; public static final MediaType APPLICATION_YAML; public static final String APPLICATION_YAML_VALUE = "application/yaml"; public static final MediaType IMAGE_GIF; public static final String IMAGE_GIF_VALUE = "image/gif"; public static final MediaType IMAGE_JPEG; public static final String IMAGE_JPEG_VALUE = "image/jpeg"; public static final MediaType IMAGE_PNG; public static final String IMAGE_PNG_VALUE = "image/png"; public static final MediaType MULTIPART_FORM_DATA; public static final String MULTIPART_FORM_DATA_VALUE = "multipart/form-data"; public static final MediaType MULTIPART_MIXED; public static final String MULTIPART_MIXED_VALUE = "multipart/mixed"; public static final MediaType MULTIPART_RELATED; public static final String MULTIPART_RELATED_VALUE = "multipart/related"; public static final MediaType TEXT_EVENT_STREAM; public static final String TEXT_EVENT_STREAM_VALUE = "text/event-stream"; public static final MediaType TEXT_HTML; public static final String TEXT_HTML_VALUE = "text/html"; public static final MediaType TEXT_MARKDOWN; public static final String TEXT_MARKDOWN_VALUE = "text/markdown"; public static final MediaType TEXT_PLAIN; public static final String TEXT_PLAIN_VALUE = "text/plain"; public static final MediaType TEXT_XML; public static final String TEXT_XML_VALUE = "text/xml"; private static final String PARAM_QUALITY_FACTOR = "q"; /** @deprecated */ @Deprecated( since = "6.0", forRemoval = true ) public static final Comparator<MediaType> QUALITY_VALUE_COMPARATOR; /** @deprecated */ @Deprecated( since = "6.0", forRemoval = true ) public static final Comparator<MediaType> SPECIFICITY_COMPARATOR; public MediaType(String type) { super(type); } public MediaType(String type, String subtype) { super(type, subtype, Collections.emptyMap()); } public MediaType(String type, String subtype, Charset charset) { super(type, subtype, charset); } public MediaType(String type, String subtype, double qualityValue) { this(type, subtype, Collections.singletonMap("q", Double.toString(qualityValue))); } public MediaType(MediaType other, Charset charset) { super(other, charset); } public MediaType(MediaType other, @Nullable Map<String, String> parameters) { super(other.getType(), other.getSubtype(), parameters); } public MediaType(String type, String subtype, @Nullable Map<String, String> parameters) { super(type, subtype, parameters); } public MediaType(MimeType mimeType) { super(mimeType); this.getParameters().forEach(this::checkParameters); } protected void checkParameters(String parameter, String value) { super.checkParameters(parameter, value); if ("q".equals(parameter)) { String unquotedValue = this.unquote(value); double d = Double.parseDouble(unquotedValue); Assert.isTrue(d >= (double)0.0F && d <= (double)1.0F, () -> "Invalid quality value \"" + unquotedValue + "\": should be between 0.0 and 1.0"); } } public double getQualityValue() { String qualityFactor = this.getParameter("q"); return qualityFactor != null ? Double.parseDouble(this.unquote(qualityFactor)) : (double)1.0F; } public boolean isMoreSpecific(MimeType other) { Assert.notNull(other, "Other must not be null"); if (other instanceof MediaType otherMediaType) { double quality1 = this.getQualityValue(); double quality2 = otherMediaType.getQualityValue(); if (quality1 > quality2) { return true; } if (quality1 < quality2) { return false; } } return super.isMoreSpecific(other); } public boolean isLessSpecific(MimeType other) { Assert.notNull(other, "Other must not be null"); return other.isMoreSpecific(this); } public boolean includes(@Nullable MediaType other) { return super.includes(other); } public boolean isCompatibleWith(@Nullable MediaType other) { return super.isCompatibleWith(other); } public MediaType copyQualityValue(MediaType mediaType) { if (!mediaType.getParameters().containsKey("q")) { return this; } else { Map<String, String> params = new LinkedHashMap(this.getParameters()); params.put("q", (String)mediaType.getParameters().get("q")); return new MediaType(this, params); } } public MediaType removeQualityValue() { if (!this.getParameters().containsKey("q")) { return this; } else { Map<String, String> params = new LinkedHashMap(this.getParameters()); params.remove("q"); return new MediaType(this, params); } } public static MediaType valueOf(String value) { return parseMediaType(value); } public static MediaType parseMediaType(String mediaType) { MimeType type; try { type = MimeTypeUtils.parseMimeType(mediaType); } catch (InvalidMimeTypeException ex) { throw new InvalidMediaTypeException(ex); } try { return new MediaType(type); } catch (IllegalArgumentException ex) { throw new InvalidMediaTypeException(mediaType, ex.getMessage()); } } public static List<MediaType> parseMediaTypes(@Nullable String mediaTypes) { if (!StringUtils.hasLength(mediaTypes)) { return Collections.emptyList(); } else { List<String> tokenizedTypes = MimeTypeUtils.tokenize(mediaTypes); List<MediaType> result = new ArrayList(tokenizedTypes.size()); for(String type : tokenizedTypes) { if (StringUtils.hasText(type)) { result.add(parseMediaType(type)); } } return result; } } public static List<MediaType> parseMediaTypes(@Nullable List<String> mediaTypes) { if (CollectionUtils.isEmpty(mediaTypes)) { return Collections.emptyList(); } else if (mediaTypes.size() == 1) { return parseMediaTypes((String)mediaTypes.get(0)); } else { List<MediaType> result = new ArrayList(8); for(String mediaType : mediaTypes) { result.addAll(parseMediaTypes(mediaType)); } return result; } } public static List<MediaType> asMediaTypes(List<MimeType> mimeTypes) { List<MediaType> mediaTypes = new ArrayList(mimeTypes.size()); for(MimeType mimeType : mimeTypes) { mediaTypes.add(asMediaType(mimeType)); } return mediaTypes; } public static MediaType asMediaType(MimeType mimeType) { if (mimeType instanceof MediaType mediaType) { return mediaType; } else { return new MediaType(mimeType.getType(), mimeType.getSubtype(), mimeType.getParameters()); } } public static String toString(Collection<MediaType> mediaTypes) { return MimeTypeUtils.toString(mediaTypes); } /** @deprecated */ @Deprecated( since = "6.0", forRemoval = true ) public static void sortBySpecificity(List<MediaType> mediaTypes) { Assert.notNull(mediaTypes, "'mediaTypes' must not be null"); if (mediaTypes.size() > 1) { mediaTypes.sort(SPECIFICITY_COMPARATOR); } } /** @deprecated */ @Deprecated( since = "6.0", forRemoval = true ) public static void sortByQualityValue(List<MediaType> mediaTypes) { Assert.notNull(mediaTypes, "'mediaTypes' must not be null"); if (mediaTypes.size() > 1) { mediaTypes.sort(QUALITY_VALUE_COMPARATOR); } } /** @deprecated */ @Deprecated( since = "6.0", forRemoval = true ) public static void sortBySpecificityAndQuality(List<MediaType> mediaTypes) { Assert.notNull(mediaTypes, "'mediaTypes' must not be null"); if (mediaTypes.size() > 1) { mediaTypes.sort(SPECIFICITY_COMPARATOR.thenComparing(QUALITY_VALUE_COMPARATOR)); } } static { APPLICATION_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8); APPLICATION_NDJSON = new MediaType("application", "x-ndjson"); APPLICATION_OCTET_STREAM = new MediaType("application", "octet-stream"); APPLICATION_PDF = new MediaType("application", "pdf"); APPLICATION_PROBLEM_JSON = new MediaType("application", "problem+json"); APPLICATION_PROBLEM_JSON_UTF8 = new MediaType("application", "problem+json", StandardCharsets.UTF_8); APPLICATION_PROBLEM_XML = new MediaType("application", "problem+xml"); APPLICATION_PROTOBUF = new MediaType("application", "x-protobuf"); APPLICATION_RSS_XML = new MediaType("application", "rss+xml"); APPLICATION_STREAM_JSON = new MediaType("application", "stream+json"); APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml"); APPLICATION_XML = new MediaType("application", "xml"); APPLICATION_YAML = new MediaType("application", "yaml"); IMAGE_GIF = new MediaType("image", "gif"); IMAGE_JPEG = new MediaType("image", "jpeg"); IMAGE_PNG = new MediaType("image", "png"); MULTIPART_FORM_DATA = new MediaType("multipart", "form-data"); MULTIPART_MIXED = new MediaType("multipart", "mixed"); MULTIPART_RELATED = new MediaType("multipart", "related"); TEXT_EVENT_STREAM = new MediaType("text", "event-stream"); TEXT_HTML = new MediaType("text", "html"); TEXT_MARKDOWN = new MediaType("text", "markdown"); TEXT_PLAIN = new MediaType("text", "plain"); TEXT_XML = new MediaType("text", "xml"); QUALITY_VALUE_COMPARATOR = (mediaType1, mediaType2) -> { double quality1 = mediaType1.getQualityValue(); double quality2 = mediaType2.getQualityValue(); int qualityComparison = Double.compare(quality2, quality1); if (qualityComparison != 0) { return qualityComparison; } else if (mediaType1.isWildcardType() && !mediaType2.isWildcardType()) { return 1; } else if (mediaType2.isWildcardType() && !mediaType1.isWildcardType()) { return -1; } else if (!mediaType1.getType().equals(mediaType2.getType())) { return 0; } else if (mediaType1.isWildcardSubtype() && !mediaType2.isWildcardSubtype()) { return 1; } else if (mediaType2.isWildcardSubtype() && !mediaType1.isWildcardSubtype()) { return -1; } else if (!mediaType1.getSubtype().equals(mediaType2.getSubtype())) { return 0; } else { int paramsSize1 = mediaType1.getParameters().size(); int paramsSize2 = mediaType2.getParameters().size(); return Integer.compare(paramsSize2, paramsSize1); } }; SPECIFICITY_COMPARATOR = new MimeType.SpecificityComparator<MediaType>() { protected int compareParameters(MediaType mediaType1, MediaType mediaType2) { double quality1 = mediaType1.getQualityValue(); double quality2 = mediaType2.getQualityValue(); int qualityComparison = Double.compare(quality2, quality1); return qualityComparison != 0 ? qualityComparison : super.compareParameters(mediaType1, mediaType2); } }; } }
最新发布
09-09
on functions.php, it says Your PHP code changes were not applied due to an error on line 82 of file wp-content/themes/woodmart-child/functions.php. Please fix and try saving again. Uncaught TypeError: method_exists(): Argument #1 ($object_or_class) must be of type object|string, null given in wp-content/themes/woodmart-child/functions.php:82 Stack trace: #0 wp-content/themes/woodmart-child/functions.php(82): method_exists() #1 wp-includes/class-wp-hook.php(324): {closure}() #2 wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters() #3 wp-includes/plugin.php(517): WP_Hook->do_action() #4 wp-settings.php(749): do_action() #5 wp-config.php(105): require_once(‘/home/u18285596…’) #6 wp-load.php(50): require_once(‘/home/u18285596…’) #7 wp-admin/admin.php(35): require_once(‘/home/u18285596…’) #8 wp-admin/theme-editor.php(10): require_once(‘/home/u18285596…’) #9 {main} thrown cart.php <?php /** * Custom Cart Page for WooCommerce with Selective Checkout */ if (!defined('ABSPATH')) { exit; } do_action('woocommerce_before_cart'); // Provide context for JS (no layout change) $pc_cart_is_empty = WC()->cart->is_empty(); function pc_uid_for_view() { if (is_user_logged_in()) return 'user_' . get_current_user_id(); if (empty($_COOKIE['pc_cart_uid'])) { $token = wp_generate_uuid4(); setcookie('pc_cart_uid', $token, time() + YEAR_IN_SECONDS, COOKIEPATH ?: '/', '', is_ssl(), false); $_COOKIE['pc_cart_uid'] = $token; } return 'guest_' . sanitize_text_field(wp_unslash($_COOKIE['pc_cart_uid'])); } $pc_uid = pc_uid_for_view(); // 已修正:仅在令牌存在时恢复购物车 if (method_exists(WC()->session, 'get')) { $token = WC()->session->get('pc_partial_token'); if ($token && !WC()->cart->is_empty()) { $payload = get_transient(pc_transient_key($token)); if (!empty($payload['snapshot'])) { pc_restore_cart_from_items($payload['snapshot']); WC()->cart->calculate_totals(); } } } ?> <div class="cart-page-section container" style="max-width: 1200px; margin: 0 auto;"> <form class="woocommerce-cart-form" action="<?php echo esc_url( wc_get_cart_url() ); ?>" method="post"> <?php do_action('woocommerce_before_cart_table'); ?> <?php wp_nonce_field('woocommerce-cart', 'woocommerce-cart-nonce'); ?> <table class="shop_table shop_table_responsive cart woocommerce-cart-form__contents"> <thead> <tr> <th class="product-select" style="width: 8%;"> <input type="checkbox" id="select-all-items" /> <label for="select-all-items" style="display: inline-block; margin-left: 5px; cursor: pointer;">全选</label> </th> <th class="product-info"><?php esc_html_e('Product', 'woocommerce'); ?></th> <th class="product-price"><?php esc_html_e('Price', 'woocommerce'); ?></th> <th class="product-quantity"><?php esc_html_e('Quantity', 'woocommerce'); ?></th> <th class="product-subtotal"><?php esc_html_e('Subtotal', 'woocommerce'); ?></th> <th class="product-remove"><?php esc_html_e('操作', 'woocommerce'); ?></th> </tr> </thead> <tbody> <?php do_action('woocommerce_before_cart_contents'); ?> <?php foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) : ?> <?php $_product = apply_filters('woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key); $product_id = apply_filters('woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key); if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters('woocommerce_cart_item_visible', true, $cart_item, $cart_item_key) ) : $product_permalink = apply_filters('woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink($cart_item) : '', $cart_item, $cart_item_key); $line_total_value = (float) ($cart_item['line_total'] + $cart_item['line_tax']); $variation_id = isset($cart_item['variation_id']) ? (int)$cart_item['variation_id'] : 0; $variation_data = isset($cart_item['variation']) ? $cart_item['variation'] : array(); $variation_json = wp_json_encode($variation_data); ?> <tr class="woocommerce-cart-form__cart-item <?php echo esc_attr( apply_filters('woocommerce_cart_item_class', 'cart_item', $cart_item, $cart_item_key) ); ?>" data-cart_item_key="<?php echo esc_attr($cart_item_key); ?>" data-product_id="<?php echo esc_attr($product_id); ?>" data-variation_id="<?php echo esc_attr($variation_id); ?>" data-variation="<?php echo esc_attr($variation_json); ?>"> <!-- Checkbox Column --> <td class="product-select" data-title="<?php esc_attr_e('Select', 'woocommerce'); ?>"> <input type="checkbox" class="item-checkbox" name="selected_items[]" value="<?php echo esc_attr($cart_item_key); ?>" data-price="<?php echo esc_attr($line_total_value); ?>" /> </td> <!-- Product Info Column --> <td class="product-info" data-title="<?php esc_attr_e('Product', 'woocommerce'); ?>"> <div class="product-image"> <?php $thumbnail = apply_filters('woocommerce_cart_item_thumbnail', $_product->get_image(), $cart_item, $cart_item_key); if ( ! $product_permalink ) : echo $thumbnail; // PHPCS: XSS ok. else : printf('<a href="%s">%s</a>', esc_url($product_permalink), $thumbnail); // PHPCS: XSS ok. endif; ?> </div> <div class="product-name"> <?php if ( ! $product_permalink ) : echo wp_kses_post( apply_filters('woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key) . ' ' ); else : echo wp_kses_post( apply_filters('woocommerce_cart_item_name', sprintf('<a href="%s">%s</a>', esc_url($product_permalink), $_product->get_name()), $cart_item, $cart_item_key) ); endif; do_action('woocommerce_after_cart_item_name', $cart_item, $cart_item_key); echo wc_get_formatted_cart_item_data($cart_item); // PHPCS: XSS ok. ?> </div> </td> <!-- Price Column --> <td class="product-price" data-title="<?php esc_attr_e('Price', 'woocommerce'); ?>"> <?php echo apply_filters('woocommerce_cart_item_price', WC()->cart->get_product_price($_product), $cart_item, $cart_item_key); // PHPCS: XSS ok. ?> </td> <!-- Quantity Column --> <td class="product-quantity" data-title="<?php esc_attr_e('Quantity', 'woocommerce'); ?>"> <?php if ( $_product->is_sold_individually() ) : $product_quantity = sprintf('1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_item_key); else : $product_quantity = woocommerce_quantity_input( array( 'input_name' => "cart[{$cart_item_key}][qty]", 'input_value' => $cart_item['quantity'], 'max_value' => $_product->get_max_purchase_quantity(), 'min_value' => '0', 'product_name' => $_product->get_name(), ), $_product, false ); endif; echo apply_filters('woocommerce_cart_item_quantity', $product_quantity, $cart_item_key, $cart_item); // PHPCS: XSS ok. ?> <small class="qty-status" style="display:none;margin-left:8px;color:#666;">保存中…</small> </td> <!-- Subtotal Column --> <td class="product-subtotal" data-title="<?php esc_attr_e('Subtotal', 'woocommerce'); ?>"> <?php echo apply_filters('woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal($_product, $cart_item['quantity']), $cart_item, $cart_item_key); // PHPCS: XSS ok. ?> </td> <!-- Remove Item Column --> <td class="product-remove" data-title="<?php esc_attr_e('操作', 'woocommerce'); ?>"> <?php echo apply_filters('woocommerce_cart_item_remove_link', sprintf( '<a href="%s" class="remove" aria-label="%s" data-product_id="%s" data-product_sku="%s">×</a>', esc_url( wc_get_cart_remove_url($cart_item_key) ), esc_attr__('Remove this item', 'woocommerce'), esc_attr($product_id), esc_attr($_product->get_sku()) ), $cart_item_key); ?> </td> </tr> <?php endif; ?> <?php endforeach; ?> <?php do_action('woocommerce_after_cart_contents'); ?> </tbody> </table> <?php do_action('woocommerce_after_cart_table'); ?> </form> </div> <!-- Sticky Footer --> <div class="cart-footer-actions sticky-footer" style="position: sticky; bottom: 0; background: white; padding: 15px; border-top: 1px solid #ddd; max-width: 1200px; margin: 0 auto;"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;"> <div class="footer-left"> <input type="checkbox" id="footer-select-all"> <label for="footer-select-all" style="display: inline-block; margin-left: 5px; cursor: pointer;">全选</label> <button type="button" class="button" id="remove-selected-items">刪除選中的商品</button> <button type="button" class="button" id="clear-cart">清空購物車</button> </div> <div class="coupon-section"> <input type="text" name="coupon_code" class="input-text" id="coupon_code" value="" placeholder="输入优惠券代码" style="padding: 8px; width: 200px; border: 1px solid #ddd; border-radius: 4px; margin-right: 5px;" /> <button type="button" class="button" id="apply-coupon">应用优惠券</button> </div> </div> <div style="display: flex; justify-content: space-between; align-items: center;"> <div class="selected-summary" style="font-size: 16px; font-weight: bold;"> 已选商品: <span id="selected-count">0</span> 件,共计: <span id="selected-total">RM0.00</span> </div> <a href="<?php echo esc_url( wc_get_checkout_url() ); ?>" class="checkout-button button alt wc-forward" id="partial-checkout">结算</a> </div> </div> <?php do_action('woocommerce_after_cart'); ?> <style> /* Layout styles (unchanged) */ .cart-page-section { background: white; padding: 30px; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); margin-bottom: 30px; } .cart-page-section table.cart { width: 100%; border-collapse: collapse; margin-bottom: 20px; } .cart-page-section table.cart th, .cart-page-section table.cart td { padding: 15px; text-align: left; border-bottom: 1px solid #eee; } .cart-page-section table.cart th { background-color: #f8f8f8; font-weight: bold; } .cart-page-section table.cart th.product-select, .cart-page-section table.cart td.product-select { width: 8%; text-align: center; vertical-align: middle; white-space: nowrap; } .cart-page-section table.cart td.product-info { width: 37%; vertical-align: top; } .cart-page-section table.cart .product-info .product-image { float: left; margin-right: 15px; width: 100px; height: 100px; } .cart-page-section table.cart .product-info .product-image img { max-width: 100%; height: auto; display: block; } .cart-page-section table.cart .product-info .product-name { overflow: hidden; } .cart-page-section table.cart td.product-price, .cart-page-section table.cart td.product-quantity, .cart-page-section table.cart td.product-subtotal { width: 15%; vertical-align: middle; } .cart-page-section table.cart td.product-remove { width: 5%; text-align: center; vertical-align: middle; } .cart-footer-actions { position: sticky; bottom: 0; background: #fff; padding: 15px; border-top: 1px solid #ddd; box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); z-index: 1000; display: flex; flex-direction: column; gap: 15px; } .footer-left { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; } .coupon-section { display: flex; align-items: center; gap: 5px; } .selected-summary { font-size: 16px; font-weight: bold; flex: 1; } .cart-footer-actions .button { padding: 10px 20px; background-color: #f44336; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.3s; white-space: nowrap; } .cart-footer-actions .button:hover { background-color: #d32f2f; } #partial-checkout { padding: 12px 24px; background-color: #2196F3; color: white; text-decoration: none; border-radius: 4px; display: inline-block; transition: background-color 0.3s; white-space: nowrap; text-align: center; font-weight: bold; } #partial-checkout:hover { background-color: #0b7dda; } #coupon_code { padding: 8px; width: 200px; border: 1px solid #ddd; border-radius: 4px; transition: border-color 0.3s; } #coupon_code:focus { border-color: #2196F3; outline: none; } /* Responsive (unchanged) */ @media (max-width: 768px) { .cart-page-section table.cart thead { display: none; } .cart-page-section table.cart td { display: block; width: 100% !important; text-align: right; padding: 10px; position: relative; padding-left: 50%; } .cart-page-section table.cart td::before { content: attr(data-title); position: absolute; left: 15px; font-weight: bold; text-align: left; } .cart-page-section table.cart .product-info .product-image { float: none; margin: 0 auto 10px; } .cart-page-section table.cart td.product-select::before { content: "选择"; } .cart-footer-actions { flex-direction: column; align-items: flex-start; } .footer-left { width: 100%; justify-content: space-between; } .coupon-section { width: 100%; margin-top: 10px; } .coupon-section input { flex: 1; } .cart-footer-actions .footer-bottom-row { flex-direction: column; align-items: stretch; } .selected-summary { text-align: center; margin-bottom: 10px; } #partial-checkout { width: 100%; padding: 15px; } .cart-footer-actions .button { padding: 12px 15px; margin: 5px 0; width: 100%; text-align: center; } } @media (max-width: 480px) { .cart-page-section { padding: 15px; } .cart-page-section table.cart td { padding-left: 45%; } .cart-page-section table.cart td::before { font-size: 14px; } .cart-footer-actions { padding: 10px; } #coupon_code { width: 100%; } } /* Loader overlay above quantity input */ .product-quantity .quantity { position: relative; /* anchor for overlay */ } .quantity-saving-overlay { position: absolute; top: 0; left: 0; right: 0; height: 100%; display: none; /* hidden by default */ align-items: flex-start; /* appear at top */ justify-content: center;/* centered horizontally */ padding-top: 2px; background: rgba(255,255,255,0.0); /* transparent so it doesn't dim UI */ z-index: 10; pointer-events: none; /* don't block typing/clicks when visible */ } .quantity-saving-loader { width: 20px; height: 20px; border: 3px solid #f3f3f3; border-top: 3px solid #3498db; border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style> <script> jQuery(function($){ // Context var PC = { ajax_url: (window.wc_cart_params && window.wc_cart_params.ajax_url) || '<?php echo esc_url( admin_url("admin-ajax.php") ); ?>', security: (function(){ var n = $('input[name="woocommerce-cart-nonce"]').val(); if (n) return n; if (window.wc_cart_params && window.wc_cart_params.cart_nonce) return window.wc_cart_params.cart_nonce; return '<?php echo wp_create_nonce("woocommerce-cart"); ?>'; })(), cart_is_empty: <?php echo $pc_cart_is_empty ? 'true' : 'false'; ?>, cart_uid: '<?php echo esc_js($pc_uid); ?>' }; // Keys function lsKey(name){ return 'pc_' + name + '_' + PC.cart_uid; } var LS_SELECTION = lsKey('selected_items'); var LS_MASTER = lsKey('cart_master'); // Utilities function getSelectedKeys(){ return $('.item-checkbox:checked').map(function(){ return this.value; }).get(); } function fmtRM(n){ n = isNaN(n) ? 0 : n; return 'RM' + Number(n).toFixed(2); } // Selection persistence function readSelection(){ try { return JSON.parse(localStorage.getItem(LS_SELECTION) || '[]'); } catch(e){ return []; } } function writeSelection(keys){ try { localStorage.setItem(LS_SELECTION, JSON.stringify(keys||[])); } catch(e){} } function restoreSelectionFromLS(){ var saved = readSelection(); if (!Array.isArray(saved)) saved = []; $('.item-checkbox').each(function(){ $(this).prop('checked', saved.indexOf(this.value) !== -1); }); } window.addEventListener('storage', function(ev){ if (ev.key === LS_SELECTION) { restoreSelectionFromLS(); updateSelectedSummary(); } }); // Selected summary function updateSelectedSummary(){ var total = 0, count = 0; $('.item-checkbox:checked').each(function(){ var price = parseFloat($(this).data('price')); if (!isNaN(price)) { total += price; count++; } }); $('#selected-count').text(count); $('#selected-total').text(fmtRM(total)); var totalCbs = $('.item-checkbox').length; var checkedCbs = $('.item-checkbox:checked').length; var allChecked = totalCbs > 0 && checkedCbs === totalCbs; $('#select-all-items, #footer-select-all').prop('checked', allChecked); writeSelection(getSelectedKeys()); } // Select all $('#select-all-items, #footer-select-all').off('change.sc').on('change.sc', function(){ var checked = $(this).prop('checked'); $('.item-checkbox').prop('checked', checked); updateSelectedSummary(); }); // 单个勾选项变化时,更新统计与“全选”状态 $(document).on('change', '.item-checkbox', updateSelectedSummary); // Snapshot cart DOM -> localStorage (guest resilience) function snapshotCartFromDOM() { var items = []; $('tr.cart_item').each(function(){ var $row = $(this); var pid = parseInt($row.attr('data-product_id'),10)||0; var vid = parseInt($row.attr('data-variation_id'),10)||0; var qty = parseFloat($row.find('input.qty').val())||0; var variation = {}; try { variation = JSON.parse($row.attr('data-variation')||'{}'); } catch(e){ variation = {}; } if (pid && qty > 0) { items.push({ product_id: pid, variation_id: vid, variation: variation, quantity: qty }); } }); try { localStorage.setItem(LS_MASTER, JSON.stringify({ ts: Date.now(), items: items })); } catch(e){} } function maybeRehydrateFromLocal() { if (!PC.cart_is_empty) return; var raw = localStorage.getItem(LS_MASTER); if (!raw) return; var data; try { data = JSON.parse(raw); } catch(e){ return; } var items = (data && data.items) ? data.items : []; if (!items.length) return; $.ajax({ url: PC.ajax_url, method: 'POST', dataType: 'json', data: { action: 'pc_rehydrate_cart', security: PC.security, items: JSON.stringify(items) } }).done(function(res){ if (res && res.success) { location.reload(); // display rehydrated items } }); } // AFTER $('#remove-selected-items').off('click.sc').on('click.sc', function(){ var keys = getSelectedKeys(); if (!keys.length) { alert('请选择要删除的商品'); return; } if (!confirm('确定要删除选中的商品吗?')) return; $.ajax({ url: PC.ajax_url, method: 'POST', dataType: 'json', data: { action: 'remove_selected_cart_items', selected_items: keys, security: PC.security } }).done(function(res){ if (res && res.success) { keys.forEach(function(k){ var $row = $('tr.cart_item[data-cart_item_key="'+k+'"]'); $row.fadeOut(250, function(){ $(this).remove(); snapshotCartFromDOM(); updateSelectedSummary(); }); }); // 从本地选择集合中剔除已删除项 var saved = readSelection().filter(function(k0){ return keys.indexOf(k0) === -1; }); writeSelection(saved); } else { alert((res && res.data && (res.data.message || res.data)) || '删除失败,请重试'); } }).fail(function(){ alert('删除失败,请重试'); }); }); // Clear cart // AFTER $('#clear-cart').off('click.sc').on('click.sc', function(){ if (!confirm('确定要清空购物车吗?')) return; $.ajax({ url: PC.ajax_url, method: 'POST', dataType: 'json', data: { action: 'empty_cart', security: PC.security } }).done(function(res){ if (res && res.success) { writeSelection([]); localStorage.removeItem(LS_MASTER); location.reload(); } else { alert((res && res.data && (res.data.message || res.data)) || '清空购物车失败,请重试'); } }).fail(function(){ alert('清空购物车失败,请重试'); }); }); // Apply coupon // AFTER $('#apply-coupon').off('click.sc').on('click.sc', function(){ var code = ($.trim($('#coupon_code').val()) || ''); if (!code) { alert('请输入优惠券代码'); return; } var $btn = $(this).prop('disabled', true).text('处理中...'); $.ajax({ url: PC.ajax_url, method: 'POST', dataType: 'json', data: { action: 'apply_coupon', coupon_code: code, security: PC.security } }).done(function(res){ if (res && res.success) { location.reload(); } else { alert((res && res.data && (res.data.message || res.data)) || '优惠券应用失败,请重试'); $btn.prop('disabled', false).text('应用优惠券'); } }).fail(function(){ alert('发生误,请重试'); $btn.prop('disabled', false).text('应用优惠券'); }); }); // ---- Quantity auto-save (fixed regex + loader above input) ---- // 已修正:正确的正则表达式 function parseCartKeyFromInputName(n) { var m = (n || '').match(/^cart\[([a-zA-Z0-9_]+)\]\[qty\]$/); return m ? m[1] : null; } function ensureLoader($quantityContainer){ var $overlay = $quantityContainer.find('.quantity-saving-overlay'); if (!$overlay.length) { $overlay = $('<div class="quantity-saving-overlay"><div class="quantity-saving-loader"></div></div>'); $quantityContainer.append($overlay); } return $overlay; } var qtyTimers = {}; $(document).on('input change', 'input.qty', function(){ var $input = $(this); var key = parseCartKeyFromInputName($input.attr('name')); if (!key) return; var $row = $input.closest('tr.cart_item'); var $quantityContainer = $input.closest('.quantity'); var val = parseInt($input.val(), 10); if (isNaN(val) || val < 0) val = 0; // debounce if (qtyTimers[key]) clearTimeout(qtyTimers[key]); var $overlay = ensureLoader($quantityContainer); $overlay.hide(); qtyTimers[key] = setTimeout(function(){ $overlay.show(); $input.prop('disabled', true); $.ajax({ url: PC.ajax_url, method: 'POST', dataType: 'json', data: { action: 'update_cart_item_qty', cart_item_key: key, qty: val, security: PC.security } }).done(function(res){ if (res && res.success && res.data){ if (res.data.subtotal_html) { $row.find('td.product-subtotal').html(res.data.subtotal_html); } var $cb = $row.find('.item-checkbox'); if ($cb.length && typeof res.data.line_total_incl_tax === 'number') { $cb.attr('data-price', res.data.line_total_incl_tax); } if (val === 0 || res.data.removed) { $row.fadeOut(300, function(){ $(this).remove(); snapshotCartFromDOM(); updateSelectedSummary(); }); } else { snapshotCartFromDOM(); updateSelectedSummary(); } } else { var msg = (res && res.data && (res.data.message || res.data)) || '数量更新失败'; alert(msg); } }).fail(function(){ alert('数量更新失败,请重试'); }).always(function(){ $overlay.hide(); $input.prop('disabled', false); }); }, 500); // 0.5s debounce }); // Partial checkout -> regular checkout page $('#partial-checkout').off('click.sc').on('click.sc', function(e){ e.preventDefault(); var keys = getSelectedKeys(); if (!keys.length) { alert('请至少选择一件商品结算'); return; } var $btn = $(this), t = $btn.text(); $btn.prop('disabled', true).text('创建订单中...'); snapshotCartFromDOM(); $.ajax({ url: PC.ajax_url, method: 'POST', dataType: 'json', data: { action: 'create_direct_order', selected_items: keys, security: PC.security } }).done(function(res){ if (res && res.success && res.data && res.data.checkout_url) { window.location.href = res.data.checkout_url; // /checkout/?pc_token=... } else { alert((res && res.data && (res.data.message || res.data)) || '创建订单失败,请重试'); $btn.prop('disabled', false).text(t); } }).fail(function(xhr){ var msg = '创建订单失败'; if (xhr && xhr.responseJSON && xhr.responseJSON.data) { msg += ':' + (xhr.responseJSON.data.message || xhr.responseJSON.data); } alert(msg); $btn.prop('disabled', false).text(t); }); }); // Keep LS selection after clicking "x" remove; also snapshot $(document).on('click', 'a.remove', function(){ var key = $(this).closest('tr.cart_item').attr('data-cart_item_key'); if (key) { var saved = readSelection().filter(function(k){ return k !== key; }); writeSelection(saved); snapshotCartFromDOM(); } }); // Init restoreSelectionFromLS(); updateSelectedSummary(); snapshotCartFromDOM(); maybeRehydrateFromLocal(); }); // NEW: Real-time cart count updater function updateHeaderCartCount() { $.ajax({ url: PC.ajax_url, method: 'POST', data: { action: 'get_cart_count', security: PC.security }, success: function(response) { if (response.success) { $('.cart-count').text(response.count); } } }); } // Update on cart changes $(document).on('cart_updated', function() { updateHeaderCartCount(); }); // Initial update updateHeaderCartCount(); }); </script> functions.php <?php defined('ABSPATH') || exit; /** * Robust partial checkout to regular /checkout/ with durable cart snapshot * - Snapshot full cart before virtualizing selection for checkout * - Do not remove anything until order is created * - On success (thank-you), rebuild cart as (snapshot - purchased) * - On cancel/back (visit cart), restore snapshot * - Guest resilience: localStorage + rehydrate AJAX */ /* ------------------------------------------------- * Helpers * ------------------------------------------------- */ /** * 新增:购物车数量AJAX端点 */ add_action('wp_ajax_get_cart_count', 'pc_get_cart_count'); add_action('wp_ajax_nopriv_get_cart_count', 'pc_get_cart_count'); function pc_get_cart_count() { check_ajax_referer('woocommerce-cart', 'security'); $count = WC()->cart->get_cart_contents_count(); wp_send_json_success(array('count' => $count)); } function pc_get_cart_uid() { if (is_user_logged_in()) { return 'user_' . get_current_user_id(); } if (empty($_COOKIE['pc_cart_uid'])) { $token = wp_generate_uuid4(); setcookie('pc_cart_uid', $token, time() + YEAR_IN_SECONDS, COOKIEPATH ?: '/', '', is_ssl(), false); $_COOKIE['pc_cart_uid'] = $token; } return 'guest_' . sanitize_text_field(wp_unslash($_COOKIE['pc_cart_uid'])); } function pc_build_item_key($product_id, $variation_id = 0) { return (int)$product_id . '|' . (int)$variation_id; } function pc_snapshot_current_cart() { if (!WC()->cart) wc_load_cart(); $items = array(); foreach (WC()->cart->get_cart() as $ci_key => $ci) { $pid = isset($ci['product_id']) ? (int)$ci['product_id'] : 0; $vid = isset($ci['variation_id']) ? (int)$ci['variation_id'] : 0; $qty = isset($ci['quantity']) ? wc_stock_amount($ci['quantity']) : 0; $var = isset($ci['variation']) && is_array($ci['variation']) ? $ci['variation'] : array(); if ($pid && $qty > 0) { $items[] = array( 'product_id' => $pid, 'variation_id' => $vid, 'variation' => array_map('wc_clean', $var), 'quantity' => $qty, ); } } return $items; } function pc_restore_cart_from_items($items) { if (!WC()->cart) wc_load_cart(); WC()->cart->empty_cart(); foreach ((array)$items as $it) { $pid = isset($it['product_id']) ? (int)$it['product_id'] : 0; $vid = isset($it['variation_id']) ? (int)$it['variation_id'] : 0; $qty = isset($it['quantity']) ? wc_stock_amount($it['quantity']) : 0; $var = isset($it['variation']) && is_array($it['variation']) ? array_map('wc_clean', $it['variation']) : array(); if ($pid && $qty > 0) { WC()->cart->add_to_cart($pid, $qty, $vid, $var); } } WC()->cart->calculate_totals(); } // Fix cart restoration logic add_action('wp_loaded', function() { if (!method_exists(WC()->session, 'get')) return; $token = WC()->session->get('pc_partial_token'); if ($token && WC()->cart->is_empty()) { $payload = get_transient(pc_transient_key($token)); if (!empty($payload['snapshot'])) { pc_restore_cart_from_items($payload['snapshot']); WC()->cart->calculate_totals(); } } }, 20); function pc_transient_key($token) { return 'pc_partial_payload_' . sanitize_key($token); } /* ------------------------------------------------- * AJAX: Local rehydrate when Woo cart is empty * ------------------------------------------------- */ add_action('wp_ajax_pc_rehydrate_cart', 'pc_rehydrate_cart'); add_action('wp_ajax_nopriv_pc_rehydrate_cart', 'pc_rehydrate_cart'); function pc_rehydrate_cart() { check_ajax_referer('woocommerce-cart', 'security'); $raw = isset($_POST['items']) ? wp_unslash($_POST['items']) : ''; $items = is_string($raw) ? json_decode($raw, true) : (array)$raw; if (!is_array($items)) { wp_send_json_error(array('message' => 'Invalid items.'), 400); } if (!WC()->cart) wc_load_cart(); if (!WC()->cart->is_empty()) { wp_send_json_success(array('message' => 'Cart not empty.')); } foreach ($items as $it) { $pid = isset($it['product_id']) ? (int)$it['product_id'] : 0; $vid = isset($it['variation_id']) ? (int)$it['variation_id'] : 0; $qty = isset($it['quantity']) ? wc_stock_amount($it['quantity']) : 0; $var = isset($it['variation']) && is_array($it['variation']) ? array_map('wc_clean', $it['variation']) : array(); if ($pid && $qty > 0) { WC()->cart->add_to_cart($pid, $qty, $vid, $var); } } WC()->cart->calculate_totals(); wp_send_json_success(array('rehydrated' => true)); } /* ------------------------------------------------- * AJAX: Update qty (per-row; no page reload) * ------------------------------------------------- */ add_action('wp_ajax_update_cart_item_qty', 'pc_update_cart_item_qty'); add_action('wp_ajax_nopriv_update_cart_item_qty', 'pc_update_cart_item_qty'); function pc_update_cart_item_qty() { check_ajax_referer('woocommerce-cart', 'security'); $key = isset($_POST['cart_item_key']) ? wc_clean(wp_unslash($_POST['cart_item_key'])) : ''; $qty = isset($_POST['qty']) ? wc_stock_amount($_POST['qty']) : null; if (!$key || $qty === null) { wp_send_json_error(array('message' => 'Missing params.'), 400); } if (!WC()->cart) wc_load_cart(); if ($qty <= 0) { $removed = WC()->cart->remove_cart_item($key); WC()->cart->calculate_totals(); wp_send_json_success(array('removed' => (bool)$removed)); } else { $set = WC()->cart->set_quantity($key, $qty, true); WC()->cart->calculate_totals(); $cart_item = WC()->cart->get_cart_item($key); if (!$cart_item) { wp_send_json_error(array('message' => 'Cart item not found after update.'), 404); } $_product = $cart_item['data']; $subtotal_html = apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal($_product, $cart_item['quantity']), $cart_item, $key ); // Use line_total + line_tax (after totals) for checkbox data-price $line_total_incl_tax = (float)($cart_item['line_total'] + $cart_item['line_tax']); wp_send_json_success(array( 'subtotal_html' => $subtotal_html, 'line_total_incl_tax' => $line_total_incl_tax, 'removed' => false, )); } } /* ------------------------------------------------- * AJAX: Remove selected * ------------------------------------------------- */ add_action('wp_ajax_remove_selected_cart_items', 'pc_remove_selected_cart_items'); add_action('wp_ajax_nopriv_remove_selected_cart_items', 'pc_remove_selected_cart_items'); function pc_remove_selected_cart_items() { check_ajax_referer('woocommerce-cart', 'security'); $keys = isset($_POST['selected_items']) ? (array) $_POST['selected_items'] : array(); if (!WC()->cart) wc_load_cart(); foreach ($keys as $k) { $k = wc_clean(wp_unslash($k)); WC()->cart->remove_cart_item($k); } WC()->cart->calculate_totals(); wp_send_json_success(true); } /* ------------------------------------------------- * AJAX: Empty cart * ------------------------------------------------- */ add_action('wp_ajax_empty_cart', 'pc_empty_cart'); add_action('wp_ajax_nopriv_empty_cart', 'pc_empty_cart'); function pc_empty_cart() { check_ajax_referer('woocommerce-cart', 'security'); if (!WC()->cart) wc_load_cart(); WC()->cart->empty_cart(); wp_send_json_success(true); } /* ------------------------------------------------- * AJAX: Apply coupon * ------------------------------------------------- */ add_action('wp_ajax_apply_coupon', 'pc_apply_coupon'); add_action('wp_ajax_nopriv_apply_coupon', 'pc_apply_coupon'); function pc_apply_coupon() { check_ajax_referer('woocommerce-cart', 'security'); $code = isset($_POST['coupon_code']) ? wc_format_coupon_code(wp_unslash($_POST['coupon_code'])) : ''; if (!$code) { wp_send_json_error(array('message' => __('请输入优惠券代码', 'woocommerce')), 400); } if (!WC()->cart) wc_load_cart(); $applied = WC()->cart->apply_coupon($code); WC()->cart->calculate_totals(); if (is_wp_error($applied)) { wp_send_json_error(array('message' => $applied->get_error_message()), 400); } if (!$applied) { wp_send_json_error(array('message' => __('优惠券应用失败', 'woocommerce')), 400); } wp_send_json_success(true); } /* ------------------------------------------------- * AJAX: Start partial checkout to regular checkout page * ------------------------------------------------- */ add_action('wp_ajax_create_direct_order', 'pc_create_direct_order'); add_action('wp_ajax_nopriv_create_direct_order', 'pc_create_direct_order'); function pc_create_direct_order() { check_ajax_referer('woocommerce-cart', 'security'); $selected_keys = isset($_POST['selected_items']) ? (array) $_POST['selected_items'] : array(); if (empty($selected_keys)) { wp_send_json_error(array('message' => __('请选择要结算的商品', 'woocommerce')), 400); } if (!WC()->cart) wc_load_cart(); // Snapshot full cart $snapshot = pc_snapshot_current_cart(); // Build selected items from current cart based on cart_item_key list $selected = array(); foreach (WC()->cart->get_cart() as $ci_key => $ci) { if (!in_array($ci_key, $selected_keys, true)) { continue; } $pid = (int)$ci['product_id']; $vid = (int)$ci['variation_id']; $qty = wc_stock_amount($ci['quantity']); $var = isset($ci['variation']) && is_array($ci['variation']) ? array_map('wc_clean', $ci['variation']) : array(); if ($pid && $qty > 0) { $selected[] = array( 'product_id' => $pid, 'variation_id' => $vid, 'variation' => $var, 'quantity' => $qty, ); } } if (empty($selected)) { wp_send_json_error(array('message' => __('没有可结算的商品', 'woocommerce')), 400); } $token = wp_generate_uuid4(); $payload = array( 'uid' => pc_get_cart_uid(), 'snapshot' => $snapshot, 'selected' => $selected, 'created' => time(), ); set_transient(pc_transient_key($token), $payload, 2 * DAY_IN_SECONDS); // Put token in session (used across checkout AJAX calls) if (method_exists(WC()->session, 'set')) { WC()->session->set('pc_partial_token', $token); } $checkout_url = add_query_arg('pc_token', rawurlencode($token), wc_get_checkout_url()); wp_send_json_success(array('checkout_url' => $checkout_url)); } /* ------------------------------------------------- * Virtualize cart on checkout for token and rebuild after purchase * ------------------------------------------------- */ // Entering checkout with token: virtualize cart to selected items add_action('woocommerce_before_checkout_form', function() { if (!isset($_GET['pc_token'])) return; $token = sanitize_text_field(wp_unslash($_GET['pc_token'])); $payload = get_transient(pc_transient_key($token)); if (empty($payload) || empty($payload['selected'])) return; if (!WC()->cart) wc_load_cart(); // Virtualize to selected items only pc_restore_cart_from_items($payload['selected']); // Persist token in session for all subsequent checkout AJAX calls if (method_exists(WC()->session, 'set')) { WC()->session->set('pc_partial_token', $token); } }, 1); // Safety: just-in-time re-virtualization before order processing add_action('woocommerce_before_checkout_process', function() { if (!method_exists(WC()->session, 'get')) return; $token = WC()->session->get('pc_partial_token'); if (!$token) return; $payload = get_transient(pc_transient_key($token)); if (empty($payload) || empty($payload['selected'])) return; // Ensure cart still equals selected set pc_restore_cart_from_items($payload['selected']); }, 1); // Tag order with token add_action('woocommerce_checkout_create_order', function($order) { $token = null; if (isset($_GET['pc_token'])) { $token = sanitize_text_field(wp_unslash($_GET['pc_token'])); } elseif (method_exists(WC()->session, 'get')) { $token = WC()->session->get('pc_partial_token'); } if ($token) { $order->update_meta_data('_pc_partial_token', $token); $order->update_meta_data('_pc_cart_snapshot', $token); } }, 10, 1); // Fixed: Only remove purchased items from cart add_action('woocommerce_thankyou', function($order_id) { $order = wc_get_order($order_id); if (!$order) return; $token = $order->get_meta('_pc_partial_token'); if (!$token) return; $payload = get_transient(pc_transient_key($token)); if (empty($payload) || empty($payload['snapshot'])) { if (method_exists(WC()->session, 'set')) { WC()->session->set('pc_partial_token', null); } delete_transient(pc_transient_key($token)); return; } // 1. Restore FULL snapshot (all items) pc_restore_cart_from_items($payload['snapshot']); WC()->cart->calculate_totals(); // 2. Remove only the purchased (selected) items foreach ($payload['selected'] as $selected_item) { $cart_item_key = pc_find_cart_item($selected_item['product_id'], $selected_item['variation_id']); if ($cart_item_key) { WC()->cart->remove_cart_item($cart_item_key); } } // 3. Update cart totals WC()->cart->calculate_totals(); // Cleanup token if (method_exists(WC()->session, 'set')) { WC()->session->set('pc_partial_token', null); } delete_transient(pc_transient_key($token)); }, 20); // Helper: Find cart item by product and variation ID function pc_find_cart_item($product_id, $variation_id = 0) { if (!WC()->cart) wc_load_cart(); foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) { $cart_pid = isset($cart_item['product_id']) ? (int)$cart_item['product_id'] : 0; $cart_vid = isset($cart_item['variation_id']) ? (int)$cart_item['variation_id'] : 0; if ($cart_pid == $product_id && $cart_vid == $variation_id) { return $cart_item_key; } } return false; } // Visiting cart with active token: restore full snapshot (cancel/back) add_action('woocommerce_before_cart', function() { if (!method_exists(WC()->session, 'get')) return; $token = WC()->session->get('pc_partial_token'); if (!$token) return; $payload = get_transient(pc_transient_key($token)); if (empty($payload) || empty($payload['snapshot'])) return; // Restore full snapshot so cart page always shows everything pc_restore_cart_from_items($payload['snapshot']); }, 1); /* ------------------------------------------------- * Keep cart count accurate during checkout process * ------------------------------------------------- */ add_filter('woocommerce_cart_contents_count', function($count) { // Check if partial token exists if (!method_exists(WC()->session, 'get')) return $count; $token = WC()->session->get('pc_partial_token'); if ($token) { $payload = get_transient(pc_transient_key($token)); // Always show full cart count even during checkout if (!empty($payload['snapshot'])) { $snapshot_count = 0; foreach ($payload['snapshot'] as $item) { $snapshot_count += (int)$item['quantity']; } return $snapshot_count; } } return $count; }); // Ensure cart item totals are accurate in header add_action('woocommerce_before_cart', function() { pc_maintain_cart_consistency(); }); add_action('woocommerce_before_checkout_form', function() { pc_maintain_cart_consistency(); }); function pc_maintain_cart_consistency() { if (!method_exists(WC()->session, 'get')) return; $token = WC()->session->get('pc_partial_token'); if (!$token) return; $payload = get_transient(pc_transient_key($token)); if (empty($payload) || empty($payload['snapshot'])) return; // Immediately restore full cart for consistent UI pc_restore_cart_from_items($payload['snapshot']); WC()->cart->calculate_totals(); }
09-06
每次重启项目1970-01-01 08:00:00.000 0-0 <no-tag> I ---------------------------- PROCESS ENDED (21062) for package com.example.kucun2 ---------------------------- 2025-06-16 02:13:56.761 22497-22497 cmd cmd E BBinder_init Processname cmd 2025-06-16 02:13:56.762 22497-22497 cmd cmd E BBinder_init hasGetProcessName cmd 2025-06-16 02:13:56.797 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.810 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684480, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.811 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684485, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.811 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684489, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.812 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684493, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.812 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684497, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.813 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684502, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.813 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684506, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.814 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684510, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.814 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684514, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.815 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684519, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.815 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684526, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.816 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684531, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.816 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684535, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.816 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684540, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.817 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684544, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.819 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684579, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.819 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684583, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.820 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684597, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.821 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684601, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.821 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684606, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.822 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684625, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.822 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684629, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.822 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684633, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.828 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684733, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.828 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684739, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.829 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684753, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.829 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684757, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.829 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684761, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.830 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684765, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.830 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684769, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.830 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684773, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.830 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684777, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.831 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684781, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.831 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684785, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.831 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684795, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.832 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684801, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.835 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684854, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.835 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684858, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.835 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684862, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.835 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684867, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.836 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.839 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.840 22500-22500 IPCThreadState service E Binder transaction failure. id: 152684962, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.841 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.842 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.847 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.850 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.850 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.852 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.853 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.856 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.860 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.869 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.870 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.877 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.881 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.895 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.900 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.907 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.909 22500-22500 Parcel service E Reading a NULL string not supported here. 2025-06-16 02:13:56.919 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686226, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.919 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686230, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.919 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686234, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.920 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686238, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.921 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686253, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.921 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686257, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.922 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686261, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.922 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686265, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.922 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686269, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.922 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686273, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.923 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686277, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.923 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686283, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.923 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686289, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.923 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686293, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.924 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686297, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.924 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686302, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.924 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686306, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.925 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686311, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.926 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686323, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.926 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686328, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.926 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686332, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.929 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686373, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.930 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686382, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.930 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686386, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.931 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686395, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.931 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686402, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.932 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686407, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.932 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686411, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.932 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686416, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.933 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686420, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.933 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686424, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.933 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686430, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.934 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686435, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.934 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686439, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.934 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686445, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.942 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686554, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.942 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686559, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.943 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686564, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.943 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686568, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.944 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686573, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.944 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686577, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.944 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686581, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.945 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686585, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.945 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686589, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.945 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686594, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.946 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686598, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.946 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686602, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.946 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686606, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.946 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686610, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.947 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686614, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.947 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686618, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.947 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686622, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.947 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686626, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.948 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686630, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.948 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686643, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.948 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686647, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.949 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686651, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.949 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686656, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.949 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686660, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.950 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686664, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.950 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686669, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.951 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686681, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.951 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686686, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.951 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686690, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.952 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686700, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.952 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686704, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.953 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686708, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.953 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686712, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.954 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686727, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.954 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686731, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.954 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686735, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.954 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686739, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.955 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686743, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.955 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686747, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.955 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686751, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.955 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686755, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.956 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686759, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.956 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686763, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.956 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686769, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.956 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686774, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.957 22500-22500 IPCThreadState service E Binder transaction failure. id: 152686779, BR_*: 29201, error: -1 (Operation not permitted) 2025-06-16 02:13:56.972 22500-22500 IPCThreadState service E Binder transaction failure. id: 152687050, BR_*: 29201, error: -1 (Operation not permitted) 1970-01-01 08:00:00.000 0-0 <no-tag> I ---------------------------- PROCESS STARTED (21838) for package com.example.kucun2 -----------package com.example.kucun2.entity.data; import static android.content.ContentValues.TAG; import android.content.Context; import android.util.Log; import com.example.kucun2.R; import com.example.kucun2.entity.Information; import com.example.kucun2.function.MyAppFnction; import com.example.kucun2.function.TLSUtils; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.lang.reflect.Type; import java.util.Map; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import okhttp3.Call; import okhttp3.Callback; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; public class ApiClient { // 使用Volley、Retrofit或OkHttp实现以下方法 private static final Gson gson = new Gson(); private static OkHttpClient client; private static final String TAG = "ApiClient"; private static final String CERT_FILE = "selfsigned.crt"; // 证书文件名 // 初始化方法 public static void init(Context context) { if (client != null) return; try { // 创建信任管理器 X509TrustManager trustManager = TLSUtils.createTrustManager(context, CERT_FILE); // 创建SSL上下文 SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{trustManager}, null); // 配置OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory(), trustManager) .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .build(); Log.d(TAG, "OkHttpClient initialized with custom certificate: " + CERT_FILE); } catch (Exception e) { Log.e(TAG, "Failed to initialize secure client", e); // 回退到默认配置(生产环境不应使用) client = new OkHttpClient(); } } public static <T, R> void post(String url, Information<T> requestData, ApiCallback<R> callback) { // 1. 构建请求体(JSON格式) String jsonRequest = ReflectionJsonUtils.toJson(requestData); RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonRequest); Log.d(TAG, "post: "+url); // 2. 创建POST请求 Request request = new Request.Builder() .url(url) .post(body) .build(); Log.d(TAG, "POST请求URL: " + url); Log.d(TAG, "请求数据: " + jsonRequest); // 3. 发送异步请求 client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "请求失败: " + e.getMessage()); if (callback != null) { callback.onError(-1, e.getMessage()); } } @Override public void onResponse(Call call, Response response) throws IOException { try (ResponseBody responseBody = response.body()) { if (!response.isSuccessful()) { Log.e(TAG, "服务器响应: " + response.code() + " - " + response.message()); if (callback != null) { callback.onError(response.code(), response.message()); } return; } // 4. 处理成功响应 String jsonResponse = responseBody.string(); Log.d(TAG, "服务器响应: " + jsonResponse); // 5. 解析为Information对象 // 注意:这里需要提前确定响应中data的类型(TypeToken) Type responseType = new TypeToken<Information<R>>() { }.getType(); Information<R> responseInfo = gson.fromJson(jsonResponse, responseType); if (callback != null) { callback.onSuccess(responseInfo); } } } }); } public static <T, R> void put(String url, Information<T> data, ApiCallback<T> callback) { String jsonRequest = ReflectionJsonUtils.toJson(data); RequestBody body = RequestBody.create(MediaType.parse("application/json"), jsonRequest); Request request = new Request.Builder() .url(url) .put(body) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "PUT request failed", e); } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful() && callback != null) { String json = response.body().string(); Type responseType = new TypeToken<Information<T>>(){}.getType(); Information<T> info = gson.fromJson(json, responseType); callback.onSuccess(info); } } }); } public static <R> void delete(String url, ApiCallback<R> callback) { Request request = new Request.Builder() .url(url) .delete() .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "DELETE request failed", e); } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful() && callback != null) { // 对于删除操作,通常返回空数据 callback.onSuccess(new Information<>(200, "Deleted", null)); } } }); } public static interface ApiCallback<T> { void onSuccess(Information<T> data); void onError(int statusCode, String error); } }package com.example.kucun2.entity.data; import android.content.Context; import android.content.SharedPreferences; import android.os.Handler; import android.os.Looper; import android.util.Log; import com.example.kucun2.entity.*; import com.example.kucun2.function.MyAppFnction; import com.example.kucun2.function.SafeLogger; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.lang.reflect.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; import okhttp3.*; /** * 核心数据管理中心 * 职责: * 1. 管理所有实体类的全局存储列表 * 2. 处理网络数据加载与本地持久化 * 3. 维护实体间关联关系 * 4. 管理数据同步状态 * * 优化点: * - 使用ConcurrentHashMap提升线程安全 * - 重构关联逻辑避免反射开销 * - 增强序列化/反序列化处理 * - 添加详细方法级注释 */ public class Data { // ====================== 实体存储区 ====================== // 所有实体列表使用线程安全的SynchronizedList // ====================== 静态数据列表声明 ====================== public static final SynchronizedList<Bancai> bancais = new SynchronizedList<>(Bancai.class); public static final SynchronizedList<Caizhi> caizhis = new SynchronizedList<>(Caizhi.class); public static final SynchronizedList<Mupi> mupis = new SynchronizedList<>(Mupi.class); public static final SynchronizedList<Chanpin> chanpins = new SynchronizedList<>(Chanpin.class); public static final SynchronizedList<Chanpin_Zujian> chanpin_zujians = new SynchronizedList<>(Chanpin_Zujian.class); public static final SynchronizedList<Dingdan> dingdans = new SynchronizedList<>(Dingdan.class); public static final SynchronizedList<Dingdan_Chanpin> dingdan_chanpins = new SynchronizedList<>(Dingdan_Chanpin.class); public static final SynchronizedList<Dingdan_chanpin_zujian> Dingdan_chanpin_zujians = new SynchronizedList<>(Dingdan_chanpin_zujian.class); public static final SynchronizedList<Kucun> kucuns = new SynchronizedList<>(Kucun.class); public static final SynchronizedList<Zujian> zujians = new SynchronizedList<>(Zujian.class); public static final SynchronizedList<User> users = new SynchronizedList<>(User.class); public static final SynchronizedList<Jinhuo> jinhuos = new SynchronizedList<>(Jinhuo.class); private static User user; // 实体类型与列表的映射表 <实体类, 对应的同步列表> public static final Map<Class, SynchronizedList<SynchronizableEntity>> dataCollectionMap = new ConcurrentHashMap<>();; private static final Gson gson = GsonFactory.createGson(); private static OkHttpClient client; private static final String TAG = "DataLoader"; // 静态初始化:建立实体类型与列表的映射关系 static { try { // 通过反射获取所有SynchronizedList字段 for (Field field : Data.class.getDeclaredFields()) { if (SynchronizedList.class.equals(field.getType())) { SynchronizedList<?> list = (SynchronizedList<?>) field.get(null); if (list != null) { // 将实体类型与列表关联 dataCollectionMap.put(list.getEntityType(), (SynchronizedList<SynchronizableEntity>) list); } } } } catch (IllegalAccessException e) { throw new RuntimeException("初始化dataCollectionMap失败", e); } } // ====================== 核心数据加载方法 ====================== /** * 从服务器加载全量数据 * @param context Android上下文 * @param callback 加载结果回调 */ public static void loadAllData(Context context, LoadDataCallback callback) { // 主线程检查 if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("必须在主线程调用Data.loadAllData"); } ensurePreservedObjects(); // 确保存在预置对象 // 使用传入的 Context 获取主线程 Handler Handler mainHandler = new Handler(context.getMainLooper()); // 确保使用安全的客户端 if (client == null) { client = MyAppFnction.getClient(); } ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(() -> { SynchronizableEntity.setSyncEnabled(false); String url = MyAppFnction.getStringResource("string", "url") + MyAppFnction.getStringResource("string", "url_all"); Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "Failed to load data", e); SynchronizableEntity.setSyncEnabled(true); mainHandler.post(() -> { if (callback != null) callback.onFailure(); }); } @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { Log.e(TAG, "Unexpected response code: " + response.code()); SynchronizableEntity.setSyncEnabled(true); mainHandler.post(() -> { if (callback != null) callback.onFailure(); }); return; } String responseData = response.body().string(); SynchronizableEntity.setSyncEnabled(true); ensurePreservedObjects(); // 在主线程处理解析 mainHandler.post(() -> { parseAndAssignData(responseData, context, callback); }); } }); }); } // ====================== 数据处理私有方法 ====================== /** * 解析JSON数据并更新到实体列表 */ private static void parseAndAssignData(String jsonData, Context context, LoadDataCallback callback) { try { // 解析顶层响应结构 Type responseType = new TypeToken<Information<AllDataResponse>>() {}.getType(); Information<AllDataResponse> info = gson.fromJson(jsonData, responseType); Log.d(TAG, "parseAndAssignData: "+jsonData); // 验证响应有效性 if (info == null || info.getData() == null || info.getStatus() != 200) { throw new IllegalStateException("无效服务器响应"); } AllDataResponse allData = info.getData(); SafeLogger.d("data", "原始数据: " + gson.toJson(allData)); // 更新所有数据列表 updateAllLists(allData); automaticAssociation(); // 建立实体关联 setAllEntitiesState(SynchronizableEntity.SyncState.MODIFIED); // 设置状态 safeCallback(callback, true); // 成功回调 } catch (Exception e) { Log.e(TAG, "数据处理异常: " + e.getMessage()); safeCallback(callback, false); } finally { SynchronizableEntity.setSyncEnabled(true); // 重新启用同步 } } /** * 批量更新所有实体列表 */ private static void updateAllLists(AllDataResponse allData) { updateList(bancais, allData.bancais); updateList(caizhis, allData.caizhis); updateList(mupis, allData.mupis); updateList(chanpins, allData.chanpins); updateList(chanpin_zujians, allData.chanpin_zujians); updateList(dingdans, allData.dingdans); updateList(dingdan_chanpins, allData.dingdan_chanpins); updateList(Dingdan_chanpin_zujians, allData.Dingdan_chanpin_zujians); updateList(kucuns, allData.kucuns); updateList(zujians, allData.zujians); updateList(users, allData.users); updateList(jinhuos, allData.jinhuos); } /** * 安全更新单个列表(保留预置对象) */ private static <T extends SynchronizableEntity> void updateList( List<T> existingList, List<T> newList ) { if (newList == null) return; // 保留现有列表中的预置对象 List<T> preservedItems = existingList.stream() .filter(item -> item != null && item.isPreservedObject()) .collect(Collectors.toList()); // 清空后重新添加(预置对象 + 有效新数据) existingList.clear(); existingList.addAll(preservedItems); existingList.addAll(newList.stream() .filter(item -> item != null && item.getId() != null && item.getId() != -1) .collect(Collectors.toList()) ); } /** * 确保每个列表都有预置对象(用于表示空值/默认值) */ private static void ensurePreservedObjects() { // 为每个实体类型检查并添加预置对象 addIfMissing(bancais, Bancai.class); addIfMissing(caizhis, Caizhi.class); addIfMissing(mupis, Mupi.class); addIfMissing(chanpins, Chanpin.class); addIfMissing(chanpin_zujians, Chanpin_Zujian.class); addIfMissing(dingdans, Dingdan.class); addIfMissing(kucuns, Kucun.class); addIfMissing(zujians, Zujian.class); addIfMissing(Dingdan_chanpin_zujians, Dingdan_chanpin_zujian.class); addIfMissing(dingdan_chanpins, Dingdan_Chanpin.class); addIfMissing(jinhuos, Jinhuo.class); addIfMissing(users, User.class); } private static <T extends SynchronizableEntity> void addIfMissing( List<T> list, Class<T> clazz ) { if (!containsPreservedObject(list)) { list.add(createInstance(clazz)); } } /** * 检查列表是否包含预置对象 * * @param list 目标实体列表 * @return 是否包含预置对象 */ private static <T extends SynchronizableEntity> boolean containsPreservedObject(List<T> list) { return list.stream().anyMatch(item -> item != null && item.isPreservedObject() ); } /** * 自动建立实体间关联关系(通过反射实现) */ private static void automaticAssociation() { for (Class<?> entityClass : dataCollectionMap.keySet()) { try { associateEntities(dataCollectionMap.get(entityClass)); } catch (Exception e) { Log.e(TAG, entityClass.getSimpleName() + " 关联失败", e); } } } private static <T extends SynchronizableEntity> void associateEntities( SynchronizedList<T> list ) throws IllegalAccessException, ClassNotFoundException { for (T entity : list) { if (entity == null) continue; for (Field field : entity.getClass().getDeclaredFields()) { field.setAccessible(true); Class<?> fieldType = field.getType(); // 处理实体引用字段 if (SynchronizableEntity.class.isAssignableFrom(fieldType)) { associateSingleReference(entity, field); } // 处理实体列表字段 else if (List.class.isAssignableFrom(fieldType)) { associateReferenceList(entity, field); } // 处理基础类型字段 else { setDefaultPrimitiveValue(entity, field); } } } } // ====================== 关联辅助方法 ====================== private static void associateSingleReference( SynchronizableEntity entity, Field field ) throws IllegalAccessException { SynchronizableEntity ref = (SynchronizableEntity) field.get(entity); Class<?> targetType = field.getType(); // 查找目标实体 SynchronizableEntity target = findTargetEntity(ref, targetType); field.set(entity, target); } private static void associateReferenceList( SynchronizableEntity entity, Field field ) throws IllegalAccessException, ClassNotFoundException { // 获取列表泛型类型 Type genericType = field.getGenericType(); if (!(genericType instanceof ParameterizedType)) return; Class<?> itemType = Class.forName( ((ParameterizedType) genericType).getActualTypeArguments()[0].getTypeName() ); // 只处理实体列表 if (!SynchronizableEntity.class.isAssignableFrom(itemType)) return; List<SynchronizableEntity> refList = (List<SynchronizableEntity>) field.get(entity); if (refList == null) { refList = new ArrayList<>(); field.set(entity, refList); } // 清理空值并重建引用 refList.removeAll(Collections.singleton(null)); for (int i = 0; i < refList.size(); i++) { refList.set(i, findTargetEntity(refList.get(i), itemType)); } } /** * 查找关联实体(优先匹配ID,找不到则使用预置对象) */ private static SynchronizableEntity findTargetEntity( SynchronizableEntity ref, Class<?> targetType ) { SynchronizedList<SynchronizableEntity> targetList = dataCollectionMap.get(targetType); if (targetList == null) return null; // 无效引用时返回预置对象 if (ref == null || ref.getId() == null || ref.getId() < 0) { return targetList.stream() .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null); } // 按ID查找目标实体 return targetList.stream() .filter(e -> ref.getId().equals(e.getId())) .findFirst() .orElseGet(() -> targetList.stream() // 找不到时回退到预置对象 .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null) ); } // ====================== 工具方法 ====================== /** * 创建带默认值的实体实例(用作预置对象) */ public static <T> T createInstance(Class<T> clazz) { try { T instance = clazz.getDeclaredConstructor().newInstance(); // 设置基础字段默认值 for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); setDefaultFieldValue(instance, field); } // 设置特殊字段 clazz.getMethod("setId", Integer.class).invoke(instance, -1); clazz.getMethod("setState", SynchronizableEntity.SyncState.class) .invoke(instance, SynchronizableEntity.SyncState.PRESERVED); return instance; } catch (Exception e) { Log.e("Data", "创建实例失败: " + clazz.getName(), e); try { return clazz.newInstance(); // 回退创建 } catch (Exception ex) { throw new RuntimeException("无法创建实例", ex); } } } private static <T> void setDefaultFieldValue(T instance, Field field) throws IllegalAccessException { Class<?> type = field.getType(); if (type == String.class) field.set(instance, "无"); else if (type == Integer.class || type == int.class) field.set(instance, -1); else if (type == Double.class || type == double.class) field.set(instance, -1.0); else if (type == Boolean.class || type == boolean.class) field.set(instance, false); else if (type == Date.class) field.set(instance, new Date()); else if (SynchronizableEntity.class.isAssignableFrom(type)) { field.set(instance, getPreservedEntity((Class<?>) type)); } else if (List.class.isAssignableFrom(type)) { field.set(instance, new ArrayList<>()); } } private static SynchronizableEntity getPreservedEntity(Class<?> type) { return dataCollectionMap.get(type).stream() .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null); } private static void setDefaultPrimitiveValue( SynchronizableEntity entity, Field field ) throws IllegalAccessException { if (field.get(entity) != null) return; Class<?> type = field.getType(); if (type == String.class) field.set(entity, "无"); else if (type == Integer.class || type == int.class) field.set(entity, -1); else if (type == Double.class || type == double.class) field.set(entity, -1.0); else if (type == Boolean.class || type == boolean.class) field.set(entity, false); else if (type == Date.class) field.set(entity, new Date()); } /** * 主线程安全回调 */ private static void safeCallback(LoadDataCallback callback, boolean success) { new Handler(Looper.getMainLooper()).post(() -> { if (callback == null) return; if (success) callback.onSuccess(); else callback.onFailure(); }); } /** * 设置所有实体同步状态 */ private static void setAllEntitiesState(SynchronizableEntity.SyncState state) { dataCollectionMap.values().forEach(list -> list.forEach(entity -> { if (entity != null) entity.setState(state); }) ); } public static String exportToJson() { ExportData exportData = new ExportData(); exportData.bancais = new ArrayList<>(bancais); exportData.caizhis = new ArrayList<>(caizhis); // 初始化其他列表... Gson gson = new GsonBuilder() .setPrettyPrinting() .registerTypeAdapter(SynchronizableEntity.class, new EntitySerializer()) .create(); return gson.toJson(exportData); } public static void importFromJson(String json, Context context) { Gson gson = new GsonBuilder() .registerTypeAdapter(SynchronizableEntity.class, new EntityDeserializer()) .create(); Type exportType = new TypeToken<ExportData>(){}.getType(); ExportData importData = gson.fromJson(json, exportType); // 更新数据列表 updateList(bancais, importData.bancais); updateList(caizhis, importData.caizhis); // 更新其他列表... automaticAssociation(); setAllEntitiesState(SynchronizableEntity.SyncState.MODIFIED); // 保存到SharedPreferences saveToPreferences(context, json); } private static void saveToPreferences(Context context, String json) { SharedPreferences prefs = context.getSharedPreferences("DataStore", Context.MODE_PRIVATE); prefs.edit().putString("jsonData", json).apply(); } public static void loadFromPreferences(Context context) { SharedPreferences prefs = context.getSharedPreferences("DataStore", Context.MODE_PRIVATE); String json = prefs.getString("jsonData", null); if (json != null) { importFromJson(json, context); } } // ====================== 内部类/接口 ====================== public interface LoadDataCallback { void onSuccess(); void onFailure(); } /** JSON响应数据结构 */ public static class AllDataResponse { public List<Bancai> bancais; public List<Caizhi> caizhis; public List<Mupi> mupis; public List<Chanpin> chanpins; public List<Chanpin_Zujian> chanpin_zujians; public List<Dingdan> dingdans; public List<Dingdan_Chanpin> dingdan_chanpins; public List<Dingdan_chanpin_zujian> Dingdan_chanpin_zujians; public List<Kucun> kucuns; public List<Zujian> zujians; public List<User> users; public List<Jinhuo> jinhuos; } // 在Data.java中添加 public static class ExportData { public List<Bancai> bancais; public List<Caizhi> caizhis; public List<Mupi> mupis; public List<Chanpin> chanpins; public List<Chanpin_Zujian> chanpin_zujians; public List<Dingdan> dingdans; public List<Dingdan_Chanpin> dingdan_chanpins; public List<Dingdan_chanpin_zujian> Dingdan_chanpin_zujians; public List<Kucun> kucuns; public List<Zujian> zujians; public List<User> users; public List<Jinhuo> jinhuos; } private static class EntityDeserializer implements JsonDeserializer<SynchronizableEntity> { @Override public SynchronizableEntity deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject obj = json.getAsJsonObject(); String entityType = obj.get("entityType").getAsString(); Integer id = obj.get("id").getAsInt(); // 创建临时实体(只包含ID) try { Class<?> clazz = Class.forName("com.example.kucun2.entity." + entityType); SynchronizableEntity entity = (SynchronizableEntity) clazz.newInstance(); entity.setId(id); return entity; } catch (Exception e) { return null; } } } private static class EntitySerializer implements JsonSerializer<SynchronizableEntity> { @Override public JsonElement serialize(SynchronizableEntity src, Type typeOfSrc, JsonSerializationContext context) { JsonObject obj = new JsonObject(); obj.addProperty("id", src.getId()); obj.addProperty("entityType", src.getClass().getSimpleName()); return obj; } } }package com.example.kucun2; import android.os.Bundle; import android.os.Looper; import android.view.View; import android.view.Menu; import com.example.kucun2.entity.data.ApiClient; import com.example.kucun2.entity.data.Data; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.navigation.NavigationView; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import androidx.drawerlayout.widget.DrawerLayout; import androidx.appcompat.app.AppCompatActivity; import com.example.kucun2.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { private AppBarConfiguration mAppBarConfiguration; private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 启动数据加载 loadAppData(); } private void initUI() { if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("Must be called on the main thread"); } ApiClient.init(this); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); // ... 其他初始化代码 ... setSupportActionBar(binding.appBarMain.toolbar); binding.appBarMain.fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null) .setAnchorView(R.id.fab).show(); } }); DrawerLayout drawer = binding.drawerLayout; NavigationView navigationView = binding.navView; // Passing each menu ID as a set of Ids because each // menu should be considered as top level destinations. mAppBarConfiguration = new AppBarConfiguration.Builder( R.id.nav_home, R.id.nav_kucun, R.id.nav_add_jinhuo, R.id.nav_diandan) .setOpenableLayout(drawer) .build(); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration); NavigationUI.setupWithNavController(navigationView, navController); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onSupportNavigateUp() { NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); return NavigationUI.navigateUp(navController, mAppBarConfiguration) || super.onSupportNavigateUp(); } public interface OnDataLoadListener { void onDataLoaded(); void onDataError(); } private OnDataLoadListener dataLoadListener; public void setOnDataLoadListener(OnDataLoadListener listener) { this.dataLoadListener = listener; } private void loadAppData() { Data.loadAllData(getApplicationContext(), new Data.LoadDataCallback() { @Override public void onSuccess() { runOnUiThread(() -> { if (dataLoadListener != null) { runOnUiThread(dataLoadListener::onDataLoaded); } initUI(); }); } @Override public void onFailure() { runOnUiThread(() -> { if (dataLoadListener != null) { runOnUiThread(dataLoadListener::onDataError); } }); } }); } }
06-17
"{\"headers\":{},\"body\":null,\"status\":\"200\", \"message\":\"OK\",\"mediaType\":null,\"payload\": \"{\\\"totalPages\\\":1,\\\"totalElements\\\":1,\\ \"numberOfElements\\\":1,\\\"size\\\":1,\\\"number \\\":0,\\\"content\\\":[{\\\"soNum\\\":\\\"SO20250 5210002\\\",\\\"sopOuCode\\\":\\\"2120\\\",\\\"soT ypeCode\\\":\\\"STANDARD_SO\\\",\\\"soStatus\\\":\ \\"RELEASED\\\",\\\"customerNumber\\\":\\\"9001\\\ ",\\\"totalAmount\\\":\\\"100.000000\\\",\\\"taxRa te\\\":\\\"0.13\\\",\\\"taxIncludedAmount\\\":\\\" 113.000000\\\",\\\"currency\\\":\\\"CNY\\\",\\\"cr eationDate\\\":\\\"2025-05-21 21:02:18\\\",\\\"las tUpdateDate\\\":\\\"2025-05-21 21:02:21\\\",\\\"ex ternalNum\\\":\\\"1\\\",\\\"remark\\\":\\\"备注\\\", \\\"salesman\\\":\\\"张三\\\",\\\"lineList\\\":[{\\\ "soLineNum\\\":\\\"1\\\",\\\"itemCode\\\":\\\"301- 00001\\\",\\\"demandQty\\\":\\\"10.000000\\\",\\\" shippedQty\\\":\\\"10.000000\\\",\\\"uom\\\":\\\"E A\\\",\\\"demandDate\\\":\\\"2025-05-21\\\",\\\"so LineStatus\\\":\\\"RELEASED\\\",\\\"soLineType\\\" :\\\"SHIP\\\",\\\"taxIncludedPrice\\\":\\\"11.3000 00\\\",\\\"unitPrice\\\":\\\"10.000000\\\",\\\"tax IncludedAmount\\\":\\\"113.000000\\\",\\\"lineAmou nt\\\":\\\"100.000000\\\",\\\"externalNum\\\":null ,\\\"lineRemark\\\":null}]}],\\\"empty\\\":false}\ ",\"apiInfo\":null,\"targetResponseHeaders\":{\"da te\":\"Wed, 28 May 2025 09:23:29 GMT\",\"expires\" :\"0\",\"transfer-encoding\":\"chunked\",\"x-conte nt-type-options\":\"nosniff\",\"x-xss-protection\" :\"1; mode=block\",\"connection\":\"keep-alive\",\ "content-type\":\"application/json;charset=UTF-8\" ,\"cache-control\":\"no-cache, no-store, max-age=0 , must-revalidate\",\"pragma\":\"no-cache\"},\"inv okeKey\":\"5487a737-dc48-45f0-af04-bc8a57d976ce\", \"responseContentType\":{\"mimeType\":\"text/plain \",\"charset\":\"ISO-8859-1\"},\"statusCode\":\"OK \",\"statusCodeValue\":200}" 使用ABAP解析进一个表格中
05-30
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值