From 89cdecef3dd7e46e18c16d064e82c30a80339623 Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Thu, 14 Aug 2025 19:05:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=AE=80=E5=8D=95?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 + .../controller/SimpleControllerSupport.java | 17 +- .../service/template/entity/SimpleEntity.java | 2 +- .../service/SimpleServiceSupport.java | 24 ++- .../service/template/TestApplication.java | 111 ++++++++++++ .../controller/CompanyController.java | 158 ++++++++++++++++++ .../service/template/entity/Company.java | 31 ++++ .../repository/CompanyRepository.java | 12 ++ .../template/service/CompanyService.java | 17 ++ src/test/resources/application.yml | 15 ++ 10 files changed, 376 insertions(+), 17 deletions(-) create mode 100644 src/test/java/com/lanyuanxiaoyao/service/template/TestApplication.java create mode 100644 src/test/java/com/lanyuanxiaoyao/service/template/controller/CompanyController.java create mode 100644 src/test/java/com/lanyuanxiaoyao/service/template/entity/Company.java create mode 100644 src/test/java/com/lanyuanxiaoyao/service/template/repository/CompanyRepository.java create mode 100644 src/test/java/com/lanyuanxiaoyao/service/template/service/CompanyService.java create mode 100644 src/test/resources/application.yml diff --git a/pom.xml b/pom.xml index d28a5b4..e6fdf7c 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,12 @@ fenix-spring-boot-starter 3.0.0 + + + com.h2database + h2 + test + diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/controller/SimpleControllerSupport.java b/src/main/java/com/lanyuanxiaoyao/service/template/controller/SimpleControllerSupport.java index e6118df..77dc4a1 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/controller/SimpleControllerSupport.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/controller/SimpleControllerSupport.java @@ -6,7 +6,6 @@ import com.lanyuanxiaoyao.service.template.entity.SimpleEntity; import com.lanyuanxiaoyao.service.template.helper.ObjectHelper; import com.lanyuanxiaoyao.service.template.service.SimpleServiceSupport; import java.util.List; -import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -22,16 +21,17 @@ public abstract class SimpleControllerSupport save(@RequestBody SAVE_ITEM item) throws Exception { - SaveItemMapper mapper = saveItemMapper(); + var mapper = saveItemMapper(); return GlobalResponse.responseSuccess(service.save(mapper.from(item))); } @GetMapping(LIST) @Override public GlobalCrudResponse list() throws Exception { - ListItemMapper mapper = listItemMapper(); + var mapper = listItemMapper(); + var result = service.list(); return GlobalCrudResponse.responseCrudData( - service.list() + result .stream() .map(entity -> { try { @@ -40,7 +40,8 @@ public abstract class SimpleControllerSupport mapper = listItemMapper(); - Page result = service.list(query); + var mapper = listItemMapper(); + var result = service.list(query); return GlobalCrudResponse.responseCrudData( result.get() .map(entity -> { @@ -69,7 +70,7 @@ public abstract class SimpleControllerSupport detail(@PathVariable("id") Long id) throws Exception { - DetailItemMapper mapper = detailItemMapper(); + var mapper = detailItemMapper(); return GlobalResponse.responseSuccess(mapper.from(service.detailOrThrow(id))); } diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/entity/SimpleEntity.java b/src/main/java/com/lanyuanxiaoyao/service/template/entity/SimpleEntity.java index bfbb859..b839a2c 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/entity/SimpleEntity.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/entity/SimpleEntity.java @@ -39,6 +39,6 @@ public class SimpleEntity extends IdOnlyEntity { return "SimpleEntity{" + "createdTime=" + createdTime + ", modifiedTime=" + modifiedTime + - '}'; + "} " + super.toString(); } } diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java b/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java index 346304a..ac303a3 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java @@ -14,12 +14,15 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; public abstract class SimpleServiceSupport implements SimpleService { + private static final Logger log = LoggerFactory.getLogger(SimpleServiceSupport.class); private static final Integer DEFAULT_PAGE_INDEX = 1; private static final Integer DEFAULT_PAGE_SIZE = 10; protected final SimpleRepository repository; @@ -31,7 +34,7 @@ public abstract class SimpleServiceSupport implemen @Transactional(rollbackOn = Throwable.class) @Override public Long save(ENTITY entity) { - if (!ObjectHelper.isNull(entity.getId())) { + if (ObjectHelper.isNotNull(entity.getId())) { var id = entity.getId(); var targetEntity = repository.findById(entity.getId()).orElseThrow(() -> new IdNotFoundException(id)); BeanUtils.copyProperties(entity, targetEntity, "id", "created_time", "modified_time"); @@ -82,7 +85,7 @@ public abstract class SimpleServiceSupport implemen if (ObjectHelper.isEmpty(column)) { throw new IllegalArgumentException("Column cannot be blank"); } - String[] columns = column.split("/"); + var columns = column.split("/"); Path path = root.get(columns[0]); for (int i = 1; i < columns.length; i++) { path = path.get(columns[i]); @@ -92,15 +95,20 @@ public abstract class SimpleServiceSupport implemen @SuppressWarnings({"unchecked", "rawtypes"}) private Object value(Path column, Object value) { - Class javaType = column.getJavaType(); + var javaType = column.getJavaType(); if (javaType.isEnum()) { - return Enum.valueOf((Class) javaType, (String) value); + if (value instanceof String enumName) { + var enumType = (Class) javaType; + return Enum.valueOf(enumType, enumName); + } else { + throw new IllegalArgumentException("枚举类型字段需要String类型的值"); + } } return value; } protected List queryPredicates(Query.Queryable queryable, Root root, CriteriaQuery query, CriteriaBuilder builder) { - List predicates = new ArrayList<>(); + var predicates = new ArrayList(); if (ObjectHelper.isNull(queryable)) { return predicates; } @@ -118,13 +126,13 @@ public abstract class SimpleServiceSupport implemen } if (ObjectHelper.isNotEmpty(queryable.getEqual())) { queryable.getEqual().forEach((column, value) -> { - Path path = column(root, column); + var path = column(root, column); predicates.add(builder.equal(path, value(path, value))); }); } if (ObjectHelper.isNotEmpty(queryable.getNotEqual())) { queryable.getEqual().forEach((column, value) -> { - Path path = column(root, column); + var path = column(root, column); predicates.add(builder.notEqual(path, value(path, value))); }); } @@ -167,7 +175,7 @@ public abstract class SimpleServiceSupport implemen @Override public Page list(Query listQuery) { - PageRequest pageRequest = PageRequest.of(DEFAULT_PAGE_INDEX - 1, DEFAULT_PAGE_SIZE, Sort.by("created_time").descending()); + var pageRequest = PageRequest.of(DEFAULT_PAGE_INDEX - 1, DEFAULT_PAGE_SIZE, Sort.by("created_time").descending()); if (ObjectHelper.isNotNull(listQuery.getPage())) { pageRequest = PageRequest.of( ObjectHelper.defaultIfNull(listQuery.getPage().getIndex(), DEFAULT_PAGE_INDEX) - 1, diff --git a/src/test/java/com/lanyuanxiaoyao/service/template/TestApplication.java b/src/test/java/com/lanyuanxiaoyao/service/template/TestApplication.java new file mode 100644 index 0000000..301a806 --- /dev/null +++ b/src/test/java/com/lanyuanxiaoyao/service/template/TestApplication.java @@ -0,0 +1,111 @@ +package com.lanyuanxiaoyao.service.template; + +import com.blinkfox.fenix.EnableFenix; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.util.Assert; +import org.springframework.web.client.RestTemplate; + +/** + * @author lanyuanxiaoyao + * @version 20250814 + */ +@SpringBootApplication +@EnableFenix +@EnableJpaAuditing +public class TestApplication { + private static final Logger log = LoggerFactory.getLogger(TestApplication.class); + private static final String BASE_URL = "http://localhost:2490"; + private static final RestTemplate REST_CLIENT = new RestTemplate(); + private static final ObjectMapper MAPPER = new ObjectMapper(); + + public static void main(String[] args) { + SpringApplication.run(TestApplication.class, args); + } + + @EventListener(ApplicationReadyEvent.class) + public void runTests() throws JsonProcessingException { + // 增 + var cid1 = saveItem("company", "{\"name\": \"Apple\"}").get("data").asLong(); + var cid2 = saveItem("company", "{\"name\": \"Banana\"}").get("data").asLong(); + var cid3 = saveItem("company", "{\"name\": \"Cheery\"}").get("data").asLong(); + + // 查 + var companies = listItems("company"); + Assert.isTrue(companies.at("/data/items").size() == 3, "数量错误"); + Assert.isTrue(companies.at("/data/total").asLong() == 3, "返回数量错误"); + + var company1 = detailItem("company", cid1); + Assert.isTrue(cid1 == company1.at("/data/id").asLong(), "id错误"); + Assert.isTrue("Apple".equals(company1.at("/data/name").asText()), "name错误"); + + // 改 + var cid4 = saveItem("company", "{\"id\": %d, \"name\": \"Dog\"}".formatted(cid2)).get("data").asLong(); + Assert.isTrue(cid2 == cid4, "id错误"); + var company2 = detailItem("company", cid2); + Assert.isTrue("Dog".equals(company2.at("/data/name").asText()), "name错误"); + + // 删 + removeItem("company", cid3); + Assert.isTrue(listItems("company").at("/data/items").size() == 2, "数量错误"); + Assert.isTrue(listItems("company").at("/data/total").asLong() == 2, "返回数量错误"); + + System.exit(0); + } + + private HttpHeaders headers() { + var headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + return headers; + } + + private JsonNode saveItem(String path, String body) throws JsonProcessingException { + var response = REST_CLIENT.postForEntity( + "%s/%s/save".formatted(BASE_URL, path), + new HttpEntity<>(body, headers()), + String.class + ); + Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "请求失败"); + Assert.notNull(response.getBody(), "请求失败"); + return MAPPER.readTree(response.getBody()); + } + + private JsonNode listItems(String path) throws JsonProcessingException { + var response = REST_CLIENT.getForEntity( + "%s/%s/list".formatted(BASE_URL, path), + String.class + ); + Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "请求失败"); + Assert.notNull(response.getBody(), "请求失败"); + return MAPPER.readTree(response.getBody()); + } + + private JsonNode detailItem(String path, Long id) throws JsonProcessingException { + var response = REST_CLIENT.getForEntity( + "%s/%s/detail/%d".formatted(BASE_URL, path, id), + String.class + ); + Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "请求失败"); + Assert.notNull(response.getBody(), "请求失败"); + return MAPPER.readTree(response.getBody()); + } + + private void removeItem(String path, Long id) { + var response = REST_CLIENT.getForEntity( + "%s/%s/remove/%d".formatted(BASE_URL, path, id), + Void.class + ); + Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "请求失败"); + } +} diff --git a/src/test/java/com/lanyuanxiaoyao/service/template/controller/CompanyController.java b/src/test/java/com/lanyuanxiaoyao/service/template/controller/CompanyController.java new file mode 100644 index 0000000..c92ec0a --- /dev/null +++ b/src/test/java/com/lanyuanxiaoyao/service/template/controller/CompanyController.java @@ -0,0 +1,158 @@ +package com.lanyuanxiaoyao.service.template.controller; + +import com.lanyuanxiaoyao.service.template.entity.Company; +import com.lanyuanxiaoyao.service.template.service.CompanyService; +import java.time.LocalDateTime; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author lanyuanxiaoyao + * @version 20250814 + */ +@RestController +@RequestMapping("company") +public class CompanyController extends SimpleControllerSupport { + public CompanyController(CompanyService service) { + super(service); + } + + @Override + protected SaveItemMapper saveItemMapper() { + return item -> { + var company = new Company(); + company.setId(item.getId()); + company.setName(item.getName()); + return company; + }; + } + + @Override + protected ListItemMapper listItemMapper() { + return company -> { + var item = new ListItem(); + item.setId(company.getId()); + item.setName(company.getName()); + return item; + }; + } + + @Override + protected DetailItemMapper detailItemMapper() { + return company -> { + var item = new DetailItem(); + item.setId(company.getId()); + item.setName(company.getName()); + item.setCreatedTime(company.getCreatedTime()); + item.setModifiedTime(company.getModifiedTime()); + return item; + }; + } + + public static class SaveItem { + private Long id; + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "SaveItem{" + + "id=" + id + + ", name='" + name + '\'' + + '}'; + } + } + + public static class ListItem { + private Long id; + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "ListItem{" + + "id=" + id + + ", name='" + name + '\'' + + '}'; + } + } + + public static class DetailItem { + private Long id; + private String name; + private LocalDateTime createdTime; + private LocalDateTime modifiedTime; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public LocalDateTime getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(LocalDateTime createdTime) { + this.createdTime = createdTime; + } + + public LocalDateTime getModifiedTime() { + return modifiedTime; + } + + public void setModifiedTime(LocalDateTime modifiedTime) { + this.modifiedTime = modifiedTime; + } + + @Override + public String toString() { + return "DetailItem{" + + "id=" + id + + ", name='" + name + '\'' + + ", createdTime=" + createdTime + + ", modifiedTime=" + modifiedTime + + '}'; + } + } +} diff --git a/src/test/java/com/lanyuanxiaoyao/service/template/entity/Company.java b/src/test/java/com/lanyuanxiaoyao/service/template/entity/Company.java new file mode 100644 index 0000000..321c36f --- /dev/null +++ b/src/test/java/com/lanyuanxiaoyao/service/template/entity/Company.java @@ -0,0 +1,31 @@ +package com.lanyuanxiaoyao.service.template.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import org.hibernate.annotations.Comment; +import org.hibernate.annotations.DynamicUpdate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + + +@Entity +@DynamicUpdate +@EntityListeners(AuditingEntityListener.class) +@Comment("企业") +public class Company extends SimpleEntity { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "Company{" + + "name='" + name + '\'' + + "} " + super.toString(); + } +} diff --git a/src/test/java/com/lanyuanxiaoyao/service/template/repository/CompanyRepository.java b/src/test/java/com/lanyuanxiaoyao/service/template/repository/CompanyRepository.java new file mode 100644 index 0000000..8c374f8 --- /dev/null +++ b/src/test/java/com/lanyuanxiaoyao/service/template/repository/CompanyRepository.java @@ -0,0 +1,12 @@ +package com.lanyuanxiaoyao.service.template.repository; + +import com.lanyuanxiaoyao.service.template.entity.Company; +import org.springframework.stereotype.Repository; + +/** + * @author lanyuanxiaoyao + * @version 20250814 + */ +@Repository +public interface CompanyRepository extends SimpleRepository { +} diff --git a/src/test/java/com/lanyuanxiaoyao/service/template/service/CompanyService.java b/src/test/java/com/lanyuanxiaoyao/service/template/service/CompanyService.java new file mode 100644 index 0000000..7376712 --- /dev/null +++ b/src/test/java/com/lanyuanxiaoyao/service/template/service/CompanyService.java @@ -0,0 +1,17 @@ +package com.lanyuanxiaoyao.service.template.service; + +import com.lanyuanxiaoyao.service.template.entity.Company; +import com.lanyuanxiaoyao.service.template.repository.CompanyRepository; +import com.lanyuanxiaoyao.service.template.repository.SimpleRepository; +import org.springframework.stereotype.Service; + +/** + * @author lanyuanxiaoyao + * @version 20250814 + */ +@Service +public class CompanyService extends SimpleServiceSupport { + public CompanyService(CompanyRepository repository) { + super(repository); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml new file mode 100644 index 0000000..41dfaaa --- /dev/null +++ b/src/test/resources/application.yml @@ -0,0 +1,15 @@ +server: + port: 2490 +spring: + application: + name: Test + datasource: + url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 + username: test + password: test + driver-class-name: org.h2.Driver + jpa: + show-sql: true + generate-ddl: true +fenix: + print-banner: false