commit 9b6996e0243997afaeb8f5db22b1194e7bfa6b23
Author: huzhengkao <562572218@qq.com>
Date: Fri May 30 13:50:28 2025 +0800
first commit
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..bcc56a2
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..fade66b
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..6560a98
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..3831ef0
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..919019a
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..5cbefd7
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..e96534f
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000..a45eb6b
--- /dev/null
+++ b/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..2cc7d4a
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..642d572
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/HELP.md b/HELP.md
new file mode 100644
index 0000000..5517db2
--- /dev/null
+++ b/HELP.md
@@ -0,0 +1,16 @@
+# Getting Started
+
+### Reference Documentation
+For further reference, please consider the following sections:
+
+* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
+* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/maven-plugin/)
+* [Spring Web](https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications)
+
+### Guides
+The following guides illustrate how to use some features concretely:
+
+* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
+* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
+* [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/)
+
diff --git a/d2d.pfx b/d2d.pfx
new file mode 100644
index 0000000..15cf0f4
Binary files /dev/null and b/d2d.pfx differ
diff --git a/mvnw b/mvnw
new file mode 100644
index 0000000..a16b543
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd.folderdownload.crdownload b/mvnw.cmd.folderdownload.crdownload
new file mode 100644
index 0000000..457b7a6
Binary files /dev/null and b/mvnw.cmd.folderdownload.crdownload differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..1226589
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,114 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.5.RELEASE
+
+
+ com.alihealth.d2d
+ provtest
+ 0.0.1-SNAPSHOT
+ war
+ provtest
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+ com.alibaba
+ fastjson
+ 1.2.66
+
+
+ commons-codec
+ commons-codec
+ 1.13
+
+
+
+
+ cn.hutool
+ hutool-all
+ 4.0.12
+
+
+
+ org.bouncycastle
+ bcpkix-jdk15on
+ 1.70
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.38
+
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+
+ com.alibaba
+ druid-spring-boot-starter
+ 1.2.23
+
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ 3.5.1
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/provtest.iml b/provtest.iml
new file mode 100644
index 0000000..1daccae
--- /dev/null
+++ b/provtest.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sql/data.sql b/sql/data.sql
new file mode 100644
index 0000000..0387f3f
--- /dev/null
+++ b/sql/data.sql
@@ -0,0 +1,48 @@
+CREATE TABLE receive_data_log (
+ event_id VARCHAR(32) PRIMARY KEY COMMENT '事件流水号',
+ process_id VARCHAR(32) NOT NULL COMMENT '传输标记号',
+ event_type VARCHAR(100) NOT NULL COMMENT '数据文件内容主业务类型',
+ sub_type VARCHAR(50) NOT NULL COMMENT '数据文件内容子业务类型',
+ data TEXT COMMENT '数据文件内容',
+ ver VARCHAR(10) COMMENT 'API协议版本',
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ is_deleted TINYINT DEFAULT 0 COMMENT '是否删除(0-否 1-是)',
+ INDEX idx_process_id (process_id),
+ INDEX idx_event_type (event_type),
+ INDEX idx_sub_type (sub_type)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='接收数据日志信息表';
+
+CREATE TABLE domestic_pharma_base_info (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
+ event_id VARCHAR(32) NOT NULL COMMENT '事件流水号',
+ tyshxydm VARCHAR(20) NOT NULL COMMENT '统一社会信用代码',
+ jnypscqymc VARCHAR(100) NOT NULL COMMENT '生产企业名称',
+ jnypscqylx VARCHAR(50) COMMENT '企业类型',
+ zsdz VARCHAR(200) COMMENT '注册地址',
+ zsdzgjhdq VARCHAR(10) COMMENT '注册地址国家/地区代码',
+ zsdzszxszzq VARCHAR(10) COMMENT '注册地址所在行政区',
+ zsdzsqzzzm VARCHAR(10) COMMENT '注册地址社区/镇/村',
+ zsdzxzzxxjs VARCHAR(10) COMMENT '注册地址乡镇/街道',
+ zsdzxzjdbsc VARCHAR(100) COMMENT '注册地址详细街道办/社区',
+ zsdzcjlld VARCHAR(100) COMMENT '注册地址村居/路/楼栋',
+ zsdzmphm VARCHAR(100) COMMENT '注册地址门牌号码',
+ fddbr VARCHAR(50) COMMENT '法定代表人',
+ zczb VARCHAR(50) COMMENT '注册资本(万元)',
+ clrq DATE COMMENT '成立日期',
+ yyqx VARCHAR(50) COMMENT '营业期限',
+ jyfw VARCHAR(500) COMMENT '经营范围',
+ djjg VARCHAR(200) COMMENT '登记机关',
+ gddhhm VARCHAR(20) COMMENT '固定电话号码',
+ czhm VARCHAR(20) COMMENT '传真号码',
+ dzxx VARCHAR(100) COMMENT '电子信箱',
+ qywz VARCHAR(100) COMMENT '企业网址',
+ lxr VARCHAR(50) COMMENT '联系人',
+ lxdh VARCHAR(20) COMMENT '联系电话',
+ zsgps VARCHAR(50) COMMENT 'GPS坐标(经度,纬度)',
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ is_deleted TINYINT DEFAULT 0 COMMENT '是否删除(0-否 1-是)',
+ INDEX idx_jnypscqymc (jnypscqymc),
+ INDEX idx_zsdzszxszzq (zsdzszxszzq)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='境内药品生产企业基本信息数据表';
\ No newline at end of file
diff --git a/src/main/java/com/alihealth/d2d/provtest/ProvTestClient.java b/src/main/java/com/alihealth/d2d/provtest/ProvTestClient.java
new file mode 100644
index 0000000..c0147d9
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/ProvTestClient.java
@@ -0,0 +1,370 @@
+package com.alihealth.d2d.provtest;
+
+import cn.hutool.http.HttpRequest;
+import com.alibaba.fastjson.JSONObject;
+import com.alihealth.d2d.provtest.utils.Base64;
+import com.alihealth.d2d.provtest.utils.EventPojo;
+import com.alihealth.d2d.provtest.utils.KeyStoreUtil;
+import com.alihealth.d2d.provtest.utils.StreamUtil;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.*;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * @ClassName ProvTestClient
+ * @Desc 省局请求 客户端 请求端
+ * @Author houduo.wk
+ * @Date 2020/3/13
+ **/
+public class ProvTestClient {
+
+ /**
+ * 请求url
+ */
+ private static final String url = "http://localhost:8888/prov/test/receive";
+ /**
+ * 证书地址
+ */
+ private static final String PFX_FILE_PATH = "D:\\ynyp\\药品追溯\\d2d\\yunnan.pfx";
+ /**
+ * 证书密码
+ */
+ private static final String PFX_PASSWORD = "12345678";
+ /**
+ * 加密算法
+ */
+ private static final String algorithm = "RSA";
+
+
+ public static void main(String[] args) throws Exception {
+ //入参
+ String fileName = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 20);
+ //需要同步的xml
+ String data = StreamUtil.getStreamContent(fileName, getData());
+ //流水号
+ String processId = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 20);
+ //事件编号
+ String eventId = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 20);
+ //数据文件内容主业务类型 10:基础信息数据
+ //20:应用信息数据
+ String eventType = "10";
+ //数据文件内容子业务类型 详见:数据类型字典表
+ String subType = "1011";
+
+ //获取公钥
+ String publicKey = getPublicKey();
+ System.out.println("publicKey:"+ publicKey);
+ String privateKey = getPrivateKey();
+ System.out.println("privateKey:"+ privateKey);
+ //公钥加密
+ String en = testEncrypt(publicKey, data);
+ System.out.println("publicKey加密后en:" + en);
+
+ EventPojo eventPojo = new EventPojo();
+ eventPojo.setData(en);
+ eventPojo.setSubType(subType);
+
+ //请求时间戳
+ String timestamp = formatDateToyyyyMMddHHmm(new Date());
+
+ //请求时间戳
+ String ver = "1.0";
+
+ Map paramMap = new HashMap<>();
+ paramMap.put("process_id", processId);
+ paramMap.put("event_id", eventId);
+ paramMap.put("event_type", eventType);
+ paramMap.put("event", JSONObject.toJSONString(eventPojo));
+ paramMap.put("timestamp", timestamp);
+ paramMap.put("ver", ver);
+ Map requestMap= new HashMap<>(4);
+ requestMap.put("req", paramMap);
+ System.out.println(JSONObject.toJSONString(requestMap));
+ String body = HttpRequest.post(url).body(JSONObject.toJSONString(requestMap)).timeout(10000).execute().body();
+ System.out.println(body);
+
+
+
+ }
+
+ /**
+ * 公钥加密
+ * @param key
+ * @param data
+ * @return
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeySpecException
+ * @throws NoSuchPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws BadPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ */
+ public static String testEncrypt(String key,String data) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException{
+ byte[] decode = java.util.Base64.getDecoder().decode(key);
+ RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(algorithm).generatePublic(new X509EncodedKeySpec(decode));
+ //RSA加密
+ Cipher ci = Cipher.getInstance(algorithm);
+ ci.init(Cipher.ENCRYPT_MODE, pubKey);
+
+
+ byte[] bytes = data.getBytes();
+ int inputLen = bytes.length;
+ int offLen = 0;//偏移量
+ int i = 0;
+ ByteArrayOutputStream bops = new ByteArrayOutputStream();
+ while(inputLen - offLen > 0){
+ byte [] cache;
+ if(inputLen - offLen > 117){
+ cache = ci.doFinal(bytes, offLen,117);
+ }else{
+ cache = ci.doFinal(bytes, offLen,inputLen - offLen);
+ }
+ bops.write(cache);
+ i++;
+ offLen = 117 * i;
+ }
+ bops.close();
+ byte[] encryptedData = bops.toByteArray();
+ String encodeToString = java.util.Base64.getEncoder().encodeToString(encryptedData);
+ return encodeToString;
+ }
+
+ /**
+ * 获取私钥
+ * @return
+ * @throws Exception
+ */
+ private static String getPrivateKey() throws Exception {
+ KeyStore keyStore = KeyStoreUtil.loadKetStore(PFX_FILE_PATH,PFX_PASSWORD);
+ return Base64.encode(KeyStoreUtil.getPrivateKey(keyStore, PFX_PASSWORD).getEncoded());
+ }
+
+ /**
+ * 获取公钥
+ * @return
+ * @throws Exception
+ */
+ private static String getPublicKey() throws Exception {
+ KeyStore keyStore = KeyStoreUtil.loadKetStore(PFX_FILE_PATH,PFX_PASSWORD);
+ PublicKey publicKey = KeyStoreUtil.getPublicKey(keyStore);
+ return Base64.encode(publicKey.getEncoded());
+ }
+ /**
+ * 返回时间字符串, 可读形式的, yy年M月d日HH:mm 格式.
+ *
+ * @return - String 格式化后的时间
+ */
+ public static String formatDateToyyyyMMddHHmm(Date date) {
+ if (date == null) {
+ return "";
+ }
+
+ SimpleDateFormat dateFormat = new SimpleDateFormat(
+ "yyyyMMddHHmmss");
+
+ return dateFormat.format(date);
+ }
+ /**
+ * 获取用于传输的xml
+ * @return
+ */
+// private static String getData(){
+// return "\n" +
+// "\n" +
+// "\n" +
+// "\n" +
+// " 疫苗生产企业基本信息数据子集\n" +
+// " \n" +
+// " \n" +
+// " 912101131181555503\n" +
+// " 辽宁依生生物制药有限公司\n" +
+// " 有限公司\n" +
+// " 新城子区财落镇大辛二村\n" +
+// " 156\n" +
+// " 210000\n" +
+// " 210100\n" +
+// " 000044\n" +
+// " 新城子区财落镇大辛二村\n" +
+// " 新城子区财落镇大辛二村\n" +
+// " 新城子区财落镇大辛二村\n" +
+// " 张译\n" +
+// " 4105.512万\n" +
+// " 19190101\n" +
+// " 2005-06-29至无固定期限\n" +
+// " 无\n" +
+// " 沈阳市沈北新区市场监督管理局\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " \n" +
+// "\n" +
+// "\n" +
+// " \n" +
+// " 91120116681888972M\n" +
+// " 康希诺生物股份公司\n" +
+// " 股份有限公司(中外合资、上市)\n" +
+// " 天津经济技术开发区西区南大街185号西区生物医药园四层401-420号\n" +
+// " 156\n" +
+// " 120000\n" +
+// " 120000\n" +
+// " 000035\n" +
+// " 西区南大街185号西区生物医药园四层401-420号\n" +
+// " 西区南大街185号西区生物医药园四层401-420号\n" +
+// " 生物医药园四层401-420号\n" +
+// " YU XUEFENG(宇学峰)\n" +
+// " 77706.99万\n" +
+// " 19910101\n" +
+// " 2009-01-13至2059-01-12\n" +
+// " 无\n" +
+// " 天津市滨海新区市场和质量监督管理局\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " www.cansinotech.com\n" +
+// " 无\n" +
+// " 无\n" +
+// " \n" +
+// "\n" +
+// "\n" +
+// "\n" +
+// " \n" +
+// " 9132129168412218X6\n" +
+// " 江苏金迪克生物技术有限公司\n" +
+// " 有限公司\n" +
+// " 泰州市郁金路12号\n" +
+// " 156\n" +
+// " 320000\n" +
+// " 321200\n" +
+// " 000025\n" +
+// " 郁金路12号\n" +
+// " 郁金路12号\n" +
+// " 郁金路12号\n" +
+// " 余军\n" +
+// " 无\n" +
+// " 19900101\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " \n" +
+// "\n" +
+// "\n" +
+// " \n" +
+// " 912200007307650053\n" +
+// " 吉林亚泰生物药业股份有限公司\n" +
+// " 股份有限公司\n" +
+// " 吉林省长春市高新开发区北湖科技开发区高科技中心B区537-A室\n" +
+// " 156\n" +
+// " 220000\n" +
+// " 220100\n" +
+// " 000000\n" +
+// " 高新开发区北湖科技开发区高科技中心B区537-A室\n" +
+// " 高新开发区北湖科技开发区高科技中心B区537-A室\n" +
+// " 高科技中心B区537-A室\n" +
+// " 刘晓峰\n" +
+// " 43564.770000万\n" +
+// " 19940307\n" +
+// " 2010-06-30至2020-12-31\n" +
+// " 小容量注射剂(卡介菌多糖核酸注射液)、疫苗[人用狂犬病疫苗(地鼠肾细胞)]生产(药品生产许可证有效期至2020年12月31日)及技术咨询、技术服务、研究开发;医疗器械研发、生产和销售;第Ⅰ类、第Ⅱ类、第Ⅲ类体外诊断试剂研发、生产及销售;消毒产品(危险化学品除外)的研发、生产及销售。(依法须经批准的项目,经相关部门批准后方可开展经营活动)\n" +
+// " 吉林省市场监督管理厅\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " 无\n" +
+// " \n" +
+// " \n" +
+// " \n" +
+// " \n";
+// }
+
+
+ private static String getData(){
+ return "\n" +
+ "\n" +
+ " \n" +
+ "境内药品生产企业基本信息数据子集\n" +
+ "\n" +
+ "\n" +
+ "911100007635353107\n" +
+ "上海药业有限公司\n" +
+ "股份有限公司\n" +
+ "吉林省长春市高新开发区北湖科技开发区高科技中心B区537-A室 \n" +
+ "156\n" +
+ "220000\n" +
+ "220100\n" +
+ "000000\n" +
+ "高新开发区北湖科技开发区高科技中心B区537-A室 \n" +
+ "高新开发区北湖科技开发区高科技中心B区537-A室\n" +
+ "高科技中心B区537-A室\n" +
+ "刘晓峰\n" +
+ "43564.770000万\n" +
+ "19940307\n" +
+ "2010-06-30至2020-12-31\n" +
+ "小容量注射剂\n" +
+ "上海市闵行区向阳路888号\n" +
+ "0531-8291****\n" +
+ "0531-8291****\n" +
+ "***@dsyywl.com\n" +
+ "www.dsyywl.com.cn\n" +
+ "张三\n" +
+ "0531-8291****\n" +
+ "113.54,22.19\n" +
+ "\n" +
+ "\n" +
+ "911100007635353107\n" +
+ "上海药业有限公司\n" +
+ "股份有限公司\n" +
+ "吉林省长春市高新开发区北湖科技开发区高科技中心B区537-A室 \n" +
+ "156\n" +
+ "220000\n" +
+ "220100\n" +
+ "000000\n" +
+ "高新开发区北湖科技开发区高科技中心B区537-A室 \n" +
+ "高新开发区北湖科技开发区高科技中心B区537-A室\n" +
+ "高科技中心B区537-A室\n" +
+ "刘晓峰\n" +
+ "43564.770000万\n" +
+ "19940307\n" +
+ "2010-06-30至2020-12-31\n" +
+ "小容量注射剂\n" +
+ "上海市闵行区向阳路888号\n" +
+ "0531-8291****\n" +
+ "0531-8291****\n" +
+ "***@dsyywl.com\n" +
+ "www.dsyywl.com.cn\n" +
+ "张三\n" +
+ "0531-8291****\n" +
+ "113.54,22.19\n" +
+ "\n" +
+ "\n" +
+ " \n";
+ }
+
+
+
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/ProvtestApplication.java b/src/main/java/com/alihealth/d2d/provtest/ProvtestApplication.java
new file mode 100644
index 0000000..7e20d48
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/ProvtestApplication.java
@@ -0,0 +1,16 @@
+package com.alihealth.d2d.provtest;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+// 指定要扫描的Mapper类的包的路径
+@MapperScan("com.alihealth.d2d.provtest.mapper")
+public class ProvtestApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ProvtestApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/controller/ProvTestServerController.java b/src/main/java/com/alihealth/d2d/provtest/controller/ProvTestServerController.java
new file mode 100644
index 0000000..f59ff6b
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/controller/ProvTestServerController.java
@@ -0,0 +1,187 @@
+package com.alihealth.d2d.provtest.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alihealth.d2d.provtest.domain.DomesticPharmaBaseInfo;
+import com.alihealth.d2d.provtest.domain.DomesticProducer;
+import com.alihealth.d2d.provtest.entity.VTTSBasic;
+import com.alihealth.d2d.provtest.enums.BuzStatusEnum;
+import com.alihealth.d2d.provtest.service.IDomesticPharmaBaseInfoService;
+import com.alihealth.d2d.provtest.service.XmlParserService;
+import com.alihealth.d2d.provtest.utils.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.*;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName ProvTestServerController
+ * @Desc 省局对接示例代码 服务端 接收端
+ * @Author houduo.wk
+ * @Date 2020/3/13
+ **/
+@RestController
+@RequestMapping("/prov/test")
+public class ProvTestServerController {
+
+ Logger logger = LoggerFactory.getLogger(getClass());
+ /**
+ * 证书地址
+ */
+ private static final String PFX_FILE_PATH = "D:\\ynyp\\药品追溯\\d2d\\yunnan.pfx";
+ /**
+ * 证书密码
+ */
+ private static final String PFX_PASSWORD = "12345678";
+ /**
+ * 加密算法
+ */
+ private static final String algorithm = "RSA";
+
+
+ @Autowired
+ private IDomesticPharmaBaseInfoService domesticPharmaBaseInfoService;
+
+ @Autowired
+ private XmlParserService xmlParserService;
+
+ @RequestMapping("/receive")
+ public @ResponseBody
+ Map receive(HttpServletRequest request) throws IOException {
+ RequestWrapper requestWrapper = new RequestWrapper(request);
+ String body = requestWrapper.getBodyString();
+ System.out.println(body);
+ Map paramsTemp = JSON.parseObject(body);
+ System.out.println(paramsTemp);
+ Map params = JSON.parseObject(paramsTemp.get("req").toString());
+ logger.info("ProvTestController.receive.in.params:{}",params);
+ try{
+ //传输流水号0
+ String processId = String.valueOf(params.get("process_id"));
+ //事件编号
+ String eventId = String.valueOf(params.get("event_id"));
+ //主数据类型
+ String eventType = String.valueOf(params.get("event_type"));
+ //主数据 包含子类型sub_type和数据data
+ String event = String.valueOf(params.get("event"));
+ //时间戳 YYMMddHHmmssSSS
+ String timeStamp = String.valueOf(params.get("timestamp"));
+ //版本号
+ String ver = String.valueOf(params.get("ver"));
+
+ logger.info("processId:{}|eventId:{}|eventType:{}|event:{}|timeStamp:{}|ver:{}"
+ ,processId,eventId,eventType,event,timeStamp,ver);
+
+ EventPojo eventPojo = JSON.parseObject(event, EventPojo.class);
+ //获取私钥
+ String privateKey = getPrivateKey();
+
+ logger.info("privateKey:{}",privateKey);
+
+
+ String de = testDecrypt(privateKey,eventPojo.getData());
+ String xml = StreamUtil.getUnStreamContent(de);
+ logger.info("解密之后的文件内容:{}", xml);
+
+ //解析xml
+ // List list= XmlStringParser.parseVaccineManufacturers(xml,eventPojo.getSubType());
+
+ //解析并保存数据
+// XmlStringParser.parseXml(xml, "1011", data -> {
+// domesticPharmaBaseInfoService.save((DomesticPharmaBaseInfo) data);
+// });
+
+ List producers = xmlParserService.parseMultiDataXml(
+ xml,
+ VTTSBasic.class,
+ DomesticProducer.class
+ );
+
+ logger.info("解析后的数据条数:{}", producers.size());
+
+ return ResponseBuilder.success();
+ }catch (Exception e){
+ logger.error("{}",e);
+ return ResponseBuilder.fail(BuzStatusEnum.SYS_ERR);
+ }
+
+ }
+
+
+ /**
+ * 私钥解密
+ * @param key
+ * @param data
+ * @return
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeySpecException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws IOException
+ */
+ public static String testDecrypt(String key,String data) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, IOException{
+ byte[] decode = java.util.Base64.getDecoder().decode(key);
+ RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(algorithm).generatePrivate(new PKCS8EncodedKeySpec(decode));
+ //RSA解密
+ Cipher ci = Cipher.getInstance(algorithm);
+ ci.init(Cipher.DECRYPT_MODE, priKey);
+
+
+ byte[] bytes = java.util.Base64.getDecoder().decode(data);
+ int inputLen = bytes.length;
+ int offLen = 0;
+ int i = 0;
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ while(inputLen - offLen > 0){
+ byte[] cache;
+ if(inputLen - offLen > 128){
+ cache = ci.doFinal(bytes,offLen,128);
+ }else{
+ cache = ci.doFinal(bytes,offLen,inputLen - offLen);
+ }
+ byteArrayOutputStream.write(cache);
+ i++;
+ offLen = 128 * i;
+
+ }
+ byteArrayOutputStream.close();
+ byte[] byteArray = byteArrayOutputStream.toByteArray();
+ return new String(byteArray);
+ }
+
+ /**
+ * 获取私钥
+ * @return
+ * @throws Exception
+ */
+ private static String getPrivateKey() throws Exception {
+ KeyStore keyStore = KeyStoreUtil.loadKetStore(PFX_FILE_PATH,PFX_PASSWORD);
+ return Base64.encode(KeyStoreUtil.getPrivateKey(keyStore, PFX_PASSWORD).getEncoded());
+ }
+
+ /**
+ * 获取公钥
+ * @return
+ * @throws Exception
+ */
+ private static String getPublicKey() throws Exception {
+ KeyStore keyStore = KeyStoreUtil.loadKetStore(PFX_FILE_PATH,PFX_PASSWORD);
+ PublicKey publicKey = KeyStoreUtil.getPublicKey(keyStore);
+ return Base64.encode(publicKey.getEncoded());
+ }
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/domain/DomesticPharmaBaseInfo.java b/src/main/java/com/alihealth/d2d/provtest/domain/DomesticPharmaBaseInfo.java
new file mode 100644
index 0000000..0cebafe
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/domain/DomesticPharmaBaseInfo.java
@@ -0,0 +1,69 @@
+package com.alihealth.d2d.provtest.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.util.Date;
+
+/**
+ * @author Jason 境内药品生产企业基本信息实体
+ * @date 2025年05月29日 10:28:21
+ */
+@Data
+@TableName("domestic_pharma_base_info")
+public class DomesticPharmaBaseInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键id
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ // 企业基础信息
+ private String tyshxydm; // 统一社会信用代码
+ private String jnypscqymc; // 疫苗生产企业名称
+ private String jnypscqylx; // 企业类型
+
+ // 注册地址信息
+ private String zsdz; // 注册地址
+ private String zsdzgjhdq; // 注册地址国家/地区代码
+ private String zsdzszxszzq; // 注册地址所在行政区
+ private String zsdzsqzzzm; // 注册地址社区/镇/村
+ private String zsdzxzzxxjs; // 注册地址乡镇/街道
+ private String zsdzxzjdbsc; // 注册地址详细街道办/社区
+ private String zsdzcjlld; // 注册地址村居/路/楼栋
+ private String zsdzmphm; // 注册地址门牌号码
+
+ // 企业负责人信息
+ private String fddbr; // 法定代表人
+ private String zczb; // 注册资本(万元)
+
+ // 日期信息
+ private LocalDate clrq; // 成立日期
+ private String yyqx; // 营业期限
+
+ // 经营信息
+ private String jyfw; // 经营范围
+ private String djjg; // 登记机关
+
+ // 联系方式
+ private String gddhhm; // 固定电话号码
+ private String czhm; // 传真号码
+ private String dzxx; // 电子信箱
+ private String qywz; // 企业网址
+ private String lxr; // 联系人
+ private String lxdh; // 联系电话
+
+ // 地理坐标
+ private String zsgps; // GPS坐标(经度,纬度)
+
+ // 系统字段
+ private Date createTime; // 创建时间
+ private Date updateTime; // 更新时间
+ private Integer isDeleted; // 是否删除(0-否 1-是)
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/domain/DomesticProducer.java b/src/main/java/com/alihealth/d2d/provtest/domain/DomesticProducer.java
new file mode 100644
index 0000000..a6b0614
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/domain/DomesticProducer.java
@@ -0,0 +1,137 @@
+package com.alihealth.d2d.provtest.domain;
+
+import com.alihealth.d2d.provtest.entity.BaseEntity;
+import com.alihealth.d2d.provtest.entity.XmlEntity;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import javax.persistence.Column;
+import javax.persistence.Embedded;
+import javax.xml.bind.annotation.*;
+import java.time.LocalDate;
+
+/**
+ * @author Jason
+ * @date 2025年05月30日 09:59:21
+ */
+@Data
+@TableName("domestic_pharma_base_info")
+@XmlRootElement(name = "data")
+@XmlAccessorType(XmlAccessType.FIELD) // 明确指定字段访问
+public class DomesticProducer extends BaseEntity{
+ @Column(name = "tyshxydm", unique = true)
+ @XmlElement(name = "TYSHXYDM")
+ private String tyshxydm;
+
+ @Column(name = "jnypscqymc")
+ @XmlElement(name = "JNYPSCQYMC")
+ private String jnypscqymc;
+
+ @Column(name = "jnypscqylx")
+ @XmlElement(name = "JNYPSCQYLX")
+ private String jnypscqylx;
+
+ @Column(name = "zsdz")
+ @XmlElement(name = "ZSDZ")
+ private String zsdz;
+
+ @Column(name = "zsdzgjhdq")
+ @XmlElement(name = "ZSDZGJHDQ")
+ private String zsdzgjhdq; // 注册地址国家/地区代码
+
+ @Column(name = "zsdzszxszzq")
+ @XmlElement(name = "ZSDZSZXSZZQ")
+ private String zsdzszxszzq; // 注册地址所在行政区
+
+ @Column(name = "zsdzsqzzzm")
+ @XmlElement(name = "ZSDZSQZZZM")
+ private String zsdzsqzzzm; // 注册地址社区/镇/村
+
+ @Column(name = "zsdzxzzxxjs")
+ @XmlElement(name = "ZSDZXZZXXJS")
+ private String zsdzxzzxxjs; // 注册地址乡镇/街道
+
+ @Column(name = "zsdzxzjdbsc")
+ @XmlElement(name = "ZSDZXZJDBSC")
+ private String zsdzxzjdbsc; // 注册地址详细街道办/社区
+
+ @Column(name = "zsdzcjlld")
+ @XmlElement(name = "ZSDZCJLLD")
+ private String zsdzcjlld; // 注册地址村居/路/楼栋
+
+ @Column(name = "zsdzmphm")
+ @XmlElement(name = "ZSDZMPHM")
+ private String zsdzmphm; // 注册地址门牌号码
+
+ // 企业负责人信息
+ @Column(name = "fddbr")
+ @XmlElement(name = "FDDBR")
+ private String fddbr; // 法定代表人
+
+ @Column(name = "zczb")
+ @XmlElement(name = "ZCZB")
+ private String zczb; // 注册资本(万元)
+
+ // 日期信息
+ @Column(name = "clrq")
+ @XmlElement(name = "CLRQ")
+ private LocalDate clrq; // 成立日期
+
+ @Column(name = "yyqx")
+ @XmlElement(name = "YYQX")
+ private String yyqx; // 营业期限
+
+ // 经营信息
+ @Column(name = "jyfw")
+ @XmlElement(name = "JYFW")
+ private String jyfw; // 经营范围
+
+ @Column(name = "djjg")
+ @XmlElement(name = "DJJG")
+ private String djjg; // 登记机关
+
+ // 联系方式
+ @Column(name = "gddhhm")
+ @XmlElement(name = "GDDHHM")
+ private String gddhhm; // 固定电话号码
+
+ @Column(name = "czhm")
+ @XmlElement(name = "CZHM")
+ private String czhm; // 传真号码
+
+ @Column(name = "dzxx")
+ @XmlElement(name = "DZXX")
+ private String dzxx; // 电子信箱
+
+ @Column(name = "qywz")
+ @XmlElement(name = "QYWZ")
+ private String qywz; // 企业网址
+
+ @Column(name = "lxr")
+ @XmlElement(name = "LXR")
+ private String lxr; // 联系人
+
+ @Column(name = "lxdh")
+ @XmlElement(name = "LXDH")
+ private String lxdh; // 联系电话
+
+ // 地理坐标
+ @Column(name = "zsgps")
+ @XmlElement(name = "ZSGPS")
+ private String zsgps; // GPS坐标(经度,纬度)
+
+
+
+ @Override
+ public Class getEntityClass() {
+ return DomesticProducer.class;
+ }
+
+ @Override
+ public String getDatasetName() {
+ return "境内药品生产企业基本信息数据子集";
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/alihealth/d2d/provtest/entity/BaseEntity.java b/src/main/java/com/alihealth/d2d/provtest/entity/BaseEntity.java
new file mode 100644
index 0000000..bda5a8b
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/entity/BaseEntity.java
@@ -0,0 +1,39 @@
+package com.alihealth.d2d.provtest.entity;
+
+import org.springframework.data.annotation.Id;
+
+import javax.persistence.*;
+import javax.xml.bind.annotation.XmlTransient;
+import java.time.LocalDateTime;
+
+/**
+ * @author Jason
+ * @date 2025年05月30日 09:55:20
+ */
+@MappedSuperclass
+@XmlTransient
+public abstract class BaseEntity> implements XmlEntity{
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "event_id")
+ private String eventId;//事件流水号
+
+ @Column(name = "create_time", updatable = false)
+ // 仅数据库字段
+ private LocalDateTime createTime = LocalDateTime.now();
+
+ @Column(name = "update_time")
+ private LocalDateTime updateTime;
+
+ @PreUpdate
+ public void preUpdate() {
+ this.updateTime = LocalDateTime.now();
+ }
+
+ // 子类必须实现这两个方法
+ public abstract Class getEntityClass();
+ public abstract String getDatasetName();
+}
+
diff --git a/src/main/java/com/alihealth/d2d/provtest/entity/VTTSBasic.java b/src/main/java/com/alihealth/d2d/provtest/entity/VTTSBasic.java
new file mode 100644
index 0000000..1e87ce7
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/entity/VTTSBasic.java
@@ -0,0 +1,33 @@
+package com.alihealth.d2d.provtest.entity;
+
+import com.alihealth.d2d.provtest.domain.DomesticProducer;
+import lombok.Data;
+
+import javax.xml.bind.annotation.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Jason
+ * @date 2025年05月30日 11:45:00
+ */
+@XmlRootElement(name = "VTTSBasic")
+@XmlAccessorType(XmlAccessType.FIELD)
+@Data
+public class VTTSBasic {
+ @XmlElement(name = "datasetName")
+ private String datasetName;
+
+ @XmlElementWrapper(name = "dataset")
+ @XmlElement(name = "data")
+ private List dataset = new ArrayList<>(); // 必须初始化集合
+
+ // 确保有无参构造器
+ public VTTSBasic() {}
+
+ // 添加@XmlSeeAlso指定实现类
+ @XmlSeeAlso({
+ DomesticProducer.class
+ })
+ public static class KnownTypes {}
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/entity/XmlEntity.java b/src/main/java/com/alihealth/d2d/provtest/entity/XmlEntity.java
new file mode 100644
index 0000000..8b96a3d
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/entity/XmlEntity.java
@@ -0,0 +1,7 @@
+package com.alihealth.d2d.provtest.entity;
+
+public interface XmlEntity {
+
+ Class getEntityClass();
+ String getDatasetName();
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/enums/BuzStatusEnum.java b/src/main/java/com/alihealth/d2d/provtest/enums/BuzStatusEnum.java
new file mode 100644
index 0000000..0f2bc6e
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/enums/BuzStatusEnum.java
@@ -0,0 +1,40 @@
+package com.alihealth.d2d.provtest.enums;
+
+/**
+ * @ClassName BuzStatusEnum
+ * @Desc 回执给协同的
+ * @Author houduo.wk
+ * @Date 2020/2/14
+ **/
+public enum BuzStatusEnum {
+ SUCCESS(0,"执行成功"),
+ PARAM_NULL_ERR(1001,"缺少必填参数"),
+ SIGN_CHECK_ERR(1002,"签名认证失败"),
+ DATA_PARSE_ERR(1003,"数据解析错误"),
+ NO_ACCESS_AUTH_ERR(1004,"无接口访问权限"),
+ SYS_ERR(999,"系统开小差,请稍后重试");
+
+ int buzStatus;
+ String buzMessage;
+
+ public int getBuzStatus() {
+ return buzStatus;
+ }
+
+ public void setBuzStatus(int buzStatus) {
+ this.buzStatus = buzStatus;
+ }
+
+ public String getBuzMessage() {
+ return buzMessage;
+ }
+
+ public void setBuzMessage(String buzMessage) {
+ this.buzMessage = buzMessage;
+ }
+
+ BuzStatusEnum(int buzStatus, String buzMessage) {
+ this.buzStatus = buzStatus;
+ this.buzMessage = buzMessage;
+ }
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/enums/StatusEnum.java b/src/main/java/com/alihealth/d2d/provtest/enums/StatusEnum.java
new file mode 100644
index 0000000..1d59325
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/enums/StatusEnum.java
@@ -0,0 +1,29 @@
+package com.alihealth.d2d.provtest.enums;
+
+public enum StatusEnum {
+ SUCCESS(0,"OK"),FAILED(1,"FAILED"),BUZ_ERROR(-1,"BUZ_ERROR");
+
+ StatusEnum(Integer status, String msg) {
+ this.status = status;
+ this.msg = msg;
+ }
+
+ private Integer status;
+ private String msg;
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/factory/DomesticPharmaBaseInfoFactory.java b/src/main/java/com/alihealth/d2d/provtest/factory/DomesticPharmaBaseInfoFactory.java
new file mode 100644
index 0000000..8c0dd8b
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/factory/DomesticPharmaBaseInfoFactory.java
@@ -0,0 +1,94 @@
+package com.alihealth.d2d.provtest.factory;
+
+import com.alihealth.d2d.provtest.domain.DomesticPharmaBaseInfo;
+import com.alihealth.d2d.provtest.utils.XmlStringParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+
+/**
+ * @author Jason 子类型1011的工厂
+ * @date 2025年05月29日 15:09:50
+ */
+public class DomesticPharmaBaseInfoFactory implements EntityFactory {
+
+ private static final Logger logger = LoggerFactory.getLogger(DomesticPharmaBaseInfoFactory.class);
+
+ public Object createEntity() {
+ return new DomesticPharmaBaseInfo();
+ }
+
+ @Override
+ public String getSupportedSubType() {
+ return "1011";
+ }
+
+
+ @Override
+ public void populateEntity(Object entity, org.w3c.dom.Element dataElement) {
+ if (!(entity instanceof DomesticPharmaBaseInfo)) {
+ return;
+ }
+
+ DomesticPharmaBaseInfo baseInfo = (DomesticPharmaBaseInfo) entity;
+ baseInfo.setTyshxydm(getElementText(dataElement, "TYSHXYDM"));
+ baseInfo.setJnypscqymc(getElementText(dataElement, "JNYPSCQYMC"));
+ baseInfo.setJnypscqylx(getElementText(dataElement, "JNYPSCQYLX"));
+ baseInfo.setZsdz(getElementText(dataElement, "ZSDZ"));
+ baseInfo.setZsdzgjhdq(getElementText(dataElement, "ZSDZGJHDQ"));
+ baseInfo.setZsdzszxszzq(getElementText(dataElement, "ZSDZSZXSZZQ"));
+ baseInfo.setJnypscqylx(getElementText(dataElement, "JNYPSCQYLX"));
+ baseInfo.setZsdzsqzzzm(getElementText(dataElement, "ZSDZSQZZZM"));
+ baseInfo.setZsdzxzzxxjs(getElementText(dataElement, "ZSDZXZZXXJS"));
+ baseInfo.setZsdzxzjdbsc(getElementText(dataElement, "ZSDZXZJDBSC"));
+
+ baseInfo.setZsdzcjlld(getElementText(dataElement, "ZSDZCJLLD"));
+ baseInfo.setZsdzmphm(getElementText(dataElement, "ZSDZMPHM"));
+ baseInfo.setFddbr(getElementText(dataElement, "FDDBR"));
+ baseInfo.setZczb(getElementText(dataElement, "ZCZB"));
+ baseInfo.setClrq(parseDate(getElementText(dataElement, "CLRQ")));
+ baseInfo.setYyqx(getElementText(dataElement, "YYQX"));
+ baseInfo.setJyfw(getElementText(dataElement, "JYFW"));
+ baseInfo.setDjjg(getElementText(dataElement, "DJJG"));
+ baseInfo.setGddhhm(getElementText(dataElement, "GDDHHM"));
+ baseInfo.setCzhm(getElementText(dataElement, "CZHM"));
+ baseInfo.setDzxx(getElementText(dataElement, "DZXX"));
+ baseInfo.setQywz(getElementText(dataElement, "QYWZ"));
+ baseInfo.setLxr(getElementText(dataElement, "LXR"));
+
+ baseInfo.setLxdh(getElementText(dataElement, "LXDH"));
+ baseInfo.setZsgps(getElementText(dataElement, "ZSGPS"));
+
+
+ }
+
+
+ private static String getElementText(org.w3c.dom.Element parent, String tagName) {
+ NodeList nodeList = parent.getElementsByTagName(tagName);
+ if (nodeList.getLength() > 0) {
+ Node node = nodeList.item(0);
+ return node.getTextContent().trim();
+ }
+ return null;
+ }
+
+ private static LocalDate parseDate(String dateStr) {
+ if (dateStr == null || dateStr.length() != 8 || "无".equals(dateStr)) {
+ return null;
+ }
+
+ try {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
+ return LocalDate.parse(dateStr, formatter);
+ } catch (Exception e) {
+ logger.warn("日期解析失败: " + dateStr, e);
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/factory/EntityFactory.java b/src/main/java/com/alihealth/d2d/provtest/factory/EntityFactory.java
new file mode 100644
index 0000000..1f0acaf
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/factory/EntityFactory.java
@@ -0,0 +1,27 @@
+package com.alihealth.d2d.provtest.factory;
+
+
+/**
+ * 实体工厂接口
+ */
+public interface EntityFactory {
+
+ /**
+ * 创建实体对象
+ * @return 新创建的实体对象
+ */
+ Object createEntity();
+
+ /**
+ * 支持的子类型
+ * @return 子类型标识
+ */
+ String getSupportedSubType();
+
+ /**
+ * 填充实体对象属性
+ * @param entity 实体对象
+ * @param dataElement XML数据元素
+ */
+ void populateEntity(Object entity, org.w3c.dom.Element dataElement);
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/factory/EntityFactoryManager.java b/src/main/java/com/alihealth/d2d/provtest/factory/EntityFactoryManager.java
new file mode 100644
index 0000000..6f649d8
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/factory/EntityFactoryManager.java
@@ -0,0 +1,31 @@
+package com.alihealth.d2d.provtest.factory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Jason 工厂管理类
+ * @date 2025年05月29日 15:08:43
+ */
+public class EntityFactoryManager {
+
+ private static final Map factoryMap = new HashMap<>();
+
+ static {
+ // 注册所有工厂
+ registerFactory(new DomesticPharmaBaseInfoFactory());//1011数据
+ // 注册更多工厂...
+ }
+
+ public static void registerFactory(EntityFactory factory) {
+ factoryMap.put(factory.getSupportedSubType(), factory);
+ }
+
+ public static EntityFactory getFactory(String subType) {
+ EntityFactory factory = factoryMap.get(subType);
+ if (factory == null) {
+ throw new IllegalArgumentException("不支持的子类型: " + subType);
+ }
+ return factory;
+ }
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/mapper/DomesticPharmaBaseInfoMapper.java b/src/main/java/com/alihealth/d2d/provtest/mapper/DomesticPharmaBaseInfoMapper.java
new file mode 100644
index 0000000..eb19802
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/mapper/DomesticPharmaBaseInfoMapper.java
@@ -0,0 +1,7 @@
+package com.alihealth.d2d.provtest.mapper;
+
+import com.alihealth.d2d.provtest.domain.DomesticPharmaBaseInfo;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+public interface DomesticPharmaBaseInfoMapper extends BaseMapper {
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/service/IDomesticPharmaBaseInfoService.java b/src/main/java/com/alihealth/d2d/provtest/service/IDomesticPharmaBaseInfoService.java
new file mode 100644
index 0000000..5e2322d
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/service/IDomesticPharmaBaseInfoService.java
@@ -0,0 +1,8 @@
+package com.alihealth.d2d.provtest.service;
+
+import com.alihealth.d2d.provtest.domain.DomesticPharmaBaseInfo;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+public interface IDomesticPharmaBaseInfoService extends IService {
+
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/service/XmlParserService.java b/src/main/java/com/alihealth/d2d/provtest/service/XmlParserService.java
new file mode 100644
index 0000000..3695b7c
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/service/XmlParserService.java
@@ -0,0 +1,60 @@
+package com.alihealth.d2d.provtest.service;
+
+import org.springframework.stereotype.Service;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Jason
+ * @date 2025年05月30日 11:41:26
+ */
+@Service
+public class XmlParserService {
+
+
+ /**
+ * 解析包含多个data节点的XML
+ * @param xml 完整XML内容
+ * @param wrapperClass 外层包装类(如VTTSBasic.class)
+ * @param dataClass 内部data节点对应的实体类
+ */
+ public List parseMultiDataXml(
+ String xml,
+ Class> wrapperClass,
+ Class dataClass) throws Exception {
+
+ JAXBContext context = JAXBContext.newInstance(wrapperClass, dataClass);
+ Unmarshaller unmarshaller = context.createUnmarshaller();
+
+ // 使用StAX提高安全性
+ XMLInputFactory xif = XMLInputFactory.newInstance();
+ xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
+ xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+
+ XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xml));
+
+ JAXBElement> root = unmarshaller.unmarshal(xsr, wrapperClass);
+ Field datasetField = wrapperClass.getDeclaredField("dataset");
+ datasetField.setAccessible(true);
+
+ @SuppressWarnings("unchecked")
+ List result = (List) datasetField.get(root.getValue());
+
+ return result != null ? result : Collections.emptyList();
+ }
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/service/impl/DomesticPharmaBaseInfoServiceImpl.java b/src/main/java/com/alihealth/d2d/provtest/service/impl/DomesticPharmaBaseInfoServiceImpl.java
new file mode 100644
index 0000000..36a8a6c
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/service/impl/DomesticPharmaBaseInfoServiceImpl.java
@@ -0,0 +1,16 @@
+package com.alihealth.d2d.provtest.service.impl;
+
+import com.alihealth.d2d.provtest.domain.DomesticPharmaBaseInfo;
+import com.alihealth.d2d.provtest.mapper.DomesticPharmaBaseInfoMapper;
+import com.alihealth.d2d.provtest.service.IDomesticPharmaBaseInfoService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author Jason
+ * @date 2025年05月29日 16:07:26
+ */
+@Service
+public class DomesticPharmaBaseInfoServiceImpl extends ServiceImpl implements IDomesticPharmaBaseInfoService {
+
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/utils/Base64.java b/src/main/java/com/alihealth/d2d/provtest/utils/Base64.java
new file mode 100644
index 0000000..f185d31
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/utils/Base64.java
@@ -0,0 +1,102 @@
+package com.alihealth.d2d.provtest.utils;
+
+
+import java.io.UnsupportedEncodingException;
+
+public class Base64 {
+ private static char[] base64EncodeChars = new char[]{
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/'};
+ private static byte[] base64DecodeChars = new byte[]{
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1};
+
+ public static String encode(byte[] data) {
+ StringBuffer sb = new StringBuffer();
+ int len = data.length;
+ int i = 0;
+ int b1, b2, b3;
+ while (i < len) {
+ b1 = data[i++] & 0xff;
+ if (i == len) {
+ sb.append(base64EncodeChars[b1 >>> 2]);
+ sb.append(base64EncodeChars[(b1 & 0x3) << 4]);
+ sb.append("==");
+ break;
+ }
+ b2 = data[i++] & 0xff;
+ if (i == len) {
+ sb.append(base64EncodeChars[b1 >>> 2]);
+ sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
+ sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
+ sb.append("=");
+ break;
+ }
+ b3 = data[i++] & 0xff;
+ sb.append(base64EncodeChars[b1 >>> 2]);
+ sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
+ sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
+ sb.append(base64EncodeChars[b3 & 0x3f]);
+ }
+ return sb.toString();
+ }
+
+ public static byte[] decode(String str) throws UnsupportedEncodingException {
+ StringBuffer sb = new StringBuffer();
+ byte[] data = str.getBytes("US-ASCII");
+ int len = data.length;
+ int i = 0;
+ int b1, b2, b3, b4;
+ while (i < len) {
+
+ do {
+ b1 = base64DecodeChars[data[i++]];
+ } while (i < len && b1 == -1);
+ if (b1 == -1) break;
+
+ do {
+ b2 = base64DecodeChars
+ [data[i++]];
+ } while (i < len && b2 == -1);
+ if (b2 == -1) break;
+ sb.append((char) ((b1 << 2) | ((b2 & 0x30) >>> 4)));
+
+ do {
+ b3 = data[i++];
+ if (b3 == 61) return sb.toString().getBytes("iso8859-1");
+ b3 = base64DecodeChars[b3];
+ } while (i < len && b3 == -1);
+ if (b3 == -1) break;
+ sb.append((char) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2)));
+
+ do {
+ b4 = data[i++];
+ if (b4 == 61) return sb.toString().getBytes("iso8859-1");
+ b4 = base64DecodeChars[b4];
+ } while (i < len && b4 == -1);
+ if (b4 == -1) break;
+ sb.append((char) (((b3 & 0x03) << 6) | b4));
+ }
+ return sb.toString().getBytes("iso8859-1");
+ }
+ public static void main(String[] args) throws UnsupportedEncodingException {
+ String s = "abcd";
+ System.out.println("����ǰ��" + s);
+ String x = encode(s.getBytes());
+ System.out.println("���ܺ�" + x);
+ String x1 = new String(decode(x));
+ System.out.println("���ܺ�" + x1);
+ }
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/utils/BouncyCastlePFXGenerator.java b/src/main/java/com/alihealth/d2d/provtest/utils/BouncyCastlePFXGenerator.java
new file mode 100644
index 0000000..55d5aac
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/utils/BouncyCastlePFXGenerator.java
@@ -0,0 +1,73 @@
+package com.alihealth.d2d.provtest.utils;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+import java.io.FileOutputStream;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.Security;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+/**
+ * @author Jason
+ * @date 2025年05月28日 15:02:12
+ */
+public class BouncyCastlePFXGenerator {
+
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ public static void generatePFX(String pfxFilePath, String password) throws Exception {
+ // 1. 生成密钥对
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(2048);
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+ // 2. 创建自签名证书
+ X500Name issuerName = new X500Name("CN=Test Certificate, OU=Test, O=Test Org, C=US");
+ BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
+ Date notBefore = new Date();
+ Date notAfter = new Date(System.currentTimeMillis() + 365L * 24 * 60 * 60 * 1000); // 1年有效期
+
+ X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
+ issuerName, serialNumber, notBefore, notAfter, issuerName, keyPair.getPublic());
+
+ ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
+ .build(keyPair.getPrivate());
+
+ X509Certificate cert = new JcaX509CertificateConverter()
+ .getCertificate(certBuilder.build(signer));
+
+ // 3. 创建PKCS12密钥库
+ KeyStore pkcs12 = KeyStore.getInstance("PKCS12");
+ pkcs12.load(null, null);
+
+ // 4. 添加证书和私钥
+ pkcs12.setKeyEntry("alias", keyPair.getPrivate(), password.toCharArray(),
+ new java.security.cert.Certificate[]{cert});
+
+ // 5. 保存PFX文件
+ try (FileOutputStream fos = new FileOutputStream(pfxFilePath)) {
+ pkcs12.store(fos, password.toCharArray());
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ generatePFX("d2d.pfx", "12345678");
+ System.out.println("使用Bouncy Castle生成的PFX证书成功!");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/utils/EntityProcessor.java b/src/main/java/com/alihealth/d2d/provtest/utils/EntityProcessor.java
new file mode 100644
index 0000000..ee579f9
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/utils/EntityProcessor.java
@@ -0,0 +1,5 @@
+package com.alihealth.d2d.provtest.utils;
+
+public interface EntityProcessor {
+ void process(T entity);
+}
\ No newline at end of file
diff --git a/src/main/java/com/alihealth/d2d/provtest/utils/EventPojo.java b/src/main/java/com/alihealth/d2d/provtest/utils/EventPojo.java
new file mode 100644
index 0000000..8dab89a
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/utils/EventPojo.java
@@ -0,0 +1,33 @@
+package com.alihealth.d2d.provtest.utils;
+
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+public class EventPojo {
+ /**
+ *数据文件内容子业务类型 详见:数据类型字典表
+ */
+ private String subType;
+ /**
+ * 详见:疫苗追溯基本数据集 ZIP 压缩XML/JSON数据文件内容(BASE64编码)
+ */
+ private String data;
+
+ @JSONField( name = "sub_type")
+ public String getSubType() {
+ return subType;
+ }
+
+ public void setSubType(String subType) {
+ this.subType = subType;
+ }
+
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/utils/KeyStoreUtil.java b/src/main/java/com/alihealth/d2d/provtest/utils/KeyStoreUtil.java
new file mode 100644
index 0000000..b475af5
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/utils/KeyStoreUtil.java
@@ -0,0 +1,604 @@
+package com.alihealth.d2d.provtest.utils;
+
+
+import javax.crypto.Cipher;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.*;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Date;
+import java.util.Enumeration;
+
+public class KeyStoreUtil {
+ /**
+ * 加载证书 pfxFilepath 证书路径 pfxPassword 密码
+ *
+ * @throws Exception
+ */
+ public static KeyStore loadKetStore(String pfxFilepath, String pfxPassword)
+ throws Exception {
+ KeyStore keyStore = null;
+ File fPkcs12 = null;
+ if (pfxFilepath != null) {
+ fPkcs12 = new File(pfxFilepath);
+ }
+ FileInputStream fis = new FileInputStream(fPkcs12);
+
+ try {
+ keyStore = KeyStore.getInstance("PKCS12");
+ } catch (KeyStoreException ex) {
+ throw new Exception("不能正确解释pfx文件! ");
+ }
+
+ try {
+ keyStore.load(fis, pfxPassword.toCharArray());
+ } catch (CertificateException ex) {
+ throw new Exception(" 证书格式问题! ");
+ } catch (NoSuchAlgorithmException ex) {
+ throw new Exception(" 算法不支持! ");
+ } catch (FileNotFoundException ex) {
+ throw new Exception(" pfx文件没找到 ");
+ } catch (IOException ex) {
+ throw new Exception(" 读取pfx有误! ");
+ }
+ return keyStore;
+ }
+
+ // 证书别名可以作为证书的识别,所以加载证书别名积极关键。
+
+ /**
+ * 获取证书别名
+ *
+ * @param keyStore
+ * @return
+ */
+ // @SuppressWarnings("unchecked")
+ public static String getKeyAlias(KeyStore keyStore) {
+ String keyAlias = "";
+ try {
+ Enumeration enums = keyStore.aliases();
+ while (enums.hasMoreElements()) {
+ keyAlias = (String) enums.nextElement();
+ }
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ }
+ return keyAlias;
+ }
+
+ // 获取私钥,可以使用私钥进行数据加密后在网络上传输。
+
+ /**
+ * 获取私钥
+ *
+ * @param keyStore
+ * @param keyAlias
+ * @param pfxPassword
+ * @return
+ */
+ public static PrivateKey getPrivateKey(KeyStore keyStore, String pfxPassword) {
+ PrivateKey privateKey = null;
+ try {
+ String keyAlias = getKeyAlias(keyStore);
+ privateKey = (PrivateKey) keyStore.getKey(keyAlias, pfxPassword
+ .toCharArray());
+ } catch (UnrecoverableKeyException e) {
+ e.printStackTrace();
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ return privateKey;
+ }
+
+ // 使用公钥对私钥加密过的数据进行解密,可以得到相关的明文。
+
+ /**
+ * 获取公钥
+ *
+ * @param keyStore
+ * @param keyAlias
+ * @return
+ */
+ public static PublicKey getPublicKey(KeyStore keyStore) {
+ PublicKey publicKey = null;
+ try {
+ String keyAlias = getKeyAlias(keyStore);
+ Certificate[] certs = keyStore.getCertificateChain(keyAlias);
+ if (certs != null && certs.length > 0) {
+ // 取得公钥
+ publicKey = certs[0].getPublicKey();
+ }
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ }
+ return publicKey;
+ }
+
+ // 使用公钥加密的数据,需要使用私钥进行解密。
+
+ /**
+ * 公钥加密
+ *
+ * @param data
+ * @param publicKey
+ * @return
+ * @throws Exception
+ */
+ public static String encryptByPublicKey(byte[] data, PublicKey publicKey)
+ throws Exception {
+ // 对数据加密
+ Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
+ cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+ byte[] encodedata = cipher.doFinal(data);
+ return byte2hex(encodedata);
+ }
+
+ // 使用公钥对私钥加密过的数据进行解密。
+
+ /**
+ * 公钥解密
+ *
+ * @param data
+ * @param publicKey
+ * @return
+ * @throws Exception
+ */
+ public static String decryptByPublicKey(String data, PublicKey publicKey)
+ throws Exception {
+ // 对数据解密
+ Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
+ cipher.init(Cipher.DECRYPT_MODE, publicKey);
+ byte[] decodedata = cipher.doFinal(hex2byte(data));
+ return new String(decodedata);
+ }
+
+ // 使用私钥加密的数据,需要使用公钥进行解密。
+
+ /**
+ * 私钥加密
+ *
+ * @param data
+ * @param privateKey
+ * @return
+ * @throws Exception
+ */
+ public static String encryptByPrivateKey(byte[] data, PrivateKey privateKey)
+ throws Exception {
+ // 对数据加密
+ Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
+ cipher.init(Cipher.ENCRYPT_MODE, privateKey);
+ byte[] encodedata = cipher.doFinal(data);
+ return byte2hex(encodedata);
+ }
+
+ // 可以使用私钥对公钥加密过的数据进行解密。
+
+ /**
+ * 私钥解密
+ *
+ * @param data
+ * @param privateKey
+ * @return
+ * @throws Exception
+ */
+ public static String decryptByPrivateKey(String encodestr,
+ PrivateKey privateKey) throws Exception {
+ // 对数据解密
+ Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
+ cipher.init(Cipher.DECRYPT_MODE, privateKey);
+ byte[] data = cipher.doFinal(hex2byte(encodestr));
+ return new String(data);
+ }
+
+ /**
+ * 验证Certificate是否过期或无效
+ *
+ * @param keyStore
+ * @param keyAlias
+ * @param date
+ * 当前时间
+ * @return
+ */
+ public static boolean verifyCertificate(KeyStore keyStore, Date date) {
+ boolean status = true;
+ try {
+ // 取得证书
+ String keyAlias = getKeyAlias(keyStore);
+ Certificate[] certs = keyStore.getCertificateChain(keyAlias);
+ if (certs != null && certs.length > 0) {
+ // 取得公钥
+ X509Certificate x509Certificate = (X509Certificate) certs[0];
+ // 验证证书是否过期或无效
+ x509Certificate.checkValidity(date);
+ }
+ } catch (Exception e) {
+ status = false;
+ }
+ return status;
+ }
+
+ /**
+ * 构造签名
+ *
+ * @param keyStore
+ * @param pfxPassword
+ * @param dataStr
+ * 证书别名
+ * @return
+ */
+ public static String createSignature(KeyStore keyStore, String pfxPassword,
+ String dataStr) {
+ try {
+ // 取得证书
+ String keyAlias = getKeyAlias(keyStore);
+ Certificate[] certs = keyStore.getCertificateChain(keyAlias);
+ X509Certificate x509Certificate = null;
+ if (certs != null && certs.length > 0) {
+ x509Certificate = (X509Certificate) certs[0];
+ // 验证证书是否过期或无效
+ }
+ if (x509Certificate != null) {
+ Signature signature = Signature.getInstance(x509Certificate
+ .getSigAlgName());
+ PrivateKey privateKey = getPrivateKey(keyStore, pfxPassword);
+ signature.initSign(privateKey);
+ signature.update(dataStr.getBytes());
+ byte[] data = signature.sign();
+ return byte2hex(data);
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * 验证签名
+ *
+ * @param keyStore
+ * @param pfxPassword
+ * @param dataStr
+ * @return
+ */
+ public static boolean verfySignature(KeyStore keyStore, String dataStr,
+ String signStr) {
+ try {
+ // 取得证书
+ String keyAlias = getKeyAlias(keyStore);
+ Certificate[] certs = keyStore.getCertificateChain(keyAlias);
+ X509Certificate x509Certificate = null;
+ if (certs != null && certs.length > 0) {
+ x509Certificate = (X509Certificate) certs[0];
+ // 验证证书是否过期或无效
+ }
+ Signature signature = Signature.getInstance(x509Certificate
+ .getSigAlgName());
+ PublicKey publicKey = getPublicKey(keyStore);
+ signature.initVerify(publicKey);
+ signature.update(dataStr.getBytes());
+ return signature.verify(hex2byte(signStr));
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * 验证签名使用公钥
+ *
+ * @param keyStore
+ * @param pfxPassword
+ * @param dataStr
+ * @return
+ */
+ public static boolean verfySignatureWithPublic(byte[] publicKeyArr,
+ String dataStr, String signStr) {
+ try {
+ Signature signature = Signature.getInstance("SHA1withRSA");
+ java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec(
+ publicKeyArr);
+ java.security.KeyFactory keyFactory = java.security.KeyFactory
+ .getInstance("RSA");
+ PublicKey publicKey = keyFactory.generatePublic(bobPubKeySpec);
+ signature.initVerify(publicKey);
+ signature.update(dataStr.getBytes());
+ return signature.verify(hex2byte(signStr));
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public static String decodeWithPrivate(byte[] privateKeyArr) {
+ // Signature signature = Signature.getInstance("SHA1withRSA");
+ java.security.spec.X509EncodedKeySpec bobPriKeySpec = new java.security.spec.X509EncodedKeySpec(privateKeyArr);
+ java.security.KeyFactory keyFactory = null;
+ try {
+ keyFactory = java.security.KeyFactory.getInstance("RSA");
+ PrivateKey privateKey = keyFactory.generatePrivate(bobPriKeySpec);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (InvalidKeySpecException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 公钥解密
+ * @param publicKeyArr
+ * @return
+ */
+ public static String decodeWithPublic(String data,byte[] publicKeyArr) {
+ // Signature signature = Signature.getInstance("SHA1withRSA");
+ java.security.spec.X509EncodedKeySpec bobPriKeySpec = new java.security.spec.X509EncodedKeySpec(publicKeyArr);
+ java.security.KeyFactory keyFactory = null;
+ try {
+ keyFactory = java.security.KeyFactory.getInstance("RSA");
+ //PrivateKey privateKey = keyFactory.generatePrivate(bobPriKeySpec);
+ PublicKey publickey = keyFactory.generatePublic(bobPriKeySpec);
+ return decryptByPublicKey(data,publickey);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (InvalidKeySpecException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 公钥加密
+ * @param publicKeyArr
+ * @return
+ */
+ public static String encodeWithPublic(String data,byte[] publicKeyArr) {
+ // Signature signature = Signature.getInstance("SHA1withRSA");
+ java.security.spec.X509EncodedKeySpec bobPriKeySpec = new java.security.spec.X509EncodedKeySpec(publicKeyArr);
+ java.security.KeyFactory keyFactory = null;
+ try {
+ keyFactory = java.security.KeyFactory.getInstance("RSA");
+ //PrivateKey privateKey = keyFactory.generatePrivate(bobPriKeySpec);
+ PublicKey publickey = keyFactory.generatePublic(bobPriKeySpec);
+ return encryptByPublicKey(data.getBytes(),publickey);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (InvalidKeySpecException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 私钥解密
+ * @param privateKeyArr
+ * @return
+ */
+ public static String decodeWithPrivate(String data,byte[] privateKeyArr) {
+ // Signature signature = Signature.getInstance("SHA1withRSA");
+ java.security.spec.X509EncodedKeySpec bobPriKeySpec = new java.security.spec.X509EncodedKeySpec(privateKeyArr);
+ java.security.KeyFactory keyFactory = null;
+ try {
+ keyFactory = java.security.KeyFactory.getInstance("RSA");
+ //PrivateKey privateKey = keyFactory.generatePrivate(bobPriKeySpec);
+ PrivateKey privatekey = keyFactory.generatePrivate(bobPriKeySpec);
+ return decryptByPrivateKey(data,privatekey);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (InvalidKeySpecException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+
+ /**
+ * 将二进制转化为16进制字符串
+ *
+ * @param b
+ * 二进制字节数组
+ * @return String
+ */
+ private static String byte2hex(byte[] b) {
+ String hs = "";
+ String stmp = "";
+ for (int n = 0; n < b.length; n++) {
+ stmp = (Integer.toHexString(b[n] & 0XFF));
+ if (stmp.length() == 1) {
+ hs = hs + "0" + stmp;
+ } else {
+ hs = hs + stmp;
+ }
+ }
+ return hs.toUpperCase();
+ }
+
+ /**
+ * 十六进制字符串转化为2进制
+ *
+ * @param hex
+ * @return
+ */
+ private static byte[] hex2byte(String hex) {
+ byte[] ret = null;
+ // try {
+ byte[] tmp = hex.getBytes();
+ int length = tmp.length / 2;
+ ret = new byte[length];
+ for (int i = 0; i < length; i++) {
+ ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
+ }
+ // } catch (UnsupportedEncodingException e) {
+ // e.printStackTrace();
+ // }
+ return ret;
+ }
+
+ /**
+ * 将两个ASCII字符合成一个字节; 如:"EF"--> 0xEF
+ *
+ * @param src0
+ * byte
+ * @param src1
+ * byte
+ * @return byte
+ */
+ private static byte uniteBytes(byte src0, byte src1) {
+ byte _b0 = Byte.decode("0x" + new String(new byte[] { src0 }))
+ .byteValue();
+ _b0 = (byte) (_b0 << 4);
+ byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 }))
+ .byteValue();
+ byte ret = (byte) (_b0 ^ _b1);
+ return ret;
+ }
+
+ // /**
+ // * 验证pkcs7格式的签名数据
+ // * @param signedData pkcs7格式的签名数据
+ // * @return 验证结果
+ // * @throws Exception
+ // */
+ // public boolean verify(byte[] signedData) throws Exception {
+ // CMSSignedData sign = new CMSSignedData(signedData);
+ // CertStore certs = sign.getCertificatesAndCRLs("Collection", "BC");
+ // SignerInformationStore signers = sign.getSignerInfos();
+ // Collection c = signers.getSigners();
+ // Iterator it = c.iterator();
+ // boolean bresult = true;
+ // //当有多个签名者信息时需要进行全部验证
+ // while (it.hasNext()) ...{
+ // SignerInformation signer = (SignerInformation) it.next();
+ // Collection certCollection = certs.getCertificates(signer.getSID());
+ // Iterator certIt = certCollection.iterator();
+ // X509Certificate cert = (X509Certificate) certIt.next();//证书链????
+ // byte[] data = signer.getSignature();
+ // logger.debug("签名后数据:" + data.length);
+ // for (int i = 0; i < data.length; i++) ...{
+ // System.out.print(" " + data[i]);
+ // }
+ // System.out.println();
+ // logger.debug("签名后数据Base64: " + new String(Base64.encode(data),"utf8"));
+ // if (signer.verify(cert.getPublicKey(), "BC")) ...{//验证过程???
+ // logger.info(" pkcs7 verifed success!");
+ // }else...{
+ // bresult = false;
+ // }
+ // }
+ // return bresult;
+ // }
+ //
+ // /**
+ // * 签名生成pkcs7结构数据
+ // * @param signText 原始数据
+ // * @param keystore 密钥库
+ // * @param keyalias 密钥别名
+ // * @param password 密钥库密码
+ // * @return 签名后数据
+ // * @throws Exception
+ // */
+ // public byte[] sign(byte[] signText,KeyStore keystore,String
+ // keyalias,char[] password) throws Exception {
+ // CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
+ // Enumeration en = keystore.aliases();
+ // //遍历密钥库的密钥别名
+ // while(en.hasMoreElements()){
+ // String alias = (String)en.nextElement();
+ // logger.debug("keyalias:" + alias);
+ // if(!alias.equalsIgnoreCase(keyalias))continue;
+ //
+ // //根据别名从证书中获取私钥
+ // PrivateKey prikey = (PrivateKey) keystore.getKey(keyalias,password);
+ // X509Certificate x509 = null;
+ //
+ // //获取该别名对应的证书链
+ // Certificate[] certs = keystore.getCertificateChain(keyalias);
+ // if (certs[0] instanceof X509Certificate) {//user's certificate
+ // x509 = (X509Certificate) certs[0];
+ // }
+ // if (certs[certs.length - 1] instanceof X509Certificate) {//root
+ // certificate
+ // x509 = (X509Certificate) certs[certs.length - 1];
+ // }
+ // gen.addSigner(prikey, x509, CMSSignedDataStreamGenerator.DIGEST_SHA1,
+ // "BC");
+ // CertStore certstore = CertStore.getInstance("Collection", new
+ // CollectionCertStoreParameters(Arrays.asList(certs)),"BC");
+ // gen.addCertificatesAndCRLs(certstore);
+ // }
+ // //输出字节流
+ // ByteArrayOutputStream bOut = new
+ // ByteArrayOutputStream();//"e:/JavaSignedData.txt"
+ // OutputStream sigOut = gen.open(bOut,true);//false,不含原始数据,减轻传输负担
+ // sigOut.write(signText);//对原始数据进行签名
+ // sigOut.close();
+ // logger.info("pkcs7 signed success!");
+ // byte[] signedData = bOut.toByteArray();//签名后数据
+ // bOut.close();
+ // byte[] signedDataBase64 = Base64.encode(signedData);
+ // logger.debug("签名后数据: " + new String(signedData,CHARSET) + " ");
+ // logger.debug("签名后数据Base64: " + new String(signedDataBase64,CHARSET) +
+ // " ");
+ // return signedData;
+ // }
+ //
+
+ public static void main(String args[]) {
+ try {
+ // 加载证书
+ KeyStore keyStore = loadKetStore("C:\\Users\\houduo.wk\\Desktop\\pfx证书制作工具\\output\\GUIZHOUD2D.pfx","12345678");
+ String alias = getKeyAlias(keyStore);
+ System.out.println("证收别名:" + alias);
+ // 获取公钥
+ PublicKey publicKey = getPublicKey(keyStore);
+ System.out.println("公钥:"+Base64.encode(publicKey.getEncoded()));
+ //公钥加密
+ String en = encryptByPublicKey("test".getBytes(), publicKey);
+ System.out.println("公钥加密后en:" + en);
+
+ // 获取私钥
+ PrivateKey privateKey = getPrivateKey(keyStore, "12345678");
+ System.out.println("私钥:" + Base64.encode(privateKey.getEncoded()));
+
+ //私钥解密
+ String de = decryptByPrivateKey(en, privateKey);
+ System.out.println("私钥解密后de:" + de);
+
+/*
+ // 签名
+ String sign = createSignature(keyStore, "88888888", "test");
+ System.out.println("sign:" + sign);
+ // 验签
+ System.out
+ .println(verfySignature(
+ keyStore,
+ "test",
+ "2FFBD4D36985621E1FA6E4A70E318651F861F7D348B7034031B38781F62F9E2DE3BC2C3B6BB6DA5A96C795417BF55789A96B5B05548F9F2D99BE0AEA5396189CBB5F333825C32631D93125F9EB5470142F5E489D89EBCEF53AD0DAE8E1B2C6E561122A1174D07D78F972590F5665BBFC8FB8D182EDA4F2ECFE1A4A2FBE077603"));
+*/
+
+/* String publickey = "30819F300D06092A864886F70D010101050003818D0030818902818100D4823C41CA161F451F1A979ABE62A1140305BD882B597BC36D8460F81C4EE31CF2D3A96CC834CC05F7FF296B53EA607603919419172C6558D25558DC1ACDDE013F995B32B69CF869A8F8F6818B0953BE61552E8A32068FACE1A0227A4CB3E7AC615F053B553E53A6E6B083108DA5BC31D5DFCF59B13B2D4167F777F2E32A3C0F0203010001";
+ String privatekey = "30820277020100300D06092A864886F70D0101010500048202613082025D02010002818100D4823C41CA161F451F1A979ABE62A1140305BD882B597BC36D8460F81C4EE31CF2D3A96CC834CC05F7FF296B53EA607603919419172C6558D25558DC1ACDDE013F995B32B69CF869A8F8F6818B0953BE61552E8A32068FACE1A0227A4CB3E7AC615F053B553E53A6E6B083108DA5BC31D5DFCF59B13B2D4167F777F2E32A3C0F02030100010281806FD1FBCD0E7E6783E2A30685298B12814A39DA0A6DED0F1FBD5FFBFA7242DC64DE2D9C281E8D11924E7598A0DE2E2505D448E529F2B4ACBB7929E9B11FF595CC125BBA927078B69AD062261D2936056251E259E9EDA1BB99EC3860D311C028613F616B26EAEDAA1177231B2A8E7E9C262FEDFD22912D03785A0666ED9FF60CC9024100F96E689B306286F81C99BBEA7D465375D19A26C5CC23852041ABBB198CD425455160AEE760E8F92224E9D60C4AF518E3EE2C39598735BBE1656B15CA54DC87F5024100DA1AE7EA66708D76A22EBC193350E1E3979A19E2CB126A9704E1F2D8FFB13E1EB32669AF9CAC15232E4255A4F3D60E19BD4C0CB6A1C405A469FB68EF96AFE57302406F0DDC5F3424BA091FDAC6D2F961E568F0A09CA0B66A1E2CA5708B862DB08AC3991EDDE0C0C219A23D232B9D1F868EC574D695D629A01FFD93C520D78BFF003D024100AAA6CEBAFAB82A179EC24E6EB15621C88000152CFC93464F2F84D30CB2D3ADB53B9B5CAA093E23B3C92821F6D4CAB171B64960D12D3540ECE9F7CA9D5C1BF8BD024100A4FE9F3CF0EAADC1C00C8B7986BAA7BA0CC0DC389EFD854761896C5AC924A77AD8EE84A6A62465504CBB77D5DCF813809A9160E23E5982280C4147337B60CAC0";
+
+ String cont = encodeWithPublic("test",hex2byte(publickey));
+ System.out.println(decodeWithPrivate(cont, hex2byte(privatekey)));*/
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/utils/RequestWrapper.java b/src/main/java/com/alihealth/d2d/provtest/utils/RequestWrapper.java
new file mode 100644
index 0000000..d926b8d
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/utils/RequestWrapper.java
@@ -0,0 +1,118 @@
+package com.alihealth.d2d.provtest.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.*;
+import java.nio.charset.Charset;
+
+public class RequestWrapper extends HttpServletRequestWrapper {
+ private Logger logger = LoggerFactory.getLogger(RequestWrapper.class);
+
+ /**
+ * 存储body数据的容器
+ */
+ private final byte[] body;
+
+ public RequestWrapper(HttpServletRequest request) throws IOException {
+ super(request);
+
+ // 将body数据存储起来
+ String bodyStr = getBodyString(request);
+ body = bodyStr.getBytes(Charset.defaultCharset());
+ }
+
+ /**
+ * 获取请求Body
+ *
+ * @param request request
+ * @return String
+ */
+ public String getBodyString(final ServletRequest request) {
+ try {
+ return inputStream2String(request.getInputStream());
+ } catch (IOException e) {
+ logger.error("Get body string error.{}",e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 获取请求Body
+ *
+ * @return String
+ */
+ public String getBodyString() {
+ final InputStream inputStream = new ByteArrayInputStream(body);
+
+ return inputStream2String(inputStream);
+ }
+
+ /**
+ * 将inputStream里的数据读取出来并转换成字符串
+ *
+ * @param inputStream inputStream
+ * @return String
+ */
+ private String inputStream2String(InputStream inputStream) {
+ StringBuilder sb = new StringBuilder();
+ BufferedReader reader = null;
+
+ try {
+ reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ logger.error("close stream error.{}", e);
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ public BufferedReader getReader() throws IOException {
+ return new BufferedReader(new InputStreamReader(getInputStream()));
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);
+
+ return new ServletInputStream() {
+ @Override
+ public int read() throws IOException {
+ return inputStream.read();
+ }
+
+ @Override
+ public boolean isFinished() {
+ return false;
+ }
+
+ @Override
+ public boolean isReady() {
+ return false;
+ }
+
+ @Override
+ public void setReadListener(ReadListener readListener) {
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/alihealth/d2d/provtest/utils/ResponseBuilder.java b/src/main/java/com/alihealth/d2d/provtest/utils/ResponseBuilder.java
new file mode 100644
index 0000000..a72c285
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/utils/ResponseBuilder.java
@@ -0,0 +1,70 @@
+package com.alihealth.d2d.provtest.utils;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alihealth.d2d.provtest.enums.BuzStatusEnum;
+import com.alihealth.d2d.provtest.enums.StatusEnum;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ResponseBuilder extends HashMap{
+
+ public void status(Integer status){
+ this.put("status", status);
+ }
+
+ public void message(String message){
+ this.put("message", message);
+ }
+
+ public void body(BuzStatusEnum buzStatusEnum){
+ Map body = new HashMap<>();
+ body.put("buz_status", buzStatusEnum.getBuzStatus());
+ body.put("buz_message", buzStatusEnum.getBuzMessage());
+ this.put("body", body);
+ }
+
+ public void body(Integer buzStatus,String buzMsg){
+ Map body = new HashMap<>();
+ body.put("buz_status", buzStatus);
+ body.put("buz_message", buzMsg);
+ this.put("body", body);
+ }
+
+ public static Map fail(BuzStatusEnum buzStatusEnumg){
+ ResponseBuilder builder = new ResponseBuilder();
+ builder.status(StatusEnum.FAILED.getStatus());
+ builder.message(StatusEnum.FAILED.getMsg());
+ builder.body(buzStatusEnumg);
+ return builder.build();
+ }
+
+ public static Map success(){
+ ResponseBuilder builder = new ResponseBuilder();
+ builder.status(StatusEnum.SUCCESS.getStatus());
+ builder.message(StatusEnum.SUCCESS.getMsg());
+ builder.body(BuzStatusEnum.SUCCESS);
+ return builder.build();
+ }
+
+ public static Map buzError(Integer buzStatus,String buzMessage){
+ ResponseBuilder builder = new ResponseBuilder();
+ builder.status(StatusEnum.BUZ_ERROR.getStatus());
+ builder.message(StatusEnum.BUZ_ERROR.getMsg());
+ builder.body(buzStatus,buzMessage);
+ return builder.build();
+ }
+
+ public Map build(){
+ Map responseMap = new HashMap<>();
+ responseMap.put("resp", this);
+
+ return responseMap;
+ }
+
+ public static void main(String[] args){
+ System.out.println(JSON.toJSONString(fail(BuzStatusEnum.SIGN_CHECK_ERR)));
+ }
+}
+
diff --git a/src/main/java/com/alihealth/d2d/provtest/utils/StreamUtil.java b/src/main/java/com/alihealth/d2d/provtest/utils/StreamUtil.java
new file mode 100644
index 0000000..8a8f00d
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/utils/StreamUtil.java
@@ -0,0 +1,91 @@
+package com.alihealth.d2d.provtest.utils;
+
+
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.util.StringUtils;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+/**
+ *
+ * @author xuwangyu
+ *
+ */
+public class StreamUtil {
+ /**
+ * 将文件内容压缩并转为base64格式
+ * @param fileName 文件名称
+ * @param fileContent 文件内容
+ * @return 压缩后Base64内容
+ * @throws Exception
+ */
+ public static String getStreamContent(String fileName ,String fileContent) throws Exception {
+ if(StringUtils.isEmpty(fileName) || StringUtils.isEmpty(fileContent)) {
+ throw new NullPointerException();
+ }
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(5 * 1024 * 1024);
+ ZipOutputStream zos = new ZipOutputStream(bos);
+ InputStream is = new ByteArrayInputStream(fileContent.getBytes());
+ BufferedInputStream fileReader = new BufferedInputStream(is);
+ zos.putNextEntry(new ZipEntry(fileName));
+ byte[] b = new byte[1024];
+ int i;
+ while ((i = fileReader.read(b)) != -1) {
+ zos.write(b, 0, i);
+ }
+ is.close();
+ zos.closeEntry();
+ zos.flush();
+ zos.close();
+ fileReader.close();
+ bos.close();
+ byte[] bytes = bos.toByteArray();
+ return Base64.encodeBase64String(bytes);
+ }
+
+ /**
+ * 先base64解密 后解压缩
+ * @param s
+ * @return
+ */
+ public static String getUnStreamContent(String s){
+ byte[] bytes = Base64.decodeBase64(s);
+ return new String(unZip(bytes));
+ }
+
+ /***
+ * 解压Zip
+ * @param data
+ * @return
+ */
+ public static byte[] unZip(byte[] data) {
+ byte[] b = null;
+ try {
+ ByteArrayInputStream bis = new ByteArrayInputStream(data);
+ ZipInputStream zip = new ZipInputStream(bis);
+ while (zip.getNextEntry() != null) {
+ byte[] buf = new byte[1024];
+ int num ;
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ while ((num = zip.read(buf, 0, buf.length)) != -1) {
+ bos.write(buf, 0, num);
+ }
+ b = bos.toByteArray();
+ bos.flush();
+ bos.close();
+ }
+ zip.close();
+ bis.close();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return b;
+ }
+
+}
diff --git a/src/main/java/com/alihealth/d2d/provtest/utils/XmlStringParser.java b/src/main/java/com/alihealth/d2d/provtest/utils/XmlStringParser.java
new file mode 100644
index 0000000..34b9ae3
--- /dev/null
+++ b/src/main/java/com/alihealth/d2d/provtest/utils/XmlStringParser.java
@@ -0,0 +1,112 @@
+package com.alihealth.d2d.provtest.utils;
+
+import com.alihealth.d2d.provtest.domain.DomesticPharmaBaseInfo;
+import com.alihealth.d2d.provtest.factory.EntityFactory;
+import com.alihealth.d2d.provtest.factory.EntityFactoryManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.ByteArrayInputStream;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Jason
+ * @date 2025年05月29日 10:27:04
+ */
+public class XmlStringParser {
+
+ private static final Logger logger = LoggerFactory.getLogger(XmlStringParser.class);
+
+
+ private static String getElementText(Element parent, String tagName) {
+ NodeList nodeList = parent.getElementsByTagName(tagName);
+ if (nodeList.getLength() > 0) {
+ Node node = nodeList.item(0);
+ return node.getTextContent().trim();
+ }
+ return null;
+ }
+
+ private static LocalDate parseDate(String dateStr) {
+ if (dateStr == null || dateStr.length() != 8 || "无".equals(dateStr)) {
+ return null;
+ }
+
+ try {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
+ return LocalDate.parse(dateStr, formatter);
+ } catch (Exception e) {
+ logger.warn("日期解析失败: " + dateStr, e);
+ return null;
+ }
+ }
+
+
+ public static void parseXml(String xmlString, String subType, EntityProcessor processor) {
+ List