Commit aa4d9869dfcee6af8eaf4a0e337f2d0ff84372a8

Authored by dengwei
1 parent 7d925fdd

feat mall user info

Showing 28 changed files with 936 additions and 24 deletions
cashier-mall/src/main/java/com/diligrp/cashier/mall/MallConstants.java
1 1 package com.diligrp.cashier.mall;
2 2  
3 3  
  4 +/**
  5 + * 商城常量
  6 + */
4 7 public interface MallConstants {
5 8 String RESULT_SUCCESS = "success";
6 9 String RESULT_FAILURE = "fail";
7 10  
8 11 Integer RT_MALL_SOURCE = 1;
  12 +
  13 + String MALL_TOKEN = "mall_token:";
  14 + String MALL_USER_INFO = "mall_user_info:";
9 15 }
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/api/MallAuthApi.java 0 → 100644
  1 +package com.diligrp.cashier.mall.api;
  2 +
  3 +import com.diligrp.cashier.mall.MallConstants;
  4 +import com.diligrp.cashier.mall.context.MallInitializeContext;
  5 +import com.diligrp.cashier.mall.domain.rtmall.RtMarkMessage;
  6 +import com.diligrp.cashier.mall.domain.rtmall.co.AuthLoginCO;
  7 +import com.diligrp.cashier.mall.domain.rtmall.co.UserInfoCO;
  8 +import com.diligrp.cashier.mall.domain.rtmall.vo.UserInfoVO;
  9 +import com.diligrp.cashier.mall.sign.RtMallSign;
  10 +import com.diligrp.cashier.shared.annotation.ParamLogPrint;
  11 +import com.diligrp.cashier.shared.annotation.Sign;
  12 +import com.diligrp.cashier.shared.domain.Message;
  13 +import com.diligrp.cashier.shared.util.JsonUtils;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.slf4j.Logger;
  17 +import org.slf4j.LoggerFactory;
  18 +import org.springframework.data.redis.core.RedisTemplate;
  19 +import org.springframework.validation.annotation.Validated;
  20 +import org.springframework.web.bind.annotation.PostMapping;
  21 +import org.springframework.web.bind.annotation.RequestBody;
  22 +import org.springframework.web.bind.annotation.RequestMapping;
  23 +import org.springframework.web.bind.annotation.RestController;
  24 +
  25 +import java.util.Objects;
  26 +
  27 +/**
  28 + * @ClassName MallAuthApi.java
  29 + * @author dengwei
  30 + * @version 1.0.0
  31 + * @Description MallAuthApi
  32 + */
  33 +@RestController
  34 +@RequestMapping("/mall")
  35 +@Validated
  36 +public class MallAuthApi {
  37 + private static final Logger log = LoggerFactory.getLogger(MallAuthApi.class);
  38 + @Resource
  39 + private RedisTemplate<String, Object> redisTemplate;
  40 +
  41 + /**
  42 + * 获取授权连接
  43 + */
  44 + @PostMapping("/auth")
  45 + @ParamLogPrint(outPrint = true)
  46 + public Message<String> authLogin(@RequestBody @Valid AuthLoginCO authLogin) {
  47 + return Message.success(MallInitializeContext.getByChannel(authLogin.getChannel()).authLogin(authLogin));
  48 + }
  49 +
  50 + /**
  51 + * 获取用户信息
  52 + */
  53 + @PostMapping("/user/info")
  54 + @ParamLogPrint(outPrint = true)
  55 + @Sign(sign = RtMallSign.class)
  56 + public RtMarkMessage<UserInfoVO> userInfo(@RequestBody @Valid Object req) {
  57 + UserInfoCO userInfo = JsonUtils.convertValue(req, UserInfoCO.class);
  58 + AuthLoginCO authLogin = JsonUtils.fromJsonString(Objects.requireNonNull(redisTemplate.opsForValue().get(MallConstants.MALL_TOKEN + userInfo.getToken())).toString(), AuthLoginCO.class);
  59 + UserInfoVO userInfoVO = MallInitializeContext.getByChannel(authLogin.getChannel()).userInfo(authLogin);
  60 + return RtMarkMessage.success(userInfoVO);
  61 + }
  62 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/context/MallInitializeContext.java 0 → 100644
  1 +package com.diligrp.cashier.mall.context;
  2 +
  3 +import com.diligrp.cashier.mall.exception.MallException;
  4 +import com.diligrp.cashier.mall.service.paychannel.AbstractPayChannel;
  5 +import com.diligrp.cashier.mall.service.sourcechannel.AbstractChannel;
  6 +import com.diligrp.cashier.shared.util.SpringContextUtils;
  7 +import com.google.common.collect.Maps;
  8 +import org.slf4j.Logger;
  9 +import org.slf4j.LoggerFactory;
  10 +import org.springframework.beans.factory.DisposableBean;
  11 +import org.springframework.beans.factory.InitializingBean;
  12 +import org.springframework.stereotype.Component;
  13 +
  14 +import java.util.Map;
  15 +import java.util.Optional;
  16 +
  17 +/**
  18 + * @author dengwei
  19 + * @version 1.0.0
  20 + * @ClassName MallInitializeContext.java
  21 + * @Description MallInitializeContext
  22 + */
  23 +@Component
  24 +public class MallInitializeContext implements InitializingBean, DisposableBean {
  25 + private static final Logger log = LoggerFactory.getLogger(MallInitializeContext.class);
  26 + public static Map<String, AbstractChannel> SOURCE_CHANNEL_MAP = Maps.newConcurrentMap();
  27 + public static Map<Integer, AbstractPayChannel> PAY_CHANNEL_MAP = Maps.newConcurrentMap();
  28 +
  29 + /**
  30 + * 容器启动完成加载
  31 + */
  32 + @Override
  33 + public void afterPropertiesSet() throws Exception {
  34 + SpringContextUtils.getBeanOfTpe(AbstractChannel.class).forEach((key, value) -> {
  35 + SOURCE_CHANNEL_MAP.put(value.channel(), value);
  36 + });
  37 +
  38 + SpringContextUtils.getBeanOfTpe(AbstractPayChannel.class).forEach((key, value) -> {
  39 + PAY_CHANNEL_MAP.put(value.getPayChannel(), value);
  40 + });
  41 + }
  42 +
  43 + /**
  44 + * 容器销毁加载
  45 + */
  46 + @Override
  47 + public void destroy() throws Exception {
  48 + log.info("spring container destroy");
  49 + }
  50 +
  51 + /**
  52 + * getByType
  53 + */
  54 + public static AbstractChannel getByChannel(String channel) {
  55 + return Optional.ofNullable(SOURCE_CHANNEL_MAP.get(channel)).orElseThrow(() -> new MallException("不支持该渠道!"));
  56 + }
  57 +
  58 + /**
  59 + * getByPayChannel
  60 + */
  61 + public static AbstractPayChannel getByPayChannel(Integer payChannel) {
  62 + return Optional.ofNullable(PAY_CHANNEL_MAP.get(payChannel)).orElseThrow(() -> new MallException("不支持该渠道!"));
  63 + }
  64 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/domain/rtmall/RtMarkBaseCO.java 0 → 100644
  1 +package com.diligrp.cashier.mall.domain.rtmall;
  2 +
  3 +import com.fasterxml.jackson.databind.PropertyNamingStrategies;
  4 +import com.fasterxml.jackson.databind.annotation.JsonNaming;
  5 +
  6 +/**
  7 + * @ClassName RtMarkBaseCO.java
  8 + * @author dengwei
  9 + * @version 1.0.0
  10 + * @Description RtMarkBaseCO
  11 + * @date 2025-12-25 16:26
  12 + */
  13 +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
  14 +public class RtMarkBaseCO {
  15 + private String appKey;
  16 + private String version;
  17 + private String timestamp;
  18 + private String nonceStr;
  19 + private String sign;
  20 +
  21 + public String getAppKey() {
  22 + return appKey;
  23 + }
  24 +
  25 + public void setAppKey(String appKey) {
  26 + this.appKey = appKey;
  27 + }
  28 +
  29 + public String getVersion() {
  30 + return version;
  31 + }
  32 +
  33 + public void setVersion(String version) {
  34 + this.version = version;
  35 + }
  36 +
  37 + public String getTimestamp() {
  38 + return timestamp;
  39 + }
  40 +
  41 + public void setTimestamp(String timestamp) {
  42 + this.timestamp = timestamp;
  43 + }
  44 +
  45 + public String getNonceStr() {
  46 + return nonceStr;
  47 + }
  48 +
  49 + public void setNonceStr(String nonceStr) {
  50 + this.nonceStr = nonceStr;
  51 + }
  52 +
  53 + public String getSign() {
  54 + return sign;
  55 + }
  56 +
  57 + public void setSign(String sign) {
  58 + this.sign = sign;
  59 + }
  60 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/domain/RtMarkMessage.java renamed to cashier-mall/src/main/java/com/diligrp/cashier/mall/domain/rtmall/RtMarkMessage.java
1   -package com.diligrp.cashier.mall.domain;
  1 +package com.diligrp.cashier.mall.domain.rtmall;
2 2  
3 3 import com.diligrp.cashier.mall.MallConstants;
4 4 import com.diligrp.cashier.mall.type.RtMarkErrorCode;
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/domain/rtmall/co/AuthLoginCO.java 0 → 100644
  1 +package com.diligrp.cashier.mall.domain.rtmall.co;
  2 +
  3 +import jakarta.validation.constraints.NotBlank;
  4 +import jakarta.validation.constraints.NotNull;
  5 +
  6 +/**
  7 + * @ClassName AuthLoginCO.java
  8 + * @author dengwei
  9 + * @version 1.0.0
  10 + * @Description AuthLoginCO
  11 + * @date 2025-12-25 10:55
  12 + */
  13 +public class AuthLoginCO {
  14 + /**
  15 + * 用户编号
  16 + */
  17 + @NotBlank(message = "用户编号不能为空!")
  18 + private String userCode;
  19 +
  20 + /**
  21 + * 用户名
  22 + */
  23 + @NotBlank(message = "用户名称不能为空!")
  24 + private String username;
  25 +
  26 + /**
  27 + * 渠道-中瑞 地利
  28 + * @see com.diligrp.cashier.mall.type.OrderChannel
  29 + */
  30 + @NotBlank(message = "渠道不能为空!")
  31 + private String channel;
  32 +
  33 + /**
  34 + * 商户编号
  35 + */
  36 + @NotBlank(message = "商户编号不能为空!")
  37 + private String mchId;
  38 +
  39 + /**
  40 + * 订单来源(1大润发 2山姆)
  41 + * @see com.diligrp.cashier.mall.type.OrderSource
  42 + */
  43 + @NotNull(message = "订单来源不能为空!")
  44 + private Integer source;
  45 +
  46 + /**
  47 + * 订单类型-1线下扫码购,2 接口扫码购,3小程序
  48 + * @see com.diligrp.cashier.mall.type.OrderType
  49 + */
  50 + @NotNull(message = "订单类型不能为空!")
  51 + private Integer orderType;
  52 +
  53 + /**
  54 + * 联系电话
  55 + */
  56 + private String mobile;
  57 +
  58 + /**
  59 + * 经度
  60 + */
  61 + private String longitude;
  62 +
  63 + /**
  64 + * 纬度
  65 + */
  66 + private String latitude;
  67 +
  68 + public String getLatitude() {
  69 + return latitude;
  70 + }
  71 +
  72 + public void setLatitude(String latitude) {
  73 + this.latitude = latitude;
  74 + }
  75 +
  76 + public String getLongitude() {
  77 + return longitude;
  78 + }
  79 +
  80 + public void setLongitude(String longitude) {
  81 + this.longitude = longitude;
  82 + }
  83 +
  84 + public Integer getOrderType() {
  85 + return orderType;
  86 + }
  87 +
  88 + public void setOrderType(Integer orderType) {
  89 + this.orderType = orderType;
  90 + }
  91 +
  92 + public String getMobile() {
  93 + return mobile;
  94 + }
  95 +
  96 + public void setMobile(String mobile) {
  97 + this.mobile = mobile;
  98 + }
  99 +
  100 + public Integer getSource() {
  101 + return source;
  102 + }
  103 +
  104 + public void setSource(Integer source) {
  105 + this.source = source;
  106 + }
  107 +
  108 + public String getMchId() {
  109 + return mchId;
  110 + }
  111 +
  112 + public void setMchId(String mchId) {
  113 + this.mchId = mchId;
  114 + }
  115 +
  116 + public String getChannel() {
  117 + return channel;
  118 + }
  119 +
  120 + public void setChannel(String channel) {
  121 + this.channel = channel;
  122 + }
  123 +
  124 + public String getUserCode() {
  125 + return userCode;
  126 + }
  127 +
  128 + public void setUserCode(String userCode) {
  129 + this.userCode = userCode;
  130 + }
  131 +
  132 + public String getUsername() {
  133 + return username;
  134 + }
  135 +
  136 + public void setUsername(String username) {
  137 + this.username = username;
  138 + }
  139 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/domain/rtmall/co/UserInfoCO.java 0 → 100644
  1 +package com.diligrp.cashier.mall.domain.rtmall.co;
  2 +
  3 +import com.diligrp.cashier.mall.domain.rtmall.RtMarkBaseCO;
  4 +import com.diligrp.cashier.shared.jackson.deserializer.SecondToDateDeserializer;
  5 +import com.diligrp.cashier.shared.jackson.serialization.DateToSecondSerializer;
  6 +import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
  7 +import com.fasterxml.jackson.databind.annotation.JsonSerialize;
  8 +import jakarta.validation.constraints.NotBlank;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +/**
  13 + * @ClassName UserInfoCO.java
  14 + * @author dengwei
  15 + * @version 1.0.0
  16 + * @Description UserInfoCO
  17 + * @date 2025-12-25 16:28
  18 + */
  19 +public class UserInfoCO extends RtMarkBaseCO {
  20 + @NotBlank(message = "token不能为空!")
  21 + private String token;
  22 +
  23 + @JsonSerialize(using = DateToSecondSerializer.class)
  24 + @JsonDeserialize(using = SecondToDateDeserializer.class)
  25 + private LocalDateTime createTime;
  26 +
  27 + public String getToken() {
  28 + return token;
  29 + }
  30 +
  31 + public void setToken(String token) {
  32 + this.token = token;
  33 + }
  34 +
  35 + public LocalDateTime getCreateTime() {
  36 + return createTime;
  37 + }
  38 +
  39 + public void setCreateTime(LocalDateTime createTime) {
  40 + this.createTime = createTime;
  41 + }
  42 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/domain/rtmall/vo/UserInfoVO.java 0 → 100644
  1 +package com.diligrp.cashier.mall.domain.rtmall.vo;
  2 +
  3 +import com.fasterxml.jackson.databind.PropertyNamingStrategies;
  4 +import com.fasterxml.jackson.databind.annotation.JsonNaming;
  5 +
  6 +/**
  7 + * @ClassName UserInfoVO.java
  8 + * @author dengwei
  9 + * @version 1.0.0
  10 + * @Description UserInfoVO
  11 + * @date 2025-12-25 16:31
  12 + */
  13 +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
  14 +public class UserInfoVO {
  15 + private String userCode;
  16 + private String mobile;
  17 + private String userName;
  18 + private String companyCode;
  19 +
  20 + public String getUserCode() {
  21 + return userCode;
  22 + }
  23 +
  24 + public void setUserCode(String userCode) {
  25 + this.userCode = userCode;
  26 + }
  27 +
  28 + public String getMobile() {
  29 + return mobile;
  30 + }
  31 +
  32 + public void setMobile(String mobile) {
  33 + this.mobile = mobile;
  34 + }
  35 +
  36 + public String getUserName() {
  37 + return userName;
  38 + }
  39 +
  40 + public void setUserName(String userName) {
  41 + this.userName = userName;
  42 + }
  43 +
  44 + public String getCompanyCode() {
  45 + return companyCode;
  46 + }
  47 +
  48 + public void setCompanyCode(String companyCode) {
  49 + this.companyCode = companyCode;
  50 + }
  51 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/exception/MallExceptionHandler.java
1 1 package com.diligrp.cashier.mall.exception;
2 2  
3   -import com.diligrp.cashier.mall.domain.RtMarkMessage;
  3 +import com.diligrp.cashier.mall.domain.rtmall.RtMarkMessage;
4 4 import org.slf4j.Logger;
5 5 import org.slf4j.LoggerFactory;
6 6 import org.springframework.core.annotation.Order;
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/property/MallDynamicProperty.java renamed to cashier-mall/src/main/java/com/diligrp/cashier/mall/property/RtMallDynamicProperty.java
... ... @@ -17,8 +17,8 @@ import java.util.Optional;
17 17 */
18 18 @Configuration
19 19 @RefreshScope
20   -@ConfigurationProperties(prefix = "sign")
21   -public class MallDynamicProperty {
  20 +@ConfigurationProperties(prefix = "rtmall.sign")
  21 +public class RtMallDynamicProperty {
22 22  
23 23 private List<AppSecretDynamicProperty> appSecrets;
24 24  
... ... @@ -26,12 +26,18 @@ public class MallDynamicProperty {
26 26 @Value("${source:1}")
27 27 private Integer source;
28 28  
  29 + @Value("${orderType:3}")
  30 + private Integer orderType;
  31 +
29 32 @Value("${appKey:}")
30 33 private String appKey;
31 34  
32 35 @Value("${appSecret:}")
33 36 private String appSecret;
34 37  
  38 + @Value("${companyCode:}")
  39 + private String companyCode;
  40 +
35 41 @Value("${authUrl:https://hourh5-em-shop.feiniugo.com}")
36 42 private String authUrl;
37 43  
... ... @@ -66,6 +72,22 @@ public class MallDynamicProperty {
66 72 public void setAuthUrl(String authUrl) {
67 73 this.authUrl = authUrl;
68 74 }
  75 +
  76 + public Integer getOrderType() {
  77 + return orderType;
  78 + }
  79 +
  80 + public void setOrderType(Integer orderType) {
  81 + this.orderType = orderType;
  82 + }
  83 +
  84 + public String getCompanyCode() {
  85 + return companyCode;
  86 + }
  87 +
  88 + public void setCompanyCode(String companyCode) {
  89 + this.companyCode = companyCode;
  90 + }
69 91 }
70 92  
71 93 public List<AppSecretDynamicProperty> getAppSecrets() {
... ... @@ -88,4 +110,30 @@ public class MallDynamicProperty {
88 110 .findFirst()
89 111 .orElse(null);
90 112 }
  113 +
  114 + /**
  115 + * getBySource
  116 + *
  117 + */
  118 + public AppSecretDynamicProperty getBySourceAndType(Integer source, Integer orderType) {
  119 + return Optional.ofNullable(appSecrets)
  120 + .orElse(Collections.emptyList())
  121 + .stream()
  122 + .filter(item -> item.getSource().equals(source) && item.getOrderType().equals(orderType))
  123 + .findFirst()
  124 + .orElse(null);
  125 + }
  126 +
  127 + /**
  128 + * getBySource
  129 + *
  130 + */
  131 + public AppSecretDynamicProperty getByAppKey(String appKey) {
  132 + return Optional.ofNullable(appSecrets)
  133 + .orElse(Collections.emptyList())
  134 + .stream()
  135 + .filter(item -> item.getAppKey().equals(appKey))
  136 + .findFirst()
  137 + .orElse(null);
  138 + }
91 139 }
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/service/paychannel/AbstractPayChannel.java 0 → 100644
  1 +package com.diligrp.cashier.mall.service.paychannel;
  2 +
  3 +import org.slf4j.Logger;
  4 +import org.slf4j.LoggerFactory;
  5 +
  6 +/**
  7 + * @ClassName AbstractPayChannel.java
  8 + * @author dengwei
  9 + * @version 1.0.0
  10 + * @Description AbstractPayChannel
  11 + * @date 2025-12-25 17:07
  12 + */
  13 +public abstract class AbstractPayChannel {
  14 + protected final Logger log = LoggerFactory.getLogger(getClass());
  15 +
  16 + public abstract Integer getPayChannel();
  17 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/service/paychannel/CardPayChannel.java 0 → 100644
  1 +package com.diligrp.cashier.mall.service.paychannel;
  2 +
  3 +import org.springframework.stereotype.Component;
  4 +
  5 +/**
  6 + * @ClassName CardPayChannel.java
  7 + * @author dengwei
  8 + * @version 1.0.0
  9 + * @Description CardPayChannel
  10 + * @date 2025-12-25 17:08
  11 + */
  12 +@Component
  13 +public class CardPayChannel extends AbstractPayChannel {
  14 + @Override
  15 + public Integer getPayChannel() {
  16 + return 0;
  17 + }
  18 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/service/paychannel/WeChatPayChannel.java 0 → 100644
  1 +package com.diligrp.cashier.mall.service.paychannel;
  2 +
  3 +import org.springframework.stereotype.Component;
  4 +
  5 +/**
  6 + * @ClassName WeChatPayChannel.java
  7 + * @author dengwei
  8 + * @version 1.0.0
  9 + * @Description WeChatPayChannel
  10 + * @date 2025-12-25 17:08
  11 + */
  12 +@Component
  13 +public class WeChatPayChannel extends AbstractPayChannel {
  14 + @Override
  15 + public Integer getPayChannel() {
  16 + return 1;
  17 + }
  18 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/service/sourcechannel/AbstractChannel.java 0 → 100644
  1 +package com.diligrp.cashier.mall.service.sourcechannel;
  2 +
  3 +import cn.hutool.core.util.IdUtil;
  4 +import com.diligrp.cashier.mall.MallConstants;
  5 +import com.diligrp.cashier.mall.domain.rtmall.co.AuthLoginCO;
  6 +import com.diligrp.cashier.mall.domain.rtmall.vo.UserInfoVO;
  7 +import com.diligrp.cashier.mall.property.RtMallDynamicProperty;
  8 +import com.diligrp.cashier.shared.util.DateUtils;
  9 +import com.diligrp.cashier.shared.util.JsonUtils;
  10 +import jakarta.annotation.Resource;
  11 +import org.slf4j.Logger;
  12 +import org.slf4j.LoggerFactory;
  13 +import org.springframework.data.redis.core.RedisTemplate;
  14 +
  15 +import java.util.concurrent.TimeUnit;
  16 +
  17 +/**
  18 + * @ClassName AbstractChannel.java
  19 + * @author dengwei
  20 + * @version 1.0.0
  21 + * @Description AbstractChannel
  22 + * @date 2025-12-25 11:19
  23 + */
  24 +public abstract class AbstractChannel {
  25 + private static final Logger log = LoggerFactory.getLogger(AbstractChannel.class);
  26 +
  27 + @Resource
  28 + protected RtMallDynamicProperty rtMallDynamicProperty;
  29 + @Resource
  30 + protected RedisTemplate<String, Object> redisTemplate;
  31 +
  32 + /**
  33 + * authLogin
  34 + */
  35 + public String authLogin(AuthLoginCO authLogin) {
  36 + String token = IdUtil.fastSimpleUUID();
  37 + long timestamp = DateUtils.timestampInSeconds();
  38 +
  39 + String authUrl = authUrl(authLogin, token, timestamp);
  40 +
  41 + // token 缓存
  42 + String tokenKey = MallConstants.MALL_TOKEN + token;
  43 + redisTemplate.opsForValue().set(tokenKey, JsonUtils.toJsonString(authLogin), 1, TimeUnit.DAYS);
  44 +
  45 + // 用户信息缓存 中瑞、dili是独立系统部署 可能存在相同用户id 所以需要加上渠道
  46 + String userKey = MallConstants.MALL_USER_INFO + authLogin.getUserCode() + "_" + authLogin.getChannel();
  47 + redisTemplate.opsForValue().set(userKey, JsonUtils.toJsonString(authLogin), 1, TimeUnit.DAYS);
  48 + return authUrl;
  49 + }
  50 +
  51 + /**
  52 + * userInfo
  53 + *
  54 + */
  55 + public UserInfoVO userInfo(AuthLoginCO authLogin) {
  56 + RtMallDynamicProperty.AppSecretDynamicProperty property = rtMallDynamicProperty.getBySourceAndType(authLogin.getSource(), authLogin.getOrderType());
  57 + log.info("authUrl property: {}", JsonUtils.toJsonString(property));
  58 +
  59 + UserInfoVO userInfoVO = JsonUtils.convertValue(authLogin, UserInfoVO.class);
  60 + userInfoVO.setUserCode(authLogin.getUserCode() + "_" + authLogin.getChannel());
  61 + userInfoVO.setUserName(authLogin.getUsername());
  62 +
  63 + userInfoChannel(userInfoVO, property);
  64 + return userInfoVO;
  65 + }
  66 +
  67 + /**
  68 + * 渠道
  69 + */
  70 + public abstract String channel();
  71 +
  72 + /**
  73 + * authLoginUrl
  74 + */
  75 + public abstract String authUrl(AuthLoginCO authLogin, String token, long timestamp);
  76 +
  77 + public abstract void userInfoChannel(UserInfoVO authLogin, RtMallDynamicProperty.AppSecretDynamicProperty userInfoVO);
  78 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/service/sourcechannel/RtMallChannel.java 0 → 100644
  1 +package com.diligrp.cashier.mall.service.sourcechannel;
  2 +
  3 +import com.diligrp.cashier.mall.domain.rtmall.co.AuthLoginCO;
  4 +import com.diligrp.cashier.mall.domain.rtmall.vo.UserInfoVO;
  5 +import com.diligrp.cashier.mall.property.RtMallDynamicProperty;
  6 +import com.diligrp.cashier.mall.type.OrderChannel;
  7 +import com.diligrp.cashier.mall.util.RtMallSignMd5Utils;
  8 +import com.diligrp.cashier.shared.util.JsonUtils;
  9 +import com.diligrp.cashier.shared.util.UrlParamParserUtils;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +import org.springframework.stereotype.Component;
  13 +
  14 +import java.util.Map;
  15 +
  16 +/**
  17 + * @ClassName RtMallChannel.java
  18 + * @author dengwei
  19 + * @version 1.0.0
  20 + * @Description RtMallChannel
  21 + * @date 2025-12-25 11:20
  22 + */
  23 +@Component
  24 +public class RtMallChannel extends AbstractChannel {
  25 + private static final Logger log = LoggerFactory.getLogger(RtMallChannel.class);
  26 +
  27 + @Override
  28 + public String authUrl(AuthLoginCO authLogin, String token, long timestamp) {
  29 + RtMallDynamicProperty.AppSecretDynamicProperty property = rtMallDynamicProperty.getBySourceAndType(authLogin.getSource(), authLogin.getOrderType());
  30 + log.info("authUrl property: {}", JsonUtils.toJsonString(property));
  31 +
  32 + // 替换参数
  33 + String authUrl = property.getAuthUrl();
  34 + authUrl = String.format(authUrl, token, timestamp, authLogin.getLongitude(), authLogin.getLatitude());
  35 + log.info("authUrl format1: {}", authUrl);
  36 +
  37 + // 获取参数
  38 + Map<String, String> queryParams = UrlParamParserUtils.parseQueryParams(authUrl);
  39 + log.info("authUrl queryParams: {}", JsonUtils.toJsonString(queryParams));
  40 +
  41 + // 生成签名
  42 + String sign = RtMallSignMd5Utils.generateSign(queryParams, property.getAppSecret());
  43 + log.info("authUrl sign: {}", sign);
  44 + authUrl = authUrl.concat("&sign=").concat(sign);
  45 + return authUrl;
  46 + }
  47 +
  48 + /**
  49 + * userInfo
  50 + */
  51 + @Override
  52 + public void userInfoChannel(final UserInfoVO authLogin,
  53 + final RtMallDynamicProperty.AppSecretDynamicProperty property) {
  54 + authLogin.setCompanyCode(property.getCompanyCode());
  55 + }
  56 +
  57 + @Override
  58 + public String channel() {
  59 + return OrderChannel.RT_MART.getCode();
  60 + }
  61 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/sign/RtMallSign.java
1 1 package com.diligrp.cashier.mall.sign;
2 2  
3   -import com.diligrp.cashier.mall.MallConstants;
4 3 import com.diligrp.cashier.mall.exception.RtMartMallException;
5   -import com.diligrp.cashier.mall.property.MallDynamicProperty;
  4 +import com.diligrp.cashier.mall.property.RtMallDynamicProperty;
6 5 import com.diligrp.cashier.mall.type.RtMarkErrorCode;
7 6 import com.diligrp.cashier.mall.util.RtMallSignMd5Utils;
8 7 import com.diligrp.cashier.shared.handler.sign.SecuritySign;
... ... @@ -31,7 +30,7 @@ public class RtMallSign implements SecuritySign {
31 30 private static final Logger log = LoggerFactory.getLogger(RtMallSign.class);
32 31  
33 32 @Resource
34   - private MallDynamicProperty mallDynamicProperty;
  33 + private RtMallDynamicProperty mallDynamicProperty;
35 34  
36 35 @Override
37 36 public void sign(HttpServletRequest request, HttpServletResponse response, Object data) {
... ... @@ -45,15 +44,21 @@ public class RtMallSign implements SecuritySign {
45 44 throw new RtMartMallException(RtMarkErrorCode.E4003.getCode(), RtMarkErrorCode.E4003.getMessage());
46 45 }
47 46  
48   - MallDynamicProperty.AppSecretDynamicProperty property = mallDynamicProperty.getBySource(MallConstants.RT_MALL_SOURCE);
  47 + Object sign = paramMap.get("sign");
  48 + if (Objects.isNull(sign)) {
  49 + throw new RtMartMallException(RtMarkErrorCode.E4004.getCode(), RtMarkErrorCode.E4004.getMessage());
  50 + }
  51 + paramMap.remove("sign");
  52 +
  53 + RtMallDynamicProperty.AppSecretDynamicProperty property = mallDynamicProperty.getByAppKey(appKey.toString());
49 54 if (Objects.isNull(property)) {
50   - throw new RtMartMallException(RtMarkErrorCode.E4001.getCode(), RtMarkErrorCode.E4001.getMessage());
  55 + throw new RtMartMallException(RtMarkErrorCode.E4003.getCode(), RtMarkErrorCode.E4003.getMessage());
51 56 }
52 57  
53 58 try {
54 59 log.info("appKey:{}, secretKey:{}", property.getAppKey(), property.getAppSecret());
55 60 String signKey = RtMallSignMd5Utils.generateSign(paramMap, property.getAppSecret());
56   - Assert.isTrue(Objects.equals(signKey, paramMap.get("sign").toString()), "验签失败!");
  61 + Assert.isTrue(Objects.equals(signKey, sign.toString()), "验签失败!");
57 62 } catch (Exception e) {
58 63 throw new RtMartMallException(RtMarkErrorCode.E4004.getCode(), RtMarkErrorCode.E4004.getMessage());
59 64 }
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/type/OrderChannel.java 0 → 100644
  1 +package com.diligrp.cashier.mall.type;
  2 +
  3 +/**
  4 + * @ClassName OrderChannel.java
  5 + * @author dengwei
  6 + * @version 1.0.0
  7 + * @Description OrderChannel
  8 + * @date 2025-12-25 11:18
  9 + */
  10 +public enum OrderChannel {
  11 + RT_MART("1", "大润发"),
  12 + SAM("2", "山姆");
  13 +
  14 + public final String code;
  15 + public final String description;
  16 +
  17 + OrderChannel(String code, String description) {
  18 + this.code = code;
  19 + this.description = description;
  20 + }
  21 +
  22 + public String getCode() {
  23 + return code;
  24 + }
  25 +
  26 + public String getDescription() {
  27 + return description;
  28 + }
  29 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/type/OrderSource.java 0 → 100644
  1 +package com.diligrp.cashier.mall.type;
  2 +
  3 +/**
  4 + * @ClassName OrderSrouce.java
  5 + * @author dengwei
  6 + * @version 1.0.0
  7 + * @Description OrderSource
  8 + * @date 2025-12-25 15:11
  9 + */
  10 +public enum OrderSource {
  11 + RT_MART(1, "大润发"),
  12 + SAM_CLUB(2, "山姆");
  13 +
  14 + public final int code;
  15 + public final String name;
  16 +
  17 + OrderSource(int code, String name) {
  18 + this.code = code;
  19 + this.name = name;
  20 + }
  21 +
  22 + public int getCode() {
  23 + return code;
  24 + }
  25 +
  26 + public String getName() {
  27 + return name;
  28 + }
  29 +}
... ...
cashier-mall/src/main/java/com/diligrp/cashier/mall/type/OrderType.java 0 → 100644
  1 +package com.diligrp.cashier.mall.type;
  2 +
  3 +/**
  4 + * @ClassName OrderType.java
  5 + * @author dengwei
  6 + * @version 1.0.0
  7 + * @Description OrderType
  8 + * @date 2025-12-25 11:17
  9 + */
  10 +public enum OrderType {
  11 + OFFLINE_SCAN(1, "线下扫码购"),
  12 + API_SCAN(2, "接口扫码购"),
  13 + MINI_PROGRAM(3, "小程序");
  14 +
  15 + private final int code;
  16 + private final String description;
  17 +
  18 + OrderType(int code, String description) {
  19 + this.code = code;
  20 + this.description = description;
  21 + }
  22 +
  23 + public int getCode() {
  24 + return code;
  25 + }
  26 +
  27 + public String getDescription() {
  28 + return description;
  29 + }
  30 +}
... ...
cashier-shared/build.gradle
... ... @@ -7,8 +7,10 @@ dependencies {
7 7 api 'org.springframework.cloud:spring-cloud-starter-loadbalancer'
8 8 api 'org.springframework.cloud:spring-cloud-starter-openfeign'
9 9 api 'org.springframework.boot:spring-boot-starter-aop'
  10 + api 'org.springframework.boot:spring-boot-starter-validation'
10 11 api libs.cache.caffeine
11 12 api libs.common.collections4
  13 + api libs.google.guava
12 14  
13 15 runtimeOnly libs.mysql.driver
14 16 }
... ...
cashier-shared/src/main/java/com/diligrp/cashier/shared/ErrorCode.java
... ... @@ -16,6 +16,8 @@ public class ErrorCode {
16 16 public static final String OBJECT_NOT_FOUND = "300004";
17 17 // 对象已存在
18 18 public static final String OBJECT_ALREADY_EXISTS = "300005";
  19 + // 请求方式
  20 + public static final String METHOD_NOT_SUPPORTED_ERROR = "300006";
19 21 // 数据并发修改
20 22 public static final String SYSTEM_BUSY_ERROR = "301000";
21 23 // 无效对象状态
... ...
cashier-shared/src/main/java/com/diligrp/cashier/shared/exception/DefaultExceptionHandler.java
... ... @@ -2,11 +2,24 @@ package com.diligrp.cashier.shared.exception;
2 2  
3 3 import com.diligrp.cashier.shared.ErrorCode;
4 4 import com.diligrp.cashier.shared.domain.Message;
  5 +import jakarta.validation.ConstraintViolation;
  6 +import jakarta.validation.ConstraintViolationException;
5 7 import org.slf4j.Logger;
6 8 import org.slf4j.LoggerFactory;
  9 +import org.springframework.context.support.DefaultMessageSourceResolvable;
  10 +import org.springframework.validation.BindException;
  11 +import org.springframework.validation.BindingResult;
  12 +import org.springframework.validation.ObjectError;
  13 +import org.springframework.web.HttpRequestMethodNotSupportedException;
  14 +import org.springframework.web.bind.MethodArgumentNotValidException;
  15 +import org.springframework.web.bind.MissingServletRequestParameterException;
7 16 import org.springframework.web.bind.annotation.ExceptionHandler;
8 17 import org.springframework.web.bind.annotation.RestControllerAdvice;
9 18  
  19 +import java.util.List;
  20 +import java.util.Set;
  21 +import java.util.stream.Collectors;
  22 +
10 23 @RestControllerAdvice
11 24 public class DefaultExceptionHandler {
12 25 private final Logger LOG = LoggerFactory.getLogger(this.getClass());
... ... @@ -23,6 +36,40 @@ public class DefaultExceptionHandler {
23 36 return Message.failure(ErrorCode.ILLEGAL_ARGUMENT_ERROR, ex.getMessage());
24 37 }
25 38  
  39 + @ExceptionHandler(value = ConstraintViolationException.class)
  40 + public Message<?> constraintViolationException(ConstraintViolationException exs) {
  41 + Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
  42 + String msg = violations.stream().map(ConstraintViolation::getMessage)
  43 + .collect(Collectors.joining(","));
  44 + return Message.failure(ErrorCode.ILLEGAL_ARGUMENT_ERROR, msg);
  45 + }
  46 +
  47 + @ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class})
  48 + public Message<?> methodArgumentNotValidException(Exception e) {
  49 + BindingResult bindingResult;
  50 + if (e instanceof MethodArgumentNotValidException) {
  51 + bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
  52 + } else {
  53 + bindingResult = ((BindException) e).getBindingResult();
  54 + }
  55 + List<ObjectError> allErrors = bindingResult.getAllErrors();
  56 + String msg = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
  57 + .collect(Collectors.joining("、"));
  58 +
  59 + return Message.failure(ErrorCode.ILLEGAL_ARGUMENT_ERROR, msg);
  60 + }
  61 +
  62 + @ExceptionHandler(MissingServletRequestParameterException.class)
  63 + public Message<?> missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) {
  64 + return Message.failure(ErrorCode.ILLEGAL_ARGUMENT_ERROR, String.format("缺少参数:%s", ex.getParameterName()));
  65 + }
  66 +
  67 + @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
  68 + public Message<?> handleRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
  69 + return Message.failure(ErrorCode.METHOD_NOT_SUPPORTED_ERROR, "请求方式错误!");
  70 + }
  71 +
  72 +
26 73 @ExceptionHandler(Exception.class)
27 74 public Message<?> defaultExceptionHandler(Exception ex) {
28 75 LOG.warn("assistant platform service exception", ex);
... ...
cashier-shared/src/main/java/com/diligrp/cashier/shared/exception/PlatformServiceException.java
... ... @@ -30,10 +30,10 @@ public class PlatformServiceException extends RuntimeException {
30 30 super(message, ex);
31 31 }
32 32  
33   - @Override
34   - public Throwable fillInStackTrace() {
35   - return stackTrace ? super.fillInStackTrace() : this;
36   - }
  33 +// @Override
  34 +// public Throwable fillInStackTrace() {
  35 +// return stackTrace ? super.fillInStackTrace() : this;
  36 +// }
37 37  
38 38 public String getCode() {
39 39 return code;
... ...
cashier-shared/src/main/java/com/diligrp/cashier/shared/jackson/deserializer/SecondToDateDeserializer.java 0 → 100644
  1 +package com.diligrp.cashier.shared.jackson.deserializer;
  2 +
  3 +import com.fasterxml.jackson.core.JacksonException;
  4 +import com.fasterxml.jackson.core.JsonParser;
  5 +import com.fasterxml.jackson.databind.DeserializationContext;
  6 +import com.fasterxml.jackson.databind.JsonDeserializer;
  7 +
  8 +import java.io.IOException;
  9 +import java.time.Instant;
  10 +import java.time.LocalDateTime;
  11 +import java.time.ZoneOffset;
  12 +
  13 +/**
  14 + * @ClassName SecondToDateDeserializer.java
  15 + * @author dengwei
  16 + * @version 1.0.0
  17 + * @Description SecondToDateDeserializer
  18 + * @date 2025-12-25 17:54
  19 + */
  20 +public class SecondToDateDeserializer extends JsonDeserializer<LocalDateTime> {
  21 + @Override
  22 + public LocalDateTime deserialize(JsonParser parser, DeserializationContext deserializationContext) throws IOException, JacksonException {
  23 + long timestamp = parser.getValueAsLong();
  24 + return LocalDateTime.ofInstant(
  25 + Instant.ofEpochSecond(timestamp),
  26 + ZoneOffset.UTC
  27 + );
  28 + }
  29 +}
... ...
cashier-shared/src/main/java/com/diligrp/cashier/shared/jackson/serialization/DateToSecondSerializer.java 0 → 100644
  1 +package com.diligrp.cashier.shared.jackson.serialization;
  2 +
  3 +import com.fasterxml.jackson.core.JsonGenerator;
  4 +import com.fasterxml.jackson.databind.JsonSerializer;
  5 +import com.fasterxml.jackson.databind.SerializerProvider;
  6 +
  7 +import java.io.IOException;
  8 +import java.time.LocalDateTime;
  9 +import java.time.ZoneOffset;
  10 +
  11 +/**
  12 + * @ClassName DateToSecondSerializer.java
  13 + * @author dengwei
  14 + * @version 1.0.0
  15 + * @Description DateToSecondSerializer
  16 + * @date 2025-12-25 17:57
  17 + */
  18 +public class DateToSecondSerializer extends JsonSerializer<LocalDateTime> {
  19 + @Override
  20 + public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
  21 + gen.writeNumber(value.toInstant(ZoneOffset.UTC).getEpochSecond());
  22 + }
  23 +}
... ...
cashier-shared/src/main/java/com/diligrp/cashier/shared/util/DateUtils.java
... ... @@ -3,6 +3,7 @@ package com.diligrp.cashier.shared.util;
3 3 import com.diligrp.cashier.shared.Constants;
4 4  
5 5 import java.text.SimpleDateFormat;
  6 +import java.time.Instant;
6 7 import java.time.LocalDate;
7 8 import java.time.LocalDateTime;
8 9 import java.time.ZoneOffset;
... ... @@ -51,11 +52,11 @@ public class DateUtils {
51 52 public static String format(Date date) {
52 53 return format(date, Constants.DATE_TIME_FORMAT);
53 54 }
54   -
  55 +
55 56 public static LocalDateTime addDays(long amount) {
56   - LocalDateTime localDateTime = LocalDateTime.now();
57   - localDateTime.plusDays(amount);
58   - return localDateTime;
  57 + LocalDateTime localDateTime = LocalDateTime.now();
  58 + localDateTime.plusDays(amount);
  59 + return localDateTime;
59 60 }
60 61  
61 62 public static String format(Date date, String format) {
... ... @@ -112,17 +113,25 @@ public class DateUtils {
112 113 }
113 114  
114 115 /**
115   - * 获取时间戳
116   - */
117   - public static long parseMilliSecond(LocalDateTime localDateTime){
118   - return parseMilliSecond(localDateTime,null);
  116 + * 获取时间戳
  117 + */
  118 + public static long parseMilliSecond(LocalDateTime localDateTime) {
  119 + return parseMilliSecond(localDateTime, null);
119 120 }
120 121  
121   - public static long parseMilliSecond(LocalDateTime localDateTime, String zoneNumStr){
  122 + public static long parseMilliSecond(LocalDateTime localDateTime, String zoneNumStr) {
122 123 //默认东八区
123   - if (ObjectUtils.isEmpty(zoneNumStr)){
  124 + if (ObjectUtils.isEmpty(zoneNumStr)) {
124 125 zoneNumStr = "+8";
125 126 }
126 127 return localDateTime.toInstant(ZoneOffset.of(zoneNumStr)).toEpochMilli();
127 128 }
  129 +
  130 + /**
  131 + * 时间戳-秒
  132 + *
  133 + */
  134 + public static long timestampInSeconds() {
  135 + return Instant.now().getEpochSecond();
  136 + }
128 137 }
... ...
cashier-shared/src/main/java/com/diligrp/cashier/shared/util/UrlParamParserUtils.java 0 → 100644
  1 +package com.diligrp.cashier.shared.util;
  2 +
  3 +import com.diligrp.cashier.shared.exception.PlatformServiceException;
  4 +
  5 +import java.net.URI;
  6 +import java.net.URLDecoder;
  7 +import java.nio.charset.StandardCharsets;
  8 +import java.util.HashMap;
  9 +import java.util.Map;
  10 +
  11 +/**
  12 + * @ClassName UrlParamParserUtils.java
  13 + * @author dengwei
  14 + * @version 1.0.0
  15 + * @Description UrlParamParserUtils
  16 + * @date 2025-12-25 15:34
  17 + */
  18 +public class UrlParamParserUtils {
  19 + public static Map<String, String> parseQueryParams(String url) {
  20 + try {
  21 + URI uri = new URI(url);
  22 + String query = uri.getQuery();
  23 + if (query == null || query.isEmpty()) {
  24 + return new HashMap<>();
  25 + }
  26 +
  27 + Map<String, String> params = new HashMap<>();
  28 + String[] pairs = query.split("&");
  29 + for (String pair : pairs) {
  30 + String[] keyValue = pair.split("=", 2);
  31 + String key = URLDecoder.decode(keyValue[0], StandardCharsets.UTF_8);
  32 + String value = keyValue.length > 1 ? URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8) : "";
  33 + params.put(key, value);
  34 + }
  35 + return params;
  36 + } catch (Exception e) {
  37 + throw new PlatformServiceException("Invalid URL");
  38 + }
  39 + }
  40 +}
... ...
gradle/libs.versions.toml
... ... @@ -9,6 +9,8 @@ mysqlDriverVersion = &quot;8.0.33&quot;
9 9 redissonVersion = "3.52.0"
10 10 caffeineVersion = "3.2.3"
11 11 collections4Version= "4.5.0"
  12 +guavaVersion = "33.5.0-jre"
  13 +
12 14  
13 15 [libraries]
14 16 #spring-boot-starter = {module = 'org.springframework.boot:spring-boot-starter', version.ref = 'springBootVersion'}
... ... @@ -20,6 +22,7 @@ mysql-driver = {module = &#39;mysql:mysql-connector-java&#39;, version.ref = &#39;mysqlDrive
20 22 redisson-starter = {module = 'org.redisson:redisson-spring-boot-starter', version.ref = 'redissonVersion'}
21 23 cache-caffeine = {module = 'com.github.ben-manes.caffeine:caffeine', version.ref = 'caffeineVersion'}
22 24 common-collections4 = {module = 'org.apache.commons:commons-collections4', version.ref = 'collections4Version'}
  25 +google-guava = {module = 'com.google.guava:guava', version.ref = 'guavaVersion'}
23 26  
24 27 [plugins]
25 28 spring-boot = {id = 'org.springframework.boot', version.ref = 'springBootVersion'}
... ...