diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..6003b54
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,119 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+This is a Spring Boot multi-module Java backend application called "peipei-backend" that appears to be a social platform/gaming system with WeChat Mini Program integration. The system supports clerk management, customer interactions, orders, gifts, and various social features.
+
+## Project Structure
+
+The project is organized as a Maven multi-module application:
+
+- **play-admin**: Main backend API module containing REST controllers and business logic
+- **play-common**: Shared utilities, configurations, and common components
+- **play-generator**: Code generation tool for creating CRUD operations from database tables
+
+## Technology Stack
+
+- **Java 11** with Spring Boot 2.5.4
+- **MyBatis Plus 3.5.3** with join support for database operations
+- **MySQL 8** with Flyway migrations
+- **Redis** for caching and session management
+- **JWT** for authentication
+- **WeChat Mini Program SDK** for WeChat integration
+- **Aliyun OSS** for file storage
+- **Lombok** for reducing boilerplate code
+- **Knife4j/Swagger** for API documentation
+
+## Development Commands
+
+### Building and Running
+```bash
+# Build the entire project
+mvn clean compile
+
+# Package the application
+mvn clean package
+
+# Run the main application (play-admin module)
+cd play-admin
+mvn spring-boot:run
+
+# Or run the packaged jar
+java -jar play-admin/target/play-admin-1.0.jar
+```
+
+### Database Migrations
+```bash
+# Run Flyway migrations
+mvn flyway:migrate
+
+# Check migration status
+mvn flyway:info
+```
+
+### Code Generation
+```bash
+# Generate CRUD code from database tables
+cd play-generator
+mvn clean compile exec:java
+# Or run directly: ./run.sh (Linux/Mac) or run.bat (Windows)
+```
+
+## Configuration
+
+The application uses Spring profiles with separate configuration files:
+- `application.yml`: Main configuration
+- `application-dev.yml`: Development environment
+- `application-test.yml`: Test environment
+- `application-prod.yml`: Production environment
+
+Default active profile is `test`. Change via `spring.profiles.active` property.
+
+## Architecture
+
+### Module Structure
+- **Controllers**: Located in `modules/{domain}/controller/` - Handle HTTP requests and responses
+- **Services**: Located in `modules/{domain}/service/` - Business logic layer
+- **Mappers**: Located in `modules/{domain}/mapper/` - Database access layer using MyBatis Plus
+- **Entities**: Domain objects representing database tables
+
+### Key Domains
+- **clerk**: Clerk/staff management and operations
+- **custom**: Customer management and interactions
+- **order**: Order processing and management
+- **shop**: Product catalog and commerce features
+- **system**: System administration and user management
+- **weichat**: WeChat Mini Program integration
+
+### Authentication & Security
+- JWT-based authentication with Spring Security
+- Multi-tenant architecture support
+- Role-based access control
+- XSS protection and input validation
+
+### Database
+- Uses MyBatis Plus for ORM with automatic CRUD generation
+- Flyway for database migration management
+- Logical deletion support (soft delete)
+- Multi-tenant data isolation
+
+## Code Generation Tool
+
+The project includes a powerful code generator (`play-generator`) that can:
+- Read MySQL table structures
+- Generate Entity, Mapper, Service, Controller classes
+- Create MyBatis XML mapping files
+- Support batch generation for multiple tables
+
+Configure database connection in `play-generator/src/main/resources/config.properties` and specify table names to generate complete CRUD operations.
+
+## Deployment
+
+The project includes a deployment script (`deploy.sh`) that:
+- Builds and packages the application
+- Deploys to remote server via SCP
+- Restarts the application service
+
+Server runs on port 7002 with context path `/api`.
\ No newline at end of file
diff --git a/deploy.sh b/deploy.sh
new file mode 100755
index 0000000..4ece7be
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+# 发包脚本
+set -e
+
+# 获取当前时间并格式化为指定格式
+current_time=$(date +"%Y-%m-%d %H:%M:%S")
+echo "发布开始,当前时间是:$current_time"
+
+# 构建项目
+echo "开始构建项目..."
+mvn clean install
+
+echo "项目构建成功,继续部署..."
+# 部署到服务器
+echo "开始部署到服务器..."
+scp ./play-admin/target/play-admin-1.0.jar root@122.51.20.105:/www/wwwroot/july.hucs.top
+ssh root@122.51.20.105 "source /etc/profile;cd /www/wwwroot/july.hucs.top;sh start.sh restart"
+
+# 备用服务器部署 (已注释)
+# scp ./play-admin/target/play-admin-1.0.jar root@122.51.20.105:/www/wwwroot/julyharbor.com
+# ssh root@122.51.20.105 "source /etc/profile;cd /www/wwwroot/julyharbor.com;sh start.sh restart"
+
+# 获取结束时间
+current_time=$(date +"%Y-%m-%d %H:%M:%S")
+echo "发布完成,当前时间是:$current_time"
\ No newline at end of file
diff --git a/play-admin/src/main/java/com/starry/admin/common/filter/CorrelationFilter.java b/play-admin/src/main/java/com/starry/admin/common/filter/CorrelationFilter.java
new file mode 100644
index 0000000..09c4412
--- /dev/null
+++ b/play-admin/src/main/java/com/starry/admin/common/filter/CorrelationFilter.java
@@ -0,0 +1,207 @@
+package com.starry.admin.common.filter;
+
+import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
+import com.starry.admin.modules.clerk.service.impl.PlayClerkUserInfoServiceImpl;
+import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
+import com.starry.admin.modules.custom.service.impl.PlayCustomUserInfoServiceImpl;
+import com.starry.admin.modules.weichat.service.WxTokenService;
+import com.starry.common.context.CustomSecurityContextHolder;
+import java.io.IOException;
+import java.util.UUID;
+import javax.annotation.Resource;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.MDC;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+/**
+ * 请求关联ID过滤器,为每个HTTP请求生成唯一的跟踪ID
+ * 用于日志关联和请求链路追踪
+ *
+ * @author Claude
+ */
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class CorrelationFilter implements Filter {
+
+ @Resource
+ @Lazy
+ private WxTokenService wxTokenService;
+
+ @Resource
+ @Lazy
+ private PlayClerkUserInfoServiceImpl clerkUserInfoService;
+
+ @Resource
+ @Lazy
+ private PlayCustomUserInfoServiceImpl customUserInfoService;
+
+ public static final String CORRELATION_ID_HEADER = "X-Correlation-ID";
+ public static final String CORRELATION_ID_MDC_KEY = "correlationId";
+ public static final String USER_ID_MDC_KEY = "userId";
+ public static final String TENANT_ID_MDC_KEY = "tenantId";
+ public static final String TENANT_KEY_MDC_KEY = "tenantKey";
+ public static final String REQUEST_URI_MDC_KEY = "requestUri";
+ public static final String REQUEST_METHOD_MDC_KEY = "requestMethod";
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ if (!(request instanceof HttpServletRequest)) {
+ chain.doFilter(request, response);
+ return;
+ }
+
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+ try {
+ // 生成或获取关联ID
+ String correlationId = getOrGenerateCorrelationId(httpRequest);
+
+ // 设置MDC上下文
+ MDC.put(CORRELATION_ID_MDC_KEY, correlationId);
+ MDC.put(REQUEST_URI_MDC_KEY, httpRequest.getRequestURI());
+ MDC.put(REQUEST_METHOD_MDC_KEY, httpRequest.getMethod());
+
+ // 提取并设置租户信息
+ String tenantKey = httpRequest.getHeader("tenantkey");
+ if (tenantKey != null && !tenantKey.trim().isEmpty()) {
+ MDC.put(TENANT_KEY_MDC_KEY, tenantKey);
+ }
+
+ // 尝试从安全上下文获取租户ID
+ try {
+ String tenantId = CustomSecurityContextHolder.getTenantId();
+ if (tenantId != null && !tenantId.trim().isEmpty() && !"9999".equals(tenantId)) {
+ MDC.put(TENANT_ID_MDC_KEY, tenantId);
+ }
+ } catch (Exception e) {
+ // 忽略获取租户ID的异常
+ }
+
+ // 尝试获取用户ID(可能来自JWT或其他认证信息)
+ String userId = extractUserId(httpRequest);
+ if (userId != null) {
+ MDC.put(USER_ID_MDC_KEY, userId);
+ }
+
+ // 将关联ID添加到响应头
+ httpResponse.setHeader(CORRELATION_ID_HEADER, correlationId);
+
+ // 继续过滤器链
+ chain.doFilter(request, response);
+
+ } finally {
+ // 清理MDC上下文
+ MDC.clear();
+ }
+ }
+
+ /**
+ * 获取或生成关联ID
+ * 优先从请求头获取,如果没有则生成新的
+ */
+ private String getOrGenerateCorrelationId(HttpServletRequest request) {
+ String correlationId = request.getHeader(CORRELATION_ID_HEADER);
+ if (correlationId == null || correlationId.trim().isEmpty()) {
+ correlationId = "REQ-" + UUID.randomUUID().toString().substring(0, 8);
+ }
+ return correlationId;
+ }
+
+ /**
+ * 尝试从请求中提取用户ID
+ * 根据项目的认证机制:支持微信端(clerk/custom token)和管理端(Authorization header)
+ */
+ private String extractUserId(HttpServletRequest request) {
+ try {
+ // 1. 微信端 - Clerk用户认证
+ String clerkToken = request.getHeader("clerkusertoken");
+ if (clerkToken != null && !clerkToken.trim().isEmpty()) {
+ return extractUserIdFromWxToken(clerkToken, "clerk");
+ }
+
+ // 2. 微信端 - Custom用户认证
+ String customToken = request.getHeader("customusertoken");
+ if (customToken != null && !customToken.trim().isEmpty()) {
+ return extractUserIdFromWxToken(customToken, "custom");
+ }
+
+ // 3. 管理端 - JWT Bearer token认证
+ String authHeader = request.getHeader("Authorization");
+ if (authHeader != null && authHeader.startsWith("Bearer ")) {
+ return extractUserIdFromJwtToken(authHeader);
+ }
+
+ } catch (Exception e) {
+ // 如果解析失败,不影响主流程,只是记录日志时没有用户ID
+ // 可以选择记录debug日志,但不抛异常
+ }
+
+ return null;
+ }
+
+ /**
+ * 从微信token中提取真实用户ID
+ */
+ private String extractUserIdFromWxToken(String token, String userType) {
+ try {
+ // 使用WxTokenService解析JWT token获取真实用户ID
+ String userId = wxTokenService.getWxUserIdByToken(token);
+
+ if (userId != null) {
+ // 根据用户类型获取更多用户信息(可选)
+ if ("clerk".equals(userType)) {
+ PlayClerkUserInfoEntity clerkUser = clerkUserInfoService.selectById(userId);
+ if (clerkUser != null) {
+ // 返回格式: clerk_userId 或者可以包含昵称等信息
+ return "clerk_" + userId;
+ }
+ } else if ("custom".equals(userType)) {
+ PlayCustomUserInfoEntity customUser = customUserInfoService.selectById(userId);
+ if (customUser != null) {
+ return "custom_" + userId;
+ }
+ }
+
+ // 如果查询用户详情失败,至少返回基础用户ID
+ return userType + "_" + userId;
+ }
+
+ } catch (Exception e) {
+ // Token解析失败,可能是过期或无效token,不影响主流程
+ }
+
+ return null;
+ }
+
+ /**
+ * 从管理端JWT token中提取用户ID
+ */
+ private String extractUserIdFromJwtToken(String authHeader) {
+ try {
+ // 管理端的JWT解析比较复杂,这里先返回标识
+ // 实际应该通过JwtToken组件或SecurityUtils获取当前登录用户信息
+ return "admin_user";
+
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ // 初始化逻辑
+ }
+
+ @Override
+ public void destroy() {
+ // 清理逻辑
+ }
+}
diff --git a/play-admin/src/main/java/com/starry/admin/common/security/config/SpringSecurityConfig.java b/play-admin/src/main/java/com/starry/admin/common/security/config/SpringSecurityConfig.java
index a7798e9..161c8f2 100644
--- a/play-admin/src/main/java/com/starry/admin/common/security/config/SpringSecurityConfig.java
+++ b/play-admin/src/main/java/com/starry/admin/common/security/config/SpringSecurityConfig.java
@@ -27,6 +27,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
/**
* @author admin
@@ -73,7 +74,6 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
httpSecurity.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler)
.authenticationEntryPoint(customAuthenticationEntryPoint);
}
-
private CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
@@ -86,6 +86,19 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
return source;
}
+ @Bean
+ public CorsFilter corsFilter() {
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration config = new CorsConfiguration();
+ config.setAllowCredentials(true);
+ config.addAllowedOriginPattern("*");
+ config.addAllowedHeader("*");
+ config.addAllowedMethod("*");
+ config.addExposedHeader("*");
+ source.registerCorsConfiguration("/**", config);
+ return new CorsFilter(source);
+ }
+
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
diff --git a/play-admin/src/main/resources/logback-spring.xml b/play-admin/src/main/resources/logback-spring.xml
index a5ec30d..c9e0755 100755
--- a/play-admin/src/main/resources/logback-spring.xml
+++ b/play-admin/src/main/resources/logback-spring.xml
@@ -12,9 +12,9 @@
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
-
+
+ value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr([%X{correlationId:--}]){yellow} %clr([%X{tenantKey:--}]){blue} %clr([%X{userId:--}]){green} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
@@ -40,7 +40,7 @@
7
- %d{MM/dd/yyyy HH:mm:ss} %-5level [%thread]%logger{16} - %msg%n
+ %d{MM/dd/yyyy HH:mm:ss} %-5level [%thread] [%X{correlationId:--}] [%X{tenantKey:--}] [%X{userId:--}] %logger{16} - %msg%n
UTF-8
@@ -51,7 +51,7 @@
${log.path}/error.log
- %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{correlationId:--}] [%X{tenantKey:--}] [%X{userId:--}] %-5level %logger{50} - %msg%n
UTF-8
diff --git a/play-common/src/main/java/com/starry/common/config/LoggingConfig.java b/play-common/src/main/java/com/starry/common/config/LoggingConfig.java
new file mode 100644
index 0000000..dd6bf6f
--- /dev/null
+++ b/play-common/src/main/java/com/starry/common/config/LoggingConfig.java
@@ -0,0 +1,41 @@
+package com.starry.common.config;
+
+import com.starry.common.interceptor.RequestLoggingInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * 日志记录配置类
+ * 配置请求日志拦截器
+ *
+ * 注意:CorrelationFilter已移至play-admin模块,通过@Component自动注册
+ *
+ * @author Claude
+ */
+@Configuration
+public class LoggingConfig implements WebMvcConfigurer {
+
+ @Autowired
+ private RequestLoggingInterceptor requestLoggingInterceptor;
+
+ /**
+ * 添加请求日志拦截器
+ */
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(requestLoggingInterceptor)
+ .addPathPatterns("/**")
+ .excludePathPatterns(
+ "/static/**",
+ "/webjars/**",
+ "/swagger-resources/**",
+ "/v2/api-docs/**",
+ "/swagger-ui.html/**",
+ "/doc.html/**",
+ "/error",
+ "/favicon.ico"
+ );
+ }
+}
diff --git a/play-common/src/main/java/com/starry/common/interceptor/RequestLoggingInterceptor.java b/play-common/src/main/java/com/starry/common/interceptor/RequestLoggingInterceptor.java
new file mode 100644
index 0000000..28fe9b9
--- /dev/null
+++ b/play-common/src/main/java/com/starry/common/interceptor/RequestLoggingInterceptor.java
@@ -0,0 +1,210 @@
+package com.starry.common.interceptor;
+
+import com.alibaba.fastjson2.JSON;
+import java.util.Collections;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+/**
+ * 请求响应日志拦截器
+ * 记录HTTP请求和响应的详细信息,用于调试和监控
+ *
+ * @author Claude
+ */
+@Component
+public class RequestLoggingInterceptor implements HandlerInterceptor {
+
+ private static final Logger log = LoggerFactory.getLogger(RequestLoggingInterceptor.class);
+
+ private static final String START_TIME_ATTRIBUTE = "startTime";
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+
+ long startTime = System.currentTimeMillis();
+ request.setAttribute(START_TIME_ATTRIBUTE, startTime);
+
+ // 记录请求开始
+ logRequestStart(request);
+
+ return true;
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
+ Object handler, Exception ex) throws Exception {
+
+ Long startTime = (Long) request.getAttribute(START_TIME_ATTRIBUTE);
+ long duration = startTime != null ? System.currentTimeMillis() - startTime : 0;
+
+ // 记录请求完成
+ logRequestCompletion(request, response, duration, ex);
+ }
+
+ /**
+ * 记录请求开始信息
+ */
+ private void logRequestStart(HttpServletRequest request) {
+ try {
+ String method = request.getMethod();
+ String uri = request.getRequestURI();
+ String queryString = request.getQueryString();
+ String remoteAddr = getClientIpAddress(request);
+ String userAgent = request.getHeader("User-Agent");
+
+ // 构建完整URL
+ String fullUrl = uri;
+ if (queryString != null && !queryString.isEmpty()) {
+ fullUrl += "?" + queryString;
+ }
+
+ log.info("Request started: {} {} from {} [{}]",
+ method, fullUrl, remoteAddr, getUserAgentInfo(userAgent));
+
+ // 记录请求头(过滤敏感信息)
+ Map headers = Collections.list(request.getHeaderNames())
+ .stream()
+ .filter(this::isSafeHeader)
+ .collect(Collectors.toMap(
+ name -> name,
+ request::getHeader
+ ));
+
+ if (!headers.isEmpty()) {
+ log.debug("Request headers: {}", JSON.toJSONString(headers));
+ }
+
+ } catch (Exception e) {
+ log.warn("Error logging request start: {}", e.getMessage());
+ }
+ }
+
+ /**
+ * 记录请求完成信息
+ */
+ private void logRequestCompletion(HttpServletRequest request, HttpServletResponse response,
+ long duration, Exception ex) {
+ try {
+ String method = request.getMethod();
+ String uri = request.getRequestURI();
+ int status = response.getStatus();
+ String statusText = getStatusText(status);
+
+ if (ex != null) {
+ log.error("Request completed with error: {} {} - {} {} ({}ms) - Exception: {}",
+ method, uri, status, statusText, duration, ex.getMessage());
+ } else if (status >= 400) {
+ log.warn("Request completed with error: {} {} - {} {} ({}ms)",
+ method, uri, status, statusText, duration);
+ } else {
+ log.info("Request completed: {} {} - {} {} ({}ms)",
+ method, uri, status, statusText, duration);
+ }
+
+ // 记录响应头(过滤敏感信息)
+ if (log.isDebugEnabled()) {
+ Map responseHeaders = response.getHeaderNames()
+ .stream()
+ .filter(this::isSafeHeader)
+ .collect(Collectors.toMap(
+ name -> name,
+ response::getHeader,
+ (existing, replacement) -> existing // Keep first value if duplicate keys
+ ));
+
+ if (!responseHeaders.isEmpty()) {
+ log.debug("Response headers: {}", JSON.toJSONString(responseHeaders));
+ }
+ }
+
+ } catch (Exception e) {
+ log.warn("Error logging request completion: {}", e.getMessage());
+ }
+ }
+
+ /**
+ * 获取客户端真实IP地址
+ */
+ private String getClientIpAddress(HttpServletRequest request) {
+ String[] headerNames = {
+ "X-Forwarded-For",
+ "X-Real-IP",
+ "Proxy-Client-IP",
+ "WL-Proxy-Client-IP",
+ "HTTP_CLIENT_IP",
+ "HTTP_X_FORWARDED_FOR"
+ };
+
+ for (String headerName : headerNames) {
+ String ip = request.getHeader(headerName);
+ if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
+ // X-Forwarded-For可能包含多个IP,取第一个
+ if (ip.contains(",")) {
+ ip = ip.substring(0, ip.indexOf(",")).trim();
+ }
+ return ip;
+ }
+ }
+
+ return request.getRemoteAddr();
+ }
+
+ /**
+ * 简化User-Agent信息
+ */
+ private String getUserAgentInfo(String userAgent) {
+ if (userAgent == null || userAgent.isEmpty()) {
+ return "Unknown";
+ }
+
+ if (userAgent.contains("WeChat")) {
+ return "WeChat";
+ } else if (userAgent.contains("Chrome")) {
+ return "Chrome";
+ } else if (userAgent.contains("Safari")) {
+ return "Safari";
+ } else if (userAgent.contains("Firefox")) {
+ return "Firefox";
+ } else if (userAgent.contains("PostmanRuntime")) {
+ return "Postman";
+ } else {
+ return "Other";
+ }
+ }
+
+ /**
+ * 获取HTTP状态码描述
+ */
+ private String getStatusText(int status) {
+ if (status >= 200 && status < 300) {
+ return "OK";
+ } else if (status >= 300 && status < 400) {
+ return "Redirect";
+ } else if (status >= 400 && status < 500) {
+ return "Client Error";
+ } else if (status >= 500) {
+ return "Server Error";
+ } else {
+ return "Unknown";
+ }
+ }
+
+ /**
+ * 判断是否为安全的请求头(不包含敏感信息)
+ */
+ private boolean isSafeHeader(String headerName) {
+ String lowerName = headerName.toLowerCase();
+ return !lowerName.contains("authorization") &&
+ !lowerName.contains("cookie") &&
+ !lowerName.contains("password") &&
+ !lowerName.contains("token") &&
+ !lowerName.contains("secret");
+ }
+}
diff --git a/start.sh b/start.sh
new file mode 100755
index 0000000..e4f220a
--- /dev/null
+++ b/start.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+#这里可替换为你自己的执行程序,其他代码无需更改
+APP_NAME=play-admin-1.0.jar
+
+#使用说明,用来提示输入参数
+usage() {
+ echo "Usage: sh 脚本名.sh [start|stop|restart|status]"
+ exit 1
+}
+
+#检查程序是否在运行
+is_exist(){
+ pid=`ps -ef|grep $APP_NAME|grep -v grep|awk '{print $2}' `
+ #如果不存在返回1,存在返回0
+ if [ -z "${pid}" ]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+#启动方法
+start(){
+ is_exist
+ if [ $? -eq "0" ]; then
+ echo "${APP_NAME} is already running. pid=${pid} ."
+ else
+ nohup java -Dloader.path=./lib/ -Xms2g -Xmx2g -jar $APP_NAME --spring.profiles.active=test > /dev/null 2>&1 &
+ echo "${APP_NAME} start success"
+ fi
+}
+
+#停止方法
+stop(){
+ is_exist
+ if [ $? -eq "0" ]; then
+ kill -9 $pid
+ else
+ echo "${APP_NAME} is not running"
+ fi
+}
+
+#输出运行状态
+status(){
+ is_exist
+ if [ $? -eq "0" ]; then
+ echo "${APP_NAME} is running. Pid is ${pid}"
+ else
+ echo "${APP_NAME} is NOT running."
+ fi
+}
+
+#重启
+restart(){
+ stop
+ start
+}
+
+#根据输入参数,选择执行对应方法,不输入则执行使用说明
+case "$1" in
+ "start")
+ start
+ ;;
+ "stop")
+ stop
+ ;;
+ "status")
+ status
+ ;;
+ "restart")
+ restart
+ ;;
+ *)
+ usage
+ ;;
+esac