ERP和MES对接接口模拟实现(待内部对接),接口映射初步工具实现(待应用)
| | |
| | | <artifactId>rsf-common</artifactId> |
| | | <version>1.0.0</version> |
| | | </dependency> |
| | | <!-- JWT依赖 --> |
| | | <dependency> |
| | | <groupId>io.jsonwebtoken</groupId> |
| | | <artifactId>jjwt-api</artifactId> |
| | | <version>0.11.5</version> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>io.jsonwebtoken</groupId> |
| | | <artifactId>jjwt-impl</artifactId> |
| | | <version>0.11.5</version> |
| | | <scope>runtime</scope> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>io.jsonwebtoken</groupId> |
| | | <artifactId>jjwt-jackson</artifactId> |
| | | <version>0.11.5</version> |
| | | <scope>runtime</scope> |
| | | </dependency> |
| | | </dependencies> |
| | | <build> |
| | | <finalName>rsf-open-api</finalName> |
| New file |
| | |
| | | package com.vincent.rsf.openApi.annotation; |
| | | |
| | | |
| | | import java.lang.annotation.*; |
| | | |
| | | /** |
| | | * 操作日志记录注解 |
| | | * |
| | | * @author vincent |
| | | * @since 2020-03-21 17:03:08 |
| | | */ |
| | | @Documented |
| | | @Target({ElementType.METHOD}) |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | public @interface OperationLog { |
| | | |
| | | /** |
| | | * 操作功能 |
| | | */ |
| | | String value() default ""; |
| | | |
| | | /** |
| | | * 操作模块 |
| | | */ |
| | | String module() default ""; |
| | | |
| | | /** |
| | | * 备注 |
| | | */ |
| | | String comments() default ""; |
| | | |
| | | /** |
| | | * 是否记录请求参数 |
| | | */ |
| | | boolean param() default true; |
| | | |
| | | /** |
| | | * 是否记录返回结果 |
| | | */ |
| | | boolean result() default true; |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.aspect; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.vincent.rsf.common.utils.Utils; |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import com.vincent.rsf.openApi.annotation.OperationLog; |
| | | import com.vincent.rsf.openApi.entity.app.ApiForeignLog; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.service.ApiForeignLogService; |
| | | 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.Pointcut; |
| | | import org.aspectj.lang.reflect.MethodSignature; |
| | | import org.springframework.core.annotation.Order; |
| | | 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.annotation.Resource; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.lang.reflect.Method; |
| | | import java.rmi.NoSuchObjectException; |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * Created by Administrator on 2019-07-09. |
| | | */ |
| | | @Component |
| | | @Aspect |
| | | @Slf4j |
| | | @Order(2) |
| | | public class LogAspect { |
| | | |
| | | // 参数、返回结果、错误信息等最大保存长度 |
| | | private static final int MAX_LENGTH = 1000; |
| | | // 用于记录请求耗时 |
| | | private final ThreadLocal<Long> startTime = new ThreadLocal<>(); |
| | | |
| | | @Resource |
| | | private ApiForeignLogService apiForeignLogService; |
| | | |
| | | public LogAspect() { |
| | | } |
| | | |
| | | /** |
| | | * 切入点 |
| | | * 匹配controller包及其所有子包下的所有类的所有方法 |
| | | */ |
| | | @Pointcut("execution(* com.vincent.rsf.openApi.controller..*.*(..))") |
| | | public void controllerPc() { |
| | | } |
| | | |
| | | /** |
| | | * 环绕通知 |
| | | * @param pjp ProceedingJoinPoint |
| | | * @return 方法结果 |
| | | */ |
| | | @Around("controllerPc()") |
| | | public Object around(ProceedingJoinPoint pjp) { |
| | | String methodName = pjp.getSignature().getName(); |
| | | try { |
| | | ServletRequestAttributes attributes = Optional.ofNullable((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) |
| | | .orElseThrow(() -> new NoSuchObjectException("前置通知中获取的 ServletRequestAttributes 对象为空")); |
| | | HttpServletRequest request = attributes.getRequest(); |
| | | |
| | | // if("getToken".equals(methodName) || "getToken".equals(methodName)) |
| | | // return pjp.proceed(); |
| | | |
| | | // 前置通知 |
| | | log.info("------【前置通知】------"); |
| | | |
| | | // 记录请求内容 |
| | | log.info("浏览器输入的网址:{}", request.getRequestURL().toString()); |
| | | log.info("HTTP_METHOD:{}", request.getMethod()); |
| | | log.info("IP:{}", request.getRemoteAddr()); |
| | | log.info("执行的业务方法名:{}", pjp.getSignature().getDeclaringTypeName() + "." + pjp.getSignature().getName()); |
| | | log.info("业务方法获得的参数:{}", Arrays.toString(pjp.getArgs())); |
| | | |
| | | Object result = pjp.proceed(); |
| | | |
| | | // 后置通知 |
| | | log.info("------【后置通知】------"); |
| | | log.info("{}方法的返回值:{}", pjp.getSignature().getName(), result); |
| | | |
| | | saveLog1(pjp, result, null); |
| | | return result; |
| | | } catch (Throwable e) { |
| | | // 异常通知 |
| | | log.error("------【异常通知】------"); |
| | | log.error("{}方法异常,参数:{},异常:", pjp.getSignature().getName(), Arrays.toString(pjp.getArgs()), e); |
| | | |
| | | saveLog1(pjp, null, (Exception) e); |
| | | return CommonResponse.error("服务器处理数据异常"); |
| | | } finally { |
| | | // 最终通知 |
| | | if(!methodName.isEmpty() && !"getRegistered".equals(methodName)){ |
| | | log.info("------【最终通知】------"); |
| | | log.info("{}方法执行结束", pjp.getSignature().getName()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void saveLog1(JoinPoint joinPoint, Object result, Exception e) { |
| | | |
| | | ApiForeignLog record = new ApiForeignLog(); |
| | | Long endTime = startTime.get(); |
| | | record.setCreateTime(new Date()); |
| | | // 记录操作耗时 |
| | | if (endTime != null) { |
| | | record.setSpendTime((int) (System.currentTimeMillis() - endTime)); |
| | | } |
| | | record.setTimestamp(String.valueOf(endTime)); |
| | | // // 记录当前登录用户id、租户id |
| | | // User user = getLoginUser(); |
| | | // if (user != null) { |
| | | // record.setUserId(user.getId()); |
| | | // record.setTenantId(user.getTenantId()); |
| | | // } |
| | | // 记录请求地址、请求方式、ip |
| | | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); |
| | | HttpServletRequest request = (attributes == null ? null : attributes.getRequest()); |
| | | if (request != null) { |
| | | record.setUrl(request.getRequestURI()); |
| | | // record.setClientIp(IpTools.gainRealIp(request)); |
| | | } |
| | | // 记录异常信息 |
| | | if (e != null) { |
| | | record.setResult(0); |
| | | record.setErr(Utils.sub(e.toString(), MAX_LENGTH)); |
| | | } else { |
| | | record.setResult(1); |
| | | } |
| | | // // 记录操作功能 |
| | | // record.setNamespace(desc); |
| | | // // 记录备注 |
| | | // if (!Cools.isEmpty(ol.comments())) { |
| | | // record.setMemo(ol.comments()); |
| | | // } |
| | | // 记录请求参数 |
| | | record.setRequest(Utils.sub(Arrays.toString(joinPoint.getArgs()), MAX_LENGTH)); |
| | | record.setResponse(Utils.sub(JSON.toJSONString(result), MAX_LENGTH)); |
| | | |
| | | apiForeignLogService.saveAsync(record); |
| | | } |
| | | |
| | | /** |
| | | * 保存操作记录 |
| | | */ |
| | | private void saveLog(JoinPoint joinPoint, Object result, Exception e) { |
| | | // 记录模块名、操作功能、请求方法、请求参数、返回结果 |
| | | MethodSignature signature = (MethodSignature) joinPoint.getSignature(); |
| | | Method method = signature.getMethod(); |
| | | if (null == method) { |
| | | return; |
| | | } |
| | | OperationLog ol = method.getAnnotation(OperationLog.class); |
| | | if (null == ol) { |
| | | return; |
| | | } |
| | | String desc = getDescription(method, ol); |
| | | if (Cools.isEmpty(desc)) { |
| | | return; |
| | | } |
| | | |
| | | ApiForeignLog record = new ApiForeignLog(); |
| | | Long endTime = startTime.get(); |
| | | record.setCreateTime(new Date()); |
| | | // 记录操作耗时 |
| | | if (endTime != null) { |
| | | record.setSpendTime((int) (System.currentTimeMillis() - endTime)); |
| | | } |
| | | record.setTimestamp(String.valueOf(endTime)); |
| | | // // 记录当前登录用户id、租户id |
| | | // User user = getLoginUser(); |
| | | // if (user != null) { |
| | | // record.setUserId(user.getId()); |
| | | // record.setTenantId(user.getTenantId()); |
| | | // } |
| | | // 记录请求地址、请求方式、ip |
| | | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); |
| | | HttpServletRequest request = (attributes == null ? null : attributes.getRequest()); |
| | | if (request != null) { |
| | | record.setUrl(request.getRequestURI()); |
| | | // record.setClientIp(IpTools.gainRealIp(request)); |
| | | } |
| | | // 记录异常信息 |
| | | if (e != null) { |
| | | record.setResult(0); |
| | | record.setErr(Utils.sub(e.toString(), MAX_LENGTH)); |
| | | } else { |
| | | record.setResult(1); |
| | | } |
| | | // 记录操作功能 |
| | | record.setNamespace(desc); |
| | | // 记录备注 |
| | | if (!Cools.isEmpty(ol.comments())) { |
| | | record.setMemo(ol.comments()); |
| | | } |
| | | // 记录请求参数 |
| | | if (ol.param() && request != null) { |
| | | record.setRequest(Utils.sub(getParams(joinPoint, request), MAX_LENGTH)); |
| | | } |
| | | // 记录请求结果 |
| | | if (ol.result() && result != null) { |
| | | record.setResponse(Utils.sub(JSON.toJSONString(result), MAX_LENGTH)); |
| | | } |
| | | apiForeignLogService.saveAsync(record); |
| | | } |
| | | |
| | | /** |
| | | * 获取操作功能 |
| | | * |
| | | * @param method Method |
| | | * @param ol OperationLog |
| | | * @return String |
| | | */ |
| | | private String getDescription(Method method, OperationLog ol) { |
| | | if (!Cools.isEmpty(ol.value())) { |
| | | return ol.value(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 获取请求参数 |
| | | * |
| | | * @param joinPoint JoinPoint |
| | | * @param request HttpServletRequest |
| | | * @return String |
| | | */ |
| | | private String getParams(JoinPoint joinPoint, HttpServletRequest request) { |
| | | String params; |
| | | |
| | | Map<String, String> paramsMap = new HashMap<>(); |
| | | |
| | | Map<String, String[]> map = Collections.unmodifiableMap(request.getParameterMap()); |
| | | for (Map.Entry<String, String[]> entry : map.entrySet()) { |
| | | paramsMap.put(entry.getKey(), Utils.join(entry.getValue(), ",")); |
| | | } |
| | | |
| | | if (paramsMap.keySet().size() > 0) { |
| | | params = JSON.toJSONString(paramsMap); |
| | | } else { |
| | | StringBuilder sb = new StringBuilder(); |
| | | for (Object arg : joinPoint.getArgs()) { |
| | | if (null == arg |
| | | || arg instanceof MultipartFile |
| | | || arg instanceof HttpServletRequest |
| | | || arg instanceof HttpServletResponse) { |
| | | continue; |
| | | } |
| | | sb.append(JSON.toJSONString(arg)).append(" "); |
| | | } |
| | | params = sb.toString(); |
| | | } |
| | | return params; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.config; |
| | | |
| | | import com.vincent.rsf.openApi.security.filter.AppIdAuthenticationFilter; |
| | | import org.springframework.boot.web.servlet.FilterRegistrationBean; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | import javax.annotation.Resource; |
| | | |
| | | /** |
| | | * API安全配置类 |
| | | * 用于注册API认证过滤器 |
| | | */ |
| | | @Configuration |
| | | public class ApiSecurityConfig { |
| | | |
| | | @Resource |
| | | private AppIdAuthenticationFilter appIdAuthenticationFilter; |
| | | |
| | | /** |
| | | * 注册API认证过滤器(支持AppId/AppSecret和Token认证) |
| | | * |
| | | * @return 过滤器注册Bean |
| | | */ |
| | | @Bean |
| | | public FilterRegistrationBean<AppIdAuthenticationFilter> apiAuthenticationFilter() { |
| | | FilterRegistrationBean<AppIdAuthenticationFilter> registrationBean = new FilterRegistrationBean<>(); |
| | | |
| | | registrationBean.setFilter(appIdAuthenticationFilter); |
| | | registrationBean.addUrlPatterns("/api/*", "/erp/*", "/mes/*", "/agv/*"); // 拦截API请求、ERP请求、MES请求、管理AGV任务请求 |
| | | registrationBean.setName("apiAuthenticationFilter"); |
| | | registrationBean.setOrder(1); // 设置过滤器优先级 |
| | | |
| | | return registrationBean; |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | import com.vincent.rsf.openApi.entity.constant.Constants; |
| | | import com.vincent.rsf.openApi.security.filter.AppIdAuthenticationFilter; |
| | | import com.vincent.rsf.openApi.utils.Http; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.web.servlet.AsyncHandlerInterceptor; |
| | |
| | | @Configuration |
| | | public class WebMvcConfig implements WebMvcConfigurer { |
| | | |
| | | @Autowired |
| | | private AppIdAuthenticationFilter appIdAuthenticationFilter; |
| | | |
| | | @Override |
| | | public void addInterceptors(InterceptorRegistry registry) { |
| | | registry.addInterceptor(getAsyncHandlerInterceptor()) |
| | | .addPathPatterns("/**") |
| | | .excludePathPatterns("/swagger-resources/**", "/webjars/**","/erp/**", "/v2/**","/v3/**","/doc.html/**", "/swagger-ui.html/**"); |
| | | } |
| | | |
| | | |
| | | @Bean |
| | | public AsyncHandlerInterceptor getAsyncHandlerInterceptor() { |
| | | return new AsyncHandlerInterceptor(){ |
| | | @Override |
| | | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
| | | Http.cors(response); |
| | | return true; |
| | | } |
| | | }; |
| | | } |
| | | |
| | | @Override |
| | | public void addResourceHandlers(ResourceHandlerRegistry registry) { |
| | | registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); |
| | | registry.addResourceHandler("swagger-ui.html") |
| | | .addResourceLocations("classpath:/META-INF/resources/"); |
| | | registry.addResourceHandler("doc.html") |
| | | .addResourceLocations("classpath:/META-INF/resources/"); |
| | | registry.addResourceHandler("/webjars/**") |
| | | .addResourceLocations("classpath:/META-INF/resources/webjars/"); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public void addCorsMappings(CorsRegistry registry) { |
| | |
| | | } |
| | | |
| | | |
| | | public static void cors(HttpServletResponse response){ |
| | | // 跨域设置 |
| | | response.setHeader("Access-Control-Max-Age", "3600"); |
| | | response.setHeader("Access-Control-Allow-Origin", "*"); |
| | | response.setHeader("Access-Control-Allow-Methods", "*"); |
| | | response.setHeader("Access-Control-Allow-Headers", "*"); |
| | | response.setHeader("Access-Control-Expose-Headers", Constants.TOKEN_HEADER_NAME); |
| | | @Bean |
| | | public AsyncHandlerInterceptor getAsyncHandlerInterceptor() { |
| | | return new AsyncHandlerInterceptor(){ |
| | | @Override |
| | | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
| | | Http.cors(response); |
| | | return true; |
| | | } |
| | | }; |
| | | } |
| | | |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller; |
| | | |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import com.vincent.rsf.openApi.entity.constant.Constants; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.entity.AppAuthParam; |
| | | import com.vincent.rsf.openApi.security.service.AppAuthService; |
| | | import com.vincent.rsf.openApi.security.utils.TokenUtils; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.annotation.Resource; |
| | | |
| | | /** |
| | | * App认证控制器 |
| | | * |
| | | * 提供AppId和AppSecret的登录认证功能 |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-05 |
| | | */ |
| | | @RestController |
| | | //@RequestMapping("/auth") |
| | | @Api(tags = "应用认证管理") |
| | | @Slf4j |
| | | public class AuthController { |
| | | |
| | | // 开启模拟数据 |
| | | @Value("${foreign.api.data.simulated}") |
| | | public static String SIMULATED_DATA_ENABLE = "1"; |
| | | |
| | | @Resource |
| | | private AppAuthService appAuthService; |
| | | |
| | | |
| | | /** |
| | | * 获取App认证Token |
| | | * |
| | | * @param param 应用ID和应用密钥 |
| | | * @return 认证Token |
| | | */ |
| | | @ApiOperation("获取App认证Token") |
| | | @PostMapping("/getToken") |
| | | public CommonResponse getToken(@RequestBody AppAuthParam param) { |
| | | String appId = param.getAppId(); |
| | | String appSecret = param.getAppSecret(); |
| | | |
| | | if (Cools.isEmpty(appId, appSecret)) { |
| | | return CommonResponse.error("AppId和AppSecret不能为空"); |
| | | } |
| | | |
| | | boolean isValid = appAuthService.validateApp(appId, appSecret); |
| | | if (isValid) { |
| | | String token = Constants.TOKEN_PREFIX + TokenUtils.generateToken(appId, appSecret); //appAuthService.generateAppToken(appId, appSecret); |
| | | return CommonResponse.ok() |
| | | .setMsg("获取Token成功") |
| | | .setData(token); |
| | | } else { |
| | | return CommonResponse.error("AppId或AppSecret无效"); |
| | | } |
| | | } |
| | | |
| | | // /** |
| | | // * 验证Token的接口 |
| | | // * |
| | | // * @param token 要验证的Token |
| | | // * @return Token验证结果 |
| | | // */ |
| | | // @PostMapping("/validateToken") |
| | | // public Map<String, Object> validateToken(@RequestHeader(name = "authorization") String token) { |
| | | // log.info("验证Token: {}", token.substring(0, Math.min(10, token.length())) + "..."); |
| | | // |
| | | // boolean isValid = TokenUtils.validateToken(token); |
| | | // |
| | | // Map<String, Object> response = new HashMap<>(); |
| | | // response.put("code", "200"); |
| | | // response.put("message", isValid ? "Token有效" : "Token无效"); |
| | | // response.put("data", Map.of( |
| | | // "valid", isValid, |
| | | // "appId", isValid ? TokenUtils.getAppIdFromToken(token) : null, |
| | | // "userId", isValid ? TokenUtils.getUserIdFromToken(token) : null |
| | | // )); |
| | | // response.put("success", isValid); |
| | | // |
| | | // return response; |
| | | // } |
| | | |
| | | // /** |
| | | // * AppId和AppSecret登录认证 |
| | | // * |
| | | // * @param param 认证参数 |
| | | // * @return 认证结果 |
| | | // */ |
| | | // @ApiOperation("AppId和AppSecret登录认证") |
| | | // @PostMapping("/login") |
| | | // public CommonResponse login(@RequestBody AppAuthParam param) { |
| | | // String appId = param.getAppId(); |
| | | // String appSecret = param.getAppSecret(); |
| | | // |
| | | // if (Cools.isEmpty(appId, appSecret)) { |
| | | // return CommonResponse.error("AppId和AppSecret不能为空"); |
| | | // } |
| | | // |
| | | // boolean isValid = appAuthService.validateApp(appId, appSecret); |
| | | // if (isValid) { |
| | | // // 生成Token |
| | | // String token = appAuthService.generateAppToken(appId, appSecret); |
| | | // return CommonResponse.ok() |
| | | // .setMsg("登录成功") |
| | | // .setData(token); |
| | | // } else { |
| | | // return CommonResponse.error("AppId或AppSecret无效"); |
| | | // } |
| | | // } |
| | | // |
| | | // |
| | | // |
| | | // /** |
| | | // * 验证App认证 |
| | | // * |
| | | // * @param request HTTP请求 |
| | | // * @return 验证结果 |
| | | // */ |
| | | // @ApiOperation("验证App认证") |
| | | // @PostMapping("/validate") |
| | | // public CommonResponse validate(HttpServletRequest request) { |
| | | // String appId = request.getHeader(Constants.HEADER_APP_ID); |
| | | // String appSecret = request.getHeader(Constants.HEADER_APP_SECRET); |
| | | // |
| | | // if (Cools.isEmpty(appId, appSecret)) { |
| | | // return CommonResponse.error("缺少AppId或AppSecret"); |
| | | // } |
| | | // |
| | | // boolean isValid = appAuthService.validateApp(appId, appSecret); |
| | | // if (isValid) { |
| | | // return CommonResponse.ok() |
| | | // .setMsg("验证成功") |
| | | // .setData(appAuthService.getAppInfo(appId)); |
| | | // } else { |
| | | // return CommonResponse.error("验证失败"); |
| | | // } |
| | | // } |
| | | // |
| | | // /** |
| | | // * 获取当前认证的App信息 |
| | | // * |
| | | // * @param request HTTP请求 |
| | | // * @return App信息 |
| | | // */ |
| | | // @ApiOperation("获取当前认证的App信息") |
| | | // @GetMapping("/info") |
| | | // public CommonResponse getAppInfo(HttpServletRequest request) { |
| | | // String appId = (String) request.getAttribute("APP_ID"); |
| | | // if (appId == null) { |
| | | // appId = request.getHeader(Constants.HEADER_APP_ID); |
| | | // } |
| | | // |
| | | // if (appId == null) { |
| | | // return CommonResponse.error("未找到AppId"); |
| | | // } |
| | | // |
| | | // var appInfo = appAuthService.getAppInfo(appId); |
| | | // if (appInfo != null) { |
| | | // return CommonResponse.ok() |
| | | // .setMsg("获取App信息成功") |
| | | // .setData(appInfo); |
| | | // } else { |
| | | // return CommonResponse.error("未找到App信息"); |
| | | // } |
| | | // } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.entity.phyz.Task; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | |
| | | import static com.vincent.rsf.openApi.controller.AuthController.SIMULATED_DATA_ENABLE; |
| | | import static com.vincent.rsf.openApi.controller.phyz.ERPController.paramsFormat; |
| | | |
| | | @RestController |
| | | @Api("任务管理接口") |
| | | @Slf4j |
| | | public class TaskController { |
| | | |
| | | @ApiOperation("点对点创建AGV搬运任务") |
| | | @PostMapping("/agv/transTask/add") |
| | | public CommonResponse addAgvTask(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<Task> tasks = JSON.parseArray(params.toJSONString(), Task.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | @ApiOperation("点对点取消AGV搬运任务") |
| | | @PostMapping("/agv/transTask/cancel") |
| | | public CommonResponse cancelAgvTask(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<Task> tasks = JSON.parseArray(params.toJSONString(), Task.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | } |
| | |
| | | import java.util.Objects; |
| | | |
| | | @RestController |
| | | @RequestMapping("/erp") |
| | | @RequestMapping("/erp1") |
| | | @Api("ERP接口对接") |
| | | public class WmsErpController { |
| | | |
| | |
| | | * @return |
| | | */ |
| | | @ApiOperation("单据修改") |
| | | @PostMapping("/order/upadte") |
| | | @PostMapping("/order/update") |
| | | public CommonResponse modifyOrderDtel(@RequestBody ErpOpParams params) { |
| | | if (Objects.isNull(params)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | |
| | | import com.vincent.rsf.framework.common.R; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.entity.dto.SyncLocsDto; |
| | | import com.vincent.rsf.openApi.entity.params.ExMsgCallbackParams; |
| | | import com.vincent.rsf.openApi.entity.params.RcsPubTaskParams; |
| | | import com.vincent.rsf.openApi.entity.params.SyncRcsLocsParam; |
| | |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | |
| | |
| | | */ |
| | | @ApiOperation("RCS库位信息同步") |
| | | @PostMapping("/sync/locs") |
| | | public R syncLocsToWms(@RequestBody SyncRcsLocsParam params) { |
| | | public CommonResponse syncLocsToWms(@RequestBody SyncRcsLocsParam params) { |
| | | if (Objects.isNull(params)) { |
| | | return R.error("参数不能为空!!"); |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | return R.ok().add(wmsRcsService.syncLocs(params)); |
| | | List<SyncLocsDto> result = wmsRcsService.syncLocs(params); |
| | | return CommonResponse.ok(result); |
| | | } |
| | | |
| | | |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller.example; |
| | | |
| | | import com.vincent.rsf.openApi.entity.constant.Constants; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * API认证示例控制器 |
| | | * 演示如何使用统一的认证机制(支持AppId/AppSecret和Token) |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/api/example/auth") |
| | | public class ApiAuthExampleController { |
| | | private static final Logger log = LoggerFactory.getLogger(ApiAuthExampleController.class); |
| | | |
| | | /** |
| | | * 获取受保护的数据 - 支持AppId/AppSecret或Token认证 |
| | | * |
| | | * @param request HTTP请求 |
| | | * @return 受保护的数据 |
| | | */ |
| | | @GetMapping("/protected-data") |
| | | public Map<String, Object> getProtectedData(HttpServletRequest request) { |
| | | // 从请求属性中获取认证信息(由AppIdAuthenticationFilter设置) |
| | | String appId = (String) request.getAttribute(Constants.REQUEST_ATTR_APP_ID); |
| | | String userId = (String) request.getAttribute(Constants.REQUEST_ATTR_USER_ID); |
| | | |
| | | log.info("访问受保护接口,AppId: {}, UserId: {}", appId, userId); |
| | | |
| | | Map<String, Object> response = new HashMap<>(); |
| | | response.put("code", "200"); |
| | | response.put("message", "访问成功"); |
| | | response.put("data", Map.of( |
| | | "appId", appId, |
| | | "userId", userId, |
| | | "protectedInfo", "这是受保护的数据", |
| | | "authType", userId != null ? "Token" : "AppId/AppSecret", |
| | | "timestamp", System.currentTimeMillis() |
| | | )); |
| | | response.put("success", true); |
| | | |
| | | return response; |
| | | } |
| | | |
| | | /** |
| | | * 获取认证信息 - 支持AppId/AppSecret或Token认证 |
| | | * |
| | | * @param request HTTP请求 |
| | | * @return 认证信息 |
| | | */ |
| | | @GetMapping("/auth-info") |
| | | public Map<String, Object> getAuthInfo(HttpServletRequest request) { |
| | | // 从请求属性中获取认证信息 |
| | | String appId = (String) request.getAttribute(Constants.REQUEST_ATTR_APP_ID); |
| | | String userId = (String) request.getAttribute(Constants.REQUEST_ATTR_USER_ID); |
| | | |
| | | log.info("获取认证信息,AppId: {}, UserId: {}", appId, userId); |
| | | |
| | | Map<String, Object> response = new HashMap<>(); |
| | | response.put("code", "200"); |
| | | response.put("message", "获取认证信息成功"); |
| | | response.put("data", Map.of( |
| | | "appId", appId, |
| | | "userId", userId, |
| | | "authType", userId != null ? "Token" : "AppId/AppSecret", |
| | | "authenticated", appId != null |
| | | )); |
| | | response.put("success", true); |
| | | |
| | | return response; |
| | | } |
| | | |
| | | /** |
| | | * 测试接口 - 不需要认证 |
| | | * |
| | | * @return 测试数据 |
| | | */ |
| | | @GetMapping("/public-test") |
| | | public Map<String, Object> getPublicTest() { |
| | | Map<String, Object> response = new HashMap<>(); |
| | | response.put("code", "200"); |
| | | response.put("message", "公开接口访问成功"); |
| | | response.put("data", Map.of( |
| | | "info", "这是一个不需要认证的公开接口", |
| | | "timestamp", System.currentTimeMillis() |
| | | )); |
| | | response.put("success", true); |
| | | |
| | | return response; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller.example; |
| | | |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.security.utils.AuthUtils; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | |
| | | /** |
| | | * App认证使用示例控制器 |
| | | * |
| | | * 演示如何在控制器中使用AppId认证 |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-05 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/example/auth") |
| | | @Api(tags = "App认证使用示例") |
| | | public class AppAuthExampleController { |
| | | |
| | | /** |
| | | * 需要App认证的接口示例 |
| | | * |
| | | * @param request HTTP请求 |
| | | * @return 响应结果 |
| | | */ |
| | | @ApiOperation("需要App认证的接口示例") |
| | | @GetMapping("/protected") |
| | | public CommonResponse protectedEndpoint(HttpServletRequest request) { |
| | | // 获取认证的AppId |
| | | String appId = AuthUtils.getAppId(request); |
| | | |
| | | // 检查是否已认证 |
| | | if (appId == null) { |
| | | return CommonResponse.error("未通过App认证"); |
| | | } |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("访问成功") |
| | | .setData("认证的AppId: " + appId); |
| | | } |
| | | |
| | | /** |
| | | * 获取当前认证的App信息 |
| | | * |
| | | * @param request HTTP请求 |
| | | * @return App信息 |
| | | */ |
| | | @ApiOperation("获取当前认证的App信息") |
| | | @GetMapping("/app-info") |
| | | public CommonResponse getAppInfo(HttpServletRequest request) { |
| | | String appId = AuthUtils.getAppId(request); |
| | | |
| | | if (appId == null) { |
| | | return CommonResponse.error("未通过App认证"); |
| | | } |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("获取App信息成功") |
| | | .setData("当前AppId: " + appId); |
| | | } |
| | | |
| | | /** |
| | | * 无需认证的公开接口 |
| | | * |
| | | * @return 响应结果 |
| | | */ |
| | | @ApiOperation("无需认证的公开接口") |
| | | @GetMapping("/public") |
| | | public CommonResponse publicEndpoint() { |
| | | return CommonResponse.ok() |
| | | .setMsg("公开接口访问成功") |
| | | .setData("任何人都可以访问此接口"); |
| | | } |
| | | |
| | | /** |
| | | * 检查认证状态 |
| | | * |
| | | * @param request HTTP请求 |
| | | * @return 认证状态 |
| | | */ |
| | | @ApiOperation("检查认证状态") |
| | | @PostMapping("/check-auth") |
| | | public CommonResponse checkAuth(HttpServletRequest request) { |
| | | boolean isAuthenticated = AuthUtils.isAuthenticated(request); |
| | | String appId = AuthUtils.getAppId(request); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("认证检查完成") |
| | | .setData("isAuthenticated: " + isAuthenticated + ", appId: " + appId); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller.example; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.utils.ParamsMapUtils; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * 接口字段映射使用示例 |
| | | * |
| | | * 展示如何在实际业务中使用 FuncMap 进行字段映射 |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("/example/mapping") |
| | | @Api(tags = "字段映射使用示例") |
| | | public class FieldMappingExampleController { |
| | | |
| | | /** |
| | | * 示例1:ERP订单接收 - 字段映射 |
| | | * |
| | | * 场景:接收ERP系统的订单数据,字段名与WMS不一致,需要进行映射 |
| | | * |
| | | * ERP字段 -> WMS字段: |
| | | * orderNumber -> code |
| | | * orderQty -> qty |
| | | * orderAmount -> anfme |
| | | */ |
| | | @ApiOperation("示例:ERP订单接收") |
| | | @PostMapping("/erp/order/receive") |
| | | public CommonResponse erpOrderReceive(@RequestBody JSONObject requestData) { |
| | | log.info("接收到ERP订单数据(映射前):{}", requestData); |
| | | |
| | | // 执行字段映射 |
| | | String appId = "ERP_SYSTEM"; |
| | | String funcId = "ORDER_SYNC"; |
| | | JSONObject mappedData = ParamsMapUtils.apiMaps(appId, funcId, requestData); |
| | | |
| | | log.info("字段映射后的数据:{}", mappedData); |
| | | |
| | | // 这里可以继续调用WMS内部的业务逻辑处理映射后的数据 |
| | | // orderService.createOrder(mappedData); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("订单接收成功") |
| | | .setData(mappedData); |
| | | } |
| | | |
| | | /** |
| | | * 示例2:物料信息同步 - 字段映射 |
| | | * |
| | | * 场景:接收ERP系统的物料数据 |
| | | * |
| | | * ERP字段 -> WMS字段: |
| | | * materialCode -> matnr |
| | | * materialName -> maktx |
| | | * materialSpec -> spec |
| | | */ |
| | | @ApiOperation("示例:物料信息同步") |
| | | @PostMapping("/erp/material/sync") |
| | | public CommonResponse materialSync(@RequestBody JSONObject requestData) { |
| | | log.info("接收到物料数据(映射前):{}", requestData); |
| | | |
| | | // 执行字段映射 |
| | | String appId = "ERP_SYSTEM"; |
| | | String funcId = "MATNR_SYNC"; |
| | | JSONObject mappedData = ParamsMapUtils.apiMaps(appId, funcId, requestData); |
| | | |
| | | log.info("字段映射后的数据:{}", mappedData); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("物料同步成功") |
| | | .setData(mappedData); |
| | | } |
| | | |
| | | /** |
| | | * 示例3:WCS任务创建 - 字段映射 |
| | | * |
| | | * 场景:接收WCS系统的任务创建请求 |
| | | * |
| | | * WCS字段 -> WMS字段: |
| | | * containerCode -> barcode |
| | | * stationCode -> sourceStaNo |
| | | * taskType -> ioType |
| | | */ |
| | | @ApiOperation("示例:WCS任务创建") |
| | | @PostMapping("/wcs/task/create") |
| | | public CommonResponse wcsTaskCreate(@RequestBody JSONObject requestData) { |
| | | log.info("接收到WCS任务数据(映射前):{}", requestData); |
| | | |
| | | // 执行字段映射 |
| | | String appId = "WCS_SYSTEM"; |
| | | String funcId = "TASK_CREATE"; |
| | | JSONObject mappedData = ParamsMapUtils.apiMaps(appId, funcId, requestData); |
| | | |
| | | log.info("字段映射后的数据:{}", mappedData); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("任务创建成功") |
| | | .setData(mappedData); |
| | | } |
| | | |
| | | /** |
| | | * 示例4:批量数据映射 |
| | | * |
| | | * 场景:批量接收订单明细,需要对每条明细进行字段映射 |
| | | */ |
| | | @ApiOperation("示例:批量订单明细映射") |
| | | @PostMapping("/erp/order/batch") |
| | | public CommonResponse batchOrderMapping(@RequestBody JSONObject requestData) { |
| | | log.info("接收到批量订单数据"); |
| | | |
| | | String appId = "ERP_SYSTEM"; |
| | | String funcId = "ORDER_SYNC"; |
| | | |
| | | // 映射订单头 |
| | | JSONObject orderHeader = requestData.getJSONObject("header"); |
| | | JSONObject mappedHeader = ParamsMapUtils.apiMaps(appId, funcId, orderHeader); |
| | | |
| | | // 映射订单明细列表 |
| | | com.alibaba.fastjson.JSONArray items = requestData.getJSONArray("items"); |
| | | com.alibaba.fastjson.JSONArray mappedItems = new com.alibaba.fastjson.JSONArray(); |
| | | |
| | | if (items != null) { |
| | | for (int i = 0; i < items.size(); i++) { |
| | | JSONObject item = items.getJSONObject(i); |
| | | JSONObject mappedItem = ParamsMapUtils.apiMaps(appId, funcId, item); |
| | | mappedItems.add(mappedItem); |
| | | } |
| | | } |
| | | |
| | | // 组装结果 |
| | | JSONObject result = new JSONObject(); |
| | | result.put("header", mappedHeader); |
| | | result.put("items", mappedItems); |
| | | |
| | | log.info("批量映射完成,共处理 {} 条明细", mappedItems.size()); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("批量处理成功") |
| | | .setData(result); |
| | | } |
| | | |
| | | /** |
| | | * 示例5:条件映射 |
| | | * |
| | | * 场景:根据不同的应用来源,使用不同的映射规则 |
| | | */ |
| | | @ApiOperation("示例:条件映射") |
| | | @PostMapping("/dynamic/mapping") |
| | | public CommonResponse dynamicMapping(@RequestBody JSONObject requestData) { |
| | | |
| | | // 从请求中获取应用标识 |
| | | String source = requestData.getString("source"); |
| | | String funcId = requestData.getString("function"); |
| | | |
| | | // 移除元数据字段 |
| | | requestData.remove("source"); |
| | | requestData.remove("function"); |
| | | |
| | | log.info("动态映射 - 来源:{},功能:{}", source, funcId); |
| | | |
| | | // 根据来源选择不同的映射规则 |
| | | String appId; |
| | | switch (source) { |
| | | case "ERP": |
| | | appId = "ERP_SYSTEM"; |
| | | break; |
| | | case "WCS": |
| | | appId = "WCS_SYSTEM"; |
| | | break; |
| | | case "MES": |
| | | appId = "MES_SYSTEM"; |
| | | break; |
| | | default: |
| | | return CommonResponse.error("未知的数据源:" + source); |
| | | } |
| | | |
| | | // 执行映射 |
| | | JSONObject mappedData = ParamsMapUtils.apiMaps(appId, funcId, requestData); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("动态映射成功") |
| | | .setData(mappedData); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller.example; |
| | | |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.utils.ParamsMapUtils; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * JSON属性名递归替换使用示例 |
| | | * |
| | | * 演示如何使用 FuncMap 递归遍历并替换JSON所有层级的属性名称 |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("/example/json-replace") |
| | | @Api(tags = "JSON属性递归替换示例") |
| | | public class JsonReplaceExampleController { |
| | | |
| | | /** |
| | | * 示例1:简单对象的属性替换 |
| | | * |
| | | * 输入: |
| | | * { |
| | | * "orderNumber": "PO001", |
| | | * "orderQty": 100, |
| | | * "orderAmount": 5000.00 |
| | | * } |
| | | * |
| | | * 输出: |
| | | * { |
| | | * "code": "PO001", |
| | | * "qty": 100, |
| | | * "anfme": 5000.00 |
| | | * } |
| | | */ |
| | | @ApiOperation("示例1:简单对象属性替换") |
| | | @PostMapping("/simple-replace") |
| | | public CommonResponse simpleReplace(@RequestBody JSONObject data) { |
| | | log.info("原始数据:{}", data); |
| | | |
| | | // 定义映射规则 |
| | | Map<String, String> mappingRules = new HashMap<>(); |
| | | mappingRules.put("orderNumber", "code"); |
| | | mappingRules.put("orderQty", "qty"); |
| | | mappingRules.put("orderAmount", "anfme"); |
| | | |
| | | // 执行替换 |
| | | JSONObject result = ParamsMapUtils.replaceJsonKeys(data, mappingRules); |
| | | |
| | | log.info("替换后数据:{}", result); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("替换成功") |
| | | .setData(result); |
| | | } |
| | | |
| | | /** |
| | | * 示例2:嵌套对象的深度替换 |
| | | * |
| | | * 输入: |
| | | * { |
| | | * "orderNumber": "PO001", |
| | | * "customer": { |
| | | * "customerName": "张三", |
| | | * "customerPhone": "13800138000", |
| | | * "address": { |
| | | * "cityName": "北京", |
| | | * "streetName": "朝阳路" |
| | | * } |
| | | * } |
| | | * } |
| | | * |
| | | * 输出: |
| | | * { |
| | | * "code": "PO001", |
| | | * "customer": { |
| | | * "name": "张三", |
| | | * "phone": "13800138000", |
| | | * "address": { |
| | | * "city": "北京", |
| | | * "street": "朝阳路" |
| | | * } |
| | | * } |
| | | * } |
| | | */ |
| | | @ApiOperation("示例2:嵌套对象深度替换") |
| | | @PostMapping("/nested-replace") |
| | | public CommonResponse nestedReplace(@RequestBody JSONObject data) { |
| | | log.info("原始嵌套数据:{}", data); |
| | | |
| | | // 定义映射规则(适用于所有层级) |
| | | Map<String, String> mappingRules = new HashMap<>(); |
| | | mappingRules.put("orderNumber", "code"); |
| | | mappingRules.put("customerName", "name"); |
| | | mappingRules.put("customerPhone", "phone"); |
| | | mappingRules.put("cityName", "city"); |
| | | mappingRules.put("streetName", "street"); |
| | | |
| | | // 递归替换所有层级 |
| | | JSONObject result = ParamsMapUtils.replaceJsonKeys(data, mappingRules); |
| | | |
| | | log.info("替换后嵌套数据:{}", result); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("嵌套替换成功") |
| | | .setData(result); |
| | | } |
| | | |
| | | /** |
| | | * 示例3:数组对象的批量替换 |
| | | * |
| | | * 输入: |
| | | * { |
| | | * "orderNumber": "PO001", |
| | | * "items": [ |
| | | * { |
| | | * "materialCode": "MAT001", |
| | | * "materialName": "物料A", |
| | | * "itemQty": 10 |
| | | * }, |
| | | * { |
| | | * "materialCode": "MAT002", |
| | | * "materialName": "物料B", |
| | | * "itemQty": 20 |
| | | * } |
| | | * ] |
| | | * } |
| | | * |
| | | * 输出: |
| | | * { |
| | | * "code": "PO001", |
| | | * "items": [ |
| | | * { |
| | | * "matnr": "MAT001", |
| | | * "maktx": "物料A", |
| | | * "qty": 10 |
| | | * }, |
| | | * { |
| | | * "matnr": "MAT002", |
| | | * "maktx": "物料B", |
| | | * "qty": 20 |
| | | * } |
| | | * ] |
| | | * } |
| | | */ |
| | | @ApiOperation("示例3:数组对象批量替换") |
| | | @PostMapping("/array-replace") |
| | | public CommonResponse arrayReplace(@RequestBody JSONObject data) { |
| | | log.info("原始数组数据:{}", data); |
| | | |
| | | // 定义映射规则 |
| | | Map<String, String> mappingRules = new HashMap<>(); |
| | | mappingRules.put("orderNumber", "code"); |
| | | mappingRules.put("materialCode", "matnr"); |
| | | mappingRules.put("materialName", "maktx"); |
| | | mappingRules.put("itemQty", "qty"); |
| | | |
| | | // 递归替换(包括数组中的所有对象) |
| | | JSONObject result = ParamsMapUtils.replaceJsonKeys(data, mappingRules); |
| | | |
| | | log.info("替换后数组数据:{}", result); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("数组替换成功") |
| | | .setData(result); |
| | | } |
| | | |
| | | /** |
| | | * 示例4:复杂结构的完整替换 |
| | | * |
| | | * 输入包含:嵌套对象 + 数组 + 多层嵌套 |
| | | */ |
| | | @ApiOperation("示例4:复杂结构完整替换") |
| | | @PostMapping("/complex-replace") |
| | | public CommonResponse complexReplace(@RequestBody JSONObject data) { |
| | | log.info("原始复杂数据:{}", data); |
| | | |
| | | // 定义完整的映射规则 |
| | | Map<String, String> mappingRules = new HashMap<>(); |
| | | // 订单字段 |
| | | mappingRules.put("orderNumber", "code"); |
| | | mappingRules.put("orderType", "type"); |
| | | mappingRules.put("orderQty", "qty"); |
| | | mappingRules.put("orderAmount", "anfme"); |
| | | mappingRules.put("orderStatus", "exceStatus"); |
| | | |
| | | // 客户字段 |
| | | mappingRules.put("customerName", "custName"); |
| | | mappingRules.put("customerCode", "custCode"); |
| | | |
| | | // 物料字段 |
| | | mappingRules.put("materialCode", "matnr"); |
| | | mappingRules.put("materialName", "maktx"); |
| | | mappingRules.put("materialSpec", "spec"); |
| | | mappingRules.put("materialUnit", "meins"); |
| | | |
| | | // 其他字段 |
| | | mappingRules.put("warehouseCode", "whCode"); |
| | | mappingRules.put("locationCode", "locCode"); |
| | | |
| | | // 执行递归替换 |
| | | JSONObject result = ParamsMapUtils.replaceJsonKeys(data, mappingRules); |
| | | |
| | | log.info("替换后复杂数据:{}", result); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("复杂替换成功") |
| | | .setData(result); |
| | | } |
| | | |
| | | /** |
| | | * 示例5:使用appId和funcId自动映射 |
| | | * |
| | | * 这个示例展示如何结合数据库配置自动进行字段映射 |
| | | */ |
| | | @ApiOperation("示例5:使用配置自动映射") |
| | | @PostMapping("/auto-replace") |
| | | public CommonResponse autoReplace(@RequestBody JSONObject request) { |
| | | String appId = request.getString("appId"); |
| | | String funcId = request.getString("funcId"); |
| | | JSONObject data = request.getJSONObject("data"); |
| | | |
| | | log.info("自动映射 - appId:{}, funcId:{}, 数据:{}", appId, funcId, data); |
| | | |
| | | // 使用 FuncMap 的 apiMaps 方法,它会自动从数据库加载映射规则并递归替换 |
| | | JSONObject result = ParamsMapUtils.apiMaps(appId, funcId, data); |
| | | |
| | | log.info("自动映射后数据:{}", result); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("自动映射成功") |
| | | .setData(result); |
| | | } |
| | | |
| | | /** |
| | | * 示例6:直接替换JSON数组 |
| | | */ |
| | | @ApiOperation("示例6:直接替换JSON数组") |
| | | @PostMapping("/array-direct-replace") |
| | | public CommonResponse arrayDirectReplace(@RequestBody JSONArray array) { |
| | | log.info("原始JSON数组:{}", array); |
| | | |
| | | // 定义映射规则 |
| | | Map<String, String> mappingRules = new HashMap<>(); |
| | | mappingRules.put("materialCode", "matnr"); |
| | | mappingRules.put("materialName", "maktx"); |
| | | mappingRules.put("stockQty", "qty"); |
| | | mappingRules.put("warehouseCode", "whCode"); |
| | | |
| | | // 直接替换数组 |
| | | JSONArray result = ParamsMapUtils.replaceJsonArrayKeys(array, mappingRules); |
| | | |
| | | log.info("替换后JSON数组:{}", result); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("数组直接替换成功") |
| | | .setData(result); |
| | | } |
| | | |
| | | /** |
| | | * 测试用例:生成示例数据 |
| | | */ |
| | | @ApiOperation("生成测试数据") |
| | | @PostMapping("/generate-test-data") |
| | | public CommonResponse generateTestData() { |
| | | // 创建复杂的测试数据 |
| | | JSONObject testData = new JSONObject(); |
| | | testData.put("orderNumber", "PO20260104001"); |
| | | testData.put("orderType", "PURCHASE"); |
| | | testData.put("orderQty", 500); |
| | | testData.put("orderAmount", 25000.00); |
| | | testData.put("orderStatus", "PENDING"); |
| | | |
| | | // 客户信息 |
| | | JSONObject customer = new JSONObject(); |
| | | customer.put("customerCode", "CUST001"); |
| | | customer.put("customerName", "北京科技有限公司"); |
| | | |
| | | JSONObject address = new JSONObject(); |
| | | address.put("cityName", "北京"); |
| | | address.put("districtName", "朝阳区"); |
| | | address.put("streetName", "建国路88号"); |
| | | customer.put("address", address); |
| | | |
| | | testData.put("customer", customer); |
| | | |
| | | // 订单明细 |
| | | JSONArray items = new JSONArray(); |
| | | for (int i = 1; i <= 3; i++) { |
| | | JSONObject item = new JSONObject(); |
| | | item.put("itemNo", i); |
| | | item.put("materialCode", "MAT00" + i); |
| | | item.put("materialName", "测试物料" + i); |
| | | item.put("materialSpec", "规格" + i); |
| | | item.put("materialUnit", "EA"); |
| | | item.put("itemQty", 100 + i * 50); |
| | | item.put("itemAmount", 5000.00 + i * 1000); |
| | | |
| | | // 仓库信息 |
| | | JSONObject warehouse = new JSONObject(); |
| | | warehouse.put("warehouseCode", "WH0" + i); |
| | | warehouse.put("locationCode", "LOC-A-0" + i); |
| | | item.put("warehouse", warehouse); |
| | | |
| | | items.add(item); |
| | | } |
| | | testData.put("items", items); |
| | | |
| | | return CommonResponse.ok() |
| | | .setMsg("测试数据生成成功") |
| | | .setData(testData); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller.example; |
| | | |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import com.vincent.rsf.openApi.entity.constant.Constants; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.entity.AppAuthParam; |
| | | import com.vincent.rsf.openApi.security.service.AppAuthService; |
| | | import com.vincent.rsf.openApi.security.utils.TokenUtils; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * Token认证示例控制器 |
| | | * 演示如何使用JWT Token进行接口保护 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/api/example/token") |
| | | public class TokenAuthExampleController { |
| | | private static final Logger log = LoggerFactory.getLogger(TokenAuthExampleController.class); |
| | | |
| | | @Resource |
| | | private AppAuthService appAuthService; |
| | | |
| | | /** |
| | | * 获取受保护的数据 - 需要有效的Token |
| | | * |
| | | * @param request HTTP请求 |
| | | * @return 受保护的数据 |
| | | */ |
| | | @GetMapping("/protected-data") |
| | | public Map<String, Object> getProtectedData(HttpServletRequest request) { |
| | | // 从请求属性中获取认证信息(由TokenAuthenticationFilter设置) |
| | | String appId = (String) request.getAttribute(Constants.REQUEST_ATTR_APP_ID); |
| | | String userId = (String) request.getAttribute(Constants.REQUEST_ATTR_USER_ID); |
| | | |
| | | log.info("访问受保护接口,AppId: {}, UserId: {}", appId, userId); |
| | | |
| | | Map<String, Object> response = new HashMap<>(); |
| | | response.put("code", "200"); |
| | | response.put("message", "访问成功"); |
| | | response.put("data", Map.of( |
| | | "appId", appId, |
| | | "userId", userId, |
| | | "protectedInfo", "这是受保护的数据", |
| | | "timestamp", System.currentTimeMillis() |
| | | )); |
| | | response.put("success", true); |
| | | |
| | | return response; |
| | | } |
| | | |
| | | /** |
| | | * 获取用户信息 - 需要有效的Token |
| | | * |
| | | * @param request HTTP请求 |
| | | * @return 用户信息 |
| | | */ |
| | | @GetMapping("/user-info") |
| | | public Map<String, Object> getUserInfo(HttpServletRequest request) { |
| | | // 从请求属性中获取认证信息 |
| | | String appId = (String) request.getAttribute(Constants.REQUEST_ATTR_APP_ID); |
| | | String userId = (String) request.getAttribute(Constants.REQUEST_ATTR_USER_ID); |
| | | |
| | | log.info("获取用户信息,AppId: {}, UserId: {}", appId, userId); |
| | | |
| | | Map<String, Object> response = new HashMap<>(); |
| | | response.put("code", "200"); |
| | | response.put("message", "获取用户信息成功"); |
| | | response.put("data", Map.of( |
| | | "appId", appId, |
| | | "userId", userId, |
| | | "userName", "用户" + (userId != null ? userId : "未知"), |
| | | "role", "USER", |
| | | "permissions", new String[]{"read", "write"} |
| | | )); |
| | | response.put("success", true); |
| | | |
| | | return response; |
| | | } |
| | | |
| | | /** |
| | | * 手动生成Token的示例接口 |
| | | * 注意:在实际应用中,这个接口通常需要其他形式的认证 |
| | | * |
| | | * @param appId 应用ID |
| | | * @param userId 用户ID |
| | | * @return 包含Token的响应 |
| | | */ |
| | | @PostMapping("/generate-token") |
| | | public Map<String, Object> generateToken(@RequestParam String appId, @RequestParam(required = false) String userId) { |
| | | log.info("生成Token,AppId: {}, UserId: {}", appId, userId); |
| | | |
| | | try { |
| | | // 生成Token |
| | | String token = TokenUtils.generateToken(appId, userId); |
| | | |
| | | Map<String, Object> response = new HashMap<>(); |
| | | response.put("code", "200"); |
| | | response.put("message", "Token生成成功"); |
| | | response.put("data", Map.of( |
| | | "token", token, |
| | | "appId", appId, |
| | | "userId", userId, |
| | | "expiresIn", 24 * 60 * 60 // 24小时过期 |
| | | )); |
| | | response.put("success", true); |
| | | |
| | | return response; |
| | | } catch (Exception e) { |
| | | log.error("生成Token失败", e); |
| | | |
| | | Map<String, Object> response = new HashMap<>(); |
| | | response.put("code", "500"); |
| | | response.put("message", "生成Token失败: " + e.getMessage()); |
| | | response.put("data", null); |
| | | response.put("success", false); |
| | | |
| | | return response; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 获取App认证Token |
| | | * |
| | | * @param param 应用ID和应用密钥 |
| | | * @return 认证Token |
| | | */ |
| | | @ApiOperation("获取App认证Token") |
| | | @PostMapping("/getToken") |
| | | public CommonResponse getToken(@RequestBody AppAuthParam param) { |
| | | String appId = param.getAppId(); |
| | | String appSecret = param.getAppSecret(); |
| | | |
| | | if (Cools.isEmpty(appId, appSecret)) { |
| | | return CommonResponse.error("AppId和AppSecret不能为空"); |
| | | } |
| | | |
| | | boolean isValid = appAuthService.validateApp(appId, appSecret); |
| | | if (isValid) { |
| | | String token = appAuthService.generateAppToken(appId, appSecret); |
| | | return CommonResponse.ok() |
| | | .setMsg("获取Token成功") |
| | | .setData(token); |
| | | } else { |
| | | return CommonResponse.error("AppId或AppSecret无效"); |
| | | } |
| | | } |
| | | |
| | | // /** |
| | | // * 验证Token的接口 |
| | | // * |
| | | // * @param token 要验证的Token |
| | | // * @return Token验证结果 |
| | | // */ |
| | | // @PostMapping("/validateToken") |
| | | // public Map<String, Object> validateToken(@RequestParam String token) { |
| | | // log.info("验证Token: {}", token.substring(0, Math.min(10, token.length())) + "..."); |
| | | // |
| | | // boolean isValid = TokenUtils.validateToken(token); |
| | | // |
| | | // Map<String, Object> response = new HashMap<>(); |
| | | // response.put("code", "200"); |
| | | // response.put("message", isValid ? "Token有效" : "Token无效"); |
| | | // response.put("data", Map.of( |
| | | // "valid", isValid, |
| | | // "appId", isValid ? TokenUtils.getAppIdFromToken(token) : null, |
| | | // "userId", isValid ? TokenUtils.getUserIdFromToken(token) : null |
| | | // )); |
| | | // response.put("success", isValid); |
| | | // |
| | | // return response; |
| | | // } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller.phyz; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.entity.phyz.*; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.compress.utils.Lists; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | |
| | | import static com.vincent.rsf.openApi.controller.AuthController.SIMULATED_DATA_ENABLE; |
| | | |
| | | @RestController |
| | | @RequestMapping("/erp") |
| | | @Api("银座新工厂(五期)ERP接口") |
| | | @Slf4j |
| | | public class ERPController { |
| | | |
| | | @ApiOperation("仓库信息同步") |
| | | @PostMapping("/wareHouse/sync") |
| | | public CommonResponse syncWareHouse(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<Warehouse> warehouseList = JSON.parseArray(params.toJSONString(), Warehouse.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | @ApiOperation("物料信息同步") |
| | | @PostMapping("/mat/sync") |
| | | public CommonResponse syncMaterial(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<Material> materialList = JSON.parseArray(params.toJSONString(), Material.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | @ApiOperation("客户信息同步") |
| | | @PostMapping("/customer/sync") |
| | | public CommonResponse syncCustomer(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<Customer> customerList = JSON.parseArray(params.toJSONString(), Customer.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | @ApiOperation("供应商信息同步") |
| | | @PostMapping("/supplier/sync") |
| | | public CommonResponse syncSupplier(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<Supplier> supplierList = JSON.parseArray(params.toJSONString(), Supplier.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | @ApiOperation("入/出库任务通知单") |
| | | @PostMapping("/order/add") |
| | | public CommonResponse addOrder(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<Order> orderList = JSON.parseArray(params.toJSONString(), Order.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | @ApiOperation("入/出库任务通知单取消") |
| | | @PostMapping("/order/cancel") |
| | | public CommonResponse cancelOrder(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<Order> orderList = JSON.parseArray(params.toJSONString(), Order.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | @ApiOperation("库存查询明细") |
| | | @PostMapping("/inventory/details") |
| | | public CommonResponse queryInventoryDetails(@RequestBody JSONObject params) { |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | String x = "[\n" + |
| | | " {\n" + |
| | | " \"locId\": \"LOC-A-01-01\",\n" + |
| | | " \"wareHouseId\": \"WH001\",\n" + |
| | | " \"wareHouseName\": \"原料仓库\",\n" + |
| | | " \"palletId\": \"PALLET001\",\n" + |
| | | " \"matNr\": \"MAT10001\",\n" + |
| | | " \"makTx\": \"钢材Q235\",\n" + |
| | | " \"spec\": \"国标GB/T700-2006\",\n" + |
| | | " \"anfme\": 10.5,\n" + |
| | | " \"unit\": \"吨\",\n" + |
| | | " \"status\": \"可用\",\n" + |
| | | " \"orderType\": 1,\n" + |
| | | " \"orderNo\": \"Order202698921\",\n" + |
| | | " \"prepareType\": 1,\n" + |
| | | " \"planNo\": \"PLAN202601060001\",\n" + |
| | | " \"batch\": \"BATCH20260106001\",\n" + |
| | | " \"stockOrgId\": \"ORG001\"\n" + |
| | | " },\n" + |
| | | " {\n" + |
| | | " \"locId\": \"LOC-B-02-03\",\n" + |
| | | " \"wareHouseId\": \"WH002\",\n" + |
| | | " \"wareHouseName\": \"成品仓库\",\n" + |
| | | " \"palletId\": \"PALLET002\",\n" + |
| | | " \"matNr\": \"MAT20001\",\n" + |
| | | " \"makTx\": \"电机组件\",\n" + |
| | | " \"spec\": \"380V 50Hz\",\n" + |
| | | " \"anfme\": 50,\n" + |
| | | " \"unit\": \"台\",\n" + |
| | | " \"status\": \"可用\",\n" + |
| | | " \"orderType\": \"1\",\n" + |
| | | " \"orderNo\": \"SO202601060001\",\n" + |
| | | " \"prepareType\": 1,\n" + |
| | | " \"planNo\": \"PLAN202601060002\",\n" + |
| | | " \"batch\": \"BATCH20260106002\",\n" + |
| | | " \"stockOrgId\": \"ORG001\"\n" + |
| | | " }\n" + |
| | | "]"; |
| | | return CommonResponse.ok(JSONArray.parseArray(x, InventoryDetails.class)); |
| | | } |
| | | |
| | | InventoryQueryCondition condition = JSON.parseObject(params.toJSONString(), InventoryQueryCondition.class); |
| | | // 数据处理,转发server |
| | | List<InventoryDetails> inventoryDetails = Lists.newArrayList(); |
| | | return new CommonResponse().setCode(200).setData(inventoryDetails); |
| | | } |
| | | |
| | | @ApiOperation("库存查询汇总") |
| | | @PostMapping("/inventory/summary") |
| | | public CommonResponse queryInventorySummary(@RequestBody JSONObject params) { |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | String s = "{\n" + |
| | | " \"code\": 200,\n" + |
| | | " \"msg\": \"操作成功\",\n" + |
| | | " \"data\": [\n" + |
| | | " {\n" + |
| | | " \"wareHouseId\": \"WH001\",\n" + |
| | | " \"wareHouseName\": \"原料仓库\",\n" + |
| | | " \"matNr\": \"MAT10001\",\n" + |
| | | " \"makTx\": \"钢材Q235\",\n" + |
| | | " \"spec\": \"国标GB/T700-2006\",\n" + |
| | | " \"anfme\": 10.5,\n" + |
| | | " \"unit\": \"吨\",\n" + |
| | | " \"stockOrgId\": \"ORG001\",\n" + |
| | | " \"batch\": \"BATCH20260106001\",\n" + |
| | | " \"planNo\": \"Plan20260106006\"\n" + |
| | | " },\n" + |
| | | " {\n" + |
| | | " \"wareHouseId\": \"WH001\",\n" + |
| | | " \"wareHouseName\": \"原料仓库\",\n" + |
| | | " \"matNr\": \"MAT10002\",\n" + |
| | | " \"makTx\": \"铝型材6061\",\n" + |
| | | " \"spec\": \"国标GB/T3190-2008\",\n" + |
| | | " \"anfme\": 20.3,\n" + |
| | | " \"unit\": \"吨\",\n" + |
| | | " \"stockOrgId\": \"ORG001\",\n" + |
| | | " \"batch\": \"BATCH20260106002\",\n" + |
| | | " \"planNo\": \"Plan20260106005\"\n" + |
| | | " },\n" + |
| | | " {\n" + |
| | | " \"wareHouseId\": \"WH002\",\n" + |
| | | " \"wareHouseName\": \"成品仓库\",\n" + |
| | | " \"matNr\": \"MAT30001\",\n" + |
| | | " \"makTx\": \"电机成品\",\n" + |
| | | " \"spec\": \"380V 50Hz 15KW\",\n" + |
| | | " \"anfme\": 100,\n" + |
| | | " \"unit\": \"台\",\n" + |
| | | " \"stockOrgId\": \"ORG001\",\n" + |
| | | " \"batch\": \"BATCH20260106003\",\n" + |
| | | " \"planNo\": \"Plan20260106004\"\n" + |
| | | " }\n" + |
| | | " ]\n" + |
| | | "}"; |
| | | return JSONObject.parseObject(s, CommonResponse.class); |
| | | } |
| | | |
| | | InventoryQueryCondition condition = JSON.parseObject(params.toJSONString(), InventoryQueryCondition.class); |
| | | // 数据处理,转发server |
| | | List<InventorySummary> inventorySummaries = Lists.newArrayList(); |
| | | return new CommonResponse().setCode(200).setData(inventorySummaries); |
| | | |
| | | } |
| | | |
| | | @ApiOperation("盘点结果确认") |
| | | @PostMapping("/check/confirm") |
| | | public CommonResponse checkConfirm(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | CheckOrder checkResult = JSON.parseObject(params.toJSONString(), CheckOrder.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | /** |
| | | * 兼容JSONObject和JSONArray格式数据 |
| | | * |
| | | * @param data json格式参数 |
| | | * @return JSONArray格式数据 |
| | | */ |
| | | public static JSONArray paramsFormat(Object data) { |
| | | if (Objects.isNull(data)) { |
| | | return new JSONArray(); |
| | | } |
| | | |
| | | try { |
| | | String jsonStr = JSON.toJSONString(data); |
| | | if (jsonStr.startsWith("[")) { |
| | | return JSON.parseArray(jsonStr); |
| | | } else if (jsonStr.startsWith("{")) { |
| | | JSONArray params = new JSONArray(); |
| | | params.add(JSON.parseObject(jsonStr)); |
| | | return params; |
| | | } |
| | | } catch (Exception e) { |
| | | // 解析失败,返回空数组 |
| | | log.error("转换参数为json格式错误", e); |
| | | } |
| | | |
| | | // 默认返回空数组 |
| | | return new JSONArray(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller.phyz; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.entity.phyz.*; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | |
| | | import static com.vincent.rsf.openApi.controller.AuthController.SIMULATED_DATA_ENABLE; |
| | | import static com.vincent.rsf.openApi.controller.phyz.ERPController.paramsFormat; |
| | | |
| | | @RestController |
| | | @RequestMapping("/mes") |
| | | @Api("银座新工厂(五期)MES接口") |
| | | public class MESController { |
| | | |
| | | @ApiOperation("备料通知") |
| | | @PostMapping("/callMaterial") |
| | | public CommonResponse callMaterial(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("0")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<MatPreparationOrder> orders = JSON.parseArray(params.toJSONString(), MatPreparationOrder.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | @ApiOperation("简单生产领料、退料AGV任务") |
| | | @PostMapping("/transTask/simpleProduction") |
| | | public CommonResponse simpleProductionTask(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("0")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<SimpleProductionTask> tasks = JSON.parseArray(params.toJSONString(), SimpleProductionTask.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | @ApiOperation("空托出库") |
| | | @PostMapping("/emptyTray/callOut") |
| | | public CommonResponse callOutEmptyTray(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<Task> tasks = JSON.parseArray(params.toJSONString(), Task.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | @ApiOperation("空托入库") |
| | | @PostMapping("/emptyTray/backIn") |
| | | public CommonResponse emptyTrayBackIn(@RequestBody Object objParams) { |
| | | if (Objects.isNull(objParams)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | // 返回模拟数据 |
| | | if (SIMULATED_DATA_ENABLE.equals("1")) { |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | JSONArray params = paramsFormat(objParams); |
| | | List<Task> tasks = JSON.parseArray(params.toJSONString(), Task.class); |
| | | // 数据处理,转发server |
| | | return CommonResponse.ok(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller.platform; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.vincent.rsf.openApi.entity.app.ApiFunction; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.service.ApiFunctionService; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * ApiFunction管理Controller |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/api/function") |
| | | @Api(tags = "接口功能管理") |
| | | public class ApiFunctionController { |
| | | |
| | | @Autowired |
| | | private ApiFunctionService apiFunctionService; |
| | | |
| | | @ApiOperation("分页查询接口功能列表") |
| | | @GetMapping("/page") |
| | | public CommonResponse page(@RequestParam(defaultValue = "1") Integer current, |
| | | @RequestParam(defaultValue = "10") Integer size) { |
| | | Page<ApiFunction> page = apiFunctionService.page(new Page<>(current, size)); |
| | | return CommonResponse.ok().setData(page); |
| | | } |
| | | |
| | | @ApiOperation("查询所有接口功能") |
| | | @GetMapping("/list") |
| | | public CommonResponse list() { |
| | | List<ApiFunction> list = apiFunctionService.list(); |
| | | return CommonResponse.ok().setData(list); |
| | | } |
| | | |
| | | @ApiOperation("根据ID查询接口功能") |
| | | @GetMapping("/{id}") |
| | | public CommonResponse getById(@PathVariable String id) { |
| | | ApiFunction function = apiFunctionService.getById(id); |
| | | return CommonResponse.ok().setData(function); |
| | | } |
| | | |
| | | @ApiOperation("新增接口功能") |
| | | @PostMapping |
| | | public CommonResponse save(@RequestBody ApiFunction function) { |
| | | boolean result = apiFunctionService.save(function); |
| | | if (result) { |
| | | apiFunctionService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("新增成功"); |
| | | } |
| | | return CommonResponse.error("新增失败"); |
| | | } |
| | | |
| | | @ApiOperation("更新接口功能") |
| | | @PutMapping |
| | | public CommonResponse update(@RequestBody ApiFunction function) { |
| | | boolean result = apiFunctionService.updateById(function); |
| | | if (result) { |
| | | apiFunctionService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("更新成功"); |
| | | } |
| | | return CommonResponse.error("更新失败"); |
| | | } |
| | | |
| | | @ApiOperation("删除接口功能") |
| | | @DeleteMapping("/{id}") |
| | | public CommonResponse delete(@PathVariable String id) { |
| | | boolean result = apiFunctionService.removeById(id); |
| | | if (result) { |
| | | apiFunctionService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("删除成功"); |
| | | } |
| | | return CommonResponse.error("删除失败"); |
| | | } |
| | | |
| | | @ApiOperation("批量删除接口功能") |
| | | @DeleteMapping("/batch") |
| | | public CommonResponse deleteBatch(@RequestBody List<String> ids) { |
| | | boolean result = apiFunctionService.removeByIds(ids); |
| | | if (result) { |
| | | apiFunctionService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("批量删除成功"); |
| | | } |
| | | return CommonResponse.error("批量删除失败"); |
| | | } |
| | | |
| | | @ApiOperation("刷新功能缓存") |
| | | @PostMapping("/refresh") |
| | | public CommonResponse refresh() { |
| | | apiFunctionService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("缓存刷新成功"); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller.platform; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.vincent.rsf.openApi.entity.app.ApiMap; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.service.ApiMapService; |
| | | import com.vincent.rsf.openApi.utils.ParamsMapUtils; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * ApiMap管理Controller |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/api/map") |
| | | @Api(tags = "字段映射管理") |
| | | public class ApiMapController { |
| | | |
| | | @Autowired |
| | | private ApiMapService apiMapService; |
| | | |
| | | |
| | | @ApiOperation("分页查询字段映射列表") |
| | | @GetMapping("/page") |
| | | public CommonResponse page(@RequestParam(defaultValue = "1") Integer current, |
| | | @RequestParam(defaultValue = "10") Integer size, |
| | | @RequestParam(required = false) String appId, |
| | | @RequestParam(required = false) String funcId) { |
| | | LambdaQueryWrapper<ApiMap> wrapper = new LambdaQueryWrapper<>(); |
| | | if (appId != null && !appId.isEmpty()) { |
| | | wrapper.eq(ApiMap::getAppId, appId); |
| | | } |
| | | if (funcId != null && !funcId.isEmpty()) { |
| | | wrapper.eq(ApiMap::getFuncId, funcId); |
| | | } |
| | | Page<ApiMap> page = apiMapService.page(new Page<>(current, size), wrapper); |
| | | return CommonResponse.ok().setData(page); |
| | | } |
| | | |
| | | @ApiOperation("查询所有字段映射") |
| | | @GetMapping("/list") |
| | | public CommonResponse list(@RequestParam(required = false) String appId, |
| | | @RequestParam(required = false) String funcId) { |
| | | LambdaQueryWrapper<ApiMap> wrapper = new LambdaQueryWrapper<>(); |
| | | if (appId != null && !appId.isEmpty()) { |
| | | wrapper.eq(ApiMap::getAppId, appId); |
| | | } |
| | | if (funcId != null && !funcId.isEmpty()) { |
| | | wrapper.eq(ApiMap::getFuncId, funcId); |
| | | } |
| | | List<ApiMap> list = apiMapService.list(wrapper); |
| | | return CommonResponse.ok().setData(list); |
| | | } |
| | | |
| | | @ApiOperation("根据ID查询字段映射") |
| | | @GetMapping("/{id}") |
| | | public CommonResponse getById(@PathVariable Integer id) { |
| | | ApiMap map = apiMapService.getById(id); |
| | | return CommonResponse.ok().setData(map); |
| | | } |
| | | |
| | | @ApiOperation("新增字段映射") |
| | | @PostMapping |
| | | public CommonResponse save(@RequestBody ApiMap map) { |
| | | boolean result = apiMapService.save(map); |
| | | if (result) { |
| | | apiMapService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("新增成功"); |
| | | } |
| | | return CommonResponse.error("新增失败"); |
| | | } |
| | | |
| | | @ApiOperation("批量新增字段映射") |
| | | @PostMapping("/batch") |
| | | public CommonResponse saveBatch(@RequestBody List<ApiMap> maps) { |
| | | boolean result = apiMapService.saveBatch(maps); |
| | | if (result) { |
| | | apiMapService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("批量新增成功"); |
| | | } |
| | | return CommonResponse.error("批量新增失败"); |
| | | } |
| | | |
| | | @ApiOperation("更新字段映射") |
| | | @PutMapping |
| | | public CommonResponse update(@RequestBody ApiMap map) { |
| | | boolean result = apiMapService.updateById(map); |
| | | if (result) { |
| | | apiMapService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("更新成功"); |
| | | } |
| | | return CommonResponse.error("更新失败"); |
| | | } |
| | | |
| | | @ApiOperation("删除字段映射") |
| | | @DeleteMapping("/{id}") |
| | | public CommonResponse delete(@PathVariable Integer id) { |
| | | boolean result = apiMapService.removeById(id); |
| | | if (result) { |
| | | apiMapService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("删除成功"); |
| | | } |
| | | return CommonResponse.error("删除失败"); |
| | | } |
| | | |
| | | @ApiOperation("批量删除字段映射") |
| | | @DeleteMapping("/batch") |
| | | public CommonResponse deleteBatch(@RequestBody List<Integer> ids) { |
| | | boolean result = apiMapService.removeByIds(ids); |
| | | if (result) { |
| | | apiMapService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("批量删除成功"); |
| | | } |
| | | return CommonResponse.error("批量删除失败"); |
| | | } |
| | | |
| | | @ApiOperation("刷新映射缓存") |
| | | @PostMapping("/refresh") |
| | | public CommonResponse refresh() { |
| | | apiMapService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("缓存刷新成功"); |
| | | } |
| | | |
| | | // @ApiOperation("刷新所有缓存") |
| | | // @PostMapping("/refresh/all") |
| | | // public CommonResponse refreshAll() { |
| | | // new ParamsMapUtils().refreshAll(); |
| | | // return CommonResponse.ok().setMsg("所有缓存刷新成功"); |
| | | // } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller.platform; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.vincent.rsf.openApi.entity.app.App; |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import com.vincent.rsf.openApi.service.AppService; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * App管理Controller |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/api/app") |
| | | @Api(tags = "应用管理") |
| | | public class AppController { |
| | | |
| | | @Autowired |
| | | private AppService appService; |
| | | |
| | | @ApiOperation("分页查询应用列表") |
| | | @GetMapping("/page") |
| | | public CommonResponse page(@RequestParam(defaultValue = "1") Integer current, |
| | | @RequestParam(defaultValue = "10") Integer size) { |
| | | Page<App> page = appService.page(new Page<>(current, size)); |
| | | return CommonResponse.ok().setData(page); |
| | | } |
| | | |
| | | @ApiOperation("查询所有应用") |
| | | @GetMapping("/list") |
| | | public CommonResponse list() { |
| | | List<App> list = appService.list(); |
| | | return CommonResponse.ok().setData(list); |
| | | } |
| | | |
| | | @ApiOperation("根据ID查询应用") |
| | | @GetMapping("/{id}") |
| | | public CommonResponse getById(@PathVariable String id) { |
| | | App app = appService.getById(id); |
| | | return CommonResponse.ok().setData(app); |
| | | } |
| | | |
| | | @ApiOperation("新增应用") |
| | | @PostMapping |
| | | public CommonResponse save(@RequestBody App app) { |
| | | boolean result = appService.save(app); |
| | | if (result) { |
| | | appService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("新增成功"); |
| | | } |
| | | return CommonResponse.error("新增失败"); |
| | | } |
| | | |
| | | @ApiOperation("更新应用") |
| | | @PutMapping |
| | | public CommonResponse update(@RequestBody App app) { |
| | | boolean result = appService.updateById(app); |
| | | if (result) { |
| | | appService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("更新成功"); |
| | | } |
| | | return CommonResponse.error("更新失败"); |
| | | } |
| | | |
| | | @ApiOperation("删除应用") |
| | | @DeleteMapping("/{id}") |
| | | public CommonResponse delete(@PathVariable String id) { |
| | | boolean result = appService.removeById(id); |
| | | if (result) { |
| | | appService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("删除成功"); |
| | | } |
| | | return CommonResponse.error("删除失败"); |
| | | } |
| | | |
| | | @ApiOperation("批量删除应用") |
| | | @DeleteMapping("/batch") |
| | | public CommonResponse deleteBatch(@RequestBody List<String> ids) { |
| | | boolean result = appService.removeByIds(ids); |
| | | if (result) { |
| | | appService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("批量删除成功"); |
| | | } |
| | | return CommonResponse.error("批量删除失败"); |
| | | } |
| | | |
| | | @ApiOperation("刷新应用缓存") |
| | | @PostMapping("/refresh") |
| | | public CommonResponse refresh() { |
| | | appService.refreshCache(); |
| | | return CommonResponse.ok().setMsg("缓存刷新成功"); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | /** |
| | | * App认证参数 |
| | | * |
| | | * 用于AppId和AppSecret的认证请求 |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-05 |
| | | */ |
| | | @Data |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "AppAuthParam", description = "App认证参数") |
| | | public class AppAuthParam { |
| | | |
| | | @ApiModelProperty(value = "应用ID", required = true) |
| | | private String appId; |
| | | |
| | | @ApiModelProperty(value = "应用密钥", required = true) |
| | | private String appSecret; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.app; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.io.Serial; |
| | | import java.io.Serializable; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | @Data |
| | | @TableName("open_api_foreign_log") |
| | | public class ApiForeignLog implements Serializable { |
| | | |
| | | @Serial |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * ID |
| | | */ |
| | | @ApiModelProperty(value= "ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 名称空间 |
| | | */ |
| | | @ApiModelProperty(value= "名称空间") |
| | | private String namespace; |
| | | |
| | | /** |
| | | * 接口地址 |
| | | */ |
| | | @ApiModelProperty(value= "接口地址") |
| | | private String url; |
| | | |
| | | /** |
| | | * 平台密钥 |
| | | */ |
| | | @ApiModelProperty(value= "平台密钥") |
| | | private String appkey; |
| | | |
| | | /** |
| | | * 时间戳 |
| | | */ |
| | | @ApiModelProperty(value= "时间戳") |
| | | private String timestamp; |
| | | |
| | | /** |
| | | * 客户端IP |
| | | */ |
| | | @ApiModelProperty(value= "客户端IP") |
| | | private String clientIp; |
| | | |
| | | /** |
| | | * 请求内容 |
| | | */ |
| | | @ApiModelProperty(value= "请求内容") |
| | | private String request; |
| | | |
| | | /** |
| | | * 响应内容 |
| | | */ |
| | | @ApiModelProperty(value= "响应内容") |
| | | private String response; |
| | | |
| | | /** |
| | | * 消耗时间 |
| | | */ |
| | | @ApiModelProperty(value= "消耗时间") |
| | | private Integer spendTime; |
| | | |
| | | /** |
| | | * 异常内容 |
| | | */ |
| | | @ApiModelProperty(value= "异常内容") |
| | | private String err; |
| | | |
| | | /** |
| | | * 结果 1: 成功 0: 失败 |
| | | */ |
| | | @ApiModelProperty(value= "结果 1: 成功 0: 失败 ") |
| | | private Integer result; |
| | | |
| | | /** |
| | | * 用户 |
| | | */ |
| | | @ApiModelProperty(value= "用户") |
| | | private Long userId; |
| | | |
| | | /** |
| | | * 所属机构 |
| | | */ |
| | | @ApiModelProperty(value= "所属机构") |
| | | private Long tenantId; |
| | | |
| | | /** |
| | | * 添加时间 |
| | | */ |
| | | @ApiModelProperty(value= "添加时间") |
| | | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") |
| | | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") |
| | | private Date createTime; |
| | | |
| | | /** |
| | | * 备注 |
| | | */ |
| | | @ApiModelProperty(value= "备注") |
| | | private String memo; |
| | | |
| | | public String getResult$(){ |
| | | if (null == this.result){ return null; } |
| | | switch (this.result){ |
| | | case 1: |
| | | return "成功"; |
| | | case 0: |
| | | return "失败"; |
| | | default: |
| | | return String.valueOf(this.result); |
| | | } |
| | | } |
| | | |
| | | public String getCreateTime$(){ |
| | | if (Cools.isEmpty(this.createTime)){ |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.app; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serial; |
| | | import java.io.Serializable; |
| | | |
| | | @Data |
| | | @TableName("open_api_function") |
| | | public class ApiFunction implements Serializable { |
| | | |
| | | @Serial |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * functionId |
| | | */ |
| | | @ApiModelProperty(value= "functionId") |
| | | @TableId(value = "id") |
| | | private String id; |
| | | |
| | | /** |
| | | * functionName |
| | | */ |
| | | @ApiModelProperty(value = "functionName") |
| | | private String name; |
| | | |
| | | /** |
| | | * functionType,1 WMS接收;2 WMS发送; |
| | | */ |
| | | @ApiModelProperty(value = "functionType") |
| | | private Integer type; |
| | | |
| | | /** |
| | | * functionUrl,对应rsf-server的controller接口相对路径 |
| | | */ |
| | | @ApiModelProperty(value = "functionUrl") |
| | | private String url; |
| | | |
| | | /** |
| | | * 租户id |
| | | */ |
| | | @ApiModelProperty(value = "租户id") |
| | | private Long tenant_id; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.app; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serial; |
| | | import java.io.Serializable; |
| | | |
| | | @Data |
| | | @TableName("open_api_attribute_map") |
| | | public class ApiMap implements Serializable { |
| | | |
| | | @Serial |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * id |
| | | */ |
| | | @ApiModelProperty(value= "id") |
| | | @TableId(value = "id") |
| | | private Integer id; |
| | | |
| | | /** |
| | | * appId |
| | | */ |
| | | @ApiModelProperty(value= "appId") |
| | | private String appId; |
| | | |
| | | /** |
| | | * func_id |
| | | */ |
| | | @ApiModelProperty(value= "func_id") |
| | | private String funcId; |
| | | |
| | | /** |
| | | * func_type |
| | | */ |
| | | @ApiModelProperty(value= "func_type") |
| | | private String funcType; |
| | | |
| | | /** |
| | | * source_attribute |
| | | */ |
| | | @ApiModelProperty(value = "source_attribute") |
| | | private String sourceAttribute; |
| | | |
| | | // /** |
| | | // * source_type,源属性类型,Integer、String、Double、JSONObject、JSONArray |
| | | // */ |
| | | // @ApiModelProperty(value = "source_type") |
| | | // private String sourceType; |
| | | |
| | | /** |
| | | * target_attribute |
| | | */ |
| | | @ApiModelProperty(value = "target_attribute") |
| | | private String targetAttribute; |
| | | |
| | | // /** |
| | | // * target_type,目标属性类型,Integer、String、Double、JSONObject、JSONArray |
| | | // */ |
| | | // @ApiModelProperty(value = "target_type") |
| | | // private String targetType; |
| | | |
| | | /** |
| | | * 是否启用,0 未启用;1 启用; |
| | | */ |
| | | @ApiModelProperty(value = "是否启用,0 未启用;1 启用;") |
| | | private Integer enable; |
| | | |
| | | /** |
| | | * 租户id |
| | | */ |
| | | @ApiModelProperty(value = "租户id") |
| | | private Long tenant_id; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.app; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serial; |
| | | import java.io.Serializable; |
| | | |
| | | @Data |
| | | @TableName("open_api_app") |
| | | public class App implements Serializable { |
| | | |
| | | @Serial |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * appId |
| | | */ |
| | | @ApiModelProperty(value= "appId") |
| | | @TableId(value = "id") |
| | | private String id; |
| | | |
| | | /** |
| | | * appScrect |
| | | */ |
| | | @ApiModelProperty(value = "appScrect") |
| | | private String screct; |
| | | |
| | | /** |
| | | * appName |
| | | */ |
| | | @ApiModelProperty(value = "appName") |
| | | private String name; |
| | | |
| | | /** |
| | | * appUrl |
| | | */ |
| | | @ApiModelProperty(value = "appUrl") |
| | | private String url; |
| | | |
| | | /** |
| | | * 是否启用,0 未启用;1 启用; |
| | | */ |
| | | @ApiModelProperty(value = "是否启用,0 未启用;1 启用;") |
| | | private Integer enable; |
| | | |
| | | /** |
| | | * 租户id |
| | | */ |
| | | @ApiModelProperty(value = "租户id") |
| | | private Long tenant_id; |
| | | } |
| | |
| | | /** |
| | | * 无权限错误码 |
| | | */ |
| | | public static final int UNAUTHORIZED_CODE = 403; |
| | | public static final String UNAUTHORIZED_CODE = "403"; |
| | | |
| | | /** |
| | | * 无权限提示信息 |
| | |
| | | /** |
| | | * 未认证错误码 |
| | | */ |
| | | public static final int UNAUTHENTICATED_CODE = 401; |
| | | public static final String UNAUTHENTICATED_CODE = "401"; |
| | | |
| | | /** |
| | | * 未认证提示信息 |
| | |
| | | */ |
| | | public static final Integer TASK_SORT_MIN_VALUE = 0; |
| | | |
| | | // AppId认证相关常量 |
| | | public static final String HEADER_APP_ID = "appId"; //X-App-Id |
| | | public static final String HEADER_APP_SECRET = "appSecret"; //X-App-Secret |
| | | public static final String HEADER_AUTHORIZATION = "authorization"; //Authorization |
| | | public static final String TOKEN_PREFIX = "Zoneyung"; |
| | | public static final String REQUEST_ATTR_APP_ID = "appId"; //request_app_id |
| | | public static final String REQUEST_ATTR_USER_ID = "appSecret"; //request_user_id |
| | | public static final String REQUEST_ATTR_APP_INFO = "APP_INFO"; |
| | | } |
| | |
| | | package com.vincent.rsf.openApi.entity.dto; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | |
| | | @ApiModelProperty("响应结果") |
| | | private Object data; |
| | | |
| | | /** |
| | | * 成功响应 |
| | | */ |
| | | public static CommonResponse ok() { |
| | | CommonResponse response = new CommonResponse(); |
| | | response.setCode(200); |
| | | response.setMsg("操作成功"); |
| | | JSONObject jsonObject = new JSONObject(); |
| | | jsonObject.put("result", "SUCCESS"); |
| | | response.setData(jsonObject); |
| | | return response; |
| | | } |
| | | |
| | | public static CommonResponse ok(Object data) { |
| | | CommonResponse response = new CommonResponse(); |
| | | response.setCode(200); |
| | | response.setMsg("操作成功"); |
| | | response.setData(data); |
| | | return response; |
| | | } |
| | | |
| | | /** |
| | | * 失败响应 |
| | | */ |
| | | public static CommonResponse error(String msg) { |
| | | CommonResponse response = new CommonResponse(); |
| | | response.setCode(500); |
| | | response.setMsg(msg); |
| | | return response; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "CheckOrder", description = "盘点单") |
| | | public class CheckOrder { |
| | | |
| | | // 盘点单号,唯一标识 |
| | | @NotNull |
| | | @JsonProperty("orderNo") |
| | | private String orderNo; |
| | | // 盘点单名称 |
| | | @JsonProperty("orderName") |
| | | private String orderName; |
| | | // 确认时间,秒级时间戳 |
| | | private Long checkTime; |
| | | // 单据类型,需ERP确定上报时需要使用哪个类型 |
| | | private String orderTypeId; |
| | | // 资产组织 |
| | | private String assetOrgId; |
| | | // 盘点方案,需ERP确定上报时使用哪个方案 |
| | | private String inventSchemeId; |
| | | // 确认结果 |
| | | private List<CheckResult> checkItems; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "CheckResult", description = "盘点结果") |
| | | public class CheckResult { |
| | | |
| | | // 仓库编码 |
| | | private String wareHouseId; |
| | | // 物料编码 |
| | | private String matNr; |
| | | // 库存数量 |
| | | private Double qty; |
| | | // 确认后库存数量,实际库存以WMS盘点为准,只保存不做库存数量调整 |
| | | private Double checkQty; |
| | | // 是否解冻,0 解冻;1 不解冻; |
| | | private Integer defrost; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "Customer", description = "客户信息同步") |
| | | public class Customer { |
| | | |
| | | // 客户编码,唯一标识 |
| | | @NotNull |
| | | @JsonProperty("customerId") |
| | | private String customerId; |
| | | // 客户名称 |
| | | @JsonProperty("customerName") |
| | | private String customerName; |
| | | // 客户昵称 |
| | | private String customerNickName; |
| | | // 客户分组,国内,国外 |
| | | private String customerGroup; |
| | | // 联系人 |
| | | private String contact; |
| | | // 联系电话 |
| | | private String telephone; |
| | | // 邮箱 |
| | | private String email; |
| | | // 地址 |
| | | private String address; |
| | | // 操作类型,1 新增;2 修改;3禁用;4 反禁用; |
| | | @JsonProperty("operateType") |
| | | private Integer operateType; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "InventoryDetails", description = "库存明细") |
| | | public class InventoryDetails { |
| | | |
| | | // 库位编码 |
| | | private String locId; |
| | | // 仓库编码 |
| | | private String wareHouseId; |
| | | // 仓库名称 |
| | | private String wareHouseName; |
| | | // 托盘码,如果一个托盘上备了2个工单号,则分为2条 |
| | | private String palletId; |
| | | // 物料编码 |
| | | private String matNr; |
| | | // 物料名称 |
| | | private String makTx; |
| | | // 规格 |
| | | private String spec; |
| | | // 数量,小数点最长6位 |
| | | private Double anfme; |
| | | // 单位 |
| | | private String unit; |
| | | // 库存状态,待ERP补充,示例:可用;冻结; |
| | | private String status; |
| | | // 绑定的订单类型,1 出库单;2 入库单;3 备料单;未绑定时为空 |
| | | private Integer orderType; |
| | | // 订单号、备料单号,未绑定时为空 |
| | | private String orderNo; |
| | | // 备料类型,为备料单时有,1 正常领料;2 生产补料; |
| | | private Integer prepareType; |
| | | // 计划跟踪号 |
| | | private String planNo; |
| | | // 批次号 |
| | | private String batch; |
| | | // 库存组织 |
| | | private String stockOrgId; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "InventoryQueryCondition", description = "库存查询条件") |
| | | public class InventoryQueryCondition { |
| | | |
| | | // 仓库编码 |
| | | private String wareHouseId; |
| | | // 物料编码 |
| | | private String matNr; |
| | | // 物料组 |
| | | private String matGroup; |
| | | // 库位编码 |
| | | private String locId; |
| | | // 订单号/工单号/MES工单号 |
| | | private String orderNo; |
| | | // 数计划跟踪号 |
| | | private String planNo; |
| | | // 批次号 |
| | | private String batch; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "InventorySummary", description = "库存汇总") |
| | | public class InventorySummary { |
| | | |
| | | // 仓库编码 |
| | | private String wareHouseId; |
| | | // 仓库名称 |
| | | private String wareHouseName; |
| | | // 物料编码 |
| | | private String matNr; |
| | | // 物料名称 |
| | | private String makTx; |
| | | // 规格 |
| | | private String spec; |
| | | // 数量,小数点最长6位 |
| | | private Double anfme; |
| | | // 单位 |
| | | private String unit; |
| | | // 库存组织 |
| | | private String stockOrgId; |
| | | // 批号 |
| | | private String batch; |
| | | // 计划跟踪号 |
| | | private String planNo; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "MatPreparationOrder", description = "备料单") |
| | | public class MatPreparationOrder { |
| | | |
| | | // 备料单号,唯一标识 |
| | | @NotNull |
| | | @JsonProperty("orderNo") |
| | | private String orderNo; |
| | | // 备料单创建时间,秒级时间戳 |
| | | @JsonProperty("orderTime") |
| | | private Long orderTime; |
| | | // 备料类型,为备料单时有,1 正常领料;2 生产补料; |
| | | @JsonProperty("prepareType") |
| | | private Integer prepareType; |
| | | // 备料详情 |
| | | private List<MatPreparationOrderItem> orderItems; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "MatPreparationOrderItem", description = "备料单明细") |
| | | public class MatPreparationOrderItem { |
| | | |
| | | // 物料编码 |
| | | @NotNull |
| | | @JsonProperty("matNr") |
| | | private String matNr; |
| | | // 备料数量 |
| | | @JsonProperty("anfme") |
| | | private Double anfme; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "Material", description = "物料信息同步") |
| | | public class Material { |
| | | |
| | | // 物料编码,唯一标识 |
| | | @NotNull |
| | | @JsonProperty("matNr") |
| | | private String matNr; |
| | | // 物料名称 |
| | | @JsonProperty("makTx") |
| | | private String makTx; |
| | | // 物料分组编码 |
| | | @JsonProperty("groupId") |
| | | private String groupId; |
| | | // 物料分组 |
| | | @JsonProperty("groupName") |
| | | private String groupName; |
| | | // 型号 |
| | | private String model; |
| | | // 重量 |
| | | private Double weight; |
| | | // 颜色 |
| | | private String color; |
| | | // 尺寸 |
| | | private String size; |
| | | // 规格 |
| | | private String spec; |
| | | // 描述 |
| | | private String describe; |
| | | // 单位 |
| | | private String unit; |
| | | // 基本单位,银座有双单位,如长度和重量,可能需要转换 |
| | | private String baseUnit; |
| | | // 使用组织编码 |
| | | private String useOrgId; |
| | | // 使用组织名称 |
| | | private String useOrgName; |
| | | // 物料属性,外购等 |
| | | private String erpClsID; |
| | | // 操作类型,1 新增;2 修改;3禁用;4 反禁用; |
| | | @JsonProperty("operateType") |
| | | private Integer operateType; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "Order", description = "入/出库通知单") |
| | | public class Order { |
| | | |
| | | // 入/出库订单号,唯一标识 |
| | | @NotNull |
| | | @JsonProperty("orderNo") |
| | | private String orderNo; |
| | | // 单据内码,唯一标识 |
| | | @JsonProperty("orderInternalCode") |
| | | private String orderInternalCode; |
| | | // 业务类型,示例: |
| | | // 入库:收料通知单(PUR_ReceiveBill)、采购入库单(STK_InStock)、退料申请单(PUR_MRAPP)、采购退料单(PUR_MRB)、 |
| | | // 退货通知单(SAL_RETURNNOTICE)、销售退货单(SAL_RETURNSTOCK)、生产退料单(PRD_ReturnMtrl)、生产入库单(PRD_INSTOCK)/生产汇报单(PRD_MORPT)、 |
| | | // 其他入库单(STK_MISCELLANEOUS) |
| | | // 出库:发货通知单(SAL_DELIVERYNOTICE)、销售出库单(SAL_OUTSTOCK)、出库申请单(STK_OutStockApply)、生产领料单(PRD_PickMtrl)、 |
| | | // 生产补料单(PRD_FeedMtrl)、其他出库单(STK_MisDelivery) |
| | | // 调拨:调拨申请单(STK_TRANSFERAPPLY)、直接调拨单(STK_TransferDirect) |
| | | private String wkType; |
| | | // 订单类型,1 出库单;2 入库单;3 调拨单; |
| | | private String type; |
| | | // 创建日期,时间戳,精确到秒 |
| | | private Long createTime; |
| | | // 业务日期,对账使用,时间戳,精确到秒 |
| | | private Long businessTime; |
| | | // 库存方向 |
| | | private String stockDirect; |
| | | // 订单明细 |
| | | private List<OrderItem> orderItems; |
| | | // 出入库接驳站点,出库时将物料出库后运输至该站点,入库时从该站点将物料运回库中 |
| | | private String stationId; |
| | | |
| | | // 客户编码 |
| | | private String customerId; |
| | | // 客户名称 |
| | | private String customerName; |
| | | // 供应商编码 |
| | | private String supplierId; |
| | | // 供应商名称 |
| | | private String supplierName; |
| | | // 收料/发货组织 |
| | | private String stockOrgId; |
| | | // 收料/发货组织名称 |
| | | private String stockOrgName; |
| | | // 采购组织 |
| | | private String purchaseOrgId; |
| | | // 采购组织名称 |
| | | private String purchaseOrgName; |
| | | // 采购员 |
| | | private String purchaseUserId; |
| | | // 采购员名称 |
| | | private String purchaseUserName; |
| | | // 生产组织 |
| | | private String prdOrgId; |
| | | // 生产组织名称 |
| | | private String prdOrgName; |
| | | // 销售组织 |
| | | private String saleOrgId; |
| | | // 销售组织名称 |
| | | private String saleOrgName; |
| | | // 销售员 |
| | | private String saleUserId; |
| | | // 销售员名称 |
| | | private String saleUserName; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "OrderItem", description = "入/出库通知单明细") |
| | | public class OrderItem { |
| | | |
| | | // 计划跟踪号 |
| | | @NotNull |
| | | @JsonProperty("planNo") |
| | | private String planNo; |
| | | // 行内码,唯一标识 |
| | | @JsonProperty("lineId") |
| | | private String lineId; |
| | | // 物料编码,唯一标识 |
| | | @NotNull |
| | | @JsonProperty("matNr") |
| | | private String matNr; |
| | | // 物料名称 |
| | | @JsonProperty("makTx") |
| | | private String makTx; |
| | | // 规格 |
| | | private String spec; |
| | | // 型号 |
| | | private String model; |
| | | // 数量 |
| | | private Double anfme; |
| | | // 批号 |
| | | private String batch; |
| | | // 单位 |
| | | private String unit; |
| | | // 基本单位 |
| | | private String baseUnitId; |
| | | // 计价单位 |
| | | private String priceUnitId; |
| | | // 建议目标仓库 |
| | | private String palletId; |
| | | // 调出仓 |
| | | private String targetWareHouseId; |
| | | // 业务日期,对账使用,时间戳,精确到秒 |
| | | private String sourceWareHouseId; |
| | | // 入库类型 |
| | | private String inStockType; |
| | | // 货主类型 |
| | | private String ownerTypeId; |
| | | // 货主 |
| | | private String ownerId; |
| | | // 货主名称 |
| | | private String ownerName; |
| | | // 保管者类型 |
| | | private String keeperTypeId; |
| | | // 保管者 |
| | | private String keeperId; |
| | | // 保管者名称 |
| | | private String keeperName; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "Pallet", description = "托盘信息") |
| | | public class Pallet { |
| | | |
| | | // 托盘条码 |
| | | @NotNull |
| | | @JsonProperty("BarCode") |
| | | private String barCode; |
| | | // 托盘编码 |
| | | @JsonProperty("PalletCode") |
| | | private String palletCode; |
| | | // 托盘名称 |
| | | @JsonProperty("PalletName") |
| | | private String palletName; |
| | | // 托盘类型编码 |
| | | @JsonProperty("PalletTypeCode") |
| | | private String palletTypeCode; |
| | | // 托盘类型 |
| | | @JsonProperty("PalletTypeName") |
| | | private String palletTypeName; |
| | | // 创建人 |
| | | @JsonProperty("CreatedBy") |
| | | private String createdBy; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @Data |
| | | public class SimpleProductionTask extends Task { |
| | | |
| | | // 领料内容 |
| | | private String matText; |
| | | // 领料区域,线下创建于MES同步 |
| | | private String regionId; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "Station", description = "站点信息") |
| | | public class Station { |
| | | |
| | | // 接驳口编码 |
| | | @NotNull |
| | | @JsonProperty("ConnPortCode") |
| | | private String connPortCode; |
| | | // 接驳口名称 |
| | | @JsonProperty("ConnPortName") |
| | | private String connPortName; |
| | | // 车间编码 |
| | | @JsonProperty("WorkshopCode") |
| | | private String workshopCode; |
| | | // 车间 |
| | | @JsonProperty("WorkshopName") |
| | | private String workshopName; |
| | | // 仓库编码 |
| | | @JsonProperty("ProductionLineCode") |
| | | private String productionLineCode; |
| | | // 仓库 |
| | | @JsonProperty("ProductionLineName") |
| | | private String productionLineName; |
| | | // 创建人 |
| | | @JsonProperty("CreatedBy") |
| | | private String createdBy; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "Supplier", description = "供应商信息同步") |
| | | public class Supplier { |
| | | |
| | | // 供应商编码,唯一标识 |
| | | @NotNull |
| | | @JsonProperty("supplierId") |
| | | private String supplierId; |
| | | // 供应商名称 |
| | | @JsonProperty("supplierName") |
| | | private String supplierName; |
| | | // 供应商昵称 |
| | | private String supplierNickName; |
| | | // 供应商分组,国内,国外 |
| | | private String supplierGroup; |
| | | // 联系人 |
| | | private String contact; |
| | | // 联系电话 |
| | | private String telephone; |
| | | // 邮箱 |
| | | private String email; |
| | | // 地址 |
| | | private String address; |
| | | // 操作类型,1 新增;2 修改;3禁用;4 反禁用; |
| | | @JsonProperty("operateType") |
| | | private Integer operateType; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "Task", description = "工作任务") |
| | | public class Task { |
| | | |
| | | // 任务号,唯一标识 |
| | | @NotNull |
| | | @JsonProperty("taskNo") |
| | | private String taskNo; |
| | | // 任务名称 |
| | | @JsonProperty("taskName") |
| | | private String taskName; |
| | | // 任务类型,1 入库;2 出库;3 转移; |
| | | private Integer taskType; |
| | | // 任务优先级,范围:1-100;数字越大,优先级越高,默认10; |
| | | private Integer initPriority; |
| | | // 起始站点编号 |
| | | private String startStationId; |
| | | // 目标站点编号 |
| | | private String endStationId; |
| | | // 托盘编号 |
| | | private String palletId; |
| | | // 托盘类型,枚举类型:1 托盘;2 料架;3 料箱;等枚举类型待后续对接 |
| | | private String palletType; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "TaskResult", description = "工作任务结果") |
| | | public class TaskResult { |
| | | |
| | | // 任务号,唯一标识 |
| | | @NotNull |
| | | @JsonProperty("TaskNo") |
| | | private String taskNo; |
| | | // 状态 |
| | | @JsonProperty("Status") |
| | | private String status; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.phyz; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "Warehouse", description = "仓库信息同步") |
| | | public class Warehouse { |
| | | |
| | | // 仓库编码,唯一标识 |
| | | @NotNull |
| | | @JsonProperty("wareHouseId") |
| | | private String wareHouseId; |
| | | // 仓库名称 |
| | | @JsonProperty("wareHouseName") |
| | | private String wareHouseName; |
| | | // 仓库位置 |
| | | private String address; |
| | | // 使用组织编码 |
| | | private String useOrgId; |
| | | // 使用组织名称 |
| | | private String useOrgName; |
| | | // 操作类型,1 新增;2 修改;3禁用;4 反禁用; |
| | | @JsonProperty("operateType") |
| | | private Integer operateType; |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.vincent.rsf.openApi.entity.app.ApiForeignLog; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface ApiForeignLogMapper extends BaseMapper<ApiForeignLog> { |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.vincent.rsf.openApi.entity.app.ApiFunction; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * ApiFunction Mapper |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @Mapper |
| | | public interface ApiFunctionMapper extends BaseMapper<ApiFunction> { |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.vincent.rsf.openApi.entity.app.ApiMap; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * ApiMap Mapper |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @Mapper |
| | | public interface ApiMapMapper extends BaseMapper<ApiMap> { |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.vincent.rsf.openApi.entity.app.App; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * App Mapper |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @Mapper |
| | | public interface AppMapper extends BaseMapper<App> { |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.security.filter; |
| | | |
| | | import com.vincent.rsf.openApi.entity.constant.Constants; |
| | | import com.vincent.rsf.openApi.security.service.AppAuthService; |
| | | import com.vincent.rsf.openApi.security.utils.TokenUtils; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.tika.utils.StringUtils; |
| | | import org.springframework.core.annotation.Order; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.filter.OncePerRequestFilter; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.servlet.FilterChain; |
| | | import javax.servlet.ServletException; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | import java.io.PrintWriter; |
| | | |
| | | /** |
| | | * AppId和AppSecret认证过滤器 |
| | | * |
| | | * 用于验证请求头中的AppId和AppSecret |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-05 |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | @Order(1) |
| | | public class AppIdAuthenticationFilter extends OncePerRequestFilter { |
| | | |
| | | @Resource |
| | | private AppAuthService appAuthService; |
| | | |
| | | |
| | | @Override |
| | | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) |
| | | throws ServletException, IOException { |
| | | |
| | | String requestURI = request.getRequestURI(); |
| | | |
| | | // 检查是否为认证请求(如获取token) |
| | | if (isAuthRequest(requestURI)) { |
| | | // 对于认证请求,允许通过 |
| | | filterChain.doFilter(request, response); |
| | | return; |
| | | } |
| | | |
| | | String authHeader = request.getHeader(Constants.HEADER_AUTHORIZATION); |
| | | if (authHeader != null) { |
| | | String token = TokenUtils.extractTokenFromHeader(authHeader); |
| | | if (token != null && TokenUtils.validateTokenTime(token)) { |
| | | // Token时间认证成功,认证AppId和AppSecret |
| | | String tokenAppId = TokenUtils.getAppIdFromToken(token); |
| | | String tokenAppSecret = TokenUtils.getSecretFromToken(token); |
| | | if (StringUtils.isBlank(tokenAppId) || StringUtils.isBlank(tokenAppSecret) |
| | | || !appAuthService.validateApp(tokenAppId, tokenAppSecret)) { |
| | | log.warn("Token验证失败"); |
| | | sendErrorResponse(response, Integer.parseInt(Constants.UNAUTHENTICATED_CODE), "认证失败,请提供有效的Token"); |
| | | return; |
| | | } else { |
| | | request.setAttribute(Constants.REQUEST_ATTR_APP_ID, tokenAppId); |
| | | } |
| | | } else { |
| | | log.warn("Token验证失败或缺失"); |
| | | sendErrorResponse(response, Integer.parseInt(Constants.UNAUTHENTICATED_CODE), "认证失败,请提供有效的Token"); |
| | | return; |
| | | } |
| | | } else { |
| | | log.warn("缺少Token认证信息"); |
| | | sendErrorResponse(response, Integer.parseInt(Constants.UNAUTHENTICATED_CODE), "认证失败,请提供有效的Token"); |
| | | return; |
| | | } |
| | | |
| | | filterChain.doFilter(request, response); |
| | | } |
| | | |
| | | /** |
| | | * 发送错误响应 |
| | | * |
| | | * @param response HTTP响应 |
| | | * @param code 错误码 |
| | | * @param message 错误消息 |
| | | * @throws IOException |
| | | */ |
| | | private void sendErrorResponse(HttpServletResponse response, int code, String message) throws IOException { |
| | | response.setStatus(code); |
| | | response.setContentType("application/json;charset=UTF-8"); |
| | | PrintWriter writer = response.getWriter(); |
| | | writer.write("{\"code\": \"" + code + "\", \"msg\": \"" + message + "\", \"data\": null}"); |
| | | writer.flush(); |
| | | } |
| | | |
| | | /** |
| | | * 检查是否为认证请求(不需要认证的请求) |
| | | * |
| | | * @param requestURI 请求URI |
| | | * @return 是否为认证请求 |
| | | */ |
| | | private boolean isAuthRequest(String requestURI) { |
| | | return requestURI.contains("/getToken"); |
| | | // || requestURI.contains("/auth/validate") || |
| | | // requestURI.contains("/auth/login"); |
| | | } |
| | | |
| | | @Override |
| | | protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { |
| | | String requestURI = request.getRequestURI(); |
| | | |
| | | // 不过滤认证相关请求和公开接口 |
| | | return requestURI.contains("/auth/") || |
| | | requestURI.contains("/public/") || |
| | | requestURI.contains("/doc.html") || |
| | | requestURI.contains("/swagger") || |
| | | requestURI.contains("/webjars") || |
| | | requestURI.contains("/v2/api-docs") || |
| | | requestURI.contains("/v3/api-docs"); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.security.service; |
| | | |
| | | import com.vincent.rsf.openApi.entity.app.App; |
| | | import com.vincent.rsf.openApi.service.AppService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | |
| | | /** |
| | | * App认证服务 |
| | | * |
| | | * 提供AppId和AppSecret的验证功能 |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-05 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class AppAuthService { |
| | | |
| | | @Resource |
| | | private AppService appService; |
| | | |
| | | /** |
| | | * 验证AppId和AppSecret |
| | | * |
| | | * @param appId 应用ID |
| | | * @param appSecret 应用密钥 |
| | | * @return 验证结果 |
| | | */ |
| | | public boolean validateApp(String appId, String appSecret) { |
| | | log.debug("验证AppId: {}, AppSecret: ****", appId); |
| | | |
| | | if (appId == null || appSecret == null) { |
| | | log.warn("AppId或AppSecret为空"); |
| | | return false; |
| | | } |
| | | |
| | | try { |
| | | // 从数据库查询应用信息 |
| | | App app = appService.getById(appId); |
| | | if (app == null) { |
| | | log.warn("未找到应用: {}", appId); |
| | | return false; |
| | | } |
| | | |
| | | // 检查应用是否启用 |
| | | if (app.getEnable() != 1) { |
| | | log.warn("应用未启用: {}", appId); |
| | | return false; |
| | | } |
| | | |
| | | // 验证密钥 |
| | | boolean isValid = appSecret.equals(app.getScrect()); |
| | | if (!isValid) { |
| | | log.warn("AppSecret验证失败: AppId={}", appId); |
| | | } else { |
| | | log.info("AppId认证成功: AppId={}", appId); |
| | | } |
| | | |
| | | return isValid; |
| | | } catch (Exception e) { |
| | | log.error("验证AppId和AppSecret时发生异常", e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | public boolean validateApp(String appId) { |
| | | App app = appService.getById(appId); |
| | | return app != null; |
| | | } |
| | | |
| | | /** |
| | | * 获取应用信息 |
| | | * |
| | | * @param appId 应用ID |
| | | * @return 应用信息 |
| | | */ |
| | | public App getAppInfo(String appId) { |
| | | if (appId == null) { |
| | | return null; |
| | | } |
| | | |
| | | try { |
| | | return appService.getById(appId); |
| | | } catch (Exception e) { |
| | | log.error("获取应用信息失败: {}", appId, e); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成AppToken(可选功能) |
| | | * |
| | | * @param appId 应用ID |
| | | * @param appSecret 应用密钥 |
| | | * @return 生成的Token |
| | | */ |
| | | public String generateAppToken(String appId, String appSecret) { |
| | | // 这里可以实现基于AppId和AppSecret的Token生成逻辑 |
| | | // 例如使用JWT生成Token |
| | | if (validateApp(appId, appSecret)) { |
| | | // 生成Token的逻辑 |
| | | long timestamp = System.currentTimeMillis(); |
| | | String tokenData = appId + ":" + timestamp; |
| | | // TODO:这里可以使用更安全的加密算法 |
| | | return java.util.Base64.getEncoder().encodeToString(tokenData.getBytes()); |
| | | } |
| | | return null; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.security.utils; |
| | | |
| | | import com.vincent.rsf.openApi.entity.constant.Constants; |
| | | import com.vincent.rsf.openApi.entity.app.App; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | |
| | | /** |
| | | * 认证工具类 |
| | | * |
| | | * 提供认证相关的通用功能 |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-05 |
| | | */ |
| | | public class AuthUtils { |
| | | |
| | | /** |
| | | * 从请求中获取AppId |
| | | * |
| | | * @param request HTTP请求 |
| | | * @return AppId |
| | | */ |
| | | public static String getAppId(HttpServletRequest request) { |
| | | // 优先从请求属性中获取(认证过滤器设置的) |
| | | String appId = (String) request.getAttribute(Constants.REQUEST_ATTR_APP_ID); |
| | | if (appId != null) { |
| | | return appId; |
| | | } |
| | | |
| | | // 从请求头获取 |
| | | return request.getHeader(Constants.HEADER_APP_ID); |
| | | } |
| | | |
| | | /** |
| | | * 从请求中获取App信息 |
| | | * |
| | | * @param request HTTP请求 |
| | | * @return App信息 |
| | | */ |
| | | public static App getAppInfo(HttpServletRequest request) { |
| | | return (App) request.getAttribute(Constants.REQUEST_ATTR_APP_INFO); |
| | | } |
| | | |
| | | /** |
| | | * 检查请求是否已通过App认证 |
| | | * |
| | | * @param request HTTP请求 |
| | | * @return 是否已认证 |
| | | */ |
| | | public static boolean isAuthenticated(HttpServletRequest request) { |
| | | return getAppId(request) != null; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.security.utils; |
| | | |
| | | import com.vincent.rsf.openApi.entity.app.App; |
| | | import com.vincent.rsf.openApi.entity.constant.Constants; |
| | | import com.vincent.rsf.openApi.service.AppService; |
| | | import io.jsonwebtoken.*; |
| | | import io.jsonwebtoken.security.Keys; |
| | | import org.apache.tika.utils.StringUtils; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.crypto.SecretKey; |
| | | import java.util.Date; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * JWT Token工具类 |
| | | * 用于生成和验证JWT Token |
| | | */ |
| | | public class TokenUtils { |
| | | private static final Logger log = LoggerFactory.getLogger(TokenUtils.class); |
| | | |
| | | // 使用一个安全的密钥,实际应用中应该从配置文件读取 |
| | | private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256); |
| | | |
| | | // Token过期时间,默认1小时 |
| | | private static final long TOKEN_EXPIRATION = 60 * 60 * 1000L; // 24小时 |
| | | |
| | | @Resource |
| | | private AppService appService; |
| | | |
| | | /** |
| | | * 生成JWT Token |
| | | * |
| | | * @param claims Token中包含的声明信息 |
| | | * @return 生成的Token字符串 |
| | | */ |
| | | public static String generateToken(Map<String, Object> claims) { |
| | | long now = System.currentTimeMillis(); |
| | | Date expiration = new Date(now + TOKEN_EXPIRATION); |
| | | |
| | | return Jwts.builder() |
| | | .setClaims(claims) |
| | | .setExpiration(expiration) |
| | | .signWith(SECRET_KEY, SignatureAlgorithm.HS256) |
| | | .compact(); |
| | | } |
| | | |
| | | /** |
| | | * 生成带AppId的Token |
| | | * |
| | | * @param appId 应用ID |
| | | * @param appSecret 应用秘钥 |
| | | * @return 生成的Token字符串 |
| | | */ |
| | | public static String generateToken(String appId, String appSecret) { |
| | | Map<String, Object> claims = Map.of( |
| | | "appId", appId, |
| | | "appSecret", appSecret, |
| | | "created", System.currentTimeMillis() |
| | | ); |
| | | return generateToken(claims); |
| | | } |
| | | |
| | | /** |
| | | * 解析Token获取声明信息 |
| | | * |
| | | * @param token Token字符串 |
| | | * @return 声明信息 |
| | | */ |
| | | public static Claims parseToken(String token) { |
| | | try { |
| | | return Jwts.parserBuilder() |
| | | .setSigningKey(SECRET_KEY) |
| | | .build() |
| | | .parseClaimsJws(token) |
| | | .getBody(); |
| | | } catch (JwtException e) { |
| | | log.error("解析Token失败: {}", e.getMessage()); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证Token时间是否有效 |
| | | * |
| | | * @param token Token字符串 |
| | | * @return 时间是否有效 |
| | | */ |
| | | public static boolean validateTokenTime(String token) { |
| | | try { |
| | | Claims claims = parseToken(token); |
| | | if (claims == null) { |
| | | return false; |
| | | } |
| | | |
| | | // 检查Token是否过期 |
| | | Date expiration = claims.getExpiration(); |
| | | return expiration != null && expiration.after(new Date()); |
| | | } catch (JwtException e) { |
| | | log.error("验证Token失败: {}", e.getMessage()); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 从Token中获取AppId |
| | | * |
| | | * @param token Token字符串 |
| | | * @return AppId |
| | | */ |
| | | public static String getAppIdFromToken(String token) { |
| | | Claims claims = parseToken(token); |
| | | if (claims != null) { |
| | | return (String) claims.get("appId"); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 从Token中获取appSecret |
| | | * |
| | | * @param token Token字符串 |
| | | * @return appSecret |
| | | */ |
| | | public static String getSecretFromToken(String token) { |
| | | Claims claims = parseToken(token); |
| | | if (claims != null) { |
| | | return (String) claims.get("appSecret"); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | // /** |
| | | // * 从Token中获取UserId |
| | | // * |
| | | // * @param token Token字符串 |
| | | // * @return UserId |
| | | // */ |
| | | // public static String getUserIdFromToken(String token) { |
| | | // Claims claims = parseToken(token); |
| | | // if (claims != null) { |
| | | // return (String) claims.get("userId"); |
| | | // } |
| | | // return null; |
| | | // } |
| | | |
| | | /** |
| | | * 从Authorization头中提取Token |
| | | * |
| | | * @param authHeader Authorization头内容 |
| | | * @return Token字符串(不包含Bearer前缀) |
| | | */ |
| | | public static String extractTokenFromHeader(String authHeader) { |
| | | if (authHeader != null && authHeader.startsWith(Constants.TOKEN_PREFIX)) { |
| | | return authHeader.substring(Constants.TOKEN_PREFIX.length()).trim(); |
| | | } |
| | | return null; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.vincent.rsf.openApi.entity.app.ApiForeignLog; |
| | | |
| | | public interface ApiForeignLogService extends IService<ApiForeignLog> { |
| | | |
| | | void saveAsync(ApiForeignLog log); |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.vincent.rsf.openApi.entity.app.ApiFunction; |
| | | |
| | | /** |
| | | * ApiFunction Service |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | public interface ApiFunctionService extends IService<ApiFunction> { |
| | | |
| | | /** |
| | | * 刷新功能缓存 |
| | | */ |
| | | void refreshCache(); |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.vincent.rsf.openApi.entity.app.ApiMap; |
| | | |
| | | /** |
| | | * ApiMap Service |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | public interface ApiMapService extends IService<ApiMap> { |
| | | |
| | | /** |
| | | * 刷新映射缓存 |
| | | */ |
| | | void refreshCache(); |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.vincent.rsf.openApi.entity.app.App; |
| | | |
| | | /** |
| | | * App Service |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | public interface AppService extends IService<App> { |
| | | |
| | | /** |
| | | * 刷新应用缓存 |
| | | */ |
| | | void refreshCache(); |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.vincent.rsf.openApi.entity.app.ApiForeignLog; |
| | | import com.vincent.rsf.openApi.mapper.ApiForeignLogMapper; |
| | | import com.vincent.rsf.openApi.service.ApiForeignLogService; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | @Service |
| | | public class ApiForeignLogServiceImpl extends ServiceImpl<ApiForeignLogMapper, ApiForeignLog> implements ApiForeignLogService { |
| | | |
| | | @Async |
| | | @Override |
| | | public void saveAsync(ApiForeignLog log) { |
| | | baseMapper.insert(log); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.vincent.rsf.openApi.entity.app.ApiFunction; |
| | | import com.vincent.rsf.openApi.mapper.ApiFunctionMapper; |
| | | import com.vincent.rsf.openApi.service.ApiFunctionService; |
| | | import com.vincent.rsf.openApi.utils.ParamsMapUtils; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * ApiFunction Service Implementation |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class ApiFunctionServiceImpl extends ServiceImpl<ApiFunctionMapper, ApiFunction> implements ApiFunctionService { |
| | | |
| | | @Override |
| | | public void refreshCache() { |
| | | log.info("开始刷新接口功能缓存..."); |
| | | List<ApiFunction> functions = this.list(); |
| | | ParamsMapUtils.FUNCTIONS = functions; |
| | | log.info("接口功能缓存刷新完成,共加载 {} 个功能", functions.size()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.vincent.rsf.openApi.entity.app.ApiMap; |
| | | import com.vincent.rsf.openApi.mapper.ApiMapMapper; |
| | | import com.vincent.rsf.openApi.service.ApiMapService; |
| | | import com.vincent.rsf.openApi.utils.ParamsMapUtils; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * ApiMap Service Implementation |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class ApiMapServiceImpl extends ServiceImpl<ApiMapMapper, ApiMap> implements ApiMapService { |
| | | |
| | | @Override |
| | | public void refreshCache() { |
| | | log.info("开始刷新字段映射缓存..."); |
| | | List<ApiMap> maps = this.list(new LambdaQueryWrapper<ApiMap>() |
| | | .eq(ApiMap::getEnable, 1)); |
| | | ParamsMapUtils.ATTRIBUTE_MAPS = maps; |
| | | log.info("字段映射缓存刷新完成,共加载 {} 条映射规则", maps.size()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.vincent.rsf.openApi.entity.app.App; |
| | | import com.vincent.rsf.openApi.mapper.AppMapper; |
| | | import com.vincent.rsf.openApi.service.AppService; |
| | | import com.vincent.rsf.openApi.utils.ParamsMapUtils; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * App Service Implementation |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService { |
| | | |
| | | @Override |
| | | public void refreshCache() { |
| | | log.info("开始刷新应用缓存..."); |
| | | List<App> apps = this.list(new LambdaQueryWrapper<App>() |
| | | .eq(App::getEnable, 1)); |
| | | ParamsMapUtils.APPS = apps; |
| | | log.info("应用缓存刷新完成,共加载 {} 个应用", apps.size()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.utils; |
| | | |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * JSON属性名递归替换测试类 |
| | | * |
| | | * 演示如何使用 FuncMap 进行JSON属性名的递归替换 |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | public class JsonReplaceTest { |
| | | |
| | | public static void main(String[] args) { |
| | | System.out.println("========== JSON属性名递归替换测试 ==========\n"); |
| | | |
| | | // 测试1:简单对象 |
| | | testSimpleObject(); |
| | | |
| | | // 测试2:嵌套对象 |
| | | testNestedObject(); |
| | | |
| | | // 测试3:包含数组 |
| | | testWithArray(); |
| | | |
| | | // 测试4:复杂结构 |
| | | testComplexStructure(); |
| | | } |
| | | |
| | | /** |
| | | * 测试1:简单对象属性替换 |
| | | */ |
| | | private static void testSimpleObject() { |
| | | System.out.println("【测试1:简单对象】"); |
| | | |
| | | // 构造测试数据 |
| | | JSONObject data = new JSONObject(); |
| | | data.put("orderNumber", "PO001"); |
| | | data.put("orderQty", 100); |
| | | data.put("orderAmount", 5000.00); |
| | | |
| | | System.out.println("原始数据:" + data.toJSONString()); |
| | | |
| | | // 定义映射规则 |
| | | Map<String, String> rules = new HashMap<>(); |
| | | rules.put("orderNumber", "code"); |
| | | rules.put("orderQty", "qty"); |
| | | rules.put("orderAmount", "anfme"); |
| | | |
| | | // 执行替换 |
| | | JSONObject result = ParamsMapUtils.replaceJsonKeys(data, rules); |
| | | |
| | | System.out.println("替换结果:" + result.toJSONString()); |
| | | System.out.println(); |
| | | } |
| | | |
| | | /** |
| | | * 测试2:嵌套对象深度替换 |
| | | */ |
| | | private static void testNestedObject() { |
| | | System.out.println("【测试2:嵌套对象】"); |
| | | |
| | | // 构造嵌套数据 |
| | | JSONObject data = new JSONObject(); |
| | | data.put("orderNumber", "PO002"); |
| | | |
| | | JSONObject customer = new JSONObject(); |
| | | customer.put("customerName", "张三"); |
| | | customer.put("customerPhone", "13800138000"); |
| | | |
| | | JSONObject address = new JSONObject(); |
| | | address.put("cityName", "北京"); |
| | | address.put("streetName", "朝阳路88号"); |
| | | customer.put("address", address); |
| | | |
| | | data.put("customer", customer); |
| | | |
| | | System.out.println("原始数据:" + data.toJSONString()); |
| | | |
| | | // 定义映射规则(会应用到所有层级) |
| | | Map<String, String> rules = new HashMap<>(); |
| | | rules.put("orderNumber", "code"); |
| | | rules.put("customerName", "name"); |
| | | rules.put("customerPhone", "phone"); |
| | | rules.put("cityName", "city"); |
| | | rules.put("streetName", "street"); |
| | | |
| | | // 执行递归替换 |
| | | JSONObject result = ParamsMapUtils.replaceJsonKeys(data, rules); |
| | | |
| | | System.out.println("替换结果:" + result.toJSONString()); |
| | | System.out.println(); |
| | | } |
| | | |
| | | /** |
| | | * 测试3:包含数组的替换 |
| | | */ |
| | | private static void testWithArray() { |
| | | System.out.println("【测试3:包含数组】"); |
| | | |
| | | // 构造包含数组的数据 |
| | | JSONObject data = new JSONObject(); |
| | | data.put("orderNumber", "PO003"); |
| | | |
| | | JSONArray items = new JSONArray(); |
| | | for (int i = 1; i <= 3; i++) { |
| | | JSONObject item = new JSONObject(); |
| | | item.put("materialCode", "MAT00" + i); |
| | | item.put("materialName", "物料" + i); |
| | | item.put("itemQty", 10 * i); |
| | | items.add(item); |
| | | } |
| | | data.put("items", items); |
| | | |
| | | System.out.println("原始数据:" + data.toJSONString()); |
| | | |
| | | // 定义映射规则 |
| | | Map<String, String> rules = new HashMap<>(); |
| | | rules.put("orderNumber", "code"); |
| | | rules.put("materialCode", "matnr"); |
| | | rules.put("materialName", "maktx"); |
| | | rules.put("itemQty", "qty"); |
| | | |
| | | // 执行替换(包括数组中的所有对象) |
| | | JSONObject result = ParamsMapUtils.replaceJsonKeys(data, rules); |
| | | |
| | | System.out.println("替换结果:" + result.toJSONString()); |
| | | System.out.println(); |
| | | } |
| | | |
| | | /** |
| | | * 测试4:复杂结构(嵌套+数组+多层) |
| | | */ |
| | | private static void testComplexStructure() { |
| | | System.out.println("【测试4:复杂结构】"); |
| | | |
| | | // 构造复杂结构 |
| | | JSONObject data = new JSONObject(); |
| | | data.put("orderNumber", "PO004"); |
| | | data.put("orderStatus", "PENDING"); |
| | | |
| | | // 客户信息 |
| | | JSONObject customer = new JSONObject(); |
| | | customer.put("customerCode", "CUST001"); |
| | | customer.put("customerName", "北京公司"); |
| | | data.put("customer", customer); |
| | | |
| | | // 订单明细数组 |
| | | JSONArray items = new JSONArray(); |
| | | for (int i = 1; i <= 2; i++) { |
| | | JSONObject item = new JSONObject(); |
| | | item.put("itemNo", i); |
| | | item.put("materialCode", "MAT00" + i); |
| | | item.put("materialName", "物料" + i); |
| | | item.put("itemQty", 50 + i * 10); |
| | | |
| | | // 仓库信息(嵌套在明细中) |
| | | JSONObject warehouse = new JSONObject(); |
| | | warehouse.put("warehouseCode", "WH0" + i); |
| | | warehouse.put("locationCode", "LOC-A-0" + i); |
| | | item.put("warehouse", warehouse); |
| | | |
| | | items.add(item); |
| | | } |
| | | data.put("items", items); |
| | | |
| | | System.out.println("原始数据:" + data.toJSONString()); |
| | | |
| | | // 定义完整的映射规则 |
| | | Map<String, String> rules = new HashMap<>(); |
| | | rules.put("orderNumber", "code"); |
| | | rules.put("orderStatus", "exceStatus"); |
| | | rules.put("customerCode", "custCode"); |
| | | rules.put("customerName", "custName"); |
| | | rules.put("materialCode", "matnr"); |
| | | rules.put("materialName", "maktx"); |
| | | rules.put("itemQty", "qty"); |
| | | rules.put("warehouseCode", "whCode"); |
| | | rules.put("locationCode", "locCode"); |
| | | |
| | | // 执行递归替换 |
| | | JSONObject result = ParamsMapUtils.replaceJsonKeys(data, rules); |
| | | |
| | | System.out.println("替换结果:" + result.toJSONString()); |
| | | System.out.println(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.openApi.utils; |
| | | |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.vincent.rsf.openApi.entity.app.ApiFunction; |
| | | import com.vincent.rsf.openApi.entity.app.ApiMap; |
| | | import com.vincent.rsf.openApi.entity.app.App; |
| | | import com.vincent.rsf.openApi.service.ApiMapService; |
| | | import com.vincent.rsf.openApi.service.ApiFunctionService; |
| | | import com.vincent.rsf.openApi.service.AppService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.tika.utils.StringUtils; |
| | | import org.springframework.boot.context.event.ApplicationReadyEvent; |
| | | import org.springframework.context.event.EventListener; |
| | | import org.springframework.stereotype.Component; |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 接口字段动态映射工具类 |
| | | * 支持从数据库加载映射配置,并提供字段转换功能 |
| | | * |
| | | * @author vincent |
| | | * @since 2026-01-04 |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | @RequiredArgsConstructor |
| | | public class ParamsMapUtils { |
| | | |
| | | private final AppService appService; |
| | | private final ApiFunctionService functionService; |
| | | private final ApiMapService mapService; |
| | | |
| | | // 缓存到内存 |
| | | public static List<App> APPS = new ArrayList<>(); |
| | | public static List<ApiFunction> FUNCTIONS = new ArrayList<>(); |
| | | public static List<ApiMap> ATTRIBUTE_MAPS = new ArrayList<>(); |
| | | |
| | | /** |
| | | * 应用完全启动后自动加载配置 |
| | | * 使用 ApplicationReadyEvent 确保 Spring 容器完全初始化 |
| | | */ |
| | | @EventListener(ApplicationReadyEvent.class) |
| | | public void init() { |
| | | log.info("=============== 开始加载接口映射配置 ==============="); |
| | | try { |
| | | appService.refreshCache(); |
| | | functionService.refreshCache(); |
| | | mapService.refreshCache(); |
| | | |
| | | log.info("接口映射配置加载完成 - 应用数:{}, 功能数:{}, 映射规则数:{}", |
| | | APPS.size(), FUNCTIONS.size(), ATTRIBUTE_MAPS.size()); |
| | | } catch (Exception e) { |
| | | log.error("接口映射配置加载失败", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 手动刷新所有缓存 |
| | | */ |
| | | public void refreshAll() { |
| | | log.info("手动刷新所有映射缓存..."); |
| | | appService.refreshCache(); |
| | | functionService.refreshCache(); |
| | | mapService.refreshCache(); |
| | | } |
| | | |
| | | /** |
| | | * 执行字段映射转换 |
| | | * |
| | | * @param appId 应用ID |
| | | * @param funcId 功能ID |
| | | * @param params 原始参数 |
| | | * @return 转换后的参数 |
| | | */ |
| | | public static JSONObject apiMaps(String appId, String funcId, JSONObject params) { |
| | | if (params == null || params.isEmpty()) { |
| | | return params; |
| | | } |
| | | |
| | | // 1、获取映射表 |
| | | List<ApiMap> maps = ATTRIBUTE_MAPS.stream() |
| | | .filter(map -> map.getAppId().equals(appId) && map.getFuncId().equals(funcId)) |
| | | .toList(); |
| | | |
| | | if (maps.isEmpty()) { |
| | | log.debug("未找到映射配置 - appId:{}, funcId:{}", appId, funcId); |
| | | return params; |
| | | } |
| | | |
| | | // 2、构建映射规则Map |
| | | Map<String, String> mappingRules = new HashMap<>(); |
| | | for (ApiMap map : maps) { |
| | | String sourceAttribute = map.getSourceAttribute(); |
| | | String targetAttribute = map.getTargetAttribute(); |
| | | if (!StringUtils.isBlank(sourceAttribute) && !StringUtils.isBlank(targetAttribute)) { |
| | | mappingRules.put(sourceAttribute, targetAttribute); |
| | | } |
| | | } |
| | | |
| | | // 3、递归替换所有层级的属性名称 |
| | | JSONObject result = replaceKeysRecursive(params, mappingRules); |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 递归替换JSON所有层级的属性名称 |
| | | * 支持嵌套对象和数组的深度遍历 |
| | | * |
| | | * @param json 原始JSON对象 |
| | | * @param mappingRules 映射规则 (源字段名 -> 目标字段名) |
| | | * @return 替换后的JSON对象 |
| | | */ |
| | | public static JSONObject replaceKeysRecursive(JSONObject json, Map<String, String> mappingRules) { |
| | | if (json == null || json.isEmpty()) { |
| | | return json; |
| | | } |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | |
| | | for (String key : json.keySet()) { |
| | | Object value = json.get(key); |
| | | |
| | | // 确定新的键名(如果有映射规则则使用映射后的名称) |
| | | String newKey = mappingRules.getOrDefault(key, key); |
| | | |
| | | if (value instanceof JSONObject) { |
| | | // 递归处理嵌套对象 |
| | | JSONObject nestedResult = replaceKeysRecursive((JSONObject) value, mappingRules); |
| | | result.put(newKey, nestedResult); |
| | | log.debug("替换对象字段: {} -> {}", key, newKey); |
| | | |
| | | } else if (value instanceof JSONArray) { |
| | | // 递归处理数组 |
| | | JSONArray arrayResult = replaceKeysInArray((JSONArray) value, mappingRules); |
| | | result.put(newKey, arrayResult); |
| | | log.debug("替换数组字段: {} -> {}", key, newKey); |
| | | |
| | | } else { |
| | | // 普通值直接赋值 |
| | | Object convertedValue = convertValue(value, newKey); |
| | | result.put(newKey, convertedValue); |
| | | if (!key.equals(newKey)) { |
| | | log.debug("替换字段: {} -> {} (值: {})", key, newKey, convertedValue); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 递归处理JSON数组中的所有元素 |
| | | * |
| | | * @param array 原始JSON数组 |
| | | * @param mappingRules 映射规则 |
| | | * @return 处理后的JSON数组 |
| | | */ |
| | | private static JSONArray replaceKeysInArray(JSONArray array, Map<String, String> mappingRules) { |
| | | if (array == null || array.isEmpty()) { |
| | | return array; |
| | | } |
| | | |
| | | JSONArray result = new JSONArray(); |
| | | |
| | | for (int i = 0; i < array.size(); i++) { |
| | | Object element = array.get(i); |
| | | |
| | | if (element instanceof JSONObject) { |
| | | // 数组元素是对象,递归处理 |
| | | JSONObject replacedObject = replaceKeysRecursive((JSONObject) element, mappingRules); |
| | | result.add(replacedObject); |
| | | |
| | | } else if (element instanceof JSONArray) { |
| | | // 数组元素是数组,递归处理 |
| | | JSONArray replacedArray = replaceKeysInArray((JSONArray) element, mappingRules); |
| | | result.add(replacedArray); |
| | | |
| | | } else { |
| | | // 基本类型,直接添加 |
| | | result.add(element); |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 通用的JSON属性名替换方法(对外提供) |
| | | * 可以直接传入映射规则进行替换 |
| | | * |
| | | * @param json 原始JSON对象 |
| | | * @param mappingRules 映射规则 Map<源字段名, 目标字段名> |
| | | * @return 替换后的JSON对象 |
| | | */ |
| | | public static JSONObject replaceJsonKeys(JSONObject json, Map<String, String> mappingRules) { |
| | | return replaceKeysRecursive(json, mappingRules); |
| | | } |
| | | |
| | | /** |
| | | * 通用的JSON数组属性名替换方法(对外提供) |
| | | * |
| | | * @param array 原始JSON数组 |
| | | * @param mappingRules 映射规则 Map<源字段名, 目标字段名> |
| | | * @return 替换后的JSON数组 |
| | | */ |
| | | public static JSONArray replaceJsonArrayKeys(JSONArray array, Map<String, String> mappingRules) { |
| | | return replaceKeysInArray(array, mappingRules); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 值类型转换(可扩展) |
| | | * |
| | | * @param value 原始值 |
| | | * @param targetField 目标字段名 |
| | | * @return 转换后的值 |
| | | */ |
| | | private static Object convertValue(Object value, String targetField) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | |
| | | // 这里可以根据需要添加更多类型转换逻辑 |
| | | // 例如:日期格式转换、数字精度转换等 |
| | | |
| | | // 示例:如果字段名包含amount、price等,转换为BigDecimal |
| | | String lowerField = targetField.toLowerCase(); |
| | | if ((lowerField.contains("amount") || lowerField.contains("price") |
| | | || lowerField.contains("qty")) && value instanceof String) { |
| | | try { |
| | | return new BigDecimal(value.toString()); |
| | | } catch (Exception e) { |
| | | log.warn("数字转换失败: {} -> {}", value, targetField); |
| | | return value; |
| | | } |
| | | } |
| | | |
| | | return value; |
| | | } |
| | | |
| | | /** |
| | | * 获取应用信息 |
| | | * |
| | | * @param appId 应用ID |
| | | * @return 应用信息 |
| | | */ |
| | | public static App getApp(String appId) { |
| | | return APPS.stream() |
| | | .filter(app -> app.getId().equals(appId)) |
| | | .findFirst() |
| | | .orElse(null); |
| | | } |
| | | |
| | | /** |
| | | * 获取功能信息 |
| | | * |
| | | * @param funcId 功能ID |
| | | * @return 功能信息 |
| | | */ |
| | | public static ApiFunction getFunction(String funcId) { |
| | | return FUNCTIONS.stream() |
| | | .filter(func -> func.getId().equals(funcId)) |
| | | .findFirst() |
| | | .orElse(null); |
| | | } |
| | | } |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 --> |
| | | <!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true --> |
| | | <!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 --> |
| | | <!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> |
| | | <configuration scan="true" scanPeriod="10 seconds"> |
| | | |
| | | <!--<include resource="org/springframework/boot/logging/logback/base.xml" />--> |
| | | |
| | | <contextName>logback</contextName> |
| | | <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被添加到logger上下文中。定义变量后,可以使“${}”来使用变量。 --> |
| | | <!-- <property name="log.path" value="./emp-log"/>--> |
| | | <!-- 彩色日志 --> |
| | | <!-- 彩色日志依赖的渲染类 --> |
| | | <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/> |
| | | <conversionRule conversionWord="wex" |
| | | converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/> |
| | | <conversionRule conversionWord="wEx" |
| | | converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/> |
| | | <!-- 配置属性 彩色日志格式 --> |
| | | <property name="CONSOLE_LOG_PATTERN" |
| | | 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(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> |
| | | |
| | | |
| | | <!--输出到控制台的appender--> |
| | | <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> |
| | | <!--日志级别过滤器--> |
| | | <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> |
| | | <!--日志过滤级别--> |
| | | <level>debug</level> |
| | | </filter> |
| | | <encoder> |
| | | <Pattern>${CONSOLE_LOG_PATTERN}</Pattern> |
| | | <!-- 设置字符集 --> |
| | | <charset>UTF-8</charset> |
| | | </encoder> |
| | | </appender> |
| | | |
| | | |
| | | <!--log输出文件路径--> |
| | | <springProperty scope="context" name="log.path" source="logging.file.path"/> |
| | | <!--日志文件路径属性--> |
| | | <property name="logback.logdir" value="${log.path}"/> |
| | | |
| | | <!-- level为 DEBUG 日志 --> |
| | | <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <!-- 正在记录的日志文件的路径及文件名 --> |
| | | <file>${logback.logdir}/log_debug.log</file> |
| | | <!--日志文件输出格式--> |
| | | <encoder> |
| | | <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> |
| | | <charset>UTF-8</charset> <!-- 设置字符集 --> |
| | | </encoder> |
| | | <!-- 指定日志记录器的拆分归档策略,按日期,按大小记录 --> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <!-- 日志归档 --> |
| | | <fileNamePattern>${logback.logdir}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern> |
| | | <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> |
| | | <maxFileSize>100MB</maxFileSize> |
| | | </timeBasedFileNamingAndTriggeringPolicy> |
| | | <!--日志文件保留天数--> |
| | | <maxHistory>15</maxHistory> |
| | | </rollingPolicy> |
| | | <!--日志级过滤规则--> |
| | | <filter class="ch.qos.logback.classic.filter.LevelFilter"> |
| | | <!--日志过滤级别--> |
| | | <level>debug</level> |
| | | <!--超过过滤级别的策略--> |
| | | <onMatch>ACCEPT</onMatch> |
| | | <!--未超过过滤级别的策略--> |
| | | <onMismatch>DENY</onMismatch> |
| | | </filter> |
| | | </appender> |
| | | |
| | | <!-- level为 INFO 日志 --> |
| | | <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <!-- 正在记录的日志文件的路径及文件名 --> |
| | | <file>${logback.logdir}/log_info.log</file> |
| | | <!--日志文件输出格式--> |
| | | <encoder> |
| | | <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> |
| | | <charset>UTF-8</charset> |
| | | </encoder> |
| | | <!-- 指定日志记录器的拆分归档策略,按日期,按大小记录 --> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <!-- 每天日志归档路径以及格式 --> |
| | | <fileNamePattern>${logback.logdir}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> |
| | | <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> |
| | | <maxFileSize>100MB</maxFileSize> |
| | | </timeBasedFileNamingAndTriggeringPolicy> |
| | | <!--日志文件保留天数--> |
| | | <maxHistory>15</maxHistory> |
| | | </rollingPolicy> |
| | | <!--日志级过滤规则--> |
| | | <filter class="ch.qos.logback.classic.filter.LevelFilter"> |
| | | <!--日志过滤级别--> |
| | | <level>info</level> |
| | | <!--超过过滤级别的策略--> |
| | | <onMatch>ACCEPT</onMatch> |
| | | <!--未超过过滤级别的策略--> |
| | | <onMismatch>DENY</onMismatch> |
| | | </filter> |
| | | </appender> |
| | | |
| | | <!-- level为 WARN 日志 --> |
| | | <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <!-- 正在记录的日志文件的路径及文件名 --> |
| | | <file>${logback.logdir}/log_warn.log</file> |
| | | <!--日志文件输出格式--> |
| | | <encoder> |
| | | <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> |
| | | <charset>UTF-8</charset> <!-- 此处设置字符集 --> |
| | | </encoder> |
| | | <!-- 指定日志记录器的拆分归档策略 --> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <fileNamePattern>${logback.logdir}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> |
| | | <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> |
| | | <maxFileSize>100MB</maxFileSize> |
| | | </timeBasedFileNamingAndTriggeringPolicy> |
| | | <!--日志文件保留天数--> |
| | | <maxHistory>15</maxHistory> |
| | | </rollingPolicy> |
| | | <!--日志级过滤规则--> |
| | | <filter class="ch.qos.logback.classic.filter.LevelFilter"> |
| | | <!--日志过滤级别--> |
| | | <level>warn</level> |
| | | <!--超过过滤级别的策略--> |
| | | <onMatch>ACCEPT</onMatch> |
| | | <!--未超过过滤级别的策略--> |
| | | <onMismatch>DENY</onMismatch> |
| | | </filter> |
| | | </appender> |
| | | |
| | | |
| | | <!-- level为 ERROR 日志 --> |
| | | <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <!-- 正在记录的日志文件的路径及文件名 --> |
| | | <file>${logback.logdir}/log_error.log</file> |
| | | <!--日志文件输出格式--> |
| | | <encoder> |
| | | <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> |
| | | <charset>UTF-8</charset> <!-- 此处设置字符集 --> |
| | | </encoder> |
| | | <!--指定日志记录器的拆分归档策略,按日期,按大小记录 --> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <fileNamePattern>${logback.logdir}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> |
| | | <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> |
| | | <maxFileSize>100MB</maxFileSize> |
| | | </timeBasedFileNamingAndTriggeringPolicy> |
| | | <!--日志文件保留天数--> |
| | | <maxHistory>15</maxHistory> |
| | | </rollingPolicy> |
| | | <!--日志级过滤规则--> |
| | | <filter class="ch.qos.logback.classic.filter.LevelFilter"> |
| | | <!--日志过滤级别--> |
| | | <level>ERROR</level> |
| | | <!--超过过滤级别的策略--> |
| | | <onMatch>ACCEPT</onMatch> |
| | | <!--未超过过滤级别的策略--> |
| | | <onMismatch>DENY</onMismatch> |
| | | </filter> |
| | | </appender> |
| | | |
| | | <!-- |
| | | <logger>用来设置某一个包或者具体的某一个类的日志打印级别、 |
| | | 以及指定<appender>。<logger>仅有一个name属性, |
| | | 一个可选的level和一个可选的addtivity属性。 |
| | | name:用来指定受此logger约束的某一个包或者具体的某一个类。 |
| | | level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, |
| | | 还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。 |
| | | 如果未设置此属性,那么当前logger将会继承上级的级别。 |
| | | addtivity:是否向上级logger传递打印信息。默认是true。 |
| | | --> |
| | | <!--<logger name="org.springframework.web" level="info"/>--> |
| | | <!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>--> |
| | | <!-- |
| | | 使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作: |
| | | 第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息 |
| | | 第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别: |
| | | --> |
| | | |
| | | |
| | | <!-- |
| | | root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性 |
| | | level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, |
| | | 不能设置为INHERITED或者同义词NULL。默认是DEBUG |
| | | 可以包含零个或多个元素,标识这个appender将会添加到这个logger。 |
| | | --> |
| | | |
| | | <!--开发环境:打印控制台--> |
| | | <springProfile name="dev"> |
| | | <logger name="com.lg.emp.controller" level="error"/> |
| | | </springProfile> |
| | | |
| | | <!--root logger 配置 --> |
| | | <root level="INFO"> |
| | | <appender-ref ref="CONSOLE"/> |
| | | <appender-ref ref="DEBUG_FILE"/> |
| | | <appender-ref ref="INFO_FILE"/> |
| | | <appender-ref ref="WARN_FILE"/> |
| | | <appender-ref ref="ERROR_FILE"/> |
| | | </root> |
| | | |
| | | <!--生产环境:输出到文件--> |
| | | <!--<springProfile name="pro">--> |
| | | <!--<root level="info">--> |
| | | <!--<appender-ref ref="CONSOLE" />--> |
| | | <!--<appender-ref ref="DEBUG_FILE" />--> |
| | | <!--<appender-ref ref="INFO_FILE" />--> |
| | | <!--<appender-ref ref="ERROR_FILE" />--> |
| | | <!--<appender-ref ref="WARN_FILE" />--> |
| | | <!--</root>--> |
| | | <!--</springProfile>--> |
| | | |
| | | </configuration> |