@ -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> |