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:
王非凡
2026-04-08 10:44:13 +00:00
committed by 正新
parent 5ff5673914
commit 68cef4b83a
17 changed files with 562 additions and 1 deletions

View File

@@ -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;
}

View File

@@ -384,4 +384,24 @@ public class RedisConstant {
* 钱包分账支付锁 * 钱包分账支付锁
*/ */
public static final String WALLET_ALLOCATION_PAY_LOCK_KEY = "wallet_allocation_pay_lock_key:{0}"; 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";
} }

View File

@@ -452,6 +452,8 @@ public enum ErrorCodeEnum {
WALLET_RE_PAY_FAIL(1830019, "重新支付异常,请联系管理员", null), WALLET_RE_PAY_FAIL(1830019, "重新支付异常,请联系管理员", null),
WALLET_PAY_CANNOT_CANCEL(1830020, "该交易无法取消", null), WALLET_PAY_CANNOT_CANCEL(1830020, "该交易无法取消", null),
TOTAL_FEES_NEED_EQUAL(1830021, "分账总金额需与合计缴费金额一致", null), TOTAL_FEES_NEED_EQUAL(1830021, "分账总金额需与合计缴费金额一致", null),
IP_LIMIT(1840000, "IP访问次数超限", null),
; ;

View File

@@ -247,4 +247,11 @@ public class StoreDao {
} }
return storeMapper.batchUpdateAddress(storeList); return storeMapper.batchUpdateAddress(storeList);
} }
/**
* 获取私域二维码
*/
public String getPrivateSphereQrCode(String storeId){
return storeMapper.getPrivateSphereQrCode(storeId);
}
} }

View File

@@ -153,4 +153,6 @@ public interface StoreMapper {
List<StoreAddressDTO> getStoreAddress(@Param("flag") Integer flag, @Param("storeCode") String storeCode); List<StoreAddressDTO> getStoreAddress(@Param("flag") Integer flag, @Param("storeCode") String storeCode);
int batchUpdateAddress(@Param("list") List<StoreDO> storeList); int batchUpdateAddress(@Param("list") List<StoreDO> storeList);
String getPrivateSphereQrCode(@Param("storeId") String storeId);
} }

View File

@@ -421,4 +421,8 @@
</foreach> </foreach>
</update> </update>
<select id="getPrivateSphereQrCode" resultType="java.lang.String">
SELECT private_sphere_qr FROM store_extend_info_${enterpriseId}
WHERE store_id = #{storeId}
</select>
</mapper> </mapper>

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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未绑定sn2门店未维护私域二维码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);
}
}

View File

@@ -127,6 +127,11 @@
<groupId>com.aliyun</groupId> <groupId>com.aliyun</groupId>
<artifactId>alibabacloud-dysmsapi20170525</artifactId> <artifactId>alibabacloud-dysmsapi20170525</artifactId>
</dependency> </dependency>
<!-- 二维码生成 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -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);
}

View File

@@ -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");
}
}

View File

@@ -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;
}
}

View File

@@ -52,7 +52,9 @@ public class OpenApiValidateFilter implements Filter {
"/zxjp/open/v1/statusRefresh", "/zxjp/open/v1/statusRefresh",
"/zxjp/open/v1/getStoreUser", "/zxjp/open/v1/getStoreUser",
"/zxjp/open/v1/callback", "/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( private static final List<String> oldUrlMapping = new ArrayList<>(Arrays.asList(
"/zxjp/open/v1/statusRefresh","/zxjp/open/v1/changePaymentStatus", "/zxjp/open/v1/statusRefresh","/zxjp/open/v1/changePaymentStatus",

View File

@@ -1,6 +1,7 @@
package com.cool.store.controller.webb; package com.cool.store.controller.webb;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.cool.store.annotation.Debounce;
import com.cool.store.dto.*; import com.cool.store.dto.*;
import com.cool.store.dto.region.BigRegionDTO; import com.cool.store.dto.region.BigRegionDTO;
import com.cool.store.dto.store.StoreUserPositionDTO; 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.ShopListRequest;
import com.cool.store.request.openapi.StoreRequest; import com.cool.store.request.openapi.StoreRequest;
import com.cool.store.request.openapi.SubRegionRequest; 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.AccountTradeCallbackRequest;
import com.cool.store.request.wallet.AddTagCallbackNoticeRequest; import com.cool.store.request.wallet.AddTagCallbackNoticeRequest;
import com.cool.store.request.wallet.OnlineCommercialBankCallbackRequest; 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.ShopResponse;
import com.cool.store.response.bigdata.ApiResponse; import com.cool.store.response.bigdata.ApiResponse;
import com.cool.store.service.*; import com.cool.store.service.*;
import com.cool.store.service.privatesphere.PrivateSphereQrService;
import com.cool.store.service.wallet.WalletService; import com.cool.store.service.wallet.WalletService;
import com.cool.store.utils.HttpHelper;
import com.cool.store.utils.poi.StringUtils; import com.cool.store.utils.poi.StringUtils;
import com.cool.store.service.close.CloseStoreService; import com.cool.store.service.close.CloseStoreService;
import com.cool.store.vo.privatesphere.PrivateSphereVO;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@@ -39,6 +44,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List; import java.util.List;
/** /**
@@ -80,6 +86,8 @@ public class OpenApiController {
BigRegionService bigRegionService; BigRegionService bigRegionService;
@Resource @Resource
RegionService regionService; RegionService regionService;
@Resource
PrivateSphereQrService privateSphereQrService;
@PostMapping("/statusRefresh") @PostMapping("/statusRefresh")
public ApiResponse<Boolean> statusRefresh(@RequestBody StatusRefreshDTO statusRefreshDTO){ public ApiResponse<Boolean> statusRefresh(@RequestBody StatusRefreshDTO statusRefreshDTO){
@@ -280,4 +288,19 @@ public class OpenApiController {
return ApiResponse.success(regionService.getSubRegionByParentId(request.getRegionId())); 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));
}
} }

View File

@@ -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));
}
}

View File

@@ -238,6 +238,12 @@
<version>1.70</version> <version>1.70</version>
</dependency> </dependency>
<!-- XML处理 --> <!-- XML处理 -->
<!-- 二维码生成 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>