Commit 7d925fddb9f63550b29ad511a0350d37f17c9f42

Authored by dengwei
1 parent e8bc3fd8

feat cashier mall init

Showing 31 changed files with 1228 additions and 13 deletions
cashier-boss/src/main/java/com/diligrp/cashier/boss/CashierServiceBootstrap.java
1 package com.diligrp.cashier.boss; 1 package com.diligrp.cashier.boss;
2 2
3 import com.diligrp.cashier.assistant.AssistantConfiguration; 3 import com.diligrp.cashier.assistant.AssistantConfiguration;
  4 +import com.diligrp.cashier.mall.MallConfiguration;
4 import com.diligrp.cashier.pipeline.PipelineConfiguration; 5 import com.diligrp.cashier.pipeline.PipelineConfiguration;
5 import com.diligrp.cashier.shared.SharedConfiguration; 6 import com.diligrp.cashier.shared.SharedConfiguration;
6 import org.springframework.boot.SpringApplication; 7 import org.springframework.boot.SpringApplication;
7 import org.springframework.boot.SpringBootConfiguration; 8 import org.springframework.boot.SpringBootConfiguration;
8 import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 9 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
9 -import org.springframework.boot.autoconfigure.SpringBootApplication;  
10 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 10 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
11 import org.springframework.context.annotation.Import; 11 import org.springframework.context.annotation.Import;
12 12
13 @SpringBootConfiguration 13 @SpringBootConfiguration
14 @EnableAutoConfiguration 14 @EnableAutoConfiguration
15 -@Import({BossConfiguration.class, PipelineConfiguration.class, AssistantConfiguration.class, SharedConfiguration.class}) 15 +@Import({BossConfiguration.class, PipelineConfiguration.class,
  16 + AssistantConfiguration.class, SharedConfiguration.class,
  17 + MallConfiguration.class
  18 +})
16 @EnableDiscoveryClient 19 @EnableDiscoveryClient
17 public class CashierServiceBootstrap { 20 public class CashierServiceBootstrap {
18 public static void main(String[] args) { 21 public static void main(String[] args) {
19 SpringApplication.run(CashierServiceBootstrap.class, args); 22 SpringApplication.run(CashierServiceBootstrap.class, args);
20 } 23 }
21 -}  
22 \ No newline at end of file 24 \ No newline at end of file
  25 +}
cashier-boss/src/main/java/com/diligrp/cashier/boss/aop/LogPrintAop.java 0 → 100644
  1 +package com.diligrp.cashier.boss.aop;
  2 +
  3 +import com.diligrp.cashier.shared.annotation.ParamLogPrint;
  4 +import com.diligrp.cashier.shared.util.JsonUtils;
  5 +import jakarta.servlet.http.HttpServletRequest;
  6 +import jakarta.servlet.http.HttpServletResponse;
  7 +import org.apache.commons.lang3.BooleanUtils;
  8 +import org.aspectj.lang.JoinPoint;
  9 +import org.aspectj.lang.Signature;
  10 +import org.aspectj.lang.annotation.AfterReturning;
  11 +import org.aspectj.lang.annotation.Aspect;
  12 +import org.aspectj.lang.annotation.Before;
  13 +import org.slf4j.Logger;
  14 +import org.slf4j.LoggerFactory;
  15 +import org.springframework.core.annotation.Order;
  16 +import org.springframework.stereotype.Component;
  17 +import org.springframework.web.multipart.MultipartFile;
  18 +
  19 +import java.util.*;
  20 +
  21 +/**
  22 + * @author dengwei
  23 + * @version 1.0.0
  24 + * @ClassName LogPrintAspect.java
  25 + * @Description 方法参数打印切面
  26 + */
  27 +@Component
  28 +@Aspect
  29 +@Order(0)
  30 +public class LogPrintAop {
  31 + private static final Logger log = LoggerFactory.getLogger(LogPrintAop.class);
  32 + private static final String VERSION = "feat_1.0.0";
  33 +
  34 + /**
  35 + * 参数
  36 + */
  37 + @Before("@annotation(commonParamLogPrint)")
  38 + public void paramLogPrint(JoinPoint joinPoint, ParamLogPrint commonParamLogPrint) {
  39 + try {
  40 + boolean print = commonParamLogPrint.print();
  41 + if (!print) {
  42 + return;
  43 + }
  44 + Signature signature = joinPoint.getSignature();
  45 + // 使用下述方法需要开启--enable-preview预览功能
  46 + // String className = STR."\{signature.getDeclaringTypeName()}.\{signature.getName()}";
  47 + String className = signature.getDeclaringTypeName() + "." + signature.getName();
  48 + Object[] args = joinPoint.getArgs();
  49 +
  50 + List<Object> argsList = this.listArgs(args);
  51 +
  52 + Map<String, Object> logInfo = new LinkedHashMap<>(2);
  53 + logInfo.put("version", VERSION);
  54 + logInfo.put("method", className);
  55 + logInfo.put("args", argsList);
  56 + log.info("{}:{}", commonParamLogPrint.desc(), JsonUtils.toJsonString(logInfo));
  57 + } catch (Exception exception) {
  58 + log.warn("param log error");
  59 + }
  60 + }
  61 +
  62 + /**
  63 + * 处理完请求后执行
  64 + */
  65 + @AfterReturning(pointcut = "@annotation(print)", returning = "jsonResult")
  66 + public void doAfterReturning(JoinPoint joinPoint, ParamLogPrint print, Object jsonResult) {
  67 + try {
  68 + if (BooleanUtils.isTrue(print.outPrint())) {
  69 + log.warn("{} rsp:{}", print.desc(), JsonUtils.toJsonString(jsonResult));
  70 + }
  71 + } catch (Exception e) {
  72 + log.warn("after return error!");
  73 + }
  74 + }
  75 +
  76 + /**
  77 + * 参数列表
  78 + *
  79 + * @param args args
  80 + * @return {@link List}<{@link Object}>
  81 + */
  82 + private List<Object> listArgs(Object[] args) {
  83 + List<Object> argsList = new LinkedList<>();
  84 + for (Object arg : args) {
  85 + Object param;
  86 + if (arg instanceof HttpServletResponse) {
  87 + param = HttpServletResponse.class.getSimpleName();
  88 + } else if (arg instanceof HttpServletRequest) {
  89 + param = HttpServletRequest.class.getSimpleName();
  90 + } else if (arg instanceof MultipartFile) {
  91 + param = MultipartFile.class.getSimpleName();
  92 + } else {
  93 + param = arg;
  94 + }
  95 + if (Objects.nonNull(param)) {
  96 + argsList.add(param);
  97 + }
  98 + }
  99 + return argsList;
  100 + }
  101 +}
cashier-mall/build.gradle
1 dependencies { 1 dependencies {
2 implementation project(':cashier-shared') 2 implementation project(':cashier-shared')
3 implementation project(':cashier-trade') 3 implementation project(':cashier-trade')
4 -}  
5 \ No newline at end of file 4 \ No newline at end of file
  5 + implementation 'cn.hutool:hutool-http:5.8.42'
  6 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/MallConfiguration.java 0 → 100644
  1 +package com.diligrp.cashier.mall;
  2 +
  3 +import com.diligrp.cashier.shared.mybatis.MybatisMapperSupport;
  4 +import org.mybatis.spring.annotation.MapperScan;
  5 +import org.springframework.context.annotation.ComponentScan;
  6 +import org.springframework.context.annotation.Configuration;
  7 +
  8 +@Configuration
  9 +@ComponentScan("com.diligrp.cashier.mall")
  10 +@MapperScan(basePackages = {"com.diligrp.cashier.mall.dao"}, markerInterface = MybatisMapperSupport.class)
  11 +public class MallConfiguration {
  12 +
  13 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/MallConstants.java 0 → 100644
  1 +package com.diligrp.cashier.mall;
  2 +
  3 +
  4 +public interface MallConstants {
  5 + String RESULT_SUCCESS = "success";
  6 + String RESULT_FAILURE = "fail";
  7 +
  8 + Integer RT_MALL_SOURCE = 1;
  9 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/api/TestRtMallApi.java 0 → 100644
  1 +package com.diligrp.cashier.mall.api;
  2 +
  3 +import com.diligrp.cashier.mall.sign.RtMallSign;
  4 +import com.diligrp.cashier.shared.annotation.Sign;
  5 +import org.slf4j.Logger;
  6 +import org.slf4j.LoggerFactory;
  7 +import org.springframework.web.bind.annotation.RequestBody;
  8 +import org.springframework.web.bind.annotation.RequestMapping;
  9 +import org.springframework.web.bind.annotation.RestController;
  10 +
  11 +/**
  12 + * @ClassName TestRtMallApi.java
  13 + * @author dengwei
  14 + * @version 1.0.0
  15 + * @Description TestRtMallApi
  16 + */
  17 +@RestController
  18 +@RequestMapping("/test")
  19 +public class TestRtMallApi {
  20 + private static final Logger log = LoggerFactory.getLogger(TestRtMallApi.class);
  21 +
  22 + @RequestMapping("/sign")
  23 + @Sign(sign = RtMallSign.class)
  24 + public String rtMall(@RequestBody Object request) {
  25 + log.info("rt mall");
  26 + return "rt mall";
  27 + }
  28 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/dao/package-info.java 0 → 100644
  1 +/**
  2 + * @ClassName package-info.java
  3 + * @author dengwei
  4 + * @version 1.0.0
  5 + * @Description TODO
  6 + * @date 2025-12-23 17:30
  7 + */
  8 +package com.diligrp.cashier.mall.dao;
cashier-mall/src/main/java/com/diligrp/cashier/mall/domain/RtMarkMessage.java 0 → 100644
  1 +package com.diligrp.cashier.mall.domain;
  2 +
  3 +import com.diligrp.cashier.mall.MallConstants;
  4 +import com.diligrp.cashier.mall.type.RtMarkErrorCode;
  5 +
  6 +/**
  7 + * 大润发返回消息
  8 + *
  9 + * @author dengwei
  10 + * @date 2025/12/24 18:05
  11 + */
  12 +public class RtMarkMessage<T> {
  13 + private String result;
  14 + private String code;
  15 + private String msg;
  16 + private T data;
  17 +
  18 + public RtMarkMessage() {
  19 + }
  20 +
  21 + public String getResult() {
  22 + return result;
  23 + }
  24 +
  25 + public void setResult(String result) {
  26 + this.result = result;
  27 + }
  28 +
  29 + public String getCode() {
  30 + return code;
  31 + }
  32 +
  33 + public void setCode(String code) {
  34 + this.code = code;
  35 + }
  36 +
  37 + public String getMsg() {
  38 + return msg;
  39 + }
  40 +
  41 + public void setMsg(String msg) {
  42 + this.msg = msg;
  43 + }
  44 +
  45 + public T getData() {
  46 + return data;
  47 + }
  48 +
  49 + public void setData(T data) {
  50 + this.data = data;
  51 + }
  52 +
  53 + public static RtMarkMessage<?> success() {
  54 + return failure(RtMarkErrorCode.E0000.getCode(), MallConstants.RESULT_SUCCESS);
  55 + }
  56 +
  57 + public static <E> RtMarkMessage<E> success(E data) {
  58 + RtMarkMessage<E> result = new RtMarkMessage<>();
  59 + result.code = RtMarkErrorCode.E0000.getCode();
  60 + result.result = MallConstants.RESULT_SUCCESS;
  61 + result.data = data;
  62 + return result;
  63 + }
  64 +
  65 + public static RtMarkMessage<?> failure(String message) {
  66 + return failure(RtMarkErrorCode.E5000.getCode(), message);
  67 + }
  68 +
  69 + public static RtMarkMessage<?> failure(String code, String message) {
  70 + RtMarkMessage<?> result = new RtMarkMessage<>();
  71 + result.setResult(MallConstants.RESULT_FAILURE);
  72 + result.code = code;
  73 + result.msg = message;
  74 + return result;
  75 + }
  76 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/exception/MallException.java 0 → 100644
  1 +package com.diligrp.cashier.mall.exception;
  2 +
  3 +
  4 +import com.diligrp.cashier.shared.ErrorCode;
  5 +import com.diligrp.cashier.shared.exception.PlatformServiceException;
  6 +
  7 +/**
  8 + * @ClassName MallException.java
  9 + * @author dengwei
  10 + * @version 1.0.0
  11 + * @date 2025-12-25 14:34
  12 + */
  13 +public class MallException extends PlatformServiceException {
  14 +
  15 + public MallException(String message) {
  16 + super(ErrorCode.SYSTEM_UNKNOWN_ERROR, message);
  17 + }
  18 +
  19 + public MallException(String code, String message) {
  20 + super(code, message);
  21 + }
  22 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/exception/MallExceptionHandler.java 0 → 100644
  1 +package com.diligrp.cashier.mall.exception;
  2 +
  3 +import com.diligrp.cashier.mall.domain.RtMarkMessage;
  4 +import org.slf4j.Logger;
  5 +import org.slf4j.LoggerFactory;
  6 +import org.springframework.core.annotation.Order;
  7 +import org.springframework.web.bind.annotation.ExceptionHandler;
  8 +import org.springframework.web.bind.annotation.RestControllerAdvice;
  9 +
  10 +/**
  11 + * 商城独立处理器
  12 + * 优先于默认
  13 + * 大润发方已经确定好了一套返回标准
  14 + *
  15 + * @author dengwei
  16 + */
  17 +@RestControllerAdvice
  18 +@Order(-1)
  19 +public class MallExceptionHandler {
  20 + private final Logger LOG = LoggerFactory.getLogger(this.getClass());
  21 +
  22 + @ExceptionHandler(RtMartMallException.class)
  23 + public RtMarkMessage<?> rtMartMallException(RtMartMallException ex) {
  24 + LOG.warn("rt mart mall exception", ex);
  25 + return RtMarkMessage.failure(ex.getCode(), ex.getMessage());
  26 + }
  27 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/exception/RtMartMallException.java 0 → 100644
  1 +package com.diligrp.cashier.mall.exception;
  2 +
  3 +import com.diligrp.cashier.mall.MallConstants;
  4 +import com.diligrp.cashier.mall.type.RtMarkErrorCode;
  5 +import com.diligrp.cashier.shared.ErrorCode;
  6 +
  7 +/**
  8 + * @ClassName RtmartMallException.java
  9 + * @author dengwei
  10 + * @version 1.0.0
  11 + * @Description 大润发异常
  12 + * @date 2025-12-24 18:05
  13 + */
  14 +public class RtMartMallException extends MallException {
  15 + public RtMartMallException(String message) {
  16 + super(RtMarkErrorCode.E5000.getCode(), message);
  17 + }
  18 +
  19 + public RtMartMallException(int code) {
  20 + super(RtMarkErrorCode.E5000.getCode(), ErrorCode.MESSAGE_SYSTEM_BUSY);
  21 + }
  22 +
  23 + public RtMartMallException(String code, String message) {
  24 + super(code, message);
  25 + }
  26 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/property/MallDynamicProperty.java 0 → 100644
  1 +package com.diligrp.cashier.mall.property;
  2 +
  3 +import org.springframework.beans.factory.annotation.Value;
  4 +import org.springframework.boot.context.properties.ConfigurationProperties;
  5 +import org.springframework.cloud.context.config.annotation.RefreshScope;
  6 +import org.springframework.context.annotation.Configuration;
  7 +
  8 +import java.util.Collections;
  9 +import java.util.List;
  10 +import java.util.Optional;
  11 +
  12 +/**
  13 + * @author dengwei
  14 + * @version 1.0.0
  15 + * @ClassName MallDynamicProperty.java
  16 + * @Description MallDynamicProperty
  17 + */
  18 +@Configuration
  19 +@RefreshScope
  20 +@ConfigurationProperties(prefix = "sign")
  21 +public class MallDynamicProperty {
  22 +
  23 + private List<AppSecretDynamicProperty> appSecrets;
  24 +
  25 + public static class AppSecretDynamicProperty {
  26 + @Value("${source:1}")
  27 + private Integer source;
  28 +
  29 + @Value("${appKey:}")
  30 + private String appKey;
  31 +
  32 + @Value("${appSecret:}")
  33 + private String appSecret;
  34 +
  35 + @Value("${authUrl:https://hourh5-em-shop.feiniugo.com}")
  36 + private String authUrl;
  37 +
  38 + public Integer getSource() {
  39 + return source;
  40 + }
  41 +
  42 + public void setSource(Integer source) {
  43 + this.source = source;
  44 + }
  45 +
  46 + public String getAppKey() {
  47 + return appKey;
  48 + }
  49 +
  50 + public void setAppKey(String appKey) {
  51 + this.appKey = appKey;
  52 + }
  53 +
  54 + public String getAppSecret() {
  55 + return appSecret;
  56 + }
  57 +
  58 + public void setAppSecret(String appSecret) {
  59 + this.appSecret = appSecret;
  60 + }
  61 +
  62 + public String getAuthUrl() {
  63 + return authUrl;
  64 + }
  65 +
  66 + public void setAuthUrl(String authUrl) {
  67 + this.authUrl = authUrl;
  68 + }
  69 + }
  70 +
  71 + public List<AppSecretDynamicProperty> getAppSecrets() {
  72 + return appSecrets;
  73 + }
  74 +
  75 + public void setAppSecrets(List<AppSecretDynamicProperty> appSecrets) {
  76 + this.appSecrets = appSecrets;
  77 + }
  78 +
  79 + /**
  80 + * getBySource
  81 + *
  82 + */
  83 + public AppSecretDynamicProperty getBySource(Integer source) {
  84 + return Optional.ofNullable(appSecrets)
  85 + .orElse(Collections.emptyList())
  86 + .stream()
  87 + .filter(item -> item.getSource().equals(source))
  88 + .findFirst()
  89 + .orElse(null);
  90 + }
  91 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/sign/RtMallSign.java 0 → 100644
  1 +package com.diligrp.cashier.mall.sign;
  2 +
  3 +import com.diligrp.cashier.mall.MallConstants;
  4 +import com.diligrp.cashier.mall.exception.RtMartMallException;
  5 +import com.diligrp.cashier.mall.property.MallDynamicProperty;
  6 +import com.diligrp.cashier.mall.type.RtMarkErrorCode;
  7 +import com.diligrp.cashier.mall.util.RtMallSignMd5Utils;
  8 +import com.diligrp.cashier.shared.handler.sign.SecuritySign;
  9 +import com.diligrp.cashier.shared.util.JsonUtils;
  10 +import com.fasterxml.jackson.core.type.TypeReference;
  11 +import jakarta.annotation.Resource;
  12 +import jakarta.servlet.http.HttpServletRequest;
  13 +import jakarta.servlet.http.HttpServletResponse;
  14 +import org.slf4j.Logger;
  15 +import org.slf4j.LoggerFactory;
  16 +import org.springframework.stereotype.Component;
  17 +import org.springframework.util.Assert;
  18 +
  19 +import java.util.Objects;
  20 +import java.util.TreeMap;
  21 +
  22 +/**
  23 + * @author dengwei
  24 + * @version 1.0.0
  25 + * @ClassName RtMallSign.java
  26 + * @Description RtMallSign
  27 + * 大润发参数验签
  28 + */
  29 +@Component
  30 +public class RtMallSign implements SecuritySign {
  31 + private static final Logger log = LoggerFactory.getLogger(RtMallSign.class);
  32 +
  33 + @Resource
  34 + private MallDynamicProperty mallDynamicProperty;
  35 +
  36 + @Override
  37 + public void sign(HttpServletRequest request, HttpServletResponse response, Object data) {
  38 + log.info("allParameters:{}", JsonUtils.toJsonString(data));
  39 +
  40 + TreeMap<String, Object> paramMap = JsonUtils.fromJsonString(JsonUtils.toJsonString(data), new TypeReference<>() {
  41 + });
  42 +
  43 + Object appKey = paramMap.get("app_key");
  44 + if (Objects.isNull(appKey)) {
  45 + throw new RtMartMallException(RtMarkErrorCode.E4003.getCode(), RtMarkErrorCode.E4003.getMessage());
  46 + }
  47 +
  48 + MallDynamicProperty.AppSecretDynamicProperty property = mallDynamicProperty.getBySource(MallConstants.RT_MALL_SOURCE);
  49 + if (Objects.isNull(property)) {
  50 + throw new RtMartMallException(RtMarkErrorCode.E4001.getCode(), RtMarkErrorCode.E4001.getMessage());
  51 + }
  52 +
  53 + try {
  54 + log.info("appKey:{}, secretKey:{}", property.getAppKey(), property.getAppSecret());
  55 + String signKey = RtMallSignMd5Utils.generateSign(paramMap, property.getAppSecret());
  56 + Assert.isTrue(Objects.equals(signKey, paramMap.get("sign").toString()), "验签失败!");
  57 + } catch (Exception e) {
  58 + throw new RtMartMallException(RtMarkErrorCode.E4004.getCode(), RtMarkErrorCode.E4004.getMessage());
  59 + }
  60 + }
  61 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/type/RtMarkErrorCode.java 0 → 100644
  1 +package com.diligrp.cashier.mall.type;
  2 +
  3 +/**
  4 + * @ClassName RtmarkErrorCode.java
  5 + * @author dengwei
  6 + * @version 1.0.0
  7 + * @Description RtmarkErrorCode
  8 + * @date 2025-12-23 18:25
  9 + */
  10 +public enum RtMarkErrorCode {
  11 + E0000("E0000", "成功"),
  12 + E4001("E4001", "验签缺少参数"),
  13 + E4002("E4002", "timestamp 不合法"),
  14 + E4003("E4003", "app_key 错误"),
  15 + E4004("E4004", "签名错误"),
  16 + E5000("E5000", "未知错误");
  17 +
  18 + private final String code;
  19 + private final String message;
  20 +
  21 + RtMarkErrorCode(String code, String message) {
  22 + this.code = code;
  23 + this.message = message;
  24 + }
  25 +
  26 + public String getCode() {
  27 + return code;
  28 + }
  29 +
  30 + public String getMessage() {
  31 + return message;
  32 + }
  33 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/util/HttpClientUtils.java 0 → 100644
  1 +package com.diligrp.cashier.mall.util;
  2 +
  3 +import cn.hutool.core.util.IdUtil;
  4 +import cn.hutool.http.HttpException;
  5 +import cn.hutool.http.HttpRequest;
  6 +import com.diligrp.cashier.mall.exception.MallException;
  7 +import com.diligrp.cashier.shared.util.JsonUtils;
  8 +import com.fasterxml.jackson.core.type.TypeReference;
  9 +import org.apache.commons.lang3.StringUtils;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +import org.springframework.http.MediaType;
  13 +
  14 +import java.util.Map;
  15 +
  16 +/**
  17 + * @author dengwei
  18 + * @version 1.0.0
  19 + * @ClassName HttpClientUtils.java
  20 + * @Description HttpClientUtils
  21 + * @date 2025-12-24 14:54
  22 + */
  23 +public class HttpClientUtils {
  24 + private static final Logger log = LoggerFactory.getLogger(HttpClientUtils.class);
  25 +
  26 + /**
  27 + * postJson
  28 + */
  29 + public static String postJson(String url,
  30 + Object body,
  31 + Map<String, String> header,
  32 + String serviceName) {
  33 +
  34 + try {
  35 + String traceId = IdUtil.fastSimpleUUID();
  36 + log.info("traceId:{},url:{}, body:{}", traceId, url, JsonUtils.toJsonString(body));
  37 + String responseBody = HttpRequest
  38 + .post(url)
  39 + .header("content-type", MediaType.APPLICATION_JSON_VALUE)
  40 + .addHeaders(header)
  41 + .timeout(60000)
  42 + .body(JsonUtils.toJsonString(body))
  43 + .execute()
  44 + .body();
  45 + if (StringUtils.isBlank(responseBody)) {
  46 + return null;
  47 + }
  48 + log.info("traceId:{},responseBody:{}", traceId, responseBody);
  49 + return responseBody;
  50 + } catch (HttpException e) {
  51 + log.error("http error url:{}", url, e);
  52 + throw new MallException(serviceName + "服务异常!");
  53 + }
  54 + }
  55 +
  56 + /**
  57 + * postJson
  58 + */
  59 + public static <R> R postJson(String url,
  60 + Object body,
  61 + Map<String, String> header,
  62 + TypeReference<R> jsonTypeReference,
  63 + String serviceName) {
  64 + String responseBody = postJson(url, body, header, serviceName);
  65 + if (StringUtils.isBlank(responseBody)) {
  66 + return null;
  67 + }
  68 + return JsonUtils.fromJsonString(responseBody, jsonTypeReference);
  69 + }
  70 +}
cashier-mall/src/main/java/com/diligrp/cashier/mall/util/RtMallSignMd5Utils.java 0 → 100644
  1 +package com.diligrp.cashier.mall.util;
  2 +
  3 +import com.diligrp.cashier.shared.util.JsonUtils;
  4 +import com.fasterxml.jackson.core.type.TypeReference;
  5 +import org.slf4j.Logger;
  6 +import org.slf4j.LoggerFactory;
  7 +
  8 +import java.security.MessageDigest;
  9 +import java.util.*;
  10 +
  11 +/**
  12 + * @ClassName SignMd5Utils.java
  13 + * @author dengwei
  14 + * @version 1.0.0
  15 + * @Description
  16 + * 签名算法
  17 + * 参考大润发定义:https://shopex.yuque.com/hl0rrx/vlp0m4/kmcazgnimg28d8ih?singleDoc#
  18 + */
  19 +public class RtMallSignMd5Utils {
  20 + private static final Logger log = LoggerFactory.getLogger(RtMallSignMd5Utils.class);
  21 +
  22 + /**
  23 + * 生成签名
  24 + *
  25 + */
  26 + public static String generateSign(Object data, String appSecret) {
  27 + Map<String, Object> params = JsonUtils.convertValueV2(data, new TypeReference<>() {
  28 + });
  29 + // 1. 过滤 null,转换 boolean,递归规范化并排序
  30 + String normalized = normalizeAndSort(params);
  31 + log.info("normalize: {}", normalized);
  32 + // 2. 拼接私钥
  33 + String signSource = appSecret + normalized + appSecret;
  34 + log.info("signSource: {}", signSource);
  35 + // 3. MD5 + 大写
  36 + String sign = md5ToUpper(signSource);
  37 + log.info("sign: {}", sign);
  38 + return sign;
  39 + }
  40 +
  41 + /**
  42 + * 递归规范化并排序,返回拼接字符串(无分隔符)
  43 + */
  44 + @SuppressWarnings("unchecked")
  45 + private static String normalizeAndSort(Object obj) {
  46 + switch (obj) {
  47 + case null -> {
  48 + return "";
  49 + }
  50 + case Map map1 -> {
  51 + Map<String, Object> map = (Map<String, Object>) obj;
  52 + // 过滤 null,并转换 boolean
  53 + Map<String, Object> filtered = new LinkedHashMap<>();
  54 + for (Map.Entry<String, Object> entry : map.entrySet()) {
  55 + if (entry.getValue() == null) {
  56 + continue;
  57 + }
  58 + Object val = convertBoolean(entry.getValue());
  59 + filtered.put(entry.getKey(), val);
  60 + }
  61 + // 按 key 字典序排序
  62 + List<String> sortedKeys = new ArrayList<>(filtered.keySet());
  63 + Collections.sort(sortedKeys); // 字典序
  64 +
  65 +
  66 + StringBuilder sb = new StringBuilder();
  67 + for (String key : sortedKeys) {
  68 + sb.append(key).append(normalizeAndSort(filtered.get(key)));
  69 + }
  70 + return sb.toString();
  71 + }
  72 + case List list1 -> {
  73 + List<Object> list = (List<Object>) obj;
  74 + // 转为 Map<String, Object>,key 为索引字符串
  75 + Map<String, Object> indexMap = new LinkedHashMap<>();
  76 + for (int i = 0; i < list.size(); i++) {
  77 + indexMap.put(String.valueOf(i), list.get(i));
  78 + }
  79 + // 递归处理(会自动过滤 null、转换 boolean、排序 key)
  80 + return normalizeAndSort(indexMap);
  81 + // 递归处理(会自动过滤 null、转换 boolean、排序 key)
  82 + }
  83 + case Object[] array -> {
  84 + // 处理数组类型,转换为 Map 并递归处理
  85 + Map<String, Object> arrayMap = new LinkedHashMap<>();
  86 + for (int i = 0; i < array.length; i++) {
  87 + arrayMap.put(String.valueOf(i), array[i]);
  88 + }
  89 + return normalizeAndSort(arrayMap);
  90 + }
  91 + default -> {
  92 + }
  93 + }
  94 +
  95 + // 基本类型:转字符串
  96 + return obj.toString();
  97 + }
  98 +
  99 + /**
  100 + * 转换 Boolean 为 "1"/"0"
  101 + */
  102 + private static Object convertBoolean(Object obj) {
  103 + if (obj instanceof Boolean) {
  104 + return (Boolean) obj ? "1" : "0";
  105 + }
  106 + return obj;
  107 + }
  108 +
  109 + /**
  110 + * MD5 并转大写
  111 + */
  112 + private static String md5ToUpper(String input) {
  113 + try {
  114 + MessageDigest md = MessageDigest.getInstance("MD5");
  115 + byte[] digest = md.digest(input.getBytes("UTF-8"));
  116 + StringBuilder sb = new StringBuilder();
  117 + for (byte b : digest) {
  118 + sb.append(String.format("%02x", b));
  119 + }
  120 + return sb.toString().toUpperCase();
  121 + } catch (Exception e) {
  122 + throw new RuntimeException("MD5 failed", e);
  123 + }
  124 + }
  125 +
  126 + public static void main(String[] args) {
  127 + // 构建顶层参数 Map
  128 + Map<String, Object> params = new HashMap<>();
  129 +
  130 + params.put("app_key", "221zzcwxee30nucj0rx1");
  131 + params.put("timestamp", 1734351194L); // 使用 Long 避免溢出
  132 + params.put("nonce_str", "cvwEv8JRNzHd3eaV");
  133 + params.put("order_id", "1234567890"); // order_id 可以是字符串或数字,这里按字符串处理更安全
  134 +
  135 + // 构建 item_list
  136 + List<Map<String, Object>> itemList = new ArrayList<>();
  137 +
  138 + for (int i = 1; i <= 11; i++) {
  139 + Map<String, Object> subOrder = new HashMap<>();
  140 + subOrder.put("sub_order_id", i);
  141 + subOrder.put("price", 100);
  142 + itemList.add(subOrder);
  143 + }
  144 + params.put("item_list", itemList);
  145 +
  146 + // 打印验证(可选)
  147 + log.info("Params: {}", JsonUtils.toJsonString(params));
  148 +
  149 + String sign = generateSign(params, "ho7ygrsrwged4zwhwpbadtdgzugmulez");
  150 + log.info("Sign: {}", sign);
  151 + }
  152 +}
cashier-mall/src/main/resources/package.json 0 → 100644
  1 +{
  2 + "name": "resources",
  3 + "version": "1.0.0",
  4 + "description": "",
  5 + "main": "index.js",
  6 + "scripts": {
  7 + "test": "echo \"Error: no test specified\" && exit 1"
  8 + },
  9 + "repository": {
  10 + "type": "git",
  11 + "url": "https://git3.nong12.com/cashierdesk/dili-cashier.git"
  12 + },
  13 + "private": true
  14 +}
cashier-shared/build.gradle
@@ -6,6 +6,7 @@ dependencies { @@ -6,6 +6,7 @@ dependencies {
6 api 'org.springframework.boot:spring-boot-starter-amqp' 6 api 'org.springframework.boot:spring-boot-starter-amqp'
7 api 'org.springframework.cloud:spring-cloud-starter-loadbalancer' 7 api 'org.springframework.cloud:spring-cloud-starter-loadbalancer'
8 api 'org.springframework.cloud:spring-cloud-starter-openfeign' 8 api 'org.springframework.cloud:spring-cloud-starter-openfeign'
  9 + api 'org.springframework.boot:spring-boot-starter-aop'
9 api libs.cache.caffeine 10 api libs.cache.caffeine
10 api libs.common.collections4 11 api libs.common.collections4
11 12
cashier-shared/src/main/java/com/diligrp/cashier/shared/annotation/ParamLogPrint.java 0 → 100644
  1 +package com.diligrp.cashier.shared.annotation;
  2 +
  3 +import java.lang.annotation.ElementType;
  4 +import java.lang.annotation.Retention;
  5 +import java.lang.annotation.RetentionPolicy;
  6 +import java.lang.annotation.Target;
  7 +
  8 +/**
  9 + * @author dengwei
  10 + * @version 1.0.0
  11 + * @ClassName LogPrint.java
  12 + * @Description 参数日志是否打印
  13 + */
  14 +@Target({ElementType.METHOD})
  15 +@Retention(RetentionPolicy.RUNTIME)
  16 +public @interface ParamLogPrint {
  17 + /**
  18 + * 使用该注解默认打印
  19 + *
  20 + * @return boolean
  21 + */
  22 + boolean print() default true;
  23 +
  24 + /**
  25 + * desc
  26 + * 触发点:HTTP MQ and so on
  27 + *
  28 + * @return {@link String}
  29 + */
  30 + String desc() default "HTTP接口";
  31 +
  32 + /**
  33 + * outPrint
  34 + */
  35 + boolean outPrint() default false;
  36 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/annotation/RepeatSubmit.java 0 → 100644
  1 +package com.diligrp.cashier.shared.annotation;
  2 +
  3 +
  4 +import com.diligrp.cashier.shared.handler.duplication.DuplicationSubmit;
  5 +import com.diligrp.cashier.shared.handler.duplication.IgnoreDuplicationSubmit;
  6 +
  7 +import java.lang.annotation.*;
  8 +import java.util.concurrent.TimeUnit;
  9 +
  10 +/**
  11 + * @author dengwei
  12 + * @version 1.0.0
  13 + * @ClassName RepeatSubmit.java
  14 + * @Description RepeatSubmit
  15 + */
  16 +@Inherited
  17 +@Target(ElementType.METHOD)
  18 +@Retention(RetentionPolicy.RUNTIME)
  19 +@Documented
  20 +public @interface RepeatSubmit {
  21 + /**
  22 + * 前缀
  23 + */
  24 + String prefix() default "";
  25 +
  26 + /**
  27 + * 用于存放SpEL表达式的数组
  28 + */
  29 + String[] value() default {};
  30 +
  31 + /**
  32 + * 是否值保留一个值
  33 + */
  34 + boolean onlyOne() default false;
  35 +
  36 + /**
  37 + * 等待获取锁的最大时长
  38 + * 未设置的-快速失败
  39 + * 设置-等待指定时间
  40 + */
  41 + int waitTime() default 0;
  42 +
  43 + /**
  44 + * 获取锁之后,锁的自动释放时间
  45 + */
  46 + int leaseTime() default 0;
  47 +
  48 + /**
  49 + * 时间单位
  50 + */
  51 + TimeUnit timeUnit() default TimeUnit.SECONDS;
  52 +
  53 + /**
  54 + * 重复提交
  55 + */
  56 + Class<? extends DuplicationSubmit> duplicationSubmit() default IgnoreDuplicationSubmit.class;
  57 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/annotation/Sign.java 0 → 100644
  1 +package com.diligrp.cashier.shared.annotation;
  2 +
  3 +
  4 +import com.diligrp.cashier.shared.handler.sign.NoSign;
  5 +import com.diligrp.cashier.shared.handler.sign.SecuritySign;
  6 +
  7 +import java.lang.annotation.*;
  8 +
  9 +/**
  10 + * @author dengwei
  11 + * @version 1.0.0
  12 + * @ClassName Sign.java
  13 + * @Description 签名验签
  14 + **/
  15 +@Target({ElementType.TYPE, ElementType.METHOD})
  16 +@Retention(RetentionPolicy.RUNTIME)
  17 +@Documented
  18 +public @interface Sign {
  19 + Class<? extends SecuritySign> sign() default NoSign.class;
  20 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/aop/RepeatSubmitAop.java 0 → 100644
  1 +package com.diligrp.cashier.shared.aop;
  2 +
  3 +import com.diligrp.cashier.shared.ErrorCode;
  4 +import com.diligrp.cashier.shared.annotation.RepeatSubmit;
  5 +import com.diligrp.cashier.shared.domain.handler.AspectIdempotent;
  6 +import com.diligrp.cashier.shared.exception.PlatformServiceException;
  7 +import com.diligrp.cashier.shared.handler.duplication.DuplicationSubmit;
  8 +import com.diligrp.cashier.shared.util.SpringContextUtils;
  9 +import jakarta.annotation.Resource;
  10 +import org.apache.commons.lang3.StringUtils;
  11 +import org.aspectj.lang.JoinPoint;
  12 +import org.aspectj.lang.ProceedingJoinPoint;
  13 +import org.aspectj.lang.annotation.AfterThrowing;
  14 +import org.aspectj.lang.annotation.Around;
  15 +import org.aspectj.lang.annotation.Aspect;
  16 +import org.redisson.api.RLock;
  17 +import org.redisson.api.RedissonClient;
  18 +import org.slf4j.Logger;
  19 +import org.slf4j.LoggerFactory;
  20 +import org.springframework.core.annotation.Order;
  21 +import org.springframework.stereotype.Component;
  22 +
  23 +/**
  24 + * @author dengwei
  25 + * @version 1.0.0
  26 + * @ClassName RepeatSubmitAop.java
  27 + * @Description 防重提交
  28 + */
  29 +@Aspect
  30 +@Component
  31 +@Order(2)
  32 +public class RepeatSubmitAop {
  33 + private static final Logger log = LoggerFactory.getLogger(RepeatSubmitAop.class);
  34 +
  35 + @Resource
  36 + private RedissonClient redissonClient;
  37 +
  38 + /**
  39 + * around
  40 + */
  41 + @Around(value = "@annotation(repeatSubmit)")
  42 + public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
  43 + DuplicationSubmit<AspectIdempotent> duplicationSubmit = SpringContextUtils.getBean(repeatSubmit.duplicationSubmit());
  44 + // 获取锁key
  45 + String key = duplicationSubmit.key(new AspectIdempotent(joinPoint, repeatSubmit));
  46 + log.info("repeat submit aspect key: {}", key);
  47 + if (StringUtils.isBlank(key)) {
  48 + return joinPoint.proceed();
  49 + }
  50 +
  51 + RLock lock = redissonClient.getLock(key);
  52 + boolean repeat = repeatSubmit.waitTime() > 0 ?
  53 + lock.tryLock(repeatSubmit.waitTime(), repeatSubmit.leaseTime(), repeatSubmit.timeUnit()) :
  54 + lock.tryLock();
  55 + try {
  56 + if (!repeat) {
  57 + log.info("重复操作 key={}", key);
  58 + throw new PlatformServiceException(ErrorCode.SYSTEM_BUSY_ERROR, "请勿重复操作!");
  59 + }
  60 + return joinPoint.proceed();
  61 + } finally {
  62 + if (lock.isHeldByCurrentThread()) {
  63 + lock.unlock();
  64 + }
  65 + }
  66 + }
  67 +
  68 + /**
  69 + * 拦截异常操作
  70 + * 未进入目标方法抛出的异常无法监听
  71 + */
  72 + @AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e")
  73 + public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) {
  74 + log.error("repeat submit aspect after throwing");
  75 + }
  76 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/aop/SecuritySignAop.java 0 → 100644
  1 +package com.diligrp.cashier.shared.aop;
  2 +
  3 +import com.diligrp.cashier.shared.annotation.Sign;
  4 +import com.diligrp.cashier.shared.handler.sign.NoSign;
  5 +import com.diligrp.cashier.shared.handler.sign.SecuritySign;
  6 +import com.diligrp.cashier.shared.util.SpringContextUtils;
  7 +import org.apache.commons.lang3.ArrayUtils;
  8 +import org.aspectj.lang.JoinPoint;
  9 +import org.aspectj.lang.annotation.Aspect;
  10 +import org.aspectj.lang.annotation.Before;
  11 +import org.springframework.core.annotation.Order;
  12 +import org.springframework.stereotype.Component;
  13 +
  14 +/**
  15 + * @author dengwei
  16 + * @version 1.0.0
  17 + * @ClassName SecuritySignAop.java
  18 + * @Description SecuritySignAop
  19 + * 验签流程 放到网关合理点 前期先放到AOP下
  20 + */
  21 +@Aspect
  22 +@Component
  23 +@Order(1)
  24 +public class SecuritySignAop {
  25 + /**
  26 + * doBefore
  27 + */
  28 + @Before(value = "@annotation(sign)")
  29 + public void doBefore(JoinPoint joinPoint, Sign sign) {
  30 + Class<? extends SecuritySign> securitySign = sign.sign();
  31 + if (securitySign == NoSign.class) {
  32 + return;
  33 + }
  34 +
  35 + Object[] args = joinPoint.getArgs();
  36 + if (ArrayUtils.isEmpty(args)) {
  37 + return;
  38 + }
  39 + SecuritySign signBean = SpringContextUtils.getBean(securitySign);
  40 + signBean.sign(null, null, args[0]);
  41 + }
  42 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/domain/handler/AspectIdempotent.java 0 → 100644
  1 +package com.diligrp.cashier.shared.domain.handler;
  2 +
  3 +import com.diligrp.cashier.shared.annotation.RepeatSubmit;
  4 +import org.aspectj.lang.ProceedingJoinPoint;
  5 +
  6 +/**
  7 + * @author dengwei
  8 + * @version 1.0.0
  9 + * @ClassName AspectIdempotent.java
  10 + * @Description AspectIdempotent
  11 + * @date 2025-07-03 10:42
  12 + */
  13 +public class AspectIdempotent {
  14 + private ProceedingJoinPoint joinPoint;
  15 + private RepeatSubmit repeatSubmit;
  16 +
  17 + public AspectIdempotent() {
  18 + }
  19 +
  20 + public AspectIdempotent(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) {
  21 + this.joinPoint = joinPoint;
  22 + this.repeatSubmit = repeatSubmit;
  23 + }
  24 +
  25 + public ProceedingJoinPoint getJoinPoint() {
  26 + return joinPoint;
  27 + }
  28 +
  29 + public void setJoinPoint(ProceedingJoinPoint joinPoint) {
  30 + this.joinPoint = joinPoint;
  31 + }
  32 +
  33 + public RepeatSubmit getRepeatSubmit() {
  34 + return repeatSubmit;
  35 + }
  36 +
  37 + public void setRepeatSubmit(RepeatSubmit repeatSubmit) {
  38 + this.repeatSubmit = repeatSubmit;
  39 + }
  40 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/handler/duplication/DuplicationSubmit.java 0 → 100644
  1 +package com.diligrp.cashier.shared.handler.duplication;
  2 +
  3 +/**
  4 + * @author dengwei
  5 + * @version 1.0.0
  6 + * @ClassName RepeatSubmit.java
  7 + * @Description RepeatSubmit
  8 + * @date 2025-07-03 10:56
  9 + */
  10 +public interface DuplicationSubmit<T> {
  11 + String key(T param);
  12 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/handler/duplication/IgnoreDuplicationSubmit.java 0 → 100644
  1 +package com.diligrp.cashier.shared.handler.duplication;
  2 +
  3 +import org.slf4j.Logger;
  4 +import org.slf4j.LoggerFactory;
  5 +import org.springframework.stereotype.Component;
  6 +
  7 +/**
  8 + * @author dengwei
  9 + * @version 1.0.0
  10 + * @ClassName TokenDuplicationSubmit.java
  11 + * @Description TokenDuplicationSubmit
  12 + */
  13 +@Component
  14 +public class IgnoreDuplicationSubmit implements DuplicationSubmit<Object> {
  15 + private static final Logger LOGGER = LoggerFactory.getLogger(IgnoreDuplicationSubmit.class);
  16 +
  17 + @Override
  18 + public String key(Object param) {
  19 + LOGGER.info("ignore");
  20 + return "ignore";
  21 + }
  22 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/handler/duplication/SpelDuplicationSubmit.java 0 → 100644
  1 +package com.diligrp.cashier.shared.handler.duplication;
  2 +
  3 +import com.diligrp.cashier.shared.annotation.RepeatSubmit;
  4 +import com.diligrp.cashier.shared.domain.handler.AspectIdempotent;
  5 +import org.apache.commons.lang3.ArrayUtils;
  6 +import org.apache.commons.lang3.StringUtils;
  7 +import org.aspectj.lang.ProceedingJoinPoint;
  8 +import org.aspectj.lang.reflect.MethodSignature;
  9 +import org.slf4j.Logger;
  10 +import org.slf4j.LoggerFactory;
  11 +import org.springframework.expression.EvaluationContext;
  12 +import org.springframework.expression.Expression;
  13 +import org.springframework.expression.ExpressionParser;
  14 +import org.springframework.expression.spel.standard.SpelExpressionParser;
  15 +import org.springframework.expression.spel.support.StandardEvaluationContext;
  16 +import org.springframework.stereotype.Component;
  17 +
  18 +import java.util.Objects;
  19 +import java.util.Optional;
  20 +import java.util.StringJoiner;
  21 +
  22 +/**
  23 + * @author dengwei
  24 + * @version 1.0.0
  25 + * @ClassName SpelDuplicationSubmit.java
  26 + * @Description SpelDuplicationSubmit
  27 + */
  28 +@Component
  29 +public class SpelDuplicationSubmit implements DuplicationSubmit<AspectIdempotent> {
  30 + private static final Logger log = LoggerFactory.getLogger(SpelDuplicationSubmit.class);
  31 +
  32 + private final ExpressionParser parser = new SpelExpressionParser();
  33 +
  34 + @Override
  35 + public String key(AspectIdempotent param) {
  36 + log.info("spel duplicationSubmit~~~~");
  37 + return getKey(param.getJoinPoint(), param.getRepeatSubmit());
  38 + }
  39 +
  40 + /**
  41 + * 根据spel获取值
  42 + */
  43 + private String getKey(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) {
  44 + String[] spelValStr = repeatSubmit.value();
  45 + if (ArrayUtils.isEmpty(spelValStr)) {
  46 + return null;
  47 + }
  48 +
  49 + // 获取方法参数名和参数值
  50 + MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  51 + String[] parameterNames = signature.getParameterNames();
  52 + Object[] args = joinPoint.getArgs();
  53 +
  54 + // 创建EvaluationContext并设置变量
  55 + EvaluationContext context = new StandardEvaluationContext();
  56 + for (int i = 0; i < parameterNames.length; i++) {
  57 + context.setVariable(parameterNames[i], args[i]);
  58 + }
  59 +
  60 + boolean onlyOne = repeatSubmit.onlyOne();
  61 + StringJoiner stringJoiner = new StringJoiner("_", repeatSubmit.prefix(), StringUtils.EMPTY);
  62 + for (String expression : spelValStr) {
  63 + try {
  64 + Expression exp = parser.parseExpression(expression);
  65 + Object targetValue = exp.getValue(context);
  66 + Optional.ofNullable(targetValue).ifPresent(value -> {
  67 + stringJoiner.add(value.toString());
  68 + });
  69 + // 如果只强制拿取一个并且不为空
  70 + if (onlyOne && Objects.nonNull(targetValue)) {
  71 + break;
  72 + }
  73 + } catch (Exception e) {
  74 + log.warn("expression get value error", e);
  75 + }
  76 + }
  77 + return stringJoiner.toString();
  78 + }
  79 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/handler/sign/NoSign.java 0 → 100644
  1 +package com.diligrp.cashier.shared.handler.sign;
  2 +
  3 +import jakarta.servlet.http.HttpServletRequest;
  4 +import jakarta.servlet.http.HttpServletResponse;
  5 +import org.slf4j.Logger;
  6 +import org.slf4j.LoggerFactory;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +/**
  10 + * @author dengwei
  11 + * @version 1.0.0
  12 + * @ClassName NoSign.java
  13 + * @Description NoSign
  14 + */
  15 +@Component
  16 +public class NoSign implements SecuritySign {
  17 + private static final Logger log = LoggerFactory.getLogger(NoSign.class);
  18 +
  19 + @Override
  20 + public void sign(HttpServletRequest request, HttpServletResponse response, Object handler) {
  21 + log.info("该无需验签~~");
  22 + }
  23 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/handler/sign/SecuritySign.java 0 → 100644
  1 +package com.diligrp.cashier.shared.handler.sign;
  2 +
  3 +import jakarta.servlet.http.HttpServletRequest;
  4 +import jakarta.servlet.http.HttpServletResponse;
  5 +
  6 +/**
  7 + * @author dengwei
  8 + * @version 1.0.0
  9 + * @ClassName ParameterSign.java
  10 + * @Description SecuritySign
  11 + * 迁移至网关实现
  12 + * @date 2025-07-09 16:08
  13 + */
  14 +public interface SecuritySign {
  15 + void sign(HttpServletRequest request, HttpServletResponse response, Object handler);
  16 +}
cashier-shared/src/main/java/com/diligrp/cashier/shared/util/JsonUtils.java
@@ -28,7 +28,7 @@ public class JsonUtils { @@ -28,7 +28,7 @@ public class JsonUtils {
28 28
29 private static ObjectMapper objectMapper = initObjectMapper(); 29 private static ObjectMapper objectMapper = initObjectMapper();
30 30
31 - private static ObjectMapper initObjectMapper(){ 31 + private static ObjectMapper initObjectMapper() {
32 Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder = new Jackson2ObjectMapperBuilder(); 32 Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder = new Jackson2ObjectMapperBuilder();
33 initObjectMapperBuilder(jackson2ObjectMapperBuilder); 33 initObjectMapperBuilder(jackson2ObjectMapperBuilder);
34 ObjectMapper objectMapper = jackson2ObjectMapperBuilder.createXmlMapper(false).build(); 34 ObjectMapper objectMapper = jackson2ObjectMapperBuilder.createXmlMapper(false).build();
@@ -42,15 +42,15 @@ public class JsonUtils { @@ -42,15 +42,15 @@ public class JsonUtils {
42 builder.timeZone(TimeZone.getTimeZone(ZoneOffset.of("+8"))); 42 builder.timeZone(TimeZone.getTimeZone(ZoneOffset.of("+8")));
43 builder.serializationInclusion(JsonInclude.Include.NON_NULL); 43 builder.serializationInclusion(JsonInclude.Include.NON_NULL);
44 builder.featuresToDisable( 44 builder.featuresToDisable(
45 - DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, // Json串的属性无JavaBean字段对应时,避免抛出异常  
46 - DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, // JavaBean中primitive类型的字段无Json属性时,避免抛出异常  
47 - DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, // Json串数字类型属性,赋值JavaBean中Enum字段时,避免抛出异常  
48 - SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,  
49 - SerializationFeature.FAIL_ON_EMPTY_BEANS 45 + DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, // Json串的属性无JavaBean字段对应时,避免抛出异常
  46 + DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, // JavaBean中primitive类型的字段无Json属性时,避免抛出异常
  47 + DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, // Json串数字类型属性,赋值JavaBean中Enum字段时,避免抛出异常
  48 + SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
  49 + SerializationFeature.FAIL_ON_EMPTY_BEANS
50 ); 50 );
51 builder.featuresToEnable( 51 builder.featuresToEnable(
52 - DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,  
53 - DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY 52 + DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,
  53 + DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY
54 ); 54 );
55 55
56 var dateTimeFormatter = DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT); 56 var dateTimeFormatter = DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT);
@@ -74,7 +74,7 @@ public class JsonUtils { @@ -74,7 +74,7 @@ public class JsonUtils {
74 } 74 }
75 } 75 }
76 76
77 - public static <T> T fromJsonString(String json, TypeReference<T> typeReference){ 77 + public static <T> T fromJsonString(String json, TypeReference<T> typeReference) {
78 try { 78 try {
79 return objectMapper.readValue(json, typeReference); 79 return objectMapper.readValue(json, typeReference);
80 } catch (JsonProcessingException ex) { 80 } catch (JsonProcessingException ex) {
@@ -106,6 +106,10 @@ public class JsonUtils { @@ -106,6 +106,10 @@ public class JsonUtils {
106 return fromJsonString(toJsonString(value), type); 106 return fromJsonString(toJsonString(value), type);
107 } 107 }
108 108
  109 + public static <T> T convertValueV2(Object value, TypeReference<T> type) {
  110 + return fromJsonString(toJsonString(value), type);
  111 + }
  112 +
109 113
110 public static <T> String toJsonString(T object) { 114 public static <T> String toJsonString(T object) {
111 try { 115 try {
cashier-shared/src/main/java/com/diligrp/cashier/shared/util/SpringContextUtils.java 0 → 100644
  1 +package com.diligrp.cashier.shared.util;
  2 +
  3 +import org.springframework.beans.BeansException;
  4 +import org.springframework.context.ApplicationContext;
  5 +import org.springframework.context.ApplicationContextAware;
  6 +import org.springframework.stereotype.Component;
  7 +
  8 +import java.util.Map;
  9 +
  10 +
  11 +/**
  12 + * SpringUtil
  13 + *
  14 + * @author dengwei
  15 + * @date 2025/12/24 18:05
  16 + */
  17 +@Component
  18 +public class SpringContextUtils implements ApplicationContextAware {
  19 +
  20 + private static ApplicationContext applicationContext;
  21 +
  22 + @Override
  23 + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  24 + if (SpringContextUtils.applicationContext == null) {
  25 + SpringContextUtils.applicationContext = applicationContext;
  26 + }
  27 + }
  28 +
  29 + public static ApplicationContext getApplicationContext() {
  30 + return applicationContext;
  31 + }
  32 +
  33 + // 通过name获取Bean
  34 + public static Object getBean(String name) {
  35 + return getApplicationContext().getBean(name);
  36 + }
  37 +
  38 + // 通过class获取Bean
  39 + public static <T> T getBean(Class<T> clazz) {
  40 + return getApplicationContext().getBean(clazz);
  41 + }
  42 +
  43 + // 通过name,以及Clazz返回指定的Bean
  44 + public static <T> T getBean(String name, Class<T> clazz) {
  45 + return getApplicationContext().getBean(name, clazz);
  46 + }
  47 +
  48 + public static <T> Map<String, T> getBeanOfTpe(Class<T> clazz) {
  49 + return getApplicationContext().getBeansOfType(clazz);
  50 + }
  51 +
  52 +}