diff --git a/文档/技术实现/20250613-Java使用Maven及Gitlab.md b/文档/技术实现/20250613-Java使用Maven及Gitlab.md
new file mode 100644
index 0000000..10cb9c3
--- /dev/null
+++ b/文档/技术实现/20250613-Java使用Maven及Gitlab.md
@@ -0,0 +1,444 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Java使用Maven及Gitlab
+
+## Java使用Maven
+### 所需依赖
+```xml
+
+
+
+
+ org.apache.maven
+ maven-embedder
+ 3.9.6
+
+
+
+ org.apache.maven
+ maven-compat
+ 3.9.6
+
+
+
+ org.apache.maven.resolver
+ maven-resolver-transport-http
+ 1.9.18
+
+
+ org.apache.maven.resolver
+ maven-resolver-connector-basic
+ 1.9.18
+
+
+
+ org.apache.maven
+ maven-resolver-provider
+ 3.9.6
+
+
+
+ org.apache.maven
+ maven-settings-builder
+ 3.9.6
+
+
+```
+### 使用Archetype生成项目
+```java
+package org.jeecg.modules.devops.archetype;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.maven.cli.MavenCli;
+import org.jeecg.modules.devops.model.YuanMengOnlineService;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+@Slf4j
+public class MavenUtils {
+
+ private static final String ARCHETYPE_GROUP_ID = "com.yuanmeng.engine";
+ private static final String ARCHETYPE_ARTIFACT_ID = "engine-archetype-web";
+ private static final String ARCHETYPE_VERSION = "2.0.2-SNAPSHOT";
+
+ public static Path createProject(YuanMengOnlineService archetypeInfo) {
+ try {
+ // 配置项目目录
+ Path projectDir = Files.createTempDirectory(archetypeInfo.getArchetypeArtifactId());
+ log.info("项目生成目录: {}", projectDir);
+
+ // 创建 settings.xml 文件
+ Path settingsFile = createTempSettingsFile();
+
+ // 下面这行代码不能缺少,否则无法执行
+ configureSystemProperties(settingsFile);
+
+ // 使用 Maven Embedder 生成项目
+ generateMavenProject(projectDir.toFile(), settingsFile, archetypeInfo);
+
+ Path path = projectDir.resolve(archetypeInfo.getArchetypeArtifactId());
+ log.info("✅ 项目成功创建于: {}", path);
+ return path;
+ } catch (Exception e) {
+ log.error("❌ 出现错误: {}, {}", e.getMessage(), archetypeInfo, e);
+ throw new RuntimeException("项目初始化失败:" + e.getMessage());
+ }
+ }
+
+ private static void generateMavenProject(File baseDir, Path settingsFile, YuanMengOnlineService archetypeInfo) {
+ // String[] argsClean = new String[]{
+ // "-X", "dependency:resolve", "-s", settingsFile.getAbsolutePath()
+ // };
+ // int i = new MavenCli().doMain(argsClean, baseDir.getAbsolutePath(), System.out, System.err);
+
+ // boolean del = FileUtil.del("/tmp/repository");
+ // log.info("delete tmp directory: {}", del);
+
+ // 构建 Maven 参数
+ String[] mvnArgs = new String[]{
+ "archetype:generate",
+ "-DinteractiveMode=false",
+ "-DarchetypeGroupId=" + ARCHETYPE_GROUP_ID,
+ "-DarchetypeArtifactId=" + ARCHETYPE_ARTIFACT_ID,
+ "-DarchetypeVersion=" + ARCHETYPE_VERSION,
+ "-DgroupId=" + archetypeInfo.getArchetypeGroupId(),
+ "-DartifactId=" + archetypeInfo.getArchetypeArtifactId(),
+ "-Dversion=" + archetypeInfo.getArchetypeVersion(),
+ "-Dapp=" + archetypeInfo.getArchetypeApp(),
+ "-Dpackage=" + archetypeInfo.getArchetypePackage(),
+ "-B", // 开启批处理模式
+ "-s", settingsFile.toString()
+ };
+
+ log.info("Maven 项目生成指令:{}", String.join(" ", mvnArgs));
+
+ // 使用 Maven Embedder
+ MavenCli cli = new MavenCli();
+ int result = cli.doMain(mvnArgs, baseDir.getAbsolutePath(), System.out, System.err);
+
+ if (result != 0) {
+ throw new RuntimeException("Maven 项目生成失败 (退出码: " + result + ")");
+ }
+
+ log.info("✅ Maven 项目生成成功");
+ }
+
+ private static Path createTempSettingsFile() {
+ try {
+ // 创建临时 settings.xml 文件
+ Path settingsPath = Files.createTempFile("maven-settings", ".xml");
+
+ // 构建 settings.xml 内容
+ String settingsContent = buildSettingsXml();
+
+ // 写入文件
+ Files.write(settingsPath, settingsContent.getBytes());
+
+ // 设置文件删除钩子(程序退出时删除)
+ settingsPath.toFile().deleteOnExit();
+
+ return settingsPath;
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to create temp settings.xml", e);
+ }
+ }
+
+ private static String buildSettingsXml() {
+ return "\n" +
+ " /tmp/repository\n" +
+ " \n" +
+ " \n" +
+ " private-repo\n" +
+ " developer\n" +
+ " deve@123\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " private-repo\n" +
+ " Private Repository Mirror\n" +
+ " http://120.79.68.191:8081/repository/maven-public/\n" +
+ " *\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " default\n" +
+ " \n" +
+ " \n" +
+ " private-repo\n" +
+ " http://120.79.68.191:8081/repository/maven-public/\n" +
+ " true\n" +
+ " true\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " private-repo\n" +
+ " http://120.79.68.191:8081/repository/maven-public/\n" +
+ " true\n" +
+ " true\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " default\n" +
+ " \n" +
+ "";
+ }
+
+ private static void configureSystemProperties(Path settingsPath) {
+ System.setProperty("maven.multiModuleProjectDirectory", System.getProperty("user.dir"));
+ // 使用自定义 settings.xml
+ System.setProperty("maven.user.settings", settingsPath.toString());
+
+ // 配置超时(可选)
+ System.setProperty("maven.wagon.http.readTimeout", "300000"); // 5分钟
+ System.setProperty("maven.wagon.http.connectionTimeout", "120000");
+
+ // 禁用 SSL 验证(如果私服使用 HTTP)
+ System.setProperty("maven.wagon.http.ssl.insecure", "true");
+ System.setProperty("maven.wagon.http.ssl.allowall", "true");
+
+ // 启用调试日志(可选)
+ System.setProperty("org.slf4j.simpleLogger.log.org.apache.maven.cli", "info");
+ }
+
+}
+```
+----
+## Java操作Git及Gitlab
+### 依赖引入
+```xml
+
+
+
+
+ org.eclipse.jgit
+ org.eclipse.jgit
+ 6.10.1.202505221210-r
+
+
+
+ org.gitlab4j
+ gitlab4j-api
+ 5.3.0
+
+
+```
+### Java代码调用
+#### Gitlab调用工具类
+```java
+package org.jeecg.modules.devops.archetype;
+
+import cn.hutool.core.collection.CollUtil;
+import com.alibaba.fastjson.JSONObject;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.gitlab4j.api.GitLabApi;
+import org.gitlab4j.api.GitLabApiException;
+import org.gitlab4j.api.models.*;
+
+import java.util.List;
+
+/**
+ * insert description here
+ *
+ * @author liuxiaohua
+ * @since 2025-06-11
+ */
+@Slf4j
+public class GitlabUtils {
+
+ private static volatile GitLabApi gitLabApi;
+
+ public static synchronized void init(ArchetypeProperties archetypeProperties) {
+ if (gitLabApi == null) {
+ synchronized (GitlabUtils.class) {
+ if (gitLabApi == null) {
+ gitLabApi = new GitLabApi(archetypeProperties.getGitlabUrl(), archetypeProperties.getAccessToken());
+ }
+ }
+ }
+ }
+
+ @SneakyThrows
+ public static void deleteProject(Long projectId) {
+ gitLabApi.getProjectApi().deleteProject(projectId);
+ }
+
+ @SneakyThrows
+ public static Project createProject(String group, String project, String desc) {
+ Group groupInfo = getGroupId(group);
+ if (groupInfo == null || groupInfo.getId() == null) {
+ throw new RuntimeException("获取分组信息失败");
+ }
+ Project gitLabRepository = createRepository(groupInfo.getId(), project, desc);
+ if (gitLabRepository == null || gitLabRepository.getId() == null) {
+ throw new RuntimeException("创建仓库失败");
+ }
+ return gitLabRepository;
+ }
+
+ @SneakyThrows
+ public static ProtectedBranch protectedBranch(Long projectId, Integer retryTimes) {
+
+ try {
+ // 获取已保护的分支
+ List protectedBranches = gitLabApi.getProtectedBranchesApi().getProtectedBranches(projectId);
+ log.info("protected branches: {}", JSONObject.toJSONString(protectedBranches));
+ // 保护分支
+ if (CollUtil.isNotEmpty(protectedBranches)
+ && protectedBranches.stream().map(ProtectedBranch::getName).anyMatch("master"::equalsIgnoreCase)) {
+ // 取消旧的配置
+ unprotectedBranch(projectId);
+ }
+ // 添加分支保护
+ return gitLabApi.getProtectedBranchesApi()
+ .protectBranch(projectId, "master", AccessLevel.NONE, AccessLevel.MAINTAINER);
+ } catch (GitLabApiException e) {
+ log.warn("protected branch failed: {}", e.getMessage());
+ }
+
+ if (retryTimes < 3) {
+ return protectedBranch(projectId, ++retryTimes);
+ }
+ log.warn("protected branch failure: {}", retryTimes);
+ throw new RuntimeException(ArchetypeUtils.WARN_MESSAGE_1);
+ }
+
+ private static void unprotectedBranch(Long projectId) {
+ try {
+ gitLabApi.getProtectedBranchesApi().unprotectBranch(projectId, "master");
+ log.info("unprotected branch master success");
+ } catch (Exception e) {
+ log.warn("unprotected branch error: {}", e.getMessage());
+ }
+ }
+
+ /**
+ * 根据分组路径获取分组ID
+ */
+ private static Group getGroupId(String groupPath) throws GitLabApiException {
+ return gitLabApi.getGroupApi().getGroup(groupPath);
+ }
+
+ /**
+ * 在 GitLab 创建新仓库
+ */
+ private static Project createRepository(Long groupId,
+ String projectName,
+ String desc) {
+ try {
+ Project project = new Project()
+ .withName(projectName)
+ .withDefaultBranch("master")
+ .withDescription(desc)
+ .withNamespaceId(groupId)
+ .withVisibility(Visibility.PRIVATE); // 私有库用 PRIVATE
+
+ return gitLabApi.getProjectApi().createProject(project); // 返回 HTTPS 仓库地址
+ } catch (Exception e) {
+ log.warn("create project error: {}", e.getMessage());
+ }
+ return null;
+ }
+
+}
+
+```
+#### Git调用工具类
+```java
+package org.jeecg.modules.devops.archetype;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * insert description here
+ *
+ * @author liuxiaohua
+ * @since 2025-06-11
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class GitUtils {
+
+ /**
+ * 初始化本地仓库并推送到 GitLab
+ */
+ public static void pushLocalCodeToGitLab(String remoteUrl,
+ String codePath,
+ ArchetypeProperties archetypeProperties) {
+ File localPath = new File(codePath);
+ CredentialsProvider credentials = new UsernamePasswordCredentialsProvider(
+ archetypeProperties.getUsername(), archetypeProperties.getAccessToken());
+
+ try (Git git = Git.init().setDirectory(localPath).call()) {
+ // 添加所有文件
+ git.add().addFilepattern(".").call();
+
+ // 提交更改
+ git.commit()
+ .setMessage(String.format("\uD83C\uDF89 [%s] 初始化提交",
+ new SimpleDateFormat("yyyy-MM-dd").format(new Date())))
+ .call();
+
+ // 添加远程仓库
+ git.remoteAdd()
+ .setName("origin")
+ .setUri(new URIish(remoteUrl))
+ .call();
+
+ // 推送到 GitLab
+ git.push()
+ .setRemote("origin")
+ .setCredentialsProvider(credentials)
+ .setForce(true)
+ .setPushAll()
+ .call();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
+
+```
\ No newline at end of file