feat(order): 普通支付交易订单管理(admin视角): NormalPayOrder+PayTrade 列表/详情/同步/关闭

This commit is contained in:
DaxPay Dev
2026-07-02 02:23:22 +08:00
parent 6fac32d596
commit 9208422abd
15 changed files with 868 additions and 103 deletions

View File

@@ -96,7 +96,7 @@ INSERT INTO "public"."iam_perm_menu" VALUES (304, 3, 'system:config', 'admin', '
INSERT INTO "public"."iam_perm_menu" VALUES (30701, 307, 'iam:online:user', 'admin', 'OnlineUser', '在线用户', 'Online User', 'menu.system.monitor.online', 'lucide:users', 'f', 'f', '/system/monitor/online/OnlineUserList', '/system/monitor/online', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-04-11 16:00:00+00', '2026-04-11 16:00:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (401, 4, 'payment:platform', 'admin', 'PaymentPlatform', '支付主数据', 'Payment Master Data', 'menu.payment.platform', 'lucide:building', 'f', 'f', NULL, '/payment/platform', NULL, 1, 'f', 't', 'f', 0, 1, 3, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-04-05 16:00:00+00', '2026-05-28 06:25:42.880461+00');
INSERT INTO "public"."iam_perm_menu" VALUES (203, 2, 'demos:region', 'admin', 'RegionCascaderDemo', '行政区划选择器', 'Region Cascader', 'menu.demos.region', 'lucide:map-pin', 'f', 'f', '/demos/region/RegionCascaderDemo', '/demos/region', NULL, 3, 'f', 't', 'f', 0, NULL, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-04-24 16:00:00+00', '2026-04-24 16:00:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (6, NULL, 'trade', 'admin', 'TransactionManagement', '交易管理', 'Transaction Management', 'menu.trade', 'lucide:arrow-left-right', 'f', 'f', NULL, '/trade', '/trade/index', 3.5, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-05-24 16:00:00+00', '2026-05-24 16:00:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (6, NULL, 'trade', 'admin', 'TransactionManagement', '交易管理', 'Transaction Management', 'menu.trade', 'lucide:arrow-left-right', 'f', 'f', NULL, '/trade', '/trade/normal-pay', 3.5, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-05-24 16:00:00+00', '2026-05-24 16:00:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (3, NULL, NULL, 'admin', 'System', '系统管理', 'System', 'menu.system', 'lucide:sliders-horizontal', 'f', 'f', NULL, '/system', NULL, 0, 'f', 't', 'f', 0, 1, 2, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-03-20 03:11:13.134079+00', '2026-06-25 02:00:30.22348+00');
INSERT INTO "public"."iam_perm_menu" VALUES (301, 3, NULL, 'admin', 'SystemBasic', '基础数据', 'Basic Data', 'menu.system.basic', 'lucide:boxes', 'f', 'f', NULL, '/system/basic', NULL, 1, 'f', 't', 'f', 0, NULL, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-03-20 03:11:13.134079+00', '2026-06-25 02:00:30.236321+00');
INSERT INTO "public"."iam_perm_menu" VALUES (30101, 301, 'system:dict', 'admin', 'SystemDict', '字典管理', 'Dictionary', 'menu.system.basic.dict', 'lucide:book-open', 'f', 'f', '/system/basic/dict/DictList', '/system/basic/dict', NULL, 1, 'f', 't', 'f', 0, 1, 2, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-03-20 03:11:13.134079+00', '2026-06-25 02:00:30.243488+00');
@@ -114,7 +114,8 @@ INSERT INTO "public"."iam_perm_menu" VALUES (101, 1, 'dashboard:analytics', 'adm
INSERT INTO "public"."iam_perm_menu" VALUES (30103, 305, 'iam:role', 'admin', 'SystemRole', '角色管理', 'Role Management', 'menu.system.perm.role', 'lucide:shield-user', 'f', 'f', '/iam/perm/role/RoleList', '/iam/perm/role', NULL, 3, 'f', 't', 'f', 0, 1, 2, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-03-20 03:11:13.134079+00', '2026-06-25 02:00:30.246504+00');
INSERT INTO "public"."iam_perm_menu" VALUES (30501, 305, 'iam:user:manager', 'admin', 'UserList', '用户管理', 'User Management', 'menu.system.perm.user', 'lucide:users-round', 'f', 'f', 'views/iam/user/UserList', '/iam/user', NULL, 10, 'f', 't', 'f', 1, 1, 4, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-03-31 00:23:04.37507+00', '2026-06-25 02:00:30.279649+00');
INSERT INTO "public"."iam_perm_menu" VALUES (30102, 301, 'iam:perm:menu', 'admin', 'SystemMenu', '菜单管理', 'Menu Management', 'menu.system.perm.menu', 'lucide:panel-top', 'f', 'f', '/iam/perm/menu/MenuList', '/system/basic/menu', NULL, 0, 'f', 't', 'f', 0, 1, 3, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-03-20 03:11:13.134079+00', '2026-06-27 10:29:51.371435+00');
INSERT INTO "public"."iam_perm_menu" VALUES (601, 6, NULL, 'admin', 'TransactionIndex', '功能开发中', 'Coming Soon', 'menu.trade.index', 'lucide:construction', 'f', 'f', '/_core/fallback/coming-soon', '/trade/index', NULL, 1, 'f', 'f', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-05-24 16:00:00+00', '2026-05-24 16:00:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (601, 6, 'payment:order', 'admin', 'NormalOrderList', '支付订单', 'Payment Orders', 'menu.trade.normalPay', 'lucide:receipt', 'f', 'f', '/payment/order/NormalOrderList', '/trade/normal-pay', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-05-24 16:00:00+00', '2026-07-01 16:00:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (602, 6, 'payment:trade', 'admin', 'PayTradeList', '资金交易', 'Fund Trades', 'menu.trade.payTrade', 'lucide:arrow-left-right', 'f', 'f', '/payment/order/PayTradeList', '/trade/pay-trade', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-01 16:00:00+00', '2026-07-01 16:00:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (40104, 401, 'payment:platform:capability', 'admin', 'PayCapabilityList', '支付能力', 'Payment Capability', 'menu.payment.platform.capability', 'lucide:zap', 'f', 'f', '/payment/masterdata/capability/PayCapabilityList', '/payment/platform/pay-capability', NULL, 3, 'f', 't', 'f', 1, 1, 4, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-05-26 16:00:00+00', '2026-05-28 06:43:27.505831+00');
INSERT INTO "public"."iam_perm_menu" VALUES (4040111, 40401, 'merchant:app:route', 'admin', 'PayRouteConfig', '通道路由', 'Channel Routing', 'menu.payment.merchant.app.payRoute', NULL, 't', 'f', '/payment/merchant/route/PayRouteConfig', '/payment/merchant/route', NULL, 2, 'f', 't', 'f', 1, 1, 1, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-05-24 22:23:46.483985+00', '2026-05-26 20:03:06.445803+00');
INSERT INTO "public"."iam_perm_menu" VALUES (40102, 401, 'payment:platform:provider', 'admin', 'PayProviderList', '支付方式', 'Payment Method', 'menu.payment.platform.provider', 'lucide:list-tree', 'f', 'f', '/payment/masterdata/provider/PayMethodList', '/payment/platform/pay-method', NULL, 1, 'f', 't', 'f', 1, 1, 5, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-05-27 20:18:01.383008+00', '2026-05-30 08:21:19.464024+00');

View File

@@ -1,100 +0,0 @@
-- 设备管理菜单: iot → device 命名修正 + 目录层级合并(去除云音箱/云打印中间目录层)
-- 执行顺序: update-tables.sql -> update-datas.sql
-- 基于现有 iam_perm_menu 记录 UPDATE/DELETE(菜单已存在, 非新增)
-- 1. 设备管理 根目录 (id=9)
UPDATE "iam_perm_menu" SET
menu_code = 'device',
name = 'DeviceManagement',
i18n_key = 'menu.device',
path = '/device',
last_modified_time = now()
WHERE id = 9;
-- 2. 码牌 (id=901)
UPDATE "iam_perm_menu" SET
menu_code = 'device:qrcode',
name = 'DeviceQrCode',
i18n_key = 'menu.device.qrcode',
path = '/device/qrcode',
last_modified_time = now()
WHERE id = 901;
-- 3. 删除 云音箱 目录 (id=902)
-- 去除中间目录层, 子菜单 90202 提升为顶层并继承 title/key/path
DELETE FROM "iam_perm_menu" WHERE id = 902;
-- 4. 删除 云打印 目录 (id=903)
-- 去除中间目录层, 子菜单 90302 提升为顶层并继承 title/key/path
DELETE FROM "iam_perm_menu" WHERE id = 903;
-- 5. 设备厂商管理 (id=90201) 提升为设备管理顶层(pid 902→9)
-- 厂商配置为平台级通用表(device_vendor_config), 统一入口, 不再按设备类型分散
UPDATE "iam_perm_menu" SET
pid = 9,
menu_code = 'payment:device:vendorConfig',
name = 'VendorManage',
title_cn = '设备厂商管理',
title_en = 'Device Vendor Management',
i18n_key = 'menu.device.vendor',
component = '/payment/device/vendor/VendorManage',
path = '/device/vendor',
sort_no = 4,
last_modified_time = now()
WHERE id = 90201;
-- 6. 云音箱 (id=90202) 提升为顶层, 合并原目录 title/key/path
-- 原: pid=902, title=云音箱管理, key=menu.device.speaker.manage, path=/device/speaker/manage
-- 现: pid=9, title=云音箱, key=menu.device.speaker, path=/device/speaker
UPDATE "iam_perm_menu" SET
pid = 9,
menu_code = 'payment:device:speaker',
name = 'DeviceSpeakerList',
title_cn = '云音箱',
title_en = 'Cloud Speaker',
i18n_key = 'menu.device.speaker',
component = '/payment/device/speaker/DeviceSpeakerList',
path = '/device/speaker',
sort_no = 2,
last_modified_time = now()
WHERE id = 90202;
-- 7. 云打印 (id=90302) 提升为顶层, 合并原目录 title/key/path, 并接入已实现的列表组件
-- 原: pid=903, title=云打印管理, key=menu.device.printer.manage, path=/device/printer/device
-- 现: pid=9, title=云打印, key=menu.device.printer, path=/device/printer
UPDATE "iam_perm_menu" SET
pid = 9,
menu_code = 'payment:device:printer',
name = 'DevicePrinterList',
title_cn = '云打印',
title_en = 'Cloud Printer',
i18n_key = 'menu.device.printer',
component = '/payment/device/printer/DevicePrinterList',
path = '/device/printer',
sort_no = 3,
last_modified_time = now()
WHERE id = 90302;
-- 8. 删除 云打印 > 厂商管理 (id=90301)
-- 厂商配置统一到顶层 90201, 各设备类型下不再单设厂商管理入口
DELETE FROM "iam_perm_menu" WHERE id = 90301;
-- 9. Dashboard 目录 (id=1): 默认进入工作台 (/analytics → /workspace)
UPDATE "iam_perm_menu" SET
redirect = '/workspace',
last_modified_time = now()
WHERE id = 1;
-- 10. 工作台 (id=102): 补菜单编码占位 + 排序提前到分析页之前
UPDATE "iam_perm_menu" SET
menu_code = 'dashboard:workspace',
sort_no = 1,
last_modified_time = now()
WHERE id = 102;
-- 11. 分析页 (id=101): 补菜单编码占位 + 排序后移到工作台之后
UPDATE "iam_perm_menu" SET
menu_code = 'dashboard:analytics',
sort_no = 2,
last_modified_time = now()
WHERE id = 101;

View File

@@ -0,0 +1,68 @@
package cn.daxpay.open.payment.admin.controller.order;
import cn.daxpay.open.platform.core.annotation.PermCode;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import cn.daxpay.open.platform.core.rest.result.Result;
import cn.daxpay.open.payment.pay.param.order.NormalPayOrderQuery;
import cn.daxpay.open.payment.pay.result.order.NormalPayOrderResult;
import cn.daxpay.open.payment.pay.service.admin.NormalPayOrderAdminService;
import cn.daxpay.open.payment.unipay.result.trade.pay.NormalPaySyncResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/// # 普通支付业务单(管理)
///
/// 面向运营/商户后台的业务订单(容器)管理: 分页查询、详情、状态同步、关闭/撤销
@PermCode(menuCode = "payment:order")
@Validated
@Tag(name = "普通支付业务单(管理)")
@RestController
@RequestMapping("/admin/order/normal-pay")
@RequiredArgsConstructor
public class NormalPayOrderAdminController {
private final NormalPayOrderAdminService normalPayOrderAdminService;
@PermCode(code = "view", nameCn = "订单查看", nameEn = "Order View")
@Operation(summary = "普通支付业务单分页")
@GetMapping("/page")
public Result<PageResult<NormalPayOrderResult>> page(PageParam pageParam, NormalPayOrderQuery query) {
return Res.ok(normalPayOrderAdminService.page(pageParam, query));
}
@PermCode(code = "view", nameCn = "订单查看", nameEn = "Order View")
@Operation(summary = "根据ID查询普通支付业务单详情")
@GetMapping("/get-by-id")
public Result<NormalPayOrderResult> findById(
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(normalPayOrderAdminService.findById(id));
}
@PermCode(code = "manage", nameCn = "订单管理", nameEn = "Order Manage")
@Operation(summary = "同步支付状态")
@PostMapping("/sync")
public Result<NormalPaySyncResult> sync(
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(normalPayOrderAdminService.sync(id));
}
@PermCode(code = "manage", nameCn = "订单管理", nameEn = "Order Manage")
@Operation(summary = "关闭/撤销订单")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@RequestParam(defaultValue = "false") boolean useCancel) {
normalPayOrderAdminService.close(id, useCancel);
return Res.ok();
}
}

View File

@@ -0,0 +1,68 @@
package cn.daxpay.open.payment.admin.controller.order;
import cn.daxpay.open.platform.core.annotation.PermCode;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import cn.daxpay.open.platform.core.rest.result.Result;
import cn.daxpay.open.payment.pay.param.order.PayTradeQuery;
import cn.daxpay.open.payment.pay.result.order.PayTradeResult;
import cn.daxpay.open.payment.pay.service.admin.PayTradeAdminService;
import cn.daxpay.open.payment.unipay.result.trade.pay.NormalPaySyncResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/// # 资金交易凭证(管理)
///
/// 面向运营/商户后台的资金交易(凭证)管理: 分页查询、详情、状态同步、关闭/撤销
@PermCode(menuCode = "payment:trade")
@Validated
@Tag(name = "资金交易凭证(管理)")
@RestController
@RequestMapping("/admin/order/pay-trade")
@RequiredArgsConstructor
public class PayTradeAdminController {
private final PayTradeAdminService payTradeAdminService;
@PermCode(code = "view", nameCn = "交易查看", nameEn = "Trade View")
@Operation(summary = "资金交易凭证分页")
@GetMapping("/page")
public Result<PageResult<PayTradeResult>> page(PageParam pageParam, PayTradeQuery query) {
return Res.ok(payTradeAdminService.page(pageParam, query));
}
@PermCode(code = "view", nameCn = "交易查看", nameEn = "Trade View")
@Operation(summary = "根据ID查询资金交易凭证详情")
@GetMapping("/get-by-id")
public Result<PayTradeResult> findById(
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(payTradeAdminService.findById(id));
}
@PermCode(code = "manage", nameCn = "交易管理", nameEn = "Trade Manage")
@Operation(summary = "同步支付状态")
@PostMapping("/sync")
public Result<NormalPaySyncResult> sync(
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(payTradeAdminService.sync(id));
}
@PermCode(code = "manage", nameCn = "交易管理", nameEn = "Trade Manage")
@Operation(summary = "关闭/撤销订单")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@RequestParam(defaultValue = "false") boolean useCancel) {
payTradeAdminService.close(id, useCancel);
return Res.ok();
}
}

View File

@@ -0,0 +1,19 @@
package cn.daxpay.open.payment.pay.convert;
import cn.daxpay.open.payment.pay.order.entity.NormalPayOrder;
import cn.daxpay.open.payment.pay.result.order.NormalPayOrderResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/// # 普通支付业务单转换器(管理)
///
/// 仅做容器(NormalPayOrder)到列表 Result 的映射;
/// 详情场景的资金凭证(PayTrade)字段由 Service 层手动补充, 避免多参数源同名字段歧义
@Mapper
public interface NormalPayOrderConvert {
NormalPayOrderConvert CONVERT = Mappers.getMapper(NormalPayOrderConvert.class);
/// NormalPayOrder → Result (列表用, 不含资金凭证字段)
NormalPayOrderResult toResult(NormalPayOrder entity);
}

View File

@@ -0,0 +1,19 @@
package cn.daxpay.open.payment.pay.convert;
import cn.daxpay.open.payment.pay.order.entity.PayTrade;
import cn.daxpay.open.payment.pay.result.order.PayTradeResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/// # 资金交易凭证转换器(管理)
///
/// 仅做凭证(PayTrade)到列表 Result 的映射;
/// 详情场景的容器(NormalPayOrder)字段由 Service 层手动补充, 避免多参数源同名字段歧义
@Mapper
public interface PayTradeAdminConvert {
PayTradeAdminConvert CONVERT = Mappers.getMapper(PayTradeAdminConvert.class);
/// PayTrade → Result (列表用, 不含容器字段)
PayTradeResult toResult(PayTrade entity);
}

View File

@@ -1,7 +1,13 @@
package cn.daxpay.open.payment.pay.order.dao;
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
import cn.daxpay.open.platform.common.mybatisplus.query.generator.QueryGenerator;
import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.payment.pay.order.entity.NormalPayOrder;
import cn.daxpay.open.payment.pay.param.order.NormalPayOrderQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@@ -17,4 +23,13 @@ public class NormalPayOrderManager extends BaseManager<NormalPayOrderMapper, Nor
.eq(NormalPayOrder::getBizOrderNo, bizOrderNo)
.oneOpt();
}
/// 分页查询(管理端), 默认按创建时间倒序
public Page<NormalPayOrder> page(PageParam pageParam, NormalPayOrderQuery query) {
Page<NormalPayOrder> mpPage = MpUtil.getMpPage(pageParam);
QueryWrapper<NormalPayOrder> wrapper = QueryGenerator.generator(query);
// 默认按创建时间倒序
wrapper.orderByDesc("create_time");
return this.page(mpPage, wrapper);
}
}

View File

@@ -1,10 +1,16 @@
package cn.daxpay.open.payment.pay.order.dao;
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
import cn.daxpay.open.platform.common.mybatisplus.query.generator.QueryGenerator;
import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil;
import cn.daxpay.open.platform.core.annotation.IgnoreTenant;
import cn.daxpay.open.platform.core.exception.DangerSqlException;
import cn.daxpay.open.platform.core.code.CommonCode;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.payment.pay.order.entity.PayTrade;
import cn.daxpay.open.payment.pay.param.order.PayTradeQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@@ -39,6 +45,15 @@ public class PayTradeManager extends BaseManager<PayTradeMapper, PayTrade> {
.oneOpt();
}
/// 分页查询(管理端), 默认按创建时间倒序
public Page<PayTrade> page(PageParam pageParam, PayTradeQuery query) {
Page<PayTrade> mpPage = MpUtil.getMpPage(pageParam);
QueryWrapper<PayTrade> wrapper = QueryGenerator.generator(query);
// 默认按创建时间倒序
wrapper.orderByDesc("create_time");
return this.page(mpPage, wrapper);
}
/// 根据id进行更新失败时抛出异常
@Override
public int updateById(PayTrade entity) {

View File

@@ -0,0 +1,79 @@
package cn.daxpay.open.payment.pay.param.order;
import cn.daxpay.open.platform.core.annotation.QueryParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.OffsetDateTime;
/// # 普通支付业务单查询参数(管理)
///
@Data
@Accessors(chain = true)
@Schema(title = "普通支付业务单查询参数")
public class NormalPayOrderQuery {
/// 商户号
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "商户号")
private String mchNo;
/// 应用号
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "应用号")
private String appId;
/// 商户业务单号
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
@Schema(description = "商户业务单号")
private String bizOrderNo;
/// 订单标题
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
@Schema(description = "订单标题")
private String title;
/// 业务状态
/// @see cn.daxpay.open.payment.common.enums.NormalPayOrderStatusEnum
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "业务状态")
private String status;
/// 支付通道
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "支付通道")
private String channel;
/// 支付方式
/// @see cn.daxpay.open.platform.core.enums.pay.channel.PayMethodEnum
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "支付方式")
private String method;
/// 支付产品
/// @see cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "支付产品")
private String product;
/// 创建时间-开始
@QueryParam(type = QueryParam.CompareTypeEnum.GE, fieldName = "create_time")
@Schema(description = "创建时间-开始")
private OffsetDateTime createTimeStart;
/// 创建时间-结束
@QueryParam(type = QueryParam.CompareTypeEnum.LE, fieldName = "create_time")
@Schema(description = "创建时间-结束")
private OffsetDateTime createTimeEnd;
/// 金额下限(最小货币单位)
@QueryParam(type = QueryParam.CompareTypeEnum.GE, fieldName = "amount")
@Schema(description = "金额下限(分)")
private Long amountMin;
/// 金额上限(最小货币单位)
@QueryParam(type = QueryParam.CompareTypeEnum.LE, fieldName = "amount")
@Schema(description = "金额上限(分)")
private Long amountMax;
}

View File

@@ -0,0 +1,90 @@
package cn.daxpay.open.payment.pay.param.order;
import cn.daxpay.open.platform.core.annotation.QueryParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.OffsetDateTime;
/// # 资金交易凭证查询参数(管理)
///
@Data
@Accessors(chain = true)
@Schema(title = "资金交易凭证查询参数")
public class PayTradeQuery {
/// 商户号
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "商户号")
private String mchNo;
/// 应用号
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "应用号")
private String appId;
/// 支付交易号
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "支付交易号")
private String tradeNo;
/// 通道订单号
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
@Schema(description = "通道订单号")
private String outOrderNo;
/// 资金状态
/// @see cn.daxpay.open.payment.common.enums.PayFundStatusEnum
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "资金状态")
private String status;
/// 交易形态
/// @see cn.daxpay.open.payment.common.enums.PayTradeTypeEnum
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "交易形态")
private String tradeType;
/// 支付通道
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "支付通道")
private String channel;
/// 支付方式
/// @see cn.daxpay.open.platform.core.enums.pay.channel.PayMethodEnum
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "支付方式")
private String method;
/// 支付产品
/// @see cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "支付产品")
private String product;
/// 关联容器ID
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Schema(description = "关联容器ID")
private Long containerId;
/// 创建时间-开始
@QueryParam(type = QueryParam.CompareTypeEnum.GE, fieldName = "create_time")
@Schema(description = "创建时间-开始")
private OffsetDateTime createTimeStart;
/// 创建时间-结束
@QueryParam(type = QueryParam.CompareTypeEnum.LE, fieldName = "create_time")
@Schema(description = "创建时间-结束")
private OffsetDateTime createTimeEnd;
/// 金额下限(最小货币单位)
@QueryParam(type = QueryParam.CompareTypeEnum.GE, fieldName = "amount")
@Schema(description = "金额下限(分)")
private Long amountMin;
/// 金额上限(最小货币单位)
@QueryParam(type = QueryParam.CompareTypeEnum.LE, fieldName = "amount")
@Schema(description = "金额上限(分)")
private Long amountMax;
}

View File

@@ -0,0 +1,158 @@
package cn.daxpay.open.payment.pay.result.order;
import cn.daxpay.open.platform.core.result.BaseResult;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.OffsetDateTime;
/// # 普通支付业务单(管理)
///
/// 列表场景仅填充容器(NormalPayOrder)字段;
/// 详情场景额外填充资金凭证(PayTrade)字段: tradeNo / outOrderNo / fundStatus 等
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "普通支付业务单")
public class NormalPayOrderResult extends BaseResult {
// ===== 容器(业务单)字段 =====
/// 商户号
@Schema(description = "商户号")
private String mchNo;
/// 应用号
@Schema(description = "应用号")
private String appId;
/// 商户业务单号
@Schema(description = "商户业务单号")
private String bizOrderNo;
/// 标题
@Schema(description = "标题")
private String title;
/// 描述
@Schema(description = "描述")
private String description;
/// 业务状态
/// @see cn.daxpay.open.payment.common.enums.NormalPayOrderStatusEnum
@Schema(description = "业务状态")
private String status;
/// 异步通知地址
@Schema(description = "异步通知地址")
private String notifyUrl;
/// 同步跳转地址
@Schema(description = "同步跳转地址")
private String returnUrl;
/// 商户附加参数
@Schema(description = "商户附加参数")
private String attach;
/// 业务单过期时间
@Schema(description = "业务单过期时间")
private OffsetDateTime expiredTime;
/// 业务单金额(最小货币单位)
@Schema(description = "业务单金额(分)")
private Long amount;
/// 币种
@Schema(description = "币种")
private String currency;
/// 支付通道
@Schema(description = "支付通道")
private String channel;
/// 支付方式
@Schema(description = "支付方式")
private String method;
/// 支付产品
@Schema(description = "支付产品")
private String product;
/// 支付成功时间
@Schema(description = "支付成功时间")
private OffsetDateTime payTime;
/// 关闭时间
@Schema(description = "关闭时间")
private OffsetDateTime closeTime;
/// 通道商户号(路由回填)
@Schema(description = "通道商户号")
private String channelMchNo;
/// 支付能力编码(路由回填)
@Schema(description = "支付能力编码")
private String capability;
/// 客户端IP
@Schema(description = "客户端IP")
private String clientIp;
/// 终端设备编码
@Schema(description = "终端设备编码")
private String terminalNo;
// ===== 资金凭证(PayTrade)联表字段, 仅详情时填充 =====
/// 资金交易号
@Schema(description = "资金交易号")
private String tradeNo;
/// 通道订单号
@Schema(description = "通道订单号")
private String outOrderNo;
/// 资金状态
/// @see cn.daxpay.open.payment.common.enums.PayFundStatusEnum
@Schema(description = "资金状态")
private String fundStatus;
/// 可退金额(最小货币单位)
@Schema(description = "可退金额(分)")
private Long refundableBalance;
/// 支付参数体
@Schema(description = "支付参数体")
private String payBody;
/// 支付参数体类型
@Schema(description = "支付参数体类型")
private String payBodyType;
/// 付款用户ID
@Schema(description = "付款用户ID")
private String buyerId;
/// 微信openid
@Schema(description = "微信openid")
private String openid;
/// 通道方记录的支付产品
@Schema(description = "通道方记录的支付产品")
private String tradeProduct;
/// 通道方记录的交易方式
@Schema(description = "通道方记录的交易方式")
private String tradeWay;
/// 银行卡类型
@Schema(description = "银行卡类型")
private String bankType;
/// 错误信息
@Schema(description = "错误信息")
private String errorMsg;
}

View File

@@ -0,0 +1,167 @@
package cn.daxpay.open.payment.pay.result.order;
import cn.daxpay.open.platform.core.result.BaseResult;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.OffsetDateTime;
/// # 资金交易凭证(管理)
///
/// 列表场景仅填充凭证(PayTrade)字段;
/// 详情场景额外填充容器(NormalPayOrder)字段: bizOrderNo / title / containerStatus
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "资金交易凭证")
public class PayTradeResult extends BaseResult {
// ===== 凭证(PayTrade)字段 =====
/// 商户号
@Schema(description = "商户号")
private String mchNo;
/// 应用号
@Schema(description = "应用号")
private String appId;
/// 支付交易号
@Schema(description = "支付交易号")
private String tradeNo;
/// 交易形态
/// @see cn.daxpay.open.payment.common.enums.PayTradeTypeEnum
@Schema(description = "交易形态")
private String tradeType;
/// 关联容器ID
@Schema(description = "关联容器ID")
private Long containerId;
/// 支付产品
@Schema(description = "支付产品")
private String product;
/// 支付通道
@Schema(description = "支付通道")
private String channel;
/// 支付方式
@Schema(description = "支付方式")
private String method;
/// 其他支付方式
@Schema(description = "其他支付方式")
private String otherMethod;
/// 限制支付类型
@Schema(description = "限制支付类型")
private String limitPay;
/// 支付渠道(微信/支付宝/银联等)
@Schema(description = "支付渠道")
private String provider;
/// 本次交易金额(最小货币单位)
@Schema(description = "本次交易金额(分)")
private Long amount;
/// 币种
@Schema(description = "币种")
private String currency;
/// 可退金额(最小货币单位)
@Schema(description = "可退金额(分)")
private Long refundableBalance;
/// 资金状态
/// @see cn.daxpay.open.payment.common.enums.PayFundStatusEnum
@Schema(description = "资金状态")
private String status;
/// 过期时间
@Schema(description = "过期时间")
private OffsetDateTime expiredTime;
/// 支付成功时间
@Schema(description = "支付成功时间")
private OffsetDateTime payTime;
/// 关闭时间
@Schema(description = "关闭时间")
private OffsetDateTime closeTime;
/// 订单来源
@Schema(description = "订单来源")
private String source;
/// 通道订单号
@Schema(description = "通道订单号")
private String outOrderNo;
/// 透传订单号
@Schema(description = "透传订单号")
private String transOrderNo;
/// 特殊通道关联订单号
@Schema(description = "特殊通道关联订单号")
private String relationOrderNo;
/// 付款用户ID
@Schema(description = "付款用户ID")
private String buyerId;
/// 微信openid
@Schema(description = "微信openid")
private String openid;
/// 通道方记录的支付产品
@Schema(description = "通道方记录的支付产品")
private String tradeProduct;
/// 通道方记录的交易方式
@Schema(description = "通道方记录的交易方式")
private String tradeWay;
/// 银行卡类型
@Schema(description = "银行卡类型")
private String bankType;
/// 付款码(被扫支付)
@Schema(description = "付款码")
private String barCode;
/// 活动类型
@Schema(description = "活动类型")
private String promotionType;
/// 支付参数体
@Schema(description = "支付参数体")
private String payBody;
/// 支付参数体类型
@Schema(description = "支付参数体类型")
private String payBodyType;
/// 错误信息
@Schema(description = "错误信息")
private String errorMsg;
// ===== 容器(NormalPayOrder)联表字段, 仅详情时填充 =====
/// 商户业务单号
@Schema(description = "商户业务单号")
private String bizOrderNo;
/// 订单标题
@Schema(description = "订单标题")
private String title;
/// 容器业务状态
/// @see cn.daxpay.open.payment.common.enums.NormalPayOrderStatusEnum
@Schema(description = "容器业务状态")
private String containerStatus;
}

View File

@@ -0,0 +1,88 @@
package cn.daxpay.open.payment.pay.service.admin;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import cn.daxpay.open.payment.pay.convert.NormalPayOrderConvert;
import cn.daxpay.open.payment.pay.order.dao.NormalPayOrderManager;
import cn.daxpay.open.payment.pay.order.dao.PayTradeManager;
import cn.daxpay.open.payment.pay.order.entity.NormalPayOrder;
import cn.daxpay.open.payment.pay.order.entity.PayTrade;
import cn.daxpay.open.payment.pay.param.order.NormalPayOrderQuery;
import cn.daxpay.open.payment.pay.result.order.NormalPayOrderResult;
import cn.daxpay.open.payment.pay.service.PayCloseService;
import cn.daxpay.open.payment.pay.service.PaySyncService;
import cn.daxpay.open.payment.unipay.result.trade.pay.NormalPaySyncResult;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Objects;
/// # 普通支付业务单管理服务(管理端)
///
/// 提供业务订单(容器)的分页/详情查询, 以及状态同步、关闭/撤销管理操作。
/// 详情场景联表资金凭证(PayTrade)补充交易字段; 同步/关闭复用 [PaySyncService] / [PayCloseService]。
@Slf4j
@Service
@RequiredArgsConstructor
public class NormalPayOrderAdminService {
private final NormalPayOrderManager normalPayOrderManager;
private final PayTradeManager payTradeManager;
private final PaySyncService paySyncService;
private final PayCloseService payCloseService;
/// 分页查询
public PageResult<NormalPayOrderResult> page(PageParam pageParam, NormalPayOrderQuery query) {
Page<NormalPayOrder> page = normalPayOrderManager.page(pageParam, query);
// 手动转换, 避免侵入核心实体(不实现 ToResult)
var records = page.getRecords().stream()
.map(NormalPayOrderConvert.CONVERT::toResult)
.toList();
return new PageResult<NormalPayOrderResult>()
.setRecords(records)
.setTotal(page.getTotal())
.setSize(page.getSize())
.setCurrent(page.getCurrent());
}
/// 详情查询(联表资金凭证补充交易字段)
public NormalPayOrderResult findById(Long id) {
NormalPayOrder entity = normalPayOrderManager.findById(id)
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
NormalPayOrderResult result = NormalPayOrderConvert.CONVERT.toResult(entity);
// 联表查询资金凭证, 补充交易字段
PayTrade trade = payTradeManager.findByContainerId(id).orElse(null);
if (Objects.nonNull(trade)) {
result.setTradeNo(trade.getTradeNo());
result.setOutOrderNo(trade.getOutOrderNo());
result.setFundStatus(trade.getStatus());
result.setRefundableBalance(trade.getRefundableBalance());
result.setPayBody(trade.getPayBody());
result.setPayBodyType(trade.getPayBodyType());
result.setBuyerId(trade.getBuyerId());
result.setOpenid(trade.getOpenid());
result.setTradeProduct(trade.getTradeProduct());
result.setTradeWay(trade.getTradeWay());
result.setBankType(trade.getBankType());
result.setErrorMsg(trade.getErrorMsg());
}
return result;
}
/// 同步支付状态(传入业务订单ID)
public NormalPaySyncResult sync(Long id) {
PayTrade trade = payTradeManager.findByContainerId(id)
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
return paySyncService.syncPayOrder(trade);
}
/// 关闭/撤销订单(传入业务订单ID)
public void close(Long id, boolean useCancel) {
PayTrade trade = payTradeManager.findByContainerId(id)
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
payCloseService.closeOrder(trade, useCancel);
}
}

View File

@@ -0,0 +1,78 @@
package cn.daxpay.open.payment.pay.service.admin;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import cn.daxpay.open.payment.pay.convert.PayTradeAdminConvert;
import cn.daxpay.open.payment.pay.order.dao.NormalPayOrderManager;
import cn.daxpay.open.payment.pay.order.dao.PayTradeManager;
import cn.daxpay.open.payment.pay.order.entity.NormalPayOrder;
import cn.daxpay.open.payment.pay.order.entity.PayTrade;
import cn.daxpay.open.payment.pay.param.order.PayTradeQuery;
import cn.daxpay.open.payment.pay.result.order.PayTradeResult;
import cn.daxpay.open.payment.pay.service.PayCloseService;
import cn.daxpay.open.payment.pay.service.PaySyncService;
import cn.daxpay.open.payment.unipay.result.trade.pay.NormalPaySyncResult;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Objects;
/// # 资金交易凭证管理服务(管理端)
///
/// 提供资金交易(凭证)的分页/详情查询, 以及状态同步、关闭/撤销管理操作。
/// 详情场景联表容器(NormalPayOrder)补充业务字段; 同步/关闭复用 [PaySyncService] / [PayCloseService]。
@Slf4j
@Service
@RequiredArgsConstructor
public class PayTradeAdminService {
private final PayTradeManager payTradeManager;
private final NormalPayOrderManager normalPayOrderManager;
private final PaySyncService paySyncService;
private final PayCloseService payCloseService;
/// 分页查询
public PageResult<PayTradeResult> page(PageParam pageParam, PayTradeQuery query) {
Page<PayTrade> page = payTradeManager.page(pageParam, query);
var records = page.getRecords().stream()
.map(PayTradeAdminConvert.CONVERT::toResult)
.toList();
return new PageResult<PayTradeResult>()
.setRecords(records)
.setTotal(page.getTotal())
.setSize(page.getSize())
.setCurrent(page.getCurrent());
}
/// 详情查询(联表容器补充业务字段)
public PayTradeResult findById(Long id) {
PayTrade entity = payTradeManager.findById(id)
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
PayTradeResult result = PayTradeAdminConvert.CONVERT.toResult(entity);
// 联表查询容器, 补充业务字段
NormalPayOrder normalOrder = normalPayOrderManager.findById(entity.getContainerId()).orElse(null);
if (Objects.nonNull(normalOrder)) {
result.setBizOrderNo(normalOrder.getBizOrderNo());
result.setTitle(normalOrder.getTitle());
result.setContainerStatus(normalOrder.getStatus());
}
return result;
}
/// 同步支付状态(传入资金交易ID)
public NormalPaySyncResult sync(Long id) {
PayTrade trade = payTradeManager.findById(id)
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
return paySyncService.syncPayOrder(trade);
}
/// 关闭/撤销订单(传入资金交易ID)
public void close(Long id, boolean useCancel) {
PayTrade trade = payTradeManager.findById(id)
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
payCloseService.closeOrder(trade, useCancel);
}
}

View File

@@ -48,7 +48,7 @@ public class UniTradeController {
@Operation(summary = "关闭和撤销接口")
@PostMapping("/close")
public DaxResult<Void> close(@RequestBody NormalPayCloseParam param){
if ( permConfigService.hasApiPerm(PaymentApiEnum.CLOSE.getCode())){
if ( !permConfigService.hasApiPerm(PaymentApiEnum.CLOSE.getCode())){
// 订单: 该商户没有此接口的权限
throw new OperationFailException(CommonCode.FAIL_CODE, "error.payment.order.merchantNoApiPermission");
}