可直接运行,修改了实体类的关系。
This commit is contained in:
parent
0537a5d8e4
commit
94aeb16a61
127
backend/README.md
Normal file
127
backend/README.md
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
`
|
||||||
|
分层结构从上到下:
|
||||||
|
`
|
||||||
|
```
|
||||||
|
Controller (表现层)
|
||||||
|
↓
|
||||||
|
Service (业务层)
|
||||||
|
↓
|
||||||
|
Repository (数据访问层)
|
||||||
|
```
|
||||||
|
`
|
||||||
|
对于外部集成:
|
||||||
|
`
|
||||||
|
```
|
||||||
|
Controller
|
||||||
|
↓
|
||||||
|
Service ←→ Integration Service (集成层)
|
||||||
|
↓
|
||||||
|
Repository
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/users - 查询所有用户
|
||||||
|
GET /api/users/list?enabled=true - 查询所有启用的用户
|
||||||
|
GET /api/users/page?pageNum=1&pageSize=10 - 分页查询用户
|
||||||
|
```
|
||||||
|
|
||||||
|
`查询日期示例`
|
||||||
|
```
|
||||||
|
UserQuery query = new UserQuery();
|
||||||
|
query.setCreateTimeRange(
|
||||||
|
LocalDateTime.now().minusDays(7), // 一周内
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
query.setEnabled(true);
|
||||||
|
query.setCreateBy("admin");
|
||||||
|
|
||||||
|
|
||||||
|
@QueryField(type = QueryType.IN)
|
||||||
|
private String status; // 可以传入 "ACTIVE,PENDING,CLOSED"
|
||||||
|
|
||||||
|
```
|
||||||
|
```
|
||||||
|
QueryDSL使用方法
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class UserServiceImpl extends BaseServiceImpl<User, Long> implements UserService {
|
||||||
|
|
||||||
|
private final UserRoleRepository userRoleRepository;
|
||||||
|
private final QUserRole qUserRole = QUserRole.userRole;
|
||||||
|
|
||||||
|
public UserServiceImpl(UserRepository userRepository, UserRoleRepository userRoleRepository) {
|
||||||
|
super(userRepository);
|
||||||
|
this.userRoleRepository = userRoleRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<UserRole> getUserRoles(Long userId) {
|
||||||
|
// 使用QueryDSL构建查询条件
|
||||||
|
Predicate predicate = qUserRole.user.id.eq(userId);
|
||||||
|
return StreamSupport.stream(
|
||||||
|
userRoleRepository.findAll(predicate).spliterator(),
|
||||||
|
false
|
||||||
|
).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assignRole(Long userId, Long roleId) {
|
||||||
|
User user = findById(userId);
|
||||||
|
Role role = roleRepository.findById(roleId)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Role not found"));
|
||||||
|
|
||||||
|
// 检查是否已存在
|
||||||
|
Predicate predicate = qUserRole.user.id.eq(userId)
|
||||||
|
.and(qUserRole.role.id.eq(roleId));
|
||||||
|
|
||||||
|
if (!userRoleRepository.exists(predicate)) {
|
||||||
|
UserRole userRole = new UserRole(user, role);
|
||||||
|
userRoleRepository.save(userRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
-- 插入正常用户数据
|
||||||
|
INSERT INTO sys_user (
|
||||||
|
username, password, nickname, email, phone,
|
||||||
|
enabled, deleted, dept_id, dept_name,
|
||||||
|
create_by, create_time, update_by, update_time, version
|
||||||
|
) VALUES
|
||||||
|
-- 管理员用户
|
||||||
|
('admin', '$2a$10$mW/yJPHjyueQ1g26YxiZNOtr6bKVF4P/w/VHLVHHhxslY.YlXhbcm', '系统管理员', 'admin@example.com', '13800138000',
|
||||||
|
true, false, 1, '技术部',
|
||||||
|
'system', '2024-01-01 09:00:00', null, null, 0),
|
||||||
|
|
||||||
|
-- 普通用户
|
||||||
|
('user01', '$2a$10$mW/yJPHjyueQ1g26YxiZNOtr6bKVF4P/w/VHLVHHhxslY.YlXhbcm', '张三', 'zhangsan@example.com', '13800138001',
|
||||||
|
true, false, 1, '技术部',
|
||||||
|
'admin', '2024-01-02 10:00:00', null, null, 0),
|
||||||
|
|
||||||
|
('user02', '$2a$10$mW/yJPHjyueQ1g26YxiZNOtr6bKVF4P/w/VHLVHHhxslY.YlXhbcm', '李四', 'lisi@example.com', '13800138002',
|
||||||
|
true, false, 2, '市场部',
|
||||||
|
'admin', '2024-01-03 11:00:00', 'admin', '2024-01-04 15:00:00', 1),
|
||||||
|
|
||||||
|
-- 已禁用的用户
|
||||||
|
('disabled_user', '$2a$10$mW/yJPHjyueQ1g26YxiZNOtr6bKVF4P/w/VHLVHHhxslY.YlXhbcm', '王五', 'wangwu@example.com', '13800138003',
|
||||||
|
false, false, 2, '市场部',
|
||||||
|
'admin', '2024-01-05 14:00:00', 'admin', '2024-01-06 16:00:00', 1),
|
||||||
|
|
||||||
|
-- 已删除的用户
|
||||||
|
('deleted_user', '$2a$10$mW/yJPHjyueQ1g26YxiZNOtr6bKVF4P/w/VHLVHHhxslY.YlXhbcm', '赵六', 'zhaoliu@example.com', '13800138004',
|
||||||
|
true, true, 3, '财务部',
|
||||||
|
'admin', '2024-01-07 10:00:00', 'admin', '2024-01-08 09:00:00', 2),
|
||||||
|
|
||||||
|
-- 最近创建的用户
|
||||||
|
('new_user01', '$2a$10$mW/yJPHjyueQ1g26YxiZNOtr6bKVF4P/w/VHLVHHhxslY.YlXhbcm', '小明', 'xiaoming@example.com', '13800138005',
|
||||||
|
true, false, 1, '技术部',
|
||||||
|
'admin', '2024-03-01 09:00:00', null, null, 0),
|
||||||
|
|
||||||
|
('new_user02', '$2a$10$mW/yJPHjyueQ1g26YxiZNOtr6bKVF4P/w/VHLVHHhxslY.YlXhbcm', '小红', 'xiaohong@example.com', '13800138006',
|
||||||
|
true, false, 2, '市场部',
|
||||||
|
'admin', '2024-03-02 10:00:00', null, null, 0),
|
||||||
|
|
||||||
|
-- 多次更新的用户
|
||||||
|
('updated_user', '$2a$10$mW/yJPHjyueQ1g26YxiZNOtr6bKVF4P/w/VHLVHHhxslY.YlXhbcm', '小张', 'xiaozhang@example.com', '13800138007',
|
||||||
|
true, false, 3, '财务部',
|
||||||
|
'admin', '2024-02-01 09:00:00', 'admin', '2024-03-01 15:00:00', 5);
|
||||||
|
```
|
||||||
@ -18,12 +18,26 @@
|
|||||||
<java.version>21</java.version>
|
<java.version>21</java.version>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
|
<spring-cloud.version>2023.0.0</spring-cloud.version>
|
||||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||||
<querydsl.version>5.0.0</querydsl.version>
|
<querydsl.version>5.0.0</querydsl.version>
|
||||||
<lombok.version>1.18.30</lombok.version>
|
<lombok.version>1.18.30</lombok.version>
|
||||||
<spring-security.version>6.2.0</spring-security.version>
|
<spring-security.version>6.2.0</spring-security.version>
|
||||||
|
<jjwt.version>0.12.3</jjwt.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-dependencies</artifactId>
|
||||||
|
<version>${spring-cloud.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- Spring Boot -->
|
<!-- Spring Boot -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -101,6 +115,31 @@
|
|||||||
<artifactId>spring-security-test</artifactId>
|
<artifactId>spring-security-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- OpenFeign -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JWT -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-api</artifactId>
|
||||||
|
<version>${jjwt.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-impl</artifactId>
|
||||||
|
<version>${jjwt.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-jackson</artifactId>
|
||||||
|
<version>${jjwt.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
`查询日期示例`
|
|
||||||
```
|
|
||||||
UserQuery query = new UserQuery();
|
|
||||||
query.setCreateTimeRange(
|
|
||||||
LocalDateTime.now().minusDays(7), // 一周内
|
|
||||||
LocalDateTime.now()
|
|
||||||
);
|
|
||||||
query.setEnabled(true);
|
|
||||||
query.setCreateBy("admin");
|
|
||||||
|
|
||||||
|
|
||||||
@QueryField(type = QueryType.IN)
|
|
||||||
private String status; // 可以传入 "ACTIVE,PENDING,CLOSED"
|
|
||||||
|
|
||||||
```
|
|
||||||
@ -2,9 +2,11 @@ package com.qqchen.deploy.backend;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableFeignClients
|
||||||
public class BackendApplication {
|
public class BackendApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import com.qqchen.deploy.backend.dto.query.UserQuery;
|
|||||||
import com.qqchen.deploy.backend.dto.request.UserRequest;
|
import com.qqchen.deploy.backend.dto.request.UserRequest;
|
||||||
import com.qqchen.deploy.backend.dto.response.UserResponse;
|
import com.qqchen.deploy.backend.dto.response.UserResponse;
|
||||||
import com.qqchen.deploy.backend.dto.request.UserRegisterRequest;
|
import com.qqchen.deploy.backend.dto.request.UserRegisterRequest;
|
||||||
import com.qqchen.deploy.backend.service.UserService;
|
import com.qqchen.deploy.backend.service.IUserService;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@ -22,11 +22,11 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RequestMapping("/api/v1/users")
|
@RequestMapping("/api/v1/users")
|
||||||
public class UserApiController extends BaseController<User, Long, UserQuery, UserRequest, UserResponse> {
|
public class UserApiController extends BaseController<User, Long, UserQuery, UserRequest, UserResponse> {
|
||||||
|
|
||||||
protected final UserService userService;
|
protected final IUserService userService;
|
||||||
|
|
||||||
public UserApiController(UserService userService, UserConverter converter) {
|
public UserApiController(IUserService userService, UserConverter converter) {
|
||||||
|
super(userService, converter);
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.converter = converter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
|
|||||||
@ -10,9 +10,8 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableJpaAuditing
|
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
|
||||||
public class JpaConfig {
|
public class JpaAuditingConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public AuditorAware<String> auditorProvider() {
|
public AuditorAware<String> auditorProvider() {
|
||||||
return () -> {
|
return () -> {
|
||||||
@ -2,11 +2,11 @@ package com.qqchen.deploy.backend.common.controller;
|
|||||||
|
|
||||||
import com.qqchen.deploy.backend.common.domain.Entity;
|
import com.qqchen.deploy.backend.common.domain.Entity;
|
||||||
import com.qqchen.deploy.backend.common.converter.BaseConverter;
|
import com.qqchen.deploy.backend.common.converter.BaseConverter;
|
||||||
|
import com.qqchen.deploy.backend.common.dto.BaseResponse;
|
||||||
import com.qqchen.deploy.backend.common.query.BaseQuery;
|
import com.qqchen.deploy.backend.common.query.BaseQuery;
|
||||||
import com.qqchen.deploy.backend.common.dto.BaseRequest;
|
import com.qqchen.deploy.backend.common.dto.BaseRequest;
|
||||||
import com.qqchen.deploy.backend.common.api.Response;
|
import com.qqchen.deploy.backend.common.api.Response;
|
||||||
import com.qqchen.deploy.backend.common.service.BaseService;
|
import com.qqchen.deploy.backend.common.service.IBaseService;
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@ -16,23 +16,25 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* 通用REST控制器
|
* 通用REST控制器
|
||||||
*/
|
*/
|
||||||
public abstract class BaseController<T extends Entity<ID>, ID extends Serializable, Q extends BaseQuery, R extends BaseRequest, V> {
|
public abstract class BaseController<T extends Entity<ID>, ID extends Serializable, Q extends BaseQuery, REQ extends BaseRequest, RESP extends BaseResponse> {
|
||||||
|
|
||||||
@Resource
|
protected final IBaseService<T, ID> service;
|
||||||
protected BaseService<T, ID> service;
|
protected final BaseConverter<T, REQ, RESP> converter;
|
||||||
|
|
||||||
@Resource
|
protected BaseController(IBaseService<T, ID> service, BaseConverter<T, REQ, RESP> converter) {
|
||||||
protected BaseConverter<T, R, V> converter;
|
this.service = service;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public Response<V> create(@RequestBody R request) {
|
public Response<RESP> create(@RequestBody REQ request) {
|
||||||
T entity = converter.toEntity(request);
|
T entity = converter.toEntity(request);
|
||||||
T savedEntity = service.create(entity);
|
T savedEntity = service.create(entity);
|
||||||
return Response.success(converter.toResponse(savedEntity));
|
return Response.success(converter.toResponse(savedEntity));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
public Response<V> update(@PathVariable ID id, @RequestBody R request) {
|
public Response<RESP> update(@PathVariable ID id, @RequestBody REQ request) {
|
||||||
T entity = service.findById(id);
|
T entity = service.findById(id);
|
||||||
converter.updateEntity(entity, request);
|
converter.updateEntity(entity, request);
|
||||||
T updatedEntity = service.update(entity);
|
T updatedEntity = service.update(entity);
|
||||||
@ -46,20 +48,26 @@ public abstract class BaseController<T extends Entity<ID>, ID extends Serializab
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public Response<V> findById(@PathVariable ID id) {
|
public Response<RESP> findById(@PathVariable ID id) {
|
||||||
T entity = service.findById(id);
|
T entity = service.findById(id);
|
||||||
return Response.success(converter.toResponse(entity));
|
return Response.success(converter.toResponse(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public Response<List<V>> findAll() {
|
public Response<List<RESP>> findAll() {
|
||||||
List<T> entities = service.findAll();
|
List<T> entities = service.findAll();
|
||||||
return Response.success(converter.toResponseList(entities));
|
return Response.success(converter.toResponseList(entities));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
public Response<Page<V>> page(Q query) {
|
public Response<Page<RESP>> page(Q query) {
|
||||||
Page<T> page = service.page(query);
|
Page<T> page = service.page(query);
|
||||||
return Response.success(page.map(entity -> converter.toResponse(entity)));
|
return Response.success(page.map(entity -> converter.toResponse(entity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/list")
|
||||||
|
public Response<List<RESP>> findAll(Q query) {
|
||||||
|
List<T> entities = service.findAll(query);
|
||||||
|
return Response.success(converter.toResponseList(entities));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.qqchen.deploy.backend.common.integration.dto;
|
||||||
|
|
||||||
|
public interface ThirdPartyDTO {
|
||||||
|
// 标记接口,表明这是第三方系统的数据结构
|
||||||
|
}
|
||||||
@ -1,34 +0,0 @@
|
|||||||
package com.qqchen.deploy.backend.common.repository;
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.domain.Entity;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.data.jpa.repository.Query;
|
|
||||||
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
|
||||||
import org.springframework.data.repository.NoRepositoryBean;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@NoRepositoryBean
|
|
||||||
public interface BaseRepository<T extends Entity<ID>, ID extends Serializable> extends JpaRepository<T, ID>, QuerydslPredicateExecutor<T> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Query("select e from #{#entityName} e where e.deleted = false")
|
|
||||||
List<T> findAll();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Query("select e from #{#entityName} e where e.id = ?1 and e.deleted = false")
|
|
||||||
Optional<T> findById(ID id);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void delete(T entity) {
|
|
||||||
entity.setDeleted(true);
|
|
||||||
save(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void deleteById(ID id) {
|
|
||||||
findById(id).ifPresent(this::delete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,129 @@
|
|||||||
|
package com.qqchen.deploy.backend.common.repository;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.domain.Entity;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||||
|
import org.springframework.data.repository.NoRepositoryBean;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@NoRepositoryBean
|
||||||
|
public interface IBaseRepository<T extends Entity<ID>, ID extends Serializable>
|
||||||
|
extends JpaRepository<T, ID>, QuerydslPredicateExecutor<T> {
|
||||||
|
// extends JpaRepository<T, ID>, QuerydslPredicateExecutor<T>, JpaSpecificationExecutor<T> {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Query("select e from #{#entityName} e where e.deleted = false")
|
||||||
|
List<T> findAll();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Query("select e from #{#entityName} e where e.id = ?1 and e.deleted = false")
|
||||||
|
Optional<T> findById(ID id);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void delete(T entity) {
|
||||||
|
entity.setDeleted(true);
|
||||||
|
save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void deleteById(ID id) {
|
||||||
|
findById(id).ifPresent(this::delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量操作
|
||||||
|
@Modifying
|
||||||
|
@Query("update #{#entityName} e set e.deleted = true where e.id in ?1")
|
||||||
|
void logicDeleteByIds(Collection<ID> ids);
|
||||||
|
|
||||||
|
// @Modifying
|
||||||
|
// @Query("update #{#entityName} e set e.enabled = ?2 where e.id in ?1")
|
||||||
|
// void updateEnabledByIds(Collection<ID> ids, boolean enabled);
|
||||||
|
|
||||||
|
// 快速查询方法
|
||||||
|
Optional<T> findByIdAndDeletedFalse(ID id);
|
||||||
|
|
||||||
|
List<T> findByIdInAndDeletedFalse(Collection<ID> ids);
|
||||||
|
|
||||||
|
boolean existsByIdAndDeletedFalse(ID id);
|
||||||
|
|
||||||
|
// 自定义查询
|
||||||
|
@Query("select e from #{#entityName} e where e.deleted = false and " +
|
||||||
|
"(?1 is null or e.createTime >= ?1) and " +
|
||||||
|
"(?2 is null or e.createTime <= ?2)")
|
||||||
|
List<T> findByCreateTimeBetween(LocalDateTime start, LocalDateTime end);
|
||||||
|
|
||||||
|
@Query("select e from #{#entityName} e where e.deleted = false and " +
|
||||||
|
"(?1 is null or e.createBy = ?1)")
|
||||||
|
List<T> findByCreateBy(String creator);
|
||||||
|
|
||||||
|
// 统计方法
|
||||||
|
@Query("select count(e) from #{#entityName} e where e.deleted = false")
|
||||||
|
long countNonDeleted();
|
||||||
|
|
||||||
|
// @Query("select count(e) from #{#entityName} e where e.deleted = false and e.enabled = ?1")
|
||||||
|
// long countByEnabled(boolean enabled);
|
||||||
|
|
||||||
|
// 高级查询
|
||||||
|
@Query("select e from #{#entityName} e where e.deleted = false " +
|
||||||
|
"order by e.createTime desc")
|
||||||
|
List<T> findLatest(Pageable pageable);
|
||||||
|
|
||||||
|
@Query("select distinct e.createBy from #{#entityName} e where e.deleted = false")
|
||||||
|
List<String> findAllCreators();
|
||||||
|
|
||||||
|
// 批量更新
|
||||||
|
@Modifying
|
||||||
|
@Query("update #{#entityName} e set e.updateBy = ?2, e.updateTime = ?3 where e.id in ?1")
|
||||||
|
void updateAuditInfo(Collection<ID> ids, String updateBy, LocalDateTime updateTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据条件查询并排序
|
||||||
|
*/
|
||||||
|
default List<T> findAllByCondition(com.querydsl.core.types.Predicate predicate, Sort sort) {
|
||||||
|
Iterable<T> iterable = findAll(predicate, sort);
|
||||||
|
List<T> result = new ArrayList<>();
|
||||||
|
iterable.forEach(result::add);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量保存并返回保存的实体
|
||||||
|
default List<T> saveAllAndReturn(Iterable<T> entities) {
|
||||||
|
return saveAll(entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据条件查询并排序
|
||||||
|
* 使用QueryDSL的Predicate进行条件查询
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
Iterable<T> findAll(com.querydsl.core.types.Predicate predicate);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Iterable<T> findAll(com.querydsl.core.types.Predicate predicate, Sort sort);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供一个默认的转换方法
|
||||||
|
*/
|
||||||
|
default List<T> findAllAndConvert(com.querydsl.core.types.Predicate predicate, Sort sort) {
|
||||||
|
Iterable<T> iterable = findAll(predicate, sort);
|
||||||
|
List<T> result = new ArrayList<>();
|
||||||
|
iterable.forEach(result::add);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,17 @@
|
|||||||
package com.qqchen.deploy.backend.common.config;
|
package com.qqchen.deploy.backend.common.security.config;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
@ -69,4 +73,14 @@ public class SecurityConfig {
|
|||||||
source.registerCorsConfiguration("/**", configuration);
|
source.registerCorsConfiguration("/**", configuration);
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
|
||||||
|
return config.getAuthenticationManager();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
package com.qqchen.deploy.backend.common.security.filter;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.security.util.JwtTokenUtil;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final JwtTokenUtil jwtTokenUtil;
|
||||||
|
|
||||||
|
private final UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
String authHeader = request.getHeader("Authorization");
|
||||||
|
String token = null;
|
||||||
|
String username = null;
|
||||||
|
|
||||||
|
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
||||||
|
token = authHeader.substring(7);
|
||||||
|
username = jwtTokenUtil.getUsernameFromToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
|
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||||
|
|
||||||
|
if (jwtTokenUtil.validateToken(token, userDetails)) {
|
||||||
|
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
||||||
|
userDetails, null, userDetails.getAuthorities());
|
||||||
|
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Cannot set user authentication", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
package com.qqchen.deploy.backend.common.security.util;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class JwtTokenUtil {
|
||||||
|
|
||||||
|
@Value("${jwt.secret}")
|
||||||
|
private String secret;
|
||||||
|
|
||||||
|
@Value("${jwt.expiration}")
|
||||||
|
private Long expiration;
|
||||||
|
|
||||||
|
private SecretKey key;
|
||||||
|
|
||||||
|
private SecretKey getKey() {
|
||||||
|
if (key == null) {
|
||||||
|
// 确保密钥长度至少为 256 位
|
||||||
|
byte[] keyBytes = new byte[32];
|
||||||
|
byte[] secretBytes = secret.getBytes(StandardCharsets.UTF_8);
|
||||||
|
System.arraycopy(secretBytes, 0, keyBytes, 0, Math.min(secretBytes.length, keyBytes.length));
|
||||||
|
key = Keys.hmacShaKeyFor(keyBytes);
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateToken(UserDetails userDetails) {
|
||||||
|
Date now = new Date();
|
||||||
|
Date expiryDate = new Date(now.getTime() + expiration * 1000);
|
||||||
|
|
||||||
|
return Jwts.builder()
|
||||||
|
.subject(userDetails.getUsername())
|
||||||
|
.issuedAt(now)
|
||||||
|
.expiration(expiryDate)
|
||||||
|
.signWith(getKey())
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsernameFromToken(String token) {
|
||||||
|
try {
|
||||||
|
Claims claims = Jwts.parser()
|
||||||
|
.verifyWith(getKey())
|
||||||
|
.build()
|
||||||
|
.parseSignedClaims(token)
|
||||||
|
.getPayload();
|
||||||
|
return claims.getSubject();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error getting username from token", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validateToken(String token, UserDetails userDetails) {
|
||||||
|
try {
|
||||||
|
String username = getUsernameFromToken(token);
|
||||||
|
Claims claims = Jwts.parser()
|
||||||
|
.verifyWith(getKey())
|
||||||
|
.build()
|
||||||
|
.parseSignedClaims(token)
|
||||||
|
.getPayload();
|
||||||
|
|
||||||
|
boolean isTokenExpired = claims.getExpiration().before(new Date());
|
||||||
|
|
||||||
|
return (username.equals(userDetails.getUsername()) && !isTokenExpired);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error validating token", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* 通用服务接口
|
* 通用服务接口
|
||||||
*/
|
*/
|
||||||
public interface BaseService<T extends Entity<ID>, ID extends Serializable> {
|
public interface IBaseService<T extends Entity<ID>, ID extends Serializable> {
|
||||||
T create(T entity);
|
T create(T entity);
|
||||||
|
|
||||||
T update(T entity);
|
T update(T entity);
|
||||||
@ -21,5 +21,7 @@ public interface BaseService<T extends Entity<ID>, ID extends Serializable> {
|
|||||||
|
|
||||||
List<T> findAll();
|
List<T> findAll();
|
||||||
|
|
||||||
|
List<T> findAll(BaseQuery query);
|
||||||
|
|
||||||
Page<T> page(BaseQuery query);
|
Page<T> page(BaseQuery query);
|
||||||
}
|
}
|
||||||
@ -2,6 +2,7 @@ package com.qqchen.deploy.backend.common.service.impl;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -11,9 +12,9 @@ import com.qqchen.deploy.backend.common.enums.QueryType;
|
|||||||
import com.qqchen.deploy.backend.common.query.BaseQuery;
|
import com.qqchen.deploy.backend.common.query.BaseQuery;
|
||||||
import com.qqchen.deploy.backend.common.query.DateRange;
|
import com.qqchen.deploy.backend.common.query.DateRange;
|
||||||
import com.qqchen.deploy.backend.common.query.Range;
|
import com.qqchen.deploy.backend.common.query.Range;
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.common.annotation.QueryField;
|
import com.qqchen.deploy.backend.common.annotation.QueryField;
|
||||||
import com.qqchen.deploy.backend.common.service.BaseService;
|
import com.qqchen.deploy.backend.common.service.IBaseService;
|
||||||
import com.qqchen.deploy.backend.common.utils.EntityPathResolver;
|
import com.qqchen.deploy.backend.common.utils.EntityPathResolver;
|
||||||
import com.querydsl.core.BooleanBuilder;
|
import com.querydsl.core.BooleanBuilder;
|
||||||
import com.querydsl.core.types.EntityPath;
|
import com.querydsl.core.types.EntityPath;
|
||||||
@ -29,17 +30,17 @@ import org.springframework.util.StringUtils;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Date;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public abstract class BaseServiceImpl<T extends Entity<ID>, ID extends Serializable>
|
public abstract class BaseServiceImpl<T extends Entity<ID>, ID extends Serializable> implements IBaseService<T, ID> {
|
||||||
implements BaseService<T, ID> {
|
|
||||||
|
|
||||||
protected final BaseRepository<T, ID> repository;
|
protected final IBaseRepository<T, ID> repository;
|
||||||
|
|
||||||
protected final EntityPath<T> entityPath;
|
protected final EntityPath<T> entityPath;
|
||||||
|
|
||||||
protected BaseServiceImpl(BaseRepository<T, ID> repository) {
|
protected BaseServiceImpl(IBaseRepository<T, ID> repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.entityPath = getEntityPath();
|
this.entityPath = getEntityPath();
|
||||||
}
|
}
|
||||||
@ -65,7 +66,6 @@ public abstract class BaseServiceImpl<T extends Entity<ID>, ID extends Serializa
|
|||||||
@Override
|
@Override
|
||||||
public Page<T> page(BaseQuery query) {
|
public Page<T> page(BaseQuery query) {
|
||||||
BooleanBuilder builder = new BooleanBuilder().and(Expressions.asBoolean(true).isTrue());
|
BooleanBuilder builder = new BooleanBuilder().and(Expressions.asBoolean(true).isTrue());
|
||||||
|
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
buildQueryPredicate(query, builder);
|
buildQueryPredicate(query, builder);
|
||||||
}
|
}
|
||||||
@ -74,7 +74,6 @@ public abstract class BaseServiceImpl<T extends Entity<ID>, ID extends Serializa
|
|||||||
if (genericTypes != null && genericTypes.length > 0) {
|
if (genericTypes != null && genericTypes.length > 0) {
|
||||||
Class<T> entityClass = (Class<T>) genericTypes[0];
|
Class<T> entityClass = (Class<T>) genericTypes[0];
|
||||||
LogicDelete softDelete = entityClass.getAnnotation(LogicDelete.class);
|
LogicDelete softDelete = entityClass.getAnnotation(LogicDelete.class);
|
||||||
|
|
||||||
if (softDelete != null && softDelete.value()) {
|
if (softDelete != null && softDelete.value()) {
|
||||||
Path<?> deletedPath = EntityPathResolver.getPath(entityPath, "deleted");
|
Path<?> deletedPath = EntityPathResolver.getPath(entityPath, "deleted");
|
||||||
if (deletedPath instanceof BooleanPath) {
|
if (deletedPath instanceof BooleanPath) {
|
||||||
@ -83,7 +82,6 @@ public abstract class BaseServiceImpl<T extends Entity<ID>, ID extends Serializa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return repository.findAll(builder, createPageRequest(query));
|
return repository.findAll(builder, createPageRequest(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,4 +282,40 @@ public abstract class BaseServiceImpl<T extends Entity<ID>, ID extends Serializa
|
|||||||
public List<T> findAll() {
|
public List<T> findAll() {
|
||||||
return repository.findAll();
|
return repository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<T> findAll(BaseQuery query) {
|
||||||
|
BooleanBuilder builder = new BooleanBuilder().and(Expressions.asBoolean(true).isTrue());
|
||||||
|
|
||||||
|
// 添加查询条件
|
||||||
|
if (query != null) {
|
||||||
|
buildQueryPredicate(query, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理软删除
|
||||||
|
if (query == null || query.getDeleted() == null) {
|
||||||
|
Class<?>[] genericTypes = GenericTypeResolver.resolveTypeArguments(getClass(), BaseServiceImpl.class);
|
||||||
|
if (genericTypes != null && genericTypes.length > 0) {
|
||||||
|
Class<T> entityClass = (Class<T>) genericTypes[0];
|
||||||
|
LogicDelete softDelete = entityClass.getAnnotation(LogicDelete.class);
|
||||||
|
if (softDelete != null && softDelete.value()) {
|
||||||
|
Path<?> deletedPath = EntityPathResolver.getPath(entityPath, "deleted");
|
||||||
|
if (deletedPath instanceof BooleanPath) {
|
||||||
|
builder.and(((BooleanPath) deletedPath).eq(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取排序信息
|
||||||
|
Sort sort = query != null && StringUtils.hasText(query.getSortField()) ?
|
||||||
|
Sort.by(Sort.Direction.fromString(query.getSortOrder()), query.getSortField()) :
|
||||||
|
Sort.by(Sort.Direction.DESC, "createTime");
|
||||||
|
|
||||||
|
// 执行查询并返回结果
|
||||||
|
Iterable<T> iterable = repository.findAll(builder, sort);
|
||||||
|
List<T> result = new ArrayList<>();
|
||||||
|
iterable.forEach(result::add);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.controller;
|
|||||||
import com.qqchen.deploy.backend.api.UserApiController;
|
import com.qqchen.deploy.backend.api.UserApiController;
|
||||||
import com.qqchen.deploy.backend.common.api.Response;
|
import com.qqchen.deploy.backend.common.api.Response;
|
||||||
import com.qqchen.deploy.backend.converter.UserConverter;
|
import com.qqchen.deploy.backend.converter.UserConverter;
|
||||||
import com.qqchen.deploy.backend.service.UserService;
|
import com.qqchen.deploy.backend.service.IUserService;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -13,8 +13,11 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RequestMapping("/mgmt/users")
|
@RequestMapping("/mgmt/users")
|
||||||
public class UserController extends UserApiController {
|
public class UserController extends UserApiController {
|
||||||
|
|
||||||
public UserController(UserService userService, UserConverter converter) {
|
protected final IUserService userService;
|
||||||
|
|
||||||
|
public UserController(IUserService userService, UserConverter converter) {
|
||||||
super(userService, converter);
|
super(userService, converter);
|
||||||
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/check-username")
|
@GetMapping("/check-username")
|
||||||
|
|||||||
@ -1,16 +1,25 @@
|
|||||||
package com.qqchen.deploy.backend.entity;
|
package com.qqchen.deploy.backend.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.qqchen.deploy.backend.common.annotation.LogicDelete;
|
import com.qqchen.deploy.backend.common.annotation.LogicDelete;
|
||||||
import com.qqchen.deploy.backend.common.domain.Entity;
|
import com.qqchen.deploy.backend.common.domain.Entity;
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import jakarta.persistence.Transient;
|
import jakarta.persistence.Transient;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@ -46,4 +55,10 @@ public class Department extends Entity<Long> {
|
|||||||
|
|
||||||
@Transient // 不映射到数据库
|
@Transient // 不映射到数据库
|
||||||
private List<Department> children = new ArrayList<>();
|
private List<Department> children = new ArrayList<>();
|
||||||
|
|
||||||
|
// 修改关联关系,通过UserDepartment关联
|
||||||
|
@OneToMany(mappedBy = "department")
|
||||||
|
@JsonIgnore
|
||||||
|
@ToString.Exclude
|
||||||
|
private Set<UserDepartment> userDepartments = new HashSet<>();
|
||||||
}
|
}
|
||||||
@ -7,6 +7,7 @@ import jakarta.persistence.Table;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@ -40,4 +41,6 @@ public class Menu extends Entity<Long> {
|
|||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Boolean enabled = true;
|
private Boolean enabled = true;
|
||||||
|
|
||||||
|
// List<RoleMenu> roleMenu;
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.entity;
|
package com.qqchen.deploy.backend.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.qqchen.deploy.backend.common.annotation.LogicDelete;
|
import com.qqchen.deploy.backend.common.annotation.LogicDelete;
|
||||||
import com.qqchen.deploy.backend.common.domain.Entity;
|
import com.qqchen.deploy.backend.common.domain.Entity;
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
@ -9,8 +10,11 @@ import jakarta.persistence.Table;
|
|||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@ -33,6 +37,8 @@ public class Role extends Entity<Long> {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Integer sort = 0;
|
private Integer sort = 0;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "role", cascade = CascadeType.ALL) // 指向 UserRole 的 role 属性
|
@OneToMany(mappedBy = "role", cascade = CascadeType.ALL)
|
||||||
private List<UserRole> userRoles;
|
@JsonIgnore
|
||||||
|
@ToString.Exclude
|
||||||
|
private Set<UserRole> userRoles = new HashSet<>();
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.entity;
|
package com.qqchen.deploy.backend.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.qqchen.deploy.backend.common.annotation.LogicDelete;
|
import com.qqchen.deploy.backend.common.annotation.LogicDelete;
|
||||||
import com.qqchen.deploy.backend.common.domain.AggregateRoot;
|
import com.qqchen.deploy.backend.common.domain.AggregateRoot;
|
||||||
import com.qqchen.deploy.backend.common.domain.Entity;
|
import com.qqchen.deploy.backend.common.domain.Entity;
|
||||||
@ -7,9 +8,11 @@ import com.qqchen.deploy.backend.event.UserRoleChangedEvent;
|
|||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.OneToOne;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -23,32 +26,35 @@ import java.util.stream.Collectors;
|
|||||||
@Table(name = "sys_user")
|
@Table(name = "sys_user")
|
||||||
@LogicDelete
|
@LogicDelete
|
||||||
public class User extends Entity<Long> {
|
public class User extends Entity<Long> {
|
||||||
|
|
||||||
@Column(unique = true, nullable = false)
|
@Column(unique = true, nullable = false)
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
@Column(length = 50)
|
@Column(length = 50)
|
||||||
private String nickname;
|
private String nickname;
|
||||||
|
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
private String phone;
|
private String phone;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Boolean enabled = true;
|
private Boolean enabled = true;
|
||||||
|
|
||||||
@Column(name = "dept_id")
|
@Column(name = "dept_id")
|
||||||
private Long deptId;
|
private Long deptId;
|
||||||
|
|
||||||
@Column(name = "dept_name")
|
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
private String deptName;
|
@JsonIgnore
|
||||||
|
@ToString.Exclude
|
||||||
|
private Set<UserRole> userRoles = new HashSet<>();
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL) // 指向 UserRole 的 user 属性
|
@JsonIgnore
|
||||||
private Set<UserRole> userRoles;
|
@ToString.Exclude
|
||||||
|
private Set<UserDepartment> userDepartments = new HashSet<>();
|
||||||
|
|
||||||
// public void addRole(Role role) {
|
// public void addRole(Role role) {
|
||||||
// UserRole userRole = new UserRole(this, role);
|
// UserRole userRole = new UserRole(this, role);
|
||||||
|
|||||||
@ -0,0 +1,48 @@
|
|||||||
|
package com.qqchen.deploy.backend.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.qqchen.deploy.backend.common.annotation.LogicDelete;
|
||||||
|
import com.qqchen.deploy.backend.common.domain.Entity;
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.OneToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@jakarta.persistence.Entity
|
||||||
|
@Table(name = "sys_user_department")
|
||||||
|
@LogicDelete
|
||||||
|
public class UserDepartment extends Entity<Long> {
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "user_id", unique = true)
|
||||||
|
@JsonIgnore
|
||||||
|
@ToString.Exclude
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "department_id")
|
||||||
|
@JsonIgnore
|
||||||
|
@ToString.Exclude
|
||||||
|
private Department department;
|
||||||
|
|
||||||
|
protected UserDepartment() {
|
||||||
|
// JPA需要无参构造函数
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDepartment(User user, Department department) {
|
||||||
|
this.user = user;
|
||||||
|
this.department = department;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.entity;
|
package com.qqchen.deploy.backend.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.qqchen.deploy.backend.common.annotation.LogicDelete;
|
import com.qqchen.deploy.backend.common.annotation.LogicDelete;
|
||||||
import com.qqchen.deploy.backend.common.domain.Entity;
|
import com.qqchen.deploy.backend.common.domain.Entity;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
@ -11,6 +12,7 @@ import jakarta.persistence.OneToOne;
|
|||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@ -19,12 +21,16 @@ import lombok.EqualsAndHashCode;
|
|||||||
@LogicDelete
|
@LogicDelete
|
||||||
public class UserRole extends Entity<Long> {
|
public class UserRole extends Entity<Long> {
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY) // 添加懒加载
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "user_id")
|
@JoinColumn(name = "user_id")
|
||||||
|
@JsonIgnore
|
||||||
|
@ToString.Exclude
|
||||||
private User user;
|
private User user;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY) // 添加懒加载
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "role_id")
|
@JoinColumn(name = "role_id")
|
||||||
|
@JsonIgnore
|
||||||
|
@ToString.Exclude
|
||||||
private Role role;
|
private Role role;
|
||||||
|
|
||||||
// 添加构造方法
|
// 添加构造方法
|
||||||
@ -50,4 +56,6 @@ public class UserRole extends Entity<Long> {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return getClass().hashCode();
|
return getClass().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
package com.qqchen.deploy.backend.integration;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.integration.client.IJenkinsClient;
|
||||||
|
import com.qqchen.deploy.backend.integration.dto.JenkinsBuildDTO;
|
||||||
|
import com.qqchen.deploy.backend.integration.dto.JenkinsConnectionDTO;
|
||||||
|
import feign.FeignException;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class JenkinsService {
|
||||||
|
|
||||||
|
// @Resource
|
||||||
|
// private final IJenkinsClient jenkinsClient;
|
||||||
|
//
|
||||||
|
// public boolean testConnection(JenkinsConnectionDTO config) {
|
||||||
|
// try {
|
||||||
|
// String authHeader = createAuthHeader(config.getUsername(), config.getPassword());
|
||||||
|
// jenkinsClient.testConnection(authHeader);
|
||||||
|
// log.info("Jenkins连接测试成功");
|
||||||
|
// return true;
|
||||||
|
// } catch (FeignException.Unauthorized e) {
|
||||||
|
// log.error("Jenkins连接测试失败: 认证失败");
|
||||||
|
// return false;
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("Jenkins连接测试失败: {}", e.getMessage());
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public BuildInfo getBuildInfo(JenkinsConnectionDTO config, String jobName, Integer buildNumber) {
|
||||||
|
// try {
|
||||||
|
// String authHeader = createAuthHeader(config.getUsername(), config.getPassword());
|
||||||
|
// JenkinsBuildDTO jenkinsBuild = jenkinsClient.getBuildInfo(authHeader, jobName, buildNumber);
|
||||||
|
// return convertToBuildInfo(jenkinsBuild);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("获取Jenkins构建信息失败: {}", e.getMessage());
|
||||||
|
// throw new RuntimeException("Failed to get build info", e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private String createAuthHeader(String username, String password) {
|
||||||
|
// String auth = username + ":" + password;
|
||||||
|
// return "Basic " + Base64.getEncoder().encodeToString(auth.getBytes());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private BuildInfo convertToBuildInfo(JenkinsBuildDTO dto) {
|
||||||
|
// BuildInfo buildInfo = new BuildInfo();
|
||||||
|
// buildInfo.setJobName(dto.getJobName());
|
||||||
|
// buildInfo.setBuildNumber(dto.getBuildNumber());
|
||||||
|
// buildInfo.setBuildUrl(dto.getUrl());
|
||||||
|
// buildInfo.setBuildTime(LocalDateTime.ofInstant(
|
||||||
|
// Instant.ofEpochMilli(dto.getTimestamp()),
|
||||||
|
// ZoneId.systemDefault()
|
||||||
|
// ));
|
||||||
|
//
|
||||||
|
// // 转换构建状态
|
||||||
|
// buildInfo.setStatus(convertBuildStatus(dto.getResult(), dto.getStatus()));
|
||||||
|
//
|
||||||
|
// return buildInfo;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private BuildInfo.BuildStatus convertBuildStatus(String result, String status) {
|
||||||
|
// if ("IN_PROGRESS".equals(status)) {
|
||||||
|
// return BuildInfo.BuildStatus.IN_PROGRESS;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return switch (result) {
|
||||||
|
// case "SUCCESS" -> BuildInfo.BuildStatus.SUCCESS;
|
||||||
|
// case "FAILURE" -> BuildInfo.BuildStatus.FAILURE;
|
||||||
|
// case "ABORTED" -> BuildInfo.BuildStatus.CANCELLED;
|
||||||
|
// default -> throw new IllegalStateException("Unknown build status: " + result);
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.qqchen.deploy.backend.integration.client;
|
||||||
|
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@FeignClient(name = "jenkins-service", url = "#{null}") // 动态URL
|
||||||
|
public interface IJenkinsClient {
|
||||||
|
|
||||||
|
@GetMapping("/api/json")
|
||||||
|
Map<String, Object> testConnection(
|
||||||
|
@RequestHeader("Authorization") String authorization
|
||||||
|
);
|
||||||
|
|
||||||
|
@PostMapping("/job/{jobName}/build")
|
||||||
|
void triggerBuild(
|
||||||
|
@RequestHeader("Authorization") String authorization,
|
||||||
|
@PathVariable("jobName") String jobName
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.qqchen.deploy.backend.integration.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Component
|
||||||
|
public class JenkinsProperties {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public String getBasicAuth() {
|
||||||
|
String auth = username + ":" + password;
|
||||||
|
return "Basic " + Base64.getEncoder().encodeToString(auth.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.qqchen.deploy.backend.integration.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BuildInfo {
|
||||||
|
private String jobName;
|
||||||
|
|
||||||
|
private Integer buildNumber;
|
||||||
|
|
||||||
|
private BuildStatus status;
|
||||||
|
|
||||||
|
private LocalDateTime buildTime;
|
||||||
|
|
||||||
|
private String buildUrl;
|
||||||
|
|
||||||
|
public enum BuildStatus {
|
||||||
|
SUCCESS, FAILURE, IN_PROGRESS, CANCELLED
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.qqchen.deploy.backend.integration.dto;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.integration.dto.ThirdPartyDTO;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class JenkinsBuildDTO implements ThirdPartyDTO {
|
||||||
|
|
||||||
|
private String jobName;
|
||||||
|
|
||||||
|
private Integer buildNumber;
|
||||||
|
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
private String result;
|
||||||
|
|
||||||
|
private Long timestamp;
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.qqchen.deploy.backend.integration.dto;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.integration.dto.ThirdPartyDTO;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class JenkinsConnectionDTO implements ThirdPartyDTO {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.qqchen.deploy.backend.integration.enums;
|
||||||
|
|
||||||
|
public enum JenkinsBuildStatus {
|
||||||
|
SUCCESS, FAILURE, IN_PROGRESS, CANCELLED
|
||||||
|
}
|
||||||
@ -1,15 +1,14 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.Department;
|
import com.qqchen.deploy.backend.entity.Department;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@Repository
|
@Repository
|
||||||
public interface DepartmentRepository extends BaseRepository<Department, Long> {
|
public interface IDepartmentRepository extends IBaseRepository<Department, Long> {
|
||||||
|
|
||||||
List<Department> findByParentIdAndDeletedFalseOrderBySort(Long parentId);
|
List<Department> findByParentIdAndDeletedFalseOrderBySort(Long parentId);
|
||||||
|
|
||||||
@ -1,13 +1,13 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.JenkinsBuild;
|
import com.qqchen.deploy.backend.entity.JenkinsBuild;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@Repository
|
@Repository
|
||||||
public interface JenkinsBuildRepository extends BaseRepository<JenkinsBuild, Long> {
|
public interface IJenkinsBuildRepository extends IBaseRepository<JenkinsBuild, Long> {
|
||||||
List<JenkinsBuild> findByJobIdAndDeletedFalse(Long jobId);
|
List<JenkinsBuild> findByJobIdAndDeletedFalse(Long jobId);
|
||||||
List<JenkinsBuild> findByJobIdAndBuildNumberAndDeletedFalse(Long jobId, Integer buildNumber);
|
List<JenkinsBuild> findByJobIdAndBuildNumberAndDeletedFalse(Long jobId, Integer buildNumber);
|
||||||
void deleteByJobIdAndDeletedFalse(Long jobId);
|
void deleteByJobIdAndDeletedFalse(Long jobId);
|
||||||
@ -1,13 +1,13 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.JenkinsConfig;
|
import com.qqchen.deploy.backend.entity.JenkinsConfig;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@Repository
|
@Repository
|
||||||
public interface JenkinsConfigRepository extends BaseRepository<JenkinsConfig, Long> {
|
public interface IJenkinsConfigRepository extends IBaseRepository<JenkinsConfig, Long> {
|
||||||
List<JenkinsConfig> findByDeletedFalseOrderBySort();
|
List<JenkinsConfig> findByDeletedFalseOrderBySort();
|
||||||
boolean existsByNameAndDeletedFalse(String name);
|
boolean existsByNameAndDeletedFalse(String name);
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.JenkinsJob;
|
import com.qqchen.deploy.backend.entity.JenkinsJob;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
@ -8,7 +8,7 @@ import org.springframework.stereotype.Repository;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@Repository
|
@Repository
|
||||||
public interface JenkinsJobRepository extends BaseRepository<JenkinsJob, Long> {
|
public interface IJenkinsJobRepository extends IBaseRepository<JenkinsJob, Long> {
|
||||||
List<JenkinsJob> findByJenkinsIdAndDeletedFalse(Long jenkinsId);
|
List<JenkinsJob> findByJenkinsIdAndDeletedFalse(Long jenkinsId);
|
||||||
List<JenkinsJob> findByJenkinsIdAndJobNameAndDeletedFalse(Long jenkinsId, String jobName);
|
List<JenkinsJob> findByJenkinsIdAndJobNameAndDeletedFalse(Long jenkinsId, String jobName);
|
||||||
void deleteByJenkinsIdAndDeletedFalse(Long jenkinsId);
|
void deleteByJenkinsIdAndDeletedFalse(Long jenkinsId);
|
||||||
@ -1,12 +1,12 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.JenkinsSyncHistory;
|
import com.qqchen.deploy.backend.entity.JenkinsSyncHistory;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@Repository
|
@Repository
|
||||||
public interface JenkinsSyncHistoryRepository extends BaseRepository<JenkinsSyncHistory, Long> {
|
public interface IJenkinsSyncHistoryRepository extends IBaseRepository<JenkinsSyncHistory, Long> {
|
||||||
List<JenkinsSyncHistory> findTop50ByOrderByStartTimeDesc();
|
List<JenkinsSyncHistory> findTop50ByOrderByStartTimeDesc();
|
||||||
}
|
}
|
||||||
@ -1,14 +1,14 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.JenkinsView;
|
import com.qqchen.deploy.backend.entity.JenkinsView;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface JenkinsViewRepository extends BaseRepository<JenkinsView, Long> {
|
public interface IJenkinsViewRepository extends IBaseRepository<JenkinsView, Long> {
|
||||||
List<JenkinsView> findByJenkinsIdAndDeletedFalse(Long jenkinsId);
|
List<JenkinsView> findByJenkinsIdAndDeletedFalse(Long jenkinsId);
|
||||||
List<JenkinsView> findByJenkinsIdAndViewNameAndDeletedFalse(Long jenkinsId, String viewName);
|
List<JenkinsView> findByJenkinsIdAndViewNameAndDeletedFalse(Long jenkinsId, String viewName);
|
||||||
void deleteByJenkinsIdAndDeletedFalse(Long jenkinsId);
|
void deleteByJenkinsIdAndDeletedFalse(Long jenkinsId);
|
||||||
@ -2,12 +2,11 @@ package com.qqchen.deploy.backend.repository;
|
|||||||
|
|
||||||
import com.qqchen.deploy.backend.entity.Menu;
|
import com.qqchen.deploy.backend.entity.Menu;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@Repository
|
@Repository
|
||||||
public interface MenuRepository extends JpaRepository<Menu, Long> {
|
public interface IMenuRepository extends JpaRepository<Menu, Long> {
|
||||||
|
|
||||||
List<Menu> findByDeletedFalseOrderBySort();
|
List<Menu> findByDeletedFalseOrderBySort();
|
||||||
|
|
||||||
@ -1,15 +1,14 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.RepositoryBranch;
|
import com.qqchen.deploy.backend.entity.RepositoryBranch;
|
||||||
import org.springframework.data.jpa.repository.Modifying;
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface RepositoryBranchRepository extends BaseRepository<RepositoryBranch, Long> {
|
public interface IRepositoryBranchRepository extends IBaseRepository<RepositoryBranch, Long> {
|
||||||
|
|
||||||
@Modifying
|
@Modifying
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -1,14 +1,14 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.RepositoryConfig;
|
import com.qqchen.deploy.backend.entity.RepositoryConfig;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface RepositoryConfigRepository extends BaseRepository<RepositoryConfig, Long> {
|
public interface IRepositoryConfigRepository extends IBaseRepository<RepositoryConfig, Long> {
|
||||||
List<RepositoryConfig> findByDeletedFalseOrderBySort();
|
List<RepositoryConfig> findByDeletedFalseOrderBySort();
|
||||||
boolean existsByNameAndDeletedFalse(String name);
|
boolean existsByNameAndDeletedFalse(String name);
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.RepositoryGroup;
|
import com.qqchen.deploy.backend.entity.RepositoryGroup;
|
||||||
import org.springframework.data.jpa.repository.Modifying;
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
@ -10,7 +10,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface RepositoryGroupRepository extends BaseRepository<RepositoryGroup, Long> {
|
public interface IRepositoryGroupRepository extends IBaseRepository<RepositoryGroup, Long> {
|
||||||
|
|
||||||
@Modifying
|
@Modifying
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.RepositoryProject;
|
import com.qqchen.deploy.backend.entity.RepositoryProject;
|
||||||
import org.springframework.data.jpa.repository.Modifying;
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
@ -10,7 +10,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface RepositoryProjectRepository extends BaseRepository<RepositoryProject, Long> {
|
public interface IRepositoryProjectRepository extends IBaseRepository<RepositoryProject, Long> {
|
||||||
|
|
||||||
@Modifying
|
@Modifying
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -1,13 +1,13 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.RepositorySyncHistory;
|
import com.qqchen.deploy.backend.entity.RepositorySyncHistory;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface RepositorySyncHistoryRepository extends BaseRepository<RepositorySyncHistory, Long> {
|
public interface IRepositorySyncHistoryRepository extends IBaseRepository<RepositorySyncHistory, Long> {
|
||||||
List<RepositorySyncHistory> findTop50ByOrderByStartTimeDesc();
|
List<RepositorySyncHistory> findTop50ByOrderByStartTimeDesc();
|
||||||
}
|
}
|
||||||
@ -1,14 +1,14 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.RoleMenu;
|
import com.qqchen.deploy.backend.entity.RoleMenu;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface RoleMenuRepository extends BaseRepository<RoleMenu, Long> {
|
public interface IRoleMenuRepository extends IBaseRepository<RoleMenu, Long> {
|
||||||
|
|
||||||
List<RoleMenu> findByRoleId(Long roleId);
|
List<RoleMenu> findByRoleId(Long roleId);
|
||||||
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.Role;
|
import com.qqchen.deploy.backend.entity.Role;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface RoleRepository extends BaseRepository<Role, Long> {
|
public interface IRoleRepository extends IBaseRepository<Role, Long> {
|
||||||
|
|
||||||
List<Role> findByDeletedFalseOrderBySort();
|
List<Role> findByDeletedFalseOrderBySort();
|
||||||
|
|
||||||
@ -1,13 +1,13 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.Tenant;
|
import com.qqchen.deploy.backend.entity.Tenant;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface TenantRepository extends BaseRepository<Tenant, Long> {
|
public interface ITenantRepository extends IBaseRepository<Tenant, Long> {
|
||||||
|
|
||||||
List<Tenant> findByDeletedFalseOrderById();
|
List<Tenant> findByDeletedFalseOrderById();
|
||||||
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.User;
|
import com.qqchen.deploy.backend.entity.User;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface UserRepository extends BaseRepository<User, Long> {
|
public interface IUserRepository extends IBaseRepository<User, Long> {
|
||||||
|
|
||||||
Optional<User> findByUsernameAndDeletedFalse(String username);
|
Optional<User> findByUsernameAndDeletedFalse(String username);
|
||||||
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
package com.qqchen.deploy.backend.repository;
|
package com.qqchen.deploy.backend.repository;
|
||||||
|
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.entity.UserRole;
|
import com.qqchen.deploy.backend.entity.UserRole;
|
||||||
import org.springframework.data.jpa.repository.Modifying;
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
@ -11,7 +11,7 @@ import org.springframework.stereotype.Repository;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface UserRoleRepository extends BaseRepository<UserRole, Long> {
|
public interface IUserRoleRepository extends IBaseRepository<UserRole, Long> {
|
||||||
|
|
||||||
@Query("SELECT ur FROM UserRole ur WHERE ur.user.id = :userId")
|
@Query("SELECT ur FROM UserRole ur WHERE ur.user.id = :userId")
|
||||||
Set<UserRole> findByUserId(@Param("userId") Long userId);
|
Set<UserRole> findByUserId(@Param("userId") Long userId);
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.qqchen.deploy.backend.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.service.IBaseService;
|
||||||
|
import com.qqchen.deploy.backend.entity.Department;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IDepartmentService extends IBaseService<Department, Long> {
|
||||||
|
|
||||||
|
List<Department> getTree();
|
||||||
|
|
||||||
|
void validateCode(String code);
|
||||||
|
|
||||||
|
void validateName(String name);
|
||||||
|
|
||||||
|
Integer getNextSort(Long parentId);
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.qqchen.deploy.backend.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.service.IBaseService;
|
||||||
|
import com.qqchen.deploy.backend.entity.JenkinsConfig;
|
||||||
|
|
||||||
|
public interface IJenkinsService extends IBaseService<JenkinsConfig, Long> {
|
||||||
|
// boolean testConnection(JenkinsTestConnectionDTO dto);
|
||||||
|
|
||||||
|
Long asyncSyncAll(Long jenkinsId);
|
||||||
|
|
||||||
|
Long asyncSyncView(Long jenkinsId);
|
||||||
|
|
||||||
|
Long asyncSyncJob(Long jenkinsId);
|
||||||
|
|
||||||
|
Long asyncSyncBuild(Long jenkinsId);
|
||||||
|
|
||||||
|
// List<JenkinsSyncHistoryDTO> getSyncHistories();
|
||||||
|
|
||||||
|
// List<ViewResponse> getViews(Long jenkinsId);
|
||||||
|
|
||||||
|
// List<JobResponse> getJobs(Long jenkinsId);
|
||||||
|
|
||||||
|
// List<BuildResponse> getBuilds(Long jenkinsId, Long jobId);
|
||||||
|
|
||||||
|
// List<JobResponse> getJobsByView(Long jenkinsId, String viewName);
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.qqchen.deploy.backend.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.service.IBaseService;
|
||||||
|
import com.qqchen.deploy.backend.entity.Menu;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IMenuService extends IBaseService<Menu, Long> {
|
||||||
|
|
||||||
|
// List<MenuDTO> getMenuTree();
|
||||||
|
|
||||||
|
// List<MenuDTO> getMenuTreeWithoutButtons();
|
||||||
|
|
||||||
|
// List<MenuDTO> getUserMenus(Long userId);
|
||||||
|
|
||||||
|
List<Menu> getMenusByRoleId(Long roleId);
|
||||||
|
|
||||||
|
void saveRoleMenus(Long roleId, List<Long> menuIds);
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package com.qqchen.deploy.backend.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.service.IBaseService;
|
||||||
|
import com.qqchen.deploy.backend.entity.RepositoryConfig;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IRepositoryService extends IBaseService<RepositoryConfig, Long> {
|
||||||
|
|
||||||
|
// 仓库配置管理
|
||||||
|
List<RepositoryConfig> listConfigs();
|
||||||
|
RepositoryConfig createConfig(RepositoryConfig config);
|
||||||
|
RepositoryConfig updateConfig(Long id, RepositoryConfig config);
|
||||||
|
void deleteConfig(Long id);
|
||||||
|
|
||||||
|
// 测试连接
|
||||||
|
boolean testConnection(RepositoryConfig config);
|
||||||
|
// boolean testConnection(TestConnectionDTO dto);
|
||||||
|
|
||||||
|
// 同步操作
|
||||||
|
void syncAll(Long repositoryId);
|
||||||
|
void syncGroups(Long repositoryId);
|
||||||
|
void syncProjects(Long repositoryId, Long groupId);
|
||||||
|
void syncBranches(Long repositoryId, Long projectId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步同步仓库数据
|
||||||
|
* @param repositoryId 仓库ID
|
||||||
|
* @return 同步历史ID
|
||||||
|
*/
|
||||||
|
Long asyncSyncAll(Long repositoryId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取同步状态
|
||||||
|
* @param historyId 同步历史ID
|
||||||
|
* @return 仓库同步状态
|
||||||
|
*/
|
||||||
|
// RepositorySyncStatusDTO getSyncStatus(Long historyId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取正在同步的仓库列表
|
||||||
|
* @return 正在同步的仓库信息列表
|
||||||
|
*/
|
||||||
|
// List<RunningSyncDTO> getRunningSyncs();
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.qqchen.deploy.backend.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.service.IBaseService;
|
||||||
|
import com.qqchen.deploy.backend.entity.Role;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IRoleService extends IBaseService<Role, Long> {
|
||||||
|
|
||||||
|
void validateCode(String code);
|
||||||
|
|
||||||
|
void validateName(String name);
|
||||||
|
|
||||||
|
List<Long> getRoleMenuIds(Long roleId);
|
||||||
|
|
||||||
|
void updateRoleMenus(Long roleId, List<Long> menuIds);
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.qqchen.deploy.backend.service;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.service.IBaseService;
|
||||||
|
import com.qqchen.deploy.backend.entity.Tenant;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ITenantService extends IBaseService<Tenant, Long> {
|
||||||
|
|
||||||
|
void validateCode(String code);
|
||||||
|
|
||||||
|
void validateName(String name);
|
||||||
|
|
||||||
|
List<Tenant> findAllEnabled();
|
||||||
|
|
||||||
|
boolean existsByCode(String code);
|
||||||
|
|
||||||
|
boolean existsByName(String name);
|
||||||
|
|
||||||
|
// List<Tenant> findAll(TenantQuery query);
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
package com.qqchen.deploy.backend.service;
|
package com.qqchen.deploy.backend.service;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.common.service.BaseService;
|
import com.qqchen.deploy.backend.common.service.IBaseService;
|
||||||
import com.qqchen.deploy.backend.entity.User;
|
import com.qqchen.deploy.backend.entity.User;
|
||||||
|
|
||||||
public interface UserService extends BaseService<User, Long> {
|
public interface IUserService extends IBaseService<User, Long> {
|
||||||
User register(User user);
|
User register(User user);
|
||||||
User findByUsername(String username);
|
User findByUsername(String username);
|
||||||
boolean checkUsernameExists(String username);
|
boolean checkUsernameExists(String username);
|
||||||
@ -0,0 +1,105 @@
|
|||||||
|
package com.qqchen.deploy.backend.service.impl;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.service.impl.BaseServiceImpl;
|
||||||
|
import com.qqchen.deploy.backend.entity.Department;
|
||||||
|
import com.qqchen.deploy.backend.repository.IDepartmentRepository;
|
||||||
|
import com.qqchen.deploy.backend.service.IDepartmentService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class DepartmentServiceImpl extends BaseServiceImpl<Department, Long> implements IDepartmentService {
|
||||||
|
|
||||||
|
private final IDepartmentRepository repository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public DepartmentServiceImpl(IDepartmentRepository repository) {
|
||||||
|
super(repository);
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Department create(Department entity) {
|
||||||
|
validateCode(entity.getCode());
|
||||||
|
validateName(entity.getName());
|
||||||
|
return repository.save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Department update(Long id, Department entity) {
|
||||||
|
Department existing = repository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("部门不存在"));
|
||||||
|
|
||||||
|
entity.setVersion(existing.getVersion());
|
||||||
|
entity.setId(id);
|
||||||
|
|
||||||
|
if (!existing.getCode().equals(entity.getCode())) {
|
||||||
|
if (repository.existsByCodeAndDeletedFalse(entity.getCode())) {
|
||||||
|
throw new RuntimeException("部门编码已存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existing.getName().equals(entity.getName())) {
|
||||||
|
if (repository.existsByNameAndDeletedFalse(entity.getName())) {
|
||||||
|
throw new RuntimeException("部门名称已存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setCreateTime(existing.getCreateTime());
|
||||||
|
entity.setCreateBy(existing.getCreateBy());
|
||||||
|
entity.setDeleted(existing.getDeleted());
|
||||||
|
|
||||||
|
return repository.save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Department> getTree() {
|
||||||
|
List<Department> departments = repository.findByDeletedFalseOrderBySort();
|
||||||
|
return buildTree(departments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateCode(String code) {
|
||||||
|
if (repository.existsByCodeAndDeletedFalse(code)) {
|
||||||
|
throw new RuntimeException("部门编码已存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateName(String name) {
|
||||||
|
if (repository.existsByNameAndDeletedFalse(name)) {
|
||||||
|
throw new RuntimeException("部门名称已存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getNextSort(Long parentId) {
|
||||||
|
return repository.findMaxSortByParentId(parentId) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Department> buildTree(List<Department> departments) {
|
||||||
|
Map<Long, List<Department>> parentIdMap = departments.stream()
|
||||||
|
.collect(Collectors.groupingBy(d -> d.getParentId() == null ? 0L : d.getParentId()));
|
||||||
|
|
||||||
|
return buildTreeNodes(0L, parentIdMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Department> buildTreeNodes(Long parentId, Map<Long, List<Department>> parentIdMap) {
|
||||||
|
List<Department> nodes = new ArrayList<>();
|
||||||
|
|
||||||
|
List<Department> children = parentIdMap.get(parentId);
|
||||||
|
if (children != null) {
|
||||||
|
for (Department child : children) {
|
||||||
|
child.setChildren(buildTreeNodes(child.getId(), parentIdMap));
|
||||||
|
nodes.add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,612 @@
|
|||||||
|
//package com.qqchen.deploy.backend.service.impl;
|
||||||
|
//
|
||||||
|
//import com.qqchen.deploy.backend.common.service.impl.BaseServiceImpl;
|
||||||
|
//import com.qqchen.deploy.backend.entity.JenkinsConfig;
|
||||||
|
//import com.qqchen.deploy.backend.repository.IJenkinsBuildRepository;
|
||||||
|
//import com.qqchen.deploy.backend.repository.IJenkinsConfigRepository;
|
||||||
|
//import com.qqchen.deploy.backend.repository.IJenkinsJobRepository;
|
||||||
|
//import com.qqchen.deploy.backend.repository.IJenkinsSyncHistoryRepository;
|
||||||
|
//import com.qqchen.deploy.backend.repository.IJenkinsViewRepository;
|
||||||
|
//import com.qqchen.deploy.backend.service.IJenkinsService;
|
||||||
|
//import jakarta.persistence.EntityManager;
|
||||||
|
//import jakarta.persistence.PersistenceContext;
|
||||||
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
|
//import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
//import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
|
//import org.springframework.core.ParameterizedTypeReference;
|
||||||
|
//import org.springframework.http.HttpEntity;
|
||||||
|
//import org.springframework.http.HttpHeaders;
|
||||||
|
//import org.springframework.http.HttpMethod;
|
||||||
|
//import org.springframework.http.HttpStatus;
|
||||||
|
//import org.springframework.http.MediaType;
|
||||||
|
//import org.springframework.http.ResponseEntity;
|
||||||
|
//import org.springframework.stereotype.Service;
|
||||||
|
//import org.springframework.transaction.annotation.Transactional;
|
||||||
|
//import org.springframework.web.client.RestTemplate;
|
||||||
|
//
|
||||||
|
//import java.time.Duration;
|
||||||
|
//import java.time.Instant;
|
||||||
|
//import java.time.LocalDateTime;
|
||||||
|
//import java.time.ZoneId;
|
||||||
|
//import java.util.ArrayList;
|
||||||
|
//import java.util.Base64;
|
||||||
|
//import java.util.Collections;
|
||||||
|
//import java.util.List;
|
||||||
|
//import java.util.Map;
|
||||||
|
//import java.util.concurrent.CompletableFuture;
|
||||||
|
//import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
//import java.util.stream.Collectors;
|
||||||
|
//
|
||||||
|
//@Slf4j
|
||||||
|
//@Service
|
||||||
|
//public class IJenkinsServiceImpl extends BaseServiceImpl<JenkinsConfig, Long> implements IJenkinsService {
|
||||||
|
//
|
||||||
|
// private final IJenkinsConfigRepository jenkinsConfigRepository;
|
||||||
|
// private final IJenkinsSyncHistoryRepository jenkinsSyncHistoryRepository;
|
||||||
|
// private final IJenkinsViewRepository jenkinsViewRepository;
|
||||||
|
// private final IJenkinsJobRepository jenkinsJobRepository;
|
||||||
|
// private final IJenkinsBuildRepository jenkinsBuildRepository;
|
||||||
|
//
|
||||||
|
// @PersistenceContext
|
||||||
|
// private EntityManager entityManager;
|
||||||
|
//
|
||||||
|
// // 使用 ConcurrentHashMap 记录正在执行的任务
|
||||||
|
// private static final Map<Long, Boolean> RUNNING_TASKS = new ConcurrentHashMap<>();
|
||||||
|
//
|
||||||
|
// @Autowired
|
||||||
|
// public IJenkinsServiceImpl(
|
||||||
|
// IJenkinsConfigRepository jenkinsConfigRepository,
|
||||||
|
// IJenkinsSyncHistoryRepository jenkinsSyncHistoryRepository,
|
||||||
|
// IJenkinsViewRepository jenkinsViewRepository,
|
||||||
|
// IJenkinsJobRepository jenkinsJobRepository,
|
||||||
|
// IJenkinsBuildRepository jenkinsBuildRepository
|
||||||
|
// ){
|
||||||
|
// super(jenkinsConfigRepository);
|
||||||
|
// this.jenkinsConfigRepository = jenkinsConfigRepository;
|
||||||
|
// this.jenkinsSyncHistoryRepository = jenkinsSyncHistoryRepository;
|
||||||
|
// this.jenkinsViewRepository = jenkinsViewRepository;
|
||||||
|
// this.jenkinsJobRepository = jenkinsJobRepository;
|
||||||
|
// this.jenkinsBuildRepository = jenkinsBuildRepository;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private RestTemplate createRestTemplate() {
|
||||||
|
// return new RestTemplateBuilder()
|
||||||
|
// .setConnectTimeout(Duration.ofSeconds(30))
|
||||||
|
// .setReadTimeout(Duration.ofSeconds(30))
|
||||||
|
// .build();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private HttpEntity<String> createHttpEntity(String username, String password) {
|
||||||
|
// HttpHeaders headers = new HttpHeaders();
|
||||||
|
// String auth = username + ":" + password;
|
||||||
|
// byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
|
||||||
|
// headers.set("Authorization", "Basic " + new String(encodedAuth));
|
||||||
|
// headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||||
|
// return new HttpEntity<>(headers);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Transactional
|
||||||
|
// public JenkinsConfig save(JenkinsConfig config) {
|
||||||
|
// if (jenkinsConfigRepository.existsByNameAndDeletedFalse(config.getName())) {
|
||||||
|
// throw new RuntimeException("Jenkins名称已存在");
|
||||||
|
// }
|
||||||
|
// return jenkinsConfigRepository.save(config);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Transactional
|
||||||
|
// public JenkinsConfig update(Long id, JenkinsConfig config) {
|
||||||
|
// JenkinsConfig existing = jenkinsConfigRepository.findById(id)
|
||||||
|
// .orElseThrow(() -> new RuntimeException("Jenkins配置不存在"));
|
||||||
|
//
|
||||||
|
// if (!existing.getName().equals(config.getName())
|
||||||
|
// && jenkinsConfigRepository.existsByNameAndDeletedFalse(config.getName())) {
|
||||||
|
// throw new RuntimeException("Jenkins名称已存在");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// config.setId(id);
|
||||||
|
// config.setVersion(existing.getVersion());
|
||||||
|
// config.setDeleted(existing.getDeleted());
|
||||||
|
// return jenkinsConfigRepository.save(config);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public boolean testConnection(JenkinsTestConnectionDTO dto) {
|
||||||
|
// try {
|
||||||
|
// String url = String.format("%s/api/json", dto.getUrl().trim());
|
||||||
|
// ResponseEntity<Map> response = createRestTemplate().exchange(
|
||||||
|
// url,
|
||||||
|
// HttpMethod.GET,
|
||||||
|
// createHttpEntity(dto.getUsername(), dto.getPassword()),
|
||||||
|
// Map.class
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
|
||||||
|
// log.info("Jenkins连接测试成功");
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.error("Jenkins连接测试失败: 响应状态码 {}", response.getStatusCode());
|
||||||
|
// return false;
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("Jenkins连接测试失败: {}", e.getMessage());
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public Long asyncSyncAll(Long jenkinsId) {
|
||||||
|
// // 检查是否已有同步任务在运行
|
||||||
|
// if (RUNNING_TASKS.containsKey(jenkinsId)) {
|
||||||
|
// log.warn("Jenkins已有同步任务正在运行中, jenkinsId: {}", jenkinsId);
|
||||||
|
// throw new ApiException("该Jenkins已有同步任务正在运行中");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.info("开始同步Jenkins数据, jenkinsId: {}", jenkinsId);
|
||||||
|
//
|
||||||
|
// JenkinsConfig config = jenkinsConfigRepository.findById(jenkinsId)
|
||||||
|
// .orElseThrow(() -> new ApiException("Jenkins配置不存在"));
|
||||||
|
//
|
||||||
|
// // 创建全量同步历史记录
|
||||||
|
// JenkinsSyncHistory allHistory = createSyncHistory(jenkinsId, JenkinsSyncHistory.SyncType.ALL);
|
||||||
|
//
|
||||||
|
// // 标记任务开始运行
|
||||||
|
// RUNNING_TASKS.put(jenkinsId, true);
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// // 1. 同步视图
|
||||||
|
// JenkinsSyncHistory viewHistory = createSyncHistory(jenkinsId, JenkinsSyncHistory.SyncType.VIEW);
|
||||||
|
// try {
|
||||||
|
// syncViews(config, viewHistory);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("视图同步失败", e);
|
||||||
|
// updateSyncHistory(viewHistory, JenkinsSyncHistory.SyncStatus.FAILED, e.getMessage());
|
||||||
|
// throw e;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 2. 同步作业
|
||||||
|
// JenkinsSyncHistory jobHistory = createSyncHistory(jenkinsId, JenkinsSyncHistory.SyncType.JOB);
|
||||||
|
// try {
|
||||||
|
// syncJobs(config, jobHistory);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("作业同步失败", e);
|
||||||
|
// updateSyncHistory(jobHistory, JenkinsSyncHistory.SyncStatus.FAILED, e.getMessage());
|
||||||
|
// throw e;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 3. 同步构建
|
||||||
|
// JenkinsSyncHistory buildHistory = createSyncHistory(jenkinsId, JenkinsSyncHistory.SyncType.BUILD);
|
||||||
|
// try {
|
||||||
|
// syncBuilds(config, buildHistory);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("构建同步失败", e);
|
||||||
|
// updateSyncHistory(buildHistory, JenkinsSyncHistory.SyncStatus.FAILED, e.getMessage());
|
||||||
|
// throw e;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 更新全量同步状态
|
||||||
|
// updateSyncHistory(allHistory, JenkinsSyncHistory.SyncStatus.SUCCESS, null);
|
||||||
|
// config.setLastAllSyncTime(LocalDateTime.now());
|
||||||
|
// jenkinsConfigRepository.save(config);
|
||||||
|
// log.info("Jenkins数据同步完成, jenkinsId: {}", jenkinsId);
|
||||||
|
//
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("Jenkins数据同步失败", e);
|
||||||
|
// updateSyncHistory(allHistory, JenkinsSyncHistory.SyncStatus.FAILED, e.getMessage());
|
||||||
|
// } finally {
|
||||||
|
// RUNNING_TASKS.remove(jenkinsId);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return allHistory.getId();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 同步Jenkins视图
|
||||||
|
// * @param config Jenkins配置
|
||||||
|
// * @param history 同步历史记录
|
||||||
|
// */
|
||||||
|
// private void syncViews(JenkinsConfig config, JenkinsSyncHistory history) {
|
||||||
|
// String url = String.format("%s/api/json?tree=views[name,url,description]", config.getUrl().trim());
|
||||||
|
// ResponseEntity<Map<String, Object>> response = createRestTemplate().exchange(
|
||||||
|
// url,
|
||||||
|
// HttpMethod.GET,
|
||||||
|
// createHttpEntity(config.getUsername(), config.getPassword()),
|
||||||
|
// new ParameterizedTypeReference<>() {}
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.getBody() != null) {
|
||||||
|
// List<Map<String, Object>> views = (List<Map<String, Object>>) response.getBody().get("views");
|
||||||
|
// List<JenkinsView> viewsToSave = new ArrayList<>();
|
||||||
|
// Map<String, JenkinsView> existingViews = jenkinsViewRepository
|
||||||
|
// .findByJenkinsIdAndDeletedFalse(config.getId())
|
||||||
|
// .stream()
|
||||||
|
// .collect(Collectors.toMap(JenkinsView::getViewName, v -> v));
|
||||||
|
//
|
||||||
|
// for (Map<String, Object> view : views) {
|
||||||
|
// String viewName = (String) view.get("name");
|
||||||
|
// // 跳过 "All" 视图
|
||||||
|
// if ("All".equalsIgnoreCase(viewName)) {
|
||||||
|
// log.debug("跳过All视图同步 - 仓库: {}", config.getName());
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// JenkinsView jenkinsView = existingViews.getOrDefault(viewName, new JenkinsView());
|
||||||
|
// jenkinsView.setJenkinsId(config.getId());
|
||||||
|
// jenkinsView.setViewName(viewName);
|
||||||
|
// jenkinsView.setViewUrl((String) view.get("url"));
|
||||||
|
// jenkinsView.setDescription((String) view.get("description"));
|
||||||
|
// viewsToSave.add(jenkinsView);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!viewsToSave.isEmpty()) {
|
||||||
|
// jenkinsViewRepository.saveAll(viewsToSave);
|
||||||
|
// log.info("同步Jenkins视图完成, 总数: {}, 实际同步: {}", views.size(), viewsToSave.size());
|
||||||
|
//
|
||||||
|
// // 更新配置的同步时间
|
||||||
|
// config.setLastViewSyncTime(LocalDateTime.now());
|
||||||
|
// jenkinsConfigRepository.save(config);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// updateSyncHistory(history, JenkinsSyncHistory.SyncStatus.SUCCESS, null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Transactional
|
||||||
|
// public void updateSyncHistory(JenkinsSyncHistory history,
|
||||||
|
// JenkinsSyncHistory.SyncStatus status,
|
||||||
|
// String errorMessage) {
|
||||||
|
// // 重新获取最新的历史记录
|
||||||
|
// JenkinsSyncHistory latestHistory = jenkinsSyncHistoryRepository.findById(history.getId())
|
||||||
|
// .orElseThrow(() -> new ApiException("同步历史记录不存在"));
|
||||||
|
//
|
||||||
|
// latestHistory.setStatus(status);
|
||||||
|
// latestHistory.setEndTime(LocalDateTime.now());
|
||||||
|
// latestHistory.setErrorMessage(errorMessage);
|
||||||
|
// jenkinsSyncHistoryRepository.save(latestHistory);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Transactional
|
||||||
|
// public JenkinsSyncHistory createSyncHistory(Long jenkinsId, JenkinsSyncHistory.SyncType syncType) {
|
||||||
|
// JenkinsSyncHistory history = new JenkinsSyncHistory();
|
||||||
|
// history.setJenkinsId(jenkinsId);
|
||||||
|
// history.setSyncType(syncType);
|
||||||
|
// history.setStartTime(LocalDateTime.now());
|
||||||
|
// history.setStatus(JenkinsSyncHistory.SyncStatus.RUNNING);
|
||||||
|
// return jenkinsSyncHistoryRepository.save(history);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 同步Jenkins作业
|
||||||
|
// * @param config Jenkins配置
|
||||||
|
// * @param history 同步历记录
|
||||||
|
// */
|
||||||
|
// private void syncJobs(JenkinsConfig config, JenkinsSyncHistory history) {
|
||||||
|
// String url = String.format("%s/api/json?tree=jobs[name,url,description,buildable,lastBuild[number,timestamp,result]]",
|
||||||
|
// config.getUrl().trim());
|
||||||
|
// ResponseEntity<Map<String, Object>> response = createRestTemplate().exchange(
|
||||||
|
// url,
|
||||||
|
// HttpMethod.GET,
|
||||||
|
// createHttpEntity(config.getUsername(), config.getPassword()),
|
||||||
|
// new ParameterizedTypeReference<>() {}
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.getBody() != null) {
|
||||||
|
// List<Map<String, Object>> jobs = (List<Map<String, Object>>) response.getBody().get("jobs");
|
||||||
|
// List<JenkinsJob> jobsToSave = new ArrayList<>();
|
||||||
|
// Map<String, JenkinsJob> existingJobs = jenkinsJobRepository
|
||||||
|
// .findByJenkinsIdAndDeletedFalse(config.getId())
|
||||||
|
// .stream()
|
||||||
|
// .collect(Collectors.toMap(JenkinsJob::getJobName, j -> j));
|
||||||
|
//
|
||||||
|
// for (Map<String, Object> job : jobs) {
|
||||||
|
// String jobName = (String) job.get("name");
|
||||||
|
// JenkinsJob jenkinsJob = existingJobs.getOrDefault(jobName, new JenkinsJob());
|
||||||
|
// jenkinsJob.setJenkinsId(config.getId());
|
||||||
|
// jenkinsJob.setJobName(jobName);
|
||||||
|
// jenkinsJob.setJobUrl((String) job.get("url"));
|
||||||
|
// jenkinsJob.setDescription((String) job.get("description"));
|
||||||
|
// jenkinsJob.setBuildable((Boolean) job.get("buildable"));
|
||||||
|
//
|
||||||
|
// Map<String, Object> lastBuild = (Map<String, Object>) job.get("lastBuild");
|
||||||
|
// if (lastBuild != null) {
|
||||||
|
// jenkinsJob.setLastBuildNumber(((Number) lastBuild.get("number")).intValue());
|
||||||
|
// long timestamp = ((Number) lastBuild.get("timestamp")).longValue();
|
||||||
|
// jenkinsJob.setLastBuildTime(LocalDateTime.ofInstant(
|
||||||
|
// Instant.ofEpochMilli(timestamp),
|
||||||
|
// ZoneId.systemDefault()
|
||||||
|
// ));
|
||||||
|
// jenkinsJob.setLastBuildStatus((String) lastBuild.get("result"));
|
||||||
|
// }
|
||||||
|
// jobsToSave.add(jenkinsJob);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!jobsToSave.isEmpty()) {
|
||||||
|
// jenkinsJobRepository.saveAll(jobsToSave);
|
||||||
|
// log.info("同步Jenkins作业完成, 总数: {}, 实际同步: {}", jobs.size(), jobsToSave.size());
|
||||||
|
//
|
||||||
|
// // 更新配置的同步时间
|
||||||
|
// config.setLastJobSyncTime(LocalDateTime.now());
|
||||||
|
// jenkinsConfigRepository.save(config);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// updateSyncHistory(history, JenkinsSyncHistory.SyncStatus.SUCCESS, null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 同步Jenkins构建
|
||||||
|
// * @param config Jenkins配置
|
||||||
|
// * @param history 同步历史记录
|
||||||
|
// */
|
||||||
|
// private void syncBuilds(JenkinsConfig config, JenkinsSyncHistory history) {
|
||||||
|
// List<JenkinsJob> jobs = jenkinsJobRepository.findByJenkinsIdAndDeletedFalse(config.getId());
|
||||||
|
// List<JenkinsBuild> allBuildsToSave = new ArrayList<>();
|
||||||
|
//
|
||||||
|
// for (JenkinsJob job : jobs) {
|
||||||
|
// String url = String.format("%s/api/json?tree=builds[number,url,timestamp,result,duration,actions[causes[*]]]",
|
||||||
|
// job.getJobUrl().trim());
|
||||||
|
// ResponseEntity<Map<String, Object>> response = createRestTemplate().exchange(
|
||||||
|
// url,
|
||||||
|
// HttpMethod.GET,
|
||||||
|
// createHttpEntity(config.getUsername(), config.getPassword()),
|
||||||
|
// new ParameterizedTypeReference<>() {}
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.getBody() != null) {
|
||||||
|
// List<Map<String, Object>> builds = (List<Map<String, Object>>) response.getBody().get("builds");
|
||||||
|
// Map<Integer, JenkinsBuild> existingBuilds = jenkinsBuildRepository
|
||||||
|
// .findByJobIdAndDeletedFalse(job.getId())
|
||||||
|
// .stream()
|
||||||
|
// .collect(Collectors.toMap(JenkinsBuild::getBuildNumber, b -> b));
|
||||||
|
//
|
||||||
|
// for (Map<String, Object> build : builds) {
|
||||||
|
// Integer buildNumber = ((Number) build.get("number")).intValue();
|
||||||
|
// JenkinsBuild jenkinsBuild = existingBuilds.getOrDefault(buildNumber, new JenkinsBuild());
|
||||||
|
// jenkinsBuild.setJenkinsId(config.getId());
|
||||||
|
// jenkinsBuild.setJobId(job.getId());
|
||||||
|
// jenkinsBuild.setBuildNumber(buildNumber);
|
||||||
|
// jenkinsBuild.setBuildUrl((String) build.get("url"));
|
||||||
|
// jenkinsBuild.setBuildStatus((String) build.get("result"));
|
||||||
|
//
|
||||||
|
// long timestamp = ((Number) build.get("timestamp")).longValue();
|
||||||
|
// jenkinsBuild.setStartTime(LocalDateTime.ofInstant(
|
||||||
|
// Instant.ofEpochMilli(timestamp),
|
||||||
|
// ZoneId.systemDefault()
|
||||||
|
// ));
|
||||||
|
//
|
||||||
|
// jenkinsBuild.setDuration(((Number) build.get("duration")).longValue());
|
||||||
|
//
|
||||||
|
// // 处理触发原因
|
||||||
|
// List<Map<String, Object>> actions = (List<Map<String, Object>>) build.get("actions");
|
||||||
|
// if (actions != null) {
|
||||||
|
// for (Map<String, Object> action : actions) {
|
||||||
|
// if (action != null && action.containsKey("causes")) {
|
||||||
|
// List<Map<String, Object>> causes = (List<Map<String, Object>>) action.get("causes");
|
||||||
|
// if (causes != null && !causes.isEmpty()) {
|
||||||
|
// jenkinsBuild.setTriggerCause((String) causes.get(0).get("shortDescription"));
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// allBuildsToSave.add(jenkinsBuild);
|
||||||
|
// }
|
||||||
|
// log.info("处理Jenkins构建历史, 作业: {}, 构建数量: {}", job.getJobName(), builds.size());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!allBuildsToSave.isEmpty()) {
|
||||||
|
// jenkinsBuildRepository.saveAll(allBuildsToSave);
|
||||||
|
// log.info("同步Jenkins构建完成, 总数: {}", allBuildsToSave.size());
|
||||||
|
//
|
||||||
|
// // 更新配置的同步时间
|
||||||
|
// config.setLastBuildSyncTime(LocalDateTime.now());
|
||||||
|
// jenkinsConfigRepository.save(config);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// updateSyncHistory(history, JenkinsSyncHistory.SyncStatus.SUCCESS, null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<JenkinsSyncHistoryDTO> getSyncHistories() {
|
||||||
|
// List<JenkinsSyncHistory> histories = jenkinsSyncHistoryRepository.findTop50ByOrderByStartTimeDesc();
|
||||||
|
//
|
||||||
|
// return histories.stream()
|
||||||
|
// .map(history -> {
|
||||||
|
// JenkinsConfig config = jenkinsConfigRepository.findById(history.getJenkinsId())
|
||||||
|
// .orElse(null);
|
||||||
|
//
|
||||||
|
// return JenkinsSyncHistoryDTO.builder()
|
||||||
|
// .id(history.getId())
|
||||||
|
// .jenkinsId(history.getJenkinsId())
|
||||||
|
// .jenkinsName(config != null ? config.getName() : "未知Jenkins")
|
||||||
|
// .syncType(history.getSyncType())
|
||||||
|
// .status(history.getStatus())
|
||||||
|
// .startTime(history.getStartTime())
|
||||||
|
// .endTime(history.getEndTime())
|
||||||
|
// .errorMessage(history.getErrorMessage())
|
||||||
|
// .build();
|
||||||
|
// })
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Transactional
|
||||||
|
// public void updateLastSyncTime(Long jenkinsId, String timeField) {
|
||||||
|
// // 使用 JPQL 更新指定字段
|
||||||
|
// String jpql = String.format("UPDATE JenkinsConfig j SET j.%s = :now WHERE j.id = :id", timeField);
|
||||||
|
// entityManager.createQuery(jpql)
|
||||||
|
// .setParameter("now", LocalDateTime.now())
|
||||||
|
// .setParameter("id", jenkinsId)
|
||||||
|
// .executeUpdate();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public Long asyncSyncView(Long jenkinsId) {
|
||||||
|
// // 检查是否已有同步任务在运行
|
||||||
|
// if (RUNNING_TASKS.containsKey(jenkinsId)) {
|
||||||
|
// log.warn("Jenkins已有同步任务正在运行中, jenkinsId: {}", jenkinsId);
|
||||||
|
// throw new ApiException("该Jenkins已有同步任务正在运行中");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.info("开始同步Jenkins视图, jenkinsId: {}", jenkinsId);
|
||||||
|
//
|
||||||
|
// JenkinsConfig config = jenkinsConfigRepository.findById(jenkinsId)
|
||||||
|
// .orElseThrow(() -> new ApiException("Jenkins配置不存在"));
|
||||||
|
//
|
||||||
|
// // 创建同步历史记录
|
||||||
|
// JenkinsSyncHistory history = createSyncHistory(jenkinsId, JenkinsSyncHistory.SyncType.VIEW);
|
||||||
|
//
|
||||||
|
// // 标记任务开始运行
|
||||||
|
// RUNNING_TASKS.put(jenkinsId, true);
|
||||||
|
//
|
||||||
|
// CompletableFuture.runAsync(() -> {
|
||||||
|
// try {
|
||||||
|
// syncViews(config, history);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("视图同步失败", e);
|
||||||
|
// updateSyncHistory(history, JenkinsSyncHistory.SyncStatus.FAILED, e.getMessage());
|
||||||
|
// } finally {
|
||||||
|
// RUNNING_TASKS.remove(jenkinsId);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// return history.getId();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public Long asyncSyncJob(Long jenkinsId) {
|
||||||
|
// // 检查是否已有同步任务在运行
|
||||||
|
// if (RUNNING_TASKS.containsKey(jenkinsId)) {
|
||||||
|
// log.warn("Jenkins已有同步任务正在运行中, jenkinsId: {}", jenkinsId);
|
||||||
|
// throw new ApiException("该Jenkins已有同步任务正在运行中");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.info("开始同步Jenkins作业, jenkinsId: {}", jenkinsId);
|
||||||
|
//
|
||||||
|
// JenkinsConfig config = jenkinsConfigRepository.findById(jenkinsId)
|
||||||
|
// .orElseThrow(() -> new ApiException("Jenkins配置不存在"));
|
||||||
|
//
|
||||||
|
// // 创建同步历史记录
|
||||||
|
// JenkinsSyncHistory history = createSyncHistory(jenkinsId, JenkinsSyncHistory.SyncType.JOB);
|
||||||
|
//
|
||||||
|
// // 标记任务开始运行
|
||||||
|
// RUNNING_TASKS.put(jenkinsId, true);
|
||||||
|
//
|
||||||
|
// CompletableFuture.runAsync(() -> {
|
||||||
|
// try {
|
||||||
|
// syncJobs(config, history);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("作业同步失败", e);
|
||||||
|
// updateSyncHistory(history, JenkinsSyncHistory.SyncStatus.FAILED, e.getMessage());
|
||||||
|
// } finally {
|
||||||
|
// RUNNING_TASKS.remove(jenkinsId);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// return history.getId();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public Long asyncSyncBuild(Long jenkinsId) {
|
||||||
|
// // 检查是否已有同步任务在运行
|
||||||
|
// if (RUNNING_TASKS.containsKey(jenkinsId)) {
|
||||||
|
// log.warn("Jenkins已有同步任务正在运行中, jenkinsId: {}", jenkinsId);
|
||||||
|
// throw new ApiException("该Jenkins已有同步任务正在运行中");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.info("开始同步Jenkins构建, jenkinsId: {}", jenkinsId);
|
||||||
|
//
|
||||||
|
// JenkinsConfig config = jenkinsConfigRepository.findById(jenkinsId)
|
||||||
|
// .orElseThrow(() -> new ApiException("Jenkins配置不存在"));
|
||||||
|
//
|
||||||
|
// // 创建同步历史记录
|
||||||
|
// JenkinsSyncHistory history = createSyncHistory(jenkinsId, JenkinsSyncHistory.SyncType.BUILD);
|
||||||
|
//
|
||||||
|
// // 标记任务开始运行
|
||||||
|
// RUNNING_TASKS.put(jenkinsId, true);
|
||||||
|
//
|
||||||
|
// CompletableFuture.runAsync(() -> {
|
||||||
|
// try {
|
||||||
|
// syncBuilds(config, history);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("构建同步失败", e);
|
||||||
|
// updateSyncHistory(history, JenkinsSyncHistory.SyncStatus.FAILED, e.getMessage());
|
||||||
|
// } finally {
|
||||||
|
// RUNNING_TASKS.remove(jenkinsId);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// return history.getId();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<ViewResponse> getViews(Long jenkinsId) {
|
||||||
|
// List<JenkinsView> views = jenkinsViewRepository.findByJenkinsIdAndDeletedFalse(jenkinsId);
|
||||||
|
// return views.stream()
|
||||||
|
// .map(view -> ViewResponse.builder()
|
||||||
|
// .id(view.getId())
|
||||||
|
// .jenkinsId(view.getJenkinsId())
|
||||||
|
// .viewName(view.getViewName())
|
||||||
|
// .viewUrl(view.getViewUrl())
|
||||||
|
// .description(view.getDescription())
|
||||||
|
// .build())
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<JobResponse> getJobs(Long jenkinsId) {
|
||||||
|
// List<JenkinsJob> jobs = jenkinsJobRepository.findByJenkinsIdAndDeletedFalse(jenkinsId);
|
||||||
|
// return jobs.stream()
|
||||||
|
// .map(job -> JobResponse.builder()
|
||||||
|
// .id(job.getId())
|
||||||
|
// .jenkinsId(job.getJenkinsId())
|
||||||
|
// .viewId(job.getView() != null ? job.getView().getId() : null)
|
||||||
|
// .viewName(job.getView() != null ? job.getView().getViewName() : null)
|
||||||
|
// .jobName(job.getJobName())
|
||||||
|
// .jobUrl(job.getJobUrl())
|
||||||
|
// .description(job.getDescription())
|
||||||
|
// .buildable(job.getBuildable())
|
||||||
|
// .lastBuildNumber(job.getLastBuildNumber())
|
||||||
|
// .lastBuildTime(job.getLastBuildTime())
|
||||||
|
// .lastBuildStatus(job.getLastBuildStatus())
|
||||||
|
// .build())
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<BuildResponse> getBuilds(Long jenkinsId, Long jobId) {
|
||||||
|
// List<JenkinsBuild> builds = jenkinsBuildRepository.findByJobIdAndDeletedFalseOrderByBuildNumberDesc(jobId);
|
||||||
|
// return builds.stream()
|
||||||
|
// .map(build -> BuildResponse.builder()
|
||||||
|
// .id(build.getId())
|
||||||
|
// .jenkinsId(build.getJenkinsId())
|
||||||
|
// .jobId(build.getJobId())
|
||||||
|
// .buildNumber(build.getBuildNumber())
|
||||||
|
// .buildUrl(build.getBuildUrl())
|
||||||
|
// .buildStatus(build.getBuildStatus())
|
||||||
|
// .startTime(build.getStartTime())
|
||||||
|
// .duration(build.getDuration())
|
||||||
|
// .triggerCause(build.getTriggerCause())
|
||||||
|
// .build())
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<JobResponse> getJobsByView(Long jenkinsId, String viewName) {
|
||||||
|
// List<JenkinsJob> jobs = jenkinsJobRepository.findJobsByJenkinsIdAndViewName(jenkinsId, viewName);
|
||||||
|
// return jobs.stream()
|
||||||
|
// .map(job -> JobResponse.builder()
|
||||||
|
// .id(job.getId())
|
||||||
|
// .jenkinsId(job.getJenkinsId())
|
||||||
|
// .viewId(job.getView().getId())
|
||||||
|
// .viewName(job.getView().getViewName())
|
||||||
|
// .jobName(job.getJobName())
|
||||||
|
// .jobUrl(job.getJobUrl())
|
||||||
|
// .description(job.getDescription())
|
||||||
|
// .buildable(job.getBuildable())
|
||||||
|
// .lastBuildNumber(job.getLastBuildNumber())
|
||||||
|
// .lastBuildTime(job.getLastBuildTime())
|
||||||
|
// .lastBuildStatus(job.getLastBuildStatus())
|
||||||
|
// .build())
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@ -0,0 +1,254 @@
|
|||||||
|
//package com.qqchen.deploy.backend.service.impl;
|
||||||
|
//
|
||||||
|
//import com.qqchen.application.dto.MenuDTO;
|
||||||
|
//import com.qqchen.application.query.MenuQuery;
|
||||||
|
//import com.qqchen.application.service.MenuService;
|
||||||
|
//import com.qqchen.common.exception.ApiException;
|
||||||
|
//import com.qqchen.common.service.impl.BaseServiceImpl;
|
||||||
|
//import com.qqchen.domain.entity.Menu;
|
||||||
|
//import com.qqchen.domain.entity.RoleMenu;
|
||||||
|
//import com.qqchen.domain.repository.MenuRepository;
|
||||||
|
//import com.qqchen.domain.repository.RoleMenuRepository;
|
||||||
|
//import lombok.RequiredArgsConstructor;
|
||||||
|
//import org.springframework.beans.BeanUtils;
|
||||||
|
//import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
//import org.springframework.stereotype.Service;
|
||||||
|
//import org.springframework.transaction.annotation.Transactional;
|
||||||
|
//
|
||||||
|
//import java.util.ArrayList;
|
||||||
|
//import java.util.Arrays;
|
||||||
|
//import java.util.Comparator;
|
||||||
|
//import java.util.List;
|
||||||
|
//import java.util.stream.Collectors;
|
||||||
|
//
|
||||||
|
//@Service
|
||||||
|
//@RequiredArgsConstructor
|
||||||
|
//public class IMenuServiceImpl extends BaseServiceImpl<Menu, MenuQuery> implements MenuService {
|
||||||
|
//
|
||||||
|
// private final MenuRepository menuRepository;
|
||||||
|
// private final RoleMenuRepository roleMenuRepository;
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// protected JpaRepository<Menu, Long> getRepository() {
|
||||||
|
// return menuRepository;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<MenuDTO> getMenuTree() {
|
||||||
|
// List<Menu> menus = menuRepository.findByDeletedFalseOrderBySort();
|
||||||
|
// return buildMenuTree(menus);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<MenuDTO> getUserMenus(Long userId) {
|
||||||
|
// // TODO: 根据用户ID获取角色,然后获取菜单
|
||||||
|
// return new ArrayList<>();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<Menu> getMenusByRoleId(Long roleId) {
|
||||||
|
// List<RoleMenu> roleMenus = roleMenuRepository.findByRoleId(roleId);
|
||||||
|
// return roleMenus.stream()
|
||||||
|
// .map(rm -> menuRepository.findById(rm.getMenuId())
|
||||||
|
// .orElseThrow(() -> new ApiException("菜单不存在")))
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public void saveRoleMenus(Long roleId, List<Long> menuIds) {
|
||||||
|
// // 先删除原有的关联
|
||||||
|
// roleMenuRepository.deleteByRoleId(roleId);
|
||||||
|
//
|
||||||
|
// // 保存新的关联
|
||||||
|
// List<RoleMenu> roleMenus = menuIds.stream()
|
||||||
|
// .map(menuId -> {
|
||||||
|
// RoleMenu rm = new RoleMenu();
|
||||||
|
// rm.setRoleId(roleId);
|
||||||
|
// rm.setMenuId(menuId);
|
||||||
|
// return rm;
|
||||||
|
// })
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
//
|
||||||
|
// roleMenuRepository.saveAll(roleMenus);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public Menu update(Long id, Menu menu) {
|
||||||
|
// Menu existing = findById(id)
|
||||||
|
// .orElseThrow(() -> new ApiException("菜单不存在"));
|
||||||
|
//
|
||||||
|
// // 设置不允许修改的字段
|
||||||
|
// menu.setId(id);
|
||||||
|
// menu.setVersion(existing.getVersion());
|
||||||
|
// menu.setDeleted(existing.getDeleted());
|
||||||
|
//
|
||||||
|
// // 如果是按钮类型,清空路径和组件
|
||||||
|
// if (menu.getType() == 2) {
|
||||||
|
// menu.setPath(null);
|
||||||
|
// menu.setComponent(null);
|
||||||
|
// menu.setIcon(null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 如果是目录,设置固定组件
|
||||||
|
// if (menu.getType() == 0) {
|
||||||
|
// menu.setComponent("LAYOUT");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 如果没有父级菜单,设置为根节点
|
||||||
|
// if (menu.getParentId() == null) {
|
||||||
|
// menu.setParentId(0L);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return menuRepository.save(menu);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public Menu save(Menu menu) {
|
||||||
|
// // 如果是按钮类型,清空路径和组件
|
||||||
|
// if (menu.getType() == 2) {
|
||||||
|
// menu.setPath(null);
|
||||||
|
// menu.setComponent(null);
|
||||||
|
// menu.setIcon(null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 如果是目录,设置固定组件
|
||||||
|
// if (menu.getType() == 0) {
|
||||||
|
// menu.setComponent("LAYOUT");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 如果没有父级菜单,设置为根节点
|
||||||
|
// if (menu.getParentId() == null) {
|
||||||
|
// menu.setParentId(0L);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// menu.setDeleted(false);
|
||||||
|
// Menu savedMenu = menuRepository.save(menu);
|
||||||
|
//
|
||||||
|
// // 如果是菜单类型,自动创建增删改查按钮
|
||||||
|
// if (menu.getType() == 1) {
|
||||||
|
// createDefaultButtons(savedMenu);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return savedMenu;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 创建默认的按钮权限
|
||||||
|
// */
|
||||||
|
// private void createDefaultButtons(Menu menu) {
|
||||||
|
// // 获取基础权限标识
|
||||||
|
// String basePermission = getBasePermission(menu.getPath());
|
||||||
|
//
|
||||||
|
// // 创建默认按钮列表
|
||||||
|
// List<Menu> buttons = List.of(
|
||||||
|
// createButton(menu.getName() + "查看", basePermission + ":view", menu.getId(), 1),
|
||||||
|
// createButton(menu.getName() + "新增", basePermission + ":add", menu.getId(), 2),
|
||||||
|
// createButton(menu.getName() + "修改", basePermission + ":edit", menu.getId(), 3),
|
||||||
|
// createButton(menu.getName() + "删除", basePermission + ":delete", menu.getId(), 4)
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// menuRepository.saveAll(buttons);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 创建按钮
|
||||||
|
// */
|
||||||
|
// private Menu createButton(String name, String permission, Long parentId, int sort) {
|
||||||
|
// Menu button = new Menu();
|
||||||
|
// button.setName(name);
|
||||||
|
// button.setPermission(permission);
|
||||||
|
// button.setType(2);
|
||||||
|
// button.setParentId(parentId);
|
||||||
|
// button.setSort(sort);
|
||||||
|
// button.setEnabled(true);
|
||||||
|
// button.setDeleted(false);
|
||||||
|
// return button;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 根据路径获取基础权限标识
|
||||||
|
// * 例如:/system/user -> system:user
|
||||||
|
// */
|
||||||
|
// private String getBasePermission(String path) {
|
||||||
|
// if (path == null) {
|
||||||
|
// return "";
|
||||||
|
// }
|
||||||
|
// // 移除开头的斜杠
|
||||||
|
// if (path.startsWith("/")) {
|
||||||
|
// path = path.substring(1);
|
||||||
|
// }
|
||||||
|
// // 将斜杠替换为冒号
|
||||||
|
// return path.replace("/", ":");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public void delete(Long id) {
|
||||||
|
// Menu menu = findById(id)
|
||||||
|
// .orElseThrow(() -> new ApiException("菜单不存在"));
|
||||||
|
//
|
||||||
|
// // 如果是菜单类型,同时删除其下的按钮
|
||||||
|
// if (menu.getType() == 1) {
|
||||||
|
// List<Menu> buttons = menuRepository.findByParentIdAndDeletedFalse(id);
|
||||||
|
// if (!buttons.isEmpty()) {
|
||||||
|
// for (Menu button : buttons) {
|
||||||
|
// button.setDeleted(true);
|
||||||
|
// menuRepository.save(button);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 检查是否有子菜单
|
||||||
|
// List<Menu> children = menuRepository.findByParentIdAndDeletedFalse(id);
|
||||||
|
// if (!children.isEmpty()) {
|
||||||
|
// throw new ApiException("该菜单下还有子菜单,不能删除");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// menu.setDeleted(true);
|
||||||
|
// menuRepository.save(menu);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private List<MenuDTO> buildMenuTree(List<Menu> menus) {
|
||||||
|
// // 先找出所有的根节点(parentId为null或0的节点)
|
||||||
|
// List<Menu> rootMenus = menus.stream()
|
||||||
|
// .filter(menu -> menu.getParentId() == null || menu.getParentId() == 0)
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
//
|
||||||
|
// // 构建子树
|
||||||
|
// return rootMenus.stream()
|
||||||
|
// .map(menu -> buildMenuNode(menu, menus))
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 构建菜单节点
|
||||||
|
// */
|
||||||
|
// private MenuDTO buildMenuNode(Menu menu, List<Menu> allMenus) {
|
||||||
|
// MenuDTO dto = new MenuDTO();
|
||||||
|
// BeanUtils.copyProperties(menu, dto);
|
||||||
|
//
|
||||||
|
// // 查找当前节点的子节点
|
||||||
|
// List<Menu> children = allMenus.stream()
|
||||||
|
// .filter(m -> menu.getId().equals(m.getParentId()))
|
||||||
|
// .sorted(Comparator.comparing(Menu::getSort))
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
//
|
||||||
|
// if (!children.isEmpty()) {
|
||||||
|
// dto.setChildren(children.stream()
|
||||||
|
// .map(child -> buildMenuNode(child, allMenus))
|
||||||
|
// .collect(Collectors.toList()));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return dto;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<MenuDTO> getMenuTreeWithoutButtons() {
|
||||||
|
// // 获取所有非按钮类型的菜单
|
||||||
|
// List<Menu> menus = menuRepository.findByTypeInAndDeletedFalseOrderBySort(Arrays.asList(0, 1)); // 0-目录 1-菜单
|
||||||
|
// return buildMenuTree(menus);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@ -0,0 +1,688 @@
|
|||||||
|
//package com.qqchen.deploy.backend.service.impl;
|
||||||
|
//
|
||||||
|
//import com.qqchen.application.dto.RepositorySyncStatusDTO;
|
||||||
|
//import com.qqchen.application.dto.RunningSyncDTO;
|
||||||
|
//import com.qqchen.application.dto.TestConnectionDTO;
|
||||||
|
//import com.qqchen.application.service.RepositoryService;
|
||||||
|
//import com.qqchen.common.exception.ApiException;
|
||||||
|
//import com.qqchen.domain.entity.RepositoryBranch;
|
||||||
|
//import com.qqchen.domain.entity.RepositoryConfig;
|
||||||
|
//import com.qqchen.domain.entity.RepositoryGroup;
|
||||||
|
//import com.qqchen.domain.entity.RepositoryProject;
|
||||||
|
//import com.qqchen.domain.entity.RepositorySyncHistory;
|
||||||
|
//import com.qqchen.domain.repository.RepositoryBranchRepository;
|
||||||
|
//import com.qqchen.domain.repository.RepositoryConfigRepository;
|
||||||
|
//import com.qqchen.domain.repository.RepositoryGroupRepository;
|
||||||
|
//import com.qqchen.domain.repository.RepositoryProjectRepository;
|
||||||
|
//import com.qqchen.domain.repository.RepositorySyncHistoryRepository;
|
||||||
|
//import lombok.Getter;
|
||||||
|
//import lombok.RequiredArgsConstructor;
|
||||||
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
|
//import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
|
//import org.springframework.core.ParameterizedTypeReference;
|
||||||
|
//import org.springframework.core.task.AsyncTaskExecutor;
|
||||||
|
//import org.springframework.http.HttpEntity;
|
||||||
|
//import org.springframework.http.HttpHeaders;
|
||||||
|
//import org.springframework.http.HttpMethod;
|
||||||
|
//import org.springframework.http.HttpStatus;
|
||||||
|
//import org.springframework.http.MediaType;
|
||||||
|
//import org.springframework.http.ResponseEntity;
|
||||||
|
//import org.springframework.stereotype.Service;
|
||||||
|
//import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
//import org.springframework.transaction.annotation.Transactional;
|
||||||
|
//import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
//import org.springframework.web.client.RestTemplate;
|
||||||
|
//
|
||||||
|
//import java.time.Duration;
|
||||||
|
//import java.time.LocalDateTime;
|
||||||
|
//import java.util.Collection;
|
||||||
|
//import java.util.Collections;
|
||||||
|
//import java.util.List;
|
||||||
|
//import java.util.Map;
|
||||||
|
//import java.util.concurrent.CompletableFuture;
|
||||||
|
//import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
//import java.util.concurrent.TimeUnit;
|
||||||
|
//import java.util.stream.Collectors;
|
||||||
|
//
|
||||||
|
//@Slf4j
|
||||||
|
//@Service
|
||||||
|
//@RequiredArgsConstructor
|
||||||
|
//public class RepositoryServiceImpl implements RepositoryService {
|
||||||
|
//
|
||||||
|
// private final RepositoryConfigRepository configRepository;
|
||||||
|
//
|
||||||
|
// private final RepositoryGroupRepository groupRepository;
|
||||||
|
//
|
||||||
|
// private final RepositoryProjectRepository projectRepository;
|
||||||
|
//
|
||||||
|
// private final RepositoryBranchRepository branchRepository;
|
||||||
|
//
|
||||||
|
// private final RepositorySyncHistoryRepository syncHistoryRepository;
|
||||||
|
//
|
||||||
|
// private final AsyncTaskExecutor taskExecutor;
|
||||||
|
//
|
||||||
|
// private final PlatformTransactionManager transactionManager;
|
||||||
|
//
|
||||||
|
// // 使用 ConcurrentHashMap 记录正在执行的任务
|
||||||
|
// private static final Map<Long, Boolean> RUNNING_TASKS = new ConcurrentHashMap<>();
|
||||||
|
//
|
||||||
|
// private RestTemplate createRestTemplate() {
|
||||||
|
// return new RestTemplateBuilder()
|
||||||
|
// .setConnectTimeout(Duration.ofSeconds(30))
|
||||||
|
// .setReadTimeout(Duration.ofSeconds(30))
|
||||||
|
// .build();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private HttpEntity<String> createHttpEntity(String accessToken) {
|
||||||
|
// HttpHeaders headers = new HttpHeaders();
|
||||||
|
// headers.set("PRIVATE-TOKEN", accessToken.trim());
|
||||||
|
// headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||||
|
// return new HttpEntity<>(headers);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<RepositoryConfig> listConfigs() {
|
||||||
|
// return configRepository.findByDeletedFalseOrderBySort();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public RepositoryConfig createConfig(RepositoryConfig config) {
|
||||||
|
// if (configRepository.existsByNameAndDeletedFalse(config.getName())) {
|
||||||
|
// throw new ApiException("仓库名称已存在");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!testConnection(config)) {
|
||||||
|
// throw new ApiException("仓库连接测试失败");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return configRepository.save(config);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public RepositoryConfig updateConfig(Long id, RepositoryConfig config) {
|
||||||
|
// RepositoryConfig existing = configRepository.findById(id)
|
||||||
|
// .orElseThrow(() -> new ApiException("仓库配置不存在"));
|
||||||
|
//
|
||||||
|
// if (!existing.getName().equals(config.getName())
|
||||||
|
// && configRepository.existsByNameAndDeletedFalse(config.getName())) {
|
||||||
|
// throw new ApiException("仓库名称已存在");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!existing.getUrl().equals(config.getUrl())
|
||||||
|
// || !existing.getAccessToken().equals(config.getAccessToken())) {
|
||||||
|
// if (!testConnection(config)) {
|
||||||
|
// throw new ApiException("仓库连接测试失败");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// config.setId(id);
|
||||||
|
// config.setVersion(existing.getVersion());
|
||||||
|
// config.setDeleted(existing.getDeleted());
|
||||||
|
// return configRepository.save(config);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public void deleteConfig(Long id) {
|
||||||
|
// RepositoryConfig config = configRepository.findById(id)
|
||||||
|
// .orElseThrow(() -> new ApiException("仓库配置不存在"));
|
||||||
|
//
|
||||||
|
// config.setDeleted(true);
|
||||||
|
// configRepository.save(config);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public boolean testConnection(RepositoryConfig config) {
|
||||||
|
// try {
|
||||||
|
// String url = String.format("%s/api/v4/user", config.getUrl().trim());
|
||||||
|
// ResponseEntity<Map> response = createRestTemplate().exchange(
|
||||||
|
// url,
|
||||||
|
// HttpMethod.GET,
|
||||||
|
// createHttpEntity(config.getAccessToken()),
|
||||||
|
// Map.class
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
|
||||||
|
// log.info("测试连接成功,用户: {}", response.getBody().get("username"));
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.error("测试连接失败: 响应状态码 {}", response.getStatusCode());
|
||||||
|
// return false;
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("测试连接失败: {}", e.getMessage());
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public boolean testConnection(TestConnectionDTO dto) {
|
||||||
|
// try {
|
||||||
|
// String url = String.format("%s/api/v4/user", dto.getUrl().trim());
|
||||||
|
// ResponseEntity<Map> response = createRestTemplate().exchange(
|
||||||
|
// url,
|
||||||
|
// HttpMethod.GET,
|
||||||
|
// createHttpEntity(dto.getAccessToken()),
|
||||||
|
// Map.class
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
|
||||||
|
// log.info("测试连接成功,用户: {}", response.getBody().get("username"));
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.error("测试连接失败: 响应状态码 {}", response.getStatusCode());
|
||||||
|
// return false;
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("测试连接失败: {}", e.getMessage());
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public Long asyncSyncAll(Long repositoryId) {
|
||||||
|
// // 检查是否已有同步任务在运行
|
||||||
|
// if (RUNNING_TASKS.containsKey(repositoryId)) {
|
||||||
|
// throw new ApiException("该仓库已有同步任务正在运行中");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.info("开始异步同步仓库数据, repositoryId: {}", repositoryId);
|
||||||
|
//
|
||||||
|
// RepositoryConfig config = configRepository.findById(repositoryId)
|
||||||
|
// .orElseThrow(() -> new ApiException("仓库配置不存在"));
|
||||||
|
//
|
||||||
|
// // 创建同步历史记录
|
||||||
|
// RepositorySyncHistory history = new RepositorySyncHistory();
|
||||||
|
// history.setRepositoryId(repositoryId);
|
||||||
|
// history.setSyncType(RepositorySyncHistory.SyncType.GROUP);
|
||||||
|
// history.setStartTime(LocalDateTime.now());
|
||||||
|
// history.setStatus(RepositorySyncHistory.SyncStatus.RUNNING);
|
||||||
|
// syncHistoryRepository.save(history);
|
||||||
|
//
|
||||||
|
// // 标记任务开始运行
|
||||||
|
// RUNNING_TASKS.put(repositoryId, true);
|
||||||
|
//
|
||||||
|
// taskExecutor.execute(() -> {
|
||||||
|
// // 用于存储同步过程中的错误信息
|
||||||
|
// StringBuilder errorMessages = new StringBuilder();
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// RestTemplate restTemplate = createRestTemplate();
|
||||||
|
// HttpEntity<String> entity = createHttpEntity(config.getAccessToken());
|
||||||
|
//
|
||||||
|
// // 1. 同步所有数据到内存
|
||||||
|
// RepositorySyncDataCache dataHolder = new RepositorySyncDataCache();
|
||||||
|
//
|
||||||
|
// // 2. 同步群组数据
|
||||||
|
// try {
|
||||||
|
// CompletableFuture<Boolean> groupsFuture = syncGroupsParallel(config, entity, restTemplate, dataHolder);
|
||||||
|
// boolean groupsSuccess = groupsFuture.get(10, TimeUnit.MINUTES);
|
||||||
|
// if (!groupsSuccess) {
|
||||||
|
// errorMessages.append("群组同步失败; ");
|
||||||
|
// }
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("群组同步失败", e);
|
||||||
|
// errorMessages.append("群组同步异常: ").append(e.getMessage()).append("; ");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 3. 如果同步成功,同步项目数据
|
||||||
|
// if (errorMessages.isEmpty() && !dataHolder.getGroups().isEmpty()) {
|
||||||
|
// try {
|
||||||
|
// CompletableFuture<Boolean> projectsFuture = syncProjectsParallel(
|
||||||
|
// config, entity, restTemplate, dataHolder.getGroups().values(), dataHolder);
|
||||||
|
// boolean projectsSuccess = projectsFuture.get(10, TimeUnit.MINUTES);
|
||||||
|
// if (!projectsSuccess) {
|
||||||
|
// errorMessages.append("项目同步失败; ");
|
||||||
|
// }
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("项目同步失败", e);
|
||||||
|
// errorMessages.append("项目同步异常: ").append(e.getMessage()).append("; ");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 4. 如果项目同步成功,同步分支数据
|
||||||
|
// if (errorMessages.isEmpty() && !dataHolder.getProjects().isEmpty()) {
|
||||||
|
// try {
|
||||||
|
// CompletableFuture<Boolean> branchesFuture = syncBranchesParallel(
|
||||||
|
// config, entity, restTemplate, dataHolder.getProjects().values(), dataHolder);
|
||||||
|
// boolean branchesSuccess = branchesFuture.get(10, TimeUnit.MINUTES);
|
||||||
|
// if (!branchesSuccess) {
|
||||||
|
// errorMessages.append("分支同步失败; ");
|
||||||
|
// }
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("分支同步失败", e);
|
||||||
|
// errorMessages.append("分支同步异常: ").append(e.getMessage()).append("; ");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 5. 如果所有数据都同步成功,在新的事务中更新数据库
|
||||||
|
// if (errorMessages.isEmpty()) {
|
||||||
|
// try {
|
||||||
|
// updateDatabaseWithTransaction(repositoryId, dataHolder);
|
||||||
|
// updateSyncHistory(history, RepositorySyncHistory.SyncStatus.SUCCESS, null);
|
||||||
|
// log.info("仓库数据同步完成, repositoryId: {}", repositoryId);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("数据库更新失败", e);
|
||||||
|
// errorMessages.append("数据库更新失败: ").append(e.getMessage());
|
||||||
|
// updateSyncHistory(history, RepositorySyncHistory.SyncStatus.FAILED, errorMessages.toString());
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// updateSyncHistory(history, RepositorySyncHistory.SyncStatus.FAILED, errorMessages.toString());
|
||||||
|
// }
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("仓库数同步失败", e);
|
||||||
|
// updateSyncHistory(history, RepositorySyncHistory.SyncStatus.FAILED, e.getMessage());
|
||||||
|
// } finally {
|
||||||
|
// // 无论成功失败,都移除运行标记
|
||||||
|
// RUNNING_TASKS.remove(repositoryId);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// return history.getId();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 使用编程式事务
|
||||||
|
// private void updateDatabaseWithTransaction(Long repositoryId, RepositorySyncDataCache dataHolder) {
|
||||||
|
// TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||||
|
// transactionTemplate.execute(status -> {
|
||||||
|
// try {
|
||||||
|
// updateDatabase(repositoryId, dataHolder);
|
||||||
|
// return null;
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// status.setRollbackOnly();
|
||||||
|
// throw e;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 实际的数据库更新操作
|
||||||
|
// private void updateDatabase(Long repositoryId, RepositorySyncDataCache dataHolder) {
|
||||||
|
// log.info("开始更新数据库...");
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// // 1. 删除旧数据
|
||||||
|
// log.info("删除旧数据...");
|
||||||
|
// branchRepository.deleteByRepositoryId(repositoryId);
|
||||||
|
// projectRepository.deleteByRepositoryId(repositoryId);
|
||||||
|
// groupRepository.deleteByRepositoryId(repositoryId);
|
||||||
|
//
|
||||||
|
// // 2. 保存新数据
|
||||||
|
// log.info("保存新数据...");
|
||||||
|
// if (!dataHolder.getGroups().isEmpty()) {
|
||||||
|
// groupRepository.saveAll(dataHolder.getGroups().values());
|
||||||
|
// }
|
||||||
|
// if (!dataHolder.getProjects().isEmpty()) {
|
||||||
|
// projectRepository.saveAll(dataHolder.getProjects().values());
|
||||||
|
// }
|
||||||
|
// if (!dataHolder.getBranches().isEmpty()) {
|
||||||
|
// branchRepository.saveAll(dataHolder.getBranches().values());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.info("数据库更新完成");
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("数据库更新失败", e);
|
||||||
|
// throw new ApiException("数据库更新失败: " + e.getMessage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private CompletableFuture<Boolean> syncGroupsParallel(
|
||||||
|
// RepositoryConfig config,
|
||||||
|
// HttpEntity<String> entity,
|
||||||
|
// RestTemplate restTemplate,
|
||||||
|
// RepositorySyncDataCache dataHolder) {
|
||||||
|
//
|
||||||
|
// String groupsUrl = String.format("%s/api/v4/groups?per_page=100", config.getUrl());
|
||||||
|
// try {
|
||||||
|
// log.info("开始同步群组 - 仓库: {}", config.getName());
|
||||||
|
//
|
||||||
|
// ResponseEntity<List<Map<String, Object>>> response = restTemplate.exchange(
|
||||||
|
// groupsUrl,
|
||||||
|
// HttpMethod.GET,
|
||||||
|
// entity,
|
||||||
|
// new ParameterizedTypeReference<>() {}
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.getBody() == null) {
|
||||||
|
// log.warn("未获取到群组数据 - 仓库: {}", config.getName());
|
||||||
|
// return CompletableFuture.completedFuture(true);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (Map<String, Object> group : response.getBody()) {
|
||||||
|
// try {
|
||||||
|
// RepositoryGroup repositoryGroup = createGroup(config.getId(), group);
|
||||||
|
// dataHolder.getGroups().put(repositoryGroup.getGroupId(), repositoryGroup);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("处理群组失败 - 仓库: {}, 群组: {}, 错误: {}",
|
||||||
|
// config.getName(), group.get("name"), e.getMessage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.info("群组同步完成 - 仓库: {}, 总数: {}", config.getName(), response.getBody().size());
|
||||||
|
// return CompletableFuture.completedFuture(true);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("同步群组失败 - 仓库: {}, 错误: {}", config.getName(), e.getMessage());
|
||||||
|
// return CompletableFuture.completedFuture(false);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private CompletableFuture<Boolean> syncProjectsParallel(
|
||||||
|
// RepositoryConfig config,
|
||||||
|
// HttpEntity<String> entity,
|
||||||
|
// RestTemplate restTemplate,
|
||||||
|
// Collection<RepositoryGroup> groups,
|
||||||
|
// RepositorySyncDataCache dataHolder) {
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// log.info("开始同步项目 - 仓库: {}", config.getName());
|
||||||
|
//
|
||||||
|
// for (RepositoryGroup group : groups) {
|
||||||
|
// try {
|
||||||
|
// String projectsUrl = String.format("%s/api/v4/groups/%s/projects?per_page=100",
|
||||||
|
// config.getUrl(), group.getGroupId());
|
||||||
|
//
|
||||||
|
// ResponseEntity<List<Map<String, Object>>> response = restTemplate.exchange(
|
||||||
|
// projectsUrl,
|
||||||
|
// HttpMethod.GET,
|
||||||
|
// entity,
|
||||||
|
// new ParameterizedTypeReference<>() {}
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.getBody() == null) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (Map<String, Object> project : response.getBody()) {
|
||||||
|
// try {
|
||||||
|
// RepositoryProject repositoryProject = createProject(config.getId(), project);
|
||||||
|
// dataHolder.getProjects().put(repositoryProject.getProjectId(), repositoryProject);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("处理项目失败 - 仓库: {}, 项目: {}, 错误: {}",
|
||||||
|
// config.getName(), project.get("name"), e.getMessage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("处理群组项目失败 - 仓库: {}, 群组: {}, 错误: {}",
|
||||||
|
// config.getName(), group.getName(), e.getMessage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.info("项目同步完成 - 仓库: {}", config.getName());
|
||||||
|
// return CompletableFuture.completedFuture(true);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("同步项目失败 - 仓库: {}, 错误: {}", config.getName(), e.getMessage());
|
||||||
|
// return CompletableFuture.completedFuture(false);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private CompletableFuture<Boolean> syncBranchesParallel(
|
||||||
|
// RepositoryConfig config,
|
||||||
|
// HttpEntity<String> entity,
|
||||||
|
// RestTemplate restTemplate,
|
||||||
|
// Collection<RepositoryProject> projects,
|
||||||
|
// RepositorySyncDataCache dataHolder) {
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// log.info("开始同步分支 - 仓库: {}", config.getName());
|
||||||
|
//
|
||||||
|
// for (RepositoryProject project : projects) {
|
||||||
|
// try {
|
||||||
|
// String branchesUrl = String.format("%s/api/v4/projects/%s/repository/branches",
|
||||||
|
// config.getUrl(), project.getProjectId());
|
||||||
|
//
|
||||||
|
// ResponseEntity<List<Map<String, Object>>> response = restTemplate.exchange(
|
||||||
|
// branchesUrl,
|
||||||
|
// HttpMethod.GET,
|
||||||
|
// entity,
|
||||||
|
// new ParameterizedTypeReference<>() {}
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.getBody() == null) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (Map<String, Object> branch : response.getBody()) {
|
||||||
|
// try {
|
||||||
|
// RepositoryBranch repositoryBranch = createBranch(
|
||||||
|
// config.getId(),
|
||||||
|
// project.getProjectId().toString(),
|
||||||
|
// branch
|
||||||
|
// );
|
||||||
|
// dataHolder.getBranches().put(
|
||||||
|
// project.getProjectId() + "-" + repositoryBranch.getName(),
|
||||||
|
// repositoryBranch
|
||||||
|
// );
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("处理分支失败 - 仓库: {}, 项目: {}, 分支: {}, 错误: {}",
|
||||||
|
// config.getName(), project.getName(), branch.get("name"), e.getMessage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("处理项目分支失败 - 仓库: {}, 项目: {}, 错误: {}",
|
||||||
|
// config.getName(), project.getName(), e.getMessage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.info("分支同步完成 - 仓库: {}", config.getName());
|
||||||
|
// return CompletableFuture.completedFuture(true);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("同步分支失败 - 仓库: {}, 错误: {}", config.getName(), e.getMessage());
|
||||||
|
// return CompletableFuture.completedFuture(false);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public RepositorySyncStatusDTO getSyncStatus(Long historyId) {
|
||||||
|
// RepositorySyncHistory history = syncHistoryRepository.findById(historyId)
|
||||||
|
// .orElseThrow(() -> new ApiException("同步历史记录不存在"));
|
||||||
|
//
|
||||||
|
// return RepositorySyncStatusDTO.builder()
|
||||||
|
// .status(history.getStatus())
|
||||||
|
// .startTime(history.getStartTime())
|
||||||
|
// .endTime(history.getEndTime())
|
||||||
|
// .errorMessage(history.getErrorMessage())
|
||||||
|
// .build();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void updateSyncHistory(RepositorySyncHistory history,
|
||||||
|
// RepositorySyncHistory.SyncStatus status,
|
||||||
|
// String errorMessage) {
|
||||||
|
// // 更新同步历史记录
|
||||||
|
// history.setStatus(status);
|
||||||
|
// history.setEndTime(LocalDateTime.now());
|
||||||
|
// history.setErrorMessage(errorMessage);
|
||||||
|
// syncHistoryRepository.save(history);
|
||||||
|
//
|
||||||
|
// // 更新仓库配置的最后同步状态
|
||||||
|
// RepositoryConfig config = configRepository.findById(history.getRepositoryId())
|
||||||
|
// .orElse(null);
|
||||||
|
// if (config != null) {
|
||||||
|
// config.setLastSyncTime(history.getEndTime());
|
||||||
|
// config.setLastSyncStatus(status);
|
||||||
|
// configRepository.save(config);
|
||||||
|
// log.info("更新仓库同步状态 - 仓库: {}, 状态: {}, 时间: {}",
|
||||||
|
// config.getName(), status, history.getEndTime());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 创建群组对象(不保存到数据库)
|
||||||
|
// private RepositoryGroup createGroup(Long repositoryId, Map<String, Object> group) {
|
||||||
|
// RepositoryGroup repositoryGroup = new RepositoryGroup();
|
||||||
|
// repositoryGroup.setRepositoryId(repositoryId);
|
||||||
|
// repositoryGroup.setGroupId(Long.valueOf(group.get("id").toString()));
|
||||||
|
// repositoryGroup.setName((String) group.get("name"));
|
||||||
|
// repositoryGroup.setPath((String) group.get("path"));
|
||||||
|
// repositoryGroup.setDescription((String) group.get("description"));
|
||||||
|
// repositoryGroup.setVisibility((String) group.get("visibility"));
|
||||||
|
// repositoryGroup.setWebUrl((String) group.get("web_url"));
|
||||||
|
// return repositoryGroup;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 创建项目对象(不保存到数据库)
|
||||||
|
// private RepositoryProject createProject(Long repositoryId, Map<String, Object> project) {
|
||||||
|
// RepositoryProject repositoryProject = new RepositoryProject();
|
||||||
|
// repositoryProject.setRepositoryId(repositoryId);
|
||||||
|
// repositoryProject.setProjectId(Long.valueOf(project.get("id").toString()));
|
||||||
|
// repositoryProject.setName((String) project.get("name"));
|
||||||
|
// repositoryProject.setPath((String) project.get("path"));
|
||||||
|
// repositoryProject.setDescription((String) project.get("description"));
|
||||||
|
// repositoryProject.setVisibility((String) project.get("visibility"));
|
||||||
|
// repositoryProject.setDefaultBranch((String) project.get("default_branch"));
|
||||||
|
// repositoryProject.setWebUrl((String) project.get("web_url"));
|
||||||
|
// repositoryProject.setSshUrl((String) project.get("ssh_url_to_repo"));
|
||||||
|
// repositoryProject.setHttpUrl((String) project.get("http_url_to_repo"));
|
||||||
|
// return repositoryProject;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 创建分支对象(不保存到数据库)
|
||||||
|
// private RepositoryBranch createBranch(Long repositoryId, String projectId, Map<String, Object> branch) {
|
||||||
|
// RepositoryBranch repositoryBranch = new RepositoryBranch();
|
||||||
|
// repositoryBranch.setRepositoryId(repositoryId);
|
||||||
|
// repositoryBranch.setProjectId(Long.valueOf(projectId));
|
||||||
|
// repositoryBranch.setName((String) branch.get("name"));
|
||||||
|
//
|
||||||
|
// Map<String, Object> commit = (Map<String, Object>) branch.get("commit");
|
||||||
|
// if (commit != null) {
|
||||||
|
// repositoryBranch.setCommitId((String) commit.get("id"));
|
||||||
|
// repositoryBranch.setCommitMessage((String) commit.get("message"));
|
||||||
|
// repositoryBranch.setCommitAuthor((String) commit.get("author_name"));
|
||||||
|
// String commitDate = (String) commit.get("committed_date");
|
||||||
|
// if (commitDate != null) {
|
||||||
|
// repositoryBranch.setCommitDate(LocalDateTime.parse(commitDate.substring(0, 19)));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// repositoryBranch.setProtected_((Boolean) branch.get("protected"));
|
||||||
|
// repositoryBranch.setDevelopersCanPush((Boolean) branch.get("developers_can_push"));
|
||||||
|
// repositoryBranch.setDevelopersCanMerge((Boolean) branch.get("developers_can_merge"));
|
||||||
|
// return repositoryBranch;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void syncAll(Long repositoryId) {
|
||||||
|
// // 为了保向后兼容,我们调用新的异步方法
|
||||||
|
// asyncSyncAll(repositoryId);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void syncGroups(Long repositoryId) {
|
||||||
|
// // 实现群组同步
|
||||||
|
// log.info("开始同步群组数据, repositoryId: {}", repositoryId);
|
||||||
|
// RepositoryConfig config = configRepository.findById(repositoryId)
|
||||||
|
// .orElseThrow(() -> new ApiException("仓库配置不存在"));
|
||||||
|
//
|
||||||
|
// RestTemplate restTemplate = createRestTemplate();
|
||||||
|
// HttpEntity<String> entity = createHttpEntity(config.getAccessToken());
|
||||||
|
// RepositorySyncDataCache dataHolder = new RepositorySyncDataCache();
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// syncGroupsParallel(config, entity, restTemplate, dataHolder).get(10, TimeUnit.MINUTES);
|
||||||
|
// updateDatabase(repositoryId, dataHolder);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("群组同步失败", e);
|
||||||
|
// throw new ApiException("群组同步失败: " + e.getMessage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void syncProjects(Long repositoryId, Long groupId) {
|
||||||
|
// // 实现项目同步
|
||||||
|
// log.info("开始同步项目数据, repositoryId: {}, groupId: {}", repositoryId, groupId);
|
||||||
|
// RepositoryConfig config = configRepository.findById(repositoryId)
|
||||||
|
// .orElseThrow(() -> new ApiException("仓库配置不存在"));
|
||||||
|
//
|
||||||
|
// RepositoryGroup group = groupRepository.findByRepositoryIdAndGroupId(repositoryId, groupId)
|
||||||
|
// .orElseThrow(() -> new ApiException("群组不存在"));
|
||||||
|
//
|
||||||
|
// RestTemplate restTemplate = createRestTemplate();
|
||||||
|
// HttpEntity<String> entity = createHttpEntity(config.getAccessToken());
|
||||||
|
// RepositorySyncDataCache dataHolder = new RepositorySyncDataCache();
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// List<RepositoryGroup> groups = Collections.singletonList(group);
|
||||||
|
// syncProjectsParallel(config, entity, restTemplate, groups, dataHolder).get(10, TimeUnit.MINUTES);
|
||||||
|
// updateDatabase(repositoryId, dataHolder);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("项目同步失败", e);
|
||||||
|
// throw new ApiException("项目同步失败: " + e.getMessage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void syncBranches(Long repositoryId, Long projectId) {
|
||||||
|
// // 实现分支同步
|
||||||
|
// log.info("开始同步分支数据, repositoryId: {}, projectId: {}", repositoryId, projectId);
|
||||||
|
// RepositoryConfig config = configRepository.findById(repositoryId)
|
||||||
|
// .orElseThrow(() -> new ApiException("仓库配置不存在"));
|
||||||
|
//
|
||||||
|
// RepositoryProject project = projectRepository.findByRepositoryIdAndProjectId(repositoryId, projectId)
|
||||||
|
// .orElseThrow(() -> new ApiException("项目不存在"));
|
||||||
|
//
|
||||||
|
// RestTemplate restTemplate = createRestTemplate();
|
||||||
|
// HttpEntity<String> entity = createHttpEntity(config.getAccessToken());
|
||||||
|
// RepositorySyncDataCache dataHolder = new RepositorySyncDataCache();
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// List<RepositoryProject> projects = Collections.singletonList(project);
|
||||||
|
// syncBranchesParallel(config, entity, restTemplate, projects, dataHolder).get(10, TimeUnit.MINUTES);
|
||||||
|
// updateDatabase(repositoryId, dataHolder);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("分支同步失败", e);
|
||||||
|
// throw new ApiException("分支同步失败: " + e.getMessage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<RunningSyncDTO> getRunningSyncs() {
|
||||||
|
// // 查询最近的50条同步历史记录
|
||||||
|
// List<RepositorySyncHistory> histories = syncHistoryRepository
|
||||||
|
// .findTop50ByOrderByStartTimeDesc();
|
||||||
|
//
|
||||||
|
// return histories.stream()
|
||||||
|
// .map(history -> {
|
||||||
|
// RepositoryConfig config = configRepository
|
||||||
|
// .findById(history.getRepositoryId())
|
||||||
|
// .orElse(null);
|
||||||
|
//
|
||||||
|
// // 检查任务是否真正在运行
|
||||||
|
// boolean isActuallyRunning = RUNNING_TASKS.containsKey(history.getRepositoryId());
|
||||||
|
//
|
||||||
|
// // 如果数据库状态为 RUNNING 但实际未运行,更新状态为 FAILED
|
||||||
|
// if (history.getStatus() == RepositorySyncHistory.SyncStatus.RUNNING
|
||||||
|
// && !isActuallyRunning) {
|
||||||
|
// history.setStatus(RepositorySyncHistory.SyncStatus.FAILED);
|
||||||
|
// history.setErrorMessage("同步任务意外终止");
|
||||||
|
// history.setEndTime(LocalDateTime.now());
|
||||||
|
// syncHistoryRepository.save(history);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return RunningSyncDTO.builder()
|
||||||
|
// .historyId(history.getId())
|
||||||
|
// .repositoryId(history.getRepositoryId())
|
||||||
|
// .repositoryName(config != null ? config.getName() : "未知仓库")
|
||||||
|
// .startTime(history.getStartTime())
|
||||||
|
// .syncType(history.getSyncType().name())
|
||||||
|
// .status(history.getStatus())
|
||||||
|
// .endTime(history.getEndTime())
|
||||||
|
// .errorMessage(history.getErrorMessage())
|
||||||
|
// .isActuallyRunning(isActuallyRunning) // 添加实际运行状态
|
||||||
|
// .build();
|
||||||
|
// })
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // ... 继续实现其他方法吗?
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 仓库同步数据缓存,用于在同步过程中临时存储数据
|
||||||
|
// */
|
||||||
|
// @Getter
|
||||||
|
// private static class RepositorySyncDataCache {
|
||||||
|
// // 使用 ConcurrentHashMap 确保线程安全
|
||||||
|
// private final Map<Long, RepositoryGroup> groups = new ConcurrentHashMap<>();
|
||||||
|
// private final Map<Long, RepositoryProject> projects = new ConcurrentHashMap<>();
|
||||||
|
// private final Map<String, RepositoryBranch> branches = new ConcurrentHashMap<>();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@ -0,0 +1,139 @@
|
|||||||
|
//package com.qqchen.deploy.backend.service.impl;
|
||||||
|
//
|
||||||
|
//import com.qqchen.application.query.RoleQuery;
|
||||||
|
//import com.qqchen.application.service.RoleService;
|
||||||
|
//import com.qqchen.common.exception.ApiException;
|
||||||
|
//import com.qqchen.common.service.impl.BaseServiceImpl;
|
||||||
|
//import com.qqchen.domain.entity.Role;
|
||||||
|
//import com.qqchen.domain.entity.RoleMenu;
|
||||||
|
//import com.qqchen.domain.repository.RoleMenuRepository;
|
||||||
|
//import com.qqchen.domain.repository.RoleRepository;
|
||||||
|
//import lombok.RequiredArgsConstructor;
|
||||||
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
|
//import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
//import org.springframework.stereotype.Service;
|
||||||
|
//import org.springframework.transaction.annotation.Transactional;
|
||||||
|
//
|
||||||
|
//import java.util.List;
|
||||||
|
//import java.util.stream.Collectors;
|
||||||
|
//
|
||||||
|
//@Slf4j
|
||||||
|
//@Service
|
||||||
|
//@RequiredArgsConstructor
|
||||||
|
//public class RoleServiceImpl extends BaseServiceImpl<Role, RoleQuery> implements RoleService {
|
||||||
|
//
|
||||||
|
// private final RoleRepository roleRepository;
|
||||||
|
// private final RoleMenuRepository roleMenuRepository;
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// protected JpaRepository<Role, Long> getRepository() {
|
||||||
|
// return roleRepository;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<Long> getRoleMenuIds(Long roleId) {
|
||||||
|
// return roleMenuRepository.findByRoleId(roleId)
|
||||||
|
// .stream()
|
||||||
|
// .map(RoleMenu::getMenuId)
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public void updateRoleMenus(Long roleId, List<Long> menuIds) {
|
||||||
|
// // 验证角色是否存在
|
||||||
|
// Role role = findById(roleId)
|
||||||
|
// .orElseThrow(() -> new ApiException("角色不存在"));
|
||||||
|
//
|
||||||
|
// // 不允许修改超级管理员的权限
|
||||||
|
// if ("ROLE_ADMIN".equals(role.getCode())) {
|
||||||
|
// throw new ApiException("不能修改超级管理员的权限");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 删除原有权限
|
||||||
|
// roleMenuRepository.deleteByRoleId(roleId);
|
||||||
|
//
|
||||||
|
// // 保存新的权限
|
||||||
|
// if (menuIds != null && !menuIds.isEmpty()) {
|
||||||
|
// List<RoleMenu> roleMenus = menuIds.stream()
|
||||||
|
// .map(menuId -> {
|
||||||
|
// RoleMenu roleMenu = new RoleMenu();
|
||||||
|
// roleMenu.setRoleId(roleId);
|
||||||
|
// roleMenu.setMenuId(menuId);
|
||||||
|
// return roleMenu;
|
||||||
|
// })
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// roleMenuRepository.saveAll(roleMenus);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.info("角色 {} 的权限已更新", role.getName());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public void delete(Long id) {
|
||||||
|
// Role role = findById(id)
|
||||||
|
// .orElseThrow(() -> new ApiException("角色不存在"));
|
||||||
|
//
|
||||||
|
// // 不允许删除超级管理员角色
|
||||||
|
// if ("ROLE_ADMIN".equals(role.getCode())) {
|
||||||
|
// throw new ApiException("不能删除超级管理员角色");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 执行软删除
|
||||||
|
// role.setDeleted(true);
|
||||||
|
// roleRepository.save(role);
|
||||||
|
// log.info("角色 {} 已删除", role.getName());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public Role save(Role entity) {
|
||||||
|
// validateCode(entity.getCode());
|
||||||
|
// validateName(entity.getName());
|
||||||
|
// entity.setDeleted(false);
|
||||||
|
// return super.save(entity);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// @Transactional
|
||||||
|
// public Role update(Long id, Role entity) {
|
||||||
|
// Role existing = findById(id)
|
||||||
|
// .orElseThrow(() -> new ApiException("角色不存在"));
|
||||||
|
//
|
||||||
|
// // 不允许修改超级管理员角色的编码
|
||||||
|
// if ("ROLE_ADMIN".equals(existing.getCode()) && !existing.getCode().equals(entity.getCode())) {
|
||||||
|
// throw new ApiException("不能修改超级管理员角色的编码");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!existing.getCode().equals(entity.getCode())) {
|
||||||
|
// validateCode(entity.getCode());
|
||||||
|
// }
|
||||||
|
// if (!existing.getName().equals(entity.getName())) {
|
||||||
|
// validateName(entity.getName());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// entity.setVersion(existing.getVersion());
|
||||||
|
// entity.setDeleted(existing.getDeleted());
|
||||||
|
// return super.update(id, entity);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void validateCode(String code) {
|
||||||
|
// if (roleRepository.existsByCodeAndDeletedFalse(code)) {
|
||||||
|
// throw new ApiException("角色编码已存在");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void validateName(String name) {
|
||||||
|
// if (roleRepository.existsByNameAndDeletedFalse(name)) {
|
||||||
|
// throw new ApiException("角色名称已存在");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<Role> findAll() {
|
||||||
|
// return roleRepository.findByDeletedFalseOrderBySort();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
//package com.qqchen.deploy.backend.service.impl;
|
||||||
|
//
|
||||||
|
//import com.qqchen.deploy.backend.common.service.impl.BaseServiceImpl;
|
||||||
|
//import com.qqchen.deploy.backend.entity.Tenant;
|
||||||
|
//import com.qqchen.deploy.backend.repository.ITenantRepository;
|
||||||
|
//import com.qqchen.deploy.backend.service.ITenantService;
|
||||||
|
//import com.querydsl.core.BooleanBuilder;
|
||||||
|
//import jakarta.transaction.Transactional;
|
||||||
|
//import org.springframework.stereotype.Service;
|
||||||
|
//import org.springframework.util.StringUtils;
|
||||||
|
//
|
||||||
|
//import java.util.List;
|
||||||
|
//
|
||||||
|
//@Service
|
||||||
|
//@Transactional
|
||||||
|
//public class TenantServiceImpl extends BaseServiceImpl<Tenant, Long> implements ITenantService {
|
||||||
|
//
|
||||||
|
// private final ITenantRepository tenantRepository;
|
||||||
|
//
|
||||||
|
// public TenantServiceImpl(ITenantRepository tenantRepository) {
|
||||||
|
// super(tenantRepository);
|
||||||
|
// this.tenantRepository = tenantRepository;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<Tenant> findAll(TenantQuery query) {
|
||||||
|
// QTenant tenant = QTenant.tenant;
|
||||||
|
// BooleanBuilder builder = new BooleanBuilder();
|
||||||
|
//
|
||||||
|
// // 基础条件:未删除
|
||||||
|
// builder.and(tenant.deleted.eq(false));
|
||||||
|
//
|
||||||
|
// // 动态条件
|
||||||
|
// if (query != null) {
|
||||||
|
// // 名称模糊查询
|
||||||
|
// if (StringUtils.hasText(query.getName())) {
|
||||||
|
// builder.and(tenant.name.like("%" + query.getName().trim() + "%"));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 编码模糊查询
|
||||||
|
// if (StringUtils.hasText(query.getCode())) {
|
||||||
|
// builder.and(tenant.code.like("%" + query.getCode().trim() + "%"));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 状态精确查询
|
||||||
|
// if (query.getEnabled() != null) {
|
||||||
|
// builder.and(tenant.enabled.eq(query.getEnabled()));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 按ID排序
|
||||||
|
// return (List<Tenant>) tenantRepository.findAll(builder, tenant.id.asc());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<Tenant> findAllEnabled() {
|
||||||
|
// QTenant tenant = QTenant.tenant;
|
||||||
|
// return (List<Tenant>) tenantRepository.findAll(
|
||||||
|
// tenant.deleted.eq(false)
|
||||||
|
// .and(tenant.enabled.eq(true)),
|
||||||
|
// tenant.id.asc()
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public Tenant save(Tenant entity) {
|
||||||
|
// validateCode(entity.getCode());
|
||||||
|
// validateName(entity.getName());
|
||||||
|
// entity.setDeleted(false);
|
||||||
|
// return super.save(entity);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public Tenant update(Long id, Tenant entity) {
|
||||||
|
// Tenant existing = findById(id)
|
||||||
|
// .orElseThrow(() -> new ApiException("租户不存在"));
|
||||||
|
//
|
||||||
|
// if (!existing.getCode().equals(entity.getCode())) {
|
||||||
|
// validateCode(entity.getCode());
|
||||||
|
// }
|
||||||
|
// if (!existing.getName().equals(entity.getName())) {
|
||||||
|
// validateName(entity.getName());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// entity.setVersion(existing.getVersion());
|
||||||
|
// entity.setDeleted(existing.getDeleted());
|
||||||
|
// return super.update(id, entity);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void validateCode(String code) {
|
||||||
|
// if (tenantRepository.existsByCodeAndDeletedFalse(code)) {
|
||||||
|
// throw new ApiException("租户编码已存在");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void validateName(String name) {
|
||||||
|
// if (tenantRepository.existsByNameAndDeletedFalse(name)) {
|
||||||
|
// throw new ApiException("租户名称已存在");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public boolean existsByCode(String code) {
|
||||||
|
// return tenantRepository.existsByCodeAndDeletedFalse(code);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public boolean existsByName(String name) {
|
||||||
|
// return tenantRepository.existsByNameAndDeletedFalse(name);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.qqchen.deploy.backend.service.impl;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.entity.User;
|
||||||
|
import com.qqchen.deploy.backend.repository.IUserRepository;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private final IUserRepository userRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
User user = userRepository.findByUsernameAndDeletedFalse(username)
|
||||||
|
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
|
||||||
|
|
||||||
|
return new org.springframework.security.core.userdetails.User(
|
||||||
|
user.getUsername(),
|
||||||
|
user.getPassword(),
|
||||||
|
user.getEnabled(),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,20 +2,20 @@ package com.qqchen.deploy.backend.service.impl;
|
|||||||
|
|
||||||
import com.qqchen.deploy.backend.common.service.impl.BaseServiceImpl;
|
import com.qqchen.deploy.backend.common.service.impl.BaseServiceImpl;
|
||||||
import com.qqchen.deploy.backend.entity.User;
|
import com.qqchen.deploy.backend.entity.User;
|
||||||
import com.qqchen.deploy.backend.repository.UserRepository;
|
import com.qqchen.deploy.backend.repository.IUserRepository;
|
||||||
import com.qqchen.deploy.backend.service.UserService;
|
import com.qqchen.deploy.backend.service.IUserService;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional
|
@Transactional
|
||||||
public class UserServiceImpl extends BaseServiceImpl<User, Long> implements UserService {
|
public class UserServiceImpl extends BaseServiceImpl<User, Long> implements IUserService {
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
private final IUserRepository userRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public UserServiceImpl(UserRepository userRepository) {
|
public UserServiceImpl(IUserRepository userRepository) {
|
||||||
super(userRepository);
|
super(userRepository);
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,9 +3,9 @@ server:
|
|||||||
|
|
||||||
spring:
|
spring:
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://192.168.1.111:3306/deploy-ease-platform?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
|
url: jdbc:mysql://127.0.0.1:3306/2024_11_24_platform?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
|
||||||
username: deploy-ease-platform
|
username: root
|
||||||
password: qichen5210523
|
password: root
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
jpa:
|
jpa:
|
||||||
hibernate:
|
hibernate:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user