commit d8b738ff93092c79ed0ac245ba7d03666f002c45 Author: zhangli <123879394@qq.com> Date: Sat Jul 12 00:13:44 2025 +0800 初始化车辆车型套餐服务 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a25437c --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +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/ +logs/ +src/main/resources/rebel.xml + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..98717d8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +# 使用 OpenJDK 8 镜像构建 +FROM openjdk:8-jdk +# 添加作者信息 +LABEL maintainer="123879394@qq.com" + +WORKDIR /app +# 复制 jar 包 +COPY target/*.jar app.jar +# 设置 JVM 参数和启动命令 +ENTRYPOINT ["java", "-jar", "-Xms128m", "-Xmx256m", "app.jar"] \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..2956378 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,78 @@ +pipeline { + agent any + tools { + maven 'M3' // 必须在 Jenkins → Manage Jenkins → Global Tool Configuration 中配置过 + } + environment { + APP_NAME = "sczx_car" + DOCKER_IMAGE = "${APP_NAME}:latest" + CONTAINER_NAME = "${APP_NAME}-container" + } + + stages { + stage('Checkout') { + steps { + echo "📦 正在拉取代码..." + git branch: 'main', url: 'http://115.190.8.52:3000/sczx_group/sczx_car.git' + } + } + + stage('Build with Maven') { + steps { + echo "🛠️ 正在使用 Maven 构建..." + sh 'mvn clean package -s settings.xml' + } + } + + stage('Check Jar File') { + steps { + sh 'ls -la target/' // 确保 jar 文件存在 + } + } + + stage('Build Docker Image') { + steps { + echo "🐋 正在构建 Docker 镜像..." + sh """ + docker build -t \${DOCKER_IMAGE} . + """ + } + } + + stage('Stop Old Container') { + steps { + echo "🛑 正在停止旧的容器(如果存在)..." + sh ''' + if [ "$(docker ps -f 'name=sczx_car-container' --format '{{.Status}}')" ]; then + docker stop sczx_car-container + docker rm sczx_car-container + fi + ''' + } + } + + stage('Run New Container') { + steps { + echo "🟢 正在运行新的容器..." + sh """ + docker run -d \ + --name \${CONTAINER_NAME} \ + --network sczx-net \ + -p 8083:8083 \ + -e JAVA_OPTS="-Xms256m -Xmx512m -Duser.timezone=Asia/Shanghai" \ + --restart always \ + \${DOCKER_IMAGE} + """ + } + } + } + + post { + success { + echo "🎉 构建成功!" + } + failure { + echo "❌ 构建失败,请检查日志!" + } + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..657408f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# sczx_store + +闪充智行-车辆、车型、套餐管理服务 \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..647c0ff --- /dev/null +++ b/pom.xml @@ -0,0 +1,275 @@ + + + 4.0.0 + + com.sczx + sczx_car + 1.0.0 + jar + + sczx_car + sczx_car service + + + 1.8 + 2.3.12.RELEASE + Hoxton.SR12 + 2.2.9.RELEASE + + + + + org.springframework.boot + spring-boot-starter-parent + 2.3.12.RELEASE + + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba.version} + pom + import + + + + + + + + + org.springframework.boot + spring-boot-starter + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-commons + + + + + org.projectlombok + lombok + 1.18.30 + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-netflix-hystrix + + + + + org.springframework.retry + spring-retry + 1.3.1 + + + + org.springframework.boot + spring-boot-starter-aop + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.3.1 + + + + + com.baomidou + mybatis-plus-boot-starter + 3.5.3.1 + + + + + com.baomidou + mybatis-plus-generator + 3.5.1 + + + + + org.freemarker + freemarker + 2.3.31 + + + + + mysql + mysql-connector-java + 8.0.33 + + + + + 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.apache.commons + commons-pool2 + 2.11.1 + + + + + io.springfox + springfox-swagger2 + 2.9.2 + + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + + io.projectreactor + reactor-core + 3.2.2.RELEASE + + + + + org.mapstruct + mapstruct + 1.5.5.Final + + + + + org.mapstruct + mapstruct-processor + 1.5.5.Final + provided + + + + + + sczx_car + + + src/main/resources + + *.yml + + true + + + src/main/resources + + *.yml + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + + \ No newline at end of file diff --git a/settings.xml b/settings.xml new file mode 100644 index 0000000..06004f8 --- /dev/null +++ b/settings.xml @@ -0,0 +1,12 @@ + + + + aliyun-maven + * + Aliyun Maven + https://maven.aliyun.com/repository/public + + + \ No newline at end of file diff --git a/src/main/java/com/sczx/car/Application.java b/src/main/java/com/sczx/car/Application.java new file mode 100644 index 0000000..d03e8e0 --- /dev/null +++ b/src/main/java/com/sczx/car/Application.java @@ -0,0 +1,34 @@ +package com.sczx.car; + + +import com.sczx.car.constant.SystemConstants; +import com.sczx.car.util.ComputerInfo; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.netflix.hystrix.EnableHystrix; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.retry.annotation.EnableRetry; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import java.io.IOException; + +@SpringBootApplication +@EnableDiscoveryClient // 启用服务注册与发现 +@EnableRetry +@EnableFeignClients(basePackages = SystemConstants.FEIGN_CLIENT_BASE_PACKAGE ) +@EnableTransactionManagement +@EnableHystrix +@MapperScan("com.sczx.store.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") + "/swagger-ui.html"); + } +} diff --git a/src/main/java/com/sczx/car/common/IApiCode.java b/src/main/java/com/sczx/car/common/IApiCode.java new file mode 100644 index 0000000..8efc36c --- /dev/null +++ b/src/main/java/com/sczx/car/common/IApiCode.java @@ -0,0 +1,12 @@ +package com.sczx.car.common; + +/** + * @Author: 张黎 + * @Date: 2025/07/09/20:20 + * @Description: + */ +public interface IApiCode { + String getCode(); + + String getMsg(); +} diff --git a/src/main/java/com/sczx/car/common/Result.java b/src/main/java/com/sczx/car/common/Result.java new file mode 100644 index 0000000..2186c7a --- /dev/null +++ b/src/main/java/com/sczx/car/common/Result.java @@ -0,0 +1,121 @@ +package com.sczx.car.common; + + +import com.sczx.car.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/car/common/enums/ApiErrorCode.java b/src/main/java/com/sczx/car/common/enums/ApiErrorCode.java new file mode 100644 index 0000000..0309df9 --- /dev/null +++ b/src/main/java/com/sczx/car/common/enums/ApiErrorCode.java @@ -0,0 +1,55 @@ +package com.sczx.car.common.enums; + + +import com.sczx.car.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/car/config/MyBatisPlusConfig.java b/src/main/java/com/sczx/car/config/MyBatisPlusConfig.java new file mode 100644 index 0000000..b1fc054 --- /dev/null +++ b/src/main/java/com/sczx/car/config/MyBatisPlusConfig.java @@ -0,0 +1,18 @@ +package com.sczx.car.config; + +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MyBatisPlusConfig { + + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); + return interceptor; + } +} diff --git a/src/main/java/com/sczx/car/config/SwaggerConfig.java b/src/main/java/com/sczx/car/config/SwaggerConfig.java new file mode 100644 index 0000000..2db7b5c --- /dev/null +++ b/src/main/java/com/sczx/car/config/SwaggerConfig.java @@ -0,0 +1,47 @@ +package com.sczx.car.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.store.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_store门店服务接口文档文档") + .version("1.0") + .build(); + } +} diff --git a/src/main/java/com/sczx/car/constant/SystemConstants.java b/src/main/java/com/sczx/car/constant/SystemConstants.java new file mode 100644 index 0000000..6f2587c --- /dev/null +++ b/src/main/java/com/sczx/car/constant/SystemConstants.java @@ -0,0 +1,23 @@ +package com.sczx.car.constant; + + +/** + * 应用模块名称

+ *

+ * 代码描述

+ *

+ * Copyright: Copyright (C) 2022 CD Finance Management Co., Ltd. All rights reserved.

+ *

+ * Company: 中和农信项目管理有限公司

+ * + * @author zhonghui + * @since 2022/4/1 3:33 PM + */ +public interface SystemConstants { + + /*** + * feign客户端所在包路径 + */ + String FEIGN_CLIENT_BASE_PACKAGE = "com.sczx.app.thirdpart.facade"; + +} diff --git a/src/main/java/com/sczx/car/exception/BizException.java b/src/main/java/com/sczx/car/exception/BizException.java new file mode 100644 index 0000000..2379e97 --- /dev/null +++ b/src/main/java/com/sczx/car/exception/BizException.java @@ -0,0 +1,85 @@ +package com.sczx.car.exception; + + +import com.sczx.car.common.IApiCode; +import com.sczx.car.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/car/exception/GlobalExceptionHandler.java b/src/main/java/com/sczx/car/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..b0c3409 --- /dev/null +++ b/src/main/java/com/sczx/car/exception/GlobalExceptionHandler.java @@ -0,0 +1,49 @@ +package com.sczx.car.exception; + + +import com.sczx.car.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/car/exception/InnerException.java b/src/main/java/com/sczx/car/exception/InnerException.java new file mode 100644 index 0000000..91ed43d --- /dev/null +++ b/src/main/java/com/sczx/car/exception/InnerException.java @@ -0,0 +1,86 @@ +package com.sczx.car.exception; + + +import com.sczx.car.common.IApiCode; +import com.sczx.car.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/car/util/ComputerInfo.java b/src/main/java/com/sczx/car/util/ComputerInfo.java new file mode 100644 index 0000000..e70ac8c --- /dev/null +++ b/src/main/java/com/sczx/car/util/ComputerInfo.java @@ -0,0 +1,159 @@ +package com.sczx.car.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/* + * <取网卡物理地址-- + * 1.在Windows,Linux系统下均可用; + * 2.通过ipconifg,ifconfig获得计算机信息; + * 3.再用模式匹配方式查找MAC地址,与操作系统的语言无关> + * + * //* Description: <取计算机名--从环境变量中取> + * abstract 限制继承/创建实例 + */ +public abstract class ComputerInfo { + private static String macAddressStr = null; + private static String computerName = System.getenv().get("COMPUTERNAME"); + + private static final String[] windowsCommand = { "ipconfig", "/all" }; + private static final String[] linuxCommand = { "/sbin/ifconfig", "-a" }; + private static final String[] macCommand = { "ifconfig", "-a" }; + private static final Pattern macPattern = Pattern.compile(".*((:?[0-9a-f]{2}[-:]){5}[0-9a-f]{2}).*", + Pattern.CASE_INSENSITIVE); + + /** + * 获取多个网卡地址 + * + * @return + * @throws IOException + */ + private final static List getMacAddressList() throws IOException { + final ArrayList macAddressList = new ArrayList(); + final String os = System.getProperty("os.name"); + final String command[]; + + if (os.startsWith("Windows")) { + command = windowsCommand; + } else if (os.startsWith("Linux")) { + command = linuxCommand; + } else if (os.startsWith("Mac")){ + command = macCommand; + } + else { + throw new IOException("Unknow operating system:" + os); + } + // 执行命令 + final Process process = Runtime.getRuntime().exec(command); + + BufferedReader bufReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + for (String line = null; (line = bufReader.readLine()) != null;) { + Matcher matcher = macPattern.matcher(line); + if (matcher.matches()) { + macAddressList.add(matcher.group(1)); + // macAddressList.add(matcher.group(1).replaceAll("[-:]", + // ""));//去掉MAC中的“-” + } + } + + process.destroy(); + bufReader.close(); + return macAddressList; + } + + /** + * 获取一个网卡地址(多个网卡时从中获取一个) + * + * @return + */ + public static String getMacAddress() { + if (macAddressStr == null || macAddressStr.equals("")) { + StringBuffer sb = new StringBuffer(); // 存放多个网卡地址用,目前只取一个非0000000000E0隧道的值 + try { + List macList = getMacAddressList(); + for (Iterator iter = macList.iterator(); iter.hasNext();) { + String amac = iter.next(); + if (!"0000000000E0".equals(amac)) { + sb.append(amac); + break; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + + macAddressStr = sb.toString(); + + } + + return macAddressStr; + } + + /** + * 获取电脑名 + * + * @return + */ + public static String getComputerName() { + if (computerName == null || computerName.equals("")) { + computerName = System.getenv().get("COMPUTERNAME"); + } + return computerName; + } + + /** + * 获取客户端IP地址 + * + * @return + */ + public static String getIpAddrAndName() throws IOException { + return InetAddress.getLocalHost().toString(); + } + + /** + * 获取客户端IP地址 + * + * @return + */ + public static String getIpAddr() throws IOException { + return InetAddress.getLocalHost().getHostAddress().toString(); + } + + /** + * 获取电脑唯一标识 + * + * @return + */ + public static String getComputerID() { + String id = getMacAddress(); + if (id == null || id.equals("")) { + try { + id = getIpAddrAndName(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return computerName; + } + + /** + * 限制创建实例 + */ + private ComputerInfo() { + + } + + public static void main(String[] args) throws IOException { + System.out.println(ComputerInfo.getMacAddress()); + System.out.println(ComputerInfo.getComputerName()); + System.out.println(ComputerInfo.getIpAddr()); + System.out.println(ComputerInfo.getIpAddrAndName()); + } +} diff --git a/src/main/java/com/sczx/car/util/JwtUtil.java b/src/main/java/com/sczx/car/util/JwtUtil.java new file mode 100644 index 0000000..fca4f0c --- /dev/null +++ b/src/main/java/com/sczx/car/util/JwtUtil.java @@ -0,0 +1,63 @@ +package com.sczx.car.util; + +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/car/util/RedisUtil.java b/src/main/java/com/sczx/car/util/RedisUtil.java new file mode 100644 index 0000000..2473807 --- /dev/null +++ b/src/main/java/com/sczx/car/util/RedisUtil.java @@ -0,0 +1,33 @@ +package com.sczx.car.util; + +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 new file mode 100644 index 0000000..d5c57f1 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,86 @@ + +server: + port: 8083 + +spring: + application: + name: sczx_car # 微服务名称 + http: + encoding: + charset: UTF-8 + enabled: true + force: true + mvc: + async: + request-timeout: -1 + cloud: + nacos: + discovery: + server-addr: 115.190.8.52:8848 # Nacos 地址 + group: DEFAULT_GROUP + 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 + password: Sczx123@ + driver-class-name: com.mysql.cj.jdbc.Driver + hikari: + maximum-pool-size: 10 + auto-commit: true + 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: + web: + exposure: + include: "*" # 暴露所有监控端点 + endpoint: + health: + show-details: always + +feign: + client: + config: + default: + connectTimeout: 5000 + readTimeout: 5000 + hystrix: + enabled: true # 启用 Feign 的 Hystrix 支持 + +hystrix: + command: + default: + execution: + isolation: + thread: + timeoutInMilliseconds: 10000 # 默认熔断超时时间 + +#springdoc: +# swagger-ui: +# url: /v3/api-docs +# path: /doc.html +# packages-to-scan: com.sczx.store.controller # 替换为你的 controller 包路径 + +mybatis-plus: + mapper-locations: classpath*:mapper/**/*.xml + type-aliases-package: com.sczx.car.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/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..e594345 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,53 @@ + + + + + + + + + ${PATTERN} + + + + + + ${LOG_FILE_PATH}/${hostname}/info.%d{yyyy-MM-dd}.log + 31 + + + + ${PATTERN} + + + + WARN + DENY + NEUTRAL + + + ERROR + DENY + NEUTRAL + + + + + + WARN + + ${LOG_FILE_PATH}/${hostname}/warn.log + true + + ${PATTERN} + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/com/sczx/car/CodeGenerator.java b/src/test/java/com/sczx/car/CodeGenerator.java new file mode 100644 index 0000000..cab6804 --- /dev/null +++ b/src/test/java/com/sczx/car/CodeGenerator.java @@ -0,0 +1,41 @@ +package com.sczx.car; + +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.store"; + 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(); + } +}