diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/controller/AttachmentController.java b/blade-service/blade-file/src/main/java/org/springblade/file/controller/AttachmentController.java
new file mode 100644
index 0000000..fadf4af
--- /dev/null
+++ b/blade-service/blade-file/src/main/java/org/springblade/file/controller/AttachmentController.java
@@ -0,0 +1,138 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.file.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.mp.support.Condition;
+import org.springblade.core.mp.support.Query;
+import org.springblade.core.secure.annotation.IsAdmin;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.file.pojo.entity.Attachment;
+import org.springblade.file.service.IAttachmentService;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 附件表 控制器
+ *
+ * @author Chill
+ */
+@NonDS
+@RestController
+@AllArgsConstructor
+//@IsAdmin
+@RequestMapping("/attachment")
+@Tag(name = "附件", description = "附件")
+public class AttachmentController extends BladeController {
+
+ private final IAttachmentService attachmentService;
+
+ /**
+ * 详情
+ */
+ @GetMapping("/detail")
+ @ApiOperationSupport(order = 1)
+ @Operation(summary = "详情", description = "传入attach")
+ public R detail(Attachment attach) {
+ Attachment detail = attachmentService.getOne(Condition.getQueryWrapper(attach));
+ return R.data(detail);
+ }
+
+ /**
+ * 分页 附件表
+ */
+ @GetMapping("/list")
+ @ApiOperationSupport(order = 2)
+ @Operation(summary = "分页", description = "传入attach")
+ public R> list(Attachment attach, Query query) {
+ attach.setSize(null);
+ query.setDescs("create_time");
+ IPage pages = attachmentService.page(Condition.getPage(query), Condition.getQueryWrapper(attach));
+ return R.data(pages);
+ }
+
+// /**
+// * 自定义分页 附件表
+// */
+// @GetMapping("/page")
+// @ApiOperationSupport(order = 3)
+// @Operation(summary = "分页", description = "传入attach")
+// public R> page(Attachment attach, Query query) {
+// IPage pages = attachmentService.selectAttachPage(Condition.getPage(query), attach);
+// return R.data(pages);
+// }
+
+ /**
+ * 新增 附件表
+ */
+ @PostMapping("/save")
+ @ApiOperationSupport(order = 4)
+ @Operation(summary = "新增", description = "传入attach")
+ public R save(@Valid @RequestBody Attachment attach) {
+ return R.status(attachmentService.save(attach));
+ }
+
+ /**
+ * 修改 附件表
+ */
+ @PostMapping("/update")
+ @ApiOperationSupport(order = 5)
+ @Operation(summary = "修改", description = "传入attach")
+ public R update(@Valid @RequestBody Attachment attach) {
+ return R.status(attachmentService.updateById(attach));
+ }
+
+ /**
+ * 新增或修改 附件表
+ */
+ @PostMapping("/submit")
+ @ApiOperationSupport(order = 6)
+ @Operation(summary = "新增或修改", description = "传入attach")
+ public R submit(@Valid @RequestBody Attachment attach) {
+ return R.status(attachmentService.saveOrUpdate(attach));
+ }
+
+
+ /**
+ * 删除 附件表
+ */
+ @PostMapping("/remove")
+ @ApiOperationSupport(order = 7)
+ @Operation(summary = "逻辑删除", description = "传入ids")
+ public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
+ return R.status(attachmentService.removeByIds(Func.toLongList(ids)));
+ }
+
+
+}
diff --git a/blade-service/blade-openapi/Dockerfile b/blade-service/blade-openapi/Dockerfile
new file mode 100644
index 0000000..d4b8014
--- /dev/null
+++ b/blade-service/blade-openapi/Dockerfile
@@ -0,0 +1,19 @@
+FROM bladex/alpine-java:openjdk17_cn_slim
+ARG ACTIVE
+
+LABEL maintainer="bladejava@qq.com"
+
+# 设置时区为上海
+ENV TZ=Asia/Shanghai
+# 设置时区信息
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+
+COPY ./target/blade-openapi.jar /opt/app.jar
+
+WORKDIR /opt
+
+EXPOSE 8108
+
+ENTRYPOINT ["java", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", "-Djava.security.egd=file:/dev/./urandom", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/opt/logs", "-XX:ErrorFile=/opt/logs/hs_err_pid%p.log", "-Xlog:gc*:file=/opt/logs/gc.log:time,uptime,level,tags:filecount=5,filesize=50M", "-XX:MaxDirectMemorySize=512m", "-Xms1024m", "-Xmx4096m", "-jar", "app.jar"]
+
+CMD ["--spring.profiles.active=${ACTIVE}","--logging.path=/opt/logs"]
diff --git a/blade-service/blade-openapi/Dockerfile-rt b/blade-service/blade-openapi/Dockerfile-rt
new file mode 100644
index 0000000..55d86d2
--- /dev/null
+++ b/blade-service/blade-openapi/Dockerfile-rt
@@ -0,0 +1,17 @@
+#FROM 10.38.16.129/bladex/alpine-java:openjdk17_cn_slim
+#FROM registry.cn-hangzhou.aliyuncs.com/vic_person/alpine-java:openjdk17_cn_slim
+FROM 10.38.16.129:80/bladex/alpine-java:openjdk17_cn_slim
+ARG ACTIVE
+
+LABEL maintainer="bladejava@qq.com"
+
+# 设置时区为上海
+ENV TZ=Asia/Shanghai
+# 设置时区信息
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+
+COPY ./target/*.jar /opt/app.jar
+
+ENTRYPOINT ["java", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/opt/app.jar"]
+
+CMD ["--spring.profiles.active=${ACTIVE}"]
diff --git a/blade-service/blade-openapi/Dockerfile-zh-dev b/blade-service/blade-openapi/Dockerfile-zh-dev
new file mode 100644
index 0000000..1e4571b
--- /dev/null
+++ b/blade-service/blade-openapi/Dockerfile-zh-dev
@@ -0,0 +1,19 @@
+FROM bladex/alpine-java:openjdk17_cn_slim
+ARG ACTIVE
+
+LABEL maintainer="bladejava@qq.com"
+
+# 设置时区为上海
+ENV TZ=Asia/Shanghai
+# 设置时区信息
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+
+COPY ./target/blade-openapi.jar /opt/app.jar
+
+WORKDIR /opt
+
+EXPOSE 8108
+
+ENTRYPOINT ["java", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", "-Djava.security.egd=file:/dev/./urandom", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/opt/logs", "-XX:ErrorFile=/opt/logs/hs_err_pid%p.log", "-Xlog:gc*:file=/opt/logs/gc.log:time,uptime,level,tags:filecount=5,filesize=50M", "-XX:MaxDirectMemorySize=512m", "-Xms1024m", "-Xmx1024m", "-jar", "app.jar"]
+
+CMD ["--spring.profiles.active=${ACTIVE}","--logging.path=/opt/logs"]
diff --git a/blade-service/blade-openapi/pom.xml b/blade-service/blade-openapi/pom.xml
new file mode 100644
index 0000000..b25a56b
--- /dev/null
+++ b/blade-service/blade-openapi/pom.xml
@@ -0,0 +1,98 @@
+
+
+ 4.0.0
+
+ blade-service
+ org.springblade
+ ${revision}
+
+
+ blade-openapi
+
+
+
+ 1.18.30
+ 1.5.5.Final
+
+
+
+
+ org.springblade
+ blade-core-boot
+
+
+ org.springblade
+ blade-starter-swagger
+
+
+ org.springblade
+ blade-starter-threadpool
+
+
+ org.springblade
+ blade-open-api
+
+
+ org.springblade
+ blade-file-api
+
+
+ org.springblade
+ blade-mk-api
+
+
+
+ org.mapstruct
+ mapstruct
+ ${org.mapstruct.version}
+
+
+
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+ ${docker.fabric.skip}
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven.plugin.version}
+
+ ${java.version}
+ ${java.version}
+ UTF-8
+
+ -parameters
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${org.mapstruct.version}
+
+
+
+
+
+
+
+
+
+
diff --git a/blade-service/blade-openapi/src/main/java/org/springblade/openapi/OpenApiApplication.java b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/OpenApiApplication.java
new file mode 100644
index 0000000..9815c2e
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/OpenApiApplication.java
@@ -0,0 +1,50 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.openapi;
+
+
+import org.dromara.dynamictp.core.spring.EnableDynamicTp;
+import org.springblade.core.cloud.client.BladeCloudApplication;
+import org.springblade.core.launch.BladeApplication;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springframework.context.annotation.ComponentScan;
+
+/**
+ * Desk启动器
+ *
+ * @author Chill
+ */
+@EnableDynamicTp
+@BladeCloudApplication
+@ComponentScan({"org.springblade.openapi", "org.springblade.**.feign"})
+public class OpenApiApplication {
+
+ public static void main(String[] args) {
+ BladeApplication.run(AppConstant.APPLICATION_OPENAPI_NAME, OpenApiApplication.class, args);
+ }
+
+}
+
diff --git a/blade-service/blade-openapi/src/main/java/org/springblade/openapi/resultcode/OpenApiResultCode.java b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/resultcode/OpenApiResultCode.java
new file mode 100644
index 0000000..3e16b4a
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/resultcode/OpenApiResultCode.java
@@ -0,0 +1,53 @@
+package org.springblade.openapi.resultcode;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springblade.core.tool.api.IResultCode;
+
+/**
+ *
+ * 业务自定义返回码
+ * 定义规则:
+ * 统一格式:[2位服务码][2位模块码][2位功能码][3位错误码]
+ *
+ * xx xx xx xx
+ * |__服务码 |__模块 |__功能 |___错误码
+ * 业务自定义返回码
+ * * 定义规则:
+ * * 统一格式:[2位服务码][2位模块码][2位功能码][3位错误码]
+ * *
+ * * xx xx xx xx
+ * * |__服务码 |__模块 |__功能 |___错误码
+ * *
+ 服务码:
+
+ 业务基础服务 10
+ 订单服务 20
+ 仓储服务 30
+ 首页/通告服务 40
+ 系统管理服务 50
+ 开放接口服务 60
+ * @author vic
+ * @date 2024/9/12 2:09 PM
+ * @description OrderResultCode
+ */
+
+@Getter
+@AllArgsConstructor
+public enum OpenApiResultCode implements IResultCode {
+
+
+
+
+
+
+;
+ /**
+ * code编码
+ */
+ final int code;
+ /**
+ * 中文信息描述
+ */
+ final String message;
+}
diff --git a/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/Api4Wps.java b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/Api4Wps.java
new file mode 100644
index 0000000..4463e87
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/Api4Wps.java
@@ -0,0 +1,134 @@
+package org.springblade.openapi.wps;
+
+import com.alibaba.fastjson2.JSON;
+import io.swagger.v3.oas.annotations.Hidden;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.common.utils.ObsUtil;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.file.feign.IFileClient;
+import org.springblade.file.pojo.vo.AttachmentDetailVO;
+import org.springblade.openapi.wps.convert.WpsFileConvert;
+import org.springblade.openapi.wps.api.IApi4Wps;
+import org.springblade.openapi.wps.constant.WpsConstant;
+import org.springblade.openapi.wps.exception.Api4WpsException;
+import org.springblade.openapi.wps.pojo.dto.Api4WpsFileInfoDTO;
+import org.springblade.openapi.wps.pojo.enums.Api4WpsError;
+import org.springblade.openapi.wps.util.TokenUtils;
+import org.springblade.openapi.wps.pojo.vo.*;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * @author bfhuange
+ * @date 2024/10/30
+ */
+@Slf4j
+@Hidden
+@AllArgsConstructor
+@RestController
+public class Api4Wps implements IApi4Wps {
+
+ /**
+ * 图片扩展名列表
+ */
+ private static final List IMAGE_EXT = List.of("bmp", "jpeg", "jpg", "png", "gif");
+
+ private final IFileClient fileClient;
+ private final WpsFileConvert convert;
+
+ @Override
+ public Api4WpsFileInfoVO getFileInfo(String fileId, String token, Api4WpsFileInfoDTO param) {
+ log.info("wps回调查询文件信息 参数 fileId:{} token:{} param:{}", fileId, token, JSON.toJSONString(param));
+ long id = Func.toLong(fileId);
+ if (id <= 0L) {
+ throw new Api4WpsException(Api4WpsError.NOT_EXISTS);
+ }
+ if (token == null) {
+ throw new Api4WpsException(Api4WpsError.PERMISSION_DENIED);
+ }
+ R result = fileClient.getAttachmentDetail(id);
+ if (result == null || !result.isSuccess() || result.getData() == null || result.getData().getId() == null) {
+ log.warn("wps回调查询文件信息 查询附件失败 :{}", JSON.toJSONString(result));
+ throw new Api4WpsException(Api4WpsError.NOT_EXISTS);
+ }
+ // 附件
+ AttachmentDetailVO attachment = result.getData();
+ Api4WpsFileVO fileVO = convert.attachment2file(attachment);
+ // 用户信息
+ Api4WpsUserVO user = TokenUtils.getWpsUser(token);
+ if (user == null) {
+ // 用户信息为空,token过期了
+ throw new Api4WpsException(Api4WpsError.SESSION_EXPIRED);
+ }
+
+ // 用户不为空,添加水印
+ boolean isImage = isImage(attachment.getAttachmentName());
+ log.info("附件名:{} 是否图片:{}", attachment.getAttachmentName(), isImage);
+ if (!isImage) {
+ // 不是图片才加水印
+ Api4WpsWatermarkVO watermark = new Api4WpsWatermarkVO();
+ watermark.setType(WpsConstant.YES);
+ watermark.setValue(user.getName());
+ watermark.setHorizontal(100);
+ watermark.setVertical(200);
+ fileVO.setWatermark(watermark);
+ }
+ Api4WpsFileInfoVO fileInfoVO = new Api4WpsFileInfoVO();
+ fileInfoVO.setUser(user);
+ fileInfoVO.setFile(fileVO);
+ // 缓存时间
+ fileInfoVO.setAttrs(new Api4WpsAttrsVO(WpsConstant.FILE_INFO_DEFAULT_CACHE_TIME));
+ log.info("wps回调查询文件信息 返回结果:{}", JSON.toJSONString(fileInfoVO));
+ return fileInfoVO;
+ }
+
+ @Override
+ public Api4WpsFileInfoVO getFileSave(String fileId, String saveType, String token, MultipartFile file, Api4WpsFileInfoDTO param) {
+ try {
+ log.info("wps回调-上传文件新版本 参数 fileId:{} token:{} param:{}", fileId, token, JSON.toJSONString(param));
+ long id = Func.toLong(fileId);
+ if (id == 0L) {
+ return null;
+ }
+ R result = fileClient.getAttachmentDetail(id);
+ if (result == null || !result.isSuccess()) {
+ log.warn("wps回调-上传文件新版本 查询附件失败 :{}", JSON.toJSONString(result));
+ return null;
+ }
+ // 附件
+ AttachmentDetailVO attachment = result.getData();
+ Api4WpsFileVO fileVO = convert.attachment2file(attachment);
+ fileVO.setVersion(2);
+ ObsUtil.putObject(attachment.getObjectKey(), file.getInputStream());
+
+ Api4WpsFileInfoVO fileInfoVO = new Api4WpsFileInfoVO();
+ fileInfoVO.setFile(fileVO);
+
+ return fileInfoVO;
+ }catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 是否图片类型
+ * @param fileName
+ * @return
+ */
+ public boolean isImage(String fileName) {
+ if (fileName == null) {
+ return false;
+ }
+ int index = fileName.lastIndexOf(".");
+ if (index < 0 || index +1 >= fileName.length() ) {
+ return false;
+ }
+ String extName = fileName.substring(index + 1).toLowerCase(Locale.getDefault());
+ return IMAGE_EXT.contains(extName);
+ }
+}
diff --git a/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/convert/WpsFileConvert.java b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/convert/WpsFileConvert.java
new file mode 100644
index 0000000..78903fa
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/convert/WpsFileConvert.java
@@ -0,0 +1,37 @@
+package org.springblade.openapi.wps.convert;
+
+import org.mapstruct.*;
+import org.springblade.openapi.wps.pojo.vo.Api4WpsFileVO;
+import org.springblade.file.pojo.vo.AttachmentDetailVO;
+
+import java.util.Date;
+
+/**
+ * @author bfhuange
+ * @date 2024/10/30
+ */
+@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface WpsFileConvert {
+
+ @Mapping(source = "attachmentName", target = "name")
+ @Mapping(source = "url", target = "downloadUrl")
+ @Mapping(source = "size", target = "size", qualifiedByName = "long2int")
+ @Mapping(source = "createUser", target = "creator")
+ @Mapping(source = "createTime", target = "createTime", qualifiedByName = "date2int")
+ @Mapping(source = "updateUser", target = "modifier")
+ @Mapping(source = "updateTime", target = "modifyTime", qualifiedByName = "date2int")
+ Api4WpsFileVO attachment2file(AttachmentDetailVO attachment);
+
+ @Named("date2int")
+ default Integer date2int(Date date) {
+ if (date == null) {
+ return null;
+ }
+ return Math.toIntExact(date.getTime() / 1000);
+ }
+
+ @Named("long2int")
+ default Integer long2int(long size) {
+ return Math.toIntExact(size);
+ }
+}
diff --git a/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/exception/Api4WpsException.java b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/exception/Api4WpsException.java
new file mode 100644
index 0000000..e0324bf
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/exception/Api4WpsException.java
@@ -0,0 +1,34 @@
+package org.springblade.openapi.wps.exception;
+
+import lombok.Getter;
+import org.springblade.openapi.wps.pojo.enums.Api4WpsError;
+
+/**
+ * wps回调接口异常
+ * @author bfhuange
+ * @since 2024/11/26
+ */
+@Getter
+public class Api4WpsException extends RuntimeException {
+
+ /**
+ * 错误枚举
+ */
+ private final Api4WpsError error;
+ /**
+ * 消息
+ */
+ private final String message;
+
+ public Api4WpsException(Api4WpsError error) {
+ super(error.getMessage());
+ this.error = error;
+ this.message = error.getMessage();
+ }
+
+ public Api4WpsException(Api4WpsError error, String message) {
+ super(message);
+ this.error = error;
+ this.message = message;
+ }
+}
diff --git a/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/exception/Api4WpsExceptionHandler.java b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/exception/Api4WpsExceptionHandler.java
new file mode 100644
index 0000000..335a474
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/exception/Api4WpsExceptionHandler.java
@@ -0,0 +1,27 @@
+package org.springblade.openapi.wps.exception;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.openapi.wps.pojo.enums.Api4WpsError;
+import org.springblade.openapi.wps.pojo.vo.Api4WpsErrorVO;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * wps 回调接口异常处理
+ * @author bfhuange
+ * @since 2024/11/26
+ */
+@Slf4j
+@RestControllerAdvice
+public class Api4WpsExceptionHandler {
+
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(Api4WpsException.class)
+ public Api4WpsErrorVO handleException(Api4WpsException exception) {
+ log.error("wps 回调接口异常:{}", exception.getMessage(), exception);
+ Api4WpsError error = exception.getError();
+ return new Api4WpsErrorVO(error.getCode(), error.getMessage(), exception.getMessage());
+ }
+}
diff --git a/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/util/TokenUtils.java b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/util/TokenUtils.java
new file mode 100644
index 0000000..313bd9a
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/java/org/springblade/openapi/wps/util/TokenUtils.java
@@ -0,0 +1,33 @@
+package org.springblade.openapi.wps.util;
+
+import io.jsonwebtoken.Claims;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.openapi.wps.pojo.vo.Api4WpsUserVO;
+
+/**
+ * @author bfhuange
+ * @date 2024/10/30
+ */
+public class TokenUtils {
+
+ /**
+ * 获取wps需要的用户信息
+ * @param token
+ * @return
+ */
+ public static Api4WpsUserVO getWpsUser(String token) {
+ if (StringUtil.isBlank(token)) {
+ return null;
+ }
+ Claims claims = AuthUtil.parseJWT(token);
+ if (claims == null) {
+ return null;
+ }
+ String userId = Func.toStr(claims.get(TokenConstant.USER_ID));
+ String nickName = Func.toStr(claims.get(TokenConstant.NICK_NAME));
+ return new Api4WpsUserVO(userId, nickName);
+ }
+}
diff --git a/blade-service/blade-openapi/src/main/resources/bootstrap-dev.yml b/blade-service/blade-openapi/src/main/resources/bootstrap-dev.yml
new file mode 100644
index 0000000..303fc85
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/resources/bootstrap-dev.yml
@@ -0,0 +1,6 @@
+#spring:
+# cloud:
+# nacos:
+# username: nacos
+# password: ${NACOS_PASSWORD:gr30wIs5%Hi7keQj}
+# server-addr: ${NACOS_ADDR:10.38.16.127:8848}
diff --git a/blade-service/blade-openapi/src/main/resources/bootstrap-prod.yml b/blade-service/blade-openapi/src/main/resources/bootstrap-prod.yml
new file mode 100644
index 0000000..673f396
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/resources/bootstrap-prod.yml
@@ -0,0 +1,6 @@
+#spring:
+# cloud:
+# nacos:
+# username: nacos
+# password: rWrMrVTWyf%ekjuw
+# server-addr: ${NACOS_ADDR:192.168.0.242:8848}
diff --git a/blade-service/blade-openapi/src/main/resources/bootstrap-test.yml b/blade-service/blade-openapi/src/main/resources/bootstrap-test.yml
new file mode 100644
index 0000000..d449d60
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/resources/bootstrap-test.yml
@@ -0,0 +1,8 @@
+#server:
+# port: 38108
+#spring:
+# cloud:
+# nacos:
+# username: nacos
+# password: gr30wIs5%Hi7keQj
+# server-addr: ${NACOS_ADDR:10.38.16.127:8848}
diff --git a/blade-service/blade-openapi/src/main/resources/bootstrap.yml b/blade-service/blade-openapi/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..41258cc
--- /dev/null
+++ b/blade-service/blade-openapi/src/main/resources/bootstrap.yml
@@ -0,0 +1,34 @@
+server:
+ port: 8108
+
+#数据源配置
+spring:
+ datasource:
+ url: ${blade.datasource.dev.url}
+ username: ${blade.datasource.dev.username}
+ password: ${blade.datasource.dev.password}
+
+#spring:
+# application:
+# name: blade-openapi
+# cloud:
+# nacos:
+# discovery:
+# namespace: ${spring.profiles.active}
+# # 不注册到nacos
+# #registerEnabled: false
+# config:
+# # 文件后缀名
+# file-extension: yaml
+# namespace: ${spring.profiles.active}
+# shared-configs:
+# - data-id: blade.yaml
+# refresh: true
+# - data-id: blade-${spring.profiles.active}.yaml
+# refresh: true
+# - data-id: third-party-api.yaml
+# refresh: true
+# extension-configs:
+# - data-id: ${spring.application.name}-dynamictp.yaml
+# group: DEFAULT_GROUP
+# refresh: true # 必须配置,负责自动刷新不生效
diff --git a/blade-service/pom.xml b/blade-service/pom.xml
index 409eb6f..4fd073a 100644
--- a/blade-service/pom.xml
+++ b/blade-service/pom.xml
@@ -20,6 +20,7 @@
blade-desk
blade-system
blade-file
+ blade-openapi