@ -0,0 +1,3 @@ | |||
Manifest-Version: 1.0 | |||
Main-Class: com.whn.hellospring.AgileToolsApplication | |||
@ -0,0 +1,14 @@ | |||
package com.whn.hellospring.config; | |||
import lombok.Data; | |||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||
import org.springframework.context.annotation.Configuration; | |||
@Data | |||
@Configuration | |||
@ConfigurationProperties(prefix = "application.gitlab") | |||
public class GitlabConfig { | |||
private String host; | |||
private String accessToken; | |||
private String name; | |||
} |
@ -0,0 +1,136 @@ | |||
package com.whn.hellospring.controller; | |||
import com.whn.hellospring.common.StateMessage; | |||
import com.whn.hellospring.common.Status; | |||
import com.whn.hellospring.model.gitlab.GitlabFile; | |||
import com.whn.hellospring.request.UpdateGitlabFileRequest; | |||
import com.whn.hellospring.request.UpdateGitlabUserRequest; | |||
import com.whn.hellospring.service.GitlabService; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.gitlab4j.api.models.Project; | |||
import org.gitlab4j.api.models.User; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.web.bind.annotation.*; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
@RestController | |||
@RequestMapping("/gitlab") | |||
public class GitlabController { | |||
@Autowired | |||
private GitlabService gitlabService; | |||
@GetMapping(value = "/list") | |||
public Status getGitlabProjectList(@RequestHeader String token){ | |||
try{ | |||
//TODO 后续需要对token进行校验 | |||
List<Project> allProjects = gitlabService.getAllProjects(); | |||
return new Status(StateMessage.SUCCESS, allProjects); | |||
}catch (Exception e){ | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
} | |||
@GetMapping(value = "fileList") | |||
public Status getGitlabFileList(@RequestHeader String token, | |||
@RequestParam Integer projectId, | |||
@RequestParam String branchName, | |||
@RequestParam(required = false) String path){ | |||
try{ | |||
//TODO 后续需要对token进行校验 | |||
if(StringUtils.isBlank(path)){ | |||
path = ""; | |||
} | |||
List<GitlabFile> dirsAndFiles = gitlabService.getDirsAndFiles(projectId, path, branchName); | |||
return new Status(StateMessage.SUCCESS, dirsAndFiles); | |||
}catch (Exception e){ | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
} | |||
@GetMapping(value = "/allBranch") | |||
public Status getAllBranchInOneProject(@RequestHeader String token, | |||
@RequestParam int projectId){ | |||
try{ | |||
//TODO 后续需要对token进行校验 | |||
List<String> branchNames = new ArrayList<>(); | |||
gitlabService.getAllBranchInOneProject(projectId).forEach( v -> { | |||
branchNames.add(v.getName()); | |||
}); | |||
return new Status(StateMessage.SUCCESS, branchNames); | |||
}catch (Exception e){ | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
} | |||
@GetMapping(value = "/fileContent") | |||
public Status getFileContent(@RequestHeader String token, | |||
@RequestParam Integer projectId, | |||
@RequestParam String branchName, | |||
@RequestParam String filePath){ | |||
try{ | |||
//TODO 后续需要对token进行校验 | |||
String fileContent = gitlabService.getFileContent(projectId, filePath, branchName); | |||
return new Status(StateMessage.SUCCESS, fileContent); | |||
}catch (Exception e){ | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
} | |||
@PostMapping(value = "/updateFile") | |||
public Status updateFile(@RequestHeader String token, | |||
@RequestBody UpdateGitlabFileRequest request){ | |||
try{ | |||
//TODO 后续需要对token进行校验 | |||
boolean resp = gitlabService.updateOneFile(request.getProjectId(), | |||
request.getFileContent(), request.getFilePath(), | |||
request.getFileName(), request.getBranchName(), request.getCommitMessage()); | |||
if(resp){ | |||
return new Status(StateMessage.SUCCESS); | |||
}else{ | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
}catch (Exception e){ | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
} | |||
@GetMapping(value = "/userDetails") | |||
public Status getUserDetails(@RequestHeader String token, | |||
@RequestParam Integer userId){ | |||
try{ | |||
//TODO 后续需要对token进行校验 | |||
User resp = gitlabService.getUserDetails(userId); | |||
return new Status(StateMessage.SUCCESS, resp); | |||
}catch (Exception e){ | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
} | |||
@PostMapping(value = "/updateUser") | |||
public Status updateUser(@RequestHeader String token, | |||
@RequestBody UpdateGitlabUserRequest request){ | |||
try{ | |||
//TODO 后续需要对token进行校验 | |||
//检验用户名是否符合gitlab的要求 | |||
if(request.getUser() != null && request.getUser().getUsername() != null){ | |||
String reg = "^(?!.*\\.(?:git|atom)?$)[\\w.][-\\w.]*$"; | |||
if(!request.getUser().getUsername().matches(reg)){ | |||
return new Status(StateMessage.GITLAB_USERNAME_ERROR); | |||
} | |||
} | |||
boolean resp = gitlabService.updateUser(request.getUser(), request.getPassword()); | |||
if(resp){ | |||
return new Status(StateMessage.SUCCESS); | |||
}else{ | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
}catch (Exception e){ | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
} | |||
} |
@ -0,0 +1,66 @@ | |||
package com.whn.hellospring.controller; | |||
import com.whn.hellospring.common.StateMessage; | |||
import com.whn.hellospring.common.Status; | |||
import com.whn.hellospring.model.zentao.ZenTaoUserDO; | |||
import com.whn.hellospring.request.UpdateZenTaoUserRequest; | |||
import com.whn.hellospring.service.UserService; | |||
import com.whn.hellospring.service.ZenTaoService; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.transaction.annotation.Propagation; | |||
import org.springframework.transaction.annotation.Transactional; | |||
import org.springframework.web.bind.annotation.*; | |||
@RestController | |||
@RequestMapping(value = "/zentao") | |||
public class ZenTaoController { | |||
@Autowired | |||
private UserService userService; | |||
@Autowired | |||
private ZenTaoService zenTaoService; | |||
@GetMapping(value = "/userDetails") | |||
@Transactional(propagation = Propagation.REQUIRES_NEW) | |||
public Status getZenTaoUserDetails(@RequestHeader String token, | |||
@RequestParam Long userId){ | |||
try { | |||
if(!userService.checkToken(token)){ | |||
return new Status(StateMessage.TOKEN_ERROR); | |||
} | |||
ZenTaoUserDO user = zenTaoService.getUserByParentUserId(userId); | |||
Status status = new Status(StateMessage.SUCCESS, user); | |||
return status; | |||
} catch (Exception e) { | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
} | |||
@PostMapping(value = "/updateUser") | |||
@Transactional(propagation = Propagation.REQUIRES_NEW) | |||
public Status updateZenTaoUser(@RequestHeader String token, | |||
@RequestBody UpdateZenTaoUserRequest request){ | |||
try { | |||
if(!userService.checkToken(token)){ | |||
return new Status(StateMessage.TOKEN_ERROR); | |||
} | |||
if(request.getUser().getEmail() != null){ | |||
if(!request.getUser().getEmail().matches("[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z0-9]+")){ | |||
return new Status(StateMessage.EMAIL_ERROR); | |||
} | |||
} | |||
boolean req = zenTaoService.updateUserById(request.getUserId(), request.getUser()); | |||
if(req){ | |||
return new Status(StateMessage.SUCCESS); | |||
}else{ | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
} catch (Exception e) { | |||
return new Status(StateMessage.UN_KNOW_REASON); | |||
} | |||
} | |||
} |
@ -1,44 +0,0 @@ | |||
package com.whn.hellospring.handler; | |||
import org.apache.ibatis.type.BaseTypeHandler; | |||
import org.apache.ibatis.type.JdbcType; | |||
import org.joda.money.CurrencyUnit; | |||
import org.joda.money.Money; | |||
import java.sql.CallableStatement; | |||
import java.sql.PreparedStatement; | |||
import java.sql.ResultSet; | |||
import java.sql.SQLException; | |||
/** | |||
* 在Money与Long之间转换的TypeHandler,处理CNY和人民币 | |||
*/ | |||
public class MoneyTypeHandler extends BaseTypeHandler<Money> { | |||
@Override | |||
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Money money, JdbcType jdbcType) throws SQLException { | |||
preparedStatement.setLong(i,money.getAmountMinorLong()); //存储为Long类型,精确到分 | |||
} | |||
@Override | |||
public Money getNullableResult(ResultSet resultSet, String s) throws SQLException { | |||
return parseMoney(resultSet.getLong(s)); | |||
} | |||
@Override | |||
public Money getNullableResult(ResultSet resultSet, int i) throws SQLException { | |||
return parseMoney(resultSet.getLong(i)); | |||
} | |||
@Override | |||
public Money getNullableResult(CallableStatement callableStatement, int i) throws SQLException { | |||
return parseMoney(callableStatement.getLong(i)); | |||
} | |||
/** | |||
* 将Long值转换为Money | |||
*/ | |||
private Money parseMoney(Long value){ | |||
return Money.ofMinor(CurrencyUnit.of("CNY"),value); | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
package com.whn.hellospring.mapper; | |||
import com.whn.hellospring.model.zentao.ZenTaoUserDO; | |||
import org.apache.ibatis.annotations.Param; | |||
import org.springframework.stereotype.Repository; | |||
@Repository | |||
public interface ZenTaoMapper{ | |||
boolean updateUser(@Param("id") Long userId, @Param("user") ZenTaoUserDO user); | |||
} | |||
@ -1,31 +0,0 @@ | |||
package com.whn.hellospring.model; | |||
import lombok.*; | |||
import org.hibernate.annotations.Type; | |||
import org.joda.money.Money; | |||
import javax.persistence.Column; | |||
import javax.persistence.Entity; | |||
import javax.persistence.Table; | |||
import java.io.Serializable; | |||
@Entity | |||
@Table(name = "T_MENU_COFFEE") | |||
@Builder | |||
@Data | |||
@ToString(callSuper = true) | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
public class MenuCoffeeDO extends BaseEntity implements Serializable { | |||
private Long menu_id_fk; | |||
private String name; | |||
@Column | |||
@Type(type = "org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyAmount", | |||
parameters = {@org.hibernate.annotations.Parameter(name = "currencyCode", value = "CNY")}) | |||
private Money price; | |||
} | |||
@ -0,0 +1,37 @@ | |||
package com.whn.hellospring.model.gitlab; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
import java.util.Date; | |||
@Data | |||
@AllArgsConstructor | |||
@NoArgsConstructor | |||
public class GitlabFile { | |||
/** | |||
* 文件名 | |||
*/ | |||
private String name; | |||
/** | |||
* 是否是一个文件夹 | |||
*/ | |||
private boolean isDir; | |||
/** | |||
* 完整路径 | |||
*/ | |||
private String path; | |||
/** | |||
* 最后修改日期 | |||
*/ | |||
private Date updateTime; | |||
/** | |||
* 修改人 | |||
*/ | |||
private String auth; | |||
} |
@ -0,0 +1,29 @@ | |||
package com.whn.hellospring.request; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import lombok.NoArgsConstructor; | |||
import java.io.Serializable; | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
@AllArgsConstructor | |||
@NoArgsConstructor | |||
public class UpdateGitlabFileRequest implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
private Integer projectId; | |||
private String fileName; | |||
private String filePath; | |||
private String fileContent; | |||
private String branchName; | |||
private String commitMessage; | |||
} |
@ -0,0 +1,16 @@ | |||
package com.whn.hellospring.request; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import lombok.NoArgsConstructor; | |||
import org.gitlab4j.api.models.User; | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
@AllArgsConstructor | |||
@NoArgsConstructor | |||
public class UpdateGitlabUserRequest { | |||
private User user; | |||
private String password; | |||
} |
@ -0,0 +1,16 @@ | |||
package com.whn.hellospring.request; | |||
import com.whn.hellospring.model.zentao.ZenTaoUserDO; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
@Data | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
public class UpdateZenTaoUserRequest { | |||
private Long userId; | |||
private ZenTaoUserDO user; | |||
} |
@ -0,0 +1,220 @@ | |||
package com.whn.hellospring.service; | |||
import com.whn.hellospring.config.GitlabConfig; | |||
import com.whn.hellospring.model.gitlab.GitlabFile; | |||
import lombok.SneakyThrows; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.gitlab4j.api.GitLabApi; | |||
import org.gitlab4j.api.models.*; | |||
import org.springframework.stereotype.Service; | |||
import java.io.BufferedReader; | |||
import java.io.InputStream; | |||
import java.io.InputStreamReader; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
@Slf4j | |||
@Service | |||
public class GitlabService { | |||
private final GitlabConfig gitlabConfig; | |||
private final GitLabApi gitLabApi; | |||
public GitlabService(final GitlabConfig gitlabConfig) { | |||
this.gitlabConfig = gitlabConfig; | |||
// 传入gitlab服务地址和token,初始化服务 | |||
gitLabApi = new GitLabApi(gitlabConfig.getHost(), gitlabConfig.getAccessToken()); | |||
} | |||
/** | |||
* 获取所有的项目 | |||
*/ | |||
@SneakyThrows | |||
public List<Project> getAllProjects(){ | |||
List<Project> projects = gitLabApi.getProjectApi().getProjects(); | |||
return projects; | |||
} | |||
/** | |||
* 获得一个项目所有的分支 | |||
*/ | |||
@SneakyThrows | |||
public List<Branch> getAllBranchInOneProject(int projectId){ | |||
List<Branch> branches = gitLabApi.getRepositoryApi().getBranches(projectId); | |||
return branches; | |||
} | |||
/** | |||
* 获取分支下某一目录的一层文件和文件夹,不会获取文件夹内的内容 | |||
*/ | |||
@SneakyThrows | |||
public List<GitlabFile> getDirsAndFiles(final Integer projectId, final String directory, final String branch){ | |||
List<GitlabFile> fileNames = new ArrayList<>(); | |||
List<TreeItem> tree = gitLabApi.getRepositoryApi().getTree(projectId, directory, branch); | |||
for( TreeItem treeItem : tree){ | |||
// final String filePath = String.join("/",directory, treeItem.getName()); | |||
GitlabFile gitlabFile = new GitlabFile(); | |||
gitlabFile.setName(treeItem.getName()); | |||
gitlabFile.setDir(false); | |||
gitlabFile.setPath(treeItem.getPath()); | |||
// 如果当前是目录,则继续获取其下的文件列表 | |||
if (treeItem.getType().equals(TreeItem.Type.TREE)) { | |||
gitlabFile.setDir(true); | |||
} | |||
//一次性调太多接口时间太久 | |||
// List<Commit> commits = gitLabApi.getCommitsApi().getCommits(projectId, branch, treeItem.getPath()); | |||
// gitlabFile.setUpdateTime(commits.get(0).getCreatedAt()); | |||
// gitlabFile.setAuth(commits.get(0).getAuthorName()); | |||
fileNames.add(gitlabFile); | |||
// log.info("add file: {}", filePath); | |||
} | |||
return fileNames; | |||
} | |||
/** | |||
* 读取一个文件的内容 | |||
*/ | |||
@SneakyThrows | |||
public String getFileContent(int projectId, String filePath, String branchName){ | |||
final InputStream inputStream = gitLabApi.getRepositoryFileApi().getRawFile(projectId, branchName, filePath); | |||
final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); | |||
final StringBuilder stringBuilder = new StringBuilder(); | |||
while (reader.ready()) { | |||
stringBuilder.append(reader.readLine()); | |||
stringBuilder.append("\r\n"); | |||
} | |||
return stringBuilder.toString(); | |||
} | |||
/** | |||
* 查询一个项目的所有流水线触发器 | |||
*/ | |||
@SneakyThrows | |||
public List<Trigger> getPipelineTriggers(int projectId){ | |||
return gitLabApi.getPipelineApi().getPipelineTriggers(projectId); | |||
} | |||
/** | |||
* 查询一个项目上是否存在流水线触发器 | |||
*/ | |||
public boolean checkPipeLineTrigger(int projectId){ | |||
if(getPipelineTriggers(projectId).size() == 0){ | |||
return false; | |||
} | |||
return true; | |||
} | |||
/** | |||
* 在一个项目上创建流水线触发器 | |||
*/ | |||
@SneakyThrows | |||
public void createPipeLineTrigger(int projectId){ | |||
gitLabApi.getPipelineApi().createPipelineTrigger(projectId, "auto created by api"); | |||
} | |||
/** | |||
* 携带参数触发流水线 | |||
*/ | |||
@SneakyThrows | |||
public void triggerPipeline(int projectId, String token, String branchName, List<Variable> variables){ | |||
gitLabApi.getPipelineApi().triggerPipeline(projectId, token, branchName, variables); | |||
} | |||
/** | |||
* 不带参数触发流水线 | |||
*/ | |||
@SneakyThrows | |||
public void triggerPipeline(int projectId, String token, String branchName){ | |||
gitLabApi.getPipelineApi().triggerPipeline(projectId, token, branchName, new ArrayList<>()); | |||
} | |||
/** | |||
* 更新单个文件到项目仓库 | |||
* @param filePath 文件完整的路径,包括文件名 Ex:cls/gg.yml | |||
* @return | |||
*/ | |||
@SneakyThrows | |||
public boolean updateOneFile(int projectId, String fileContent, String filePath, | |||
String fileName, String branchName, String commitMessage){ | |||
RepositoryFile repositoryFile = new RepositoryFile(); | |||
repositoryFile.setContent(fileContent); | |||
repositoryFile.setFileName(fileName); | |||
repositoryFile.setFilePath(filePath); | |||
// repositoryFile.setEncoding(Constants.Encoding.TEXT); | |||
RepositoryFile repositoryFile1 = gitLabApi.getRepositoryFileApi().updateFile(projectId, repositoryFile, branchName, commitMessage); | |||
if(repositoryFile1 != null){ | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* 获取Runners | |||
*/ | |||
@SneakyThrows | |||
public List<Runner> getRunners(){ | |||
List<Runner> runners = gitLabApi.getRunnersApi().getRunners(); | |||
return runners; | |||
} | |||
/** | |||
* 注册一个Runner | |||
* xxx 注册之后仍需手动配置 | |||
*/ | |||
@SneakyThrows | |||
public boolean registerRunner(String token){ | |||
RunnerDetail register_by_api = gitLabApi.getRunnersApi().registerRunner( | |||
token, | |||
"register by api", | |||
true, new ArrayList<String>(), | |||
true, false, null); | |||
return register_by_api != null; | |||
} | |||
@SneakyThrows | |||
public User getUserDetails(Integer userId){ | |||
User user = gitLabApi.getUserApi().getUser(userId); | |||
return user; | |||
} | |||
/** | |||
* 更新用户信息 | |||
* @param user id指定更新的用户,剩余变量值都可为null | |||
* @param password 可为null | |||
*/ | |||
@SneakyThrows | |||
public boolean updateUser(User user, String password){ | |||
gitLabApi.getUserApi().updateUser(user, password); | |||
return true; | |||
} | |||
/** | |||
* 会获取分支下所有的文件,如果文件很多则消耗时间较多,尽量不使用这个方法 | |||
*/ | |||
public void getFilesInOneBranch(int projectId, String dir, String branchName){ | |||
List<String> allFiles = getAllFiles(projectId, dir, branchName); | |||
} | |||
/** | |||
* 会获取分支下所有的文件,如果文件很多则消耗时间较多,尽量不使用这个方法 | |||
*/ | |||
@SneakyThrows | |||
private List<String> getAllFiles(final Integer projectId, final String directory, final String branch) { | |||
List<String> fileNames = new ArrayList<>(); | |||
gitLabApi.getRepositoryApi().getTree(projectId, directory, branch).forEach(file -> { | |||
// 如果当前是目录,则继续获取其下的文件列表 | |||
if (file.getType().equals(TreeItem.Type.TREE)) { | |||
fileNames.addAll(getAllFiles(projectId, file.getPath(), branch)); | |||
return; | |||
} | |||
// 如果是文件类型,直接添加 | |||
final String filePath = String.join("/",directory, file.getName()); | |||
fileNames.add(filePath); | |||
log.info("add file: {}", filePath); | |||
}); | |||
return fileNames; | |||
} | |||
} |
@ -0,0 +1,27 @@ | |||
<?xml version="1.0" encoding="UTF-8" ?> | |||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" > | |||
<mapper namespace="com.whn.hellospring.mapper.ZenTaoMapper"> | |||
<update id="updateUser" > | |||
update zt_user | |||
<set> | |||
<if test="user.role!=null"> | |||
role=#{user.role}, | |||
</if> | |||
<if test="user.realname!=null"> | |||
realname=#{user.realname}, | |||
</if> | |||
<if test="user.gender!=null"> | |||
gender=#{user.gender}, | |||
</if> | |||
<if test="user.email!=null"> | |||
email=#{user.email}, | |||
</if> | |||
</set> | |||
where id=#{id} | |||
</update> | |||
</mapper> |