feat: 移除mock支付、卡不可复用、新增设备端取消订单接口
1. 移除所有mock支付逻辑,订单强制走微信支付
2. 卡状态流转改为单向:UNUSED->RESERVED->USED(终态不可复用)
3. 解绑接口改为仅清除device_id,卡状态保持USED
4. 新增设备端取消订单接口 POST /api/jst/daycard/cancel/{orderNo}
5. 修复微信回调验签header大小写问题
6. 临时注释分账标记(商户号未开通分账权限)
7. 删除多余的App UI辅助接口(ApiJstUiController)
8. 清理application.yml中无用UI配置
This commit is contained in:
@@ -159,8 +159,6 @@ public final class DayCardConstants {
|
|||||||
public static final String REFUND_STATUS_CLOSED = "CLOSED";
|
public static final String REFUND_STATUS_CLOSED = "CLOSED";
|
||||||
|
|
||||||
// ==================== 支付渠道 ====================
|
// ==================== 支付渠道 ====================
|
||||||
/** Mock支付 */
|
|
||||||
public static final String PAY_CHANNEL_MOCK = "MOCK";
|
|
||||||
/** 微信支付 */
|
/** 微信支付 */
|
||||||
public static final String PAY_CHANNEL_WECHAT = "WECHAT";
|
public static final String PAY_CHANNEL_WECHAT = "WECHAT";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public class DayCardManageController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 解绑设备天卡权益(Web端手动操作)
|
* 解绑设备天卡权益(Web端手动操作)
|
||||||
* <p>将设备绑定的 USED 卡转为 REUSABLE,释放设备。橘视厅权益保留,后续新设备下单可复用此卡。</p>
|
* <p>清除卡上的 device_id 绑定,卡状态保持 USED 不变,不可被其他设备复用。</p>
|
||||||
*/
|
*/
|
||||||
@AuditAction(value = "DAYCARD_DEVICE_UNBIND", description = "解绑设备天卡权益")
|
@AuditAction(value = "DAYCARD_DEVICE_UNBIND", description = "解绑设备天卡权益")
|
||||||
@PostMapping("/device/unbind")
|
@PostMapping("/device/unbind")
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class DayCardOrderEntity implements Serializable {
|
|||||||
private Date createdAt;
|
private Date createdAt;
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
private Date updatedAt;
|
private Date updatedAt;
|
||||||
/** 支付渠道 MOCK/WECHAT */
|
/** 支付渠道 WECHAT */
|
||||||
private String payChannel;
|
private String payChannel;
|
||||||
/** 微信预支付ID */
|
/** 微信预支付ID */
|
||||||
private String prepayId;
|
private String prepayId;
|
||||||
|
|||||||
@@ -66,6 +66,15 @@ public interface DayCardOrderService extends IService<DayCardOrderEntity> {
|
|||||||
*/
|
*/
|
||||||
DayCardOrderEntity getByOrderNo(String orderNo);
|
DayCardOrderEntity getByOrderNo(String orderNo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备端取消未支付订单
|
||||||
|
* <p>校验订单归属设备后,关单微信支付、释放卡、更新订单CANCELLED、清理Redis锁</p>
|
||||||
|
*
|
||||||
|
* @param orderNo 订单号
|
||||||
|
* @param deviceId 设备ID(从 sign 解析,校验订单归属)
|
||||||
|
*/
|
||||||
|
void cancelOrder(String orderNo, Long deviceId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询设备的天卡权益(激活后 App 获取播放凭证)
|
* 查询设备的天卡权益(激活后 App 获取播放凭证)
|
||||||
* <p>从 day_card_pool 表查询 status=USED 且 expire_at > NOW() 的卡,返回 thirdCardNo 和 expireAt</p>
|
* <p>从 day_card_pool 表查询 status=USED 且 expire_at > NOW() 的卡,返回 thirdCardNo 和 expireAt</p>
|
||||||
@@ -86,7 +95,7 @@ public interface DayCardOrderService extends IService<DayCardOrderEntity> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 解绑设备天卡权益(Web端手动操作)
|
* 解绑设备天卡权益(Web端手动操作)
|
||||||
* <p>将 USED 卡转为 REUSABLE,释放设备绑定,保留橘视厅权益供后续设备复用</p>
|
* <p>清除卡上的 device_id 绑定,卡状态保持 USED 不变,不可被其他设备复用</p>
|
||||||
*
|
*
|
||||||
* @param deviceId 设备ID
|
* @param deviceId 设备ID
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
order.setChannelOrgId(channelOrgId);
|
order.setChannelOrgId(channelOrgId);
|
||||||
order.setAmount(amount);
|
order.setAmount(amount);
|
||||||
order.setStatus(DayCardConstants.ORDER_STATUS_CREATED);
|
order.setStatus(DayCardConstants.ORDER_STATUS_CREATED);
|
||||||
order.setPayChannel("MOCK");
|
order.setPayChannel(DayCardConstants.PAY_CHANNEL_WECHAT);
|
||||||
order.setDeleteFlag(DayCardConstants.DELETE_FLAG_UNDEL);
|
order.setDeleteFlag(DayCardConstants.DELETE_FLAG_UNDEL);
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
order.setCreatedAt(now);
|
order.setCreatedAt(now);
|
||||||
@@ -179,35 +179,12 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
baseMapper.insert(order);
|
baseMapper.insert(order);
|
||||||
|
|
||||||
// 7. 微信 Native 支付下单
|
// 7. 微信 Native 支付下单
|
||||||
if (wechatPayService.isConfigured()) {
|
String codeUrl = wechatPayService.createNativePay(orderNo, "天卡订单", amount, channelOrgId, agentOrgId, storeOrgId);
|
||||||
try {
|
order.setCodeUrl(codeUrl);
|
||||||
String codeUrl = wechatPayService.createNativePay(orderNo, "天卡订单", amount, channelOrgId, agentOrgId, storeOrgId);
|
UpdateWrapper<DayCardOrderEntity> payUpdate = new UpdateWrapper<>();
|
||||||
order.setCodeUrl(codeUrl);
|
payUpdate.eq("id", order.getId())
|
||||||
order.setPayChannel("WECHAT");
|
.set("code_url", codeUrl);
|
||||||
UpdateWrapper<DayCardOrderEntity> payUpdate = new UpdateWrapper<>();
|
baseMapper.update(null, payUpdate);
|
||||||
payUpdate.eq("id", order.getId())
|
|
||||||
.set("code_url", codeUrl)
|
|
||||||
.set("pay_channel", "WECHAT");
|
|
||||||
baseMapper.update(null, payUpdate);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.warn("微信下单失败,回退到MOCK模式: orderNo={}", orderNo, e);
|
|
||||||
order.setPayChannel("MOCK");
|
|
||||||
order.setCodeUrl("mock://pay?orderNo=" + orderNo);
|
|
||||||
UpdateWrapper<DayCardOrderEntity> payUpdate = new UpdateWrapper<>();
|
|
||||||
payUpdate.eq("id", order.getId())
|
|
||||||
.set("code_url", order.getCodeUrl())
|
|
||||||
.set("pay_channel", "MOCK");
|
|
||||||
baseMapper.update(null, payUpdate);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
order.setPayChannel("MOCK");
|
|
||||||
order.setCodeUrl("mock://pay?orderNo=" + orderNo);
|
|
||||||
UpdateWrapper<DayCardOrderEntity> payUpdate = new UpdateWrapper<>();
|
|
||||||
payUpdate.eq("id", order.getId())
|
|
||||||
.set("code_url", order.getCodeUrl())
|
|
||||||
.set("pay_channel", "MOCK");
|
|
||||||
baseMapper.update(null, payUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. SKIP LOCKED 抢卡(通用池,不按 partner 过滤)
|
// 8. SKIP LOCKED 抢卡(通用池,不按 partner 过滤)
|
||||||
DayCardPoolEntity pool = dayCardPoolDao.selectOneForUpdateSkipLocked();
|
DayCardPoolEntity pool = dayCardPoolDao.selectOneForUpdateSkipLocked();
|
||||||
@@ -219,7 +196,7 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
// 9. 乐观锁更新卡状态 UNUSED -> RESERVED
|
// 9. 乐观锁更新卡状态 UNUSED -> RESERVED
|
||||||
UpdateWrapper<DayCardPoolEntity> poolUpdate = new UpdateWrapper<>();
|
UpdateWrapper<DayCardPoolEntity> poolUpdate = new UpdateWrapper<>();
|
||||||
poolUpdate.eq("id", pool.getId())
|
poolUpdate.eq("id", pool.getId())
|
||||||
.in("status", DayCardConstants.POOL_STATUS_UNUSED, DayCardConstants.POOL_STATUS_REUSABLE)
|
.eq("status", DayCardConstants.POOL_STATUS_UNUSED)
|
||||||
.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL)
|
.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL)
|
||||||
.set("status", DayCardConstants.POOL_STATUS_RESERVED)
|
.set("status", DayCardConstants.POOL_STATUS_RESERVED)
|
||||||
.set("order_no", orderNo)
|
.set("order_no", orderNo)
|
||||||
@@ -298,7 +275,7 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
order.setChannelOrgId(channelOrgId);
|
order.setChannelOrgId(channelOrgId);
|
||||||
order.setAmount(amount);
|
order.setAmount(amount);
|
||||||
order.setStatus(DayCardConstants.ORDER_STATUS_CREATED);
|
order.setStatus(DayCardConstants.ORDER_STATUS_CREATED);
|
||||||
order.setPayChannel("MOCK");
|
order.setPayChannel(DayCardConstants.PAY_CHANNEL_WECHAT);
|
||||||
order.setDeleteFlag(DayCardConstants.DELETE_FLAG_UNDEL);
|
order.setDeleteFlag(DayCardConstants.DELETE_FLAG_UNDEL);
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
order.setCreatedAt(now);
|
order.setCreatedAt(now);
|
||||||
@@ -314,36 +291,12 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
baseMapper.insert(order);
|
baseMapper.insert(order);
|
||||||
|
|
||||||
// 6.5 微信 Native 支付下单
|
// 6.5 微信 Native 支付下单
|
||||||
if (wechatPayService.isConfigured()) {
|
String codeUrl = wechatPayService.createNativePay(orderNo, "天卡订单", amount, channelOrgId, agentOrgId, storeOrgId);
|
||||||
try {
|
order.setCodeUrl(codeUrl);
|
||||||
String codeUrl = wechatPayService.createNativePay(orderNo, "天卡订单", amount, channelOrgId, agentOrgId, storeOrgId);
|
UpdateWrapper<DayCardOrderEntity> payUpdate = new UpdateWrapper<>();
|
||||||
order.setCodeUrl(codeUrl);
|
payUpdate.eq("id", order.getId())
|
||||||
order.setPayChannel("WECHAT");
|
.set("code_url", codeUrl);
|
||||||
UpdateWrapper<DayCardOrderEntity> payUpdate = new UpdateWrapper<>();
|
baseMapper.update(null, payUpdate);
|
||||||
payUpdate.eq("id", order.getId())
|
|
||||||
.set("code_url", codeUrl)
|
|
||||||
.set("pay_channel", "WECHAT");
|
|
||||||
baseMapper.update(null, payUpdate);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.warn("微信下单失败,回退到MOCK模式: orderNo={}", orderNo, e);
|
|
||||||
order.setPayChannel("MOCK");
|
|
||||||
order.setCodeUrl("mock://pay?orderNo=" + orderNo);
|
|
||||||
UpdateWrapper<DayCardOrderEntity> payUpdate = new UpdateWrapper<>();
|
|
||||||
payUpdate.eq("id", order.getId())
|
|
||||||
.set("code_url", order.getCodeUrl())
|
|
||||||
.set("pay_channel", "MOCK");
|
|
||||||
baseMapper.update(null, payUpdate);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 开发模式:使用 Mock 支付
|
|
||||||
order.setPayChannel("MOCK");
|
|
||||||
order.setCodeUrl("mock://pay?orderNo=" + orderNo);
|
|
||||||
UpdateWrapper<DayCardOrderEntity> payUpdate = new UpdateWrapper<>();
|
|
||||||
payUpdate.eq("id", order.getId())
|
|
||||||
.set("code_url", order.getCodeUrl())
|
|
||||||
.set("pay_channel", "MOCK");
|
|
||||||
baseMapper.update(null, payUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. SKIP LOCKED 抢卡
|
// 7. SKIP LOCKED 抢卡
|
||||||
DayCardPoolEntity pool = dayCardPoolDao.selectOneForUpdateSkipLocked();
|
DayCardPoolEntity pool = dayCardPoolDao.selectOneForUpdateSkipLocked();
|
||||||
@@ -355,7 +308,7 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
// 8. 乐观锁更新卡状态 UNUSED -> RESERVED
|
// 8. 乐观锁更新卡状态 UNUSED -> RESERVED
|
||||||
UpdateWrapper<DayCardPoolEntity> poolUpdate = new UpdateWrapper<>();
|
UpdateWrapper<DayCardPoolEntity> poolUpdate = new UpdateWrapper<>();
|
||||||
poolUpdate.eq("id", pool.getId())
|
poolUpdate.eq("id", pool.getId())
|
||||||
.in("status", DayCardConstants.POOL_STATUS_UNUSED, DayCardConstants.POOL_STATUS_REUSABLE)
|
.eq("status", DayCardConstants.POOL_STATUS_UNUSED)
|
||||||
.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL)
|
.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL)
|
||||||
.set("status", DayCardConstants.POOL_STATUS_RESERVED)
|
.set("status", DayCardConstants.POOL_STATUS_RESERVED)
|
||||||
.set("order_no", orderNo)
|
.set("order_no", orderNo)
|
||||||
@@ -444,12 +397,12 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
redisUtils.delete(DayCardConstants.REDIS_DEVICE_ORDER_PREFIX + order.getDeviceSn());
|
redisUtils.delete(DayCardConstants.REDIS_DEVICE_ORDER_PREFIX + order.getDeviceSn());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 触发分账(异步)
|
// 6. 触发分账(商户号开通分账权限后取消注释启用)
|
||||||
try {
|
// try {
|
||||||
profitSharingExecuteService.executeSharing(orderNo);
|
// profitSharingExecuteService.executeSharing(orderNo);
|
||||||
} catch (Exception e) {
|
// } catch (Exception e) {
|
||||||
logger.error("触发分账失败: orderNo={}", orderNo, e);
|
// logger.error("触发分账失败: orderNo={}", orderNo, e);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -646,7 +599,7 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
// 更新订单为CANCELLED
|
// 更新订单为CANCELLED
|
||||||
UpdateWrapper<DayCardOrderEntity> orderUpdate = new UpdateWrapper<>();
|
UpdateWrapper<DayCardOrderEntity> orderUpdate = new UpdateWrapper<>();
|
||||||
orderUpdate.eq("id", order.getId())
|
orderUpdate.eq("id", order.getId())
|
||||||
.eq("status", DayCardConstants.ORDER_STATUS_CREATED)
|
.in("status", DayCardConstants.ORDER_STATUS_CREATED, DayCardConstants.ORDER_STATUS_PAYING)
|
||||||
.set("status", DayCardConstants.ORDER_STATUS_CANCELLED)
|
.set("status", DayCardConstants.ORDER_STATUS_CANCELLED)
|
||||||
.set("cancel_time", now)
|
.set("cancel_time", now)
|
||||||
.set("updated_at", now);
|
.set("updated_at", now);
|
||||||
@@ -672,6 +625,29 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
return baseMapper.selectOne(wrapper);
|
return baseMapper.selectOne(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void cancelOrder(String orderNo, Long deviceId) {
|
||||||
|
if (StringUtils.isBlank(orderNo) || deviceId == null) {
|
||||||
|
throw new ASException("订单号和设备ID不能为空");
|
||||||
|
}
|
||||||
|
DayCardOrderEntity order = getByOrderNo(orderNo);
|
||||||
|
if (order == null) {
|
||||||
|
throw new ASException("订单不存在: " + orderNo);
|
||||||
|
}
|
||||||
|
if (!deviceId.equals(order.getDeviceId())) {
|
||||||
|
throw new ASException("无权操作此订单");
|
||||||
|
}
|
||||||
|
if (DayCardConstants.ORDER_STATUS_PAID.equals(order.getStatus())) {
|
||||||
|
throw new ASException("订单已支付,无法取消");
|
||||||
|
}
|
||||||
|
if (DayCardConstants.ORDER_STATUS_CANCELLED.equals(order.getStatus())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cancelSingleOrder(order);
|
||||||
|
logger.info("设备端取消订单: orderNo={}, deviceId={}", orderNo, deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DayCardPoolEntity getDeviceBenefits(Long deviceId) {
|
public DayCardPoolEntity getDeviceBenefits(Long deviceId) {
|
||||||
QueryWrapper<DayCardPoolEntity> wrapper = new QueryWrapper<>();
|
QueryWrapper<DayCardPoolEntity> wrapper = new QueryWrapper<>();
|
||||||
@@ -706,7 +682,6 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
UpdateWrapper<DayCardPoolEntity> update = new UpdateWrapper<>();
|
UpdateWrapper<DayCardPoolEntity> update = new UpdateWrapper<>();
|
||||||
update.eq("id", pool.getId())
|
update.eq("id", pool.getId())
|
||||||
.eq("status", DayCardConstants.POOL_STATUS_USED)
|
.eq("status", DayCardConstants.POOL_STATUS_USED)
|
||||||
.set("status", DayCardConstants.POOL_STATUS_REUSABLE)
|
|
||||||
.set("device_id", null)
|
.set("device_id", null)
|
||||||
.set("updated_at", new Date());
|
.set("updated_at", new Date());
|
||||||
int updated = dayCardPoolDao.update(null, update);
|
int updated = dayCardPoolDao.update(null, update);
|
||||||
@@ -723,17 +698,16 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
|||||||
throw new ASException("设备ID不能为空");
|
throw new ASException("设备ID不能为空");
|
||||||
}
|
}
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
// 1. 释放设备当前天卡权益(若有):USED -> REUSABLE,清卡上的 device_id
|
// 1. 解绑设备当前天卡权益(若有):清除 device_id,卡状态保持 USED 不变
|
||||||
DayCardPoolEntity pool = getDeviceBenefits(deviceId);
|
DayCardPoolEntity pool = getDeviceBenefits(deviceId);
|
||||||
if (pool != null) {
|
if (pool != null) {
|
||||||
UpdateWrapper<DayCardPoolEntity> poolUpdate = new UpdateWrapper<>();
|
UpdateWrapper<DayCardPoolEntity> poolUpdate = new UpdateWrapper<>();
|
||||||
poolUpdate.eq("id", pool.getId())
|
poolUpdate.eq("id", pool.getId())
|
||||||
.eq("status", DayCardConstants.POOL_STATUS_USED)
|
.eq("status", DayCardConstants.POOL_STATUS_USED)
|
||||||
.set("status", DayCardConstants.POOL_STATUS_REUSABLE)
|
|
||||||
.set("device_id", null)
|
.set("device_id", null)
|
||||||
.set("updated_at", now);
|
.set("updated_at", now);
|
||||||
dayCardPoolDao.update(null, poolUpdate);
|
dayCardPoolDao.update(null, poolUpdate);
|
||||||
logger.info("设备组织解绑-释放天卡权益: deviceId={}, poolId={}, cardCode={}",
|
logger.info("设备组织解绑-解除天卡权益绑定: deviceId={}, poolId={}, cardCode={}",
|
||||||
deviceId, pool.getId(), pool.getCardCode());
|
deviceId, pool.getId(), pool.getCardCode());
|
||||||
}
|
}
|
||||||
// 2. 清除设备的门店组织关联(storeOrgId),设备可在新场所用新激活码重新绑定
|
// 2. 清除设备的门店组织关联(storeOrgId),设备可在新场所用新激活码重新绑定
|
||||||
|
|||||||
@@ -157,18 +157,13 @@ public class DayCardRefundServiceImpl extends ServiceImpl<DayCardRefundDao, DayC
|
|||||||
// 7. 尝试回退分账(设置关联分账单状态为 CLOSED)
|
// 7. 尝试回退分账(设置关联分账单状态为 CLOSED)
|
||||||
rollbackSettleRecords(orderNo, now);
|
rollbackSettleRecords(orderNo, now);
|
||||||
|
|
||||||
// 8. 调用微信退款(Mock支付则跳过)
|
// 8. 调用微信退款
|
||||||
String payNo = order.getPayNo();
|
BigDecimal totalAmount = order.getAmount();
|
||||||
if (isWechatPayment(payNo)) {
|
WechatPayResult refundResult = wechatPayService.refund(orderNo, refundNo, refundAmount, totalAmount, reason);
|
||||||
BigDecimal totalAmount = order.getAmount();
|
if (!refundResult.isSuccess()) {
|
||||||
WechatPayResult refundResult = wechatPayService.refund(orderNo, refundNo, refundAmount, totalAmount, reason);
|
throw new ASException("微信退款失败: " + refundResult.getErrorMsg());
|
||||||
if (!refundResult.isSuccess()) {
|
|
||||||
throw new ASException("微信退款失败: " + refundResult.getErrorMsg());
|
|
||||||
}
|
|
||||||
logger.info("微信退款成功: orderNo={}, refundNo={}", orderNo, refundNo);
|
|
||||||
} else {
|
|
||||||
logger.info("Mock支付退款,无需调用微信接口: orderNo={}, refundNo={}", orderNo, refundNo);
|
|
||||||
}
|
}
|
||||||
|
logger.info("微信退款成功: orderNo={}, refundNo={}", orderNo, refundNo);
|
||||||
|
|
||||||
// 9. 更新退款记录 status=SUCCESS
|
// 9. 更新退款记录 status=SUCCESS
|
||||||
refund.setStatus(DayCardConstants.REFUND_STATUS_SUCCESS);
|
refund.setStatus(DayCardConstants.REFUND_STATUS_SUCCESS);
|
||||||
@@ -274,21 +269,6 @@ public class DayCardRefundServiceImpl extends ServiceImpl<DayCardRefundDao, DayC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否为微信支付
|
|
||||||
* <p>Mock 支付的 payNo 以 "MOCK_" 开头,其余视为微信支付</p>
|
|
||||||
*
|
|
||||||
* @param payNo 支付单号
|
|
||||||
* @return true=微信支付
|
|
||||||
*/
|
|
||||||
private boolean isWechatPayment(String payNo) {
|
|
||||||
if (StringUtils.isBlank(payNo)) {
|
|
||||||
// 没有支付单号,默认视为微信支付
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return !payNo.startsWith(DayCardConstants.PAY_CHANNEL_MOCK + "_");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成退款单号:REFUND + yyyyMMddHHmmss + 6位随机数
|
* 生成退款单号:REFUND + yyyyMMddHHmmss + 6位随机数
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -195,6 +195,28 @@ public class ApiJstController {
|
|||||||
return buildSuccessResponse(data);
|
return buildSuccessResponse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消天卡订单(用户放弃支付时调用)
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "取消天卡订单",
|
||||||
|
notes = "请求头必传 sign。用户取消支付时调用,取消订单并释放卡,允许设备重新下单。")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "sign", value = "设备签名", required = true, paramType = "header", dataType = "string")
|
||||||
|
})
|
||||||
|
@PostMapping("/daycard/cancel/{orderNo}")
|
||||||
|
public Map<String, Object> cancelDayCard(@PathVariable("orderNo") String orderNo, HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
Long deviceId = deviceIdFrom(request);
|
||||||
|
dayCardOrderService.cancelOrder(orderNo, deviceId);
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("orderNo", orderNo);
|
||||||
|
data.put("status", "CANCELLED");
|
||||||
|
return buildSuccessResponse(data);
|
||||||
|
} catch (ASException e) {
|
||||||
|
return rrExceptionResponse(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备心跳:更新设备心跳时间,续期 sign
|
* 设备心跳:更新设备心跳时间,续期 sign
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,253 +0,0 @@
|
|||||||
package io.as.modules.openapi.controller;
|
|
||||||
|
|
||||||
import io.as.modules.device.dto.DeviceOrgContext;
|
|
||||||
import io.as.modules.device.service.DeviceResolveService;
|
|
||||||
import io.as.modules.openapi.filter.ApiDeviceSignFilter;
|
|
||||||
import io.as.modules.sys.entity.SysOrgEntity;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 橘视听 App UI 数据接口
|
|
||||||
* <p>提供顶栏按钮、筛选分类、筛选内容列表、独立配置等 App 展示所需数据</p>
|
|
||||||
* <p>所有接口均需请求头 sign(由 ApiDeviceSignFilter 校验)</p>
|
|
||||||
* <p>返回格式:{code:0, msg:"success", data:{...}}</p>
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/jst")
|
|
||||||
public class ApiJstUiController {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ApiJstUiController.class);
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DeviceResolveService deviceResolveService;
|
|
||||||
|
|
||||||
@Value("${renren.daycard.ui.logo-url:https://daycard.oss.cn/logo.png}")
|
|
||||||
private String defaultLogoUrl;
|
|
||||||
|
|
||||||
@Value("${renren.daycard.ui.login-bg-url:https://daycard.oss.cn/login_bg.png}")
|
|
||||||
private String defaultLoginBgUrl;
|
|
||||||
|
|
||||||
@Value("${renren.daycard.ui.search-normal-url:https://daycard.oss.cn/search_normal.png}")
|
|
||||||
private String searchNormalUrl;
|
|
||||||
|
|
||||||
@Value("${renren.daycard.ui.search-focus-url:https://daycard.oss.cn/search_focus.png}")
|
|
||||||
private String searchFocusUrl;
|
|
||||||
|
|
||||||
@Value("${renren.daycard.ui.help-normal-url:https://daycard.oss.cn/help_normal.png}")
|
|
||||||
private String helpNormalUrl;
|
|
||||||
|
|
||||||
@Value("${renren.daycard.ui.help-focus-url:https://daycard.oss.cn/help_focus.png}")
|
|
||||||
private String helpFocusUrl;
|
|
||||||
|
|
||||||
// ==================== 1. 顶栏按钮 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 顶栏按钮数据:返回 logo + 搜索 + 帮助
|
|
||||||
* <p>logo 优先取设备所属渠道的 brand_logo_url,无则用全局默认</p>
|
|
||||||
*/
|
|
||||||
@PostMapping("/topButtons")
|
|
||||||
public Map<String, Object> topButtons(@RequestBody(required = false) Map<String, Object> body,
|
|
||||||
HttpServletRequest request) {
|
|
||||||
Long deviceId = deviceIdFrom(request);
|
|
||||||
String logoUrl = resolveLogoUrl(deviceId);
|
|
||||||
|
|
||||||
List<Map<String, Object>> buttons = new ArrayList<>();
|
|
||||||
|
|
||||||
// type=1 Logo(仅 back_url)
|
|
||||||
Map<String, Object> logoBtn = new HashMap<>();
|
|
||||||
logoBtn.put("type", 1);
|
|
||||||
logoBtn.put("back_url", logoUrl);
|
|
||||||
buttons.add(logoBtn);
|
|
||||||
|
|
||||||
// type=3 搜索(back_url + focus_picurl)
|
|
||||||
Map<String, Object> searchBtn = new HashMap<>();
|
|
||||||
searchBtn.put("type", 3);
|
|
||||||
searchBtn.put("back_url", searchNormalUrl);
|
|
||||||
searchBtn.put("focus_picurl", searchFocusUrl);
|
|
||||||
buttons.add(searchBtn);
|
|
||||||
|
|
||||||
// type=6 帮助(back_url + focus_picurl)
|
|
||||||
Map<String, Object> helpBtn = new HashMap<>();
|
|
||||||
helpBtn.put("type", 6);
|
|
||||||
helpBtn.put("back_url", helpNormalUrl);
|
|
||||||
helpBtn.put("focus_picurl", helpFocusUrl);
|
|
||||||
buttons.add(helpBtn);
|
|
||||||
|
|
||||||
return buildJstResponse(buttons);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 2. 筛选分类 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 筛选分类:返回栏目/类型/地区/年代四级分类
|
|
||||||
* <p>当前为固定分类数据,后续可对接橘视厅媒资 API</p>
|
|
||||||
*/
|
|
||||||
@PostMapping("/filter/tags")
|
|
||||||
public Map<String, Object> filterTags(@RequestBody(required = false) Map<String, Object> body) {
|
|
||||||
List<Map<String, Object>> series = new ArrayList<>();
|
|
||||||
|
|
||||||
// type=1 栏目
|
|
||||||
series.add(buildSeries(1, "全部栏目", new String[][]{
|
|
||||||
{"all", "全部"}, {"movie", "电影"}, {"tv", "电视剧"}, {"variety", "综艺"}, {"anime", "动漫"}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// type=2 类型
|
|
||||||
series.add(buildSeries(2, "全部类型", new String[][]{
|
|
||||||
{"all", "全部"}, {"action", "动作"}, {"comedy", "喜剧"}, {"romance", "爱情"},
|
|
||||||
{"scifi", "科幻"}, {"suspense", "悬疑"}, {"animation", "动画"}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// type=3 地区
|
|
||||||
series.add(buildSeries(3, "全部地区", new String[][]{
|
|
||||||
{"all", "全部"}, {"cn", "内地"}, {"hk", "中国香港"}, {"tw", "中国台湾"},
|
|
||||||
{"us", "美国"}, {"kr", "韩国"}, {"jp", "日本"}, {"uk", "英国"}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// type=4 年代
|
|
||||||
series.add(buildSeries(4, "全部年代", new String[][]{
|
|
||||||
{"all", "全部"}, {"2026", "2026"}, {"2025", "2025"}, {"2024", "2024"},
|
|
||||||
{"2023", "2023"}, {"2022", "2022"}, {"2021", "2021"}
|
|
||||||
}));
|
|
||||||
|
|
||||||
Map<String, Object> data = new HashMap<>();
|
|
||||||
data.put("series", series);
|
|
||||||
return buildJstResponse(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 3. 筛选内容列表 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 筛选内容列表:根据筛选条件返回内容列表
|
|
||||||
* <p>TODO: 后续对接橘视厅媒资 API 或自建媒资表,当前返回 mock 数据骨架</p>
|
|
||||||
*/
|
|
||||||
@PostMapping("/filter/content")
|
|
||||||
public Map<String, Object> filterContent(@RequestBody Map<String, Object> body) {
|
|
||||||
int page = parseInt(body.get("page"), 1);
|
|
||||||
int pageSize = parseInt(body.get("pageSize"), 20);
|
|
||||||
|
|
||||||
// TODO: 对接真实媒资数据源,当前返回 mock 数据
|
|
||||||
List<Map<String, Object>> list = new ArrayList<>();
|
|
||||||
String[][] mockContents = {
|
|
||||||
{"MOV001", "测试影片一", "https://daycard.oss.cn/poster/1.jpg", "8", "true"},
|
|
||||||
{"MOV002", "测试影片二", "https://daycard.oss.cn/poster/2.jpg", "1", "false"},
|
|
||||||
{"MOV003", "测试影片三", "https://daycard.oss.cn/poster/3.jpg", "8", "true"},
|
|
||||||
{"MOV004", "测试影片四", "https://daycard.oss.cn/poster/4.jpg", "1", "false"},
|
|
||||||
{"MOV005", "测试影片五", "https://daycard.oss.cn/poster/5.jpg", "8", "true"}
|
|
||||||
};
|
|
||||||
for (String[] item : mockContents) {
|
|
||||||
Map<String, Object> content = new HashMap<>();
|
|
||||||
content.put("contentCode", item[0]);
|
|
||||||
content.put("title", item[1]);
|
|
||||||
content.put("posterUrl", item[2]);
|
|
||||||
content.put("parentContentType", item[3]);
|
|
||||||
content.put("isFree", Boolean.parseBoolean(item[4]));
|
|
||||||
list.add(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> data = new HashMap<>();
|
|
||||||
data.put("total", 5);
|
|
||||||
data.put("currentPage", page);
|
|
||||||
data.put("totalPage", 1);
|
|
||||||
data.put("list", list);
|
|
||||||
return buildJstResponse(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 4. 独立配置接口 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 独立配置:返回 logoUrl + loginBg
|
|
||||||
* <p>logoUrl 优先取设备所属渠道的 brand_logo_url</p>
|
|
||||||
*/
|
|
||||||
@GetMapping("/config")
|
|
||||||
public Map<String, Object> config(@RequestParam(required = false) String deviceId, HttpServletRequest request) {
|
|
||||||
Long devId = deviceIdFrom(request);
|
|
||||||
if (devId == null && deviceId != null) {
|
|
||||||
try {
|
|
||||||
devId = Long.parseLong(deviceId);
|
|
||||||
} catch (NumberFormatException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String logoUrl = resolveLogoUrl(devId);
|
|
||||||
|
|
||||||
Map<String, Object> data = new HashMap<>();
|
|
||||||
data.put("logoUrl", logoUrl);
|
|
||||||
data.put("loginBg", defaultLoginBgUrl);
|
|
||||||
return buildJstResponse(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 内部方法 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从设备所属渠道解析 logoUrl,无则用全局默认
|
|
||||||
*/
|
|
||||||
private String resolveLogoUrl(Long deviceId) {
|
|
||||||
if (deviceId == null) {
|
|
||||||
return defaultLogoUrl;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
DeviceOrgContext ctx = deviceResolveService.resolveDevice(deviceId);
|
|
||||||
SysOrgEntity channelOrg = ctx.getChannelOrg();
|
|
||||||
if (channelOrg != null && channelOrg.getBrandLogoUrl() != null
|
|
||||||
&& !channelOrg.getBrandLogoUrl().trim().isEmpty()) {
|
|
||||||
return channelOrg.getBrandLogoUrl().trim();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.warn("解析设备渠道logo失败,使用默认logo: deviceId={}", deviceId, e);
|
|
||||||
}
|
|
||||||
return defaultLogoUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建筛选分类维度
|
|
||||||
*/
|
|
||||||
private Map<String, Object> buildSeries(int type, String name, String[][] items) {
|
|
||||||
Map<String, Object> series = new HashMap<>();
|
|
||||||
series.put("type", type);
|
|
||||||
series.put("name", name);
|
|
||||||
List<Map<String, String>> itemList = new ArrayList<>();
|
|
||||||
for (String[] item : items) {
|
|
||||||
Map<String, String> m = new HashMap<>();
|
|
||||||
m.put("id", item[0]);
|
|
||||||
m.put("name", item[1]);
|
|
||||||
itemList.add(m);
|
|
||||||
}
|
|
||||||
series.put("items", itemList);
|
|
||||||
return series;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建统一响应
|
|
||||||
*/
|
|
||||||
private static Map<String, Object> buildJstResponse(Object data) {
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
result.put("code", 0);
|
|
||||||
result.put("msg", "success");
|
|
||||||
result.put("data", data);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Long deviceIdFrom(HttpServletRequest request) {
|
|
||||||
return (Long) request.getAttribute(ApiDeviceSignFilter.REQUEST_ATTR_DEVICE_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int parseInt(Object val, int defaultValue) {
|
|
||||||
if (val == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(String.valueOf(val));
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,7 +26,6 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -160,33 +159,6 @@ public class PaymentController {
|
|||||||
return R.dataSuccess(data);
|
return R.dataSuccess(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Mock 支付(仅开发测试用,直接触发发卡逻辑,绕过微信支付)
|
|
||||||
* <p>用法:POST /openapi/payment/mock-pay/{orderNo}</p>
|
|
||||||
*/
|
|
||||||
@ApiOperation("Mock支付(仅开发测试)")
|
|
||||||
@AuditAction(value = "PAYMENT_MOCK", description = "Mock支付(测试)")
|
|
||||||
@PostMapping("/mock-pay/{orderNo}")
|
|
||||||
public R mockPay(@PathVariable("orderNo") String orderNo) {
|
|
||||||
DayCardOrderEntity order = dayCardOrderService.getByOrderNo(orderNo);
|
|
||||||
if (order == null) {
|
|
||||||
throw new ASException("订单不存在: " + orderNo);
|
|
||||||
}
|
|
||||||
// 设置支付渠道为 MOCK
|
|
||||||
order.setPayChannel("MOCK");
|
|
||||||
dayCardOrderService.updateById(order);
|
|
||||||
|
|
||||||
// 直接触发发卡逻辑
|
|
||||||
String mockPayNo = "MOCK_" + System.currentTimeMillis();
|
|
||||||
dayCardOrderService.payCallback(orderNo, mockPayNo, order.getAmount());
|
|
||||||
|
|
||||||
Map<String, Object> data = new HashMap<>();
|
|
||||||
data.put("orderNo", orderNo);
|
|
||||||
data.put("payNo", mockPayNo);
|
|
||||||
data.put("message", "Mock支付成功,发卡逻辑已触发");
|
|
||||||
return R.dataSuccess(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信退款回调通知
|
* 微信退款回调通知
|
||||||
* <p>无需签名鉴权,微信服务器直接调用。</p>
|
* <p>无需签名鉴权,微信服务器直接调用。</p>
|
||||||
@@ -271,14 +243,15 @@ public class PaymentController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 提取请求头(用于微信验签)
|
* 提取请求头(用于微信验签)
|
||||||
|
* <p>HttpServletRequest.getHeader() 大小写不敏感,直接按标准名称提取,
|
||||||
|
* 避免 HashMap 大小写敏感导致取不到值</p>
|
||||||
*/
|
*/
|
||||||
private Map<String, String> extractHeaders(HttpServletRequest request) {
|
private Map<String, String> extractHeaders(HttpServletRequest request) {
|
||||||
Map<String, String> headers = new HashMap<>();
|
Map<String, String> headers = new HashMap<>();
|
||||||
Enumeration<String> headerNames = request.getHeaderNames();
|
headers.put("Wechatpay-Serial", request.getHeader("Wechatpay-Serial"));
|
||||||
while (headerNames.hasMoreElements()) {
|
headers.put("Wechatpay-Nonce", request.getHeader("Wechatpay-Nonce"));
|
||||||
String name = headerNames.nextElement();
|
headers.put("Wechatpay-Signature", request.getHeader("Wechatpay-Signature"));
|
||||||
headers.put(name, request.getHeader(name));
|
headers.put("Wechatpay-Timestamp", request.getHeader("Wechatpay-Timestamp"));
|
||||||
}
|
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,6 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public interface WechatPayService {
|
public interface WechatPayService {
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查微信支付配置是否完整(可用于判断是否走 Mock 逻辑)
|
|
||||||
*/
|
|
||||||
boolean isConfigured();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建 Native 支付订单(扫码支付)
|
* 创建 Native 支付订单(扫码支付)
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -108,10 +108,10 @@ public class WechatPayServiceImpl implements WechatPayService {
|
|||||||
payAmount.setCurrency("CNY");
|
payAmount.setCurrency("CNY");
|
||||||
request.setAmount(payAmount);
|
request.setAmount(payAmount);
|
||||||
|
|
||||||
// 标记订单支持分账,支付成功后可调用分账 API
|
// 分账标记:商户号开通分账权限后取消下方注释即可启用
|
||||||
SettleInfo settleInfo = new SettleInfo();
|
// SettleInfo settleInfo = new SettleInfo();
|
||||||
settleInfo.setProfitSharing(true);
|
// settleInfo.setProfitSharing(true);
|
||||||
request.setSettleInfo(settleInfo);
|
// request.setSettleInfo(settleInfo);
|
||||||
|
|
||||||
PrepayResponse response = service.prepay(request);
|
PrepayResponse response = service.prepay(request);
|
||||||
String codeUrl = response.getCodeUrl();
|
String codeUrl = response.getCodeUrl();
|
||||||
@@ -262,10 +262,6 @@ public class WechatPayServiceImpl implements WechatPayService {
|
|||||||
if (StringUtils.isBlank(orderNo)) {
|
if (StringUtils.isBlank(orderNo)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!isConfigured()) {
|
|
||||||
logger.info("微信关单-Mock模式(未配置微信支付): orderNo={}", orderNo);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
logger.info("微信关单: orderNo={}", orderNo);
|
logger.info("微信关单: orderNo={}", orderNo);
|
||||||
try {
|
try {
|
||||||
WechatPayConfigEntity dbCfg = resolveConfigByOrderNo(orderNo);
|
WechatPayConfigEntity dbCfg = resolveConfigByOrderNo(orderNo);
|
||||||
@@ -287,11 +283,6 @@ public class WechatPayServiceImpl implements WechatPayService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WechatPayResult refund(String orderNo, String refundNo, BigDecimal refundAmount, BigDecimal totalAmount, String reason) {
|
public WechatPayResult refund(String orderNo, String refundNo, BigDecimal refundAmount, BigDecimal totalAmount, String reason) {
|
||||||
if (!isConfigured()) {
|
|
||||||
int refundFen = yuanToFen(refundAmount);
|
|
||||||
logger.info("微信退款-Mock模式: orderNo={}, refundNo={}, refundAmount={}分, reason={}", orderNo, refundNo, refundFen, reason);
|
|
||||||
return WechatPayResult.ok(orderNo, refundNo, refundAmount);
|
|
||||||
}
|
|
||||||
logger.info("微信退款: orderNo={}, refundNo={}, refundAmount={}", orderNo, refundNo, refundAmount);
|
logger.info("微信退款: orderNo={}, refundNo={}, refundAmount={}", orderNo, refundNo, refundAmount);
|
||||||
try {
|
try {
|
||||||
// TODO: 接入 wechatpay-java RefundService
|
// TODO: 接入 wechatpay-java RefundService
|
||||||
@@ -313,11 +304,6 @@ public class WechatPayServiceImpl implements WechatPayService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public WechatPayResult createProfitSharing(String transactionId, String outOrderNo, List<Map<String, Object>> receivers) {
|
public WechatPayResult createProfitSharing(String transactionId, String outOrderNo, List<Map<String, Object>> receivers) {
|
||||||
if (!isConfigured()) {
|
|
||||||
logger.info("微信分账-Mock模式: transactionId={}, outOrderNo={}, receivers={}", transactionId, outOrderNo, receivers != null ? receivers.size() : 0);
|
|
||||||
return WechatPayResult.ok(outOrderNo, outOrderNo, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
int receiverCount = receivers != null ? receivers.size() : 0;
|
int receiverCount = receivers != null ? receivers.size() : 0;
|
||||||
logger.info("微信分账: transactionId={}, outOrderNo={}, receivers={}", transactionId, outOrderNo, receiverCount);
|
logger.info("微信分账: transactionId={}, outOrderNo={}, receivers={}", transactionId, outOrderNo, receiverCount);
|
||||||
|
|
||||||
@@ -370,10 +356,6 @@ public class WechatPayServiceImpl implements WechatPayService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean finishProfitSharing(String outOrderNo, String transactionId) {
|
public boolean finishProfitSharing(String outOrderNo, String transactionId) {
|
||||||
if (!isConfigured()) {
|
|
||||||
logger.info("完结分账-Mock模式: outOrderNo={}, transactionId={}", outOrderNo, transactionId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
logger.info("完结分账: outOrderNo={}, transactionId={}", outOrderNo, transactionId);
|
logger.info("完结分账: outOrderNo={}, transactionId={}", outOrderNo, transactionId);
|
||||||
try {
|
try {
|
||||||
Config config = getOrCreateConfig();
|
Config config = getOrCreateConfig();
|
||||||
@@ -402,10 +384,6 @@ public class WechatPayServiceImpl implements WechatPayService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public WechatPayResult queryProfitSharing(String outOrderNo) {
|
public WechatPayResult queryProfitSharing(String outOrderNo) {
|
||||||
if (!isConfigured()) {
|
|
||||||
logger.info("查询分账-Mock模式: outOrderNo={}", outOrderNo);
|
|
||||||
return WechatPayResult.ok(outOrderNo, outOrderNo, null);
|
|
||||||
}
|
|
||||||
logger.info("查询分账: outOrderNo={}", outOrderNo);
|
logger.info("查询分账: outOrderNo={}", outOrderNo);
|
||||||
try {
|
try {
|
||||||
Config config = getOrCreateConfig();
|
Config config = getOrCreateConfig();
|
||||||
@@ -429,10 +407,6 @@ public class WechatPayServiceImpl implements WechatPayService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public WechatPayResult profitSharingReturn(String outOrderNo, String receiverAccount, int amount, String outReturnNo) {
|
public WechatPayResult profitSharingReturn(String outOrderNo, String receiverAccount, int amount, String outReturnNo) {
|
||||||
if (!isConfigured()) {
|
|
||||||
logger.info("分账回退-Mock模式: outOrderNo={}, receiverAccount={}, amount={}分, outReturnNo={}", outOrderNo, receiverAccount, amount, outReturnNo);
|
|
||||||
return WechatPayResult.ok(outReturnNo, outReturnNo, null);
|
|
||||||
}
|
|
||||||
logger.info("分账回退: outOrderNo={}, receiverAccount={}, amount={}分", outOrderNo, receiverAccount, amount);
|
logger.info("分账回退: outOrderNo={}, receiverAccount={}, amount={}分", outOrderNo, receiverAccount, amount);
|
||||||
try {
|
try {
|
||||||
Config config = getOrCreateConfig();
|
Config config = getOrCreateConfig();
|
||||||
@@ -460,10 +434,6 @@ public class WechatPayServiceImpl implements WechatPayService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public WechatPayResult addReceiver(String receiverType, String receiverAccount, String receiverName, String relationType) {
|
public WechatPayResult addReceiver(String receiverType, String receiverAccount, String receiverName, String relationType) {
|
||||||
if (!isConfigured()) {
|
|
||||||
logger.info("添加分账接收方-Mock模式: type={}, account={}, name={}, relation={}", receiverType, receiverAccount, receiverName, relationType);
|
|
||||||
return WechatPayResult.ok(null, "MOCK_BIND_" + System.currentTimeMillis(), null);
|
|
||||||
}
|
|
||||||
logger.info("添加分账接收方: type={}, account={}, name={}", receiverType, receiverAccount, receiverName);
|
logger.info("添加分账接收方: type={}, account={}, name={}", receiverType, receiverAccount, receiverName);
|
||||||
try {
|
try {
|
||||||
WechatPayConfigEntity dbCfg = getActiveDbConfig();
|
WechatPayConfigEntity dbCfg = getActiveDbConfig();
|
||||||
@@ -494,10 +464,6 @@ public class WechatPayServiceImpl implements WechatPayService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public WechatPayResult deleteReceiver(String receiverType, String receiverAccount) {
|
public WechatPayResult deleteReceiver(String receiverType, String receiverAccount) {
|
||||||
if (!isConfigured()) {
|
|
||||||
logger.info("删除分账接收方-Mock模式: type={}, account={}", receiverType, receiverAccount);
|
|
||||||
return WechatPayResult.ok(null, receiverAccount, null);
|
|
||||||
}
|
|
||||||
logger.info("删除分账接收方: type={}, account={}", receiverType, receiverAccount);
|
logger.info("删除分账接收方: type={}, account={}", receiverType, receiverAccount);
|
||||||
try {
|
try {
|
||||||
WechatPayConfigEntity dbCfg = getActiveDbConfig();
|
WechatPayConfigEntity dbCfg = getActiveDbConfig();
|
||||||
@@ -520,22 +486,9 @@ public class WechatPayServiceImpl implements WechatPayService {
|
|||||||
|
|
||||||
// ==================== 配置管理 ====================
|
// ==================== 配置管理 ====================
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConfigured() {
|
|
||||||
try {
|
|
||||||
validateConfig();
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验微信支付配置是否完整(DB 配置优先,回退 yml 环境变量)
|
* 校验微信支付配置是否完整(DB 配置优先,回退 yml 环境变量)
|
||||||
*/
|
*/
|
||||||
private void validateConfig() {
|
|
||||||
validateConfig(null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateConfig(Long channelOrgId, Long agentOrgId, Long storeOrgId) {
|
private void validateConfig(Long channelOrgId, Long agentOrgId, Long storeOrgId) {
|
||||||
WechatPayConfigEntity dbCfg = getActiveDbConfig(channelOrgId, agentOrgId, storeOrgId);
|
WechatPayConfigEntity dbCfg = getActiveDbConfig(channelOrgId, agentOrgId, storeOrgId);
|
||||||
|
|||||||
@@ -70,14 +70,6 @@ renren:
|
|||||||
card-valid-hours: ${DAYCARD_VALID_HOURS:24}
|
card-valid-hours: ${DAYCARD_VALID_HOURS:24}
|
||||||
# 超时取消任务每次处理数量
|
# 超时取消任务每次处理数量
|
||||||
cancel-batch-size: ${DAYCARD_CANCEL_BATCH:200}
|
cancel-batch-size: ${DAYCARD_CANCEL_BATCH:200}
|
||||||
# App UI 资源配置(顶栏按钮图标、登录背景图)
|
|
||||||
ui:
|
|
||||||
logo-url: ${UI_LOGO_URL:https://daycard.oss-cn-hangzhou.aliyuncs.com/logo.png}
|
|
||||||
login-bg-url: ${UI_LOGIN_BG_URL:https://daycard.oss-cn-hangzhou.aliyuncs.com/login_bg.png}
|
|
||||||
search-normal-url: ${UI_SEARCH_NORMAL:https://daycard.oss-cn-hangzhou.aliyuncs.com/search_normal.png}
|
|
||||||
search-focus-url: ${UI_SEARCH_FOCUS:https://daycard.oss-cn-hangzhou.aliyuncs.com/search_focus.png}
|
|
||||||
help-normal-url: ${UI_HELP_NORMAL:https://daycard.oss-cn-hangzhou.aliyuncs.com/help_normal.png}
|
|
||||||
help-focus-url: ${UI_HELP_FOCUS:https://daycard.oss-cn-hangzhou.aliyuncs.com/help_focus.png}
|
|
||||||
# 微信支付配置
|
# 微信支付配置
|
||||||
wechat:
|
wechat:
|
||||||
pay:
|
pay:
|
||||||
|
|||||||
@@ -7,13 +7,8 @@
|
|||||||
<select id="selectOneForUpdateSkipLocked" resultType="io.as.modules.daycard.entity.DayCardPoolEntity">
|
<select id="selectOneForUpdateSkipLocked" resultType="io.as.modules.daycard.entity.DayCardPoolEntity">
|
||||||
SELECT * FROM day_card_pool
|
SELECT * FROM day_card_pool
|
||||||
WHERE delete_flag = 'UNDEL'
|
WHERE delete_flag = 'UNDEL'
|
||||||
AND (
|
AND status = 'UNUSED'
|
||||||
status = 'UNUSED'
|
ORDER BY id ASC
|
||||||
OR (status = 'REUSABLE' AND expire_at > NOW())
|
|
||||||
)
|
|
||||||
ORDER BY
|
|
||||||
CASE WHEN status = 'REUSABLE' THEN 0 ELSE 1 END,
|
|
||||||
id ASC
|
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
FOR UPDATE SKIP LOCKED
|
FOR UPDATE SKIP LOCKED
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
Reference in New Issue
Block a user