feat(payment): 添加微信支付商家转账场景报备信息配置
- 在 application-cms.yml、application-dev.yml、application-prod.yml 和 application-yd.yml 中 添加 wechatpay.transfer.scene-id 和 scene-report-infos-json 配置项 - 重构 CmsNavigation 实体类,将 modelName 字段位置调整到正确位置 - 修改 CmsNavigationMapper.xml 添加模型名称关联查询 - 更新 JSONUtil 工具类,注册 JavaTimeModule 支持 LocalDateTime 等 Java8 时间类型 - 扩展 ShopDealerUser 实体类,添加 dealerName 和 community 字段 - 在 ShopDealerUserController 中添加手机号排重逻辑 - 修改 ShopDealerUserMapper.xml 增加关键词搜索字段 - 移除 ShopDealerWithdrawController 中多余的操作日志注解 - 扩展 ShopGoods 实体类,添加 categoryName 字段并修改关联查询 - 更新 WxLoginController 构造函数注入 ObjectMapper - 增强 WxTransferService 添加转账场景报备信息验证和日志记录
This commit is contained in:
@@ -43,6 +43,10 @@ public class CmsNavigation implements Serializable {
|
||||
@Schema(description = "模型")
|
||||
private String model;
|
||||
|
||||
@Schema(description = "模型名称")
|
||||
@TableField(exist = false)
|
||||
private String modelName;
|
||||
|
||||
@Schema(description = "标识")
|
||||
private String code;
|
||||
|
||||
@@ -114,9 +118,6 @@ public class CmsNavigation implements Serializable {
|
||||
@TableField(exist = false)
|
||||
private Integer parentPosition;
|
||||
|
||||
@Schema(description = "模型名称")
|
||||
private String modelName;
|
||||
|
||||
@Schema(description = "绑定的页面(已废弃)")
|
||||
private Integer pageId;
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
<!-- 关联查询sql -->
|
||||
<sql id="selectSql">
|
||||
SELECT a.*, b.title as parentName, b.position as parentPosition
|
||||
SELECT a.*, b.title as parentName, b.position as parentPosition, c.name as modelName
|
||||
FROM cms_navigation a
|
||||
LEFT JOIN cms_navigation b ON a.parent_id = b.navigation_id
|
||||
LEFT JOIN cms_model c ON a.model = c.model
|
||||
<where>
|
||||
<if test="param.navigationId != null">
|
||||
AND a.navigation_id = #{param.navigationId}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package com.gxwebsoft.common.core.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* JSON解析工具类
|
||||
@@ -11,8 +16,21 @@ import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
* @since 2017-06-10 10:10:39
|
||||
*/
|
||||
public class JSONUtil {
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final ObjectWriter objectWriter = objectMapper.writerWithDefaultPrettyPrinter();
|
||||
/**
|
||||
* 注意:不要直接 new ObjectMapper() 否则不支持 Java8 时间类型(LocalDateTime 等)。
|
||||
* 这里做最小可用配置,避免在 Redis/日志/签名等场景序列化失败。
|
||||
*/
|
||||
private static final ObjectMapper objectMapper;
|
||||
private static final ObjectWriter objectWriter;
|
||||
|
||||
static {
|
||||
objectMapper = new ObjectMapper();
|
||||
objectMapper.registerModule(new JavaTimeModule());
|
||||
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
|
||||
objectWriter = objectMapper.writerWithDefaultPrettyPrinter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象转json字符串
|
||||
|
||||
@@ -54,7 +54,7 @@ import static com.gxwebsoft.common.core.constants.RedisConstants.MP_WX_KEY;
|
||||
public class WxLoginController extends BaseController {
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
private final OkHttpClient http = new OkHttpClient();
|
||||
private final ObjectMapper om = new ObjectMapper();
|
||||
private final ObjectMapper om;
|
||||
private volatile long tokenExpireEpoch = 0L; // 过期的 epoch 秒
|
||||
@Resource
|
||||
private SettingService settingService;
|
||||
@@ -80,8 +80,9 @@ public class WxLoginController extends BaseController {
|
||||
private CmsWebsiteService cmsWebsiteService;
|
||||
|
||||
|
||||
public WxLoginController(StringRedisTemplate redisTemplate) {
|
||||
public WxLoginController(StringRedisTemplate redisTemplate, ObjectMapper objectMapper) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
this.om = objectMapper;
|
||||
}
|
||||
|
||||
@Operation(summary = "获取微信AccessToken")
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.wechat.pay.java.core.http.JsonRequestBody;
|
||||
import com.wechat.pay.java.core.http.MediaType;
|
||||
import com.wechat.pay.java.core.exception.ServiceException;
|
||||
import com.wechat.pay.java.core.util.GsonUtil;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -121,6 +122,18 @@ public class WxTransferService {
|
||||
userName = null;
|
||||
}
|
||||
|
||||
// 升级版接口必填:transfer_scene_report_infos(且必须与 transfer_scene_id 的报备信息一致)
|
||||
List<TransferSceneReportInfo> sceneReportInfos = parseTransferSceneReportInfos();
|
||||
if (sceneReportInfos == null
|
||||
|| sceneReportInfos.isEmpty()
|
||||
|| sceneReportInfos.stream().anyMatch(i -> i == null
|
||||
|| StrUtil.isBlank(i.getInfoType())
|
||||
|| StrUtil.isBlank(i.getInfoContent()))) {
|
||||
throw PaymentException.paramError(
|
||||
"未传入完整且对应的转账场景报备信息:请在配置中设置 wechatpay.transfer.scene-report-infos-json(需与 transfer_scene_id="
|
||||
+ transferSceneId + " 的报备信息一致)");
|
||||
}
|
||||
|
||||
Payment paymentConfig = wxPayConfigService.getPaymentConfigForStrategy(tenantId);
|
||||
Config wxPayConfig = wxPayConfigService.getWxPayConfig(tenantId);
|
||||
|
||||
@@ -135,10 +148,9 @@ public class WxTransferService {
|
||||
request.setOpenid(openid);
|
||||
request.setTransferAmount(amountFen);
|
||||
request.setTransferRemark(limitLen(remark, 32));
|
||||
List<TransferSceneReportInfo> sceneReportInfos = parseTransferSceneReportInfos();
|
||||
if (sceneReportInfos != null && !sceneReportInfos.isEmpty()) {
|
||||
request.setTransferSceneReportInfos(sceneReportInfos);
|
||||
}
|
||||
log.debug("微信商家转账(升级版)请求参数: tenantId={}, outBillNo={}, transferSceneId={}, sceneReportInfosCount={}",
|
||||
tenantId, outBillNo, transferSceneId, sceneReportInfos.size());
|
||||
|
||||
// 可选:转账结果通知地址(必须 https)
|
||||
if (StrUtil.isNotBlank(paymentConfig.getNotifyUrl()) && paymentConfig.getNotifyUrl().startsWith("https://")) {
|
||||
@@ -244,7 +256,9 @@ public class WxTransferService {
|
||||
|
||||
@lombok.Data
|
||||
private static class TransferSceneReportInfo {
|
||||
@SerializedName(value = "info_type", alternate = {"infoType"})
|
||||
private String infoType;
|
||||
@SerializedName(value = "info_content", alternate = {"infoContent"})
|
||||
private String infoContent;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.gxwebsoft.shop.controller;
|
||||
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.gxwebsoft.common.core.utils.JSONUtil;
|
||||
import com.gxwebsoft.common.core.web.BaseController;
|
||||
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
||||
@@ -67,6 +68,10 @@ public class ShopDealerUserController extends BaseController {
|
||||
if (loginUser != null) {
|
||||
shopDealerUser.setUserId(loginUser.getUserId());
|
||||
}
|
||||
// 排重
|
||||
if (shopDealerUserService.count(new LambdaQueryWrapper<ShopDealerUser>().eq(ShopDealerUser::getMobile, shopDealerUser.getMobile())) > 0) {
|
||||
return fail("添加失败,手机号码已存在!");
|
||||
}
|
||||
if (shopDealerUserService.save(shopDealerUser)) {
|
||||
return success("添加成功", shopDealerUser);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,6 @@ public class ShopDealerWithdrawController extends BaseController {
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('shop:shopDealerWithdraw:save')")
|
||||
@OperationLog
|
||||
@Transactional(rollbackFor = {Exception.class})
|
||||
@Operation(summary = "添加分销商提现明细表")
|
||||
@PostMapping()
|
||||
@@ -87,7 +86,6 @@ public class ShopDealerWithdrawController extends BaseController {
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('shop:shopDealerWithdraw:update')")
|
||||
@OperationLog
|
||||
@Transactional(rollbackFor = {Exception.class})
|
||||
@Operation(summary = "修改分销商提现明细表")
|
||||
@PutMapping()
|
||||
|
||||
@@ -37,6 +37,12 @@ public class ShopDealerUser implements Serializable {
|
||||
@TableField(exist = false)
|
||||
private String openid;
|
||||
|
||||
@Schema(description = "店铺名称")
|
||||
private String dealerName;
|
||||
|
||||
@Schema(description = "小区名称")
|
||||
private String community;
|
||||
|
||||
@Schema(description = "头像")
|
||||
@TableField(exist = false)
|
||||
private String avatar;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gxwebsoft.shop.entity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import java.time.LocalDateTime;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
@@ -44,6 +45,10 @@ public class ShopGoods implements Serializable {
|
||||
@Schema(description = "产品分类ID")
|
||||
private Integer categoryId;
|
||||
|
||||
@Schema(description = "分类名称")
|
||||
@TableField(exist = false)
|
||||
private String categoryName;
|
||||
|
||||
@Schema(description = "路由地址")
|
||||
private String path;
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
</if>
|
||||
<if test="param.keywords != null">
|
||||
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
|
||||
OR a.user_id = #{param.keywords} OR a.dealer_name LIKE CONCAT('%', #{param.keywords}, '%') OR a.real_name LIKE CONCAT('%', #{param.keywords}, '%') OR a.mobile LIKE CONCAT('%', #{param.keywords}, '%')
|
||||
)
|
||||
</if>
|
||||
</where>
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
<!-- 关联查询sql -->
|
||||
<sql id="selectSql">
|
||||
SELECT a.*
|
||||
SELECT a.*, b.title AS categoryName
|
||||
FROM shop_goods a
|
||||
LEFT JOIN cms_navigation b ON a.category_id = b.navigation_id
|
||||
<where>
|
||||
<if test="param.goodsId != null">
|
||||
AND a.goods_id = #{param.goodsId}
|
||||
|
||||
@@ -75,3 +75,9 @@ payment:
|
||||
key-prefix: "Payment:1"
|
||||
# 缓存过期时间(小时)
|
||||
expire-hours: 24
|
||||
|
||||
# 微信支付-商家转账(升级版)转账场景报备信息(必须与商户平台 transfer_scene_id=1005 的报备信息一致)
|
||||
wechatpay:
|
||||
transfer:
|
||||
scene-id: 1005
|
||||
scene-report-infos-json: '[{"info_type":"岗位类型","info_content":"业务员"},{"info_type":"报酬说明","info_content":"配送费"}]'
|
||||
|
||||
@@ -63,3 +63,9 @@ aliyun:
|
||||
access-key-id: LTAI5tEsyhW4GCKbds1qsopg
|
||||
access-key-secret: zltFlQrYVAoq2KMFDWgLa3GhkMNeyO
|
||||
endpoint: mt.cn-hangzhou.aliyuncs.com
|
||||
|
||||
# 微信支付-商家转账(升级版)转账场景报备信息(必须与商户平台 transfer_scene_id=1005 的报备信息一致)
|
||||
wechatpay:
|
||||
transfer:
|
||||
scene-id: 1005
|
||||
scene-report-infos-json: '[{"info_type":"岗位类型","info_content":"业务员"},{"info_type":"报酬说明","info_content":"配送费"}]'
|
||||
|
||||
@@ -77,3 +77,7 @@ aliyun:
|
||||
access-key-id: LTAI5tEsyhW4GCKbds1qsopg
|
||||
access-key-secret: zltFlQrYVAoq2KMFDWgLa3GhkMNeyO
|
||||
endpoint: mt.cn-hangzhou.aliyuncs.com
|
||||
wechatpay:
|
||||
transfer:
|
||||
scene-id: 1005
|
||||
scene-report-infos-json: '[{"info_type":"岗位类型","info_content":"业务员"},{"info_type":"报酬说明","info_content":"配送费"}]'
|
||||
|
||||
@@ -75,3 +75,9 @@ payment:
|
||||
key-prefix: "Payment:1"
|
||||
# 缓存过期时间(小时)
|
||||
expire-hours: 24
|
||||
|
||||
# 微信支付-商家转账(升级版)转账场景报备信息(必须与商户平台 transfer_scene_id=1005 的报备信息一致)
|
||||
wechatpay:
|
||||
transfer:
|
||||
scene-id: 1005
|
||||
scene-report-infos-json: '[{"info_type":"岗位类型","info_content":"业务员"},{"info_type":"报酬说明","info_content":"配送费"}]'
|
||||
|
||||
Reference in New Issue
Block a user