## C++大模型SDK开发实践:DeepSeek模型接入与质量保障
在大模型技术快速发展的背景下,构建稳定可靠的C++ SDK成为连接应用与AI能力的重要桥梁。本文将系统介绍DeepSeek模型接入的完整流程,涵盖HTTP通信实现、错误处理机制以及基于GTest的单元测试方案,为开发者提供可落地的工程实践指南。
### SDK整体架构设计
一个完整的大模型SDK应采用分层架构,确保各模块职责清晰、便于测试和维护。以下是核心架构设计:
```cpp
// SDK整体架构示例
namespace DeepSeekSDK {
// 配置层:管理API密钥、端点等配置
class Config {
public:
static Config& instance() {
static Config config;
return config;
}
void set_api_key(const std::string& key) { api_key_ = key; }
void set_base_url(const std::string& url) { base_url_ = url; }
void set_timeout(int seconds) { timeout_seconds_ = seconds; }
private:
std::string api_key_;
std::string base_url_ = "https://api.deepseek.com";
int timeout_seconds_ = 30;
};
// HTTP通信层:抽象网络请求
class HttpClient {
public:
virtual Response post(const std::string& endpoint,
const std::string& body) = 0;
virtual ~HttpClient() = default;
};
// 模型层:封装DeepSeek模型接口
class DeepSeekModel {
public:
explicit DeepSeekModel(std::shared_ptr<HttpClient> client);
struct CompletionRequest {
std::string prompt;
double temperature = 0.7;
int max_tokens = 1000;
// ... 其他参数
};
struct CompletionResponse {
std::string text;
int tokens_used;
bool success;
std::string error_message;
};
CompletionResponse complete(const CompletionRequest& request);
private:
std::shared_ptr<HttpClient> http_client_;
};
}
```
### HTTP通信实现方案
HTTP通信层需要处理网络请求、响应解析、错误重试等复杂逻辑。我们采用接口抽象,便于测试和替换不同的HTTP实现。
```cpp
// HTTP通信实现
#include <curl/curl.h>
#include <memory>
#include <string>
#include <system_error>
namespace DeepSeekSDK {
// 回调函数处理响应数据
static size_t write_callback(char* ptr, size_t size,
size_t nmemb, void* userdata) {
size_t total_size = size * nmemb;
std::string* response = static_cast<std::string*>(userdata);
response->append(ptr, total_size);
return total_size;
}
class CurlHttpClient : public HttpClient {
public:
CurlHttpClient() {
curl_global_init(CURL_GLOBAL_DEFAULT);
}
~CurlHttpClient() override {
curl_global_cleanup();
}
struct Response {
int status_code;
std::string body;
std::string error;
std::chrono::milliseconds duration;
};
Response post(const std::string& endpoint,
const std::string& body) override {
auto start_time = std::chrono::steady_clock::now();
Response response;
CURL* curl = curl_easy_init();
if (!curl) {
response.error = "Failed to initialize CURL";
return response;
}
std::string full_url = Config::instance().get_base_url() + endpoint;
std::string response_data;
// 设置CURL选项
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers,
("Authorization: Bearer " + Config::instance().get_api_key()).c_str());
curl_easy_setopt(curl, CURLOPT_URL, full_url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data);
curl_easy_setopt(curl, CURLOPT_TIMEOUT,
Config::instance().get_timeout());
curl_easy_setopt(curl, CURLOPT_USERAGENT, "DeepSeek-CPP-SDK/1.0");
// SSL验证设置
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
// 执行请求
CURLcode res = curl_easy_perform(curl);
if (res == CURLE_OK) {
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
response.status_code = static_cast<int>(http_code);
response.body = response_data;
} else {
response.error = curl_easy_strerror(res);
}
// 获取请求统计信息
double total_time = 0;
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time);
response.duration = std::chrono::milliseconds(
static_cast<int>(total_time * 1000));
// 清理资源
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
auto end_time = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - start_time);
// 记录请求日志
log_request(endpoint, body, response, elapsed);
return response;
}
private:
void log_request(const std::string& endpoint,
const std::string& request_body,
const Response& response,
std::chrono::milliseconds elapsed) {
// 实现请求日志记录
std::cout << "[HTTP Request] " << endpoint
<< " - Status: " << response.status_code
<< " - Duration: " << elapsed.count() << "ms"
<< std::endl;
}
};
// 重试策略实现
class RetryStrategy {
public:
struct RetryConfig {
int max_retries = 3;
int base_delay_ms = 1000;
float backoff_factor = 2.0f;
std::vector<int> retry_status_codes = {408, 429, 500, 502, 503, 504};
};
template<typename Func>
static auto execute_with_retry(Func func,
const RetryConfig& config = RetryConfig()) {
int attempt = 0;
std::exception_ptr last_exception;
while (attempt <= config.max_retries) {
try {
if (attempt > 0) {
int delay = static_cast<int>(
config.base_delay_ms *
std::pow(config.backoff_factor, attempt - 1));
std::this_thread::sleep_for(
std::chrono::milliseconds(delay));
}
return func();
} catch (const HttpException& e) {
last_exception = std::current_exception();
// 检查是否应该重试
if (attempt == config.max_retries ||
!should_retry(e.status_code(), config.retry_status_codes)) {
std::rethrow_exception(last_exception);
}
attempt++;
} catch (...) {
// 非HTTP异常,不重试
throw;
}
}
std::rethrow_exception(last_exception);
}
private:
static bool should_retry(int status_code,
const std::vector<int>& retry_codes) {
return std::find(retry_codes.begin(),
retry_codes.end(),
status_code) != retry_codes.end();
}
};
}
```
### DeepSeek模型接入实现
模型接入层负责将HTTP响应转换为业务对象,并提供友好的API接口。
```cpp
// DeepSeek模型接入实现
#include <nlohmann/json.hpp>
namespace DeepSeekSDK {
class DeepSeekModel::Impl {
public:
explicit Impl(std::shared_ptr<HttpClient> client)
: http_client_(std::move(client)) {}
CompletionResponse complete(const CompletionRequest& request) {
CompletionResponse response;
try {
// 构建请求体
nlohmann::json request_json;
request_json["model"] = "deepseek-chat";
request_json["messages"] = nlohmann::json::array({
{{"role", "user"}, {"content", request.prompt}}
});
request_json["temperature"] = request.temperature;
request_json["max_tokens"] = request.max_tokens;
request_json["stream"] = false;
// 执行HTTP请求(带重试)
auto http_response = RetryStrategy::execute_with_retry(
[&]() {
return http_client_->post("/v1/chat/completions",
request_json.dump());
}
);
// 解析响应
if (http_response.status_code == 200) {
auto json_response = nlohmann::json::parse(http_response.body);
response.text = json_response["choices"][0]["message"]["content"];
response.tokens_used = json_response["usage"]["total_tokens"];
response.success = true;
} else {
handle_error_response(http_response, response);
}
} catch (const std::exception& e) {
response.success = false;
response.error_message = std::string("Request failed: ") + e.what();
}
return response;
}
// 流式响应支持
class StreamHandler {
public:
using ChunkCallback = std::function<void(const std::string& chunk)>;
using ErrorCallback = std::function<void(const std::string& error)>;
void stream_complete(const CompletionRequest& request,
ChunkCallback chunk_callback,
ErrorCallback error_callback) {
nlohmann::json request_json;
request_json["model"] = "deepseek-chat";
request_json["messages"] = nlohmann::json::array({
{{"role", "user"}, {"content", request.prompt}}
});
request_json["temperature"] = request.temperature;
request_json["max_tokens"] = request.max_tokens;
request_json["stream"] = true;
<"u9.a8k1.org.cn"><"b2.a8k1.org.cn"><"x5.a8k1.org.cn">
// 设置流式响应处理器
http_client_->set_stream_handler(
[chunk_callback](const std::string& chunk) {
parse_stream_chunk(chunk, chunk_callback);
}
);
try {
auto response = http_client_->post("/v1/chat/completions",
request_json.dump());
if (response.status_code != 200) {
error_callback("Stream request failed with status: " +
std::to_string(response.status_code));
}
} catch (const std::exception& e) {
error_callback(std::string("Stream error: ") + e.what());
}
}
private:
static void parse_stream_chunk(const std::string& chunk,
ChunkCallback callback) {
if (chunk.empty() || chunk == "data: [DONE]") {
return;
}
try {
// 移除"data: "前缀
if (chunk.rfind("data: ", 0) == 0) {
std::string json_str = chunk.substr(6);
auto json = nlohmann::json::parse(json_str);
if (json.contains("choices") &&
json["choices"].size() > 0 &&
json["choices"][0].contains("delta") &&
json["choices"][0]["delta"].contains("content")) {
std::string content =
json["choices"][0]["delta"]["content"];
if (!content.empty()) {
callback(content);
}
}
}
} catch (const nlohmann::json::exception&) {
// 忽略解析错误,继续处理下一个chunk
}
}
};
private:
void handle_error_response(const HttpClient::Response& http_response,
CompletionResponse& response) {
response.success = false;
try {
auto error_json = nlohmann::json::parse(http_response.body);
if (error_json.contains("error") &&
error_json["error"].is_object() &&
error_json["error"].contains("message")) {
response.error_message = error_json["error"]["message"];
} else {
response.error_message = "HTTP " +
std::to_string(http_response.status_code);
}
} catch (...) {
response.error_message = "HTTP " +
std::to_string(http_response.status_code) +
": " + http_response.error;
}
}
std::shared_ptr<HttpClient> http_client_;
};
// 公开接口实现
DeepSeekModel::DeepSeekModel(std::shared_ptr<HttpClient> client)
: impl_(std::make_unique<Impl>(std::move(client))) {}
DeepSeekModel::~DeepSeekModel() = default;
DeepSeekModel::CompletionResponse
DeepSeekModel::complete(const CompletionRequest& request) {
return impl_->complete(request);
}
}
```
### 基于GTest的单元测试体系
单元测试是保证SDK质量的关键。我们采用GTest构建全面的测试套件。
```cpp
// 单元测试实现
#include <gtest/gtest.h>
#include <gmock/gmock.h>
namespace DeepSeekSDK::Test {
// Mock HTTP客户端
class MockHttpClient : public HttpClient {
public:
MOCK_METHOD(Response, post,
(const std::string& endpoint, const std::string& body),
(override));
};
// 配置测试
class ConfigTest : public ::testing::Test {
protected:
void SetUp() override {
// 保存原始配置
original_api_key = Config::instance().get_api_key();
original_base_url = Config::instance().get_base_url();
}
void TearDown() override {
// 恢复原始配置
Config::instance().set_api_key(original_api_key);
Config::instance().set_base_url(original_base_url);
}
private:
std::string original_api_key;
std::string original_base_url;
};
TEST_F(ConfigTest, SetAndGetApiKey) {
Config::instance().set_api_key("test-key-123");
EXPECT_EQ(Config::instance().get_api_key(), "test-key-123");
}
TEST_F(ConfigTest, DefaultBaseUrl) {
EXPECT_EQ(Config::instance().get_base_url(),
"https://api.deepseek.com");
}
// HTTP客户端测试
class HttpClientTest : public ::testing::Test {
protected:
std::shared_ptr<MockHttpClient> mock_client;
void SetUp() override {
mock_client = std::make_shared<MockHttpClient>();
}
};
TEST_F(HttpClientTest, PostSuccess) {
// 设置期望
HttpClient::Response expected_response;
expected_response.status_code = 200;
expected_response.body = R"({
"choices": [{"message": {"content": "Hello, world!"}}],
"usage": {"total_tokens": 10}
})";
EXPECT_CALL(*mock_client, post("/v1/chat/completions", testing::_))
.WillOnce(testing::Return(expected_response));
// 创建模型实例
DeepSeekModel model(mock_client);
// 执行测试
DeepSeekModel::CompletionRequest request;
request.prompt = "Say hello";
auto response = model.complete(request);
// 验证结果
EXPECT_TRUE(response.success);
EXPECT_EQ(response.text, "Hello, world!");
EXPECT_EQ(response.tokens_used, 10);
EXPECT_TRUE(response.error_message.empty());
}
TEST_F(HttpClientTest, PostNetworkError) {
// 模拟网络错误
EXPECT_CALL(*mock_client, post(testing::_, testing::_))
.WillOnce(testing::Throw(
std::runtime_error("Network connection failed")));
DeepSeekModel model(mock_client);
DeepSeekModel::CompletionRequest request;
request.prompt = "Test prompt";
auto response = model.complete(request);
EXPECT_FALSE(response.success);
EXPECT_TRUE(response.text.empty());
EXPECT_EQ(response.tokens_used, 0);
EXPECT_NE(response.error_message.find("Request failed"),
std::string::npos);
}
TEST_F(HttpClientTest, PostServerError) {
HttpClient::Response error_response;
error_response.status_code = 500;
error_response.body = R"({
"error": {
"message": "Internal server error",
"type": "server_error"
}
})";
EXPECT_CALL(*mock_client, post(testing::_, testing::_))
.WillOnce(testing::Return(error_response));
DeepSeekModel model(mock_client);
DeepSeekModel::CompletionRequest request;
request.prompt = "Test prompt";
auto response = model.complete(request);
EXPECT_FALSE(response.success);
EXPECT_TRUE(response.text.empty());
EXPECT_EQ(response.tokens_used, 0);
EXPECT_EQ(response.error_message, "Internal server error");
}
// 重试策略测试
class RetryStrategyTest : public ::testing::Test {
protected:
int call_count = 0;
std::function<HttpClient::Response()> create_failing_func(
int fail_times, int success_status = 200) {
return [this, fail_times, success_status]() {
call_count++;
if (call_count <= fail_times) {
HttpClient::Response response;
response.status_code = 500;
throw HttpException("Server error", 500);
}
HttpClient::Response response;
response.status_code = success_status;
response.body = "Success";
return response;
};
}
};
TEST_F(RetryStrategyTest, SuccessWithoutRetry) {
call_count = 0;
auto func = create_failing_func(0);
EXPECT_NO_THROW({
auto result = RetryStrategy::execute_with_retry(func);
EXPECT_EQ(result.status_code, 200);
});
EXPECT_EQ(call_count, 1);
}
TEST_F(RetryStrategyTest, SuccessAfterRetries) {
call_count = 0;
auto func = create_failing_func(2);
RetryStrategy::RetryConfig config;
config.max_retries = 3;
EXPECT_NO_THROW({
auto result = RetryStrategy::execute_with_retry(func, config);
EXPECT_EQ(result.status_code, 200);
});
EXPECT_EQ(call_count, 3); // 失败2次 + 成功1次
}
TEST_F(RetryStrategyTest, ExceedMaxRetries) {
call_count = 0;
auto func = create_failing_func(5);
RetryStrategy::RetryConfig config;
config.max_retries = 3;
EXPECT_THROW({
RetryStrategy::execute_with_retry(func, config);
}, HttpException);
EXPECT_EQ(call_count, 4); // 初始调用 + 3次重试
}
// JSON解析测试
class JsonParsingTest : public ::testing::Test {};
TEST_F(JsonParsingTest, ParseValidResponse) {
std::string json_str = R"({
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "deepseek-chat",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello there!"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21
}
})";
auto json = nlohmann::json::parse(json_str);
EXPECT_EQ(json["id"], "chatcmpl-123");
EXPECT_EQ(json["object"], "chat.completion");
EXPECT_EQ(json["choices"][0]["message"]["content"], "Hello there!");
EXPECT_EQ(json["usage"]["total_tokens"], 21);
}
TEST_F(JsonParsingTest, HandleMissingFields) {
std::string json_str = R"({
"choices": [{}],
"usage": {}
})";
auto json = nlohmann::json::parse(json_str);
// 安全访问字段
std::string content = json.value("choices", nlohmann::json::array())[0]
.value("message", nlohmann::json::object())
.value("content", "");
EXPECT_TRUE(content.empty());
}
// 集成测试(需要实际API密钥)
class IntegrationTest : public ::testing::Test {
protected:
void SetUp() override {
// 从环境变量读取测试配置
const char* api_key = std::getenv("DEEPSEEK_API_KEY");
if (api_key) {
Config::instance().set_api_key(api_key);
} else {
GTEST_SKIP() << "DEEPSEEK_API_KEY environment variable not set";
}
}
};
TEST_F(IntegrationTest, CompleteRequest) {
auto http_client = std::make_shared<CurlHttpClient>();
DeepSeekModel model(http_client);
DeepSeekModel::CompletionRequest request;
request.prompt = "Say hello in English";
request.max_tokens = 50;
auto response = model.complete(request);
EXPECT_TRUE(response.success) << "Error: " << response.error_message;
EXPECT_FALSE(response.text.empty());
EXPECT_GT(response.tokens_used, 0);
// 验证响应内容包含相关词汇
std::string lower_response;
std::transform(response.text.begin(), response.text.end(),
std::back_inserter(lower_response),
[](unsigned char c) { return std::tolower(c); });
EXPECT_TRUE(lower_response.find("hello") != std::string::npos ||
lower_response.find("hi") != std::string::npos);
}
// 性能测试
class PerformanceTest : public ::testing::Test {
protected:
static constexpr int NUM_REQUESTS = 10;
void SetUp() override {
// 设置模拟客户端返回固定响应
mock_client = std::make_shared<MockHttpClient>();
HttpClient::Response mock_response;
mock_response.status_code = 200;
mock_response.body = R"({
"choices": [{"message": {"content": "Test response"}}],
"usage": {"total_tokens": 10}
})";
EXPECT_CALL(*mock_client, post(testing::_, testing::_))
.WillRepeatedly(testing::Return(mock_response));
}
std::shared_ptr<MockHttpClient> mock_client;
};
TEST_F(PerformanceTest, ConcurrentRequests) {
DeepSeekModel model(mock_client);
DeepSeekModel::CompletionRequest request;
request.prompt = "Test";
std::vector<std::future<DeepSeekModel::CompletionResponse>> futures;
auto start_time = std::chrono::high_resolution_clock::now();
// 发起并发请求
for (int i = 0; i < NUM_REQUESTS; ++i) {
futures.push_back(std::async(std::launch::async, [&]() {
return model.complete(request);
}));
}
// 等待所有请求完成
for (auto& future : futures) {
auto response = future.get();
EXPECT_TRUE(response.success);
}
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - start_time);
std::cout << "Completed " << NUM_REQUESTS
<< " requests in " << duration.count() << "ms" << std::endl;
}
<"r1.a8k1.org.cn"><"k8.a8k1.org.cn"><"p0.a8k1.org.cn">
// 内存泄漏测试
class MemoryTest : public ::testing::Test {};
TEST_F(MemoryTest, NoMemoryLeakOnMultipleInstances) {
constexpr int NUM_INSTANCES = 1000;
// 使用Valgrind或类似工具检测内存泄漏
for (int i = 0; i < NUM_INSTANCES; ++i) {
auto client = std::make_shared<MockHttpClient>();
auto model = std::make_unique<DeepSeekModel>(client);
// 模型对象在循环结束时自动销毁
}
// 如果没有内存泄漏,测试通过
SUCCEED();
}
}
// 测试主程序
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
// 设置测试环境
::testing::GTEST_FLAG(catch_exceptions) = false;
::testing::GTEST_FLAG(break_on_failure) = true;
// 运行所有测试
return RUN_ALL_TESTS();
}
```
### CMake构建配置
为了确保跨平台兼容性,提供完整的CMake构建配置。
```cmake
# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(DeepSeekSDK VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 依赖查找
find_package(CURL REQUIRED)
find_package(nlohmann_json 3.9 REQUIRED)
find_package(GTest REQUIRED)
# SDK库
add_library(deepseek_sdk
src/config.cpp
src/http_client.cpp
src/deepseek_model.cpp
src/retry_strategy.cpp
)
target_include_directories(deepseek_sdk
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(deepseek_sdk
PUBLIC
CURL::libcurl
nlohmann_json::nlohmann_json
)
# 安装配置
install(TARGETS deepseek_sdk
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(DIRECTORY include/ DESTINATION include)
# 测试
enable_testing()
add_executable(deepseek_sdk_tests
tests/config_test.cpp
tests/http_client_test.cpp
tests/model_test.cpp
tests/retry_strategy_test.cpp
tests/integration_test.cpp
)
target_link_libraries(deepseek_sdk_tests
deepseek_sdk
GTest::gtest
GTest::gtest_main
)
add_test(NAME deepseek_sdk_unit_tests
COMMAND deepseek_sdk_tests)
# 示例程序
add_executable(example
examples/basic_usage.cpp
)
target_link_libraries(example
deepseek_sdk
)
# 代码覆盖率(如果启用)
if(ENABLE_COVERAGE)
target_compile_options(deepseek_sdk_tests
PRIVATE --coverage
)
target_link_options(deepseek_sdk_tests
PRIVATE --coverage
)
endif()
```
### 持续集成配置
为了确保代码质量,设置GitHub Actions持续集成流程。
```yaml
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libcurl4-openssl-dev build-essential cmake
- name: Configure CMake
run: |
cmake -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_TESTS=ON \
-DCMAKE_CXX_FLAGS="-Wall -Wextra -Werror"
- name: Build
run: cmake --build build --config Debug
- name: Run unit tests
run: |
cd build
ctest --output-on-failure
- name: Run integration tests
run: |
cd build
DEEPSEEK_API_KEY=${{ secrets.DEEPSEEK_API_KEY }} \
./tests/deepseek_sdk_tests --gtest_filter="*Integration*"
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
- name: Generate coverage report
if: success()
run: |
cd build
lcov --capture --directory . --output-file coverage.info
lcov --remove coverage.info '/usr/*' --output-file coverage.info
lcov --list coverage.info
```
### 总结
通过系统化的架构设计、健壮的HTTP通信实现和全面的单元测试覆盖,我们构建了一个高质量的C++ DeepSeek模型SDK。该SDK具备良好的可测试性、错误处理能力和性能表现,能够满足生产环境的需求。
在实际开发过程中,需要持续关注API接口的更新,及时调整SDK实现。同时,建议建立完善的监控和日志系统,跟踪SDK在实际使用中的表现。通过持续集成和自动化测试,确保SDK的质量和稳定性,为上层应用提供可靠的AI能力支持。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3109814/ ,如需转载,请注明出处,否则将追究法律责任。