diff --git a/Dockerfile.buildagent b/Dockerfile.buildagent new file mode 100644 index 0000000..f893f6c --- /dev/null +++ b/Dockerfile.buildagent @@ -0,0 +1,15 @@ +# 使用 JDK 8 作为构建环境 +FROM openjdk:8-jdk + +# 使用阿里云的 apt 镜像源(Debian 11 bullseye) +RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \ + sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list + +# 更新包列表并安装 Maven 和 Git +RUN apt update && \ + apt install -y maven git && \ + mvn --version && \ + git --version + +# 设置工作目录 +WORKDIR /home/jenkins/workspace \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index a5dab04..875843c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,10 +1,10 @@ pipeline { agent any - tools { - maven 'M3' // 必须在 Jenkins → Manage Jenkins → Global Tool Configuration 中配置过 - } +// tools { +// maven 'M3' // 必须在 Jenkins → Manage Jenkins → Global Tool Configuration 中配置过 +// } environment { - APP_NAME = "sczx-app" + APP_NAME = "sczx_order" DOCKER_IMAGE = "${APP_NAME}:latest" CONTAINER_NAME = "${APP_NAME}-container" } @@ -13,11 +13,16 @@ pipeline { stage('Checkout') { steps { echo "📦 正在拉取代码..." - git branch: 'main', url: 'http://115.190.8.52:3000/sczx_group/sczx-app.git' + git branch: 'main', url: 'http://115.190.8.52:3000/sczx_group/sczx_order.git' } } - stage('Build with Maven') { + stage('Build with Maven in JDK 8') { + agent { + dockerfile { + filename "Dockerfile.buildagent" + } + } steps { echo "🛠️ 正在使用 Maven 构建..." sh 'mvn clean package -s settings.xml' @@ -31,32 +36,34 @@ pipeline { } stage('Build Docker Image') { + agent any steps { echo "🐋 正在构建 Docker 镜像..." - sh """ - docker build -t \${DOCKER_IMAGE} . - """ + sh 'docker build -t "$DOCKER_IMAGE" .' } } stage('Stop Old Container') { + agent any steps { echo "🛑 正在停止旧的容器(如果存在)..." sh ''' - if [ "$(docker ps -f 'name=sczx-app-container' --format '{{.Status}}')" ]; then - docker stop sczx-app-container - docker rm sczx-app-container + if [ "$(docker ps -f 'name=sczx_order-container' --format '{{.Status}}')" ]; then + docker stop sczx_order-container + docker rm sczx_order-container fi ''' } } stage('Run New Container') { + agent any steps { echo "🟢 正在运行新的容器..." sh """ docker run -d \ --name \${CONTAINER_NAME} \ + --network sczx-net \ -p 8088:8088 \ -e JAVA_OPTS="-Xms256m -Xmx512m -Duser.timezone=Asia/Shanghai" \ --restart always \ diff --git a/README.md b/README.md index 4469b90..dddb645 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -闪充智行后端 +闪充智行订单服务 diff --git a/pom.xml b/pom.xml index 74685a8..9810601 100644 --- a/pom.xml +++ b/pom.xml @@ -4,12 +4,12 @@ 4.0.0 com.sczx - sczx-app + sczx_order 1.0.0 jar - sczx-app - sczx-app service + sczx_order + sczx_order service 1.8 @@ -146,17 +146,46 @@ 8.0.33 - + - org.springdoc - springdoc-openapi-ui - 1.6.14 + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + + + + + org.springframework.boot + spring-boot-starter-data-redis - org.webjars - swagger-ui - 4.15.5 + org.apache.commons + commons-pool2 + 2.11.1 + + + + + io.springfox + springfox-swagger2 + 2.9.2 + + + + io.springfox + springfox-swagger-ui + 2.9.2 @@ -171,11 +200,33 @@ reactor-core 3.2.2.RELEASE + + + + org.mapstruct + mapstruct + 1.5.5.Final + + + + + org.mapstruct + mapstruct-processor + 1.5.5.Final + provided + + + + + com.alibaba + fastjson + 1.2.83 + - sczx-app + sczx_order src/main/resources @@ -214,32 +265,6 @@ - - org.apache.maven.plugins - maven-dependency-plugin - 3.6.0 - - - unpack - process-resources - - unpack - - - - - org.webjars - swagger-ui - 4.15.5 - jar - ${project.build.outputDirectory} - META-INF/resources/** - - - - - - org.apache.maven.plugins maven-source-plugin diff --git a/src/main/java/com/sczx/app/config/SpringDocConfig.java b/src/main/java/com/sczx/app/config/SpringDocConfig.java deleted file mode 100644 index b8d3623..0000000 --- a/src/main/java/com/sczx/app/config/SpringDocConfig.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.sczx.app.config; - - -import org.springdoc.core.GroupedOpenApi; -import org.springframework.context.annotation.Bean; - -//@Configuration -public class SpringDocConfig { - @Bean - public GroupedOpenApi publicApi() { - return GroupedOpenApi.builder() - .group("sczx-service") - .packagesToScan("com.sczx.app.controller") - .build(); - } -} diff --git a/src/main/java/com/sczx/app/config/WebConfig.java b/src/main/java/com/sczx/app/config/WebConfig.java deleted file mode 100644 index bef3184..0000000 --- a/src/main/java/com/sczx/app/config/WebConfig.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.sczx.app.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.http.MediaType; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.StringHttpMessageConverter; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; - -@Configuration -@EnableWebMvc -public class WebConfig implements WebMvcConfigurer { - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/swagger-ui/**") - .addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/4.15.5/"); - registry.addResourceHandler("/swagger-ui.html") - .addResourceLocations("classpath:/META-INF/resources/swagger-ui.html"); - registry.addResourceHandler("/v3/api-docs/**") - .addResourceLocations("classpath:/META-INF/resources/swagger-ui/"); - } - - @Override - public void configureMessageConverters(List> converters) { - - converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8)); - // 添加支持 application/json;charset=UTF-8 的 Jackson 转换器 - converters.add(new MappingJackson2HttpMessageConverter() { - @Override - public List getSupportedMediaTypes() { - return Collections.singletonList(MediaType.APPLICATION_JSON_UTF8); - } - }); - - - } -} diff --git a/src/main/java/com/sczx/app/controller/DemoController.java b/src/main/java/com/sczx/app/controller/DemoController.java deleted file mode 100644 index 5b4f286..0000000 --- a/src/main/java/com/sczx/app/controller/DemoController.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.sczx.app.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@Tag(name = "示例接口", description = "用于演示 Springdoc 的接口") -public class DemoController { - @GetMapping("/test") - @Operation(summary = "测试接口", description = "返回一个测试字符串") - public String test() { - return "OK"; - } -} diff --git a/src/main/java/com/sczx/app/Application.java b/src/main/java/com/sczx/order/Application.java similarity index 74% rename from src/main/java/com/sczx/app/Application.java rename to src/main/java/com/sczx/order/Application.java index 5a49fc7..f36cc1c 100644 --- a/src/main/java/com/sczx/app/Application.java +++ b/src/main/java/com/sczx/order/Application.java @@ -1,7 +1,8 @@ -package com.sczx.app; +package com.sczx.order; -import com.sczx.app.common.constant.SystemConstants; -import com.sczx.app.utils.ComputerInfo; +import com.sczx.order.common.constant.SystemConstants; +import com.sczx.order.utils.ComputerInfo; +import lombok.extern.slf4j.Slf4j; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -15,19 +16,19 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.io.IOException; +@Slf4j @SpringBootApplication @EnableDiscoveryClient // 启用服务注册与发现 @EnableRetry @EnableFeignClients(basePackages = SystemConstants.FEIGN_CLIENT_BASE_PACKAGE ) @EnableTransactionManagement @EnableHystrix -@MapperScan("com.sczx.app.mapper") // 扫描 Mapper 接口 +@MapperScan("com.sczx.order.mapper") // 扫描 Mapper 接口 public class Application { public static void main(String[] args) throws IOException { ConfigurableApplicationContext context = SpringApplication.run(Application.class, args); Environment environment = context.getBean(Environment.class); - System.out.println("启动成功,后端服务API地址:http://" + ComputerInfo.getIpAddr() + ":" - + environment.getProperty("server.port") + "/doc.html"); + log.info("启动成功,后端服务API地址:http://{}:{}/swagger-ui.html", ComputerInfo.getIpAddr(), environment.getProperty("server.port")); } } diff --git a/src/main/java/com/sczx/order/aspect/FacadeAspect.java b/src/main/java/com/sczx/order/aspect/FacadeAspect.java new file mode 100644 index 0000000..041f79b --- /dev/null +++ b/src/main/java/com/sczx/order/aspect/FacadeAspect.java @@ -0,0 +1,58 @@ +package com.sczx.order.aspect; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +/** + * @Author Huang Kai + * @Date 2023/1/18 17:01 + * @Version 1.0 + */ +@Aspect +@Component +@Slf4j +public class FacadeAspect { + + + + /** 以 controller 包下定义的所有请求为切入点 */ + @Pointcut("execution(public * com..facade.*.*(..))") + public void facadeLog() {} + + /** + * 在切点之前织入 + * @param joinPoint + */ + @Before("facadeLog()") + public void doBefore(JoinPoint joinPoint) { + // 打印请求相关参数 + log.info("=========================remote Start ========================="); + // 打印 Http method + //log.info("HTTP Method : {}", request.getMethod()); + // 打印调用 controller 的全路径以及执行方法 + log.info("Class Method:{}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); + // 打印请求的 IP + //log.info("IP : {}", request.getRemoteAddr()); + // 打印请求入参 + log.info("facadeAspect_Request:{}", JSON.toJSONString(joinPoint.getArgs())); + } + + @Around("facadeLog()") + public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { + long startTime = System.currentTimeMillis(); + Object result = proceedingJoinPoint.proceed(); + // 打印出参 + log.info("facadeAspect_Response:{}", JSON.toJSONString(result)); + // 执行耗时 + log.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime); + log.info("=========================remote End ========================="); + return result; + } +} \ No newline at end of file diff --git a/src/main/java/com/sczx/order/aspect/WebLogAspect.java b/src/main/java/com/sczx/order/aspect/WebLogAspect.java new file mode 100644 index 0000000..d098fbf --- /dev/null +++ b/src/main/java/com/sczx/order/aspect/WebLogAspect.java @@ -0,0 +1,97 @@ +package com.sczx.order.aspect; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.*; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @Author Huang Kai + * @Date 2023/1/18 17:01 + * @Version 1.0 + */ +@Aspect +@Component +@Slf4j +public class WebLogAspect { + + + + /** 以 controller 包下定义的所有请求为切入点 */ + @Pointcut("execution(public * com.sczx.*.controller.*.*(..))") + public void webLog() {} + + /** + * 在切点之前织入 + * @param joinPoint + */ + @Before("webLog()") + public void doBefore(JoinPoint joinPoint) { + String uid = UUID.randomUUID().toString(); + MDC.put("requestId",uid.substring(24,36)); + // 开始打印请求日志 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = null; + if (attributes != null) { + request = attributes.getRequest(); + } + // 打印请求相关参数 + log.info("========================= Start ========================="); + // 打印请求入参 + try{ + Object[] args = joinPoint.getArgs(); + List filteredArgs = new ArrayList<>(); + for (Object arg : args) { + //如果是文件类,则只打印文件名称和大小 + if (arg instanceof MultipartFile) { + MultipartFile file = (MultipartFile) arg; + JSONObject fileJson = new JSONObject(); + fileJson.put("filename", file.getOriginalFilename()); + fileJson.put("size", file.getSize()); + filteredArgs.add(fileJson); + } else { + filteredArgs.add(arg); + } + } + if (request != null) { + log.info("URL:{} , {}", request.getRequestURL().toString(), JSON.toJSONString(filteredArgs)); + } + } catch (Exception e){ + log.error("打印WebLogAspect_Request失败,args无法被序列化", e); + } + + } + + /** + * 处理完请求后执行 + * @param joinPoint + * @param result + */ + @AfterReturning(pointcut = "webLog()", returning = "result") + public void doAfterReturning(JoinPoint joinPoint, Object result) { + // 打印出参 + try { + log.info("WebLogAspect_Response:{}", JSON.toJSON(result)); + }catch (Exception e){ + log.error("打印WebLogAspect_Response失败,result无法被序列化", e); + } + log.info("========================= End ========================="); + MDC.clear(); + } + + @AfterThrowing(throwing="ex",pointcut="webLog()") + public void doRecoveryActions(Throwable ex){ + MDC.clear(); + } +} \ No newline at end of file diff --git a/src/main/java/com/sczx/order/common/IApiCode.java b/src/main/java/com/sczx/order/common/IApiCode.java new file mode 100644 index 0000000..d2d8d88 --- /dev/null +++ b/src/main/java/com/sczx/order/common/IApiCode.java @@ -0,0 +1,12 @@ +package com.sczx.order.common; + +/** + * @Author: 张黎 + * @Date: 2025/07/09/20:20 + * @Description: + */ +public interface IApiCode { + String getCode(); + + String getMsg(); +} diff --git a/src/main/java/com/sczx/order/common/Result.java b/src/main/java/com/sczx/order/common/Result.java new file mode 100644 index 0000000..46dc9e0 --- /dev/null +++ b/src/main/java/com/sczx/order/common/Result.java @@ -0,0 +1,123 @@ +package com.sczx.order.common; + + + + +import com.sczx.order.common.enums.ApiErrorCode; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Data; +import org.apache.commons.lang.StringUtils; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * @Author: 张黎 + * @Date: 2025/07/09/20:25 + * @Description: + */ +@ApiModel(value = "公共返回结果") +@Builder +@Data +public class Result implements Serializable { + private static final long serialVersionUID = 1331134667810352183L; + @ApiModelProperty(value = "成功标识") + private boolean success; + @ApiModelProperty(value = "返回编码") + private String code; + @ApiModelProperty(value = "返回信息说明") + private String msg; + private String issue; + @ApiModelProperty(value = "返回数据") + private T data; + + public Result(boolean success, String code, String msg, String issue, T data) { + this.success = success; + this.code = code; + this.msg = msg; + this.issue = issue; + this.data = data; + } + + public static Result result(IApiCode apiCode) { + return result(apiCode, (Object)null); + } + + public static Result result(IApiCode apiCode, Object data) { + return result(apiCode, apiCode.getMsg(), data); + } + + public static Result result(IApiCode apiCode, String issue, Object data) { + return result((IApiCode)apiCode, (String)null, issue, data); + } + + public static Result result(IApiCode apiCode, String msg, String issue, Object data) { + String message = apiCode.getMsg(); + if (StringUtils.isNotBlank(msg)) { + message = msg; + } + + return result(apiCode.getCode(), message, issue, data); + } + + public static Result result(String code, String msg, String issue, Object data) { + return builder().code(code).success("0".equals(code)).msg(msg).issue(issue).data(data).build(); + } + + public static Result ok() { + return ok((Object)null); + } + + public static Result ok(Object data) { + return result(ApiErrorCode.SUCCESS, data); + } + + public static Result ok(String key, Object value) { + Map map = new HashMap(1); + map.put(key, value); + return ok(map); + } + + public static Result fail(String msg) { + return result((IApiCode)ApiErrorCode.FAIL, msg, msg, (Object)null); + } + + public static Result fail(IApiCode apiCode) { + return result(apiCode, (Object)null); + } + + public static Result fail(IApiCode apiCode, String issue) { + return result(apiCode, issue, (Object)null); + } + + public static Result fail(String code, String msg) { + return result((String)code, msg, msg, (Object)null); + } + + public static Result fail(String code, String msg, String issue) { + return result((String)code, msg, issue, (Object)null); + } + + public static Result fail(String code, String msg, String issue, Object data) { + return result(code, msg, issue, data); + } + + public static Result fail(IApiCode apiCode, String issue, Object data) { + if (ApiErrorCode.SUCCESS == apiCode) { + throw new RuntimeException("失败结果状态码不能为" + ApiErrorCode.SUCCESS.getCode()); + } else { + return result(apiCode, issue, data); + } + } + + public static Result fail(String key, Object value) { + Map map = new HashMap(); + map.put(key, value); + return result(ApiErrorCode.FAIL, map); + } + public Result() { + } +} diff --git a/src/main/java/com/sczx/app/common/constant/SystemConstants.java b/src/main/java/com/sczx/order/common/constant/SystemConstants.java similarity index 75% rename from src/main/java/com/sczx/app/common/constant/SystemConstants.java rename to src/main/java/com/sczx/order/common/constant/SystemConstants.java index eeadc5b..612eb0f 100644 --- a/src/main/java/com/sczx/app/common/constant/SystemConstants.java +++ b/src/main/java/com/sczx/order/common/constant/SystemConstants.java @@ -1,4 +1,4 @@ -package com.sczx.app.common.constant; +package com.sczx.order.common.constant; /** @@ -18,6 +18,6 @@ public interface SystemConstants { /*** * feign客户端所在包路径 */ - String FEIGN_CLIENT_BASE_PACKAGE = "com.sczx.app.thirdpart.facade"; + String FEIGN_CLIENT_BASE_PACKAGE = "com.sczx.order.thirdpart.facade"; } diff --git a/src/main/java/com/sczx/order/common/enums/ApiErrorCode.java b/src/main/java/com/sczx/order/common/enums/ApiErrorCode.java new file mode 100644 index 0000000..a8f33d4 --- /dev/null +++ b/src/main/java/com/sczx/order/common/enums/ApiErrorCode.java @@ -0,0 +1,55 @@ +package com.sczx.order.common.enums; + + +import com.sczx.order.common.IApiCode; + +/** + * @Author: 张黎 + * @Date: 2025/07/09/20:22 + * @Description: + */ +public enum ApiErrorCode implements IApiCode { + SUCCESS("0", "操作成功"), + ARG_ERROR("401000", "参数错误"), + NOT_PERMISSION("401001", "没有权限"), + HTTP_MEDIA_TYPE_NOT_SUPPORTED_ERROR("401002", "media类型出错"), + HTTP_METHOD_NOT_ALLOW_ERROR("401003", "http请求method错误"), + BODY_NOT_MATCH("401004", "请求的数据格式不符!"), + NOT_FOUND("404000", "你请求的路径不存在"), + UNAUTHORIZED("404001", "非法访问"), + FAIL("500000", "操作失败"), + INNER_ERROR("500001", "服务器内部异常"); + + private final String code; + private final String msg; + + private ApiErrorCode(String code, String msg) { + this.code = code; + this.msg = msg; + } + + public static ApiErrorCode getApiCode(String code) { + ApiErrorCode[] ecs = values(); + ApiErrorCode[] var2 = ecs; + int var3 = ecs.length; + + for(int var4 = 0; var4 < var3; ++var4) { + ApiErrorCode ec = var2[var4]; + if (ec.getCode().equals(code)) { + return ec; + } + } + + return SUCCESS; + } + + @Override + public String getCode() { + return this.code; + } + + @Override + public String getMsg() { + return this.msg; + } +} diff --git a/src/main/java/com/sczx/app/config/MyBatisPlusConfig.java b/src/main/java/com/sczx/order/config/MyBatisPlusConfig.java similarity index 94% rename from src/main/java/com/sczx/app/config/MyBatisPlusConfig.java rename to src/main/java/com/sczx/order/config/MyBatisPlusConfig.java index 7e11094..1334669 100644 --- a/src/main/java/com/sczx/app/config/MyBatisPlusConfig.java +++ b/src/main/java/com/sczx/order/config/MyBatisPlusConfig.java @@ -1,4 +1,4 @@ -package com.sczx.app.config; +package com.sczx.order.config; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; diff --git a/src/main/java/com/sczx/order/config/SwaggerConfig.java b/src/main/java/com/sczx/order/config/SwaggerConfig.java new file mode 100644 index 0000000..468c060 --- /dev/null +++ b/src/main/java/com/sczx/order/config/SwaggerConfig.java @@ -0,0 +1,47 @@ +package com.sczx.order.config; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.ParameterBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.schema.ModelRef; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.Arrays; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.basePackage("com.sczx.order.controller")) // 修改为你的 controller 包路径 + .paths(PathSelectors.any()) + .build() + .globalOperationParameters(Arrays.asList( + new ParameterBuilder() + .name("Authorization") + .description("Bearer Token") + .modelRef(new ModelRef("string")) + .parameterType("header") + .required(true) + .build() + )); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("订单服务接口文档") + .description("sczx_order订单服务接口文档") + .version("1.0") + .build(); + } +} diff --git a/src/main/java/com/sczx/order/controller/DemoController.java b/src/main/java/com/sczx/order/controller/DemoController.java new file mode 100644 index 0000000..f3e28f3 --- /dev/null +++ b/src/main/java/com/sczx/order/controller/DemoController.java @@ -0,0 +1,14 @@ +package com.sczx.order.controller; + + + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class DemoController { + @GetMapping("/test") + public String test() { + return "OK"; + } +} diff --git a/src/main/java/com/sczx/order/exception/BizException.java b/src/main/java/com/sczx/order/exception/BizException.java new file mode 100644 index 0000000..9cf7ffc --- /dev/null +++ b/src/main/java/com/sczx/order/exception/BizException.java @@ -0,0 +1,85 @@ +package com.sczx.order.exception; + + +import com.sczx.order.common.IApiCode; +import com.sczx.order.common.enums.ApiErrorCode; + +/** + * + * @author zhangli + */ +public class BizException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + protected String errorCode; + /** + * 错误信息 + */ + protected String errorMsg; + + public BizException() { + super(); + } + + public BizException(IApiCode apiCode) { + super(apiCode.getCode()); + this.errorCode = apiCode.getCode(); + this.errorMsg = apiCode.getMsg(); + } + + public BizException(IApiCode apiCode, Throwable cause) { + super(apiCode.getCode(), cause); + this.errorCode = apiCode.getCode(); + this.errorMsg = apiCode.getMsg(); + } + + public BizException(String errorMsg) { + super(errorMsg); + this.errorCode = ApiErrorCode.FAIL.getCode(); + this.errorMsg = errorMsg; + } + + public BizException(String errorCode, String errorMsg) { + super(errorCode); + this.errorCode = errorCode; + this.errorMsg = errorMsg; + } + + public BizException(String errorCode, String errorMsg, Throwable cause) { + super(errorCode, cause); + this.errorCode = errorCode; + this.errorMsg = errorMsg; + } + + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + @Override + public String getMessage() { + return errorMsg; + } + + @Override + public Throwable fillInStackTrace() { + return this; + } + +} diff --git a/src/main/java/com/sczx/order/exception/GlobalExceptionHandler.java b/src/main/java/com/sczx/order/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..77602be --- /dev/null +++ b/src/main/java/com/sczx/order/exception/GlobalExceptionHandler.java @@ -0,0 +1,50 @@ +package com.sczx.order.exception; + + + +import com.sczx.order.common.Result; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; + + +/** + * @author zhangli + */ +@Slf4j +@ControllerAdvice +public class GlobalExceptionHandler { + + + /** + * 处理自定义的业务异常 + * + * @param req + * @param e + * @return + */ + @ExceptionHandler(value = BizException.class) + @ResponseBody + public Result bizExceptionHandler(HttpServletRequest req, BizException e) { + log.warn("发生业务异常!原因是:{}", e.getErrorMsg(), e); + return Result.fail(e.getErrorCode(), e.getErrorMsg()); + } + + @ExceptionHandler(value = InnerException.class) + @ResponseBody + public Result innerExceptionHandler(HttpServletRequest req, InnerException e) { + log.error("发生服务内部异常!原因是:{}", e.getErrorMsg(), e); + return Result.fail(e.getErrorCode(), e.getErrorMsg()); + } + + @ExceptionHandler(value = Exception.class) + @ResponseBody + public Result exceptionHandler(HttpServletRequest req, Exception e) { + log.error("意料外异常!原因是:{}", e.getMessage(), e); + return Result.fail("99999", "系统内部异常"); + } + +} diff --git a/src/main/java/com/sczx/order/exception/InnerException.java b/src/main/java/com/sczx/order/exception/InnerException.java new file mode 100644 index 0000000..31e5a97 --- /dev/null +++ b/src/main/java/com/sczx/order/exception/InnerException.java @@ -0,0 +1,86 @@ +package com.sczx.order.exception; + + +import com.sczx.order.common.IApiCode; +import com.sczx.order.common.enums.ApiErrorCode; + +/** + * + * @author zhangli + + */ +public class InnerException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + protected String errorCode; + /** + * 错误信息 + */ + protected String errorMsg; + + public InnerException() { + super(); + } + + public InnerException(IApiCode apiCode) { + super(apiCode.getCode()); + this.errorCode = apiCode.getCode(); + this.errorMsg = apiCode.getMsg(); + } + + public InnerException(IApiCode apiCode, Throwable cause) { + super(apiCode.getCode(), cause); + this.errorCode = apiCode.getCode(); + this.errorMsg = apiCode.getMsg(); + } + + public InnerException(String errorMsg) { + super(errorMsg); + this.errorCode = ApiErrorCode.INNER_ERROR.getCode(); + this.errorMsg = errorMsg; + } + + public InnerException(String errorCode, String errorMsg) { + super(errorCode); + this.errorCode = errorCode; + this.errorMsg = errorMsg; + } + + public InnerException(String errorCode, String errorMsg, Throwable cause) { + super(errorCode, cause); + this.errorCode = errorCode; + this.errorMsg = errorMsg; + } + + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + @Override + public String getMessage() { + return errorMsg; + } + + @Override + public Throwable fillInStackTrace() { + return this; + } + +} diff --git a/src/main/java/com/sczx/app/utils/ComputerInfo.java b/src/main/java/com/sczx/order/utils/ComputerInfo.java similarity index 99% rename from src/main/java/com/sczx/app/utils/ComputerInfo.java rename to src/main/java/com/sczx/order/utils/ComputerInfo.java index 38b0424..a28ff9c 100644 --- a/src/main/java/com/sczx/app/utils/ComputerInfo.java +++ b/src/main/java/com/sczx/order/utils/ComputerInfo.java @@ -1,4 +1,4 @@ -package com.sczx.app.utils; +package com.sczx.order.utils; import java.io.BufferedReader; import java.io.IOException; diff --git a/src/main/java/com/sczx/order/utils/JwtUtil.java b/src/main/java/com/sczx/order/utils/JwtUtil.java new file mode 100644 index 0000000..686a1a0 --- /dev/null +++ b/src/main/java/com/sczx/order/utils/JwtUtil.java @@ -0,0 +1,63 @@ +package com.sczx.order.utils; + +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.util.Date; + +/** + * @Author: 张黎 + * @Date: 2025/07/06/14:00 + * @Description: + */ +@Slf4j +@Component +public class JwtUtil { + + @Value("${auth.token-expiration}") + private long expiration; + private final SecretKey key; + + public JwtUtil(@Value("${auth.secret-key}") String secretKey) { + this.key = Keys.hmacShaKeyFor(secretKey.getBytes()); + +// this.key = Keys.secretKeyFor(SignatureAlgorithm.HS512); + log.info("JWT 密钥:{}", secretKey); + } + + public String generateToken(String username, String role) { + return Jwts.builder() + .setSubject(username) + .claim("role", role) + .setExpiration(new Date(System.currentTimeMillis() + expiration)) + .signWith(key, SignatureAlgorithm.HS512) + .compact(); + } + + public String extractUsername(String token) { + return Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token) + .getBody() + .getSubject(); + } + + public boolean validateToken(String token) { + try { + Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token); + return true; + } catch (JwtException ex) { + return false; + } + } +} diff --git a/src/main/java/com/sczx/order/utils/RedisUtil.java b/src/main/java/com/sczx/order/utils/RedisUtil.java new file mode 100644 index 0000000..144ea00 --- /dev/null +++ b/src/main/java/com/sczx/order/utils/RedisUtil.java @@ -0,0 +1,33 @@ +package com.sczx.order.utils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * @Author: 张黎 + * @Date: 2025/07/06/14:21 + * @Description: + */ +@Component +public class RedisUtil { + + + @Autowired + private StringRedisTemplate redisTemplate; + + public void set(String key, String value, long timeout, TimeUnit unit) { + redisTemplate.opsForValue().set(key, value, timeout, unit); + } + + public String get(String key) { + return redisTemplate.opsForValue().get(key); + } + + public void delete(String key) { + redisTemplate.delete(key); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 847e6ff..235c7ad 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -4,7 +4,7 @@ server: spring: application: - name: sczx-app # 微服务名称 + name: sczx-order # 微服务名称 http: encoding: charset: UTF-8 @@ -13,7 +13,6 @@ spring: mvc: async: request-timeout: -1 - cloud: nacos: discovery: @@ -22,7 +21,8 @@ spring: metadata: version: 1.0.0 env: dev - + lifecycle: + timeout-per-shutdown-phase: 30s # 设置优雅停机时间 datasource: url: jdbc:mysql://115.190.8.52:3306/sczx?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true username: sczx_user @@ -31,15 +31,16 @@ spring: hikari: maximum-pool-size: 10 auto-commit: true - - jpa: - hibernate: - ddl-auto: update - show-sql: true - database-platform: org.hibernate.dialect.MySQL5InnoDBDialect - - lifecycle: - timeout-per-shutdown-phase: 30s # 设置优雅停机时间 + redis: + host: 115.190.8.52 + port: 6379 + lettuce: + pool: + max-active: 8 + max-wait: 2000ms + max-idle: 4 + min-idle: 1 + max-life-time: 300000ms management: endpoints: @@ -67,15 +68,14 @@ hystrix: thread: timeoutInMilliseconds: 10000 # 默认熔断超时时间 -springdoc: - swagger-ui: - url: /v3/api-docs - path: /doc.html - packages-to-scan: com.sczx.app.controller # 替换为你的 controller 包路径 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml - type-aliases-package: com.sczx.app.entity # 实体类包路径 + type-aliases-package: com.sczx.order.po # 实体类包路径 configuration: mapUnderscoreToCamelCase: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印 SQL(调试用) + +auth: + secret-key: his-is-a-very-long-and-secure-secret-key-for-jwt-signing-please-dont-use-short-keys + token-expiration: 86400000 # 24小时 diff --git a/src/test/java/com/sczx/app/service/CodeGenerator.java b/src/test/java/com/sczx/app/service/CodeGenerator.java deleted file mode 100644 index 757d7f6..0000000 --- a/src/test/java/com/sczx/app/service/CodeGenerator.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.sczx.app.service; - -import com.baomidou.mybatisplus.generator.FastAutoGenerator; - -public class CodeGenerator { - public static void main(String[] args) { - FastAutoGenerator.create("jdbc:mysql://115.190.8.52:3306/sczx", "sczx_user", "Sczx123@") - .globalConfig(builder -> { - builder.author("zhangli") - .outputDir(System.getProperty("user.dir") + "/src/main/java"); - }) - .packageConfig(builder -> { - builder.parent("com.sczx.app") - .moduleName("user"); - }) - .strategyConfig(builder -> { - builder.addInclude("user"); // 表名 - }) - .execute(); - } -} diff --git a/src/test/java/com/sczx/order/service/CodeGenerator.java b/src/test/java/com/sczx/order/service/CodeGenerator.java new file mode 100644 index 0000000..fd92ff3 --- /dev/null +++ b/src/test/java/com/sczx/order/service/CodeGenerator.java @@ -0,0 +1,41 @@ +package com.sczx.order.service; + +import com.baomidou.mybatisplus.generator.FastAutoGenerator; +import com.baomidou.mybatisplus.generator.config.DataSourceConfig; +import com.baomidou.mybatisplus.generator.config.OutputFile; +import com.baomidou.mybatisplus.generator.config.rules.DateType; +import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; + +import java.util.Collections; + +public class CodeGenerator { + + private static final String parentPackage = "com.sczx.order"; + private static final String jdbcUrl = "jdbc:mysql://115.190.8.52:3306/sczx?useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&useSSL=false&serverTimezone=Asia/Shanghai"; + private static final String username = "sczx_user"; + private static final String password = "Sczx123@"; + private static final String author = "zhangli"; + public static void main(String[] args) { + DataSourceConfig.Builder DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(jdbcUrl, username, password); + + FastAutoGenerator.create(DATA_SOURCE_CONFIG) + // 全局配置 + .globalConfig(builder -> builder.outputDir(System.getProperty("user.dir") + "/src/main/java") + .author(author).disableOpenDir().enableSwagger() + .dateType(DateType.TIME_PACK).commentDate("yyyy-MM-dd HH:mm:ss") + .fileOverride()) + // 包配置 + .packageConfig(builder -> builder.parent(parentPackage).entity("po").service("repository").serviceImpl("repository.impl").xml("") + .mapper("mapper").pathInfo(Collections.singletonMap(OutputFile.mapperXml, System.getProperty("user.dir") + "/src/main/resources/mapper"))) + + // 策略配置 + .strategyConfig((scanner, builder) -> builder.addInclude(scanner.apply("请输入表名:")) + .addTablePrefix("zc_"/*,"zd_"*/)//可支持多个,按逗号分隔 + .entityBuilder().formatFileName("%sPO").enableLombok() + .mapperBuilder().formatMapperFileName("%sMapper").formatXmlFileName("%sMapper") + .controllerBuilder().formatFileName("%sController").enableRestStyle() + .serviceBuilder().formatServiceFileName("%sRepo").formatServiceImplFileName("%sRepoImpl")) + .templateEngine(new FreemarkerTemplateEngine()) + .execute(); + } +}