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 = "模型")
|
@Schema(description = "模型")
|
||||||
private String model;
|
private String model;
|
||||||
|
|
||||||
|
@Schema(description = "模型名称")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String modelName;
|
||||||
|
|
||||||
@Schema(description = "标识")
|
@Schema(description = "标识")
|
||||||
private String code;
|
private String code;
|
||||||
|
|
||||||
@@ -114,9 +118,6 @@ public class CmsNavigation implements Serializable {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private Integer parentPosition;
|
private Integer parentPosition;
|
||||||
|
|
||||||
@Schema(description = "模型名称")
|
|
||||||
private String modelName;
|
|
||||||
|
|
||||||
@Schema(description = "绑定的页面(已废弃)")
|
@Schema(description = "绑定的页面(已废弃)")
|
||||||
private Integer pageId;
|
private Integer pageId;
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
|
|
||||||
<!-- 关联查询sql -->
|
<!-- 关联查询sql -->
|
||||||
<sql id="selectSql">
|
<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
|
FROM cms_navigation a
|
||||||
LEFT JOIN cms_navigation b ON a.parent_id = b.navigation_id
|
LEFT JOIN cms_navigation b ON a.parent_id = b.navigation_id
|
||||||
|
LEFT JOIN cms_model c ON a.model = c.model
|
||||||
<where>
|
<where>
|
||||||
<if test="param.navigationId != null">
|
<if test="param.navigationId != null">
|
||||||
AND a.navigation_id = #{param.navigationId}
|
AND a.navigation_id = #{param.navigationId}
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
package com.gxwebsoft.common.core.utils;
|
package com.gxwebsoft.common.core.utils;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON解析工具类
|
* JSON解析工具类
|
||||||
@@ -11,8 +16,21 @@ import com.fasterxml.jackson.databind.ObjectWriter;
|
|||||||
* @since 2017-06-10 10:10:39
|
* @since 2017-06-10 10:10:39
|
||||||
*/
|
*/
|
||||||
public class JSONUtil {
|
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字符串
|
* 对象转json字符串
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ import static com.gxwebsoft.common.core.constants.RedisConstants.MP_WX_KEY;
|
|||||||
public class WxLoginController extends BaseController {
|
public class WxLoginController extends BaseController {
|
||||||
private final StringRedisTemplate redisTemplate;
|
private final StringRedisTemplate redisTemplate;
|
||||||
private final OkHttpClient http = new OkHttpClient();
|
private final OkHttpClient http = new OkHttpClient();
|
||||||
private final ObjectMapper om = new ObjectMapper();
|
private final ObjectMapper om;
|
||||||
private volatile long tokenExpireEpoch = 0L; // 过期的 epoch 秒
|
private volatile long tokenExpireEpoch = 0L; // 过期的 epoch 秒
|
||||||
@Resource
|
@Resource
|
||||||
private SettingService settingService;
|
private SettingService settingService;
|
||||||
@@ -80,8 +80,9 @@ public class WxLoginController extends BaseController {
|
|||||||
private CmsWebsiteService cmsWebsiteService;
|
private CmsWebsiteService cmsWebsiteService;
|
||||||
|
|
||||||
|
|
||||||
public WxLoginController(StringRedisTemplate redisTemplate) {
|
public WxLoginController(StringRedisTemplate redisTemplate, ObjectMapper objectMapper) {
|
||||||
this.redisTemplate = redisTemplate;
|
this.redisTemplate = redisTemplate;
|
||||||
|
this.om = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取微信AccessToken")
|
@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.http.MediaType;
|
||||||
import com.wechat.pay.java.core.exception.ServiceException;
|
import com.wechat.pay.java.core.exception.ServiceException;
|
||||||
import com.wechat.pay.java.core.util.GsonUtil;
|
import com.wechat.pay.java.core.util.GsonUtil;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@@ -121,6 +122,18 @@ public class WxTransferService {
|
|||||||
userName = null;
|
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);
|
Payment paymentConfig = wxPayConfigService.getPaymentConfigForStrategy(tenantId);
|
||||||
Config wxPayConfig = wxPayConfigService.getWxPayConfig(tenantId);
|
Config wxPayConfig = wxPayConfigService.getWxPayConfig(tenantId);
|
||||||
|
|
||||||
@@ -135,10 +148,9 @@ public class WxTransferService {
|
|||||||
request.setOpenid(openid);
|
request.setOpenid(openid);
|
||||||
request.setTransferAmount(amountFen);
|
request.setTransferAmount(amountFen);
|
||||||
request.setTransferRemark(limitLen(remark, 32));
|
request.setTransferRemark(limitLen(remark, 32));
|
||||||
List<TransferSceneReportInfo> sceneReportInfos = parseTransferSceneReportInfos();
|
request.setTransferSceneReportInfos(sceneReportInfos);
|
||||||
if (sceneReportInfos != null && !sceneReportInfos.isEmpty()) {
|
log.debug("微信商家转账(升级版)请求参数: tenantId={}, outBillNo={}, transferSceneId={}, sceneReportInfosCount={}",
|
||||||
request.setTransferSceneReportInfos(sceneReportInfos);
|
tenantId, outBillNo, transferSceneId, sceneReportInfos.size());
|
||||||
}
|
|
||||||
|
|
||||||
// 可选:转账结果通知地址(必须 https)
|
// 可选:转账结果通知地址(必须 https)
|
||||||
if (StrUtil.isNotBlank(paymentConfig.getNotifyUrl()) && paymentConfig.getNotifyUrl().startsWith("https://")) {
|
if (StrUtil.isNotBlank(paymentConfig.getNotifyUrl()) && paymentConfig.getNotifyUrl().startsWith("https://")) {
|
||||||
@@ -244,7 +256,9 @@ public class WxTransferService {
|
|||||||
|
|
||||||
@lombok.Data
|
@lombok.Data
|
||||||
private static class TransferSceneReportInfo {
|
private static class TransferSceneReportInfo {
|
||||||
|
@SerializedName(value = "info_type", alternate = {"infoType"})
|
||||||
private String infoType;
|
private String infoType;
|
||||||
|
@SerializedName(value = "info_content", alternate = {"infoContent"})
|
||||||
private String infoContent;
|
private String infoContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.gxwebsoft.shop.controller;
|
|||||||
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
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.utils.JSONUtil;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
||||||
@@ -67,6 +68,10 @@ public class ShopDealerUserController extends BaseController {
|
|||||||
if (loginUser != null) {
|
if (loginUser != null) {
|
||||||
shopDealerUser.setUserId(loginUser.getUserId());
|
shopDealerUser.setUserId(loginUser.getUserId());
|
||||||
}
|
}
|
||||||
|
// 排重
|
||||||
|
if (shopDealerUserService.count(new LambdaQueryWrapper<ShopDealerUser>().eq(ShopDealerUser::getMobile, shopDealerUser.getMobile())) > 0) {
|
||||||
|
return fail("添加失败,手机号码已存在!");
|
||||||
|
}
|
||||||
if (shopDealerUserService.save(shopDealerUser)) {
|
if (shopDealerUserService.save(shopDealerUser)) {
|
||||||
return success("添加成功", shopDealerUser);
|
return success("添加成功", shopDealerUser);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ public class ShopDealerWithdrawController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopDealerWithdraw:save')")
|
@PreAuthorize("hasAuthority('shop:shopDealerWithdraw:save')")
|
||||||
@OperationLog
|
|
||||||
@Transactional(rollbackFor = {Exception.class})
|
@Transactional(rollbackFor = {Exception.class})
|
||||||
@Operation(summary = "添加分销商提现明细表")
|
@Operation(summary = "添加分销商提现明细表")
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
@@ -87,7 +86,6 @@ public class ShopDealerWithdrawController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopDealerWithdraw:update')")
|
@PreAuthorize("hasAuthority('shop:shopDealerWithdraw:update')")
|
||||||
@OperationLog
|
|
||||||
@Transactional(rollbackFor = {Exception.class})
|
@Transactional(rollbackFor = {Exception.class})
|
||||||
@Operation(summary = "修改分销商提现明细表")
|
@Operation(summary = "修改分销商提现明细表")
|
||||||
@PutMapping()
|
@PutMapping()
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ public class ShopDealerUser implements Serializable {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String openid;
|
private String openid;
|
||||||
|
|
||||||
|
@Schema(description = "店铺名称")
|
||||||
|
private String dealerName;
|
||||||
|
|
||||||
|
@Schema(description = "小区名称")
|
||||||
|
private String community;
|
||||||
|
|
||||||
@Schema(description = "头像")
|
@Schema(description = "头像")
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String avatar;
|
private String avatar;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.gxwebsoft.shop.entity;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
@@ -44,6 +45,10 @@ public class ShopGoods implements Serializable {
|
|||||||
@Schema(description = "产品分类ID")
|
@Schema(description = "产品分类ID")
|
||||||
private Integer categoryId;
|
private Integer categoryId;
|
||||||
|
|
||||||
|
@Schema(description = "分类名称")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String categoryName;
|
||||||
|
|
||||||
@Schema(description = "路由地址")
|
@Schema(description = "路由地址")
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
</if>
|
</if>
|
||||||
<if test="param.keywords != null">
|
<if test="param.keywords != null">
|
||||||
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
|
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>
|
</if>
|
||||||
</where>
|
</where>
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
<!-- 关联查询sql -->
|
<!-- 关联查询sql -->
|
||||||
<sql id="selectSql">
|
<sql id="selectSql">
|
||||||
SELECT a.*
|
SELECT a.*, b.title AS categoryName
|
||||||
FROM shop_goods a
|
FROM shop_goods a
|
||||||
|
LEFT JOIN cms_navigation b ON a.category_id = b.navigation_id
|
||||||
<where>
|
<where>
|
||||||
<if test="param.goodsId != null">
|
<if test="param.goodsId != null">
|
||||||
AND a.goods_id = #{param.goodsId}
|
AND a.goods_id = #{param.goodsId}
|
||||||
|
|||||||
@@ -75,3 +75,9 @@ payment:
|
|||||||
key-prefix: "Payment:1"
|
key-prefix: "Payment:1"
|
||||||
# 缓存过期时间(小时)
|
# 缓存过期时间(小时)
|
||||||
expire-hours: 24
|
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-id: LTAI5tEsyhW4GCKbds1qsopg
|
||||||
access-key-secret: zltFlQrYVAoq2KMFDWgLa3GhkMNeyO
|
access-key-secret: zltFlQrYVAoq2KMFDWgLa3GhkMNeyO
|
||||||
endpoint: mt.cn-hangzhou.aliyuncs.com
|
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-id: LTAI5tEsyhW4GCKbds1qsopg
|
||||||
access-key-secret: zltFlQrYVAoq2KMFDWgLa3GhkMNeyO
|
access-key-secret: zltFlQrYVAoq2KMFDWgLa3GhkMNeyO
|
||||||
endpoint: mt.cn-hangzhou.aliyuncs.com
|
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"
|
key-prefix: "Payment:1"
|
||||||
# 缓存过期时间(小时)
|
# 缓存过期时间(小时)
|
||||||
expire-hours: 24
|
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