feat(device): 码牌支持扫码链接并移除单条新增

新增 get-code-link 按网关地址拼接 H5 码牌页链接;运营端改为批量生成+绑定,删除快捷 add。
This commit is contained in:
DaxPay Dev
2026-07-10 23:06:14 +08:00
parent ea9db98e13
commit 2b5ac311ff
3 changed files with 26 additions and 45 deletions

View File

@@ -39,14 +39,6 @@ public class DeviceQrCodeAdminController {
private final DeviceQrCodeAdminService deviceQrCodeAdminService;
@PermCode(code = "manage", nameCn = "码牌管理", nameEn = "QrCode Manage")
@Operation(summary = "新增码牌(快捷绑定商户)")
@PostMapping("/add")
public Result<Void> add(@RequestBody @Validated(ValidationGroup.add.class) DeviceQrCodeParam param) {
deviceQrCodeAdminService.add(param);
return Res.ok();
}
@PermCode(code = "manage", nameCn = "码牌管理", nameEn = "QrCode Manage")
@Operation(summary = "批量创建空白码牌")
@PostMapping("/create-batch")
@@ -100,6 +92,13 @@ public class DeviceQrCodeAdminController {
return Res.ok(deviceQrCodeAdminService.findById(id));
}
@PermCode(code = "view", nameCn = "码牌查看", nameEn = "QrCode View")
@Operation(summary = "获取码牌扫码链接")
@GetMapping("/get-code-link")
public Result<String> getCodeLink(@NotBlank(message = "{validation.field.code.notBlank}") String code) {
return Res.ok(deviceQrCodeAdminService.getCodeLink(code));
}
@PermCode(code = "manage", nameCn = "码牌管理", nameEn = "QrCode Manage")
@Operation(summary = "删除码牌")
@PostMapping("/delete")

View File

@@ -25,16 +25,6 @@ public class DeviceQrCodeParam {
@Size(max = 100, message = "{validation.field.qrCodeName.size}")
private String name;
/// 商户号(新增快捷绑定时必填; 编辑不改归属, 仅 add 校验)
@Schema(description = "商户号")
@NotBlank(message = "{validation.field.mchNo.notBlank}", groups = ValidationGroup.add.class)
private String mchNo;
/// 关联应用号(可空, 空则使用商户默认应用)
@Schema(description = "关联应用号")
@Size(max = 50, message = "{validation.field.appId.size}")
private String appId;
/// 金额类型: random-自定义金额 / fixed-固定金额
/// @see cn.daxpay.open.payment.device.enums.QrCodeAmountTypeEnum
@Schema(description = "金额类型(random-自定义/fixed-固定)")

View File

@@ -18,7 +18,8 @@ import cn.daxpay.open.platform.core.exception.DataNotExistException;
import cn.daxpay.open.platform.core.exception.operation.OperationFailException;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import cn.hutool.core.util.IdUtil;
import cn.daxpay.open.platform.system.entity.config.platform.infra.PlatformUrlConfig;
import cn.daxpay.open.platform.system.service.config.infra.PlatformUrlConfigService;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -37,33 +38,13 @@ import java.util.stream.IntStream;
@RequiredArgsConstructor
public class DeviceQrCodeAdminService {
/// 码牌编码前缀(单条快捷新增)
private static final String CODE_PREFIX = "QR";
/// H5 码牌支付页路径前缀(与 dax-pay-h5 RoutePath.CODE_PAY 一致)
private static final String CODE_PAY_PATH = "/code-pay/";
private final DeviceQrCodeManager deviceQrCodeManager;
private final MerchantContextLoader merchantContextLoader;
private final TransService transService;
/// 新增码牌(快捷路径: 直接绑定商户, 自动生成编码)
@Transactional(rollbackFor = Exception.class)
public void add(DeviceQrCodeParam param) {
// 金额类型校验
QrCodeAmountTypeEnum amountType = QrCodeAmountTypeEnum.findByCode(param.getAmountType());
// 固定金额: fixed 类型必填且大于 0
validateFixedAmount(amountType, param.getFixedAmount());
// 应用解析: appId 空则取商户默认应用, 否则校验启用与归属, 回填 appId
String appId = resolveAppId(param.getMchNo(), param.getAppId());
DeviceQrCode entity = new DeviceQrCode()
.setCode(generateCode())
.setName(param.getName())
.setMchNo(param.getMchNo())
.setAppId(appId)
.setAmountType(amountType.getCode())
.setFixedAmount(amountType == QrCodeAmountTypeEnum.FIXED ? param.getFixedAmount() : null)
.setStatus(QrCodeStatusEnum.ENABLED.getCode())
.setRemark(param.getRemark());
deviceQrCodeManager.save(entity);
}
private final PlatformUrlConfigService platformUrlConfigService;
/// 批量创建空白码牌(不绑商户, 进入平台库存)
@Transactional(rollbackFor = Exception.class)
@@ -171,9 +152,20 @@ public class DeviceQrCodeAdminService {
deviceQrCodeManager.updateById(entity);
}
/// 生成码牌编码: 前缀 + 雪花ID
private String generateCode() {
return CODE_PREFIX + IdUtil.getSnowflakeNextId();
/// 获取码牌 H5 扫码链接
///
/// 拼接规则: {paymentGatewayBaseUrl}/code-pay/{code}
public String getCodeLink(String code) {
deviceQrCodeManager.findByCode(code)
// 码牌: 码牌不存在
.orElseThrow(() -> new DataNotExistException("error.device.qrcode.notFound"));
PlatformUrlConfig urlConfig = platformUrlConfigService.getUrlConfig();
String gatewayBase = urlConfig.getPaymentGatewayBaseUrl();
if (StrUtil.isBlank(gatewayBase)) {
// 支付网关前端地址未配置
throw new OperationFailException(CommonCode.FAIL_CODE, "error.common.gatewayUrlNotConfigured");
}
return gatewayBase + CODE_PAY_PATH + code;
}
/// 应用解析: 复用 [MerchantContextLoader.resolveApp], appId 空取商户默认应用, 返回解析后的 appId