From ac5a30844e2f0839e5407c9582e1cf378924626d Mon Sep 17 00:00:00 2001 From: CloseIn Date: Thu, 7 May 2026 15:54:19 +0800 Subject: [PATCH] init --- .gitignore | 33 ++++++++++ Dockerfile | 15 +++++ deploy/build.ps1 | 15 +++++ deploy/build.sh | 19 ++++++ deploy/run.ps1 | 14 +++++ deploy/run.sh | 16 +++++ pom.xml | 60 +++++++++++++++++++ .../java/com/gmp/mock/MockApplication.java | 12 ++++ .../gmp/mock/controller/DocxController.java | 36 +++++++++++ .../java/com/gmp/mock/dto/ApiResponse.java | 50 ++++++++++++++++ .../java/com/gmp/mock/dto/DocxExportData.java | 28 +++++++++ .../com/gmp/mock/dto/DocxExportRequest.java | 31 ++++++++++ .../mock/handler/GlobalExceptionHandler.java | 22 +++++++ .../com/gmp/mock/service/DocxService.java | 37 ++++++++++++ .../gmp/mock/template/TemplateProvider.java | 14 +++++ .../gmp/mock/template/TemplateRegistry.java | 27 +++++++++ .../annualReport/AnnualReportProvider.java | 26 ++++++++ src/main/resources/application.yml | 6 ++ 18 files changed, 461 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 deploy/build.ps1 create mode 100644 deploy/build.sh create mode 100644 deploy/run.ps1 create mode 100644 deploy/run.sh create mode 100644 pom.xml create mode 100644 src/main/java/com/gmp/mock/MockApplication.java create mode 100644 src/main/java/com/gmp/mock/controller/DocxController.java create mode 100644 src/main/java/com/gmp/mock/dto/ApiResponse.java create mode 100644 src/main/java/com/gmp/mock/dto/DocxExportData.java create mode 100644 src/main/java/com/gmp/mock/dto/DocxExportRequest.java create mode 100644 src/main/java/com/gmp/mock/handler/GlobalExceptionHandler.java create mode 100644 src/main/java/com/gmp/mock/service/DocxService.java create mode 100644 src/main/java/com/gmp/mock/template/TemplateProvider.java create mode 100644 src/main/java/com/gmp/mock/template/TemplateRegistry.java create mode 100644 src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java create mode 100644 src/main/resources/application.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3d689e --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.MD +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d4834ed --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM eclipse-temurin:25-jre-alpine + +RUN addgroup -S appgroup && adduser -S appuser -G appgroup + +WORKDIR /app + +COPY target/gmp-mock-service.jar app.jar + +RUN chown -R appuser:appgroup /app + +USER appuser + +EXPOSE 8080 + +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/deploy/build.ps1 b/deploy/build.ps1 new file mode 100644 index 0000000..90054eb --- /dev/null +++ b/deploy/build.ps1 @@ -0,0 +1,15 @@ +Write-Host "===== 开始构建 gmp-mock-service =====" -ForegroundColor Cyan + +mvn clean package -DskipTests + +if ($LASTEXITCODE -eq 0) { + Write-Host "===== 构建成功 =====" -ForegroundColor Green + $jarPath = "target\gmp-mock-service.jar" + if (Test-Path $jarPath) { + $size = [math]::Round((Get-Item $jarPath).Length / 1MB, 2) + Write-Host "JAR 文件: $jarPath ($size MB)" -ForegroundColor Green + } +} else { + Write-Host "===== 构建失败 =====" -ForegroundColor Red + exit 1 +} diff --git a/deploy/build.sh b/deploy/build.sh new file mode 100644 index 0000000..67345e9 --- /dev/null +++ b/deploy/build.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +echo "===== 开始构建 gmp-mock-service =====" + +mvn clean package -DskipTests + +if [ $? -eq 0 ]; then + echo "===== 构建成功 =====" + JAR_PATH="target/gmp-mock-service.jar" + if [ -f "$JAR_PATH" ]; then + SIZE=$(du -h "$JAR_PATH" | cut -f1) + echo "JAR 文件: $JAR_PATH ($SIZE)" + fi +else + echo "===== 构建失败 =====" + exit 1 +fi diff --git a/deploy/run.ps1 b/deploy/run.ps1 new file mode 100644 index 0000000..089e01c --- /dev/null +++ b/deploy/run.ps1 @@ -0,0 +1,14 @@ +$jarPath = "target\gmp-mock-service.jar" + +if (-not (Test-Path $jarPath)) { + Write-Host "JAR 文件不存在,请先执行 build.ps1 进行构建" -ForegroundColor Red + exit 1 +} + +$env:JAVA_OPTS = $env:JAVA_OPTS ?? "-Xms256m -Xmx512m" + +Write-Host "===== 启动 gmp-mock-service =====" -ForegroundColor Cyan +Write-Host "端口: 8080" -ForegroundColor Cyan +Write-Host "接口: POST http://localhost:8080/api/docx/export" -ForegroundColor Cyan + +java $env:JAVA_OPTS -jar $jarPath diff --git a/deploy/run.sh b/deploy/run.sh new file mode 100644 index 0000000..8741236 --- /dev/null +++ b/deploy/run.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +JAR_PATH="target/gmp-mock-service.jar" + +if [ ! -f "$JAR_PATH" ]; then + echo "JAR 文件不存在,请先执行 build.sh 进行构建" + exit 1 +fi + +JAVA_OPTS="${JAVA_OPTS:--Xms256m -Xmx512m}" + +echo "===== 启动 gmp-mock-service =====" +echo "端口: 8080" +echo "接口: POST http://localhost:8080/api/docx/export" + +java $JAVA_OPTS -jar "$JAR_PATH" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..879a6ce --- /dev/null +++ b/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.5.1 + + + + com.gmp + gmp-service + 1.0.0 + jar + gmp-mock-service + GMP Mock Service - 提供简单接口的模拟服务 + + + 25 + 5.4.0 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/src/main/java/com/gmp/mock/MockApplication.java b/src/main/java/com/gmp/mock/MockApplication.java new file mode 100644 index 0000000..c2da927 --- /dev/null +++ b/src/main/java/com/gmp/mock/MockApplication.java @@ -0,0 +1,12 @@ +package com.gmp.mock; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MockApplication { + + public static void main(String[] args) { + SpringApplication.run(MockApplication.class, args); + } +} diff --git a/src/main/java/com/gmp/mock/controller/DocxController.java b/src/main/java/com/gmp/mock/controller/DocxController.java new file mode 100644 index 0000000..c5cf96b --- /dev/null +++ b/src/main/java/com/gmp/mock/controller/DocxController.java @@ -0,0 +1,36 @@ +package com.gmp.mock.controller; + +import java.io.IOException; + +import jakarta.validation.Valid; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.gmp.mock.dto.ApiResponse; +import com.gmp.mock.dto.DocxExportData; +import com.gmp.mock.dto.DocxExportRequest; +import com.gmp.mock.service.DocxService; + +@RestController +@RequestMapping("/api/docx") +public class DocxController { + + private final DocxService docxService; + + public DocxController(DocxService docxService) { + this.docxService = docxService; + } + + @PostMapping("/export") + public ResponseEntity> exportDocx(@Valid @RequestBody DocxExportRequest request) throws IOException { + DocxExportData data = docxService.export(request.getExportId()); + if (data == null) { + return ResponseEntity.ok(ApiResponse.error(404, "模板不存在")); + } + return ResponseEntity.ok(ApiResponse.success(data)); + } +} diff --git a/src/main/java/com/gmp/mock/dto/ApiResponse.java b/src/main/java/com/gmp/mock/dto/ApiResponse.java new file mode 100644 index 0000000..80b5183 --- /dev/null +++ b/src/main/java/com/gmp/mock/dto/ApiResponse.java @@ -0,0 +1,50 @@ +package com.gmp.mock.dto; + +public class ApiResponse { + + private int code; + private T data; + private String msg; + + private ApiResponse(int code, T data, String msg) { + this.code = code; + this.data = data; + this.msg = msg; + } + + public static ApiResponse success(T data) { + return new ApiResponse<>(0, data, "success"); + } + + public static ApiResponse success(T data, String msg) { + return new ApiResponse<>(0, data, msg); + } + + public static ApiResponse error(int code, String msg) { + return new ApiResponse<>(code, null, msg); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/src/main/java/com/gmp/mock/dto/DocxExportData.java b/src/main/java/com/gmp/mock/dto/DocxExportData.java new file mode 100644 index 0000000..cfa46f3 --- /dev/null +++ b/src/main/java/com/gmp/mock/dto/DocxExportData.java @@ -0,0 +1,28 @@ +package com.gmp.mock.dto; + +public class DocxExportData { + + private String fileName; + private String fileBase64; + + public DocxExportData(String fileName, String fileBase64) { + this.fileName = fileName; + this.fileBase64 = fileBase64; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getFileBase64() { + return fileBase64; + } + + public void setFileBase64(String fileBase64) { + this.fileBase64 = fileBase64; + } +} diff --git a/src/main/java/com/gmp/mock/dto/DocxExportRequest.java b/src/main/java/com/gmp/mock/dto/DocxExportRequest.java new file mode 100644 index 0000000..12a9ca4 --- /dev/null +++ b/src/main/java/com/gmp/mock/dto/DocxExportRequest.java @@ -0,0 +1,31 @@ +package com.gmp.mock.dto; + +import java.util.Map; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class DocxExportRequest { + + @NotBlank(message = "exportId 不能为空") + private String exportId; + + @NotNull(message = "businessData 不能为空") + private Map businessData; + + public String getExportId() { + return exportId; + } + + public void setExportId(String exportId) { + this.exportId = exportId; + } + + public Map getBusinessData() { + return businessData; + } + + public void setBusinessData(Map businessData) { + this.businessData = businessData; + } +} diff --git a/src/main/java/com/gmp/mock/handler/GlobalExceptionHandler.java b/src/main/java/com/gmp/mock/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..df42315 --- /dev/null +++ b/src/main/java/com/gmp/mock/handler/GlobalExceptionHandler.java @@ -0,0 +1,22 @@ +package com.gmp.mock.handler; + +import java.util.stream.Collectors; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import com.gmp.mock.dto.ApiResponse; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleValidation(MethodArgumentNotValidException ex) { + String message = ex.getBindingResult().getFieldErrors().stream() + .map(e -> e.getDefaultMessage()) + .collect(Collectors.joining("; ")); + return ResponseEntity.ok(ApiResponse.error(400, message)); + } +} diff --git a/src/main/java/com/gmp/mock/service/DocxService.java b/src/main/java/com/gmp/mock/service/DocxService.java new file mode 100644 index 0000000..a546ab9 --- /dev/null +++ b/src/main/java/com/gmp/mock/service/DocxService.java @@ -0,0 +1,37 @@ +package com.gmp.mock.service; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Base64; + +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.springframework.stereotype.Service; + +import com.gmp.mock.dto.DocxExportData; +import com.gmp.mock.template.TemplateProvider; +import com.gmp.mock.template.TemplateRegistry; + +@Service +public class DocxService { + + private final TemplateRegistry templateRegistry; + + public DocxService(TemplateRegistry templateRegistry) { + this.templateRegistry = templateRegistry; + } + + public DocxExportData export(String exportId) throws IOException { + TemplateProvider provider = templateRegistry.get(exportId); + if (provider == null) { + return null; + } + + try (XWPFDocument document = new XWPFDocument(); + ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + provider.generate(document); + document.write(baos); + String base64 = Base64.getEncoder().encodeToString(baos.toByteArray()); + return new DocxExportData(provider.getFileName(), base64); + } + } +} diff --git a/src/main/java/com/gmp/mock/template/TemplateProvider.java b/src/main/java/com/gmp/mock/template/TemplateProvider.java new file mode 100644 index 0000000..6a034fb --- /dev/null +++ b/src/main/java/com/gmp/mock/template/TemplateProvider.java @@ -0,0 +1,14 @@ +package com.gmp.mock.template; + +import java.io.IOException; + +import org.apache.poi.xwpf.usermodel.XWPFDocument; + +public interface TemplateProvider { + + String getTemplateId(); + + String getFileName(); + + void generate(XWPFDocument document) throws IOException; +} diff --git a/src/main/java/com/gmp/mock/template/TemplateRegistry.java b/src/main/java/com/gmp/mock/template/TemplateRegistry.java new file mode 100644 index 0000000..f82b102 --- /dev/null +++ b/src/main/java/com/gmp/mock/template/TemplateRegistry.java @@ -0,0 +1,27 @@ +package com.gmp.mock.template; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Component; + +@Component +public class TemplateRegistry { + + private final Map providers; + + public TemplateRegistry(List providers) { + this.providers = providers.stream() + .collect(Collectors.toMap(TemplateProvider::getTemplateId, Function.identity())); + } + + public TemplateProvider get(String templateId) { + return providers.get(templateId); + } + + public boolean exists(String templateId) { + return providers.containsKey(templateId); + } +} diff --git a/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java b/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java new file mode 100644 index 0000000..a0819e0 --- /dev/null +++ b/src/main/java/com/gmp/mock/template/annualReport/AnnualReportProvider.java @@ -0,0 +1,26 @@ +package com.gmp.mock.template.annualReport; + +import java.io.IOException; + +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.springframework.stereotype.Component; + +import com.gmp.mock.template.TemplateProvider; + +@Component +public class AnnualReportProvider implements TemplateProvider { + + @Override + public String getTemplateId() { + return "annualReport"; + } + + @Override + public String getFileName() { + return "annualReport.docx"; + } + + @Override + public void generate(XWPFDocument document) throws IOException { + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..e746ec2 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,6 @@ +server: + port: 8080 + +spring: + application: + name: gmp-mock-service