Merge #88 into master from cc_20260401_private_sphere_qr
fix:私域二维码 * cc_20260401_private_sphere_qr: (3 commits squashed) - fix:新增防抖 - Merge branch 'refs/heads/cc_20260302_debounce' into cc_20260401_private_sphere_qr - fix:私域二维码 Signed-off-by: 王非凡 <accounts_67eba0c5fee9c49c80c8e2b4@mail.teambition.com> Merged-by: 正新 <accounts_6964c7bcd2a2c377c5bbd01b@mail.teambition.com> CR-link: https://codeup.aliyun.com/692ea314dec569489f6f167c/hangzhou/java/custom_zxjp/change/88
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package com.cool.store.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 防抖
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Debounce {
|
||||
/**
|
||||
* 防抖时间窗口(毫秒)
|
||||
*/
|
||||
long timeMs() default 1000;
|
||||
|
||||
/**
|
||||
* key 的 SpEL
|
||||
* 例:"'order:' + #req.orderId"
|
||||
*/
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* 是否根据用户区分
|
||||
*/
|
||||
boolean diffUser() default true;
|
||||
|
||||
/**
|
||||
* 是否为pc端,pc端使用CurrentUserHolder获取用户信息,否则使用PartnerUserHolder获取
|
||||
*/
|
||||
boolean pc() default true;
|
||||
|
||||
/**
|
||||
* true: 在窗口内触发则抛异常
|
||||
* false: 在窗口内触发则直接返回上一次结果
|
||||
*/
|
||||
boolean throwOnDebounce() default true;
|
||||
}
|
||||
@@ -384,4 +384,24 @@ public class RedisConstant {
|
||||
* 钱包分账支付锁
|
||||
*/
|
||||
public static final String WALLET_ALLOCATION_PAY_LOCK_KEY = "wallet_allocation_pay_lock_key:{0}";
|
||||
|
||||
/**
|
||||
* 私域设备SN->绑定二维码地址
|
||||
*/
|
||||
public static final String PRIVATE_SN_QR_CODE = "private_sphere_qr";
|
||||
|
||||
/**
|
||||
* 私域设备SN->门店id
|
||||
*/
|
||||
public static final String PRIVATE_STORE_SN_BIND = "private_sphere_store_sn";
|
||||
|
||||
/**
|
||||
* 私域设备SN->门店私域二维码地址
|
||||
*/
|
||||
public static final String PRIVATE_STORE_SN_QR_CODE = "private_sphere_store_sn_qr_code";
|
||||
|
||||
/**
|
||||
* IP限制key
|
||||
*/
|
||||
public static final String IP_LIMIT = "ip_limit";
|
||||
}
|
||||
|
||||
@@ -452,6 +452,8 @@ public enum ErrorCodeEnum {
|
||||
WALLET_RE_PAY_FAIL(1830019, "重新支付异常,请联系管理员", null),
|
||||
WALLET_PAY_CANNOT_CANCEL(1830020, "该交易无法取消", null),
|
||||
TOTAL_FEES_NEED_EQUAL(1830021, "分账总金额需与合计缴费金额一致", null),
|
||||
|
||||
IP_LIMIT(1840000, "IP访问次数超限", null),
|
||||
;
|
||||
|
||||
|
||||
|
||||
@@ -247,4 +247,11 @@ public class StoreDao {
|
||||
}
|
||||
return storeMapper.batchUpdateAddress(storeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取私域二维码
|
||||
*/
|
||||
public String getPrivateSphereQrCode(String storeId){
|
||||
return storeMapper.getPrivateSphereQrCode(storeId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,4 +153,6 @@ public interface StoreMapper {
|
||||
List<StoreAddressDTO> getStoreAddress(@Param("flag") Integer flag, @Param("storeCode") String storeCode);
|
||||
|
||||
int batchUpdateAddress(@Param("list") List<StoreDO> storeList);
|
||||
|
||||
String getPrivateSphereQrCode(@Param("storeId") String storeId);
|
||||
}
|
||||
|
||||
@@ -421,4 +421,8 @@
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<select id="getPrivateSphereQrCode" resultType="java.lang.String">
|
||||
SELECT private_sphere_qr FROM store_extend_info_${enterpriseId}
|
||||
WHERE store_id = #{storeId}
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.cool.store.request.privatesphere;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 门店设备sn绑定
|
||||
* </p>
|
||||
*
|
||||
* @author wangff
|
||||
* @since 2026/4/2
|
||||
*/
|
||||
@Data
|
||||
public class PrivateSphereBindRequest {
|
||||
@ApiModelProperty("门店id")
|
||||
@NotBlank(message = "门店id不能为空")
|
||||
private String storeId;
|
||||
|
||||
@ApiModelProperty("设备sn")
|
||||
@NotBlank(message = "设备sn不能为空")
|
||||
private String sn;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.cool.store.request.privatesphere;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 设备sn
|
||||
* </p>
|
||||
*
|
||||
* @author wangff
|
||||
* @since 2026/4/2
|
||||
*/
|
||||
@Data
|
||||
public class PrivateSphereSnRequest {
|
||||
@ApiModelProperty("设备sn")
|
||||
@NotBlank(message = "设备sn不能为空")
|
||||
private String sn;
|
||||
|
||||
@ApiModelProperty(value = "客户端IP", hidden = true)
|
||||
private String clientIp;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.cool.store.vo.privatesphere;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 私域二维码VO
|
||||
* </p>
|
||||
*
|
||||
* @author wangff
|
||||
* @since 2026/4/2
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class PrivateSphereVO {
|
||||
@ApiModelProperty("状态,0正常,1未绑定sn,2门店未维护私域二维码,3IP限制")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty("二维码地址")
|
||||
private String qrCodeUrl;
|
||||
|
||||
public PrivateSphereVO(String url) {
|
||||
this.qrCodeUrl = url;
|
||||
this.status = 0;
|
||||
}
|
||||
|
||||
public static PrivateSphereVO unbind() {
|
||||
return new PrivateSphereVO(1, null);
|
||||
}
|
||||
|
||||
public static PrivateSphereVO noMaintain() {
|
||||
return new PrivateSphereVO(2, null);
|
||||
}
|
||||
|
||||
public static PrivateSphereVO ipLimit() {
|
||||
return new PrivateSphereVO(3, null);
|
||||
}
|
||||
}
|
||||
@@ -127,6 +127,11 @@
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>alibabacloud-dysmsapi20170525</artifactId>
|
||||
</dependency>
|
||||
<!-- 二维码生成 -->
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.cool.store.service.privatesphere;
|
||||
|
||||
import com.cool.store.request.privatesphere.PrivateSphereBindRequest;
|
||||
import com.cool.store.request.privatesphere.PrivateSphereSnRequest;
|
||||
import com.cool.store.vo.privatesphere.PrivateSphereVO;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 私域二维码服务
|
||||
* </p>
|
||||
*
|
||||
* @author wangff
|
||||
* @since 2026/4/1
|
||||
*/
|
||||
public interface PrivateSphereQrService {
|
||||
|
||||
/**
|
||||
* 生成二维码并上传到OSS
|
||||
* 相同sn优先从redis中获取二维码图片路径
|
||||
*
|
||||
* @param request 设备sn
|
||||
* @return 二维码图片的OSS路径
|
||||
*/
|
||||
PrivateSphereVO generateQrCode(PrivateSphereSnRequest request);
|
||||
|
||||
/**
|
||||
* 门店绑定设备sn
|
||||
* @param request 门店设备sn绑定
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean bindStoreSn(PrivateSphereBindRequest request);
|
||||
|
||||
/**
|
||||
* 根据设备sn获取门店私域二维码
|
||||
* @param request 设备sn
|
||||
* @return 私域二维码
|
||||
*/
|
||||
PrivateSphereVO getPrivateSphereQr(PrivateSphereSnRequest request);
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.cool.store.service.privatesphere.impl;
|
||||
|
||||
import cn.hutool.extra.qrcode.QrCodeUtil;
|
||||
import cn.hutool.extra.qrcode.QrConfig;
|
||||
import com.cool.store.constants.RedisConstant;
|
||||
import com.cool.store.dao.StoreDao;
|
||||
import com.cool.store.enums.ErrorCodeEnum;
|
||||
import com.cool.store.exception.ServiceException;
|
||||
import com.cool.store.oss.OssClientService;
|
||||
import com.cool.store.request.privatesphere.PrivateSphereBindRequest;
|
||||
import com.cool.store.request.privatesphere.PrivateSphereSnRequest;
|
||||
import com.cool.store.service.privatesphere.PrivateSphereQrService;
|
||||
import com.cool.store.utils.RedisUtilPool;
|
||||
import com.cool.store.utils.poi.StringUtils;
|
||||
import com.cool.store.vo.privatesphere.PrivateSphereVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 私域二维码服务实现
|
||||
* </p>
|
||||
*
|
||||
* @author wangff
|
||||
* @since 2026/4/1
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PrivateSphereQrServiceImpl implements PrivateSphereQrService {
|
||||
|
||||
private final RedisUtilPool redisUtilPool;
|
||||
private final OssClientService ossClientService;
|
||||
private final StoreDao storeDao;
|
||||
|
||||
/**
|
||||
* 二维码尺寸
|
||||
*/
|
||||
private static final int QR_CODE_SIZE = 1280;
|
||||
/**
|
||||
* sn缓存时间
|
||||
*/
|
||||
private static final int SN_CACHE_TIME = 86400;
|
||||
/**
|
||||
* IP限制时间(1小时)
|
||||
*/
|
||||
private static final int IP_LIMIT_TIME = 3600;
|
||||
/**
|
||||
* IP每小时最大生成次数
|
||||
*/
|
||||
private static final int IP_MAX_COUNT = 5;
|
||||
|
||||
@Override
|
||||
public PrivateSphereVO generateQrCode(PrivateSphereSnRequest request) {
|
||||
// 先从Redis中获取缓存
|
||||
String cachedUrl = redisUtilPool.hashGet(RedisConstant.PRIVATE_SN_QR_CODE, request.getSn());
|
||||
if (StringUtils.isNotBlank(cachedUrl)) {
|
||||
return new PrivateSphereVO(cachedUrl);
|
||||
}
|
||||
verify(request.getClientIp(), "rq_ge_limit");
|
||||
// 生成二维码并上传到OSS
|
||||
try {
|
||||
String qrCodeUrl = generateAndUploadQrCode(request.getSn());
|
||||
// 存储到Redis
|
||||
redisUtilPool.hashSet(RedisConstant.PRIVATE_SN_QR_CODE, request.getSn(), qrCodeUrl, SN_CACHE_TIME);
|
||||
log.info("生成二维码并上传成功,sn: {}, url: {}", request.getSn(), qrCodeUrl);
|
||||
return new PrivateSphereVO(qrCodeUrl);
|
||||
} catch (Exception e) {
|
||||
log.error("生成二维码失败,sn: {}", request.getSn(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void verify(String ip, String suffix) {
|
||||
// IP限制检查:同一IP一小时内最多生成5次
|
||||
if (StringUtils.isNotBlank(ip)) {
|
||||
String countStr = redisUtilPool.hashGet(RedisConstant.IP_LIMIT + "_" + suffix, ip);
|
||||
int count = StringUtils.isNotBlank(countStr) ? Integer.parseInt(countStr) : 0;
|
||||
if (count >= IP_MAX_COUNT) {
|
||||
log.error("IP生成次数超限,ip: {}, count: {}", ip, count);
|
||||
throw new ServiceException(ErrorCodeEnum.IP_LIMIT);
|
||||
}
|
||||
// 增加计数
|
||||
redisUtilPool.hashSet(RedisConstant.IP_LIMIT + "_" + suffix, ip, String.valueOf(count + 1), IP_LIMIT_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean bindStoreSn(PrivateSphereBindRequest request) {
|
||||
redisUtilPool.hashSet(RedisConstant.PRIVATE_STORE_SN_BIND, request.getSn(), request.getStoreId(), SN_CACHE_TIME);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateSphereVO getPrivateSphereQr(PrivateSphereSnRequest request) {
|
||||
String storeId = redisUtilPool.hashGet(RedisConstant.PRIVATE_STORE_SN_BIND, request.getSn());
|
||||
if (StringUtils.isBlank(storeId)) {
|
||||
return PrivateSphereVO.unbind();
|
||||
}
|
||||
String privateSphereQrCode = redisUtilPool.hashGet(RedisConstant.PRIVATE_STORE_SN_QR_CODE, request.getSn());
|
||||
if (privateSphereQrCode == null) {
|
||||
privateSphereQrCode = storeDao.getPrivateSphereQrCode(storeId);
|
||||
redisUtilPool.hashSet(RedisConstant.PRIVATE_STORE_SN_QR_CODE, request.getSn(), privateSphereQrCode == null ? "" : privateSphereQrCode, 120);
|
||||
}
|
||||
if (StringUtils.isBlank(privateSphereQrCode)) {
|
||||
return PrivateSphereVO.noMaintain();
|
||||
}
|
||||
return new PrivateSphereVO(privateSphereQrCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成二维码并上传到OSS
|
||||
*
|
||||
* @param sn 二维码内容
|
||||
* @return OSS图片路径
|
||||
*/
|
||||
private String generateAndUploadQrCode(String sn) throws Exception {
|
||||
// 配置二维码
|
||||
QrConfig qrConfig = new QrConfig(QR_CODE_SIZE, QR_CODE_SIZE);
|
||||
qrConfig.setMargin(2);
|
||||
|
||||
// 生成二维码图片到内存
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
QrCodeUtil.generate(sn, qrConfig, "png", outputStream);
|
||||
|
||||
String fileName = "qrcode" + sn + ".png";
|
||||
|
||||
// 上传到OSS
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||
long contentLength = outputStream.size();
|
||||
|
||||
return ossClientService.putObjectWithoutPrefix(fileName, inputStream, contentLength, "image/png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.cool.store.aspect;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.cool.store.annotation.Debounce;
|
||||
import com.cool.store.context.CurrentUserHolder;
|
||||
import com.cool.store.context.PartnerUserHolder;
|
||||
import com.cool.store.enums.ErrorCodeEnum;
|
||||
import com.cool.store.exception.ServiceException;
|
||||
import com.cool.store.utils.RedisUtilPool;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 防抖切面
|
||||
* </p>
|
||||
*
|
||||
* @author wangff
|
||||
* @since 2026/2/7
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DebounceAspect {
|
||||
@Resource
|
||||
private RedisUtilPool redisUtilPool;
|
||||
|
||||
private static final String KEY_PREFIX = "debounce:";
|
||||
private static final String FIELD_NEXT_ALLOWED_AT = "nextAllowedAt";
|
||||
private static final String FIELD_LAST_RESULT = "lastResult";
|
||||
|
||||
private final ExpressionParser spelParser = new SpelExpressionParser();
|
||||
|
||||
@Around("execution(public * *(..)) && (@within(com.cool.store.annotation.Debounce) || @annotation(com.cool.store.annotation.Debounce))")
|
||||
public Object around(ProceedingJoinPoint pjp) throws Throwable {
|
||||
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
|
||||
|
||||
Debounce debounce = AnnotatedElementUtils.findMergedAnnotation(method, Debounce.class);
|
||||
if (debounce == null) {
|
||||
debounce = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Debounce.class);
|
||||
}
|
||||
if (debounce == null || debounce.timeMs() <= 0) {
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
String key = KEY_PREFIX + buildKey(debounce, pjp, method);
|
||||
long now = System.currentTimeMillis();
|
||||
long ttlMs = debounce.timeMs();
|
||||
|
||||
Long nextAllowedAt = readNextAllowedAt(key);
|
||||
if (nextAllowedAt != null && now < nextAllowedAt) {
|
||||
if (debounce.throwOnDebounce()) {
|
||||
throw new ServiceException(ErrorCodeEnum.OPERATION_OVER_TIME);
|
||||
}
|
||||
String val = redisUtilPool.getString(key + ":" + FIELD_LAST_RESULT);
|
||||
return parseToReturnType(val, method);
|
||||
}
|
||||
|
||||
boolean allowed = trySetNextAllowedAt(key, now + ttlMs, ttlMs);
|
||||
if (!allowed) {
|
||||
if (debounce.throwOnDebounce()) {
|
||||
throw new ServiceException(ErrorCodeEnum.OPERATION_OVER_TIME);
|
||||
}
|
||||
String val = redisUtilPool.getString(key + ":" + FIELD_LAST_RESULT);
|
||||
return parseToReturnType(val, method);
|
||||
}
|
||||
|
||||
Object result = null;
|
||||
try {
|
||||
result = pjp.proceed();
|
||||
return result;
|
||||
} finally {
|
||||
if (!isVoidReturn(method) && Objects.nonNull(result)) {
|
||||
redisUtilPool.setNxExpire(key + ":" + FIELD_LAST_RESULT, JSONObject.toJSONString(result), (int) ttlMs * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Long readNextAllowedAt(String key) {
|
||||
String val = redisUtilPool.getString(key + ":" + FIELD_NEXT_ALLOWED_AT);
|
||||
if (StringUtils.isBlank(val)) {
|
||||
return null;
|
||||
}
|
||||
Object obj = JSONObject.parse(val);
|
||||
if (obj instanceof Number) {
|
||||
return ((Number) obj).longValue();
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(val);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean trySetNextAllowedAt(String key, long nextAllowedAt, long ttlMs) {
|
||||
return redisUtilPool.setNxExpire(key + ":" + FIELD_NEXT_ALLOWED_AT, String.valueOf(nextAllowedAt), (int) ttlMs);
|
||||
}
|
||||
|
||||
private boolean isVoidReturn(Method method) {
|
||||
return Void.TYPE.equals(method.getReturnType()) || Void.class.equals(method.getReturnType());
|
||||
}
|
||||
|
||||
private Object parseToReturnType(String json, Method method) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
return null;
|
||||
}
|
||||
Class<?> returnType = method.getReturnType();
|
||||
if (returnType == null || Object.class.equals(returnType)) {
|
||||
return JSONObject.parse(json);
|
||||
}
|
||||
if (String.class.equals(returnType)) {
|
||||
return json;
|
||||
}
|
||||
return JSON.parseObject(json, returnType);
|
||||
}
|
||||
|
||||
private String buildKey(Debounce debounce, ProceedingJoinPoint pjp, Method method) {
|
||||
String spel = debounce.key();
|
||||
String suffix = debounce.diffUser() ? ":" + (debounce.pc() ? CurrentUserHolder.getUserId() : PartnerUserHolder.getUser().getPartnerId()) : "";
|
||||
if (spel != null && !spel.isEmpty()) {
|
||||
EvaluationContext ctx = new StandardEvaluationContext();
|
||||
Object[] args = pjp.getArgs();
|
||||
String[] paramNames = ((MethodSignature) pjp.getSignature()).getParameterNames();
|
||||
if (paramNames != null) {
|
||||
for (int i = 0; i < paramNames.length; i++) {
|
||||
ctx.setVariable(paramNames[i], args[i]);
|
||||
}
|
||||
}
|
||||
Object val = spelParser.parseExpression(spel).getValue(ctx);
|
||||
return "spel:" + method.getDeclaringClass().getName() + "#" + method.getName() + ":" + val + suffix;
|
||||
}
|
||||
return "default:" + method.getDeclaringClass().getName() + "#" + method.getName() + ":" + Arrays.deepHashCode(pjp.getArgs()) + suffix;
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,9 @@ public class OpenApiValidateFilter implements Filter {
|
||||
"/zxjp/open/v1/statusRefresh",
|
||||
"/zxjp/open/v1/getStoreUser",
|
||||
"/zxjp/open/v1/callback",
|
||||
"/zxjp/open/v1/wallet/onlineCommercialBankCallback"
|
||||
"/zxjp/open/v1/wallet/onlineCommercialBankCallback",
|
||||
"/zxjp/open/v1/ps/geBindQr",
|
||||
"/zxjp/open/v1/ps/getPrivateSphereQr"
|
||||
));
|
||||
private static final List<String> oldUrlMapping = new ArrayList<>(Arrays.asList(
|
||||
"/zxjp/open/v1/statusRefresh","/zxjp/open/v1/changePaymentStatus",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.cool.store.controller.webb;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.cool.store.annotation.Debounce;
|
||||
import com.cool.store.dto.*;
|
||||
import com.cool.store.dto.region.BigRegionDTO;
|
||||
import com.cool.store.dto.store.StoreUserPositionDTO;
|
||||
@@ -17,6 +18,7 @@ import com.cool.store.request.notice.ThirdMatterRequest;
|
||||
import com.cool.store.request.openapi.ShopListRequest;
|
||||
import com.cool.store.request.openapi.StoreRequest;
|
||||
import com.cool.store.request.openapi.SubRegionRequest;
|
||||
import com.cool.store.request.privatesphere.PrivateSphereSnRequest;
|
||||
import com.cool.store.request.wallet.AccountTradeCallbackRequest;
|
||||
import com.cool.store.request.wallet.AddTagCallbackNoticeRequest;
|
||||
import com.cool.store.request.wallet.OnlineCommercialBankCallbackRequest;
|
||||
@@ -27,9 +29,12 @@ import com.cool.store.response.ResponseResult;
|
||||
import com.cool.store.response.ShopResponse;
|
||||
import com.cool.store.response.bigdata.ApiResponse;
|
||||
import com.cool.store.service.*;
|
||||
import com.cool.store.service.privatesphere.PrivateSphereQrService;
|
||||
import com.cool.store.service.wallet.WalletService;
|
||||
import com.cool.store.utils.HttpHelper;
|
||||
import com.cool.store.utils.poi.StringUtils;
|
||||
import com.cool.store.service.close.CloseStoreService;
|
||||
import com.cool.store.vo.privatesphere.PrivateSphereVO;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
@@ -39,6 +44,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -80,6 +86,8 @@ public class OpenApiController {
|
||||
BigRegionService bigRegionService;
|
||||
@Resource
|
||||
RegionService regionService;
|
||||
@Resource
|
||||
PrivateSphereQrService privateSphereQrService;
|
||||
|
||||
@PostMapping("/statusRefresh")
|
||||
public ApiResponse<Boolean> statusRefresh(@RequestBody StatusRefreshDTO statusRefreshDTO){
|
||||
@@ -280,4 +288,19 @@ public class OpenApiController {
|
||||
return ApiResponse.success(regionService.getSubRegionByParentId(request.getRegionId()));
|
||||
}
|
||||
|
||||
@ApiOperation("私域二维码-生成绑定二维码")
|
||||
@PostMapping("/ps/geBindQr")
|
||||
@Debounce(key = "#request.sn", diffUser = false, timeMs = 5000)
|
||||
public ApiResponse<PrivateSphereVO> geBindQr(@RequestBody @Validated PrivateSphereSnRequest request, HttpServletRequest httpRequest) {
|
||||
request.setClientIp(HttpHelper.getIpAddr(httpRequest));
|
||||
return ApiResponse.success(privateSphereQrService.generateQrCode(request));
|
||||
}
|
||||
|
||||
@ApiOperation("私域二维码-根据设备sn获取门店私域二维码")
|
||||
@PostMapping("/ps/getPrivateSphereQr")
|
||||
@Debounce(key = "#request.sn", diffUser = false, timeMs = 5000)
|
||||
public ApiResponse<PrivateSphereVO> getPrivateSphereQr(@RequestBody @Validated PrivateSphereSnRequest request, HttpServletRequest httpRequest) {
|
||||
request.setClientIp(HttpHelper.getIpAddr(httpRequest));
|
||||
return ApiResponse.success(privateSphereQrService.getPrivateSphereQr(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.cool.store.controller.webc;
|
||||
|
||||
import com.cool.store.request.privatesphere.PrivateSphereBindRequest;
|
||||
import com.cool.store.response.ResponseResult;
|
||||
import com.cool.store.service.privatesphere.PrivateSphereQrService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mini门店私域二维码
|
||||
* </p>
|
||||
*
|
||||
* @author wangff
|
||||
* @since 2026/4/2
|
||||
*/
|
||||
@RestController
|
||||
@Api(tags = "Mini门店私域二维码")
|
||||
@RequestMapping({"/mini/sp"})
|
||||
@RequiredArgsConstructor
|
||||
public class MiniPrivateSphereQrController {
|
||||
private final PrivateSphereQrService privateSphereQrService;
|
||||
|
||||
@ApiOperation("门店绑定设备sn")
|
||||
@PostMapping("/bind")
|
||||
public ResponseResult<Boolean> bindStoreSn(@RequestBody @Validated PrivateSphereBindRequest request) {
|
||||
return ResponseResult.success(privateSphereQrService.bindStoreSn(request));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user