综合平台编程器项目的远程存储
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

218 lines
6.0 KiB

  1. #include "project_service.h"
  2. #include <algorithm>
  3. #include <chrono>
  4. #include <cctype>
  5. #include <random>
  6. #include <sstream>
  7. #include <utility>
  8. namespace {
  9. // 生成唯一工程 ID:project-时间戳-随机数
  10. std::string generateProjectId()
  11. {
  12. const auto timestamp = static_cast<unsigned long long>(
  13. std::chrono::high_resolution_clock::now().time_since_epoch().count());
  14. std::random_device random_device;
  15. const auto random_value = static_cast<unsigned long long>(random_device());
  16. std::ostringstream stream;
  17. stream << "project-" << std::hex << timestamp << '-' << random_value;
  18. return stream.str();
  19. }
  20. } // namespace
  21. ProjectService::ProjectService(
  22. ProjectStorage &storage,
  23. const ProjectLimitSettings &project_limits)
  24. : storage_(storage),
  25. project_limits_(project_limits),
  26. project_(makeNewProject("未命名工程"))
  27. {
  28. }
  29. const Project &ProjectService::project() const
  30. {
  31. return project_;
  32. }
  33. Project &ProjectService::editProject()
  34. {
  35. modified_ = true;
  36. return project_;
  37. }
  38. bool ProjectService::hasCurrentFile() const
  39. {
  40. return !current_file_path_.empty();
  41. }
  42. // 查询是否存在未保存修改
  43. bool ProjectService::isModified() const
  44. {
  45. return modified_;
  46. }
  47. const ProjectLimitSettings &ProjectService::projectLimits() const
  48. {
  49. return project_limits_;
  50. }
  51. void ProjectService::restoreModifiedState(bool modified)
  52. {
  53. modified_ = modified;
  54. }
  55. ProjectOperationResult ProjectService::createNewProject(const std::string &name)
  56. {
  57. if (isBlank(name))
  58. {
  59. return {false,
  60. ProjectServiceError::InvalidProjectName,
  61. ProjectStorageError::None,
  62. "工程名称不能为空"};
  63. }
  64. // 新工程创建成功后清除原文件关联,并标记为待保存
  65. project_ = makeNewProject(name);
  66. current_file_path_.clear();
  67. modified_ = true;
  68. return {true, ProjectServiceError::None, ProjectStorageError::None, {}};
  69. }
  70. ProjectOperationResult ProjectService::save()
  71. {
  72. if (!hasCurrentFile())
  73. {
  74. return {false,
  75. ProjectServiceError::FilePathRequired,
  76. ProjectStorageError::None,
  77. "必须指定工程文件路径"};
  78. }
  79. // 复用另存为流程,确保两种保存方式拥有相同的校验和错误处理
  80. return saveAs(current_file_path_);
  81. }
  82. ProjectOperationResult ProjectService::saveAs(const std::string &file_path)
  83. {
  84. if (isBlank(file_path))
  85. {
  86. return {false,
  87. ProjectServiceError::FilePathRequired,
  88. ProjectStorageError::None,
  89. "必须指定工程文件路径"};
  90. }
  91. std::string validation_error;
  92. if (!project_.validate(project_limits_, &validation_error))
  93. {
  94. return {false,
  95. ProjectServiceError::InvalidProject,
  96. ProjectStorageError::InvalidProject,
  97. validation_error};
  98. }
  99. // 先完成领域校验,避免将非法工程交给存储层
  100. const ProjectSaveResult result = storage_.save(project_, file_path);
  101. if (!result.succeeded)
  102. {
  103. return storageFailure(result.error, result.message);
  104. }
  105. // 只有存储成功后才更新文件关联和未保存状态
  106. current_file_path_ = file_path;
  107. modified_ = false;
  108. return {true, ProjectServiceError::None, ProjectStorageError::None, {}};
  109. }
  110. ProjectOperationResult ProjectService::exportAs(const std::string &file_path) const
  111. {
  112. if (isBlank(file_path))
  113. {
  114. return {false,
  115. ProjectServiceError::FilePathRequired,
  116. ProjectStorageError::None,
  117. "必须指定导出文件路径"};
  118. }
  119. std::string validation_error;
  120. if (!project_.validate(project_limits_, &validation_error))
  121. {
  122. return {false,
  123. ProjectServiceError::InvalidProject,
  124. ProjectStorageError::InvalidProject,
  125. validation_error};
  126. }
  127. const ProjectSaveResult result = storage_.save(project_, file_path);
  128. if (!result.succeeded)
  129. {
  130. return storageFailure(result.error, result.message);
  131. }
  132. return {true, ProjectServiceError::None, ProjectStorageError::None, {}};
  133. }
  134. ProjectOperationResult ProjectService::load(const std::string &file_path)
  135. {
  136. if (isBlank(file_path))
  137. {
  138. return {false,
  139. ProjectServiceError::FilePathRequired,
  140. ProjectStorageError::None,
  141. "必须指定工程文件路径"};
  142. }
  143. // 存储层先在临时结果中解析,避免半个工程替换当前工程
  144. ProjectLoadResult result = storage_.load(file_path);
  145. if (!result.succeeded)
  146. {
  147. return storageFailure(result.error, result.message);
  148. }
  149. std::string validation_error;
  150. if (!result.project.validate(project_limits_, &validation_error))
  151. {
  152. return {false,
  153. ProjectServiceError::InvalidProject,
  154. ProjectStorageError::InvalidProject,
  155. validation_error};
  156. }
  157. // 文件读取和领域校验均成功后,才替换当前工程状态
  158. project_ = std::move(result.project);
  159. current_file_path_ = file_path;
  160. modified_ = false;
  161. return {true, ProjectServiceError::None, ProjectStorageError::None, {}};
  162. }
  163. // 创建一个全新空白工程实例
  164. Project ProjectService::makeNewProject(const std::string &name)
  165. {
  166. Project project;
  167. project.metadata.id = generateProjectId();
  168. project.metadata.name = name;
  169. // 新工程固定使用当前存储格式版本
  170. project.metadata.formatVersion = "3.0";
  171. return project;
  172. }
  173. // 判断字符串是不是空白:空字符串 / 全是空格、制表符都算空白
  174. bool ProjectService::isBlank(const std::string &value)
  175. {
  176. return value.empty()
  177. || std::all_of(
  178. value.cbegin(),
  179. value.cend(),
  180. [](unsigned char character)
  181. {
  182. return std::isspace(character) != 0;
  183. });
  184. }
  185. // 包装存储层失败结果
  186. ProjectOperationResult ProjectService::storageFailure(
  187. ProjectStorageError error, const std::string &message)
  188. {
  189. return {false, ProjectServiceError::StorageFailure, error, message};
  190. }