From 8385196b384f9d5f62fb3b9144b51e1322a92a13 Mon Sep 17 00:00:00 2001 From: zhaogd Date: Thu, 6 Aug 2026 13:43:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=A7=BB=E9=99=A4mock=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E3=80=81=E5=8D=A1=E4=B8=8D=E5=8F=AF=E5=A4=8D=E7=94=A8?= =?UTF-8?q?=E3=80=81=E6=96=B0=E5=A2=9E=E8=AE=BE=E5=A4=87=E7=AB=AF=E5=8F=96?= =?UTF-8?q?=E6=B6=88=E8=AE=A2=E5=8D=95=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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配置 --- .../daycard/constants/DayCardConstants.java | 2 - .../controller/DayCardManageController.java | 2 +- .../daycard/entity/DayCardOrderEntity.java | 2 +- .../daycard/service/DayCardOrderService.java | 11 +- .../service/impl/DayCardOrderServiceImpl.java | 122 ++++----- .../impl/DayCardRefundServiceImpl.java | 32 +-- .../openapi/controller/ApiJstController.java | 22 ++ .../controller/ApiJstUiController.java | 253 ------------------ .../payment/controller/PaymentController.java | 39 +-- .../payment/service/WechatPayService.java | 5 - .../service/impl/WechatPayServiceImpl.java | 55 +--- src/main/resources/application.yml | 8 - .../mapper/daycard/DayCardPoolDao.xml | 9 +- 13 files changed, 100 insertions(+), 462 deletions(-) delete mode 100644 src/main/java/io/as/modules/openapi/controller/ApiJstUiController.java diff --git a/src/main/java/io/as/modules/daycard/constants/DayCardConstants.java b/src/main/java/io/as/modules/daycard/constants/DayCardConstants.java index d64ab23..95c394a 100644 --- a/src/main/java/io/as/modules/daycard/constants/DayCardConstants.java +++ b/src/main/java/io/as/modules/daycard/constants/DayCardConstants.java @@ -159,8 +159,6 @@ public final class DayCardConstants { 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"; } diff --git a/src/main/java/io/as/modules/daycard/controller/DayCardManageController.java b/src/main/java/io/as/modules/daycard/controller/DayCardManageController.java index 3000f42..6edfca2 100644 --- a/src/main/java/io/as/modules/daycard/controller/DayCardManageController.java +++ b/src/main/java/io/as/modules/daycard/controller/DayCardManageController.java @@ -125,7 +125,7 @@ public class DayCardManageController { /** * 解绑设备天卡权益(Web端手动操作) - *

将设备绑定的 USED 卡转为 REUSABLE,释放设备。橘视厅权益保留,后续新设备下单可复用此卡。

+ *

清除卡上的 device_id 绑定,卡状态保持 USED 不变,不可被其他设备复用。

*/ @AuditAction(value = "DAYCARD_DEVICE_UNBIND", description = "解绑设备天卡权益") @PostMapping("/device/unbind") diff --git a/src/main/java/io/as/modules/daycard/entity/DayCardOrderEntity.java b/src/main/java/io/as/modules/daycard/entity/DayCardOrderEntity.java index ab87c05..91390a8 100644 --- a/src/main/java/io/as/modules/daycard/entity/DayCardOrderEntity.java +++ b/src/main/java/io/as/modules/daycard/entity/DayCardOrderEntity.java @@ -56,7 +56,7 @@ public class DayCardOrderEntity implements Serializable { private Date createdAt; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date updatedAt; - /** 支付渠道 MOCK/WECHAT */ + /** 支付渠道 WECHAT */ private String payChannel; /** 微信预支付ID */ private String prepayId; diff --git a/src/main/java/io/as/modules/daycard/service/DayCardOrderService.java b/src/main/java/io/as/modules/daycard/service/DayCardOrderService.java index 982125a..217cbc5 100644 --- a/src/main/java/io/as/modules/daycard/service/DayCardOrderService.java +++ b/src/main/java/io/as/modules/daycard/service/DayCardOrderService.java @@ -66,6 +66,15 @@ public interface DayCardOrderService extends IService { */ DayCardOrderEntity getByOrderNo(String orderNo); + /** + * 设备端取消未支付订单 + *

校验订单归属设备后,关单微信支付、释放卡、更新订单CANCELLED、清理Redis锁

+ * + * @param orderNo 订单号 + * @param deviceId 设备ID(从 sign 解析,校验订单归属) + */ + void cancelOrder(String orderNo, Long deviceId); + /** * 查询设备的天卡权益(激活后 App 获取播放凭证) *

从 day_card_pool 表查询 status=USED 且 expire_at > NOW() 的卡,返回 thirdCardNo 和 expireAt

@@ -86,7 +95,7 @@ public interface DayCardOrderService extends IService { /** * 解绑设备天卡权益(Web端手动操作) - *

将 USED 卡转为 REUSABLE,释放设备绑定,保留橘视厅权益供后续设备复用

+ *

清除卡上的 device_id 绑定,卡状态保持 USED 不变,不可被其他设备复用

* * @param deviceId 设备ID */ diff --git a/src/main/java/io/as/modules/daycard/service/impl/DayCardOrderServiceImpl.java b/src/main/java/io/as/modules/daycard/service/impl/DayCardOrderServiceImpl.java index 5da3d37..d46c51a 100644 --- a/src/main/java/io/as/modules/daycard/service/impl/DayCardOrderServiceImpl.java +++ b/src/main/java/io/as/modules/daycard/service/impl/DayCardOrderServiceImpl.java @@ -165,7 +165,7 @@ public class DayCardOrderServiceImpl extends ServiceImpl payUpdate = new UpdateWrapper<>(); - 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 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 payUpdate = new UpdateWrapper<>(); - payUpdate.eq("id", order.getId()) - .set("code_url", order.getCodeUrl()) - .set("pay_channel", "MOCK"); - baseMapper.update(null, payUpdate); - } + String codeUrl = wechatPayService.createNativePay(orderNo, "天卡订单", amount, channelOrgId, agentOrgId, storeOrgId); + order.setCodeUrl(codeUrl); + UpdateWrapper payUpdate = new UpdateWrapper<>(); + payUpdate.eq("id", order.getId()) + .set("code_url", codeUrl); + baseMapper.update(null, payUpdate); // 8. SKIP LOCKED 抢卡(通用池,不按 partner 过滤) DayCardPoolEntity pool = dayCardPoolDao.selectOneForUpdateSkipLocked(); @@ -219,7 +196,7 @@ public class DayCardOrderServiceImpl extends ServiceImpl RESERVED UpdateWrapper poolUpdate = new UpdateWrapper<>(); 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) .set("status", DayCardConstants.POOL_STATUS_RESERVED) .set("order_no", orderNo) @@ -298,7 +275,7 @@ public class DayCardOrderServiceImpl extends ServiceImpl payUpdate = new UpdateWrapper<>(); - 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 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 payUpdate = new UpdateWrapper<>(); - payUpdate.eq("id", order.getId()) - .set("code_url", order.getCodeUrl()) - .set("pay_channel", "MOCK"); - baseMapper.update(null, payUpdate); - } + String codeUrl = wechatPayService.createNativePay(orderNo, "天卡订单", amount, channelOrgId, agentOrgId, storeOrgId); + order.setCodeUrl(codeUrl); + UpdateWrapper payUpdate = new UpdateWrapper<>(); + payUpdate.eq("id", order.getId()) + .set("code_url", codeUrl); + baseMapper.update(null, payUpdate); // 7. SKIP LOCKED 抢卡 DayCardPoolEntity pool = dayCardPoolDao.selectOneForUpdateSkipLocked(); @@ -355,7 +308,7 @@ public class DayCardOrderServiceImpl extends ServiceImpl RESERVED UpdateWrapper poolUpdate = new UpdateWrapper<>(); 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) .set("status", DayCardConstants.POOL_STATUS_RESERVED) .set("order_no", orderNo) @@ -444,12 +397,12 @@ public class DayCardOrderServiceImpl extends ServiceImpl orderUpdate = new UpdateWrapper<>(); 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("cancel_time", now) .set("updated_at", now); @@ -672,6 +625,29 @@ public class DayCardOrderServiceImpl extends ServiceImpl wrapper = new QueryWrapper<>(); @@ -706,7 +682,6 @@ public class DayCardOrderServiceImpl extends ServiceImpl update = new UpdateWrapper<>(); update.eq("id", pool.getId()) .eq("status", DayCardConstants.POOL_STATUS_USED) - .set("status", DayCardConstants.POOL_STATUS_REUSABLE) .set("device_id", null) .set("updated_at", new Date()); int updated = dayCardPoolDao.update(null, update); @@ -723,17 +698,16 @@ public class DayCardOrderServiceImpl extends ServiceImpl REUSABLE,清卡上的 device_id + // 1. 解绑设备当前天卡权益(若有):清除 device_id,卡状态保持 USED 不变 DayCardPoolEntity pool = getDeviceBenefits(deviceId); if (pool != null) { UpdateWrapper poolUpdate = new UpdateWrapper<>(); poolUpdate.eq("id", pool.getId()) .eq("status", DayCardConstants.POOL_STATUS_USED) - .set("status", DayCardConstants.POOL_STATUS_REUSABLE) .set("device_id", null) .set("updated_at", now); dayCardPoolDao.update(null, poolUpdate); - logger.info("设备组织解绑-释放天卡权益: deviceId={}, poolId={}, cardCode={}", + logger.info("设备组织解绑-解除天卡权益绑定: deviceId={}, poolId={}, cardCode={}", deviceId, pool.getId(), pool.getCardCode()); } // 2. 清除设备的门店组织关联(storeOrgId),设备可在新场所用新激活码重新绑定 diff --git a/src/main/java/io/as/modules/daycard/service/impl/DayCardRefundServiceImpl.java b/src/main/java/io/as/modules/daycard/service/impl/DayCardRefundServiceImpl.java index 60f12a7..20823e0 100644 --- a/src/main/java/io/as/modules/daycard/service/impl/DayCardRefundServiceImpl.java +++ b/src/main/java/io/as/modules/daycard/service/impl/DayCardRefundServiceImpl.java @@ -157,18 +157,13 @@ public class DayCardRefundServiceImpl extends ServiceImplMock 支付的 payNo 以 "MOCK_" 开头,其余视为微信支付

- * - * @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位随机数 */ diff --git a/src/main/java/io/as/modules/openapi/controller/ApiJstController.java b/src/main/java/io/as/modules/openapi/controller/ApiJstController.java index d12af24..1eca4e4 100644 --- a/src/main/java/io/as/modules/openapi/controller/ApiJstController.java +++ b/src/main/java/io/as/modules/openapi/controller/ApiJstController.java @@ -195,6 +195,28 @@ public class ApiJstController { return buildSuccessResponse(data); } + /** + * 取消天卡订单(用户放弃支付时调用) + */ + @ApiOperation(value = "取消天卡订单", + notes = "请求头必传 sign。用户取消支付时调用,取消订单并释放卡,允许设备重新下单。") + @ApiImplicitParams({ + @ApiImplicitParam(name = "sign", value = "设备签名", required = true, paramType = "header", dataType = "string") + }) + @PostMapping("/daycard/cancel/{orderNo}") + public Map cancelDayCard(@PathVariable("orderNo") String orderNo, HttpServletRequest request) { + try { + Long deviceId = deviceIdFrom(request); + dayCardOrderService.cancelOrder(orderNo, deviceId); + Map data = new HashMap<>(); + data.put("orderNo", orderNo); + data.put("status", "CANCELLED"); + return buildSuccessResponse(data); + } catch (ASException e) { + return rrExceptionResponse(e); + } + } + /** * 设备心跳:更新设备心跳时间,续期 sign */ diff --git a/src/main/java/io/as/modules/openapi/controller/ApiJstUiController.java b/src/main/java/io/as/modules/openapi/controller/ApiJstUiController.java deleted file mode 100644 index bfc4ee1..0000000 --- a/src/main/java/io/as/modules/openapi/controller/ApiJstUiController.java +++ /dev/null @@ -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 数据接口 - *

提供顶栏按钮、筛选分类、筛选内容列表、独立配置等 App 展示所需数据

- *

所有接口均需请求头 sign(由 ApiDeviceSignFilter 校验)

- *

返回格式:{code:0, msg:"success", data:{...}}

- */ -@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 + 搜索 + 帮助 - *

logo 优先取设备所属渠道的 brand_logo_url,无则用全局默认

- */ - @PostMapping("/topButtons") - public Map topButtons(@RequestBody(required = false) Map body, - HttpServletRequest request) { - Long deviceId = deviceIdFrom(request); - String logoUrl = resolveLogoUrl(deviceId); - - List> buttons = new ArrayList<>(); - - // type=1 Logo(仅 back_url) - Map logoBtn = new HashMap<>(); - logoBtn.put("type", 1); - logoBtn.put("back_url", logoUrl); - buttons.add(logoBtn); - - // type=3 搜索(back_url + focus_picurl) - Map 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 helpBtn = new HashMap<>(); - helpBtn.put("type", 6); - helpBtn.put("back_url", helpNormalUrl); - helpBtn.put("focus_picurl", helpFocusUrl); - buttons.add(helpBtn); - - return buildJstResponse(buttons); - } - - // ==================== 2. 筛选分类 ==================== - - /** - * 筛选分类:返回栏目/类型/地区/年代四级分类 - *

当前为固定分类数据,后续可对接橘视厅媒资 API

- */ - @PostMapping("/filter/tags") - public Map filterTags(@RequestBody(required = false) Map body) { - List> 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 data = new HashMap<>(); - data.put("series", series); - return buildJstResponse(data); - } - - // ==================== 3. 筛选内容列表 ==================== - - /** - * 筛选内容列表:根据筛选条件返回内容列表 - *

TODO: 后续对接橘视厅媒资 API 或自建媒资表,当前返回 mock 数据骨架

- */ - @PostMapping("/filter/content") - public Map filterContent(@RequestBody Map body) { - int page = parseInt(body.get("page"), 1); - int pageSize = parseInt(body.get("pageSize"), 20); - - // TODO: 对接真实媒资数据源,当前返回 mock 数据 - List> 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 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 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 - *

logoUrl 优先取设备所属渠道的 brand_logo_url

- */ - @GetMapping("/config") - public Map 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 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 buildSeries(int type, String name, String[][] items) { - Map series = new HashMap<>(); - series.put("type", type); - series.put("name", name); - List> itemList = new ArrayList<>(); - for (String[] item : items) { - Map 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 buildJstResponse(Object data) { - Map 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; - } - } -} diff --git a/src/main/java/io/as/modules/payment/controller/PaymentController.java b/src/main/java/io/as/modules/payment/controller/PaymentController.java index 9d7dec0..19fbe5e 100644 --- a/src/main/java/io/as/modules/payment/controller/PaymentController.java +++ b/src/main/java/io/as/modules/payment/controller/PaymentController.java @@ -26,7 +26,6 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.math.BigDecimal; -import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @@ -160,33 +159,6 @@ public class PaymentController { return R.dataSuccess(data); } - /** - * Mock 支付(仅开发测试用,直接触发发卡逻辑,绕过微信支付) - *

用法:POST /openapi/payment/mock-pay/{orderNo}

- */ - @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 data = new HashMap<>(); - data.put("orderNo", orderNo); - data.put("payNo", mockPayNo); - data.put("message", "Mock支付成功,发卡逻辑已触发"); - return R.dataSuccess(data); - } - /** * 微信退款回调通知 *

无需签名鉴权,微信服务器直接调用。

@@ -271,14 +243,15 @@ public class PaymentController { /** * 提取请求头(用于微信验签) + *

HttpServletRequest.getHeader() 大小写不敏感,直接按标准名称提取, + * 避免 HashMap 大小写敏感导致取不到值

*/ private Map extractHeaders(HttpServletRequest request) { Map headers = new HashMap<>(); - Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String name = headerNames.nextElement(); - headers.put(name, request.getHeader(name)); - } + headers.put("Wechatpay-Serial", request.getHeader("Wechatpay-Serial")); + headers.put("Wechatpay-Nonce", request.getHeader("Wechatpay-Nonce")); + headers.put("Wechatpay-Signature", request.getHeader("Wechatpay-Signature")); + headers.put("Wechatpay-Timestamp", request.getHeader("Wechatpay-Timestamp")); return headers; } } diff --git a/src/main/java/io/as/modules/payment/service/WechatPayService.java b/src/main/java/io/as/modules/payment/service/WechatPayService.java index d9e717d..4ea7251 100644 --- a/src/main/java/io/as/modules/payment/service/WechatPayService.java +++ b/src/main/java/io/as/modules/payment/service/WechatPayService.java @@ -9,11 +9,6 @@ import java.util.Map; */ public interface WechatPayService { - /** - * 检查微信支付配置是否完整(可用于判断是否走 Mock 逻辑) - */ - boolean isConfigured(); - /** * 创建 Native 支付订单(扫码支付) * diff --git a/src/main/java/io/as/modules/payment/service/impl/WechatPayServiceImpl.java b/src/main/java/io/as/modules/payment/service/impl/WechatPayServiceImpl.java index 9637fe4..9d99792 100644 --- a/src/main/java/io/as/modules/payment/service/impl/WechatPayServiceImpl.java +++ b/src/main/java/io/as/modules/payment/service/impl/WechatPayServiceImpl.java @@ -108,10 +108,10 @@ public class WechatPayServiceImpl implements WechatPayService { payAmount.setCurrency("CNY"); request.setAmount(payAmount); - // 标记订单支持分账,支付成功后可调用分账 API - SettleInfo settleInfo = new SettleInfo(); - settleInfo.setProfitSharing(true); - request.setSettleInfo(settleInfo); + // 分账标记:商户号开通分账权限后取消下方注释即可启用 + // SettleInfo settleInfo = new SettleInfo(); + // settleInfo.setProfitSharing(true); + // request.setSettleInfo(settleInfo); PrepayResponse response = service.prepay(request); String codeUrl = response.getCodeUrl(); @@ -262,10 +262,6 @@ public class WechatPayServiceImpl implements WechatPayService { if (StringUtils.isBlank(orderNo)) { return false; } - if (!isConfigured()) { - logger.info("微信关单-Mock模式(未配置微信支付): orderNo={}", orderNo); - return true; - } logger.info("微信关单: orderNo={}", orderNo); try { WechatPayConfigEntity dbCfg = resolveConfigByOrderNo(orderNo); @@ -287,11 +283,6 @@ public class WechatPayServiceImpl implements WechatPayService { @Override 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); try { // TODO: 接入 wechatpay-java RefundService @@ -313,11 +304,6 @@ public class WechatPayServiceImpl implements WechatPayService { */ @Override public WechatPayResult createProfitSharing(String transactionId, String outOrderNo, List> 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; logger.info("微信分账: transactionId={}, outOrderNo={}, receivers={}", transactionId, outOrderNo, receiverCount); @@ -370,10 +356,6 @@ public class WechatPayServiceImpl implements WechatPayService { */ @Override public boolean finishProfitSharing(String outOrderNo, String transactionId) { - if (!isConfigured()) { - logger.info("完结分账-Mock模式: outOrderNo={}, transactionId={}", outOrderNo, transactionId); - return true; - } logger.info("完结分账: outOrderNo={}, transactionId={}", outOrderNo, transactionId); try { Config config = getOrCreateConfig(); @@ -402,10 +384,6 @@ public class WechatPayServiceImpl implements WechatPayService { */ @Override public WechatPayResult queryProfitSharing(String outOrderNo) { - if (!isConfigured()) { - logger.info("查询分账-Mock模式: outOrderNo={}", outOrderNo); - return WechatPayResult.ok(outOrderNo, outOrderNo, null); - } logger.info("查询分账: outOrderNo={}", outOrderNo); try { Config config = getOrCreateConfig(); @@ -429,10 +407,6 @@ public class WechatPayServiceImpl implements WechatPayService { */ @Override 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); try { Config config = getOrCreateConfig(); @@ -460,10 +434,6 @@ public class WechatPayServiceImpl implements WechatPayService { */ @Override 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); try { WechatPayConfigEntity dbCfg = getActiveDbConfig(); @@ -494,10 +464,6 @@ public class WechatPayServiceImpl implements WechatPayService { */ @Override 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); try { 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 环境变量) */ - private void validateConfig() { - validateConfig(null, null, null); - } private void validateConfig(Long channelOrgId, Long agentOrgId, Long storeOrgId) { WechatPayConfigEntity dbCfg = getActiveDbConfig(channelOrgId, agentOrgId, storeOrgId); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index fb37cac..856d99d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -70,14 +70,6 @@ renren: card-valid-hours: ${DAYCARD_VALID_HOURS:24} # 超时取消任务每次处理数量 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: pay: diff --git a/src/main/resources/mapper/daycard/DayCardPoolDao.xml b/src/main/resources/mapper/daycard/DayCardPoolDao.xml index 57edac8..56a8ea7 100644 --- a/src/main/resources/mapper/daycard/DayCardPoolDao.xml +++ b/src/main/resources/mapper/daycard/DayCardPoolDao.xml @@ -7,13 +7,8 @@