diff --git a/src/main/java/com/project/logistics/domain/service/SfApiService.java b/src/main/java/com/project/logistics/domain/service/SfApiService.java index 85dcdc0..6759ed6 100644 --- a/src/main/java/com/project/logistics/domain/service/SfApiService.java +++ b/src/main/java/com/project/logistics/domain/service/SfApiService.java @@ -78,24 +78,61 @@ public class SfApiService { /** * 执行二进制流上传 (HTTP PUT) */ - private void executeBinaryPut(String uploadUrl, byte[] pdfBytes, String fileMd5) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); - headers.set("Content-MD5", fileMd5); // 必须与第一步的 fileMd5 一致 +// private void executeBinaryPut(String uploadUrl, byte[] pdfBytes, String fileMd5) { +// HttpHeaders headers = new HttpHeaders(); +// headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); +// headers.set("Content-MD5", fileMd5); // 必须与第一步的 fileMd5 一致 +// +// HttpEntity requestEntity = new HttpEntity<>(pdfBytes, headers); +// try { +// // 使用 PUT 方法上传 +// ResponseEntity response = restTemplate.exchange(uploadUrl, HttpMethod.PUT, requestEntity, String.class); +// if (response.getStatusCode() != HttpStatus.OK) { +// throw new RuntimeException("文件流上传失败, 状态码: " + response.getStatusCode()); +// } +// } catch (Exception e) { +// log.error(">>> 二进制 PUT 上传发生异常", e); +// throw new RuntimeException("SF_FILE_PUT_FAILED"); +// } +// } - HttpEntity requestEntity = new HttpEntity<>(pdfBytes, headers); + private void executeBinaryPut(String uploadUrl, byte[] pdfBytes, String fileMd5) { try { - // 使用 PUT 方法上传 - ResponseEntity response = restTemplate.exchange(uploadUrl, HttpMethod.PUT, requestEntity, String.class); + // 【核心修正 1】:使用 java.net.URI 包装 URL,防止 RestTemplate 自动转义参数 + java.net.URI uri = new java.net.URI(uploadUrl); + + HttpHeaders headers = new HttpHeaders(); + + // 【核心修正 2】:Content-Type 设为二进制流,或者根据顺丰要求设为空 + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + + // 【核心修正 3】:确保 Content-MD5 Header 存在且与第一步一致 + headers.set("Content-MD5", fileMd5); + + // 有些环境可能需要强制指定 Content-Length + headers.setContentLength(pdfBytes.length); + + HttpEntity requestEntity = new HttpEntity<>(pdfBytes, headers); + + log.info(">>> 正在发起 PUT 上传,目标路径: {}", uri.getPath()); + + // 执行请求 + ResponseEntity response = restTemplate.exchange( + uri, + HttpMethod.PUT, + requestEntity, + String.class + ); + if (response.getStatusCode() != HttpStatus.OK) { - throw new RuntimeException("文件流上传失败, 状态码: " + response.getStatusCode()); + log.error(">>> 上传失败,响应码: {}, 响应体: {}", response.getStatusCode(), response.getBody()); + throw new RuntimeException("SF_PUT_FILE_FAILED"); } } catch (Exception e) { - log.error(">>> 二进制 PUT 上传发生异常", e); - throw new RuntimeException("SF_FILE_PUT_FAILED"); + log.error(">>> PUT 上传过程发生异常: {}", e.getMessage()); + throw new RuntimeException("SF_PUT_FILE_EXCEPTION", e); } } - /** * 通用 API 调用基础方法 (Content-Type: x-www-form-urlencoded) */ diff --git a/src/main/resources/application-sftest.yml b/src/main/resources/application-sftest.yml new file mode 100644 index 0000000..1585571 --- /dev/null +++ b/src/main/resources/application-sftest.yml @@ -0,0 +1,118 @@ +server: + port: 9088 +spring: + main: + # 允许 Bean 覆盖,解决 dynamic-datasource 与 JPA 的初始化冲突 + allow-bean-definition-overriding: true + datasource: + url: jdbc:mysql://8.129.84.155:3306/auto_logistics_dev?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useSSL=false + username: logistics_admin + password: Itc@123456 + driver-class-name: com.mysql.cj.jdbc.Driver + + # dynamic: + # primary: master + # datasource: + # master: + # driverClassName: com.mysql.cj.jdbc.Driver + # password: Itc@123456 + # url: jdbc:mysql://8.129.84.155:3306/auto_logistics?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useSSL=false + # username: logistics_admin + + data: + redis: + host: 8.129.84.155 + port: 6379 + password: 123456 + database: 5 + timeout: 5000ms + lettuce: + pool: + max-active: 8 + max-idle: 30 + max-wait: 10000 + min-idle: 10 + jpa: + hibernate: + # 确保是 update + ddl-auto: update + # 显式指定数据库平台 + database-platform: org.hibernate.dialect.MySQL8Dialect + show-sql: true + # 关键:告诉 Hibernate 自动扫描实体类 + open-in-view: true + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL8Dialect + # 显式指定命名策略,防止大小写或下划线解析错误 + physical_strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl +sf: + api: + partnerId: KJNSZ19PJ7VQ + secret: OG2rwyxIwb3LkmFi9bObYV38DQZcdtsb + tokenUrl: https://sfapi.sit.sf-express.com:45273/oauth2/accessToken + baseUrl: https://sfapi.sit.sf-express.com:45273/std/service + channelCode: MCS-CAS-API-BOX +mybatis-plus: + configuration: + # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发环境下打印SQL + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl + map-underscore-to-camel-case: true # 开启驼峰命名 + global-config: + db-config: + id-type: assign_id # 使用雪花算法生成本地订单主键ID +webdav: + url: "http://8.129.84.155:8881" + username: "admin" + password: "123456" +logistics: + scanner: + enabled: true + # 每 10 分钟唤醒一次 (系统开销微乎其微) + cron: "0 0/10 * * * ?" + windows: + # 这个窗口设置10分钟有效期,配合10分钟一次的唤醒,只会跑一次 + - name: "上午11点班次" + startTime: "11:08" + endTime: "11:08" + intervalMinutes: 60 + # 这个窗口在15-20点之间,每隔20分钟会真正跑一次逻辑 + - name: "下午至傍晚波次" + startTime: "15:08" + endTime: "20:08" + intervalMinutes: 60 + sample-scanner: + enabled: true + # 每 10 分钟唤醒一次 (系统开销微乎其微) + cron: "0 0/10 * * * ?" + windows: + # 这个窗口设置10分钟有效期,配合10分钟一次的唤醒,只会跑一次 + - name: "上午11点班次" + startTime: "11:08" + endTime: "11:08" + intervalMinutes: 60 + # 这个窗口在15-20点之间,每隔20分钟会真正跑一次逻辑 + - name: "下午至傍晚波次" + startTime: "15:08" + endTime: "20:08" + intervalMinutes: 20 +logging: + level: + # 强制打印 Hibernate 初始化过程 + org.hibernate.SQL: debug + org.hibernate.orm.deprecation: error + org.hibernate.tool.schema: debug + # 看看 Spring 到底有没有加载 JPA + org.springframework.orm.jpa: debug +u9-source: + url: jdbc:sqlserver://192.168.4.202:1433;databaseName=20241030;encrypt=false;trustServerCertificate=true + username: sa + password: 'Liujun1928374650' + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver +fixed-rule: + senderPayContact: '王盛荣' + senderMobile: '13480155048' + senderAddress: '广东省广州市番禺区石碁镇南荔东路56号' + monthlyCard: '9999999999' +scheduled-task: + owner: test \ No newline at end of file diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 3aa1aeb..3397e26 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -52,7 +52,7 @@ sf: secret: ZXmoWOQdSd2UTBmSP6Kv3VW9Q4N5dJqz tokenUrl: https://sfapi-sbox.sf-express.com/oauth2/accessToken baseUrl: https://sfapi-sbox.sf-express.com/std/service - channelCode: MCS-CAS-API-BOX + channelCode: INC-VMOS-CORE mybatis-plus: configuration: # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发环境下打印SQL @@ -78,8 +78,8 @@ logistics: intervalMinutes: 60 # 这个窗口在15-20点之间,每隔20分钟会真正跑一次逻辑 - name: "下午至傍晚波次" - startTime: "15:00" - endTime: "20:00" + startTime: "15:08" + endTime: "20:08" intervalMinutes: 60 sample-scanner: enabled: true @@ -88,14 +88,14 @@ logistics: windows: # 这个窗口设置10分钟有效期,配合10分钟一次的唤醒,只会跑一次 - name: "上午11点班次" - startTime: "11:00" - endTime: "11:10" + startTime: "11:08" + endTime: "11:08" intervalMinutes: 60 # 这个窗口在15-20点之间,每隔20分钟会真正跑一次逻辑 - name: "下午至傍晚波次" - startTime: "15:00" - endTime: "20:00" - intervalMinutes: 20 + startTime: "15:08" + endTime: "20:08" + intervalMinutes: 60 logging: level: # 强制打印 Hibernate 初始化过程 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4884923..3057fac 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -88,14 +88,14 @@ logistics: windows: # 这个窗口设置10分钟有效期,配合10分钟一次的唤醒,只会跑一次 - name: "上午11点班次" - startTime: "11:00" - endTime: "11:10" + startTime: "11:08" + endTime: "11:08" intervalMinutes: 60 # 这个窗口在15-20点之间,每隔20分钟会真正跑一次逻辑 - name: "下午至傍晚波次" - startTime: "15:00" - endTime: "20:00" - intervalMinutes: 20 + startTime: "15:08" + endTime: "20:08" + intervalMinutes: 60 logging: level: # 强制打印 Hibernate 初始化过程 @@ -115,4 +115,4 @@ fixed-rule: senderAddress: '广东省广州市番禺区石碁镇南荔东路56号' monthlyCard: '7551234567' scheduled-task: - owner: local \ No newline at end of file + owner: test \ No newline at end of file