mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-10 14:56:06 +08:00
feat(merchant): 商户应用事件通知配置(entity/service/controller/菜单SQL/i18n)
This commit is contained in:
@@ -156,6 +156,8 @@ INSERT INTO "public"."iam_perm_menu" VALUES (90302, 9, 'device:printer', 'admin'
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (40105, 405, 'payment:config:product_config', 'admin', 'ProductConfig', '支付产品管理', 'Payment Product Management', 'menu.payment.config.productConfig', 'lucide:layout-grid', 'f', 'f', '/payment/config/product/ProductConfig', '/payment/config/product', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-13 00:00:00+00', '2026-06-27 13:01:24.317032+00');
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (4040116, 401, 'payment:platform:provider_manage', 'admin', 'PayProviderManage', '支付渠道', 'Payment Provider', 'menu.payment.platform.provider.manage', 'lucide:wallet', 'f', 'f', '/payment/masterdata/provider/PayProviderList', '/payment/platform/pay-provider', NULL, 0.5, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-17 10:00:31.640482+00', '2026-06-27 13:01:24.318649+00');
|
||||
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (4040118, 40401, 'merchant:notify_config', 'admin', 'MchAppNotifyConfig', '通知配置', 'Notify Config', 'menu.payment.merchant.app.notifyConfig', NULL, 't', 'f', '/payment/merchant/app/MchAppNotifyConfig', '/payment/merchant/app/notify-config', NULL, 11, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-02 16:00:00+00', '2026-07-02 16:00:00+00');
|
||||
|
||||
-- ----------------------------
|
||||
-- Primary Key structure for table iam_perm_menu
|
||||
-- ----------------------------
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- 商户应用事件通知配置: 菜单(权限码 merchant:notify_config)
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (4040118, 40401, 'merchant:notify_config', 'admin', 'MchAppNotifyConfig', '通知配置', 'Notify Config', 'menu.payment.merchant.app.notifyConfig', NULL, 't', 'f', '/payment/merchant/app/MchAppNotifyConfig', '/payment/merchant/app/notify-config', NULL, 11, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-02 16:00:00+00', '2026-07-02 16:00:00+00');
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package cn.daxpay.open.payment.admin.controller.merchant.config;
|
||||
|
||||
import cn.daxpay.open.platform.core.annotation.PermCode;
|
||||
import cn.daxpay.open.platform.core.rest.Res;
|
||||
import cn.daxpay.open.platform.core.rest.result.Result;
|
||||
import cn.daxpay.open.payment.merchant.param.config.MchAppNotifyConfigParam;
|
||||
import cn.daxpay.open.payment.merchant.result.config.MchAppNotifyConfigResult;
|
||||
import cn.daxpay.open.payment.merchant.service.config.MchAppNotifyConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/// # 商户应用事件通知配置管理控制器
|
||||
///
|
||||
@PermCode(menuCode = "merchant:notify_config")
|
||||
@Validated
|
||||
@Tag(name = "商户应用事件通知配置管理")
|
||||
@RestController
|
||||
@RequestMapping("/admin/merchant/app-notify-config")
|
||||
@RequiredArgsConstructor
|
||||
public class MchAppNotifyConfigAdminController {
|
||||
|
||||
private final MchAppNotifyConfigService notifyConfigService;
|
||||
|
||||
@PermCode(code = "view", nameCn = "商户查看", nameEn = "Merchant View")
|
||||
@Operation(summary = "根据应用ID查询通知配置")
|
||||
@GetMapping("/get-by-app-id")
|
||||
public Result<MchAppNotifyConfigResult> findByAppId(
|
||||
@NotBlank(message = "{validation.field.appId.notBlank}") String appId) {
|
||||
return Res.ok(notifyConfigService.findByAppId(appId));
|
||||
}
|
||||
|
||||
@PermCode(code = "notify_config_update", nameCn = "通知配置更新", nameEn = "Notify Config Update")
|
||||
@Operation(summary = "保存或更新通知配置")
|
||||
@PostMapping("/save-or-update")
|
||||
public Result<Void> saveOrUpdate(@RequestBody @Validated MchAppNotifyConfigParam param) {
|
||||
notifyConfigService.saveOrUpdate(param);
|
||||
return Res.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.daxpay.open.payment.merchant.convert.config;
|
||||
|
||||
import cn.daxpay.open.payment.merchant.entity.config.MchAppNotifyConfig;
|
||||
import cn.daxpay.open.payment.merchant.param.config.MchAppNotifyConfigParam;
|
||||
import cn.daxpay.open.payment.merchant.result.config.MchAppNotifyConfigResult;
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 商户应用事件通知配置转换
|
||||
///
|
||||
@Mapper
|
||||
public interface MchAppNotifyConfigConvert {
|
||||
MchAppNotifyConfigConvert CONVERT = Mappers.getMapper(MchAppNotifyConfigConvert.class);
|
||||
|
||||
MchAppNotifyConfig toEntity(MchAppNotifyConfigParam param);
|
||||
|
||||
MchAppNotifyConfigResult toResult(MchAppNotifyConfig entity);
|
||||
|
||||
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
void copy(MchAppNotifyConfigParam param, @MappingTarget MchAppNotifyConfig entity);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.daxpay.open.payment.merchant.dao.config;
|
||||
|
||||
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
|
||||
import cn.daxpay.open.payment.merchant.entity.config.MchAppNotifyConfig;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 商户应用事件通知配置管理
|
||||
///
|
||||
@Repository
|
||||
public class MchAppNotifyConfigManager extends BaseManager<MchAppNotifyConfigMapper, MchAppNotifyConfig> {
|
||||
|
||||
/// 根据应用ID查询
|
||||
public Optional<MchAppNotifyConfig> findByAppId(String appId) {
|
||||
return findByField(MchAppNotifyConfig::getAppId, appId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package cn.daxpay.open.payment.merchant.dao.config;
|
||||
|
||||
import cn.daxpay.open.payment.merchant.entity.config.MchAppNotifyConfig;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 商户应用事件通知配置
|
||||
///
|
||||
@Mapper
|
||||
public interface MchAppNotifyConfigMapper extends MPJBaseMapper<MchAppNotifyConfig> {
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.daxpay.open.payment.merchant.entity.config;
|
||||
|
||||
import cn.daxpay.open.platform.common.mybatisplus.function.ToResult;
|
||||
import cn.daxpay.open.payment.common.entity.merchant.MchBaseEntity;
|
||||
import cn.daxpay.open.payment.merchant.convert.config.MchAppNotifyConfigConvert;
|
||||
import cn.daxpay.open.payment.merchant.result.config.MchAppNotifyConfigResult;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 商户应用事件通知配置
|
||||
///
|
||||
/// 应用级通用事件通知配置, 与支付订单级回调并行, 不局限于支付领域
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName("mch_app_notify_config")
|
||||
public class MchAppNotifyConfig extends MchBaseEntity implements ToResult<MchAppNotifyConfigResult> {
|
||||
|
||||
/// 应用ID
|
||||
private String appId;
|
||||
|
||||
/// 回调地址 (https)
|
||||
private String notifyUrl;
|
||||
|
||||
/// 通知方式 (http-HTTP异步回调)
|
||||
private String notifyWay = "http";
|
||||
|
||||
/// 订阅事件类型 (逗号分隔, TradeTypeEnum的code: pay/cashouts/settle)
|
||||
private String subscribedEvents;
|
||||
|
||||
/// 启用状态
|
||||
private Boolean status = false;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public MchAppNotifyConfigResult toResult() {
|
||||
return MchAppNotifyConfigConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package cn.daxpay.open.payment.merchant.param.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 商户应用事件通知配置参数
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "商户应用事件通知配置参数")
|
||||
public class MchAppNotifyConfigParam {
|
||||
|
||||
/// 应用ID
|
||||
@Schema(description = "应用ID")
|
||||
@NotBlank(message = "{validation.field.appId.notBlank}")
|
||||
private String appId;
|
||||
|
||||
/// 回调地址
|
||||
@Schema(description = "回调地址(https)")
|
||||
@Size(max = 255, message = "{validation.field.notifyUrl.size}")
|
||||
private String notifyUrl;
|
||||
|
||||
/// 通知方式
|
||||
@Schema(description = "通知方式(http-HTTP异步回调)")
|
||||
private String notifyWay;
|
||||
|
||||
/// 订阅事件类型
|
||||
@Schema(description = "订阅事件类型(逗号分隔,TradeTypeEnum的code)")
|
||||
private String subscribedEvents;
|
||||
|
||||
/// 启用状态
|
||||
@Schema(description = "启用状态")
|
||||
private Boolean status;
|
||||
|
||||
/// 备注
|
||||
@Schema(description = "备注")
|
||||
@Size(max = 255, message = "{validation.field.remark.size}")
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.daxpay.open.payment.merchant.result.config;
|
||||
|
||||
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;
|
||||
|
||||
/// # 商户应用事件通知配置结果
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "商户应用事件通知配置结果")
|
||||
public class MchAppNotifyConfigResult extends BaseResult {
|
||||
|
||||
/// 商户号
|
||||
@Schema(description = "商户号")
|
||||
private String mchNo;
|
||||
|
||||
/// 应用ID
|
||||
@Schema(description = "应用ID")
|
||||
private String appId;
|
||||
|
||||
/// 回调地址
|
||||
@Schema(description = "回调地址")
|
||||
private String notifyUrl;
|
||||
|
||||
/// 通知方式
|
||||
@Schema(description = "通知方式")
|
||||
private String notifyWay;
|
||||
|
||||
/// 订阅事件类型
|
||||
@Schema(description = "订阅事件类型")
|
||||
private String subscribedEvents;
|
||||
|
||||
/// 启用状态
|
||||
@Schema(description = "启用状态")
|
||||
private Boolean status;
|
||||
|
||||
/// 备注
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package cn.daxpay.open.payment.merchant.service.config;
|
||||
|
||||
import cn.daxpay.open.platform.core.exception.DataNotExistException;
|
||||
import cn.daxpay.open.payment.merchant.convert.config.MchAppNotifyConfigConvert;
|
||||
import cn.daxpay.open.payment.merchant.dao.appinfo.MchAppInfoManager;
|
||||
import cn.daxpay.open.payment.merchant.dao.config.MchAppNotifyConfigManager;
|
||||
import cn.daxpay.open.payment.merchant.entity.appinfo.MchAppInfo;
|
||||
import cn.daxpay.open.payment.merchant.entity.config.MchAppNotifyConfig;
|
||||
import cn.daxpay.open.payment.merchant.param.config.MchAppNotifyConfigParam;
|
||||
import cn.daxpay.open.payment.merchant.result.config.MchAppNotifyConfigResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/// # 商户应用事件通知配置服务
|
||||
///
|
||||
/// 应用级通用事件通知配置, 与支付订单级回调并行, 当前版本仅维护配置数据,
|
||||
/// 发送链路(任务/重试/记录)后续阶段实现
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MchAppNotifyConfigService {
|
||||
|
||||
private final MchAppNotifyConfigManager notifyConfigManager;
|
||||
|
||||
private final MchAppInfoManager mchAppInfoManager;
|
||||
|
||||
/// 根据应用ID查询通知配置, 无记录时返回默认空对象(不落库)
|
||||
public MchAppNotifyConfigResult findByAppId(String appId) {
|
||||
return notifyConfigManager.findByAppId(appId)
|
||||
.map(MchAppNotifyConfig::toResult)
|
||||
.orElseGet(() -> new MchAppNotifyConfigResult()
|
||||
.setAppId(appId)
|
||||
.setNotifyWay("http")
|
||||
.setStatus(false));
|
||||
}
|
||||
|
||||
/// 保存或更新(按应用ID upsert)
|
||||
public void saveOrUpdate(MchAppNotifyConfigParam param) {
|
||||
// 校验应用存在并获取商户号(通知配置冗余商户号, 便于按商户过滤与鉴权)
|
||||
MchAppInfo mchApp = mchAppInfoManager.findByAppId(param.getAppId())
|
||||
// 商户: 商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.payment.merchant.mchAppNotFound"));
|
||||
var existing = notifyConfigManager.findByAppId(param.getAppId());
|
||||
if (existing.isPresent()) {
|
||||
MchAppNotifyConfig entity = existing.get();
|
||||
MchAppNotifyConfigConvert.CONVERT.copy(param, entity);
|
||||
notifyConfigManager.updateById(entity);
|
||||
} else {
|
||||
MchAppNotifyConfig entity = MchAppNotifyConfigConvert.CONVERT.toEntity(param);
|
||||
entity.setMchNo(mchApp.getMchNo());
|
||||
notifyConfigManager.save(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -534,6 +534,9 @@
|
||||
},
|
||||
"preAuthToken": {
|
||||
"notBlank": "Pre-auth token cannot be blank"
|
||||
},
|
||||
"remark": {
|
||||
"size": "Remark cannot exceed 255 characters"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,6 +537,9 @@
|
||||
},
|
||||
"preAuthToken": {
|
||||
"notBlank": "预认证令牌不能为空"
|
||||
},
|
||||
"remark": {
|
||||
"size": "备注不可超过255位"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user