新增接口文档

This commit is contained in:
super
2026-03-20 19:31:02 +08:00
parent 7efb86ed3a
commit d39d310544
7 changed files with 101 additions and 17 deletions

View File

@@ -17,7 +17,6 @@
<properties> <properties>
<java.version>21</java.version> <java.version>21</java.version>
<springdoc.version>2.6.0</springdoc.version>
<knife4j.version>4.5.0</knife4j.version> <knife4j.version>4.5.0</knife4j.version>
<mybatis-plus.version>3.5.7</mybatis-plus.version> <mybatis-plus.version>3.5.7</mybatis-plus.version>
<mapstruct.version>1.5.5.Final</mapstruct.version> <mapstruct.version>1.5.5.Final</mapstruct.version>
@@ -63,11 +62,6 @@
<artifactId>aliyun-sdk-oss</artifactId> <artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun.oss.version}</version> <version>${aliyun.oss.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.github.xiaoymin</groupId> <groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>

View File

@@ -6,6 +6,10 @@ import com.nanri.aiimage.modules.convert.model.vo.ConvertHistoryVo;
import com.nanri.aiimage.modules.convert.model.vo.ConvertRunVo; import com.nanri.aiimage.modules.convert.model.vo.ConvertRunVo;
import com.nanri.aiimage.modules.convert.service.ConvertRunService; import com.nanri.aiimage.modules.convert.service.ConvertRunService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -52,12 +56,20 @@ public class ConvertRunController {
输出文件名遵循模板配置的 outputFilename若临时目录中重名则自动追加序号。 输出文件名遵循模板配置的 outputFilename若临时目录中重名则自动追加序号。
""") """)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "执行成功,返回转换结果统计", content = @Content(schema = @Schema(implementation = ConvertRunVo.class))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "请求参数不合法或模板不存在"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "执行格式转换失败")
})
public ApiResponse<ConvertRunVo> run(@Valid @RequestBody ConvertRunRequest request) { public ApiResponse<ConvertRunVo> run(@Valid @RequestBody ConvertRunRequest request) {
return ApiResponse.success(convertRunService.run(request)); return ApiResponse.success(convertRunService.run(request));
} }
@GetMapping("/history") @GetMapping("/history")
@Operation(summary = "查询格式转换历史", description = "查询最近的格式转换成功记录,用于页面右侧历史下载列表展示。") @Operation(summary = "查询格式转换历史", description = "查询最近的格式转换成功记录,用于页面右侧历史下载列表展示。")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = ConvertHistoryVo.class)))
})
public ApiResponse<ConvertHistoryVo> history() { public ApiResponse<ConvertHistoryVo> history() {
ConvertHistoryVo vo = new ConvertHistoryVo(); ConvertHistoryVo vo = new ConvertHistoryVo();
vo.setItems(convertRunService.listHistory()); vo.setItems(convertRunService.listHistory());
@@ -66,14 +78,22 @@ public class ConvertRunController {
@DeleteMapping("/history/{resultId}") @DeleteMapping("/history/{resultId}")
@Operation(summary = "删除格式转换历史", description = "删除一条格式转换历史记录。") @Operation(summary = "删除格式转换历史", description = "删除一条格式转换历史记录。")
public ApiResponse<Void> deleteHistory(@PathVariable Long resultId) { @ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "删除成功"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "结果记录不存在")
})
public ApiResponse<Void> deleteHistory(@Parameter(description = "格式转换结果记录 ID", required = true) @PathVariable Long resultId) {
convertRunService.deleteHistory(resultId); convertRunService.deleteHistory(resultId);
return ApiResponse.success(null); return ApiResponse.success(null);
} }
@GetMapping("/results/{resultId}/download") @GetMapping("/results/{resultId}/download")
@Operation(summary = "下载格式转换结果", description = "根据结果记录 ID 下载已生成的转换结果文件。桌面端前端可继续通过 save_file_from_url 选择保存位置。") @Operation(summary = "下载格式转换结果", description = "根据结果记录 ID 下载已生成的转换结果文件。桌面端前端可继续通过 save_file_from_url 选择保存位置。")
public ResponseEntity<Resource> download(@PathVariable Long resultId) { @ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "返回结果文件流"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "结果文件不存在")
})
public ResponseEntity<Resource> download(@Parameter(description = "格式转换结果记录 ID", required = true) @PathVariable Long resultId) {
File resultFile = convertRunService.getResultFile(resultId); File resultFile = convertRunService.getResultFile(resultId);
String encodedFilename = URLEncoder.encode(resultFile.getName(), StandardCharsets.UTF_8).replaceAll("\\+", "%20"); String encodedFilename = URLEncoder.encode(resultFile.getName(), StandardCharsets.UTF_8).replaceAll("\\+", "%20");
return ResponseEntity.ok() return ResponseEntity.ok()

View File

@@ -5,6 +5,10 @@ import com.nanri.aiimage.modules.convert.model.dto.ConvertTemplateImportRequest;
import com.nanri.aiimage.modules.convert.model.vo.ConvertTemplateVo; import com.nanri.aiimage.modules.convert.model.vo.ConvertTemplateVo;
import com.nanri.aiimage.modules.convert.service.ConvertTemplateService; import com.nanri.aiimage.modules.convert.service.ConvertTemplateService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
@@ -27,26 +31,42 @@ public class ConvertTemplateController {
@GetMapping @GetMapping
@Operation(summary = "查询启用模板", description = "返回当前启用的格式转换模板列表,供前端‘格式转换’模块展示。") @Operation(summary = "查询启用模板", description = "返回当前启用的格式转换模板列表,供前端‘格式转换’模块展示。")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "查询成功,返回模板列表")
})
public ApiResponse<List<ConvertTemplateVo>> listTemplates() { public ApiResponse<List<ConvertTemplateVo>> listTemplates() {
return ApiResponse.success(convertTemplateService.listEnabledTemplates()); return ApiResponse.success(convertTemplateService.listEnabledTemplates());
} }
@PostMapping("/import") @PostMapping("/import")
@Operation(summary = "导入模板", description = "导入自定义 txt 模板,成功后返回新模板并可立即刷新模板列表。") @Operation(summary = "导入模板", description = "导入自定义 txt 模板,成功后返回新模板并可立即刷新模板列表。")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "导入成功,返回新模板信息", content = @Content(schema = @Schema(implementation = ConvertTemplateVo.class))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "模板内容不合法或必填字段缺失")
})
public ApiResponse<ConvertTemplateVo> importTemplate(@RequestBody ConvertTemplateImportRequest request) { public ApiResponse<ConvertTemplateVo> importTemplate(@RequestBody ConvertTemplateImportRequest request) {
return ApiResponse.success(convertTemplateService.importTemplate(request)); return ApiResponse.success(convertTemplateService.importTemplate(request));
} }
@PostMapping("/{templateCode}/default") @PostMapping("/{templateCode}/default")
@Operation(summary = "设为默认模板", description = "将指定模板设为默认模板。") @Operation(summary = "设为默认模板", description = "将指定模板设为默认模板。")
public ApiResponse<Void> setDefault(@PathVariable String templateCode) { @ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "设置成功"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "模板不存在")
})
public ApiResponse<Void> setDefault(@Parameter(description = "模板编码", required = true) @PathVariable String templateCode) {
convertTemplateService.setDefaultTemplate(templateCode); convertTemplateService.setDefaultTemplate(templateCode);
return ApiResponse.success(null); return ApiResponse.success(null);
} }
@DeleteMapping("/{templateCode}") @DeleteMapping("/{templateCode}")
@Operation(summary = "删除模板", description = "删除指定模板。内置模板不允许删除。") @Operation(summary = "删除模板", description = "删除指定模板。内置模板不允许删除。")
public ApiResponse<Void> delete(@PathVariable String templateCode) { @ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "删除成功"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "内置模板不允许删除"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "模板不存在")
})
public ApiResponse<Void> delete(@Parameter(description = "模板编码", required = true) @PathVariable String templateCode) {
convertTemplateService.deleteTemplate(templateCode); convertTemplateService.deleteTemplate(templateCode);
return ApiResponse.success(null); return ApiResponse.success(null);
} }

View File

@@ -6,6 +6,10 @@ import com.nanri.aiimage.modules.dedupe.model.vo.DedupeHistoryVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeRunVo; import com.nanri.aiimage.modules.dedupe.model.vo.DedupeRunVo;
import com.nanri.aiimage.modules.dedupe.service.DedupeRunService; import com.nanri.aiimage.modules.dedupe.service.DedupeRunService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -43,12 +47,20 @@ public class DedupeRunController {
- keepUnderscoreIds=true 时保留类似 1_1 的 ID - keepUnderscoreIds=true 时保留类似 1_1 的 ID
- 输出文件命名遵循 原文件名_cleaned.xlsx若重名自动追加序号。 - 输出文件命名遵循 原文件名_cleaned.xlsx若重名自动追加序号。
""") """)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "执行成功,返回处理统计与结果列表", content = @Content(schema = @Schema(implementation = DedupeRunVo.class))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "请求参数不合法或缺少必要字段"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "执行数据去重失败")
})
public ApiResponse<DedupeRunVo> run(@Valid @RequestBody DedupeRunRequest request) { public ApiResponse<DedupeRunVo> run(@Valid @RequestBody DedupeRunRequest request) {
return ApiResponse.success(dedupeRunService.run(request)); return ApiResponse.success(dedupeRunService.run(request));
} }
@GetMapping("/history") @GetMapping("/history")
@Operation(summary = "查询去重历史", description = "查询最近的去重成功记录,用于页面右侧历史下载列表展示。") @Operation(summary = "查询去重历史", description = "查询最近的去重成功记录,用于页面右侧历史下载列表展示。")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = DedupeHistoryVo.class)))
})
public ApiResponse<DedupeHistoryVo> history() { public ApiResponse<DedupeHistoryVo> history() {
DedupeHistoryVo vo = new DedupeHistoryVo(); DedupeHistoryVo vo = new DedupeHistoryVo();
vo.setItems(dedupeRunService.listHistory()); vo.setItems(dedupeRunService.listHistory());
@@ -57,14 +69,22 @@ public class DedupeRunController {
@DeleteMapping("/history/{resultId}") @DeleteMapping("/history/{resultId}")
@Operation(summary = "删除去重历史", description = "删除一条去重历史记录。") @Operation(summary = "删除去重历史", description = "删除一条去重历史记录。")
public ApiResponse<Void> deleteHistory(@PathVariable Long resultId) { @ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "删除成功"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "结果记录不存在")
})
public ApiResponse<Void> deleteHistory(@Parameter(description = "去重结果记录 ID", required = true) @PathVariable Long resultId) {
dedupeRunService.deleteHistory(resultId); dedupeRunService.deleteHistory(resultId);
return ApiResponse.success(null); return ApiResponse.success(null);
} }
@GetMapping("/results/{resultId}/download") @GetMapping("/results/{resultId}/download")
@Operation(summary = "下载数据去重结果", description = "根据结果记录 ID 下载清洗后的 Excel 文件。") @Operation(summary = "下载数据去重结果", description = "根据结果记录 ID 下载清洗后的 Excel 文件。")
public ResponseEntity<Resource> download(@PathVariable Long resultId) { @ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "返回结果文件流"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "结果文件不存在")
})
public ResponseEntity<Resource> download(@Parameter(description = "去重结果记录 ID", required = true) @PathVariable Long resultId) {
Resource resource = dedupeRunService.getResultFile(resultId); Resource resource = dedupeRunService.getResultFile(resultId);
return ResponseEntity.ok() return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM) .contentType(MediaType.APPLICATION_OCTET_STREAM)

View File

@@ -4,6 +4,11 @@ import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.modules.file.model.vo.UploadFileVo; import com.nanri.aiimage.modules.file.model.vo.UploadFileVo;
import com.nanri.aiimage.modules.file.service.LocalFileStorageService; import com.nanri.aiimage.modules.file.service.LocalFileStorageService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -31,7 +36,14 @@ public class FileUploadController {
- 然后把 fileKey 传给 dedupe/convert/split 执行接口; - 然后把 fileKey 传给 dedupe/convert/split 执行接口;
- 处理完成后,桌面端再调用下载接口把结果文件保存回用户本地目录。 - 处理完成后,桌面端再调用下载接口把结果文件保存回用户本地目录。
""") """)
public ApiResponse<UploadFileVo> upload(MultipartFile file) throws Exception { @ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "上传成功,返回临时文件信息", content = @Content(schema = @Schema(implementation = UploadFileVo.class))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "未传文件或文件不合法"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "服务器保存临时文件失败")
})
public ApiResponse<UploadFileVo> upload(
@Parameter(name = "file", description = "待上传的 Excel 或业务源文件", required = true, in = ParameterIn.QUERY)
MultipartFile file) throws Exception {
return ApiResponse.success(localFileStorageService.saveTempFile(file)); return ApiResponse.success(localFileStorageService.saveTempFile(file));
} }
} }

View File

@@ -6,6 +6,10 @@ import com.nanri.aiimage.modules.split.model.vo.SplitHistoryVo;
import com.nanri.aiimage.modules.split.model.vo.SplitRunVo; import com.nanri.aiimage.modules.split.model.vo.SplitRunVo;
import com.nanri.aiimage.modules.split.service.SplitRunService; import com.nanri.aiimage.modules.split.service.SplitRunService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -44,12 +48,20 @@ public class SplitRunController {
- 输出文件命名遵循 原文件名_split_序号.xlsx - 输出文件命名遵循 原文件名_split_序号.xlsx
- 返回结果中 rowCount 表示该结果文件包含的数据行数。 - 返回结果中 rowCount 表示该结果文件包含的数据行数。
""") """)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "执行成功,返回拆分结果统计", content = @Content(schema = @Schema(implementation = SplitRunVo.class))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "请求参数不合法或拆分模式错误"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "执行数据拆分失败")
})
public ApiResponse<SplitRunVo> run(@Valid @RequestBody SplitRunRequest request) { public ApiResponse<SplitRunVo> run(@Valid @RequestBody SplitRunRequest request) {
return ApiResponse.success(splitRunService.run(request)); return ApiResponse.success(splitRunService.run(request));
} }
@GetMapping("/history") @GetMapping("/history")
@Operation(summary = "查询数据拆分历史", description = "查询最近的数据拆分成功记录,用于页面右侧历史下载列表展示。") @Operation(summary = "查询数据拆分历史", description = "查询最近的数据拆分成功记录,用于页面右侧历史下载列表展示。")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = SplitHistoryVo.class)))
})
public ApiResponse<SplitHistoryVo> history() { public ApiResponse<SplitHistoryVo> history() {
SplitHistoryVo vo = new SplitHistoryVo(); SplitHistoryVo vo = new SplitHistoryVo();
vo.setItems(splitRunService.listHistory()); vo.setItems(splitRunService.listHistory());
@@ -58,14 +70,22 @@ public class SplitRunController {
@DeleteMapping("/history/{resultId}") @DeleteMapping("/history/{resultId}")
@Operation(summary = "删除数据拆分历史", description = "删除一条数据拆分历史记录。") @Operation(summary = "删除数据拆分历史", description = "删除一条数据拆分历史记录。")
public ApiResponse<Void> deleteHistory(@PathVariable Long resultId) { @ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "删除成功"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "结果记录不存在")
})
public ApiResponse<Void> deleteHistory(@Parameter(description = "数据拆分结果记录 ID", required = true) @PathVariable Long resultId) {
splitRunService.deleteHistory(resultId); splitRunService.deleteHistory(resultId);
return ApiResponse.success(null); return ApiResponse.success(null);
} }
@GetMapping("/results/{resultId}/download") @GetMapping("/results/{resultId}/download")
@Operation(summary = "下载数据拆分结果", description = "根据结果记录 ID 下载拆分后的 Excel 文件。") @Operation(summary = "下载数据拆分结果", description = "根据结果记录 ID 下载拆分后的 Excel 文件。")
public ResponseEntity<Resource> download(@PathVariable Long resultId) { @ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "返回结果文件流"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "结果文件不存在")
})
public ResponseEntity<Resource> download(@Parameter(description = "数据拆分结果记录 ID", required = true) @PathVariable Long resultId) {
Resource resource = splitRunService.getResultFile(resultId); Resource resource = splitRunService.getResultFile(resultId);
return ResponseEntity.ok() return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM) .contentType(MediaType.APPLICATION_OCTET_STREAM)

View File

@@ -30,8 +30,6 @@ mybatis-plus:
springdoc: springdoc:
api-docs: api-docs:
enabled: true enabled: true
swagger-ui:
path: /swagger-ui.html
knife4j: knife4j:
enable: true enable: true