diff --git a/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/dto/QueryUserVipDto.java b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/dto/QueryUserVipDto.java new file mode 100644 index 0000000..689c6ee --- /dev/null +++ b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/dto/QueryUserVipDto.java @@ -0,0 +1,23 @@ +package com.bnyer.img.api.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +/** + * @author :WXC + * @Date :2023/03/31 + * @description : + */ +@Getter +@Setter +@ApiModel("会员vip查询类") +public class QueryUserVipDto { + @ApiModelProperty(value="主键Id") + private Long id; + + @ApiModelProperty(value="vip客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信") + private Integer userClientType; + +} diff --git a/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/factory/RemoteImgFallbackFactory.java b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/factory/RemoteImgFallbackFactory.java index 0c2c1eb..576254b 100644 --- a/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/factory/RemoteImgFallbackFactory.java +++ b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/factory/RemoteImgFallbackFactory.java @@ -5,7 +5,7 @@ import com.bnyer.common.core.dto.CreatorLoginDto; import com.bnyer.common.core.dto.FhLoginDto; import com.bnyer.common.core.dto.TiktokLoginDto; import com.bnyer.common.core.dto.WxLoginDto; -import com.bnyer.img.api.RemoteImgService; +import com.bnyer.img.api.remote.RemoteImgService; import com.bnyer.img.api.dto.TiktokImgMiniDto; import com.bnyer.img.api.model.LoginCreator; import com.bnyer.img.api.model.LoginFhUser; diff --git a/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/factory/RemoteWxMiniFallbackFactory.java b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/factory/RemoteWxMiniFallbackFactory.java new file mode 100644 index 0000000..3196423 --- /dev/null +++ b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/factory/RemoteWxMiniFallbackFactory.java @@ -0,0 +1,36 @@ +package com.bnyer.img.api.factory; + +import com.bnyer.common.core.domain.R; +import com.bnyer.img.api.dto.QueryUserVipDto; +import com.bnyer.img.api.remote.RemoteWxMiniService; +import com.bnyer.img.api.vo.UserVipInfoVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 图文服务降级处理 + * + * @author penny + */ +@Component +public class RemoteWxMiniFallbackFactory implements FallbackFactory +{ + private static final Logger log = LoggerFactory.getLogger(RemoteWxMiniFallbackFactory.class); + + + @Override + public RemoteWxMiniService create(Throwable throwable) { + log.error("api图文服务调用失败:{}", throwable.getMessage()); + return new RemoteWxMiniService() { + @Override + public R> queryUserVipList(QueryUserVipDto dto) { + return R.fail("获取会员信息失败:+"+throwable.getMessage()); + } + + }; + } +} diff --git a/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/RemoteImgService.java b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/remote/RemoteImgService.java similarity index 98% rename from bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/RemoteImgService.java rename to bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/remote/RemoteImgService.java index 5d30c78..a91586f 100644 --- a/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/RemoteImgService.java +++ b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/remote/RemoteImgService.java @@ -1,4 +1,4 @@ -package com.bnyer.img.api; +package com.bnyer.img.api.remote; import com.bnyer.common.core.constant.ServiceNameConstants; import com.bnyer.common.core.domain.R; diff --git a/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/remote/RemoteWxMiniService.java b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/remote/RemoteWxMiniService.java new file mode 100644 index 0000000..578a966 --- /dev/null +++ b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/remote/RemoteWxMiniService.java @@ -0,0 +1,27 @@ +package com.bnyer.img.api.remote; + +import com.bnyer.common.core.constant.ServiceNameConstants; +import com.bnyer.common.core.domain.R; +import com.bnyer.img.api.dto.QueryUserVipDto; +import com.bnyer.img.api.factory.RemoteWxMiniFallbackFactory; +import com.bnyer.img.api.vo.UserVipInfoVo; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +/** + * @author :WXC + * @description : + */ +@FeignClient(contextId = "remoteWxMiniService", value = ServiceNameConstants.IMG_SERVICE, fallbackFactory = RemoteWxMiniFallbackFactory.class) +public interface RemoteWxMiniService { + + /** + * 获取会员列表 + * @return + */ + @PostMapping(value = "/img/mini/fh/queryUserVipList") + R> queryUserVipList(@RequestBody QueryUserVipDto dto); +} diff --git a/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/vo/UserVipInfoVo.java b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/vo/UserVipInfoVo.java new file mode 100644 index 0000000..dad6916 --- /dev/null +++ b/bnyer-api/bnyer-api-img/src/main/java/com/bnyer/img/api/vo/UserVipInfoVo.java @@ -0,0 +1,63 @@ +package com.bnyer.img.api.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.math.BigDecimal; + + +@Getter +@Setter +@ApiModel("会员vip响应类") +public class UserVipInfoVo implements Serializable { + + @ApiModelProperty(value="主键Id") + private Long id; + + @ApiModelProperty(value="vip类型id") + private Long vipTypeId; + + @ApiModelProperty(value="vip编码") + private String vipCode; + + @ApiModelProperty(value="vip名称") + private String vipName; + + @ApiModelProperty(value="原价") + private BigDecimal originPrice; + + @ApiModelProperty(value="售价") + private BigDecimal price; + + @ApiModelProperty(value="描述") + private String description; + + @ApiModelProperty(value="热门描述") + private String hotSignDesc; + + @ApiModelProperty(value="排序") + private Integer sort; + + @ApiModelProperty(value="是否到期自动续费(0>否;1->是)") + private String isDelay; + + @ApiModelProperty(value = "用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信") + private Integer userClientType; + + @ApiModelProperty(value = "有效时长单位:0天,1周,2月,3季,4年") + private Integer validTimeUnit; + + @ApiModelProperty(value = "有效时长:表示几天、几周、几月、几年") + private Integer validTimeNum; + + @ApiModelProperty(value = "会员类型编码") + private String vipTypeCode; + + @ApiModelProperty(value = "会员类型名称") + private String vipTypeName; + + private static final long serialVersionUID = 1L; +} diff --git a/bnyer-api/bnyer-api-order/pom.xml b/bnyer-api/bnyer-api-order/pom.xml new file mode 100644 index 0000000..11675c8 --- /dev/null +++ b/bnyer-api/bnyer-api-order/pom.xml @@ -0,0 +1,26 @@ + + + + com.dimensionalnode + bnyer-api + 1.0.0 + + 4.0.0 + + bnyer-api-order + + + bnyer-api-order订单服务接口模块 + + + + + + com.dimensionalnode + bnyer-common-core + + + + diff --git a/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/dto/QueryVipOrderDto.java b/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/dto/QueryVipOrderDto.java new file mode 100644 index 0000000..f4f4d1a --- /dev/null +++ b/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/dto/QueryVipOrderDto.java @@ -0,0 +1,23 @@ +package com.bnyer.order.api.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class QueryVipOrderDto implements Serializable { + + @ApiModelProperty(value="订单号") + private String orderNo; + +} diff --git a/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/factory/RemoteVipOrderFallbackFactory.java b/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/factory/RemoteVipOrderFallbackFactory.java new file mode 100644 index 0000000..be0e0f9 --- /dev/null +++ b/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/factory/RemoteVipOrderFallbackFactory.java @@ -0,0 +1,36 @@ +package com.bnyer.order.api.factory; + +import com.bnyer.common.core.domain.R; +import com.bnyer.order.api.dto.QueryVipOrderDto; +import com.bnyer.order.api.remote.RemoteVipOrderService; +import com.bnyer.order.api.vo.VipOrderVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description :订单服务:vip订单接口熔断降级 + */ +@Component +public class RemoteVipOrderFallbackFactory implements FallbackFactory +{ + private static final Logger log = LoggerFactory.getLogger(RemoteVipOrderFallbackFactory.class); + + + @Override + public RemoteVipOrderService create(Throwable throwable) { + log.error("api订单服务调用失败:{}", throwable.getMessage()); + return new RemoteVipOrderService() { + + @Override + public R> getVipOrderList(QueryVipOrderDto dto) { + return R.fail("获取会员订单信息失败:+"+throwable.getMessage()); + } + }; + } +} diff --git a/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/remote/RemoteVipOrderService.java b/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/remote/RemoteVipOrderService.java new file mode 100644 index 0000000..32319bb --- /dev/null +++ b/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/remote/RemoteVipOrderService.java @@ -0,0 +1,29 @@ +package com.bnyer.order.api.remote; + +import com.bnyer.common.core.constant.ServiceNameConstants; +import com.bnyer.common.core.domain.R; +import com.bnyer.order.api.dto.QueryVipOrderDto; +import com.bnyer.order.api.factory.RemoteVipOrderFallbackFactory; +import com.bnyer.order.api.vo.VipOrderVo; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description :订单服务:vip订单接口远程调用 + */ +@FeignClient(contextId = "remoteVipOrderService", value = ServiceNameConstants.ORDER_SERVICE, fallbackFactory = RemoteVipOrderFallbackFactory.class) +public interface RemoteVipOrderService { + + /** + * 获取会员订单信息 + * @param dto + * @return + */ + @PostMapping("/getVipOrderList") + R> getVipOrderList(@RequestBody QueryVipOrderDto dto); +} diff --git a/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/vo/VipOrderVo.java b/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/vo/VipOrderVo.java new file mode 100644 index 0000000..9c1c480 --- /dev/null +++ b/bnyer-api/bnyer-api-order/src/main/java/com/bnyer/order/api/vo/VipOrderVo.java @@ -0,0 +1,69 @@ +package com.bnyer.order.api.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class VipOrderVo { + + @ApiModelProperty(value="主键") + private Long id; + + @ApiModelProperty(value="订单号") + private String orderNo; + + @ApiModelProperty(value="手机号") + private String phone; + + @ApiModelProperty(value="用户id") + private Long userId; + + @ApiModelProperty(value="vip表id") + private Long vipId; + + @ApiModelProperty(value = "vip编码") + private String vipCode; + + @ApiModelProperty(value = "vip名称") + private String vipName; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value="开始时间") + private Date startTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value="到期时间") + private Date endTime; + + @ApiModelProperty(value = "用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信") + private Integer userClientType; + + @ApiModelProperty(value="是否到期自动续费(0>否;1->是)") + private String isDelay; + + @ApiModelProperty(value="时长天数") + private Integer days; + + @ApiModelProperty(value="支付金额") + private BigDecimal payAmount; + + @ApiModelProperty(value = "vip类型名称") + private String vipTypeName; + + @ApiModelProperty(value="支付状态:1000未支付;1001支付成功 ;1002支付失败") + private Integer payStatus; + +} diff --git a/bnyer-api/pom.xml b/bnyer-api/pom.xml index 69e5e4d..c3f6e43 100644 --- a/bnyer-api/pom.xml +++ b/bnyer-api/pom.xml @@ -12,6 +12,7 @@ bnyer-api-system bnyer-api-img bnyer-api-file + bnyer-api-order bnyer-api diff --git a/bnyer-auth/src/main/java/com/bnyer/auth/service/CreatorLoginService.java b/bnyer-auth/src/main/java/com/bnyer/auth/service/CreatorLoginService.java index b41251a..e507945 100644 --- a/bnyer-auth/src/main/java/com/bnyer/auth/service/CreatorLoginService.java +++ b/bnyer-auth/src/main/java/com/bnyer/auth/service/CreatorLoginService.java @@ -4,7 +4,7 @@ import com.bnyer.common.core.constant.TiktokConstant; import com.bnyer.common.core.domain.R; import com.bnyer.common.core.dto.CreatorLoginDto; import com.bnyer.common.core.exception.ServiceException; -import com.bnyer.img.api.RemoteImgService; +import com.bnyer.img.api.remote.RemoteImgService; import com.bnyer.img.api.model.LoginCreator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -36,4 +36,4 @@ public class CreatorLoginService { return creatorInfo.getData(); } -} \ No newline at end of file +} diff --git a/bnyer-auth/src/main/java/com/bnyer/auth/service/FhUserLoginService.java b/bnyer-auth/src/main/java/com/bnyer/auth/service/FhUserLoginService.java index 3f16429..6008724 100644 --- a/bnyer-auth/src/main/java/com/bnyer/auth/service/FhUserLoginService.java +++ b/bnyer-auth/src/main/java/com/bnyer/auth/service/FhUserLoginService.java @@ -1,7 +1,7 @@ package com.bnyer.auth.service; import com.bnyer.common.core.dto.FhLoginDto; -import com.bnyer.img.api.RemoteImgService; +import com.bnyer.img.api.remote.RemoteImgService; import com.bnyer.img.api.model.LoginFhUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -21,4 +21,4 @@ public class FhUserLoginService { return remoteImgService.getFhLoginUserByLoginParam(dto).getData(); } -} \ No newline at end of file +} diff --git a/bnyer-auth/src/main/java/com/bnyer/auth/service/TiktokUserLoginService.java b/bnyer-auth/src/main/java/com/bnyer/auth/service/TiktokUserLoginService.java index 24018be..3dfefe8 100644 --- a/bnyer-auth/src/main/java/com/bnyer/auth/service/TiktokUserLoginService.java +++ b/bnyer-auth/src/main/java/com/bnyer/auth/service/TiktokUserLoginService.java @@ -1,14 +1,14 @@ package com.bnyer.auth.service; import com.bnyer.common.core.dto.TiktokLoginDto; -import com.bnyer.img.api.RemoteImgService; +import com.bnyer.img.api.remote.RemoteImgService; import com.bnyer.img.api.model.LoginTiktokUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 抖音小程序端用户登录校验方法 - * + * * @author penny */ @Component @@ -21,4 +21,4 @@ public class TiktokUserLoginService { return remoteImgService.getTiktokLoginUserByLoginParam(dto).getData(); } -} \ No newline at end of file +} diff --git a/bnyer-auth/src/main/java/com/bnyer/auth/service/WxUserLoginService.java b/bnyer-auth/src/main/java/com/bnyer/auth/service/WxUserLoginService.java index aa2b169..31ac702 100644 --- a/bnyer-auth/src/main/java/com/bnyer/auth/service/WxUserLoginService.java +++ b/bnyer-auth/src/main/java/com/bnyer/auth/service/WxUserLoginService.java @@ -1,9 +1,7 @@ package com.bnyer.auth.service; -import com.bnyer.common.core.dto.FhLoginDto; import com.bnyer.common.core.dto.WxLoginDto; -import com.bnyer.img.api.RemoteImgService; -import com.bnyer.img.api.model.LoginFhUser; +import com.bnyer.img.api.remote.RemoteImgService; import com.bnyer.img.api.model.LoginWechatUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -23,4 +21,4 @@ public class WxUserLoginService { return remoteImgService.getWxLoginUserByLoginParam(dto).getData(); } -} \ No newline at end of file +} diff --git a/bnyer-common/bnyer-common-core/pom.xml b/bnyer-common/bnyer-common-core/pom.xml index dfa52ae..d1d4b4c 100644 --- a/bnyer-common/bnyer-common-core/pom.xml +++ b/bnyer-common/bnyer-common-core/pom.xml @@ -65,6 +65,11 @@ jackson-databind + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + com.alibaba diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/annotation/CustomParamsValidation.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/annotation/CustomParamsValidation.java new file mode 100644 index 0000000..861c600 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/annotation/CustomParamsValidation.java @@ -0,0 +1,351 @@ +package com.bnyer.common.core.annotation; + + +import org.apache.commons.lang3.StringUtils; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.lang.annotation.ElementType.*; + +/** + * @author :WXC + * @Date :2023/04/11 + * @description : + */ +@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint(validatedBy = { CustomParamsValidation.CustomParamsValidationImpl.class }) +public @interface CustomParamsValidation { + + /** + * 预定义的正则表达式 + */ + class Regexp{ + /** + * 常规输入框:匹配中文,字母,数字 + */ + private final static String INPUTTXT="^[A-Za-z0-9\u4e00-\u9fa5]*$"; + + /** + * 昵称匹配:匹配中文,字母,数字 下划线 + */ + private final static String NICKNAME="^[A-Za-z0-9_\u4e00-\u9fa5]*$"; + + /**********以上public 的正则提供 regexp 属性使用**********/ + + /**********以下private 的正则提供 paramType 计算**********/ + /** + * 匹配邮箱 + */ + private final static String EMAIL="^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$"; + /** + * uuid + * 格式 + * {8}-{4}-{4}-{4}-{12} + * 如 + * 4cbd6c8b-1111-45f4-b477-a524707aedfd + */ + private static final String UUID="^[a-z0-9]{8}(-[a-z0-9]{4}){3}-[a-z0-9]{12}$"; + /** + * yyyy-MM-dd + */ + private static final String DATE = "^[1-9][0-9]{3}-((01|03|05|07|08|10|12)-(0[1-9]|[1-2][0-9]|30|31)|(04|06|09|11)-(0[1-9]|[1-2][0-9]|30)|(02)-(0[1-9]|1[0-9]|2[0-9]))$"; + /** + * yyyyMMdd + */ + private static final String DATE2 = "^[1-9][0-9]{3}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|30|31)|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|(02)(0[1-9]|1[0-9]|2[0-9]))$"; + /** + * yyyy-MM-dd HH:mm:ss + */ + private static final String DATETIME = "^[1-9][0-9]{3}-((01|03|05|07|08|10|12)-(0[1-9]|[1-2][0-9]|30|31)|(04|06|09|11)-(0[1-9]|[1-2][0-9]|30)|(02)-(0[1-9]|1[0-9]|2[0-9])) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"; + /** + * yyyyMMddHHmmss + */ + private static final String DATETIME2 = "^[1-9][0-9]{3}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|30|31)|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|(02)(0[1-9]|1[0-9]|2[0-9]))([01][0-9]|2[0-3])([0-5][0-9])([0-5][0-9])$"; + /** + * yyyy-MM-dd HH:mm + */ + private static final String DATETIME3 = "^[1-9][0-9]{3}-((01|03|05|07|08|10|12)-(0[1-9]|[1-2][0-9]|30|31)|(04|06|09|11)-(0[1-9]|[1-2][0-9]|30)|(02)-(0[1-9]|1[0-9]|2[0-9])) ([01][0-9]|2[0-3]):([0-5][0-9])$"; + + /** + * HH:mm:ss + */ + private static final String TIME = "^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"; + /** + * HHmmss + */ + private static final String TIME2 = "^([01][0-9]|2[0-3])([0-5][0-9])([0-5][0-9])$"; + + /** + * 手机号码 + */ + private static final String MOBILEPHONE="^1\\d{10}$"; + + /** + * 匹配IPV4地址 + */ + private final static String IPV4="((25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))"; + //=============可以在此扩展正则============= + + + /** + * 校验正则 + * @param regexp + * @param value + * @return + */ + public static boolean validateRegexp(String regexp,String value){ + Pattern pattern = Pattern.compile(regexp); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } + } + + + /** + * 预定义的参数类型 + */ + class ParamType { + //匹配IPV4地址 + public static final String IPV4 = "ipv4"; + /** + * uuid + */ + public static final String UUID="uuid"; + /** + * 匹配中文 + */ + public static final String INPUTTXT="inputtxt"; + /** + * 匹配中文英文数字下划线 + */ + public static final String NICKNAME="nickname"; + + /** + * 匹配邮箱 + */ + public static final String EMAIL="email"; + + /** + * 手机号码 + */ + public static final String MOBILEPHONE="mobilephone"; + + /** + * yyyy-MM-dd + */ + public static final String DATE = "yyyy-MM-dd"; + /** + * yyyyMMdd + */ + public static final String DATE2 = "yyyyMMdd"; + /** + * yyyy-MM-dd HH:mm:ss + */ + public static final String DATETIME = "yyyy-MM-dd HH:mm:ss"; + /** + * yyyyMMddHHmmss + */ + public static final String DATETIME2 = "yyyyMMddHHmmss"; + /** + * yyyy-MM-dd HH:mm + */ + public static final String DATETIME3 = "yyyy-MM-dd HH:mm"; + + /** + * HH:mm:ss + */ + public static final String TIME = "HH:mm:ss"; + /** + * HHmmss + */ + public static final String TIME2 = "HHmmss"; + /** + * 参数类型校验 + * + * @param paramType + * @param value + * @return + */ + public static boolean validateParamType(String paramType, String value) { + boolean flag = false; + + switch (paramType) { + case ParamType.IPV4: + value = value.replaceAll(" ",""); + flag = Regexp.validateRegexp(Regexp.IPV4, value); + break; + case ParamType.MOBILEPHONE: flag = Regexp.validateRegexp(Regexp.MOBILEPHONE, value);break; + case ParamType.NICKNAME: flag = Regexp.validateRegexp(Regexp.NICKNAME, value);break; + case ParamType.EMAIL: flag = Regexp.validateRegexp(Regexp.EMAIL, value);break; + case ParamType.UUID : flag = Regexp.validateRegexp(Regexp.UUID, value);break; + case ParamType.INPUTTXT : flag = Regexp.validateRegexp(Regexp.INPUTTXT, value);break; + case ParamType.DATE : flag = validateDateParamType(ParamType.DATE, value);break; + case ParamType.DATE2 : flag = validateDateParamType(ParamType.DATE2, value);break; + case ParamType.DATETIME : flag = validateDateParamType(ParamType.DATETIME, value);break; + case ParamType.DATETIME2 : flag = validateDateParamType(ParamType.DATETIME2, value);break; + case ParamType.DATETIME3 : flag = validateDateParamType(ParamType.DATETIME3, value);break; + case ParamType.TIME : flag = validateDateParamType(ParamType.TIME, value);break; + case ParamType.TIME2 : flag = validateDateParamType(ParamType.TIME2, value);break; + } + return flag; + } + + /** + * dateFormat 属性配置,配置的值 不是内置值,验证无法通过 + * @param dateFormat + * @param value + * @return + */ + private static boolean validateDateParamType(String dateFormat,String value){ + String regexp = null; + + if(DATE.equals(dateFormat)){ + regexp = Regexp.DATE; + }else if(DATE2.equals(dateFormat)){ + regexp = Regexp.DATE2; + }else if(DATETIME.equals(dateFormat)){ + regexp = Regexp.DATETIME; + }else if(DATETIME2.equals(dateFormat)){ + regexp = Regexp.DATETIME2; + }else if(DATETIME3.equals(dateFormat)){ + regexp = Regexp.DATETIME3; + }else if(TIME.equals(dateFormat)){ + regexp = Regexp.TIME; + }else if(TIME2.equals(dateFormat)){ + regexp = Regexp.TIME2; + } + + if(regexp!=null){ + boolean flag = Regexp.validateRegexp(regexp, value); + //时间不用做2月29日瑞年判断 + if(TIME.equals(dateFormat) || TIME2.equals(dateFormat)){ + return flag; + } + if(flag){ + //月份和日期部分 + String monthAndDay = ""; + //2月29日常量 + String monthAndDayConstant = ""; + Integer year = null; + switch (dateFormat) { + case DATE: + case DATETIME: + case DATETIME3: + monthAndDay = StringUtils.substring(value, 5, 10); + monthAndDayConstant = "02-29"; + year = Integer.valueOf(StringUtils.substring(value, 0, 4)); + break; + case DATE2: + case DATETIME2: + monthAndDay = StringUtils.substring(value, 4, 8); + monthAndDayConstant = "0229"; + year = Integer.valueOf(StringUtils.substring(value, 0, 4)); + break; + } + //2月29日判断是否是润年,非润年没有2月29日 + if(monthAndDay.equals(monthAndDayConstant)){ + if(!isLeapYear(year)){ + flag = false; + } + } + } + return flag; + } + + return false; + } + + /** + * 判断是否是润年 + * @param year + * @return + */ + private static boolean isLeapYear(int year){ + return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0); + } + } + + + /** + * 预定义Range + * + */ + class Range{ + private static boolean contains(String range,String value){ + boolean flag = false; + if(StringUtils.isEmpty(value)){ + return false; + } + if(StringUtils.isNotEmpty(range)){ + String[] arr = range.split("\\|"); + for (String string : arr) { + if(value.equals(string)){ + flag = true; + break; + } + } + } + return flag; + } + } + + + /** + * 参数类型,最优先推荐使用的属性 + * @return + */ + String paramType() default ""; + + /** + * 限制字符串或者数字 可选项,用 | 分割 ,如 00|10|20 + * @return + */ + String range() default ""; + + String message() default "参数基本校验不通过错误:注意格式及长度"; + + Class[]groups() default {}; + + Class[]payload() default {}; + + class CustomParamsValidationImpl implements ConstraintValidator { + + String paramType; + + String range; + + + @Override + public void initialize(CustomParamsValidation constraintAnnotation) { + paramType = constraintAnnotation.paramType(); + range = constraintAnnotation.range(); + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if(StringUtils.isNotEmpty(value)){ + //根据参数类型校验正则 + if(StringUtils.isNotEmpty(paramType)){ + return ParamType.validateParamType(paramType, value); + } + //校验参数范围 + if (StringUtils.isNotEmpty(range)){ + return Range.contains(range, value); + } + } + return true; + } + } + +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/constant/RedisKeyConstant.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/constant/RedisKeyConstant.java index 9d20096..178f6eb 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/constant/RedisKeyConstant.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/constant/RedisKeyConstant.java @@ -151,4 +151,9 @@ public class RedisKeyConstant { * 热搜词存入时间 */ public static final String HOT_KEY_WORD_TIME_KEY = "bnyer.hotkeywordtime"; + + /** + * 支付回调rediskey + */ + public static final String PAY_NOTIFY_LOCK_KEY = "bnyer.pay.notify.lock:"; } diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/constant/ServiceNameConstants.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/constant/ServiceNameConstants.java index edf8047..275279b 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/constant/ServiceNameConstants.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/constant/ServiceNameConstants.java @@ -27,6 +27,11 @@ public class ServiceNameConstants */ public static final String IMG_SERVICE = "bnyer-img"; + /** + * 订单服务的serviceid + */ + public static final String ORDER_SERVICE = "bnyer-order"; + /** * 文件服务的serviceid */ diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/AlipayConfig.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/AlipayConfig.java new file mode 100644 index 0000000..1378c7a --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/AlipayConfig.java @@ -0,0 +1,77 @@ +package com.bnyer.common.core.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author :WXC + * @description : + */ +/** + * 支付宝支付配置表 + */ +@ApiModel(value="com-bnyer-common-core-domain-PayAlipayConfig") +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "pay_alipay_config") +public class AlipayConfig extends BaseDomain { + + /** + * appid + */ + @TableField(value = "appid") + @ApiModelProperty(value="appid") + private String appid; + + /** + * app私钥 + */ + @TableField(value = "app_private_key") + @ApiModelProperty(value="app私钥") + private String appPrivateKey; + + /** + * 阿里公钥 + */ + @TableField(value = "alipay_public_key") + @ApiModelProperty(value="阿里公钥") + private String alipayPublicKey; + + /** + * RSA/RSA2 + */ + @TableField(value = "key_type") + @ApiModelProperty(value="RSA/RSA2") + private String keyType; + + /** + * 回调地址url + */ + @TableField(value = "backurl") + @ApiModelProperty(value="回调地址url") + private String backurl; + + /** + * 帐号状态(0正常 1停用) + */ + @TableField(value = "status") + @ApiModelProperty(value="帐号状态(0正常 1停用)") + private String status; + + @TableField(value = "remark") + @ApiModelProperty(value="") + private String remark; +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/DypayConfig.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/DypayConfig.java new file mode 100644 index 0000000..ee4ee11 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/DypayConfig.java @@ -0,0 +1,69 @@ +package com.bnyer.common.core.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author :WXC + * @description : + */ +/** + * 抖音支付配置表 + */ +@ApiModel(value="com-bnyer-common-core-domain-PayDypayConfig") +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "pay_dypay_config") +public class DypayConfig extends BaseDomain { + /** + * appid + */ + @TableField(value = "appid") + @ApiModelProperty(value="appid") + private String appid; + + /** + * 秘钥 + */ + @TableField(value = "salt") + @ApiModelProperty(value="秘钥") + private String salt; + + /** + * 令牌 + */ + @TableField(value = "token") + @ApiModelProperty(value="令牌") + private String token; + + /** + * 回调地址url + */ + @TableField(value = "backurl") + @ApiModelProperty(value="回调地址url") + private String backurl; + + /** + * 帐号状态(0正常 1停用) + */ + @TableField(value = "status") + @ApiModelProperty(value="帐号状态(0正常 1停用)") + private String status; + + @TableField(value = "remark") + @ApiModelProperty(value="") + private String remark; +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/KspayConfig.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/KspayConfig.java new file mode 100644 index 0000000..84f2c3f --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/KspayConfig.java @@ -0,0 +1,62 @@ +package com.bnyer.common.core.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author :WXC + * @description : + */ +/** + * 快手支付配置表 + */ +@ApiModel(value="com-bnyer-common-core-domain-PayKspayConfig") +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "pay_kspay_config") +public class KspayConfig extends BaseDomain { + /** + * appid + */ + @TableField(value = "appid") + @ApiModelProperty(value="appid") + private String appid; + + /** + * 秘钥 + */ + @TableField(value = "secret") + @ApiModelProperty(value="秘钥") + private String secret; + + /** + * 回调地址url + */ + @TableField(value = "backurl") + @ApiModelProperty(value="回调地址url") + private String backurl; + + /** + * 帐号状态(0正常 1停用) + */ + @TableField(value = "status") + @ApiModelProperty(value="帐号状态(0正常 1停用)") + private String status; + + @TableField(value = "remark") + @ApiModelProperty(value="") + private String remark; +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/PayInfo.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/PayInfo.java new file mode 100644 index 0000000..913f9b4 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/PayInfo.java @@ -0,0 +1,180 @@ +package com.bnyer.common.core.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author :WXC + * @description : + */ +/** + * 付款信息表 + */ +@ApiModel(value="com-bnyer-common-core-domain-PayPayInfo") +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "pay_pay_info") +public class PayInfo extends BaseDomain { + + /** + * 支付单号(内部生成) + */ + @TableField(value = "pay_id") + @ApiModelProperty(value="支付单号(内部生成)") + private String payId; + + /** + * 业务主订单号:关联内部业务订单表 + */ + @TableField(value = "order_no") + @ApiModelProperty(value="业务主订单号:关联内部业务订单表") + private String orderNo; + + /** + * 支付状态:1000未支付;1001支付成功 ;1002支付失败 + */ + @TableField(value = "pay_status") + @ApiModelProperty(value="支付状态:1000未支付;1001支付成功 ;1002支付失败") + private Integer payStatus; + + /** + * 单笔对账状态:1001 对账成功 + */ + @TableField(value = "single_status") + @ApiModelProperty(value="单笔对账状态:1001 对账成功") + private Integer singleStatus; + + /** + * 单笔对账时间 + */ + @TableField(value = "single_time") + @ApiModelProperty(value="单笔对账时间") + private Date singleTime; + + + /** + * 支付类型:wxpay/alipay/kspay/dypay + */ + @TableField(value = "pay_type") + @ApiModelProperty(value="支付类型:wxpay/alipay/kspay/dypay") + private String payType; + + /** + * 支付渠道 + */ + @TableField(value = "pay_channel") + @ApiModelProperty(value="支付渠道:wxpay/alipay") + private String payChannel; + + /** + * 交易类型:JSAPI等 + */ + @TableField(value = "trade_type") + @ApiModelProperty(value = "交易类型:JSAPI等") + private String tradeType; + + /** + * 支付单号(第三方返回) + */ + @TableField(value = "pay_no") + @ApiModelProperty(value="支付单号(第三方返回)") + private String payNo; + + /** + * 用户侧订单号(第三方返回) + */ + @TableField(value = "trade_no") + @ApiModelProperty(value="用户侧订单号(第三方返回)") + private String tradeNo; + + /** + * appid + */ + @TableField(value = "appid") + @ApiModelProperty(value="appid") + private String appid; + + /** + * 商品标题 + */ + @TableField(value = "goods_subject") + @ApiModelProperty(value="商品标题") + private String goodsSubject; + + /** + * 商品描述 + */ + @TableField(value = "goods_desc") + @ApiModelProperty(value="商品描述") + private String goodsDesc; + + /** + * 支付金额,单位元 + */ + @TableField(value = "pay_amount") + @ApiModelProperty(value="支付金额,单位元") + private BigDecimal payAmount; + + /** + * 支付时间 + */ + @TableField(value = "pay_time") + @ApiModelProperty(value="支付时间") + private Date payTime; + + /** + * 支付场景:1.会员充值 + */ + @TableField(value = "scene_code") + @ApiModelProperty(value="支付场景:1.会员充值") + private Integer sceneCode; + + /** + * 用户ip + */ + @TableField(value = "ip") + @ApiModelProperty(value="用户ip") + private String ip; + + /** + * 调用第三方下单返回 + */ + @TableField(value = "third_code") + @ApiModelProperty(value="调用第三方下单返回") + private String thirdCode; + + /** + * 调用第三方下单返回 + */ + @TableField(value = "third_msg") + @ApiModelProperty(value="调用第三方下单返回") + private String thirdMsg; + + /** + * 调用第三方下单返回 + */ + @TableField(value = "third_no") + @ApiModelProperty(value="调用第三方下单返回") + private String thirdNo; + + /** + * 备注 + */ + @TableField(value = "remark") + @ApiModelProperty(value="备注") + private String remark; +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/R.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/R.java index 2ae3ccc..825ea22 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/R.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/R.java @@ -3,6 +3,8 @@ package com.bnyer.common.core.domain; import java.io.Serializable; import com.bnyer.common.core.constant.Constants; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.web.domain.AjaxResult; /** * 响应信息主体 @@ -65,6 +67,16 @@ public class R implements Serializable return restResult(null, code, msg); } + /** + * 返回错误消息 + * @param responseEnum + * @return + */ + public static R fail(ResponseEnum responseEnum) + { + return restResult(null,responseEnum.getCode(), responseEnum.getMsg()); + } + private static R restResult(T data, int code, String msg) { R apiResult = new R<>(); @@ -103,4 +115,9 @@ public class R implements Serializable { this.data = data; } + + public boolean isSuccess(){ + return this.code == SUCCESS; + } + } diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/UserVip.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/UserVip.java index 54fa10e..b4cd7aa 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/UserVip.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/UserVip.java @@ -19,6 +19,13 @@ import java.math.BigDecimal; @TableName(value = "img_user_vip") public class UserVip extends BaseDomain { + /** + * vip类型id + */ + @TableField(value = "vip_type_id") + @ApiModelProperty(value="vip类型id") + private Long vipTypeId; + /** * vip名称 */ @@ -61,13 +68,6 @@ public class UserVip extends BaseDomain { @ApiModelProperty(value="热门描述") private String hotSignDesc; - /** - * 时长天数 - */ - @TableField(value = "days") - @ApiModelProperty(value="时长天数") - private Integer days; - /** * 是否到期自动续费(0>否;1->是) */ @@ -75,5 +75,19 @@ public class UserVip extends BaseDomain { @ApiModelProperty(value="是否到期自动续费(0>否;1->是)") private String isDelay; + /** + * 有效时长单位:0天,1周,2月,3季,4年 + */ + @TableField(value = "valid_time_unit") + @ApiModelProperty(value = "有效时长单位:0天,1周,2月,3季,4年") + private Integer validTimeUnit; + + /** + * 有效时长:表示几天、几周、几月、几年 + */ + @TableField(value = "valid_time_num") + @ApiModelProperty(value = "有效时长:表示几天、几周、几月、几年") + private Integer validTimeNum; + private static final long serialVersionUID = 1L; -} \ No newline at end of file +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/UserVipRecord.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/UserVipRecord.java index 75bee33..0bb7f43 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/UserVipRecord.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/UserVipRecord.java @@ -1,15 +1,12 @@ package com.bnyer.common.core.domain; -import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.*; -import java.math.BigDecimal; import java.util.Date; @ApiModel(value="com-bnyer-common-core-domain-UserVipRecord") @@ -22,11 +19,18 @@ import java.util.Date; public class UserVipRecord extends BaseDomain { /** - * 订单id + * 订单号 */ - @TableField(value = "order_id") - @ApiModelProperty(value="订单id") - private String orderId; + @TableField(value = "order_no") + @ApiModelProperty(value="订单号") + private String orderNo; + + /** + * 用户id + */ + @TableField(value = "user_id") + @ApiModelProperty(value="用户id") + private Long userId; /** * 用户手机号 @@ -36,11 +40,11 @@ public class UserVipRecord extends BaseDomain { private String phone; /** - * 用户vip表id + * vip表id */ - @TableField(value = "user_vip_id") - @ApiModelProperty(value="用户vip表id") - private Long userVipId; + @TableField(value = "vip_id") + @ApiModelProperty(value="vip表id") + private Long vipId; /** * 开始时间 @@ -59,25 +63,32 @@ public class UserVipRecord extends BaseDomain { private Date endTime; /** - * 支付金额 + * 支付状态(0->待支付;1->已支付;2->支付失败;3->支付异常) + */ + @TableField(value = "status") + @ApiModelProperty(value="支付状态(0->待支付;1->已支付;2->支付失败;3->支付异常)") + private Integer status; + + /** + * vip名称 */ - @TableField(value = "price") - @ApiModelProperty(value="支付金额") - private BigDecimal price; + @TableField(value = "vip_name") + @ApiModelProperty(value="vip名称") + private String vipName; /** - * vip类型状态(0->月卡;1->季卡;2->年卡) + * vip类型名称 */ - @TableField(value = "type") - @ApiModelProperty(value="vip类型状态(0->月卡;1->季卡;2->年卡)") - private String type; + @TableField(value = "vip_type_name") + @ApiModelProperty(value = "vip类型名称") + private String vipTypeName; /** - * 支付状态(0->待支付;1->已支付;2->支付失败;3->支付异常) + * 用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信 */ - @TableField(value = "status") - @ApiModelProperty(value="支付状态(0->待支付;1->已支付;2->支付失败;3->支付异常)") - private String status; + @TableField(value = "user_client_type") + @ApiModelProperty(value = "用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信") + private Integer userClientType; private static final long serialVersionUID = 1L; } diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/VipOrder.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/VipOrder.java new file mode 100644 index 0000000..7f50451 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/VipOrder.java @@ -0,0 +1,168 @@ +package com.bnyer.common.core.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author :WXC + * @description : + */ +/** + * 会员订单表 + */ +@ApiModel(value="com-bnyer-common-core-domain-OrderVipOrder") +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "order_vip_order") +public class VipOrder extends BaseDomain { + + /** + * 订单号 + */ + @TableField(value = "order_no") + @ApiModelProperty(value="订单号") + private String orderNo; + + /** + * 用户手机号 + */ + @TableField(value = "phone") + @ApiModelProperty(value="用户手机号") + private String phone; + + /** + * 用户id + */ + @TableField(value = "user_id") + @ApiModelProperty(value="用户id") + private Long userId; + + /** + * vip主键Id + */ + @TableField(value = "vip_id") + @ApiModelProperty(value="vip主键Id") + private Long vipId; + + /** + * vip编码 + */ + @TableField(value = "vip_code") + @ApiModelProperty(value="vip编码") + private String vipCode; + + /** + * vip名称 + */ + @TableField(value = "vip_name") + @ApiModelProperty(value="vip名称") + private String vipName; + + /** + * 支付金额,单位元 + */ + @TableField(value = "pay_amount") + @ApiModelProperty(value="支付金额,单位元") + private BigDecimal payAmount; + + /** + * 是否到期自动续费(0>否;1->是) + */ + @TableField(value = "is_delay") + @ApiModelProperty(value="是否到期自动续费(0>否;1->是)") + private String isDelay; + + /** + * 时长天数 + */ + @TableField(value = "days") + @ApiModelProperty(value="时长天数") + private Integer days; + + /** + * 开始时间 + */ + @TableField(value = "start_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value="开始时间") + private Date startTime; + + /** + * 到期时间 + */ + @TableField(value = "end_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value="到期时间") + private Date endTime; + + /** + * 订单状态:0未处理;1成功;2失败 + */ + @TableField(value = "order_status") + @ApiModelProperty(value="订单状态:0未处理;1成功;2失败") + private Integer orderStatus; + + /** + * 订单关闭原因:0超时未支付; 1买家取消 + */ + @TableField(value = "close_type") + @ApiModelProperty(value="订单关闭原因:0超时未支付; 1买家取消") + private Integer closeType; + + /** + * 支付时间 + */ + @TableField(value = "pay_time") + @ApiModelProperty(value="支付时间") + private Date payTime; + + /** + * 取消时间 + */ + @TableField(value = "cancel_time") + @ApiModelProperty(value="取消时间") + private Date cancelTime; + + /** + * 用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信 + */ + @TableField(value = "user_client_type") + @ApiModelProperty(value = "用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信") + private Integer userClientType; + + /** + * vip类型名称 + */ + @TableField(value = "vip_type_name") + @ApiModelProperty(value = "vip类型名称") + private String vipTypeName; + + /** + * 备注 + */ + @TableField(value = "remark") + @ApiModelProperty(value="备注") + private String remark; + + /** + * 支付状态:1000未支付;1001支付成功 ;1002支付失败 + */ + @TableField(value = "pay_status") + @ApiModelProperty(value="支付状态:1000未支付;1001支付成功 ;1002支付失败") + private Integer payStatus; +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/WxpayConfig.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/WxpayConfig.java new file mode 100644 index 0000000..a9a60d8 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/WxpayConfig.java @@ -0,0 +1,83 @@ +package com.bnyer.common.core.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author :WXC + * @description : + */ +/** + * 微信支付配置表 + */ +@ApiModel(value="com-bnyer-common-core-domain-PayWxpayConfig") +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "pay_wxpay_config") +public class WxpayConfig extends BaseDomain { + /** + * 主键 + */ + @TableId(value = "id", type = IdType.AUTO) + @ApiModelProperty(value="主键") + private Long id; + + /** + * APP|JSAPI|MWEB|MINIPROGRAM 支付 + */ + @TableField(value = "trade_type") + @ApiModelProperty(value="APP|JSAPI|MWEB|MINIPROGRAM 支付") + private String tradeType; + + /** + * appid + */ + @TableField(value = "appid") + @ApiModelProperty(value="appid") + private String appid; + + /** + * 商户号 + */ + @TableField(value = "mchid") + @ApiModelProperty(value="商户号") + private String mchid; + + /** + * 回调地址url + */ + @TableField(value = "backurl") + @ApiModelProperty(value="回调地址url") + private String backurl; + + /** + * 密钥 + */ + @TableField(value = "key") + @ApiModelProperty(value="密钥") + private String key; + + /** + * 帐号状态(0正常 1停用) + */ + @TableField(value = "status") + @ApiModelProperty(value="帐号状态(0正常 1停用)") + private String status; + + @TableField(value = "remark") + @ApiModelProperty(value="") + private String remark; +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/AddUserVipRecordDto.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/AddUserVipRecordDto.java new file mode 100644 index 0000000..091f238 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/AddUserVipRecordDto.java @@ -0,0 +1,44 @@ +package com.bnyer.common.core.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import java.util.Date; + +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +public class AddUserVipRecordDto { + + @ApiModelProperty(value="订单号") + private String orderNo; + + @ApiModelProperty(value="用户id") + private Long userId; + + @ApiModelProperty(value="用户手机号") + private String phone; + + @ApiModelProperty(value="vip表id") + private Long vipId; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value="开始时间") + private Date startTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value="到期时间") + private Date endTime; + + @ApiModelProperty(value="vip名称") + private String vipName; + + @ApiModelProperty(value = "vip类型名称") + private String vipTypeName; + + @ApiModelProperty(value = "用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信") + private Integer userClientType; +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/PayUserVipDto.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/PayUserVipDto.java index 83e2f01..80af424 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/PayUserVipDto.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/PayUserVipDto.java @@ -21,7 +21,7 @@ public class PayUserVipDto implements Serializable { @NotNull(message = "用户vipId不能为空!") @ApiModelProperty(value="用户vip表id") - private Long userVipId; + private Long vipId; @NotBlank(message = "vip类型状态不能为空!") @ApiModelProperty(value="vip类型状态(0->月卡;1->季卡;2->年卡)") diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/QueryUserVipDto.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/QueryUserVipDto.java new file mode 100644 index 0000000..ea669fe --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/QueryUserVipDto.java @@ -0,0 +1,23 @@ +package com.bnyer.common.core.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +/** + * @author :WXC + * @Date :2023/03/31 + * @description : + */ +@Getter +@Setter +@ApiModel("会员vip查询类") +public class QueryUserVipDto { + @ApiModelProperty(value="主键Id") + private Long id; + + @ApiModelProperty(value="vip客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信") + private Integer userClientType; + +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/UserVipDto.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/UserVipDto.java index c067a5a..0e51540 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/UserVipDto.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/UserVipDto.java @@ -56,7 +56,7 @@ public class UserVipDto implements Serializable { if(StringUtils.isNotBlank(this.getHotSignDesc())){ userVip.setHotSignDesc(this.getHotSignDesc()); } - userVip.setDays(this.getDays()); +// userVip.setDays(this.getDays()); userVip.setIsDelay(this.getIsDelay()); return userVip; } diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumPayStatus.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumPayStatus.java new file mode 100644 index 0000000..a4c610e --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumPayStatus.java @@ -0,0 +1,31 @@ +package com.bnyer.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumPayStatus { + + NO_PAY(1000,"未支付"), + SUCCESS(1001,"已支付"), + FAILS(1002,"支付失败"), + ; + + private final int status; + + private final String name; + + public static EnumPayStatus getEnumPayStatusByStatus(int status) { + return Arrays.stream(values()) + .filter(payStatus -> payStatus.getStatus() == status) + .findFirst().orElseThrow(() -> new SecurityException("status 未匹配上对应的支付状态")); + } + +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumPayType.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumPayType.java new file mode 100644 index 0000000..1d6a469 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumPayType.java @@ -0,0 +1,42 @@ +package com.bnyer.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumPayType { + + WX_PAY("wxpay","微信支付"), + ALI_PAY("alipay","支付宝支付"), + DY_PAY("dypay","抖音支付"), + KS_PAY("kspay","快手支付"), + ; + + private final String type; + + private final String name; + + public static String getName(String type) { + for (EnumPayType s : EnumPayType.values()) { + if (Objects.equals(type, s.getType())) { + return s.getName(); + } + } + return null; + } + + public static EnumPayType getEnumPayTypeByType(String type) { + return Arrays.stream(values()) + .filter(payType -> Objects.equals(payType.getType(), type)) + .findFirst().orElseThrow(() -> new SecurityException("type 未匹配上对应的支付方式")); + } + +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumSceneCode.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumSceneCode.java new file mode 100644 index 0000000..f536127 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumSceneCode.java @@ -0,0 +1,38 @@ +package com.bnyer.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumSceneCode { + + VIP_RECHARGE(1,"会员充值"), + UNKNOWN(-1,"未知"), + ; + + private final int sceneCode; + + private final String name; + + public static String getSceneCodeName(int sceneCode) { + for (EnumSceneCode s : EnumSceneCode.values()) { + if (sceneCode == s.getSceneCode()) { + return s.getName(); + } + } + return null; + } + + public static EnumSceneCode getSceneCodeByCode(int code) { + return Arrays.stream(values()) + .filter(sceneCode -> sceneCode.getSceneCode() == code) + .findFirst().orElse(EnumSceneCode.UNKNOWN); + } +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumTimeUnit.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumTimeUnit.java new file mode 100644 index 0000000..c8318d3 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumTimeUnit.java @@ -0,0 +1,36 @@ +package com.bnyer.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * @author :WXC + * @Date :2023/04/23 + * @description :时间单位 + */ +@Getter +@AllArgsConstructor +public enum EnumTimeUnit { + + DAY(0,"天"), + WEEK(1,"周"), + MONTH(2,"月"), + QUARTER(3,"季"), + YEAR(4,"年"), + HOURS(5,"时"), + MINUTE(6,"分"), + SECONDS(7,"秒"), + ; + + private final int unit; + private final String name; + + public static EnumTimeUnit getEnumTimeUnitByUnit(int unit) { + return Arrays.stream(values()) + .filter(vipTimeUnit -> unit == vipTimeUnit.getUnit()) + .findFirst().orElseThrow(() -> new SecurityException("unit 未匹配上对应的时间单位")); + } + +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumUserClientType.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumUserClientType.java new file mode 100644 index 0000000..79958fd --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumUserClientType.java @@ -0,0 +1,34 @@ +package com.bnyer.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumUserClientType { + + DU(10,"DU","抖音用户"), + KU(20,"KU","快手用户"), + WU(30,"WU","微信用户"), + WY(40,"WY","微信艺术家"), + ; + + private final int type; + + private final String code; + + private final String desc; + + public static String getCodeByType(int type) { + for (EnumUserClientType s : EnumUserClientType.values()) { + if (type == s.type) { + return s.getCode(); + } + } + return null; + } +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumVipType.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumVipType.java new file mode 100644 index 0000000..544a5a8 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumVipType.java @@ -0,0 +1,41 @@ +package com.bnyer.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumVipType { + + IMG_VIP("img","下图会员"), + VIDEO_VIP("video","视频会员"), + AI_VIP("ai","ai会员"), + UNKNOWN("unknown","未知"), + ; + + private final String code; + + private final String name; + + public static String getNameByCode(String code) { + for (EnumVipType s : EnumVipType.values()) { + if (Objects.equals(code, s.code)) { + return s.getName(); + } + } + return null; + } + + public static EnumVipType getVipTypeByCode(String code) { + return Arrays.stream(values()) + .filter(vipCategory -> Objects.equals(vipCategory.getCode(), code)) + .findFirst().orElse(EnumVipType.UNKNOWN); + } +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/ResponseEnum.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/ResponseEnum.java new file mode 100644 index 0000000..17efcb3 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/ResponseEnum.java @@ -0,0 +1,73 @@ +package com.bnyer.common.core.enums; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : + */ +public enum ResponseEnum { + + //======================系统异常========================, + SERVER_ERROR(500, "系统繁忙,请稍候重试!"), + PARAM_ERROR(400, "参数异常!"), + NOT_EXIST(110001, "查询为空"), + PAY_CONFIG_ERROR(110002, "支付配置未启用或未配置!"), + + + //======================订单异常======================== + + /** + * 订单已过期,当前端看到该状态码的时候,提示订单信息已过期,请重新确认后提交,此时用户点击确定,前端刷新页面。 + */ + ORDER_EXPIRED(210001, "订单已过期"), + + /** + * 请勿重复提交订单, + * 1.当前端遇到该异常时,说明前端防多次点击没做好 + * 2.提示用户 订单已发生改变,请勿重复下单 + */ + REPEAT_ORDER(210002,"请勿重复提交订单"), + + + + //======================第三方接口调用异常======================== + PAY_FAILS(220001,"支付失败,请稍后重试"), + ORDER_QUERY_FAILS(220002,"订单查询失败,请稍后重试"), + + ; + + + private final int code; + + private final String msg; + + public int getCode() { + return code; + } + + public String getMsg() { + return msg; + } + + ResponseEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String toString() { + return "ResponseEnum{" + + "code=" + code + + ", msg='" + msg + '\'' + + '}'; + } + + public static String getCodeMsg(int code) { + for (ResponseEnum s : ResponseEnum.values()) { + if (code == s.getCode()) { + return s.getMsg(); + } + } + return null; + } +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/exception/ServiceException.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/exception/ServiceException.java index 484d759..e828fa7 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/exception/ServiceException.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/exception/ServiceException.java @@ -1,5 +1,10 @@ package com.bnyer.common.core.exception; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.utils.StringUtils; + +import java.util.Arrays; + /** * 业务异常 * @@ -44,6 +49,15 @@ public final class ServiceException extends RuntimeException this.code = code; } + public ServiceException(ResponseEnum responseEnum,String... params) { + this.code = responseEnum.getCode(); + if (StringUtils.isNotEmpty(params)){ + this.message = StringUtils.join(params,",") + responseEnum.getMsg(); + }else { + this.message = responseEnum.getMsg(); + } + } + public String getDetailMessage() { return detailMessage; diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/DateUtils.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/DateUtils.java index f84a53a..07b3e37 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/DateUtils.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/DateUtils.java @@ -3,14 +3,15 @@ package com.bnyer.common.core.utils; import java.lang.management.ManagementFactory; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAdjusters; import java.util.Calendar; import java.util.Date; +import com.bnyer.common.core.enums.EnumTimeUnit; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.exception.ServiceException; import org.apache.commons.lang3.time.DateFormatUtils; /** @@ -193,7 +194,124 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils return now.getTime(); } + /** + * 根据类型获取指定日期下一类型的当天时间 + * @param type + * @return + */ + public static Date getNextDateByType(int type,Date date){ + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS); + Instant instant = date.toInstant(); + ZoneId zoneId = ZoneId.systemDefault(); + LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); + String format = ""; + switch (type){ + case 0:{ + // 下个月的今天 + LocalDateTime nextMonthToday = localDateTime.plusMonths(1); + format = nextMonthToday.format(formatter); + break; + } + case 1:{ + // 下一季度的今天 + int currentMonth = localDateTime.getMonthValue(); + int nextQuarterMonth = (currentMonth - 1) / 3 * 3 + 4; + LocalDateTime nextQuarterToday = localDateTime.withMonth(nextQuarterMonth); + format = nextQuarterToday.format(formatter); + break; + } + case 2:{ + // 下一年的今天 + LocalDateTime nextYearToday = localDateTime.plusYears(1); + format = nextYearToday.format(formatter); + break; + } + default: + throw new ServiceException(ResponseEnum.PARAM_ERROR); + } + return DateUtils.parseDate(format); + } + + /** + * 根据时间类型+周期创建时间 + * @param unit 时间单位 + * @param date 时间 + * @param num 周期数 + * @return + */ + public static Date getDateByType(EnumTimeUnit unit, Date date, long num){ + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS); + Instant instant = date.toInstant(); + ZoneId zoneId = ZoneId.systemDefault(); + LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); + String format = ""; + switch (unit){ + case SECONDS: + //秒 + LocalDateTime secondsDateTime = localDateTime.plusSeconds(num); + format = secondsDateTime.format(formatter); + break; + case MINUTE: + //分 + LocalDateTime minuteDateTime = localDateTime.plusMinutes(num); + format = minuteDateTime.format(formatter); + break; + case HOURS: + //时 + LocalDateTime hoursDateTime = localDateTime.plusHours(num); + format = hoursDateTime.format(formatter); + break; + case DAY: + //日 + LocalDateTime daysDateTime = localDateTime.plusDays(num); + format = daysDateTime.format(formatter); + break; + case MONTH: + //月 + LocalDateTime monthsDateTime = localDateTime.plusMonths(num); + format = monthsDateTime.format(formatter); + break; + case YEAR: + //年 + LocalDateTime yearsDateTime = localDateTime.plusYears(num); + format = yearsDateTime.format(formatter); + break; + case WEEK: + //周 + LocalDateTime weeksDateTime = localDateTime.plusWeeks(num); + format = weeksDateTime.format(formatter); + break; + case QUARTER: + //季 + int monthValue = localDateTime.getMonthValue(); + int nextQuarterMonth = (monthValue - 1) / 3 * 3 + ((int) num * 4); + LocalDateTime nextQuarterToday = localDateTime.withMonth(nextQuarterMonth); + format = nextQuarterToday.format(formatter); + break; + default: + throw new IllegalArgumentException("时间类型参数错误"); + } + return DateUtils.parseDate(format); + } + + public static void main(String[] args) { - System.out.println(getDateAfter(new Date(), 365)); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime now = LocalDateTime.now(); + + // 下个月的今天 + LocalDateTime nextMonthToday = now.plusMonths(1); + + // 下一季度的今天 + int currentMonth = now.getMonthValue(); + int nextQuarterMonth = (currentMonth - 1) / 3 * 3 + (2 * 4); + LocalDateTime nextQuarterToday = now.withMonth(nextQuarterMonth); + + // 下一年的今天 + LocalDateTime nextYearToday = now.plusYears(1); + + System.out.println("下个月的今天:" + nextMonthToday.format(formatter)); + System.out.println("下一季度的今天:" + nextQuarterToday.format(formatter)); + System.out.println("下一年的今天:" + nextYearToday.format(formatter)); } } diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/OrderUtil.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/OrderUtil.java new file mode 100644 index 0000000..562ad5b --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/OrderUtil.java @@ -0,0 +1,30 @@ +package com.bnyer.common.core.utils; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Random; + +public class OrderUtil { + + /** + * 生成流水(不超过32位) + * + * @frontStr 费用类型 RV=充值会员 + * @return 2费用类型+14时间+2客户端类型+11userId+3随机位 + */ + public static String getOrderNo(String frontStr, Date curTime, String userClientType, String userId) { + SimpleDateFormat simple = new SimpleDateFormat("yyyyMMddHHmmss"); + String ms = simple.format(curTime); + + Random rand = new Random(); + StringBuilder strRand = new StringBuilder(String.valueOf(rand.nextInt(100))); + while (strRand.length() < 3) { + strRand.insert(0, "0"); + } + return frontStr+userClientType + ms + userId+ strRand; + } + + public static void main(String[] args) { + System.out.println(getOrderNo("RV",new Date(),"DU","12345678910")); + } +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/bean/EntityConvertUtil.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/bean/EntityConvertUtil.java new file mode 100644 index 0000000..6184b37 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/bean/EntityConvertUtil.java @@ -0,0 +1,203 @@ +package com.bnyer.common.core.utils.bean; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.util.CollectionUtils; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.*; +import java.util.*; + +@Slf4j +public class EntityConvertUtil { + + /** + * 数组集合转化为指定对象集合 + * 指定的实体对象必须包含所以字段的构造方法,数组的元素的顺序将和构造方法顺序和类型一一对应 + * + * @param list 集合 + * @param clazz c + * @param 类型 + * @return List + * @description 用于jpa查询自定义vo用的 + */ + public static List castEntity(List list, Class clazz) { + List returnList = new ArrayList<>(); + if (list.size() == 0) { + return returnList; + } + Class[] c2 = null; + Constructor[] constructors = clazz.getConstructors(); + for (Constructor constructor : constructors) { + Class[] tClass = constructor.getParameterTypes(); + if (tClass.length == list.get(0).length) { + c2 = tClass; + break; + } + } + //构造方法实例化对象 + for (Object[] o : list) { + Constructor constructor = null; + try { + constructor = clazz.getConstructor(c2); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + try { + assert constructor != null; + returnList.add(constructor.newInstance(o)); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + return returnList; + } + + + /** + * @param object 要强转的对象 , entityClass 强转后的类型 + */ + public static T convertBean(Object object, Class entityClass) { + if (null == object) { + return null; + } + return JSON.parseObject(JSON.toJSONString(object), entityClass); + } + + + /** + * @param object 要转话的对象 + */ + public static Map objectToMap(Object object) { + return convertBean(object, Map.class); + } + + + /** + * @param map map集合, t 对象 + */ + public static T mapToObject(Map map, Class beanClass) throws InstantiationException, IllegalAccessException, InvocationTargetException { + T t = beanClass.newInstance(); + Field[] declaredFields = t.getClass().getDeclaredFields(); + for(Field field:declaredFields){ + int mod = field.getModifiers(); + if(Modifier.isStatic(mod) || Modifier.isFinal(mod)){ + continue; + } + field.setAccessible(true); + field.set(t, map.get(field.getName())); + } + return t; + } + + + /** + * @param source 资源对象, target 目标对象, ignoreProperties 赋值new String[]{} + * @return T target对象 + */ + public static T copy(Object source, Class target, String... ignoreProperties) { + T targetInstance = null; + try { + targetInstance = target.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + if (ArrayUtils.isEmpty(ignoreProperties)) { + assert targetInstance != null; + BeanUtils.copyProperties(source, targetInstance); + } else { + assert targetInstance != null; + BeanUtils.copyProperties(source, targetInstance, ignoreProperties); + } + return targetInstance; + + } + + /** + * 从List copy到List + * @param list List + * @param clazz B + * @return List + */ + public static List copy(List list,Class clazz){ + String oldOb = JSON.toJSONString(list); + return JSON.parseArray(oldOb, clazz); + } + + public static List copyList(List list, Class target, String... ignoreProperties) { + List targetList = new ArrayList<>(); + if (CollectionUtils.isEmpty(list)) { + return targetList; + } + for (E e : list) { + targetList.add(copy(e, target, ignoreProperties)); + } + return targetList; + } + + + /** + * 功能描述: Bean --> Map 1: 利用Introspector和PropertyDescriptor 将Bean --> Map + * + */ + public static Map transBean2Map(Object obj) { + if (obj == null) { + return null; + } + Map map = new HashMap(); + try { + BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor property : propertyDescriptors) { + String key = property.getName(); + // 过滤class属性 + if (!"class".equals(key)) { + // 得到property对应的getter方法 + Method getter = property.getReadMethod(); + Object value = getter.invoke(obj); + map.put(key, value.toString()); + } + } + } catch (Exception e) { + log.error("transBean2Map Error " + e); + } + return map; + } + + /** + * 属性复制,跳过为null的值 + * @param source 提供值的obj + * @param target 接收值的obj + * @param + * @return target + */ + public static T copyNotNullProperties(T source, T target){ + BeanUtils.copyProperties(source, target, getNullPropertyNames(source)); + return target; + } + + /** + * 获取值为Null的字段名称 + * @param source + * @return + */ + public static String[] getNullPropertyNames (Object source) { + final BeanWrapper src = new BeanWrapperImpl(source); + java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors(); + + Set emptyNames = new HashSet<>(); + for(java.beans.PropertyDescriptor pd : pds) { + Object srcValue = src.getPropertyValue(pd.getName()); + if (srcValue == null) emptyNames.add(pd.getName()); + } + String[] result = new String[emptyNames.size()]; + return emptyNames.toArray(result); + } +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/web/domain/AjaxResult.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/web/domain/AjaxResult.java index 990ea28..741a214 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/web/domain/AjaxResult.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/web/domain/AjaxResult.java @@ -1,9 +1,10 @@ package com.bnyer.common.core.web.domain; -import java.util.HashMap; import com.bnyer.common.core.constant.HttpStatus; import com.bnyer.common.core.utils.StringUtils; +import java.util.HashMap; + /** * 操作消息提醒 * @@ -159,4 +160,5 @@ public class AjaxResult extends HashMap { return new AjaxResult(code, msg, null); } + } diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/adapter/CacheTtlAdapter.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/adapter/CacheTtlAdapter.java new file mode 100644 index 0000000..7997513 --- /dev/null +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/adapter/CacheTtlAdapter.java @@ -0,0 +1,20 @@ +package com.bnyer.common.redis.adapter; + +import com.bnyer.common.redis.bo.CacheNameWithTtlBo; + +import java.util.List; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : 实现该接口之后,根据缓存的cacheName和ttl将缓存进行过期 + */ +public interface CacheTtlAdapter { + + /** + * 根据缓存的cacheName和ttl将缓存进行过期 + * @return 需要独立设置过期时间的缓存列表 + */ + List listCacheNameWithTtl(); + +} diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/bo/CacheNameWithTtlBo.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/bo/CacheNameWithTtlBo.java new file mode 100644 index 0000000..6c29cb2 --- /dev/null +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/bo/CacheNameWithTtlBo.java @@ -0,0 +1,28 @@ +package com.bnyer.common.redis.bo; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : 通过 cacheName 配置 和 时间告诉缓存多久清楚一遍 + */ +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class CacheNameWithTtlBo { + + /** + * 缓存名称 + */ + private String cacheName; + + /** + * 过期时间 + */ + private Integer ttl; +} diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/configure/RedisConfig.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/configure/RedisConfig.java index 17ef8ed..eafe6e2 100644 --- a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/configure/RedisConfig.java +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/configure/RedisConfig.java @@ -1,50 +1,149 @@ package com.bnyer.common.redis.configure; +import com.bnyer.common.redis.adapter.CacheTtlAdapter; +import com.bnyer.common.redis.bo.CacheNameWithTtlBo; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; + +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; /** * redis配置 - * + * * @author ruoyi */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { + @Bean - @SuppressWarnings(value = { "unchecked", "rawtypes" }) - public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) - { - RedisTemplate template = new RedisTemplate<>(); - template.setConnectionFactory(connectionFactory); + public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, CacheTtlAdapter cacheTtlAdapter) { - FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + RedisCacheManager redisCacheManager = new RedisCacheManager( + RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), + // 默认策略,未配置的 key 会使用这个 + this.getRedisCacheConfigurationWithTtl(3600), + // 指定 key 策略 + this.getRedisCacheConfigurationMap(cacheTtlAdapter)); - ObjectMapper mapper = new ObjectMapper(); - mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); - mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); - serializer.setObjectMapper(mapper); + redisCacheManager.setTransactionAware(true); + return redisCacheManager; + } - // 使用StringRedisSerializer来序列化和反序列化redis的key值 - template.setKeySerializer(new StringRedisSerializer()); - template.setValueSerializer(serializer); + private Map getRedisCacheConfigurationMap(CacheTtlAdapter cacheTtlAdapter) { + if (cacheTtlAdapter == null) { + return Collections.emptyMap(); + } + Map redisCacheConfigurationMap = new HashMap<>(16); - // Hash的key也采用StringRedisSerializer的序列化方式 - template.setHashKeySerializer(new StringRedisSerializer()); - template.setHashValueSerializer(serializer); + for (CacheNameWithTtlBo cacheNameWithTtlBO : cacheTtlAdapter.listCacheNameWithTtl()) { + redisCacheConfigurationMap.put(cacheNameWithTtlBO.getCacheName(), + getRedisCacheConfigurationWithTtl(cacheNameWithTtlBO.getTtl())); + } + return redisCacheConfigurationMap; + } + private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) { + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); + redisCacheConfiguration = redisCacheConfiguration + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())) + .entryTtl(Duration.ofSeconds(seconds)); + + return redisCacheConfiguration; + } + + /** + * 自定义redis序列化的机制,重新定义一个ObjectMapper.防止和MVC的冲突 + */ + @Bean + public RedisSerializer redisSerializer() { + ObjectMapper objectMapper = new ObjectMapper(); +// // 反序列化时候遇到不匹配的属性并不抛出异常 +// objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); +// // 序列化时候遇到空对象不抛出异常 +// objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); +// // 反序列化的时候如果是无效子类型,不抛出异常 +// objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); +// // 不使用默认的dateTime进行序列化, +// objectMapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false); +// // 使用JSR310提供的序列化类,里面包含了大量的JDK8时间序列化类 +// objectMapper.registerModule(new JavaTimeModule()); + // 启用反序列化所需的类型信息,在属性中添加@class + objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, + JsonTypeInfo.As.PROPERTY); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + // 配置null值的序列化器 + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + serializer.setObjectMapper(objectMapper); + return serializer; + } + +// @Bean +// @SuppressWarnings(value = { "unchecked", "rawtypes" }) +// public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) +// { +// RedisTemplate template = new RedisTemplate<>(); +// template.setConnectionFactory(connectionFactory); +// +// FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); +// +// ObjectMapper mapper = new ObjectMapper(); +// mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); +// mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); +// serializer.setObjectMapper(mapper); +// +// // 使用StringRedisSerializer来序列化和反序列化redis的key值 +// template.setKeySerializer(new StringRedisSerializer()); +// template.setValueSerializer(serializer); +// +// // Hash的key也采用StringRedisSerializer的序列化方式 +// template.setHashKeySerializer(new StringRedisSerializer()); +// template.setHashValueSerializer(serializer); +// +// template.afterPropertiesSet(); +// return template; +// } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory, + RedisSerializer redisSerializer) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(redisConnectionFactory); + template.setDefaultSerializer(redisSerializer); + template.setValueSerializer(redisSerializer); + template.setHashValueSerializer(redisSerializer); + template.setKeySerializer(StringRedisSerializer.UTF_8); + template.setHashKeySerializer(StringRedisSerializer.UTF_8); template.afterPropertiesSet(); return template; } + + + @Bean + @ConditionalOnMissingBean + public CacheTtlAdapter cacheTtl() { + return Collections::emptyList; + } + } diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/CacheNames.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/CacheNames.java new file mode 100644 index 0000000..c3051a2 --- /dev/null +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/CacheNames.java @@ -0,0 +1,20 @@ +package com.bnyer.common.redis.constant; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : + */ +public class CacheNames { + /** + * + * 参考CacheKeyPrefix + * cacheNames 与 key 之间的默认连接字符 + */ + public final static String UNION = "::"; + + /** + * key内部的连接字符(自定义) + */ + public final static String UNION_KEY = ":"; +} diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/OrderCacheNames.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/OrderCacheNames.java new file mode 100644 index 0000000..b601dd6 --- /dev/null +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/OrderCacheNames.java @@ -0,0 +1,25 @@ +package com.bnyer.common.redis.constant; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +public class OrderCacheNames { + + /** + * 会员订单前缀 + */ + public static final String VIP_ORDER_PREFIX = "vip_cloud_order:"; + + /** + * 确认订单信息缓存 + */ + public static final String ORDER_CONFIRM_KEY = "order:confirm"; + + /** + * 订单uid + */ + public static final String ORDER_CONFIRM_UID_KEY = "order:uid_confirm"; + +} diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisCacheService.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisCacheService.java new file mode 100644 index 0000000..4ce0fa2 --- /dev/null +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisCacheService.java @@ -0,0 +1,49 @@ +package com.bnyer.common.redis.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Component; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : + */ +@Component +public class RedisCacheService { + private final CacheManager cacheManager; + + @Autowired + public RedisCacheService(CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + + @SuppressWarnings({ "unchecked" }) + public T getCache(String cacheName, String key) { + Cache cache = cacheManager.getCache(cacheName); + if (cache == null) { + return null; + } + Cache.ValueWrapper valueWrapper = cache.get(key); + if (valueWrapper == null) { + return null; + } + return (T) valueWrapper.get(); + } + + public void putCache(String cacheName, String key, Object value) { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null) { + cache.put(key, value); + } + } + + public void evictCache(String cacheName, String key) { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null) { + cache.evict(key); + } + } + +} diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisService.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisService.java index 9e47907..ccec3a2 100644 --- a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisService.java +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisService.java @@ -1,17 +1,16 @@ package com.bnyer.common.redis.service; -import java.time.Duration; -import java.util.*; -import java.util.concurrent.TimeUnit; - -import org.apache.poi.ss.formula.functions.T; +import cn.hutool.core.util.StrUtil; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.exception.ServiceException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.*; import org.springframework.data.redis.core.script.DefaultRedisScript; -import org.springframework.data.redis.core.script.RedisScript; import org.springframework.stereotype.Component; +import java.util.*; +import java.util.concurrent.TimeUnit; + /** * spring redis 工具类 * @@ -353,4 +352,21 @@ public class RedisService return redisTemplate.opsForHyperLogLog().add(today + page+"UV", ip); } + + public boolean cad(String key, String value) { + + if (key.contains(StrUtil.SPACE) || value.contains(StrUtil.SPACE)) { + throw new ServiceException(ResponseEnum.SERVER_ERROR); + } + + String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; + + //通过lure脚本原子验证令牌和删除令牌 + Long result = (Long) redisTemplate.execute(new DefaultRedisScript(script, Long.class), + Collections.singletonList(key), + value); + + return !Objects.equals(result, 0L); + } + } diff --git a/bnyer-common/bnyer-common-redis/src/main/resources/META-INF/spring.factories b/bnyer-common/bnyer-common-redis/src/main/resources/META-INF/spring.factories index 6dfffb0..d180a1f 100644 --- a/bnyer-common/bnyer-common-redis/src/main/resources/META-INF/spring.factories +++ b/bnyer-common/bnyer-common-redis/src/main/resources/META-INF/spring.factories @@ -2,6 +2,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.bnyer.common.redis.configure.RedisConfig,\ com.bnyer.common.redis.service.RedisService,\ com.bnyer.common.redis.configure.RedissonConfig,\ - com.bnyer.common.redis.service.RedissonService + com.bnyer.common.redis.service.RedissonService,\ + com.bnyer.common.redis.service.RedisCacheService diff --git a/bnyer-common/bnyer-common-rocketmq/pom.xml b/bnyer-common/bnyer-common-rocketmq/pom.xml new file mode 100644 index 0000000..2196bce --- /dev/null +++ b/bnyer-common/bnyer-common-rocketmq/pom.xml @@ -0,0 +1,32 @@ + + + + com.dimensionalnode + bnyer-common + 1.0.0 + + 4.0.0 + + + bnyer-common-rocketmq消息队列模块 + + + bnyer-common-rocketmq + + + + + com.dimensionalnode + bnyer-common-core + + + + + org.apache.rocketmq + rocketmq-spring-boot-starter + + + + diff --git a/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqAdapter.java b/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqAdapter.java new file mode 100644 index 0000000..6b03f74 --- /dev/null +++ b/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqAdapter.java @@ -0,0 +1,38 @@ +package com.bnyer.common.rocketmq.config; + +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.apache.rocketmq.spring.support.RocketMQMessageConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description : + */ +@RefreshScope +@Configuration +public class RocketMqAdapter { + + @Autowired + private RocketMQMessageConverter rocketMqMessageConverter; + + @Value("${rocketmq.name-server:}") + private String nameServer; + + public RocketMQTemplate getTemplateByTopicName(String topic){ + RocketMQTemplate mqTemplate = new RocketMQTemplate(); + DefaultMQProducer producer = new DefaultMQProducer(topic); + producer.setNamesrvAddr(nameServer); + producer.setRetryTimesWhenSendFailed(RocketMqConstant.SYNC_RETRY_FAILED_COUNT); + producer.setRetryTimesWhenSendAsyncFailed(RocketMqConstant.ASYNC_RETRY_FAILED_COUNT); + producer.setSendMsgTimeout((int) RocketMqConstant.TIMEOUT); + mqTemplate.setProducer(producer); + mqTemplate.setMessageConverter(rocketMqMessageConverter.getMessageConverter()); + return mqTemplate; + } + +} diff --git a/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqConstant.java b/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqConstant.java new file mode 100644 index 0000000..03a3c9f --- /dev/null +++ b/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqConstant.java @@ -0,0 +1,45 @@ +package com.bnyer.common.rocketmq.config; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description : + */ +public class RocketMqConstant { + /** + * 默认发送消息超时时间 + */ + public static final long TIMEOUT = 3000; + + /** + * 延迟队列取消订单时间,实际上30分钟 + * 按顺序匹配:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h (1-18) + */ + public static final int CANCEL_ORDER_DELAY_LEVEL = 16; + + /** + * 发送同步消息失败重试次数,默认2 + */ + public static final int SYNC_RETRY_FAILED_COUNT = 2; + + /** + * 发送异步消息失败重试次数,默认2 + */ + public static final int ASYNC_RETRY_FAILED_COUNT = 2; + + /** + * vip订单取消 + */ + public static final String VIP_ORDER_CANCEL_TOPIC = "vip-order-cancel-topic"; + + /** + * vip订单支付成功 + */ + public static final String VIP_ORDER_PAY_NOTIFY_TOPIC = "vip-order-pay-notify-topic"; + + /** + * vip记录创建 + */ + public static final String VIP_RECORD_CREATE_TOPIC = "vip-record-create-topic"; + +} diff --git a/bnyer-common/pom.xml b/bnyer-common/pom.xml index 197f450..ac9d216 100644 --- a/bnyer-common/pom.xml +++ b/bnyer-common/pom.xml @@ -16,6 +16,7 @@ bnyer-common-security bnyer-common-datascope bnyer-common-datasource + bnyer-common-rocketmq bnyer-common diff --git a/bnyer-services/bnyer-file/src/main/java/com/bnyer/file/service/impl/FileServiceImpl.java b/bnyer-services/bnyer-file/src/main/java/com/bnyer/file/service/impl/FileServiceImpl.java index 0b85b33..0d32885 100644 --- a/bnyer-services/bnyer-file/src/main/java/com/bnyer/file/service/impl/FileServiceImpl.java +++ b/bnyer-services/bnyer-file/src/main/java/com/bnyer/file/service/impl/FileServiceImpl.java @@ -2,30 +2,20 @@ package com.bnyer.file.service.impl; import com.bnyer.common.redis.service.RedisService; import com.bnyer.file.constants.RedisKeyConstant; -import com.bnyer.file.dto.FileDto; -import com.bnyer.file.dto.FileUploadDto; import com.bnyer.file.service.IFileService; import com.bnyer.file.service.IQiniuService; import com.bnyer.file.service.ITikTokImage; import com.bnyer.file.utils.ImgUtil; import com.bnyer.file.vo.ChekFileVo; -import com.bnyer.img.api.RemoteImgService; -import com.bnyer.img.api.dto.TiktokImgMiniDto; -import org.apache.http.entity.ContentType; +import com.bnyer.img.api.remote.RemoteImgService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import lombok.extern.slf4j.Slf4j; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.UUID; /** * @Author: Yeman diff --git a/bnyer-services/bnyer-img/pom.xml b/bnyer-services/bnyer-img/pom.xml index 76e544d..a7ee76b 100644 --- a/bnyer-services/bnyer-img/pom.xml +++ b/bnyer-services/bnyer-img/pom.xml @@ -77,6 +77,12 @@ bnyer-common-swagger + + + com.dimensionalnode + bnyer-common-rocketmq + + com.dimensionalnode diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipTypeConstant.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipTypeConstant.java deleted file mode 100644 index fd0fa76..0000000 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipTypeConstant.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.bnyer.img.constants; - -/** - * 用户会员vip常量 - * @author chengkun - * @date 2022/4/21 18:12 - */ -public class UserVipTypeConstant { - - /** - * 月卡 - */ - public static final String MONTH_CARD = "0"; - - /** - * 季卡 - */ - public static final String SEASON_CARD = "1"; - - /** - * 年卡 - */ - public static final String YEAR_CARD = "2"; -} diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/FhMiniController.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/FhMiniController.java index 9b7b329..378c584 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/FhMiniController.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/FhMiniController.java @@ -306,9 +306,9 @@ public class FhMiniController extends BaseController { } @ApiOperation(value="获取用户会员vip列表") - @GetMapping(value = "/queryUserVipList") - public AjaxResult queryUserVipList(){ - return AjaxResult.success(userVipService.queryFront()); + @PostMapping(value = "/queryUserVipList") + public R> queryUserVipList(@RequestBody QueryUserVipDto dto){ + return R.ok(userVipService.queryFront(dto)); } @ApiOperation(value="支付购买用户会员vip") diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/TiktokMiniController.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/TiktokMiniController.java index d60772a..e41c13c 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/TiktokMiniController.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/TiktokMiniController.java @@ -335,8 +335,8 @@ public class TiktokMiniController extends BaseController { @ApiOperation(value="获取用户会员vip列表") @GetMapping(value = "/queryUserVipList") - public AjaxResult queryUserVipList(){ - return AjaxResult.success(userVipService.queryFront()); + public R> queryUserVipList(QueryUserVipDto dto){ + return R.ok(userVipService.queryFront(dto)); } diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/WxMiniController.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/WxMiniController.java index 3352d36..8ca8379 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/WxMiniController.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/WxMiniController.java @@ -12,10 +12,7 @@ import com.bnyer.common.core.web.domain.AjaxResult; import com.bnyer.common.core.web.page.TableDataInfo; import com.bnyer.img.api.model.LoginWechatUser; import com.bnyer.img.service.*; -import com.bnyer.img.vo.CreatorDetailsVo; -import com.bnyer.img.vo.CreatorFollowVo; -import com.bnyer.img.vo.SignImgVo; -import com.bnyer.img.vo.TiktokImgVo; +import com.bnyer.img.vo.*; import com.github.pagehelper.PageHelper; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -324,11 +321,10 @@ public class WxMiniController extends BaseController { @ApiOperation(value="获取用户会员vip列表") @GetMapping(value = "/queryUserVipList") - public AjaxResult queryUserVipList(){ - return AjaxResult.success(userVipService.queryFront()); + public R> queryUserVipList(QueryUserVipDto dto){ + return R.ok(userVipService.queryFront(dto)); } - @ApiOperation(value="支付购买用户会员vip") @PostMapping(value = "/payUserVip") public AjaxResult payUserVip(@Validated @RequestBody @ApiParam("购买会员vip对象") PayUserVipDto dto){ diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/enums/EnumUserVipRecordStatus.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/enums/EnumUserVipRecordStatus.java new file mode 100644 index 0000000..0cd1c0c --- /dev/null +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/enums/EnumUserVipRecordStatus.java @@ -0,0 +1,31 @@ +package com.bnyer.img.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumUserVipRecordStatus { + + EXPIRE(0,"已到期"), + VALID(1,"已生效"), + ; + + private final int status; + + private final String name; + + public static String getStatusName(int status) { + for (EnumUserVipRecordStatus s : EnumUserVipRecordStatus.values()) { + if (status == s.status) { + return s.getName(); + } + } + return null; + } + +} diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/listener/VipRecordCreateConsumer.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/listener/VipRecordCreateConsumer.java new file mode 100644 index 0000000..b365133 --- /dev/null +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/listener/VipRecordCreateConsumer.java @@ -0,0 +1,36 @@ +package com.bnyer.img.listener; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.bnyer.common.core.domain.UserVipRecord; +import com.bnyer.common.core.dto.AddUserVipRecordDto; +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import com.bnyer.img.service.UserVipRecordService; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description :取消订单mq消费监听 + */ +@Slf4j +@Component +@RocketMQMessageListener(topic = RocketMqConstant.VIP_RECORD_CREATE_TOPIC,consumerGroup = RocketMqConstant.VIP_RECORD_CREATE_TOPIC) +public class VipRecordCreateConsumer implements RocketMQListener { + + @Autowired + private UserVipRecordService userVipRecordService; + + @Override + public void onMessage(String message) { + log.info("收到消息:{}", message); + JSONObject jsonObject = JSON.parseObject(message); + AddUserVipRecordDto addUserVipRecordDto = JSON.toJavaObject(jsonObject, AddUserVipRecordDto.class); + //添加用户会员记录 + userVipRecordService.addUserVipRecord(addUserVipRecordDto); + } +} diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/mapper/UserVipMapper.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/mapper/UserVipMapper.java index 683996b..73675ec 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/mapper/UserVipMapper.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/mapper/UserVipMapper.java @@ -2,6 +2,7 @@ package com.bnyer.img.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.bnyer.common.core.domain.UserVip; +import com.bnyer.common.core.dto.QueryUserVipDto; import com.bnyer.img.vo.UserVipVo; import org.apache.ibatis.annotations.Mapper; @@ -14,6 +15,6 @@ public interface UserVipMapper extends BaseMapper { * 获取小程序端会员vip列表 * @return - */ - List queryFront(); + List queryFront(QueryUserVipDto dto); } \ No newline at end of file diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipRecordService.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipRecordService.java index b778e2d..d6d4c08 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipRecordService.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipRecordService.java @@ -1,5 +1,6 @@ package com.bnyer.img.service; +import com.bnyer.common.core.dto.AddUserVipRecordDto; import com.bnyer.common.core.dto.PayUserVipDto; public interface UserVipRecordService { @@ -11,4 +12,12 @@ public interface UserVipRecordService { * @return - */ boolean payUserVip(PayUserVipDto param); + + + /** + * 添加用户会员记录 + * @param dto + */ + void addUserVipRecord(AddUserVipRecordDto dto); + } diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipService.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipService.java index b873ec7..ad7f06e 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipService.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipService.java @@ -1,5 +1,6 @@ package com.bnyer.img.service; +import com.bnyer.common.core.dto.QueryUserVipDto; import com.bnyer.img.vo.UserVipVo; import java.util.List; @@ -10,5 +11,6 @@ public interface UserVipService { * 获取小程序端会员vip列表 * @return - */ - List queryFront(); + List queryFront(QueryUserVipDto dto); + } diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceImpl.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceImpl.java index a6fd3bf..4f61520 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceImpl.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceImpl.java @@ -1,5 +1,6 @@ package com.bnyer.img.service.impl; +import com.bnyer.common.core.dto.QueryUserVipDto; import com.bnyer.img.mapper.UserVipMapper; import com.bnyer.img.service.UserVipService; import com.bnyer.img.vo.UserVipVo; @@ -17,7 +18,7 @@ public class UserVipServiceImpl implements UserVipService { private UserVipMapper userVipMapper; @Override - public List queryFront() { - return userVipMapper.queryFront(); + public List queryFront(QueryUserVipDto dto) { + return userVipMapper.queryFront(dto); } } diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java index 98a7363..2e081cb 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java @@ -3,12 +3,14 @@ package com.bnyer.img.service.impl; import cn.hutool.core.util.IdUtil; import com.bnyer.common.core.constant.RedisKeyConstant; import com.bnyer.common.core.domain.UserVipRecord; +import com.bnyer.common.core.domain.VipOrder; +import com.bnyer.common.core.dto.AddUserVipRecordDto; import com.bnyer.common.core.dto.PayUserVipDto; import com.bnyer.common.core.exception.ServiceException; -import com.bnyer.common.core.utils.DateUtils; +import com.bnyer.common.core.utils.bean.EntityConvertUtil; import com.bnyer.common.redis.service.RedissonService; import com.bnyer.img.constants.UserVipOrderStatusConstant; -import com.bnyer.img.constants.UserVipTypeConstant; +import com.bnyer.img.enums.EnumUserVipRecordStatus; import com.bnyer.img.mapper.UserVipRecordMapper; import com.bnyer.img.service.UserVipRecordService; import lombok.extern.slf4j.Slf4j; @@ -33,53 +35,62 @@ public class UserVipServiceRecordImpl implements UserVipRecordService { @Override public boolean payUserVip(PayUserVipDto param) { - UserVipRecord userVipRecord = new UserVipRecord(); - //生成订单id - userVipRecord.setOrderId(IdUtil.getSnowflakeNextIdStr()); - //获取分布式锁 - RLock lock = redissonService.getRLock(RedisKeyConstant.PAY_USER_VIP_LOCK_KEY + userVipRecord.getOrderId()); - try{ - if(lock.tryLock(500, 10000, TimeUnit.MILLISECONDS)){ - //调用支付 - - //保存订单 - userVipRecord.setCreateTime(new Date()); - userVipRecord.setUpdateTime(new Date()); - userVipRecord.setPrice(new BigDecimal(param.getPrice())); - userVipRecord.setStatus(UserVipOrderStatusConstant.UN_PAY); - userVipRecord.setIsShow("1"); - Date startTime = new Date(); - userVipRecord.setStartTime(startTime); - if(userVipRecord.getType().equals(UserVipTypeConstant.MONTH_CARD)){ - //计算月卡(30天)的结束时间 - userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 30)); - }else if(userVipRecord.getType().equals(UserVipTypeConstant.SEASON_CARD)){ - //结算季卡(90天)的结束时间 - userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 90)); - }else{ - //计算年卡(365天)的结束时间 - userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 365)); - } - }else{ - userVipRecord.setStatus(UserVipOrderStatusConstant.PAY_EXCEPTION); - log.error("用户会员vip支付异常,锁被占用!"); - throw new ServiceException("系统繁忙,请稍候重试!"); - } - }catch (Exception e){ - userVipRecord.setStatus(UserVipOrderStatusConstant.PAY_FAIL); - log.error("用户会员vip支付失败!错误原因为【{}】",e.getMessage()); - throw new ServiceException("系统繁忙,请勿重复操作!"); - }finally { - int insert = userVipRecordMapper.insert(userVipRecord); - if(insert > 0){ - return true; - } - //释放锁 - if(lock.isHeldByCurrentThread()){ - lock.unlock(); - log.info("用户会员vip支付操作执行完毕,释放锁成功!"); - } - } +// UserVipRecord userVipRecord = new UserVipRecord(); +// //生成订单id +// userVipRecord.setOrderId(IdUtil.getSnowflakeNextIdStr()); +// //获取分布式锁 +// RLock lock = redissonService.getRLock(RedisKeyConstant.PAY_USER_VIP_LOCK_KEY + userVipRecord.getOrderId()); +// try{ +// if(lock.tryLock(500, 10000, TimeUnit.MILLISECONDS)){ +// //调用支付 +// +// //保存订单 +// userVipRecord.setCreateTime(new Date()); +// userVipRecord.setUpdateTime(new Date()); +// userVipRecord.setPrice(new BigDecimal(param.getPrice())); +// userVipRecord.setStatus(UserVipOrderStatusConstant.UN_PAY); +// userVipRecord.setIsShow("1"); +// Date startTime = new Date(); +// userVipRecord.setStartTime(startTime); +//// if(userVipRecord.getType().equals(UserVipTypeConstant.MONTH_CARD)){ +//// //计算月卡(30天)的结束时间 +//// userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 30)); +//// }else if(userVipRecord.getType().equals(UserVipTypeConstant.SEASON_CARD)){ +//// //结算季卡(90天)的结束时间 +//// userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 90)); +//// }else{ +//// //计算年卡(365天)的结束时间 +//// userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 365)); +//// } +// }else{ +// userVipRecord.setStatus(UserVipOrderStatusConstant.PAY_EXCEPTION); +// log.error("用户会员vip支付异常,锁被占用!"); +// throw new ServiceException("系统繁忙,请稍候重试!"); +// } +// }catch (Exception e){ +// userVipRecord.setStatus(UserVipOrderStatusConstant.PAY_FAIL); +// log.error("用户会员vip支付失败!错误原因为【{}】",e.getMessage()); +// throw new ServiceException("系统繁忙,请勿重复操作!"); +// }finally { +// int insert = userVipRecordMapper.insert(userVipRecord); +// if(insert > 0){ +// return true; +// } +// //释放锁 +// if(lock.isHeldByCurrentThread()){ +// lock.unlock(); +// log.info("用户会员vip支付操作执行完毕,释放锁成功!"); +// } +// } return false; } + + @Override + public void addUserVipRecord(AddUserVipRecordDto dto) { + UserVipRecord userVipRecord = EntityConvertUtil.copy(dto, UserVipRecord.class); + userVipRecord.setStatus(EnumUserVipRecordStatus.VALID.getStatus()); + userVipRecord.setCreateTime(new Date()); + userVipRecord.setIsShow("1"); + userVipRecordMapper.insert(userVipRecord); + } } diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/vo/UserVipVo.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/vo/UserVipVo.java index 2ca8e07..b247082 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/vo/UserVipVo.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/vo/UserVipVo.java @@ -1,7 +1,5 @@ package com.bnyer.img.vo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; @@ -19,9 +17,18 @@ public class UserVipVo implements Serializable { @ApiModelProperty(value="主键Id") private Long id; + @ApiModelProperty(value="vip类型id") + private Long vipTypeId; + + @ApiModelProperty(value="vip编码") + private String vipCode; + @ApiModelProperty(value="vip名称") private String vipName; + @ApiModelProperty(value = "vip类型:下图:0(月卡) 1(季卡) 2(年卡)") + private String vipType; + @ApiModelProperty(value="原价") private BigDecimal originPrice; @@ -37,5 +44,23 @@ public class UserVipVo implements Serializable { @ApiModelProperty(value="排序") private Integer sort; + @ApiModelProperty(value="是否到期自动续费(0>否;1->是)") + private String isDelay; + + @ApiModelProperty(value = "用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信") + private Integer userClientType; + + @ApiModelProperty(value = "有效时长单位:0天,1周,2月,3季,4年") + private Integer validTimeUnit; + + @ApiModelProperty(value = "有效时长:表示几天、几周、几月、几年") + private Integer validTimeNum; + + @ApiModelProperty(value = "会员类型编码") + private String vipTypeCode; + + @ApiModelProperty(value = "会员类型名称") + private String vipTypeName; + private static final long serialVersionUID = 1L; } diff --git a/bnyer-services/bnyer-img/src/main/resources/com/bnyer/img/mapper/UserVipMapper.xml b/bnyer-services/bnyer-img/src/main/resources/com/bnyer/img/mapper/UserVipMapper.xml index 84f4c52..26c8868 100644 --- a/bnyer-services/bnyer-img/src/main/resources/com/bnyer/img/mapper/UserVipMapper.xml +++ b/bnyer-services/bnyer-img/src/main/resources/com/bnyer/img/mapper/UserVipMapper.xml @@ -6,12 +6,15 @@ + + - + + @@ -20,14 +23,33 @@ - id, vip_name, vip_code, origin_price, price, description, hot_sign_desc, `days`, - is_delay, is_show, create_time, update_time, sort + v.id, + v.vip_name, + v.vip_code, + v.vip_type_id, + v.origin_price, + v.price, + v.description, + v.hot_sign_desc, + v.valid_time_unit, + v.valid_time_num, + v.is_delay, + v.is_show, + v.create_time, + v.update_time, + v.sort \ No newline at end of file diff --git a/bnyer-services/bnyer-img/src/main/resources/com/bnyer/img/mapper/UserVipRecordMapper.xml b/bnyer-services/bnyer-img/src/main/resources/com/bnyer/img/mapper/UserVipRecordMapper.xml index 29a6113..a037825 100644 --- a/bnyer-services/bnyer-img/src/main/resources/com/bnyer/img/mapper/UserVipRecordMapper.xml +++ b/bnyer-services/bnyer-img/src/main/resources/com/bnyer/img/mapper/UserVipRecordMapper.xml @@ -5,14 +5,16 @@ - + + - + - - - + + + + @@ -20,7 +22,7 @@ - id,order_id, phone,price,status, user_vip_id, start_time, end_time,type, is_show, create_time, update_time, + id,order_no,user_id , phone,vip_name,vip_type_name,user_client_type,status, vip_id, start_time, end_time, is_show, create_time, update_time, sort diff --git a/bnyer-services/bnyer-order/pom.xml b/bnyer-services/bnyer-order/pom.xml index bd5406a..e8cab7e 100644 --- a/bnyer-services/bnyer-order/pom.xml +++ b/bnyer-services/bnyer-order/pom.xml @@ -84,6 +84,11 @@ mybatis-plus-boot-starter + + + com.dimensionalnode + bnyer-common-rocketmq + @@ -123,4 +128,4 @@ - \ No newline at end of file + diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/BnyerOrderApplication.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/BnyerOrderApplication.java index 4a7db6a..2674afb 100644 --- a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/BnyerOrderApplication.java +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/BnyerOrderApplication.java @@ -15,7 +15,7 @@ import org.springframework.scheduling.annotation.EnableAsync; @EnableCustomConfig @EnableCustomSwagger2 @EnableRyFeignClients -@SpringBootApplication +@SpringBootApplication(scanBasePackages = { "com.bnyer" }) @EnableAsync public class BnyerOrderApplication { diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/OrderCacheTtlAdapter.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/OrderCacheTtlAdapter.java new file mode 100644 index 0000000..9155ba0 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/OrderCacheTtlAdapter.java @@ -0,0 +1,27 @@ +package com.bnyer.order.config; + +import com.bnyer.common.redis.adapter.CacheTtlAdapter; +import com.bnyer.common.redis.bo.CacheNameWithTtlBo; +import com.bnyer.common.redis.constant.OrderCacheNames; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : + */ +@Component +public class OrderCacheTtlAdapter implements CacheTtlAdapter{ + + @Override + public List listCacheNameWithTtl() { + List cacheNameWithTtls = new ArrayList<>(); + // 确认订单缓存30分钟 + cacheNameWithTtls.add(new CacheNameWithTtlBo(OrderCacheNames.VIP_ORDER_PREFIX + OrderCacheNames.ORDER_CONFIRM_UID_KEY, 60 * 30)); + cacheNameWithTtls.add(new CacheNameWithTtlBo(OrderCacheNames.VIP_ORDER_PREFIX + OrderCacheNames.ORDER_CONFIRM_KEY, 60 * 30)); + return cacheNameWithTtls; + } +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/RocketMqConfig.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/RocketMqConfig.java new file mode 100644 index 0000000..ad5d314 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/RocketMqConfig.java @@ -0,0 +1,35 @@ +package com.bnyer.order.config; + +import com.bnyer.common.rocketmq.config.RocketMqAdapter; +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description : + */ +@RefreshScope +@Configuration +public class RocketMqConfig { + @Autowired + private RocketMqAdapter rocketMqAdapter; + + @Lazy + @Bean(destroyMethod = "destroy") + public RocketMQTemplate orderCancelMqTemplate() { + return rocketMqAdapter.getTemplateByTopicName(RocketMqConstant.VIP_ORDER_CANCEL_TOPIC); + } + + @Lazy + @Bean(destroyMethod = "destroy") + public RocketMQTemplate vipRecordMqTemplate() { + return rocketMqAdapter.getTemplateByTopicName(RocketMqConstant.VIP_RECORD_CREATE_TOPIC); + } + +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/VipOrderController.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/VipOrderController.java new file mode 100644 index 0000000..107c996 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/VipOrderController.java @@ -0,0 +1,54 @@ +package com.bnyer.order.controller; + +import com.bnyer.common.core.domain.R; +import com.bnyer.common.core.web.controller.BaseController; +import com.bnyer.order.dto.AddVipOrderDto; +import com.bnyer.order.dto.QueryVipOrderDto; +import com.bnyer.order.service.VipOrderService; +import com.bnyer.order.vo.VipOrderVo; +import io.swagger.annotations.Api; +import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description : + */ +@Api(value = "会员订单相关接口",tags = "会员订单相关接口") +@RestController +@RequestMapping("/vip") +@Slf4j +public class VipOrderController extends BaseController { + + + @Autowired + private VipOrderService vipOrderService; + + + /** + * 提交订单 + */ + @PostMapping("/addVipOrder") + @Operation(summary = "生成会员订单,返回订单号" , description = "生成会员订单,返回订单号,通过订单号调用支付接口") + public R addVipOrder(@Valid @RequestBody AddVipOrderDto addVipOrderDto) { + String orderNo = vipOrderService.addVipOrder(addVipOrderDto); + return R.ok(orderNo); + } + + /** + * 查询订单信息 + */ + @PostMapping("/getVipOrderList") + @Operation(summary = "查询会员订单列表信息" , description = "查询会员订单列表信息") + public R> getVipOrderList(@Valid @RequestBody QueryVipOrderDto dto) { + List vipOrderVoList = vipOrderService.getVipOrderList(dto); + return R.ok(vipOrderVoList); + } + +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/testController.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/testController.java deleted file mode 100644 index ade0f8a..0000000 --- a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/testController.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bnyer.order.controller; - -public class testController { -} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/dto/AddVipOrderDto.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/dto/AddVipOrderDto.java new file mode 100644 index 0000000..5ece95d --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/dto/AddVipOrderDto.java @@ -0,0 +1,36 @@ +package com.bnyer.order.dto; + +import com.bnyer.common.core.annotation.CustomParamsValidation; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.validator.constraints.Range; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class AddVipOrderDto implements Serializable { + + @NotBlank(message = "手机号不能为空!") + @ApiModelProperty(value="手机号") + private String phone; + + @NotNull(message = "vipId不能为空!") + @ApiModelProperty(value="vip表id") + private Long vipId; + + @NotNull(message = "用户客户端类型不能为空!") + @ApiModelProperty(value="用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信") + private Integer userClientType; + +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/dto/QueryVipOrderDto.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/dto/QueryVipOrderDto.java new file mode 100644 index 0000000..74bd07c --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/dto/QueryVipOrderDto.java @@ -0,0 +1,23 @@ +package com.bnyer.order.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class QueryVipOrderDto implements Serializable { + + @ApiModelProperty(value="订单号") + private String orderNo; + +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumVipOrderStatus.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumVipOrderStatus.java new file mode 100644 index 0000000..45fd1e5 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumVipOrderStatus.java @@ -0,0 +1,32 @@ +package com.bnyer.order.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumVipOrderStatus { + + NOT_PROCESS(0,"未处理"), + SUCCESS(1,"成功"), + FAILS(2,"失败"), + ; + + private final int status; + + private final String name; + + public static String getStatusName(int status) { + for (EnumVipOrderStatus s : EnumVipOrderStatus.values()) { + if (status == s.status) { + return s.getName(); + } + } + return null; + } + +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumVipType.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumVipType.java new file mode 100644 index 0000000..559ff0b --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumVipType.java @@ -0,0 +1,50 @@ +package com.bnyer.order.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 用户会员vip常量 + * @author chengkun + * @date 2022/4/21 18:12 + */ +@Getter +@AllArgsConstructor +public enum EnumVipType { + + //========================下图会员=================== + MONTH_CARD("0","月卡", com.bnyer.common.core.enums.EnumVipType.IMG_VIP), + SEASON_CARD("1","季卡", com.bnyer.common.core.enums.EnumVipType.IMG_VIP), + YEAR_CARD("2","年卡", com.bnyer.common.core.enums.EnumVipType.IMG_VIP), + + //========================视频会员(待扩展)=================== + + + + //========================AI会员(待扩展)==================== + ; + + private final String typeCode; + + private final String typeName; + + private final com.bnyer.common.core.enums.EnumVipType enumVipType; + + public static String getNameByTypeCode(String typeCode) { + for (EnumVipType s : com.bnyer.order.enums.EnumVipType.values()) { + if (typeCode.equals(s.getTypeCode())) { + return s.getTypeName(); + } + } + return null; + } + + public static String getCategoryByTypeCode(String typeCode){ + for (EnumVipType s : com.bnyer.order.enums.EnumVipType.values()) { + if (typeCode.equals(s.getTypeCode())) { + return s.getEnumVipType().getCode(); + } + } + return null; + } +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderCancelConsumer.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderCancelConsumer.java new file mode 100644 index 0000000..028cdf4 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderCancelConsumer.java @@ -0,0 +1,31 @@ +package com.bnyer.order.listener.vip; + +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import com.bnyer.order.service.VipOrderService; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description :取消订单mq消费监听 + */ +@Slf4j +@Component +@RocketMQMessageListener(topic = RocketMqConstant.VIP_ORDER_CANCEL_TOPIC,consumerGroup = RocketMqConstant.VIP_ORDER_CANCEL_TOPIC) +public class VipOrderCancelConsumer implements RocketMQListener { + + @Autowired + private VipOrderService vipOrderService; + + @Override + public void onMessage(String orderNo) { + log.info("收到消息:{}", orderNo); + // 如果订单未支付的话,将订单设为取消状态 + vipOrderService.cancelVipOrder(Lists.newArrayList(orderNo),0); + } +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderPayNotifyConsumer.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderPayNotifyConsumer.java new file mode 100644 index 0000000..33b4a2d --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderPayNotifyConsumer.java @@ -0,0 +1,82 @@ +package com.bnyer.order.listener.vip; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bnyer.common.core.domain.VipOrder; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.SpringUtils; +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import com.bnyer.order.mapper.VipOrderMapper; +import com.bnyer.order.service.VipOrderService; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Objects; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description :订单支付回调mq消费监听 + */ +@Slf4j +@Component +@RocketMQMessageListener(topic = RocketMqConstant.VIP_ORDER_PAY_NOTIFY_TOPIC,consumerGroup = RocketMqConstant.VIP_ORDER_PAY_NOTIFY_TOPIC) +public class VipOrderPayNotifyConsumer implements RocketMQListener { + + @Resource + private VipOrderMapper vipOrderMapper; + + @Resource + private RocketMQTemplate vipRecordMqTemplate; + + @Override + public void onMessage(String message) { + log.info("收到消息:{}", message); + //修改订单并添加会员记录 + VipOrderService vipOrderService = SpringUtils.getBean(VipOrderService.class); + JSONObject orderObj = JSON.parseObject(message); + String orderNo = orderObj.getString("orderNo"); + VipOrder vipOrder = vipOrderMapper.selectOne(new LambdaQueryWrapper().eq(VipOrder::getOrderNo, orderNo)); + if (Objects.isNull(vipOrder)){ + log.error("订单不存在,订单号:{}",orderNo); + return; + } + //修改订单表状态为已支付 + vipOrderService.updateByToPaySuccess(vipOrder); + //发消息,添加用户会员记录 + String msg = buildVipRecordMsg(vipOrder); + SendStatus sendStatus = vipRecordMqTemplate.syncSend(RocketMqConstant.VIP_RECORD_CREATE_TOPIC, new GenericMessage<>(msg)).getSendStatus(); + if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) { + // 消息发不出去就抛异常 + throw new ServiceException(ResponseEnum.SERVER_ERROR); + } + } + + /** + * 构建vip记录消息体 + * @param vipOrder + * @return + */ + private String buildVipRecordMsg(VipOrder vipOrder) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("orderNo",vipOrder.getOrderNo()); + jsonObject.put("startTime",vipOrder.getStartTime()); + jsonObject.put("endTime",vipOrder.getEndTime()); + jsonObject.put("vipId",vipOrder.getVipId()); + jsonObject.put("vipName",vipOrder.getVipName()); + jsonObject.put("vipTypeName",vipOrder.getVipTypeName()); + jsonObject.put("userClientType",vipOrder.getUserClientType()); + jsonObject.put("phone",vipOrder.getPhone()); + jsonObject.put("userId",vipOrder.getUserId()); + return JSON.toJSONString(jsonObject); + } + +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/mapper/TestMapper.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/mapper/TestMapper.java deleted file mode 100644 index a1d8962..0000000 --- a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/mapper/TestMapper.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.bnyer.order.mapper; - -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public class TestMapper { -} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/mapper/VipOrderMapper.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/mapper/VipOrderMapper.java new file mode 100644 index 0000000..750e432 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/mapper/VipOrderMapper.java @@ -0,0 +1,36 @@ +package com.bnyer.order.mapper; + +import java.util.List; + +import com.bnyer.order.dto.QueryVipOrderDto; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bnyer.common.core.domain.VipOrder; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * @author :WXC + * @description : + */ +@Mapper +public interface VipOrderMapper extends BaseMapper { + /** + * 查询订单信息 + * @param dto + * @return + */ + List queryVipOrderList(QueryVipOrderDto dto); + + /** + * 取消订单 + * @param orderNos + */ + void cancelVipOrder(@Param("orderNos") List orderNos, @Param("closeType") Integer closeType); + + /** + * 修改订单表状态为已支付 + * @param orderNo + */ + void updateByToPaySuccess(@Param("orderNo") String orderNo); +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/TestService.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/TestService.java deleted file mode 100644 index 20ed969..0000000 --- a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/TestService.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bnyer.order.service; - -public interface TestService { -} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/VipOrderService.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/VipOrderService.java new file mode 100644 index 0000000..83e70f1 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/VipOrderService.java @@ -0,0 +1,45 @@ +package com.bnyer.order.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.bnyer.common.core.domain.VipOrder; +import com.bnyer.order.dto.QueryVipOrderDto; +import com.bnyer.order.dto.AddVipOrderDto; +import com.bnyer.order.vo.VipOrderVo; + +import java.util.List; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +public interface VipOrderService extends IService { + + /** + * 生成会员订单,返回订单号 + * + * @param addVipOrderDto 下单所需要的参数 + * @return 支付流水号 + */ + String addVipOrder(AddVipOrderDto addVipOrderDto); + + /** + * 取消订单 + * @param orderNos 订单号 + * @param closeType 订单关闭原因类型 + */ + void cancelVipOrder(List orderNos,Integer closeType); + + /** + * 查询会员订单列表信息 + * @param dto + * @return + */ + List getVipOrderList(QueryVipOrderDto dto); + + /** + * 更新订单信息 + * @param vipOrder + */ + void updateByToPaySuccess(VipOrder vipOrder); +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/TestServiceImpl.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/TestServiceImpl.java deleted file mode 100644 index 4bb1f8f..0000000 --- a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/TestServiceImpl.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.bnyer.order.service.impl; - -import com.bnyer.order.service.TestService; -import org.springframework.stereotype.Service; - -@Service -public class TestServiceImpl implements TestService { -} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/VipOrderServiceImpl.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/VipOrderServiceImpl.java new file mode 100644 index 0000000..64ef74d --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/VipOrderServiceImpl.java @@ -0,0 +1,195 @@ +package com.bnyer.order.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bnyer.common.core.domain.R; +import com.bnyer.common.core.domain.VipOrder; +import com.bnyer.common.core.enums.*; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.DateUtils; +import com.bnyer.common.core.utils.OrderUtil; +import com.bnyer.common.core.utils.bean.EntityConvertUtil; +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import com.bnyer.common.security.utils.SecurityUtils; +import com.bnyer.img.api.dto.QueryUserVipDto; +import com.bnyer.img.api.remote.RemoteWxMiniService; +import com.bnyer.img.api.vo.UserVipInfoVo; +import com.bnyer.order.dto.AddVipOrderDto; +import com.bnyer.order.dto.QueryVipOrderDto; +import com.bnyer.order.enums.EnumVipOrderStatus; +import com.bnyer.order.mapper.VipOrderMapper; +import com.bnyer.order.service.VipOrderService; +import com.bnyer.order.vo.VipOrderVo; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : + */ +@Slf4j +@Service +public class VipOrderServiceImpl extends ServiceImpl implements VipOrderService { + + @Autowired + private RocketMQTemplate orderCancelMqTemplate; + + @Autowired + private VipOrderMapper vipOrderMapper; + + @Autowired + private RemoteWxMiniService remoteWxMiniService; + + /** + * 查询会员订单列表信息 + * @param dto + * @return + */ + @Override + public List getVipOrderList(QueryVipOrderDto dto) { + List vipOrderList = vipOrderMapper.queryVipOrderList(dto); + List vipOrderVoList = EntityConvertUtil.copy(vipOrderList, VipOrderVo.class); + return vipOrderVoList; + } + + + /** + * 生成会员订单,返回订单号 + * @param addVipOrderDto 下单所需要的参数 + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public String addVipOrder(AddVipOrderDto addVipOrderDto) { + //构建订单 + VipOrder vipOrder = buildVipOrder(addVipOrderDto); + vipOrderMapper.insert(vipOrder); + String orderNo = vipOrder.getOrderNo(); + //发送消息,如果三十分钟后没有支付,则取消订单 + SendStatus sendStatus = orderCancelMqTemplate.syncSend(RocketMqConstant.VIP_ORDER_CANCEL_TOPIC, new GenericMessage<>(orderNo), RocketMqConstant.TIMEOUT, RocketMqConstant.CANCEL_ORDER_DELAY_LEVEL).getSendStatus(); + if (!Objects.equals(sendStatus,SendStatus.SEND_OK)) { + // 消息发不出去就抛异常,发的出去无所谓 + throw new ServiceException(ResponseEnum.SERVER_ERROR); + }else { + log.info("消息发送成功,topic:{}",RocketMqConstant.VIP_ORDER_CANCEL_TOPIC); + } + return orderNo; + } + + /** + * 构建订单 + * @param addVipOrderDto + * @return + */ + private VipOrder buildVipOrder(AddVipOrderDto addVipOrderDto) { + //查询会员信息 + QueryUserVipDto queryUserVipDto = new QueryUserVipDto(); + queryUserVipDto.setId(addVipOrderDto.getVipId()); + R> userVipVoListResult = remoteWxMiniService.queryUserVipList(queryUserVipDto); + if (!userVipVoListResult.isSuccess()){ + log.error("内部接口调用异常:url:{}request{},result{},error:{}","queryUserVipList", JSON.toJSONString(queryUserVipDto),JSON.toJSONString(userVipVoListResult),userVipVoListResult.getMsg()); + throw new ServiceException(userVipVoListResult.getMsg()); + } + List userVipInfoVoList = userVipVoListResult.getData(); + if (CollUtil.isEmpty(userVipInfoVoList)){ + throw new ServiceException(ResponseEnum.NOT_EXIST,"会员信息"); + } + UserVipInfoVo userVipInfoVo = userVipInfoVoList.get(0); + VipOrder vipOrder = null; + EnumVipType enumVipType = EnumVipType.getVipTypeByCode(userVipInfoVo.getVipTypeCode()); + switch (enumVipType){ + case IMG_VIP: + vipOrder = buildImgVipOrder(userVipInfoVo,addVipOrderDto); + break; + case VIDEO_VIP: + break; + case AI_VIP: + break; + default: + throw new ServiceException("vipTypeCode 未匹配上对应分类"); + } + return vipOrder; + } + + /** + * 构建下图会员订单 + * @param userVipInfoVo + * @param addVipOrderDto + * @return + */ + private VipOrder buildImgVipOrder(UserVipInfoVo userVipInfoVo, AddVipOrderDto addVipOrderDto) { + Long userId = SecurityUtils.getUserId(); + VipOrder vipOrder = EntityConvertUtil.copy(userVipInfoVo, VipOrder.class); + vipOrder.setPhone(addVipOrderDto.getPhone()); + vipOrder.setVipId(addVipOrderDto.getVipId()); + vipOrder.setVipName(userVipInfoVo.getVipName()); + vipOrder.setVipTypeName(userVipInfoVo.getVipTypeName()); + vipOrder.setPayAmount(userVipInfoVo.getPrice()); + vipOrder.setVipTypeName(userVipInfoVo.getVipTypeName()); + vipOrder.setUserId(userId); +// vipOrder.setIsDelay("0"); + Date nowDate = new Date(); + vipOrder.setStartTime(nowDate); + EnumTimeUnit enumTimeUnitByUnit = EnumTimeUnit.getEnumTimeUnitByUnit(userVipInfoVo.getValidTimeUnit()); + //计算会员结束时间 + vipOrder.setEndTime(DateUtils.getDateByType(enumTimeUnitByUnit,nowDate, userVipInfoVo.getValidTimeNum())); + //开通的天数 + vipOrder.setDays((int) DateUtil.betweenDay(vipOrder.getStartTime(), vipOrder.getEndTime(), true)); + vipOrder.setUserClientType(addVipOrderDto.getUserClientType()); + vipOrder.setOrderNo(OrderUtil.getOrderNo("RV",nowDate, EnumUserClientType.getCodeByType(addVipOrderDto.getUserClientType()),String.valueOf(userId))); + vipOrder.setCreateTime(nowDate); + return vipOrder; + } + + + /** + * 取消订单 + * @param orderNos 订单id + * @param closeType 订单关闭原因类型 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void cancelVipOrder(List orderNos,Integer closeType) { + List vipOrderList = vipOrderMapper.selectList(new LambdaQueryWrapper().in(VipOrder::getOrderNo, orderNos)); + if (CollUtil.isEmpty(vipOrderList)){ + return; + } + List cancelOrderNos = vipOrderList.stream().filter(vipOrder -> Objects.nonNull(vipOrder.getOrderStatus()) && EnumVipOrderStatus.FAILS.getStatus() + != vipOrder.getOrderStatus()).map(VipOrder::getOrderNo).collect(Collectors.toList()); + if (CollUtil.isEmpty(cancelOrderNos)){ + return; + } + vipOrderMapper.cancelVipOrder(cancelOrderNos,closeType); + } + + + /** + * 更新订单信息为已支付 + * @param vipOrder + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void updateByToPaySuccess(VipOrder vipOrder) { + vipOrder.setPayStatus(EnumPayStatus.SUCCESS.getStatus()); + vipOrder.setOrderStatus(EnumVipOrderStatus.SUCCESS.getStatus()); + vipOrder.setUpdateTime(new Date()); + vipOrder.setPayTime(new Date()); + //更新订单 + vipOrderMapper.updateById(vipOrder); + } + +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/vo/VipOrderVo.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/vo/VipOrderVo.java new file mode 100644 index 0000000..cf40fc4 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/vo/VipOrderVo.java @@ -0,0 +1,71 @@ +package com.bnyer.order.vo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class VipOrderVo { + + @ApiModelProperty(value="主键") + private Long id; + + @ApiModelProperty(value="订单号") + private String orderNo; + + @ApiModelProperty(value="手机号") + private String phone; + + @ApiModelProperty(value="用户id") + private Long userId; + + @ApiModelProperty(value="vip表id") + private Long vipId; + + @ApiModelProperty(value = "vip编码") + private String vipCode; + + @ApiModelProperty(value = "vip名称") + private String vipName; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value="开始时间") + private Date startTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value="到期时间") + private Date endTime; + + @ApiModelProperty(value = "用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信") + private Integer userClientType; + + @ApiModelProperty(value="是否到期自动续费(0>否;1->是)") + private String isDelay; + + @ApiModelProperty(value="时长天数") + private Integer days; + + @ApiModelProperty(value="支付金额") + private BigDecimal payAmount; + + @ApiModelProperty(value = "vip类型名称") + private String vipTypeName; + + @ApiModelProperty(value="支付状态:1000未支付;1001支付成功 ;1002支付失败") + private Integer payStatus; + +} diff --git a/bnyer-services/bnyer-order/src/main/resources/com/bnyer/order/mapper/VipOrderMapper.xml b/bnyer-services/bnyer-order/src/main/resources/com/bnyer/order/mapper/VipOrderMapper.xml new file mode 100644 index 0000000..1a20417 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/resources/com/bnyer/order/mapper/VipOrderMapper.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + o.id, + o.order_no, + o.phone, + o.user_id, + o.vip_id, + o.vip_code, + o.vip_name, + o.pay_amount, + o.is_delay, + o.`days`, + o.vip_type_name, + o.start_time, + o.end_time, + o.order_status, + o.close_type, + o.pay_time, + o.cancel_time, + o.user_client_type, + o.remark, + o.pay_status, + o.sort, + o.is_show, + o.create_time, + o.update_time + + + + + + + + update order_vip_order + set + order_status = 2, + close_type = #{closeType,jdbcType=VARCHAR}, + cancel_time = now(), + update_time=now() + where pay_status = 1000 and order_no in + + #{orderNo} + + + + update order_vip_order + set + order_status = 1, + pay_status = 1001, + pay_time = now(), + update_time = now() + where order_no = #{orderNo} + + diff --git a/bnyer-services/bnyer-pay/pom.xml b/bnyer-services/bnyer-pay/pom.xml index d0b2d0b..3eec3b1 100644 --- a/bnyer-services/bnyer-pay/pom.xml +++ b/bnyer-services/bnyer-pay/pom.xml @@ -89,6 +89,29 @@ mybatis-plus-boot-starter + + + com.dimensionalnode + bnyer-common-rocketmq + + + + + com.alipay.sdk + alipay-sdk-java + + + + + com.github.binarywang + weixin-java-pay + + + + com.dimensionalnode + bnyer-api-order + 1.0.0 + @@ -128,4 +151,4 @@ - \ No newline at end of file + diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/BnyerPayApplication.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/BnyerPayApplication.java index a30abb8..b3112d7 100644 --- a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/BnyerPayApplication.java +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/BnyerPayApplication.java @@ -15,7 +15,7 @@ import org.springframework.scheduling.annotation.EnableAsync; @EnableCustomConfig @EnableCustomSwagger2 @EnableRyFeignClients -@SpringBootApplication +@SpringBootApplication(scanBasePackages = { "com.bnyer" }) @EnableAsync public class BnyerPayApplication { diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/bo/QueryOrderBo.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/bo/QueryOrderBo.java new file mode 100644 index 0000000..2f6c7a9 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/bo/QueryOrderBo.java @@ -0,0 +1,30 @@ +package com.bnyer.pay.bo; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * @author :WXC + * @Date :2023/05/05 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class QueryOrderBo { + + /** + * 支付类型:wxpay/alipay/kspay/dypay + */ + private String payType; + /** + * 交易类型 1--JSAPI支付(小程序appId支付)、2--Native支付、3--app支付,4--JSAPI支付(公众号appId支付)5--H5支付,微信支付必填 + */ + private String tradeType; + /** + * 支付单号 + */ + private String payId; + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/bo/UnifiedOrderBo.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/bo/UnifiedOrderBo.java new file mode 100644 index 0000000..d722651 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/bo/UnifiedOrderBo.java @@ -0,0 +1,55 @@ +package com.bnyer.pay.bo; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Date; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class UnifiedOrderBo { + + /** + * 内部支付单号 + */ + private String payId; + /** + * trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。 + */ + private String openId; + /** + * 支付金额:元 + */ + private String payAmount; + /** + * ip地址 + */ + private String ip; + /** + * 当前时间 + */ + private Date currDate; + /** + * 交易类型 1--JSAPI支付(小程序appId支付)、2--Native支付、3--app支付,4--JSAPI支付(公众号appId支付)5--H5支付,微信支付必填 + */ + private String tradeType; + /** + * 商品类型:快手支付需要 + */ + private Integer goodsType; + /** + * 商品标题:会员充值 + */ + private String goodsSubject; + /** + * 商品描述: 会员充值 + */ + private String goodsDesc; +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RestTemplateConfiguration.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RestTemplateConfiguration.java new file mode 100644 index 0000000..9a4fe66 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RestTemplateConfiguration.java @@ -0,0 +1,27 @@ +package com.bnyer.pay.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * @author chengkun + * @date 2022/4/28 17:27 + */ +@Configuration +public class RestTemplateConfiguration { + @Bean + public RestTemplate restTemplate(ClientHttpRequestFactory factory) { + return new RestTemplate(factory); + } + + @Bean + public ClientHttpRequestFactory simpleClientHttpRequestFactory(){ + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setConnectTimeout(15000); + factory.setReadTimeout(10000); + return factory; + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RocketMqConfig.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RocketMqConfig.java new file mode 100644 index 0000000..3b4cc5f --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RocketMqConfig.java @@ -0,0 +1,28 @@ +package com.bnyer.pay.config; + +import com.bnyer.common.rocketmq.config.RocketMqAdapter; +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description : + */ +@RefreshScope +@Configuration +public class RocketMqConfig { + @Autowired + private RocketMqAdapter rocketMqAdapter; + + @Lazy + @Bean(destroyMethod = "destroy") + public RocketMQTemplate vipOrderPayNotifyMqTemplate() { + return rocketMqAdapter.getTemplateByTopicName(RocketMqConstant.VIP_ORDER_PAY_NOTIFY_TOPIC); + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/TestConfig.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/TestConfig.java deleted file mode 100644 index 263d3ce..0000000 --- a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/TestConfig.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bnyer.pay.config; - -public class TestConfig { -} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/AliPayConstant.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/AliPayConstant.java new file mode 100644 index 0000000..8b10c79 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/AliPayConstant.java @@ -0,0 +1,24 @@ +package com.bnyer.pay.constant; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description : + */ +public class AliPayConstant { + +// /** +// * 超时时间 +// */ +// public static final String timeoutExpress = "30m"; + + /*** + * 接口地址 + */ +// public static final String aliUrl = "https://openapi.alipay.com/gateway.do"; + + /** + * 沙箱地址 + */ + public static final String aliUrl = "https://openapi.alipaydev.com/gateway.do"; +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/DYPayConstants.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/DYPayConstants.java new file mode 100644 index 0000000..86a7259 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/DYPayConstants.java @@ -0,0 +1,31 @@ +package com.bnyer.pay.constant; + +/** + * @author :WXC + * @Date :2023/04/23 + * @description :抖音支付常量池 + */ +public class DYPayConstants { + /** + * 登陆 + */ + public static final String CODE_2_SESSION = "https://developer.toutiao.com/api/apps/v2/jscode2session"; + /** + * 生成预支付单 + */ + public static final String CREATE_ORDER = "https://developer.toutiao.com/api/apps/ecpay/v1/create_order"; + /** + * 查询订单 + */ + public static final String QUERY_ORDER = "https://developer.toutiao.com/api/apps/ecpay/v1/query_order"; + /** + * 分账 + */ + public static final String SETTLE = "https://developer.toutiao.com/api/apps/ecpay/v1/settle"; + + /** + * 退款 + */ + public static final String CREATE_REFUND = "https://developer.toutiao.com/api/apps/ecpay/v1/create_refund"; +} + diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/KSPayConstants.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/KSPayConstants.java new file mode 100644 index 0000000..1065a8f --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/KSPayConstants.java @@ -0,0 +1,56 @@ +package com.bnyer.pay.constant; + +/** + * @author :WXC + * @Date :2023/04/23 + * @description : 快手支付常量池 + */ +public class KSPayConstants { + /** + * https://mp.kuaishou.com/docs/develop/server/code2Session.html + * code获取openId sessionKey + */ + public static final String CODE_2_SESSION = "https://open.kuaishou.com/oauth2/mp/code2session"; + + /** + * https://mp.kuaishou.com/docs/develop/server/getAccessToken.html + * 接口调用凭证 + */ + public static final String GET_ACCESS_TOKEN = "https://open.kuaishou.com/oauth2/access_token"; + + /** + * https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html + * 预下单接口 + */ + public static final String CREATE_ORDER = "https://open.kuaishou.com/openapi/mp/developer/epay/create_order"; + + /** + * https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html + * 订单查询接口 + */ + public static final String QUERY_ORDER = "https://open.kuaishou.com/openapi/mp/developer/epay/query_order"; + + /** + * 退款 + * https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html#_1-3%E6%94%AF%E4%BB%98%E5%9B%9E%E8%B0%83 + */ + public static final String APPLY_REFUND = "https://open.kuaishou.com/openapi/mp/developer/epay/apply_refund"; + + /** + * 结算 + * https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html#_1-3%E6%94%AF%E4%BB%98%E5%9B%9E%E8%B0%83 + */ + public static final String APPLY_SETTLE = "https://open.kuaishou.com/openapi/mp/developer/epay/settle"; + + /** + * 超时时间 + */ + public static final int expireTime = 1800; + + + /** + * https://mp.kuaishou.com/docs/operate/platformAgreement/epayServiceCharge.html + * 商品类目编号 + */ + public static final int GOODS_TYPE_VIP = 1273; +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/AliPayController.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/AliPayController.java new file mode 100644 index 0000000..82a0b8a --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/AliPayController.java @@ -0,0 +1,48 @@ +package com.bnyer.pay.controller; + +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.pay.design.factory.PayFactory; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description : + */ +@Api(value = "支付宝支付相关接口",tags = "支付宝支付相关接口") +@RestController +@Slf4j +public class AliPayController { + + + @ApiOperation(value="支付宝支付回调") + @PostMapping("/alipayBack") + public String alipayBack(ServletRequest arg0){ + log.info("=======================alipayBack===================="); + HttpServletRequest req = (HttpServletRequest)arg0; + Map inMap = new HashMap<>(); + Map requestParams = req.getParameterMap(); + for (String name : requestParams.keySet()) { + String[] values = requestParams.get(name); + String valueStr = ""; + for (int i = 0; i < values.length; i++) { + valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; + } + //乱码解决 + valueStr = new String(valueStr.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + inMap.put(name, valueStr); + } + System.out.println("====================alipayBack:"+inMap); + return PayFactory.getInstance().getConcreteStrategy(EnumPayType.ALI_PAY.getType()).parsePayNotify(inMap.toString()); + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/DYPayController.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/DYPayController.java new file mode 100644 index 0000000..5f4a950 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/DYPayController.java @@ -0,0 +1,42 @@ +package com.bnyer.pay.controller; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.pay.design.factory.PayFactory; +import com.bnyer.pay.design.strategy.IPayStrategy; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author :WXC + * @Date :2023/04/24 + * @description : + */ +@Api(value = "抖音支付相关接口",tags = "抖音支付相关接口") +@RestController +@Slf4j +public class DYPayController { + + /** + * 抖音支付结果通知 + */ + @ApiOperation(value = "抖音支付结果通知") + @ResponseBody + @PostMapping("/dypayBack") + public JSONObject dyPayNotify(@RequestBody JSONObject object, HttpServletRequest request) { + log.info("抖音支付异步通知开始==============》{}", object); + IPayStrategy payStrategy = PayFactory.getInstance().getConcreteStrategy(EnumPayType.DY_PAY.getType()); + String payNotify = payStrategy.parsePayNotify(JSON.toJSONString(object)); + return JSON.parseObject(payNotify); + } + + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/KSPayController.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/KSPayController.java new file mode 100644 index 0000000..08c477f --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/KSPayController.java @@ -0,0 +1,50 @@ +package com.bnyer.pay.controller; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.pay.design.factory.PayFactory; +import com.bnyer.pay.design.strategy.IPayStrategy; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author :WXC + * @Date :2023/04/24 + * @description : + */ +@Api(value = "快手支付相关接口",tags = "快手支付相关接口") +@RestController +@Slf4j +public class KSPayController { + + /** + * 快手支付结果通知 + */ + @ApiOperation(value = "快手支付结果通知") + @ResponseBody + @PostMapping("/kspayBack") + public JSONObject ksPayNotify(@RequestBody JSONObject object, HttpServletRequest request) { + log.info("快手微信支付异步通知开始==============》{}", object); + log.info("快手微信支付kwaisign==============》{}", request.getHeader("kwaisign")); + String kwaisign = request.getHeader("kwaisign"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("data",object); + jsonObject.put("sign",kwaisign); + String string = JSONObject.toJSONString(jsonObject); + IPayStrategy payStrategy = PayFactory.getInstance().getConcreteStrategy(EnumPayType.KS_PAY.getType()); + String payNotify = payStrategy.parsePayNotify(string); + return JSON.parseObject(payNotify); + } + + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/PayInfoController.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/PayInfoController.java new file mode 100644 index 0000000..a741e41 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/PayInfoController.java @@ -0,0 +1,19 @@ +package com.bnyer.pay.controller; + +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description : + */ +@Api(value = "支付订单相关接口",tags = "支付订单相关接口") +@RestController +@RequestMapping("/payinfo") +@Slf4j +public class PayInfoController { + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/TestController.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/TestController.java deleted file mode 100644 index 710a945..0000000 --- a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/TestController.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bnyer.pay.controller; - -public class TestController { -} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/UnifiedPayController.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/UnifiedPayController.java new file mode 100644 index 0000000..512d799 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/UnifiedPayController.java @@ -0,0 +1,50 @@ +package com.bnyer.pay.controller; + +import com.bnyer.common.core.domain.R; +import com.bnyer.pay.dto.InOrderDto; +import com.bnyer.pay.dto.QueryOrderDto; +import com.bnyer.pay.service.UnifiedPayService; +import com.bnyer.pay.vo.InOrderVo; +import com.bnyer.pay.vo.QueryOrderVo; +import com.bnyer.pay.vo.ThirdInOrderVo; +import io.swagger.annotations.Api; +import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; + +/** + * @author :WXC + * @Date :2023/05/05 + * @description : + */ +@Api(value = "统一支付相关接口",tags = "统一支付相关接口") +@RestController +@RequestMapping("/unified") +@Slf4j +public class UnifiedPayController { + + @Autowired + private UnifiedPayService unifiedPayService; + + @PostMapping("/inOrder") + @Operation(summary = "统一下单,并生成支付订单" , description = "生成支付订单,返回前端支付所需参数") + public R inOrder(@Valid @RequestBody InOrderDto dto, HttpServletRequest request){ + InOrderVo inOrderVo = unifiedPayService.inOrder(dto,request); + return R.ok(inOrderVo); + } + + @PostMapping("/queryOrder") + @Operation(summary = "统一订单查询" , description = "订单查询,查询支付结果") + public R queryOrder(@Valid @RequestBody QueryOrderDto dto){ + QueryOrderVo queryOrderVo = unifiedPayService.queryOrder(dto); + return R.ok(queryOrderVo); + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/WxPayController.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/WxPayController.java new file mode 100644 index 0000000..a09cda5 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/WxPayController.java @@ -0,0 +1,29 @@ +package com.bnyer.pay.controller; + +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.pay.design.factory.PayFactory; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description : + */ +@Api(value = "微信支付相关接口",tags = "微信支付相关接口") +@RestController +@Slf4j +public class WxPayController { + + @ApiOperation(value="微信支付回调") + @PostMapping("/wxpayBack") + public @ResponseBody String wxpayBack(@RequestBody String inXml) { + log.info("====================wxpayBack========="); + return PayFactory.getInstance().getConcreteStrategy(EnumPayType.WX_PAY.getType()).parsePayNotify(inXml); + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/factory/PayFactory.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/factory/PayFactory.java new file mode 100644 index 0000000..9127098 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/factory/PayFactory.java @@ -0,0 +1,36 @@ +package com.bnyer.pay.design.factory; + +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.pay.design.strategy.AliPayStrategy; +import com.bnyer.pay.design.strategy.IPayStrategy; +import com.bnyer.pay.design.strategy.WxPayStrategy; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description :支付工厂 + */ +public class PayFactory { + private static final Map strategyMap = new ImmutableMap.Builder() + .put(EnumPayType.ALI_PAY.getType(),new AliPayStrategy()) + .put(EnumPayType.WX_PAY.getType(),new WxPayStrategy()) + .put(EnumPayType.DY_PAY.getType(),new WxPayStrategy()) + .put(EnumPayType.KS_PAY.getType(),new WxPayStrategy()) + .build(); + + public static class SingletonHolder{ + public static PayFactory payStrategy = new PayFactory(); + } + + public static PayFactory getInstance(){ + return SingletonHolder.payStrategy; + } + + public IPayStrategy getConcreteStrategy(String payType){ + return PayFactory.strategyMap.get(payType); + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/AbstractPayStrategy.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/AbstractPayStrategy.java new file mode 100644 index 0000000..9ade772 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/AbstractPayStrategy.java @@ -0,0 +1,114 @@ +package com.bnyer.pay.design.strategy; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alipay.api.msg.MsgConstants; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bnyer.common.core.constant.RedisKeyConstant; +import com.bnyer.common.core.domain.PayInfo; +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.common.redis.service.RedissonService; +import com.bnyer.pay.dto.PayNotifyCheckDto; +import com.bnyer.pay.mapper.PayInfoMapper; +import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; + +import javax.annotation.Resource; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * @author :WXC + * @Date :2023/04/12 + * @description :提供公共的方法 + */ +@Slf4j +public abstract class AbstractPayStrategy implements IPayStrategy { + + @Resource + private PayInfoMapper payInfoMapper; + + @Resource + private RedissonService redissonService; + + /** + * 校验是否已支付避免重复调用 + * @param checkDto + * @return + */ + public String payNotifyCheck(PayNotifyCheckDto checkDto){ + log.error("回调结果校验开始,支付单号:{}",checkDto.getPayId()); + String resultMsg = ""; + RLock rLock = redissonService.getRLock(RedisKeyConstant.PAY_NOTIFY_LOCK_KEY + checkDto.getPayId()); + try{ + if(rLock.tryLock(2L, 10L, TimeUnit.SECONDS)){ + PayInfo payInfo = payInfoMapper.selectOne(new LambdaQueryWrapper().eq(PayInfo::getPayId, checkDto.getPayId()).eq(PayInfo::getIsShow, "1")); + if (Objects.isNull(payInfo)){ + return buildNotifyCheckResultMsg(checkDto.getPayType(),false,"business fail"); + } + log.info("查询到支付订单信息为:{}", JSON.toJSONString(payInfo)); + //订单中的金额 + String payInfoPayAmount = payInfo.getPayAmount().toString(); + //回调中的金额 + String notifyPayAmount = checkDto.getPayAmount(); + //对账状态 + Integer singleStatus = payInfo.getSingleStatus(); + log.info("回调中的金额:{},订单中的金额:{}",notifyPayAmount,payInfoPayAmount); + if (payInfoPayAmount.equals(notifyPayAmount)){ + if (Objects.nonNull(singleStatus)){ + resultMsg = buildNotifyCheckResultMsg(checkDto.getPayType(),true,checkDto.getMsg()); + } + }else { + resultMsg = buildNotifyCheckResultMsg(checkDto.getPayType(),false,"business fail"); + } + }else { + log.error("获取锁失败,支付单号:{}",checkDto.getPayId()); + resultMsg = buildNotifyCheckResultMsg(checkDto.getPayType(),false,"business fail"); + } + }catch (Exception e){ + e.printStackTrace(); + log.error("回调结果校验失败,支付单号:{}",checkDto.getPayId()); + resultMsg = buildNotifyCheckResultMsg(checkDto.getPayType(),false,"business fail"); + }finally { + rLock.unlock(); + } + return resultMsg; + } + + /** + * 构建回调返回结果信息 + * @param payType 支付方式 + * @param isSuccess 是否构建成功消息 + * @param msg 消息内容 + * @return + */ + public String buildNotifyCheckResultMsg(EnumPayType payType,boolean isSuccess,String msg){ + String result = ""; + if (EnumPayType.WX_PAY == payType){ + result = isSuccess?WxPayNotifyResponse.success(msg):WxPayNotifyResponse.fail(msg); + }else if (EnumPayType.ALI_PAY == payType){ + result = isSuccess?MsgConstants.SUCCESS:MsgConstants.FAIL; + } else if (EnumPayType.KS_PAY == payType) { + JSONObject returnObj = new JSONObject(); + if (isSuccess){ + returnObj.put("result", 1); + }else { + returnObj.put("result", 0); + } + returnObj.put("message_id", msg); + result = JSON.toJSONString(returnObj); + } else if (EnumPayType.DY_PAY == payType) { + JSONObject returnObj = new JSONObject(); + if (isSuccess){ + returnObj.put("err_no", 0); + }else { + returnObj.put("err_no", -1); + } + returnObj.put("err_tips", msg); + result = JSON.toJSONString(returnObj); + } + return result; + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/AliPayStrategy.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/AliPayStrategy.java new file mode 100644 index 0000000..2e9f86f --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/AliPayStrategy.java @@ -0,0 +1,239 @@ +package com.bnyer.pay.design.strategy; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.alipay.api.AlipayClient; +import com.alipay.api.AlipayConstants; +import com.alipay.api.DefaultAlipayClient; +import com.alipay.api.domain.AlipayTradeAppPayModel; +import com.alipay.api.domain.AlipayTradeQueryModel; +import com.alipay.api.internal.util.AlipaySignature; +import com.alipay.api.msg.MsgConstants; +import com.alipay.api.request.AlipayTradeAppPayRequest; +import com.alipay.api.request.AlipayTradeQueryRequest; +import com.alipay.api.response.AlipayTradeAppPayResponse; +import com.alipay.api.response.AlipayTradeQueryResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bnyer.common.core.domain.AlipayConfig; +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.common.core.enums.EnumTimeUnit; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.DateUtils; +import com.bnyer.common.core.utils.SpringUtils; +import com.bnyer.common.core.utils.StringUtils; +import com.bnyer.pay.bo.QueryOrderBo; +import com.bnyer.pay.constant.AliPayConstant; +import com.bnyer.pay.dto.EditPayInfoNotifyDto; +import com.bnyer.pay.dto.PayNotifyCheckDto; +import com.bnyer.pay.bo.UnifiedOrderBo; +import com.bnyer.pay.enums.EnumPayChannel; +import com.bnyer.pay.enums.EnumPayConfigStatus; +import com.bnyer.pay.manager.AliPayManager; +import com.bnyer.pay.mapper.AlipayConfigMapper; +import com.bnyer.pay.service.PayInfoService; +import com.bnyer.pay.vo.ThirdInOrderVo; +import com.bnyer.pay.vo.ThirdQueryOrderVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description :支付宝支付策略 + */ +@Slf4j +@Component +public class AliPayStrategy extends AbstractPayStrategy { + + private static AliPayManager aliPayManager; + + private static PayInfoService payInfoService; + + + @Autowired + public void setUserServer(AliPayManager aliPayManager,PayInfoService payInfoService) { + AliPayStrategy.aliPayManager = aliPayManager; + AliPayStrategy.payInfoService = payInfoService; + } + +// /** +// * 查询支付配置 +// * @return +// */ +// private AlipayConfig getAlipayConfig() { +// List alipayConfigList = alipayConfigMapper.selectList(new LambdaQueryWrapper().eq(AlipayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode())); +// if (CollUtil.isEmpty(alipayConfigList)){ +// throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR); +// } +// AlipayConfig alipayConfig = alipayConfigList.get(0); +// return alipayConfig; +// } + + @Override + public ThirdInOrderVo unifiedOrder(UnifiedOrderBo bo) { + log.info("支付宝支付:统一下单接口调用开始,AliPayStrategy.unifiedOrder bo:{}", JSON.toJSONString(bo)); + AlipayConfig alipayConfig = aliPayManager.getAlipayConfig(); + String appid = alipayConfig.getAppid(); + String alipayPublicKey = alipayConfig.getAlipayPublicKey(); + String appPrivateKey = alipayConfig.getAppPrivateKey(); + String keyType = alipayConfig.getKeyType(); + String backurl = alipayConfig.getBackurl(); + //实例化客户端 + AlipayClient alipayClient = new DefaultAlipayClient(AliPayConstant.aliUrl, appid,appPrivateKey, AlipayConstants.FORMAT_JSON, AlipayConstants.CHARSET_UTF8, alipayPublicKey, keyType); + // 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay + AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); + // SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。 + AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); + model.setSubject(bo.getGoodsSubject()); + model.setBody(bo.getGoodsDesc()); + model.setOutTradeNo(bo.getPayId()); + //直接固定好过期时间比较好 + model.setTimeExpire(DateUtil.formatDateTime(DateUtils.getDateByType(EnumTimeUnit.MINUTE,bo.getCurrDate(),30))); +// model.setTimeoutExpress(AliPayConstant.timeoutExpress); + //元 + model.setTotalAmount(bo.getPayAmount()); + request.setBizModel(model); + request.setNotifyUrl(backurl); + try { + // 这里和普通的接口调用不同,使用的是sdkExecute + AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request); + if(response.isSuccess()){ + ThirdInOrderVo vo = new ThirdInOrderVo(); + ThirdInOrderVo.AliThirdInOrderVo aliThirdInOrderVo = new ThirdInOrderVo.AliThirdInOrderVo(); + aliThirdInOrderVo.setOutStr(response.getBody()); + vo.setAliThirdInOrderVo(aliThirdInOrderVo); + vo.setOutOrderNo(bo.getPayId()); + return vo; + }else{ + throw new ServiceException(response.getMsg()); + } + } catch (Exception e) { + e.printStackTrace(); + log.error("支付宝下单接口调用失败,error:{}",e.getMessage()); + throw new ServiceException(ResponseEnum.PAY_FAILS); + } + } + + @Override + public String parsePayNotify(String params) { + Map inMap = JSON.parseObject(params, new TypeReference>() {}); + log.info("支付宝支付回调开始: AliPayStrategy.parsePayNotify inMap:{}",JSON.toJSONString(inMap)); + try{ + //--------------------------1.解析并拿到参数--------------------- + String appId = inMap.get("app_id"); + //支付订单号 + String tradeNo = inMap.get("trade_no"); + //支付流水号 + String outTradeNo = inMap.get("out_trade_no"); + //交易状态:TRADE_FINISHED,TRADE_SUCCESS,TRADE_CLOSED + String tradeStatus = inMap.get("trade_status"); + //交易付款时间 + String gmtPayment = inMap.get("gmt_payment"); + //交易退款时间 + String gmtRefund = inMap.get("gmt_refund"); + //订单金额(元) + String totalAmount = inMap.get("total_amount"); + //通过appid查询配置的密钥:用于校验签名 +// List alipayConfigList = alipayConfigMapper.selectList(new LambdaQueryWrapper() +// .eq(AlipayConfig::getAppid,appId).eq(AlipayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode())); +// if (CollUtil.isEmpty(alipayConfigList)){ +// log.error(ResponseEnum.PAY_CONFIG_ERROR.getMsg()); +// return MsgConstants.FAIL; +// } + AlipayConfig alipayConfig = aliPayManager.getAlipayConfig(appId); + //-----------------------2.签名校验--------------------------- + String alipayPublicKey = alipayConfig.getAlipayPublicKey(); + boolean flag = AlipaySignature.rsaCheckV1(inMap, alipayPublicKey, AlipayConstants.CHARSET_UTF8,AlipayConstants.SIGN_TYPE_RSA2); + if(!flag){ + log.error("支付宝支付回调:签名校验失败"); + return MsgConstants.FAIL; + } + log.info("支付宝支付回调:签名校验成功"); + //交易完成、成功 + if(tradeStatus.equals("TRADE_FINISHED") || tradeStatus.equals("TRADE_SUCCESS")){ + PayNotifyCheckDto payNotifyCheckDto = new PayNotifyCheckDto(); + payNotifyCheckDto.setPayType(EnumPayType.ALI_PAY); + payNotifyCheckDto.setPayId(outTradeNo); + payNotifyCheckDto.setPayAmount(totalAmount); + payNotifyCheckDto.setMsg("OK"); + String notifyCheckResult = super.payNotifyCheck(payNotifyCheckDto); + if (StringUtils.isNotBlank(notifyCheckResult)){ + return notifyCheckResult; + } + log.info("支付宝支付回调,交易正常:封装参数修改内部支付单信息"); + //时间处理 + gmtPayment = gmtPayment.replaceAll(" ", "").replaceAll("-", "").replaceAll(":", ""); + EditPayInfoNotifyDto editPayInfoNotifyDto = new EditPayInfoNotifyDto(); + editPayInfoNotifyDto.setPayId(outTradeNo); + editPayInfoNotifyDto.setPayTime(gmtPayment); + editPayInfoNotifyDto.setPayAmount(totalAmount); + editPayInfoNotifyDto.setAppId(appId); + editPayInfoNotifyDto.setPayNo(tradeNo); + editPayInfoNotifyDto.setPayChannel(EnumPayChannel.getPayChannelName("alipay",EnumPayType.ALI_PAY)); + payInfoService.editPayInfoNotify(editPayInfoNotifyDto); + }else if(tradeStatus.equals("TRADE_CLOSED")){ + log.info("支付宝支付回调:TRADE_CLOSED 状态不做处理"); + } + }catch (Exception e){ + log.info("支付宝支付回调:处理过程异常,error:{}",e.getMessage()); + return MsgConstants.FAIL; + } + return MsgConstants.SUCCESS; + } + + @Override + public ThirdQueryOrderVo queryOrder(QueryOrderBo bo) { + AlipayConfig alipayConfig = aliPayManager.getAlipayConfig(); + String appPrivateKey = alipayConfig.getAppPrivateKey(); + String alipayPublicKey = alipayConfig.getAlipayPublicKey(); + String signType = alipayConfig.getKeyType(); + String appid = alipayConfig.getAppid(); + //调用支付宝接口查询 + AlipayClient alipayClient = new DefaultAlipayClient(AliPayConstant.aliUrl, appid,appPrivateKey, AlipayConstants.FORMAT_JSON, AlipayConstants.CHARSET_UTF8, alipayPublicKey, signType); + //查询 + AlipayTradeQueryRequest alipayTradeQueryRequest = new AlipayTradeQueryRequest(); + AlipayTradeQueryModel alipayTradeQueryModel = new AlipayTradeQueryModel(); + alipayTradeQueryModel.setOutTradeNo(bo.getPayId()); + alipayTradeQueryRequest.setBizModel(alipayTradeQueryModel); + try { + //通过alipayClient调用API,获得对应的response类 + AlipayTradeQueryResponse response = alipayClient.execute(alipayTradeQueryRequest); + log.info("=================================="); + log.info("支付宝订单查询result{}", response.getBody()); + log.info("=================================="); + // 签名认证了 + if(response.isSuccess()){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String tradeStatus = response.getTradeStatus(); + String totalAmount = response.getTotalAmount(); + String payDate = sdf.format(response.getSendPayDate()); + String tradeNo = response.getTradeNo(); + ThirdQueryOrderVo alipayTradeQueryVo = new ThirdQueryOrderVo(); + alipayTradeQueryVo.setOutOrderNo(bo.getPayId()); + alipayTradeQueryVo.setPayTime(payDate); + alipayTradeQueryVo.setTotalAmount(totalAmount); + alipayTradeQueryVo.setThirdOrderNo(tradeNo); + alipayTradeQueryVo.setPayStatus(tradeStatus); + return alipayTradeQueryVo; + }else { + log.error("支付宝支付:订单查询异常,payId:{},error{}", bo.getPayId(),response.getSubMsg()); + throw new ServiceException(ResponseEnum.ORDER_QUERY_FAILS); + } + } catch (Exception e) { + log.error("支付宝支付:订单查询异常,payId:{},error{}", bo.getPayId(),e.getMessage()); + throw new ServiceException(ResponseEnum.ORDER_QUERY_FAILS); + } + } + + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/DYPayStrategy.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/DYPayStrategy.java new file mode 100644 index 0000000..1e043db --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/DYPayStrategy.java @@ -0,0 +1,289 @@ +package com.bnyer.pay.design.strategy; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bnyer.common.core.domain.DypayConfig; +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.StringUtils; +import com.bnyer.pay.bo.QueryOrderBo; +import com.bnyer.pay.constant.DYPayConstants; +import com.bnyer.pay.constant.KSPayConstants; +import com.bnyer.pay.dto.EditPayInfoNotifyDto; +import com.bnyer.pay.dto.PayNotifyCheckDto; +import com.bnyer.pay.bo.UnifiedOrderBo; +import com.bnyer.pay.enums.EnumPayChannel; +import com.bnyer.pay.enums.EnumPayConfigStatus; +import com.bnyer.pay.mapper.DypayConfigMapper; +import com.bnyer.pay.service.PayInfoService; +import com.bnyer.pay.utils.DYPayUtil; +import com.bnyer.pay.utils.MoneyUtil; +import com.bnyer.pay.utils.PayRestTemplateUtil; +import com.bnyer.pay.vo.ThirdInOrderVo; +import com.bnyer.pay.vo.ThirdQueryOrderVo; +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * @author :WXC + * @Date :2023/04/23 + * @description : + */ +@Slf4j +@Component +public class DYPayStrategy extends AbstractPayStrategy{ + + @Autowired + private DYPayUtil dyPayUtil; + + @Autowired + private PayRestTemplateUtil payRestTemplateUtil; + + @Autowired + private PayInfoService payInfoService; + + @Autowired + private DypayConfigMapper dypayConfigMapper; + + /** + * 查询支付配置 + * @return + */ + private DypayConfig getDypayConfig() { + List dypayConfigList = dypayConfigMapper.selectList(new LambdaQueryWrapper().eq(DypayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode())); + if (CollUtil.isEmpty(dypayConfigList)){ + throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR); + } + DypayConfig dypayConfig = dypayConfigList.get(0); + return dypayConfig; + } + + @Override + public ThirdInOrderVo unifiedOrder(UnifiedOrderBo bo) { + String result = ""; + try { + DypayConfig dypayConfig = getDypayConfig(); + String appId = dypayConfig.getAppid(); + String backurl = dypayConfig.getBackurl(); + String salt = dypayConfig.getSalt(); + //加签验签的参数需要排序 + Map params = new TreeMap<>(); + //小程序APPID + params.put("app_id",appId); + //开发者侧的订单号。需保证同一小程序下不可重复 + params.put("out_order_no", bo.getPayId()); + //支付价格。单位为[分],取值范围:[1,10000000000] 100元 = 100*100 分 + params.put("total_amount", BaseWxPayRequest.yuanToFen(bo.getPayAmount())); + //商品描述。 + params.put("subject", bo.getGoodsSubject()); + //商品详情 + params.put("body", bo.getGoodsDesc()); + //订单过期时间(秒) 5min-2day + params.put("valid_time", 1800); + //开发者自定义字段,回调原样回传。超过最大长度会被截断 +// params.put("cp_extra", "xx平台充值"); + //通知地址 + params.put("notify_url", backurl); + //签名,详见https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/ecpay/TE + String sign = dyPayUtil.getSign(params,salt); + params.put("sign", sign); + + //以JSON格式拼好以下参数发送请求 + JSONObject payJson = new JSONObject(); + payJson.put("app_id", appId); + payJson.put("out_order_no", bo.getPayId()); + //此处需要传入一个数值类型,string会报错。。 + payJson.put("total_amount", BaseWxPayRequest.yuanToFen(bo.getPayAmount())); + payJson.put("subject",bo.getGoodsSubject()); + payJson.put("body", bo.getGoodsDesc()); + payJson.put("valid_time", 1800); + payJson.put("sign", sign); + //payJson.put("cp_extra", "开发者自定义字段"); + payJson.put("notify_url",backurl); + log.info("请求参数{}", payJson); + //预下单接口 + result = payRestTemplateUtil.dyPostRequest(payJson, DYPayConstants.CREATE_ORDER); + } catch (Exception e) { + e.printStackTrace(); + log.error("抖音支付:支付异常,payId:{},error{}", bo.getPayId(), e.getMessage()); + throw new ServiceException(ResponseEnum.PAY_FAILS); + } + log.info("=================================="); + log.info("抖音预下单result{}", result); + log.info("=================================="); + if (StringUtils.isBlank(result)){ + log.error("抖音支付:统一下单接口调用失败,payId:{},error{}", bo.getPayId(), "请求结果返回为空"); + throw new ServiceException(ResponseEnum.PAY_FAILS); + } + JSONObject jsonObject = JSONObject.parseObject(result); + String errNo = jsonObject.getString("err_no"); + if (!"0".equals(errNo)) { + log.error("抖音支付:统一下单接口调用失败,payId:{},error{}", bo.getPayId(), jsonObject.getString("error_msg")); + throw new ServiceException(ResponseEnum.PAY_FAILS); + } + JSONObject data = jsonObject.getJSONObject("data"); + String orderId = data.getString("order_id"); + String orderToken = data.getString("order_token"); + if (StringUtils.isBlank(orderId) || StringUtils.isBlank(orderToken)){ + log.error("抖音支付:统一下单接口调用失败,payId:{},result:{},error{}", bo.getPayId(),JSON.toJSONString(data), "返回信息为空"); + throw new ServiceException(ResponseEnum.PAY_FAILS); + } + //保存预下单信息 + ThirdInOrderVo thirdInOrderVo = new ThirdInOrderVo(); + ThirdInOrderVo.DyThirdInOrderVo dyThirdInOrderVo = new ThirdInOrderVo.DyThirdInOrderVo(); + thirdInOrderVo.setOutOrderNo(bo.getPayId()); + //把order_no和order_info_token返回前端用于调起收银台 + dyThirdInOrderVo.setOrderId(orderId); + dyThirdInOrderVo.setOrderToken(orderToken); + thirdInOrderVo.setDyThirdInOrderVo(dyThirdInOrderVo); + return thirdInOrderVo; + } + + @Override + public String parsePayNotify(String params) { + DypayConfig dypayConfig = getDypayConfig(); + String appId = dypayConfig.getAppid(); + String token = dypayConfig.getToken(); + JSONObject object = JSONObject.parseObject(params); + //随机数 + String nonce = object.getString("nonce"); + //时间戳 + Integer timestamp = object.getInteger("timestamp"); + //签名 + String msgSignature = object.getString("msg_signature"); + //订单信息的json字符串 + String message = object.getString("msg"); + JSONObject msgJsonObj = JSON.parseObject(message); + //校验回调签名 + String signMessage = dyPayUtil.getCallbackSignature(timestamp, nonce, message,token); + //签名校验 + if (msgSignature.equals(signMessage)) { + //固定值SUCCESS + String status = msgJsonObj.getString("status"); + //开发者侧的订单号 + String outOrderNo = msgJsonObj.getString("cp_orderno"); + //支付渠道: 1-微信支付,2-支付宝支付,10-抖音支付 + String payChannel = msgJsonObj.getString("way"); + //支付金额,单位为分 + Integer totalAmount = msgJsonObj.getInteger("total_amount"); + //支付渠道侧PC单号,支付页面可见(微信支付宝侧的订单号) + String paymentOrderNo = msgJsonObj.getString("payment_order_no"); + //抖音侧订单号 + String dyOrderId = msgJsonObj.getString("order_id"); + //这里无论回调失败还是成功,都需要都各个业务层去处理相关逻辑 + if("success".equals(status)){ + //处理业务 + PayNotifyCheckDto payNotifyCheckDto = new PayNotifyCheckDto(); + payNotifyCheckDto.setPayType(EnumPayType.DY_PAY); + payNotifyCheckDto.setPayAmount(BaseWxPayResult.fenToYuan(totalAmount)); + payNotifyCheckDto.setPayId(outOrderNo); + payNotifyCheckDto.setMsg("success"); + String notifyCheck = super.payNotifyCheck(payNotifyCheckDto); + if (StringUtils.isNotBlank(notifyCheck)){ + return notifyCheck; + } + log.info("抖音支付回调,交易正常:封装参数修改内部支付单信息"); + EditPayInfoNotifyDto editPayInfoNotifyDto = new EditPayInfoNotifyDto(); + editPayInfoNotifyDto.setPayAmount(MoneyUtil.fenToYuan(String.valueOf(totalAmount))); + editPayInfoNotifyDto.setPayTime(DateUtil.formatDateTime(new Date())); + editPayInfoNotifyDto.setAppId(appId); + editPayInfoNotifyDto.setPayId(outOrderNo); + editPayInfoNotifyDto.setPayNo(dyOrderId); + editPayInfoNotifyDto.setTradeNo(paymentOrderNo); + editPayInfoNotifyDto.setPayChannel(EnumPayChannel.getPayChannelName(payChannel,EnumPayType.DY_PAY)); + payInfoService.editPayInfoNotify(editPayInfoNotifyDto); + //正确处理后返回以下内容格式通知小程序平台不再持续回调 + return super.buildNotifyCheckResultMsg(EnumPayType.DY_PAY,true,"success"); + }else { + log.info("抖音支付回调不是支付成功不做处理:支付状态:{},payId:{}",status,outOrderNo); + } + } else { + log.info("抖音支付回调签名校验失败,payId:{}",msgJsonObj.getString("cp_orderno")); + } + return super.buildNotifyCheckResultMsg(EnumPayType.DY_PAY,true,"business fail"); + } + + @Override + public ThirdQueryOrderVo queryOrder(QueryOrderBo bo) { + DypayConfig dypayConfig = getDypayConfig(); + String appId = dypayConfig.getAppid(); + String salt = dypayConfig.getSalt(); + String result = ""; + try { + //加签验签的参数需要排序 + Map params = new TreeMap<>(); + //小程序APPID + params.put("app_id", appId); + //开发者侧的订单号。需保证同一小程序下不可重复 + params.put("out_order_no", bo.getPayId()); + //签名 + String sign = dyPayUtil.getSign(params, salt); + JSONObject queryOrderJson = new JSONObject(); + queryOrderJson.put("app_id", appId); + queryOrderJson.put("out_order_no", bo.getPayId()); + queryOrderJson.put("sign", sign); + //发起请求 + result = payRestTemplateUtil.dyPostRequest(queryOrderJson, DYPayConstants.QUERY_ORDER); + }catch (Exception e){ + e.printStackTrace(); + log.error("抖音支付:订单查询异常,payId:{},error{}", bo.getPayId(), e.getMessage()); + throw new ServiceException(ResponseEnum.ORDER_QUERY_FAILS); + } + log.info("=================================="); + log.info("抖音订单查询result{}", result); + log.info("=================================="); + if (StringUtils.isBlank(result)) { + log.error("抖音支付:订单查询异常,payId:{},error{}", bo.getPayId(), "请求结果返回为空"); + throw new ServiceException(ResponseEnum.ORDER_QUERY_FAILS); + } + JSONObject jsonObject = JSONObject.parseObject(result); + String errNo = jsonObject.getString("err_no"); + if (!"0".equals(errNo)) { + log.error("抖音支付:订单查询异常,payId:{},error{}", bo.getPayId(), jsonObject.getString("err_tips")); + throw new ServiceException(ResponseEnum.ORDER_QUERY_FAILS); + } + //开发者下单单号 + String outOrderNo = jsonObject.getString("out_order_no"); + //抖音侧的订单号 + String orderId = jsonObject.getString("order_id"); + //详细信息 + JSONObject data = jsonObject.getJSONObject("payment_info"); + //支付状态:SUCCESS:成功 TIMEOUT:超时未支付 PROCESSING:处理中 FAIL:失败 + String payStatus = data.getString("order_status"); + //预下单用户支付金额 + Integer totalAmount = data.getInteger("total_amount"); + //预下单用户支付金额 + String payTime = data.getString("pay_time"); + //支付渠道, 1-微信支付,2-支付宝支付,10-抖音支付 + Integer payChannel = data.getInteger("way"); + //开发者回传的订单同步状态 + String orderStatus = data.getString("order_status"); + //抖音小程序平台订单号 + String openId = data.getString("open_id"); + //支付渠道侧的支付单号 + String channelNo = data.getString("channel_no"); + ThirdQueryOrderVo thirdQueryOrderVo = new ThirdQueryOrderVo(); + thirdQueryOrderVo.setOutOrderNo(outOrderNo); + thirdQueryOrderVo.setPayStatus(payStatus); + thirdQueryOrderVo.setTotalAmount(MoneyUtil.fenToYuan(String.valueOf(totalAmount))); + thirdQueryOrderVo.setPayTime(payTime); + thirdQueryOrderVo.setDyPayChannel(payChannel); + thirdQueryOrderVo.setThirdOrderNo(orderId); + thirdQueryOrderVo.setOrderStatus(orderStatus); + thirdQueryOrderVo.setOpenId(openId); + thirdQueryOrderVo.setChannelNo(channelNo); + return thirdQueryOrderVo; + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/IPayStrategy.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/IPayStrategy.java new file mode 100644 index 0000000..c0c46e3 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/IPayStrategy.java @@ -0,0 +1,45 @@ +package com.bnyer.pay.design.strategy; + +import com.bnyer.pay.bo.QueryOrderBo; +import com.bnyer.pay.bo.UnifiedOrderBo; +import com.bnyer.pay.vo.ThirdInOrderVo; +import com.bnyer.pay.vo.ThirdQueryOrderVo; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description :支付策略 + */ +public interface IPayStrategy { + /** + * 统一下单 + * @param bo + * @return + */ + ThirdInOrderVo unifiedOrder(UnifiedOrderBo bo); + + /** + * 支付回调处理 + * 满足支付成功需要几个条件: + * 1.回调到pay服务时要保证修改支付状态:payStatus为1001、对账状态:singStatus为1001 + * 2.pay服务处理完以后发送消息到order服务修改order表订单状态:orderStatus为1、支付状态:payStatus为1001 + * + * 如果pay服务发送消息失败可利用第三方支付回调的重试机制多次发送,如果order服务消费失败可利用mq重试机制重试消费,重试多次还是失败就人工处理该订单 + * @param params + * @return + */ + String parsePayNotify(String params); + + /** + * 订单查询 + */ + ThirdQueryOrderVo queryOrder(QueryOrderBo bo); + //===========待完成================ + // TODO: 2023/04/03 订单查询 + + // TODO: 2023/04/03 退款 +// void refund(); + + // TODO: 2023/04/03 退款查询 +// void refundQuery(); +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/KSPayStrategy.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/KSPayStrategy.java new file mode 100644 index 0000000..3165f95 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/KSPayStrategy.java @@ -0,0 +1,292 @@ +package com.bnyer.pay.design.strategy; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bnyer.common.core.context.SecurityContextHolder; +import com.bnyer.common.core.domain.KspayConfig; +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.StringUtils; +import com.bnyer.pay.bo.QueryOrderBo; +import com.bnyer.pay.constant.KSPayConstants; +import com.bnyer.pay.dto.EditPayInfoNotifyDto; +import com.bnyer.pay.dto.PayNotifyCheckDto; +import com.bnyer.pay.bo.UnifiedOrderBo; +import com.bnyer.pay.enums.EnumPayChannel; +import com.bnyer.pay.enums.EnumPayConfigStatus; +import com.bnyer.pay.mapper.KspayConfigMapper; +import com.bnyer.pay.service.PayInfoService; +import com.bnyer.pay.utils.KSPayUtil; +import com.bnyer.pay.utils.MoneyUtil; +import com.bnyer.pay.utils.PayRestTemplateUtil; +import com.bnyer.pay.vo.ThirdInOrderVo; +import com.bnyer.pay.vo.ThirdQueryOrderVo; +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * @author :WXC + * @Date :2023/04/23 + * @description : + */ +@Slf4j +@Component +public class KSPayStrategy extends AbstractPayStrategy{ + + @Autowired + private KSPayUtil ksPayUtil; + + @Autowired + private PayRestTemplateUtil payRestTemplateUtil; + + @Autowired + private PayInfoService payInfoService; + + @Autowired + private KspayConfigMapper kspayConfigMapper; + + /** + * 查询支付配置 + * @return + */ + private KspayConfig getKspayConfig() { + List kspayConfigList = kspayConfigMapper.selectList(new LambdaQueryWrapper().eq(KspayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode())); + if (CollUtil.isEmpty(kspayConfigList)){ + throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR); + } + KspayConfig kspayConfig = kspayConfigList.get(0); + return kspayConfig; + } + + @Override + public ThirdInOrderVo unifiedOrder(UnifiedOrderBo bo) { + String result = ""; + try { + KspayConfig kspayConfig = getKspayConfig(); + String openId = SecurityContextHolder.getFhOpenId(); + String appId = kspayConfig.getAppid(); + String backurl = kspayConfig.getBackurl(); + String secret = kspayConfig.getSecret(); + //加签验签的参数需要排序 + Map params = new TreeMap<>(); + //小程序APPID + params.put("app_id", appId); + //开发者侧的订单号。需保证同一小程序下不可重复 + params.put("out_order_no", bo.getPayId()); + //快手用户在当前小程序的open_id,可通过login操作获取 + params.put("open_id", openId); + //用户支付金额,单位为[分]。不允许传非整数的数值。 + params.put("total_amount", BaseWxPayRequest.yuanToFen(bo.getPayAmount())); + //商品描述。 + params.put("subject", bo.getGoodsSubject()); + //商品详情 + params.put("detail", bo.getGoodsDesc()); + //商品类型,不同商品类目的编号见 担保支付商品类目编号 + params.put("type", bo.getGoodsType()); + //订单过期时间,单位秒,300s - 172800s + params.put("expire_time", KSPayConstants.expireTime); + //开发者自定义字段,回调原样回传。超过最大长度会被截断 +// params.put("attach", "支付测试"); + //通知地址 + params.put("notify_url", backurl); + String sign = ksPayUtil.calcSign(params, secret); + + JSONObject payJson = new JSONObject(); + payJson.put("out_order_no", bo.getPayId()); + payJson.put("open_id", openId); + payJson.put("total_amount", BaseWxPayRequest.yuanToFen(bo.getPayAmount())); + payJson.put("subject", bo.getGoodsSubject()); + payJson.put("detail", bo.getGoodsDesc()); + payJson.put("type", bo.getGoodsType()); + payJson.put("expire_time", 1800); + payJson.put("sign", sign); +// payJson.put("attach", "测试支付"); + payJson.put("notify_url", bo.getCurrDate()); + log.info("请求参数{}", payJson); + //预下单接口 + String ksPayAccessToken = payRestTemplateUtil.ksPostRequestUrlencoded(kspayConfig); + result = payRestTemplateUtil.ksPostRequestJson(payJson, KSPayConstants.CREATE_ORDER, appId, ksPayAccessToken); + log.info("=================================="); + log.info("快手预下单result{}", result); + log.info("=================================="); + } catch (Exception e) { + e.printStackTrace(); + log.error("快手支付:支付异常,payId:{},error{}", bo.getPayId(), e.getMessage()); + throw new ServiceException(ResponseEnum.PAY_FAILS); + } + if (StringUtils.isBlank(result)){ + log.error("快手支付:统一下单接口调用失败,payId:{},error{}", bo.getPayId(), "请求结果返回为空"); + throw new ServiceException(ResponseEnum.PAY_FAILS); + } + JSONObject jsonObject = JSONObject.parseObject(result); + String resultCode = jsonObject.getString("result"); + if (!"1".equals(resultCode)) { + log.error("快手支付:统一下单接口调用失败,payId:{},error{}", bo.getPayId(), jsonObject.getString("error_msg")); + throw new ServiceException(ResponseEnum.PAY_FAILS); + } + JSONObject data = jsonObject.getJSONObject("order_info"); + String orderNo = data.getString("order_no"); + String orderToken = data.getString("order_info_token"); + if (StringUtils.isBlank(orderNo) || StringUtils.isBlank(orderToken)) { + log.error("快手支付:统一下单接口调用失败,payId:{},result:{},error{}", bo.getPayId(),JSON.toJSONString(data), "返回信息为空"); + throw new ServiceException(ResponseEnum.PAY_FAILS); + } + //保存预下单信息 + ThirdInOrderVo thirdInOrderVo = new ThirdInOrderVo(); + ThirdInOrderVo.KsThirdInOrderVo ksThirdInOrderVo = new ThirdInOrderVo.KsThirdInOrderVo(); + thirdInOrderVo.setOutOrderNo(bo.getPayId()); + //把order_no和order_info_token返回前端用于调起收银台 + ksThirdInOrderVo.setOrderNo(orderNo); + ksThirdInOrderVo.setOrderToken(orderToken); + thirdInOrderVo.setKsThirdInOrderVo(ksThirdInOrderVo); + return thirdInOrderVo; + } + + @Override + public String parsePayNotify(String params) { + KspayConfig kspayConfig = getKspayConfig(); + String appId = kspayConfig.getAppid(); + String secret = kspayConfig.getSecret(); + JSONObject jsonObject = JSONObject.parseObject(params); + JSONObject object = jsonObject.getJSONObject("data"); + String kwaisign = jsonObject.getString("sign"); + String jsonString = JSONObject.toJSONString(object, SerializerFeature.WRITE_MAP_NULL_FEATURES, SerializerFeature.QuoteFieldNames); + log.info("快手支付:统一回调处理,params:{}",jsonString); + if (StringUtils.isNotBlank(kwaisign)) { + jsonString = jsonString + secret; + //签名校验 + if (kwaisign.equals(DigestUtils.md5Hex(jsonString))) { + //当前回调消息的唯一ID,在同一个消息多次通知时,保持一致。 + String messageId = object.getString("message_id"); + JSONObject data = object.getJSONObject("data"); + //支付渠道。取值:UNKNOWN - 未知|WECHAT-微信|ALIPAY-支付宝 + String channel = data.getString("channel"); + //订单支付状态。 取值: PROCESSING-处理中|SUCCESS-成功|FAILED-失败 + String status = data.getString("status"); + //订单金额 + Integer orderAmount = data.getInteger("order_amount"); + //用户侧支付页交易单号 + String tradeNo = data.getString("trade_no"); + //商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一 + String outOrderNo = data.getString("out_order_no"); + //快手小程序平台订单号。 + String ksOrderNo = data.getString("ks_order_no"); + //回调支付成功 + if ("SUCCESS".equals(status)) { + //处理业务 + PayNotifyCheckDto payNotifyCheckDto = new PayNotifyCheckDto(); + payNotifyCheckDto.setPayType(EnumPayType.KS_PAY); + payNotifyCheckDto.setPayAmount(MoneyUtil.fenToYuan(String.valueOf(orderAmount))); + payNotifyCheckDto.setPayId(outOrderNo); + payNotifyCheckDto.setMsg(messageId); + String notifyCheck = super.payNotifyCheck(payNotifyCheckDto); + if (StringUtils.isNotBlank(notifyCheck)){ + return notifyCheck; + } + log.info("快手支付回调,交易正常:封装参数修改内部支付单信息"); + EditPayInfoNotifyDto editPayInfoNotifyDto = new EditPayInfoNotifyDto(); + editPayInfoNotifyDto.setPayAmount(MoneyUtil.fenToYuan(String.valueOf(orderAmount))); + editPayInfoNotifyDto.setPayTime(DateUtil.formatDateTime(new Date())); + editPayInfoNotifyDto.setAppId(appId); + editPayInfoNotifyDto.setPayId(outOrderNo); + editPayInfoNotifyDto.setPayNo(ksOrderNo); + editPayInfoNotifyDto.setTradeNo(tradeNo); + editPayInfoNotifyDto.setPayChannel(EnumPayChannel.getPayChannelName(channel,EnumPayType.KS_PAY)); + payInfoService.editPayInfoNotify(editPayInfoNotifyDto); + //正确处理后返回以下内容格式通知小程序平台不再持续回调 + return super.buildNotifyCheckResultMsg(EnumPayType.KS_PAY,true,messageId); + }else { + log.info("快手支付回调不是支付成功不做处理:支付状态:{},payId:{}",status,object.getString("trade_no")); + } + } else { + log.info("快手支付回调签名校验失败,payId:{}",object.getString("trade_no")); + } + } else { + log.info("快手支付回调参数丢失,payId:{}",object.getString("trade_no")); + } + return super.buildNotifyCheckResultMsg(EnumPayType.KS_PAY,true,"business fail"); + } + + @Override + public ThirdQueryOrderVo queryOrder(QueryOrderBo bo) { + KspayConfig kspayConfig = getKspayConfig(); + String appId = kspayConfig.getAppid(); + String secret = kspayConfig.getSecret(); + String result = ""; + try { + //accessToken + String ksPayAccessToken = payRestTemplateUtil.ksPostRequestUrlencoded(kspayConfig); + //加签验签的参数需要排序 + Map params = new TreeMap<>(); + //小程序APPID + params.put("app_id", appId); + //开发者侧的订单号。需保证同一小程序下不可重复 + params.put("out_order_no", bo.getPayId()); + //签名 + String sign = ksPayUtil.calcSign(params, secret); + JSONObject queryOrderJson = new JSONObject(); + queryOrderJson.put("out_order_no", bo.getPayId()); + queryOrderJson.put("sign", sign); + //发起请求 + result = payRestTemplateUtil.ksPostRequestJson(queryOrderJson, KSPayConstants.QUERY_ORDER, appId, ksPayAccessToken); + }catch (Exception e){ + e.printStackTrace(); + log.error("快手支付:订单查询异常,payId:{},error{}", bo.getPayId(), e.getMessage()); + throw new ServiceException(ResponseEnum.ORDER_QUERY_FAILS); + } + log.info("=================================="); + log.info("快手订单查询result{}", result); + log.info("=================================="); + if (StringUtils.isBlank(result)) { + log.error("快手支付:订单查询异常,payId:{},error{}", bo.getPayId(), "请求结果返回为空"); + throw new ServiceException(ResponseEnum.ORDER_QUERY_FAILS); + } + JSONObject jsonObject = JSONObject.parseObject(result); + String resultCode = jsonObject.getString("result"); + if (!"1".equals(resultCode)) { + log.error("快手支付:订单查询异常,payId:{},error{}", bo.getPayId(), jsonObject.getString("error_msg")); + throw new ServiceException(ResponseEnum.ORDER_QUERY_FAILS); + } + JSONObject data = jsonObject.getJSONObject("payment_info"); + //开发者下单单号 + String outOrderNo = data.getString("out_order_no"); + //支付状态。 取值: PROCESSING-处理中|SUCCESS-成功|FAILED-失败 TIMEOUT-超时 + String payStatus = data.getString("pay_status"); + //预下单用户支付金额 + String totalAmount = data.getString("total_amount"); + //预下单用户支付金额 + String payTime = data.getString("pay_time"); + //支付渠道。取值:UNKNOWN - 未知|WECHAT-微信 |ALIPAY-支付宝。(注:如果用户还未支付,这里返回的是UNKNOWN.) + String payChannel = data.getString("pay_channel"); + //快手小程序平台订单号 + String ksOrderNo = data.getString("ks_order_no"); + //开发者回传的订单同步状态 + String orderStatus = data.getString("order_status"); + //快手小程序平台订单号 + String openId = data.getString("open_id"); + ThirdQueryOrderVo thirdQueryOrderVo = new ThirdQueryOrderVo(); + thirdQueryOrderVo.setOutOrderNo(outOrderNo); + thirdQueryOrderVo.setPayStatus(payStatus); + thirdQueryOrderVo.setTotalAmount(MoneyUtil.fenToYuan(String.valueOf(totalAmount))); + thirdQueryOrderVo.setPayTime(payTime); + thirdQueryOrderVo.setKsPayChannel(payChannel); + thirdQueryOrderVo.setThirdOrderNo(ksOrderNo); + thirdQueryOrderVo.setOrderStatus(orderStatus); + thirdQueryOrderVo.setOpenId(openId); + return thirdQueryOrderVo; + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/WxPayStrategy.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/WxPayStrategy.java new file mode 100644 index 0000000..341b242 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/WxPayStrategy.java @@ -0,0 +1,199 @@ +package com.bnyer.pay.design.strategy; + +import com.alibaba.fastjson.JSON; +import com.bnyer.common.core.context.SecurityContextHolder; +import com.bnyer.common.core.domain.WxpayConfig; +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.StringUtils; +import com.bnyer.pay.bo.QueryOrderBo; +import com.bnyer.pay.bo.UnifiedOrderBo; +import com.bnyer.pay.dto.EditPayInfoNotifyDto; +import com.bnyer.pay.dto.PayNotifyCheckDto; +import com.bnyer.pay.enums.EnumPayChannel; +import com.bnyer.pay.enums.EnumTradeType; +import com.bnyer.pay.enums.EnumWxPayStatus; +import com.bnyer.pay.manager.WxPayManager; +import com.bnyer.pay.service.PayInfoService; +import com.bnyer.pay.utils.WXPayUtil; +import com.bnyer.pay.vo.ThirdInOrderVo; +import com.bnyer.pay.vo.ThirdQueryOrderVo; +import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryRequest; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult; +import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result; +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description :微信支付策略 + */ +@Slf4j +@Component +public class WxPayStrategy extends AbstractPayStrategy { + + @Autowired + private WxPayManager wxPayManager; + + @Autowired + private PayInfoService payInfoService; + + @Override + public ThirdInOrderVo unifiedOrder(UnifiedOrderBo bo) { + log.info("微信支付:统一下单接口调用开始,WxPayStrategy.unifiedOrder bo:{}",JSON.toJSONString(bo)); + ThirdInOrderVo thirdInOrderVo = null; + EnumTradeType payByTradeType = EnumTradeType.getPayByTradeType(bo.getTradeType()); + switch (payByTradeType){ + case JSAPI_MINI: + thirdInOrderVo = jsApiPay(bo); + break; + case JSAPI_PUBLIC: + break; + default: + throw new ServiceException("TradeType未匹配上对应支付方式"); + } + return thirdInOrderVo; + } + + /** + * JSAPI支付 + * @param bo + * @return + */ + private ThirdInOrderVo jsApiPay(UnifiedOrderBo bo) { + WxpayConfig wxPayConfig = wxPayManager.getWxPayConfigByTradeType(bo.getTradeType()); + WxPayService wxPayService = wxPayManager.getWxPayService(wxPayConfig); + String openId = SecurityContextHolder.getWechatOpenId(); + try { + WxPayUnifiedOrderV3Request orderRequest = new WxPayUnifiedOrderV3Request(); + WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer(); + payer.setOpenid(openId); + orderRequest.setPayer(payer); + orderRequest.setDescription(bo.getGoodsDesc()); + orderRequest.setOutTradeNo(bo.getPayId()); + WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount(); + amount.setTotal(BaseWxPayRequest.yuanToFen(bo.getPayAmount())); + orderRequest.setAmount(amount); + //调用微信支付接口 + WxPayUnifiedOrderV3Result wxPayUnifiedOrderV3Result = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, orderRequest); + WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = wxPayUnifiedOrderV3Result.getPayInfo(TradeTypeEnum.JSAPI, wxPayConfig.getAppid(), wxPayConfig.getMchid(), wxPayService.getConfig().getPrivateKey()); + //返回数据 + ThirdInOrderVo thirdInOrderVo = new ThirdInOrderVo(); + ThirdInOrderVo.WxThirdInOrderVo wxThirdInOrderVo = new ThirdInOrderVo.WxThirdInOrderVo(); + wxThirdInOrderVo.setPackageValue(jsapiResult.getPackageValue()); + wxThirdInOrderVo.setTimeStamp(jsapiResult.getTimeStamp()); + wxThirdInOrderVo.setNonceStr(jsapiResult.getNonceStr()); + wxThirdInOrderVo.setPaySign(jsapiResult.getPaySign()); + thirdInOrderVo.setOutOrderNo(bo.getPayId()); + thirdInOrderVo.setAppId(jsapiResult.getAppId()); + return thirdInOrderVo; + } catch (WxPayException e) { + log.error("微信支付:统一下单接口调用失败,payId:{},error{}", bo.getPayId(), e.getMessage()); + throw new ServiceException(ResponseEnum.PAY_FAILS); + } + } + + /** + * 支付回调处理 + * @param params + * @return + */ + @Override + public String parsePayNotify(String params) { + log.info("微信支付回调开始: WxPayStrategy.parsePayNotify params:{}",params); + Map inMap = null; + try { + inMap = WXPayUtil.xmlToMap(params); + } catch (Exception e) { + log.error("微信支付回调结果异常,异常原因{}", e.getMessage()); + return WxPayNotifyResponse.fail(e.getMessage()); + } + log.info("微信支付回调开始: WxPayStrategy.parsePayNotify inMap:{}",JSON.toJSONString(inMap)); + WxpayConfig wxpayConfig = wxPayManager.getWxPayConfigByAppId(inMap.get("appid")); + WxPayService wxPayService = wxPayManager.getWxPayService(wxpayConfig); + try { + //解析支付结果通知 + WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = wxPayService.parseOrderNotifyV3Result(params, null); + WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult(); + //appid + String appid = result.getAppid(); + //支付订单号 + String outTradeNo = result.getOutTradeNo(); + //微信支付流水号 + String transactionId = result.getTransactionId(); + //支付金额 + String fenToYuan = BaseWxPayResult.fenToYuan(result.getAmount().getPayerTotal()); + //支付时间 + String timeEnd = result.getSuccessTime(); + //支付状态 + String tradeState = result.getTradeState(); + if (!EnumWxPayStatus.SUCCESS.getStatus().equals(tradeState)){ + return super.buildNotifyCheckResultMsg(EnumPayType.WX_PAY,false,"交易失败"); + } + //校验是否已支付避免重复调用 + PayNotifyCheckDto payNotifyCheckDto = new PayNotifyCheckDto(); + payNotifyCheckDto.setPayType(EnumPayType.WX_PAY); + payNotifyCheckDto.setPayId(outTradeNo); + payNotifyCheckDto.setPayAmount(fenToYuan); + payNotifyCheckDto.setMsg("OK"); + String notifyCheck = super.payNotifyCheck(payNotifyCheckDto); + if (StringUtils.isNotBlank(notifyCheck)){ + return notifyCheck; + } + log.info("微信支付回调,交易正常:封装参数修改内部支付单信息"); + EditPayInfoNotifyDto editPayInfoNotifyDto = new EditPayInfoNotifyDto(); + editPayInfoNotifyDto.setPayAmount(fenToYuan); + editPayInfoNotifyDto.setPayTime(timeEnd); + editPayInfoNotifyDto.setAppId(appid); + editPayInfoNotifyDto.setPayId(outTradeNo); + editPayInfoNotifyDto.setPayNo(transactionId); + editPayInfoNotifyDto.setPayChannel(EnumPayChannel.getPayChannelName("wxpay",EnumPayType.WX_PAY)); + payInfoService.editPayInfoNotify(editPayInfoNotifyDto); + return super.buildNotifyCheckResultMsg(EnumPayType.WX_PAY,true,"OK"); + } catch (Exception e) { + log.error("微信支付回调结果异常,异常原因{}", e.getMessage()); + return super.buildNotifyCheckResultMsg(EnumPayType.WX_PAY,false,e.getMessage()); + } + } + + @Override + public ThirdQueryOrderVo queryOrder(QueryOrderBo bo) { + if (StringUtils.isBlank(bo.getTradeType())){ + throw new ServiceException("tradeType is null"); + } + WxpayConfig wxPayConfig = wxPayManager.getWxPayConfigByTradeType(bo.getTradeType()); + WxPayService wxPayService = wxPayManager.getWxPayService(wxPayConfig); + try { + WxPayOrderQueryRequest wxPayOrderQueryRequest = new WxPayOrderQueryRequest(); + wxPayOrderQueryRequest.setOutTradeNo(bo.getPayId()); + WxPayOrderQueryResult wxPayOrderQueryResult = wxPayService.queryOrder(wxPayOrderQueryRequest); + log.info("=================================="); + log.info("微信订单查询result{}", JSON.toJSONString(wxPayOrderQueryResult)); + log.info("=================================="); + ThirdQueryOrderVo thirdQueryOrderVo = new ThirdQueryOrderVo(); + thirdQueryOrderVo.setOutOrderNo(wxPayOrderQueryResult.getOutTradeNo()); + thirdQueryOrderVo.setPayStatus(wxPayOrderQueryResult.getTradeState()); + thirdQueryOrderVo.setOpenId(wxPayOrderQueryResult.getOpenid()); + //处理时间 + thirdQueryOrderVo.setPayTime(wxPayOrderQueryResult.getTimeEnd()); + thirdQueryOrderVo.setThirdOrderNo(wxPayOrderQueryResult.getTransactionId()); + return thirdQueryOrderVo; + } catch (WxPayException e) { + log.error("微信支付:订单查询异常,payId:{},error{}", bo.getPayId(),e.getMessage()); + throw new ServiceException(ResponseEnum.ORDER_QUERY_FAILS); + } + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/AddPayInfoDto.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/AddPayInfoDto.java new file mode 100644 index 0000000..cc6a75d --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/AddPayInfoDto.java @@ -0,0 +1,87 @@ +package com.bnyer.pay.dto; + +import com.baomidou.mybatisplus.annotation.TableField; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author :WXC + * @Date :2023/05/05 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class AddPayInfoDto { + + @ApiModelProperty(value="支付单号(内部生成)") + private String payId; + + @ApiModelProperty(value="业务主订单号:关联内部业务订单表") + private String orderNo; + + @ApiModelProperty(value="支付状态:1000未支付;1001支付成功 ;1002支付失败") + private Integer payStatus; + + @ApiModelProperty(value="单笔对账状态:1001 对账成功") + private Integer singleStatus; + + @ApiModelProperty(value="单笔对账时间") + private Date singleTime; + + @ApiModelProperty(value="支付类型:wxpay/alipay/kspay/dypay") + private String payType; + + @ApiModelProperty(value="支付渠道:wxpay/alipay") + private String payChannel; + + @ApiModelProperty(value = "交易类型:JSAPI等") + private String tradeType; + + @ApiModelProperty(value="支付单号(第三方返回)") + private String payNo; + + @ApiModelProperty(value="用户侧订单号(第三方返回)") + private String tradeNo; + + @ApiModelProperty(value="appid") + private String appid; + + @ApiModelProperty(value="商品标题") + private String goodsSubject; + + @ApiModelProperty(value="商品描述") + private String goodsDesc; + + @ApiModelProperty(value="支付金额,单位元") + private BigDecimal payAmount; + + @ApiModelProperty(value="支付时间") + private Date payTime; + + @ApiModelProperty(value="支付场景:1.会员充值") + private Integer sceneCode; + + @ApiModelProperty(value="用户ip") + private String ip; + + @ApiModelProperty(value="调用第三方下单返回") + private String thirdCode; + + @ApiModelProperty(value="调用第三方下单返回") + private String thirdMsg; + + @ApiModelProperty(value="调用第三方下单返回") + private String thirdNo; + + @ApiModelProperty(value="备注") + private String remark; + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/EditPayInfoNotifyDto.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/EditPayInfoNotifyDto.java new file mode 100644 index 0000000..79af948 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/EditPayInfoNotifyDto.java @@ -0,0 +1,37 @@ +package com.bnyer.pay.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * @author :WXC + * @Date :2023/04/10 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class EditPayInfoNotifyDto { + + //appid + private String appId; + //业务系统支付订单号 + private String payId; + //支付号:第三方 + private String payNo; + //快手用户侧订单号/抖音支付宝微信侧单号:第三方 + private String tradeNo; + //交易付款时间 + private String payTime; + //交易退款时间 + private String refundTime; + //订单金额(元) + private String payAmount; + //支付渠道:wxpay/alipay/dypay + private String payChannel; + + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/EditPayInfoSingleDto.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/EditPayInfoSingleDto.java new file mode 100644 index 0000000..0008067 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/EditPayInfoSingleDto.java @@ -0,0 +1,37 @@ +package com.bnyer.pay.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.ibatis.annotations.Param; + +/** + * @author :WXC + * @Date :2023/04/24 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class EditPayInfoSingleDto { + /** + * 支付单号:商户侧 + */ + private String payId; + /** + * 支付单号:第三方返回 + */ + private String payNo; + /** + * 快手用户侧订单号/抖音支付宝微信侧单号:第三方返回 + */ + private String tradeNo; + /** + * 支付时间 + */ + private String payTime; + /** + * 支付渠道 + */ + private String payChannel; +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/InOrderDto.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/InOrderDto.java new file mode 100644 index 0000000..4691d0c --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/InOrderDto.java @@ -0,0 +1,40 @@ +package com.bnyer.pay.dto; + +import com.bnyer.common.core.annotation.CustomParamsValidation; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author :WXC + * @Date :2023/04/03 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class InOrderDto { + + @NotBlank(message = "业务主订单号不能为空") + @ApiModelProperty(value="业务主订单号:关联内部业务订单表",required = true) + private String orderNo; + + @NotNull(message = "支付场景不能为空") + @ApiModelProperty(value="支付场景:1.会员充值",required = true) + private Integer sceneCode; + + @NotBlank(message = "支付类型不能为空") + @ApiModelProperty(value="支付类型:wxpay/alipay",required = true) + private String payType; + + @ApiModelProperty(value="交易类型 1--JSAPI支付(小程序appId支付)、2--Native支付、3--app支付,4--JSAPI支付(公众号appId支付)5--H5支付,微信支付必填",example = "1") + @CustomParamsValidation(range = "1",message = "交易类型暂时只支持JSAPI") + private String tradeType; + + @ApiModelProperty(value="备注") + private String remark; +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/PayNotifyCheckDto.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/PayNotifyCheckDto.java new file mode 100644 index 0000000..c65a65f --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/PayNotifyCheckDto.java @@ -0,0 +1,37 @@ +package com.bnyer.pay.dto; + +import com.bnyer.common.core.enums.EnumPayType; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * @author :WXC + * @Date :2023/04/13 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class PayNotifyCheckDto { + + /** + * 支付方式 + */ + private EnumPayType payType; + + /** + * 支付单号 + */ + private String payId; + + /** + * 支付金额 + */ + private String payAmount; + + /** + * 消息 + */ + private String msg; +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/QueryOrderDto.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/QueryOrderDto.java new file mode 100644 index 0000000..ec414bc --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/QueryOrderDto.java @@ -0,0 +1,33 @@ +package com.bnyer.pay.dto; + +import com.bnyer.common.core.annotation.CustomParamsValidation; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; + +/** + * @author :WXC + * @Date :2023/05/05 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class QueryOrderDto { + + @NotBlank(message = "支付类型不能为空") + @ApiModelProperty(value="支付类型:wxpay/alipay/kspay/dypay",required = true) + private String payType; + + @ApiModelProperty(value="交易类型 1--JSAPI支付(小程序appId支付)、2--Native支付、3--app支付,4--JSAPI支付(公众号appId支付)5--H5支付,微信支付必填",example = "1") + @CustomParamsValidation(range = "1",message = "交易类型暂时只支持JSAPI") + private String tradeType; + + @NotBlank(message = "支付单号不能为空") + @ApiModelProperty(value="支付单号",required = true) + private String payId; + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumAliPayStatus.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumAliPayStatus.java new file mode 100644 index 0000000..b5e931d --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumAliPayStatus.java @@ -0,0 +1,37 @@ +package com.bnyer.pay.enums; + +import com.bnyer.common.core.enums.EnumPayStatus; +import com.bnyer.common.core.exception.ServiceException; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +/** + * @author :WXC + * @Date :2023/05/05 + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumAliPayStatus { + + WAIT_BUYER_PAY("WAIT_BUYER_PAY",EnumPayStatus.NO_PAY), + TRADE_SUCCESS("TRADE_SUCCESS",EnumPayStatus.SUCCESS), + TRADE_FINISHED("TRADE_FINISHED",EnumPayStatus.SUCCESS), + TRADE_CLOSED("TRADE_CLOSED",EnumPayStatus.FAILS), + ; + private final String status; + + private final EnumPayStatus enumPayStatus; + + public static EnumPayStatus getEnumPayStatusByStatus(String status) { + for (EnumAliPayStatus s : EnumAliPayStatus.values()) { + if (Objects.equals(status, s.getStatus())) { + return s.getEnumPayStatus(); + } + } + throw new ServiceException("status 未匹配上对应的支付状态"); + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumDyPayStatus.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumDyPayStatus.java new file mode 100644 index 0000000..11a05de --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumDyPayStatus.java @@ -0,0 +1,37 @@ +package com.bnyer.pay.enums; + +import com.bnyer.common.core.enums.EnumPayStatus; +import com.bnyer.common.core.exception.ServiceException; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +/** + * @author :WXC + * @Date :2023/05/05 + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumDyPayStatus { + + PROCESSING("PROCESSING",EnumPayStatus.NO_PAY), + SUCCESS("SUCCESS",EnumPayStatus.SUCCESS), + FAIL("FAIL",EnumPayStatus.FAILS), + TIMEOUT("TIMEOUT",EnumPayStatus.FAILS), + ; + private final String status; + + private final EnumPayStatus enumPayStatus; + + public static EnumPayStatus getEnumPayStatusByStatus(String status) { + for (EnumDyPayStatus s : EnumDyPayStatus.values()) { + if (Objects.equals(status, s.getStatus())) { + return s.getEnumPayStatus(); + } + } + throw new ServiceException("status 未匹配上对应的支付状态"); + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumKsPayStatus.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumKsPayStatus.java new file mode 100644 index 0000000..e491170 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumKsPayStatus.java @@ -0,0 +1,37 @@ +package com.bnyer.pay.enums; + +import com.bnyer.common.core.enums.EnumPayStatus; +import com.bnyer.common.core.exception.ServiceException; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +/** + * @author :WXC + * @Date :2023/05/05 + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumKsPayStatus { + + PROCESSING("PROCESSING",EnumPayStatus.NO_PAY), + SUCCESS("SUCCESS",EnumPayStatus.SUCCESS), + FAILED("FAILED",EnumPayStatus.FAILS), + TIMEOUT("TIMEOUT",EnumPayStatus.FAILS), + ; + private final String status; + + private final EnumPayStatus enumPayStatus; + + public static EnumPayStatus getEnumPayStatusByStatus(String status) { + for (EnumKsPayStatus s : EnumKsPayStatus.values()) { + if (Objects.equals(status, s.getStatus())) { + return s.getEnumPayStatus(); + } + } + throw new ServiceException("status 未匹配上对应的支付状态"); + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumPayChannel.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumPayChannel.java new file mode 100644 index 0000000..240c629 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumPayChannel.java @@ -0,0 +1,69 @@ +package com.bnyer.pay.enums; + +import com.bnyer.common.core.enums.EnumPayType; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumPayChannel { + + ALI_PAY("alipay","2","ALIPAY","alipay"), + WX_PAY("wxpay","1","WECHAT","wxpay"), + DY_PAY("10","","","dypay"), + ; + + private final String code1; + + private final String code2; + + private final String code3; + + private final String name; + + public static String getPayChannelName(String code, EnumPayType payType) { + List enumPayChannelByPayType = getEnumPayChannelByPayType(payType); + for (EnumPayChannel value : enumPayChannelByPayType) { + if (value.code1.equals(code)){ + return value.getName(); + } + if (value.code2.equals(code)){ + return value.getName(); + } + if (value.code3.equals(code)){ + return value.getName(); + } + } + return null; + } + + public static List getEnumPayChannelByPayType(EnumPayType payType){ + List enumPayChannelList = new ArrayList<>(); + switch (payType){ + case ALI_PAY: + enumPayChannelList.add(ALI_PAY); + break; + case WX_PAY: + enumPayChannelList.add(WX_PAY); + break; + case DY_PAY: + enumPayChannelList.add(ALI_PAY); + enumPayChannelList.add(WX_PAY); + enumPayChannelList.add(DY_PAY); + break; + case KS_PAY: + enumPayChannelList.add(ALI_PAY); + enumPayChannelList.add(WX_PAY); + break; + } + return enumPayChannelList; + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumPayConfigStatus.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumPayConfigStatus.java new file mode 100644 index 0000000..515826f --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumPayConfigStatus.java @@ -0,0 +1,24 @@ +package com.bnyer.pay.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumPayConfigStatus { + + ENABLE("0","正常"), + DISABLE("1","停用"), + ; + + private final String code; + + private final String name; + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumTradeType.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumTradeType.java new file mode 100644 index 0000000..b9fa152 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumTradeType.java @@ -0,0 +1,37 @@ +package com.bnyer.pay.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumTradeType { + + JSAPI_MINI("1","JSAPI","JSAPI支付(小程序appId支付)"), + NATIVE("2","NATIVE","Native支付"), + APP("3","APP","app支付"), + JSAPI_PUBLIC("4","JSAPI","JSAPI支付(公众号appId支付)"), + H5("5","JSAPI","H5支付"), + + UNKNOWN("-1", "UNKNOWN", "未知支付"), + ; + + private final String code; + + private final String name; + + private final String desc; + + public static EnumTradeType getPayByTradeType(String code) { + return Arrays.stream(values()) + .filter(tradeType -> tradeType.getCode().equals(code)) + .findFirst().orElse(EnumTradeType.UNKNOWN); + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumWxPayStatus.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumWxPayStatus.java new file mode 100644 index 0000000..9235d4f --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumWxPayStatus.java @@ -0,0 +1,37 @@ +package com.bnyer.pay.enums; + +import com.bnyer.common.core.enums.EnumPayStatus; +import com.bnyer.common.core.exception.ServiceException; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +/** + * @author :WXC + * @Date :2023/05/05 + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumWxPayStatus { + + NOT_PAY("NOTPAY",EnumPayStatus.NO_PAY), + USER_PAYING("USERPAYING",EnumPayStatus.NO_PAY), + SUCCESS("SUCCESS",EnumPayStatus.SUCCESS), + PAY_ERROR("PAYERROR",EnumPayStatus.FAILS), + ; + private final String status; + + private final EnumPayStatus enumPayStatus; + + public static EnumPayStatus getEnumPayStatusByStatus(String status) { + for (EnumWxPayStatus s : EnumWxPayStatus.values()) { + if (Objects.equals(status, s.getStatus())) { + return s.getEnumPayStatus(); + } + } + throw new ServiceException("status 未匹配上对应的支付状态"); + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/manager/AliPayManager.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/manager/AliPayManager.java new file mode 100644 index 0000000..7b02ff9 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/manager/AliPayManager.java @@ -0,0 +1,55 @@ +package com.bnyer.pay.manager; + +import cn.hutool.core.collection.CollUtil; +import com.alipay.api.msg.MsgConstants; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bnyer.common.core.domain.AlipayConfig; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.SpringUtils; +import com.bnyer.pay.enums.EnumPayConfigStatus; +import com.bnyer.pay.mapper.AlipayConfigMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.List; + +/** + * @author :WXC + * @Date :2023/05/06 + * @description : + */ +@Slf4j +@Component +public class AliPayManager { + + @Resource + private AlipayConfigMapper alipayConfigMapper; + /** + * 查询支付配置 + * @return + */ + public AlipayConfig getAlipayConfig() { + List alipayConfigList = alipayConfigMapper.selectList(new LambdaQueryWrapper().eq(AlipayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode())); + if (CollUtil.isEmpty(alipayConfigList)){ + throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR); + } + AlipayConfig alipayConfig = alipayConfigList.get(0); + return alipayConfig; + } + + /** + * 查询支付配置 + * @return + */ + public AlipayConfig getAlipayConfig(String appId){ + List alipayConfigList = alipayConfigMapper.selectList(new LambdaQueryWrapper() + .eq(AlipayConfig::getAppid,appId).eq(AlipayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode())); + if (CollUtil.isEmpty(alipayConfigList)){ + throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR); + } + return alipayConfigList.get(0); + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/manager/WxPayManager.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/manager/WxPayManager.java new file mode 100644 index 0000000..14162a2 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/manager/WxPayManager.java @@ -0,0 +1,78 @@ +package com.bnyer.pay.manager; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bnyer.common.core.domain.WxpayConfig; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.SpringUtils; +import com.bnyer.pay.enums.EnumPayConfigStatus; +import com.bnyer.pay.enums.EnumTradeType; +import com.bnyer.pay.mapper.WxpayConfigMapper; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +/** + * @author :WXC + * @Date :2023/04/06 + * @description : + */ +@Component +public class WxPayManager { + + @Autowired + private WxpayConfigMapper wxpayConfigMapper; + + /** + * 获取支付配置 + * @return + */ + public WxpayConfig getWxPayConfigByTradeType(String tradeType){ + //查询数据库配置:appId,mchId,key,backUrl等 + WxpayConfig wxpayConfig = wxpayConfigMapper.selectOne(new LambdaQueryWrapper().eq(WxpayConfig::getTradeType,tradeType).eq(WxpayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode())); + if (Objects.isNull(wxpayConfig)) { + throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR); + } + return wxpayConfig; + } + + /** + * 获取支付配置 + * @return + */ + public WxpayConfig getWxPayConfigByAppId(String appId){ + //查询数据库配置:appId,mchId,key,backUrl等 + WxpayConfig wxpayConfig = wxpayConfigMapper.selectOne(new LambdaQueryWrapper().eq(WxpayConfig::getAppid,appId).eq(WxpayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode())); + if (Objects.isNull(wxpayConfig)) { + throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR); + } + return wxpayConfig; + } + + /** + * 获取微信支付服务 + * @return + */ + public WxPayService getWxPayService(WxpayConfig wxpayConfig){ + String appId = wxpayConfig.getAppid(); + String mchId = wxpayConfig.getMchid(); + String key = wxpayConfig.getKey(); + String backUrl = wxpayConfig.getBackurl(); + WxPayConfig payConfig = new WxPayConfig(); + payConfig.setAppId(appId); + payConfig.setMchId(mchId); + payConfig.setMchKey(key); + payConfig.setSignType(WxPayConstants.SignType.MD5); + payConfig.setNotifyUrl(backUrl); + payConfig.setTradeType(EnumTradeType.getPayByTradeType(wxpayConfig.getTradeType()).getName()); + WxPayService wxPayService = new WxPayServiceImpl(); + wxPayService.setConfig(payConfig); + return wxPayService; + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/AlipayConfigMapper.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/AlipayConfigMapper.java new file mode 100644 index 0000000..98beeed --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/AlipayConfigMapper.java @@ -0,0 +1,13 @@ +package com.bnyer.pay.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bnyer.common.core.domain.AlipayConfig; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author :WXC + * @description : + */ +@Mapper +public interface AlipayConfigMapper extends BaseMapper { +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/DypayConfigMapper.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/DypayConfigMapper.java new file mode 100644 index 0000000..4c28dc9 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/DypayConfigMapper.java @@ -0,0 +1,13 @@ +package com.bnyer.pay.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bnyer.common.core.domain.DypayConfig; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author :WXC + * @description : + */ +@Mapper +public interface DypayConfigMapper extends BaseMapper { +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/KspayConfigMapper.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/KspayConfigMapper.java new file mode 100644 index 0000000..81ac1cc --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/KspayConfigMapper.java @@ -0,0 +1,13 @@ +package com.bnyer.pay.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bnyer.common.core.domain.KspayConfig; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author :WXC + * @description : + */ +@Mapper +public interface KspayConfigMapper extends BaseMapper { +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/PayInfoMapper.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/PayInfoMapper.java new file mode 100644 index 0000000..07257fc --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/PayInfoMapper.java @@ -0,0 +1,20 @@ +package com.bnyer.pay.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bnyer.common.core.domain.PayInfo; +import com.bnyer.pay.dto.EditPayInfoSingleDto; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * @author :WXC + * @description : + */ +@Mapper +public interface PayInfoMapper extends BaseMapper { + /** + * 修改支付订单状态和对账信息 + * @return + */ + int editPayInfoForSingle(EditPayInfoSingleDto dto); +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/TestMapper.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/TestMapper.java deleted file mode 100644 index 76a93f7..0000000 --- a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/TestMapper.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bnyer.pay.mapper; - -public class TestMapper { -} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/WxpayConfigMapper.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/WxpayConfigMapper.java new file mode 100644 index 0000000..c32dab8 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/WxpayConfigMapper.java @@ -0,0 +1,13 @@ +package com.bnyer.pay.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bnyer.common.core.domain.WxpayConfig; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author :WXC + * @description : + */ +@Mapper +public interface WxpayConfigMapper extends BaseMapper { +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/AlipayConfigService.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/AlipayConfigService.java new file mode 100644 index 0000000..0a97d8a --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/AlipayConfigService.java @@ -0,0 +1,12 @@ +package com.bnyer.pay.service; + +import com.bnyer.common.core.domain.AlipayConfig; +import com.baomidou.mybatisplus.extension.service.IService; + /** + * @author :WXC + * @description : + */ +public interface AlipayConfigService extends IService{ + + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/DypayConfigService.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/DypayConfigService.java new file mode 100644 index 0000000..591d5d6 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/DypayConfigService.java @@ -0,0 +1,12 @@ +package com.bnyer.pay.service; + +import com.bnyer.common.core.domain.DypayConfig; +import com.baomidou.mybatisplus.extension.service.IService; + /** + * @author :WXC + * @description : + */ +public interface DypayConfigService extends IService{ + + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/KspayConfigService.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/KspayConfigService.java new file mode 100644 index 0000000..7d5ecbe --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/KspayConfigService.java @@ -0,0 +1,12 @@ +package com.bnyer.pay.service; + +import com.bnyer.common.core.domain.KspayConfig; +import com.baomidou.mybatisplus.extension.service.IService; + /** + * @author :WXC + * @description : + */ +public interface KspayConfigService extends IService{ + + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/PayInfoService.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/PayInfoService.java new file mode 100644 index 0000000..4e9896b --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/PayInfoService.java @@ -0,0 +1,33 @@ +package com.bnyer.pay.service; + +import com.bnyer.common.core.domain.PayInfo; +import com.baomidou.mybatisplus.extension.service.IService; +import com.bnyer.pay.dto.AddPayInfoDto; +import com.bnyer.pay.dto.EditPayInfoNotifyDto; +import com.bnyer.pay.vo.PayInfoDetailsVo; + +/** + * @author :WXC + * @description : + */ +public interface PayInfoService extends IService{ + /** + * 添加支付订单 + * @param dto + * @return + */ + void addPayInfo(AddPayInfoDto dto); + + /** + * 修改支付单、并发消息到订单系统修改订单状态 + * @param editPayInfoNotifyDto + */ + void editPayInfoNotify(EditPayInfoNotifyDto editPayInfoNotifyDto); + + /** + * 查询订单详情 + * @param payId + * @return + */ + PayInfoDetailsVo queryOrder(String payId); +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/TestService.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/TestService.java deleted file mode 100644 index 26046f1..0000000 --- a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/TestService.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bnyer.pay.service; - -public class TestService { -} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/UnifiedPayService.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/UnifiedPayService.java new file mode 100644 index 0000000..1e807f2 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/UnifiedPayService.java @@ -0,0 +1,31 @@ +package com.bnyer.pay.service; + +import com.bnyer.pay.dto.InOrderDto; +import com.bnyer.pay.dto.QueryOrderDto; +import com.bnyer.pay.vo.InOrderVo; +import com.bnyer.pay.vo.QueryOrderVo; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author :WXC + * @Date :2023/05/05 + * @description : + */ +public interface UnifiedPayService { + + /** + * 统一下单,并生成支付订单 + * @param dto + * @param request + * @return + */ + InOrderVo inOrder(InOrderDto dto, HttpServletRequest request); + + /** + * 统一订单查询 + * @param dto + * @return + */ + QueryOrderVo queryOrder(QueryOrderDto dto); +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/WxpayConfigService.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/WxpayConfigService.java new file mode 100644 index 0000000..d0cd045 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/WxpayConfigService.java @@ -0,0 +1,12 @@ +package com.bnyer.pay.service; + +import com.bnyer.common.core.domain.WxpayConfig; +import com.baomidou.mybatisplus.extension.service.IService; + /** + * @author :WXC + * @description : + */ +public interface WxpayConfigService extends IService{ + + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/AlipayConfigServiceImpl.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/AlipayConfigServiceImpl.java new file mode 100644 index 0000000..e0d7809 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/AlipayConfigServiceImpl.java @@ -0,0 +1,15 @@ +package com.bnyer.pay.service.impl; + +import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bnyer.pay.mapper.AlipayConfigMapper; +import com.bnyer.common.core.domain.AlipayConfig; +import com.bnyer.pay.service.AlipayConfigService; +/** + * @author :WXC + * @description : + */ +@Service +public class AlipayConfigServiceImpl extends ServiceImpl implements AlipayConfigService { + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/DypayConfigServiceImpl.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/DypayConfigServiceImpl.java new file mode 100644 index 0000000..01bafed --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/DypayConfigServiceImpl.java @@ -0,0 +1,15 @@ +package com.bnyer.pay.service.impl; + +import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bnyer.common.core.domain.DypayConfig; +import com.bnyer.pay.mapper.DypayConfigMapper; +import com.bnyer.pay.service.DypayConfigService; +/** + * @author :WXC + * @description : + */ +@Service +public class DypayConfigServiceImpl extends ServiceImpl implements DypayConfigService { + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/KspayConfigServiceImpl.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/KspayConfigServiceImpl.java new file mode 100644 index 0000000..32188dc --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/KspayConfigServiceImpl.java @@ -0,0 +1,15 @@ +package com.bnyer.pay.service.impl; + +import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bnyer.common.core.domain.KspayConfig; +import com.bnyer.pay.mapper.KspayConfigMapper; +import com.bnyer.pay.service.KspayConfigService; +/** + * @author :WXC + * @description : + */ +@Service +public class KspayConfigServiceImpl extends ServiceImpl implements KspayConfigService { + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/PayInfoServiceImpl.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/PayInfoServiceImpl.java new file mode 100644 index 0000000..834e1e1 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/PayInfoServiceImpl.java @@ -0,0 +1,138 @@ +package com.bnyer.pay.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bnyer.common.core.domain.PayInfo; +import com.bnyer.common.core.enums.EnumSceneCode; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.bean.EntityConvertUtil; +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import com.bnyer.pay.dto.AddPayInfoDto; +import com.bnyer.pay.dto.EditPayInfoNotifyDto; +import com.bnyer.pay.dto.EditPayInfoSingleDto; +import com.bnyer.pay.mapper.PayInfoMapper; +import com.bnyer.pay.service.PayInfoService; +import com.bnyer.pay.vo.PayInfoDetailsVo; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.Objects; + +/** + * @author :WXC + * @description : + */ +@Slf4j +@Service +public class PayInfoServiceImpl extends ServiceImpl implements PayInfoService { + + @Autowired + private PayInfoMapper payInfoMapper; + + @Autowired + private RocketMQTemplate vipOrderPayNotifyMqTemplate; + + /** + * 添加支付订单 + * @param dto + * @return + */ + @Override + public void addPayInfo(AddPayInfoDto dto) { + PayInfo payInfo = EntityConvertUtil.copy(dto, PayInfo.class); + payInfo.setCreateTime(new Date()); + payInfo.setIsShow("1"); + payInfoMapper.insert(payInfo); + } + + /** + * 修改支付单、并发消息到订单系统修改订单状态 + * @param editPayInfoNotifyDto + */ + @Transactional(rollbackFor=Exception.class) + @Override + public void editPayInfoNotify(EditPayInfoNotifyDto editPayInfoNotifyDto) { + log.info("开始修改内部支付订单:回调入参, editPayInfoNotifyDto:{}",JSON.toJSONString(editPayInfoNotifyDto)); + String payId = editPayInfoNotifyDto.getPayId(); + PayInfo payInfo = payInfoMapper.selectOne(new LambdaQueryWrapper().eq(PayInfo::getPayId, payId)); + if (Objects.isNull(payInfo)){ + log.error("查询支付订单数据返回失败"); + throw new ServiceException("查询支付订单数据返回失败"); + } + //修改支付单信息 + EditPayInfoSingleDto editPayInfoSingleDto = buildEditPayInfoSingleDto(editPayInfoNotifyDto); + int i = payInfoMapper.editPayInfoForSingle(editPayInfoSingleDto); + if(i==0){ + log.error("内部支付订单修改失败,payId:{}",payId); + throw new ServiceException("内部支付订单修改失败"); + } + //支付场景 + Integer sceneCode = payInfo.getSceneCode(); + //关联的订单表id + String orderNo = payInfo.getOrderNo(); + EnumSceneCode enumSceneCode = EnumSceneCode.getSceneCodeByCode(sceneCode); + switch (enumSceneCode){ + //会员充值场景 + case VIP_RECHARGE: + // 发送消息,订单支付成功 + JSONObject vipRechargeMsgObj = new JSONObject(); + vipRechargeMsgObj.put("orderNo",orderNo); + String vipRechargeMsgStr = JSON.toJSONString(vipRechargeMsgObj); + // TODO: 2023/04/23可优化为:添加一张消息日志表,字段:topicName,消息内容,消息状态(发送中、成功、失败),错误信息 +// vipOrderPayNotifyMqTemplate.asyncSend(RocketMqConstant.VIP_ORDER_PAY_NOTIFY_TOPIC,vipRechargeMsgStr,new SendCallback() { +// @Override +// public void onSuccess(SendResult sendResult) { +// log.info("topic:{}消息发送成功",RocketMqConstant.VIP_ORDER_PAY_NOTIFY_TOPIC); +// } +// @Override +// public void onException(Throwable e) { +// log.info("topic:{}消息发送失败,error:{}",RocketMqConstant.VIP_ORDER_PAY_NOTIFY_TOPIC,e.getMessage()); +// } +// }); + SendStatus sendStatus = vipOrderPayNotifyMqTemplate.syncSend(RocketMqConstant.VIP_ORDER_PAY_NOTIFY_TOPIC, + new GenericMessage<>(vipRechargeMsgStr)).getSendStatus(); + if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) { + // 消息发不出去就抛异常,因为订单回调会有多次,几乎不可能每次都无法发送出去,发的出去无所谓 + throw new ServiceException(ResponseEnum.SERVER_ERROR); + } + break; + } + } + + /** + * 构建修改支付单信息实体 + * @param editPayInfoNotifyDto + * @return + */ + private EditPayInfoSingleDto buildEditPayInfoSingleDto(EditPayInfoNotifyDto editPayInfoNotifyDto) { + EditPayInfoSingleDto editPayInfoSingleDto = new EditPayInfoSingleDto(); + editPayInfoSingleDto.setPayId(editPayInfoNotifyDto.getPayId()); + editPayInfoSingleDto.setPayTime(editPayInfoNotifyDto.getPayTime()); + editPayInfoSingleDto.setPayChannel(editPayInfoSingleDto.getPayChannel()); + return editPayInfoSingleDto; + } + + /** + * 查询订单详情 + * @param payId 支付单号 + * @return + */ + @Override + public PayInfoDetailsVo queryOrder(String payId) { + PayInfo payInfo = payInfoMapper.selectOne(new LambdaQueryWrapper().eq(PayInfo::getPayId, payId)); + if (Objects.isNull(payInfo)){ + throw new ServiceException(ResponseEnum.NOT_EXIST,"支付订单"); + } + PayInfoDetailsVo payInfoDetailsVo = EntityConvertUtil.copy(payInfo, PayInfoDetailsVo.class); + return payInfoDetailsVo; + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/TestServiceImpl.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/TestServiceImpl.java deleted file mode 100644 index 024ad89..0000000 --- a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/TestServiceImpl.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bnyer.pay.service.impl; - -public class TestServiceImpl { -} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/UnifiedPayServiceImpl.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/UnifiedPayServiceImpl.java new file mode 100644 index 0000000..bd0096c --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/UnifiedPayServiceImpl.java @@ -0,0 +1,209 @@ +package com.bnyer.pay.service.impl; + +import com.bnyer.common.core.domain.R; +import com.bnyer.common.core.enums.EnumPayStatus; +import com.bnyer.common.core.enums.EnumPayType; +import com.bnyer.common.core.enums.EnumSceneCode; +import com.bnyer.common.core.enums.EnumUserClientType; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.OrderUtil; +import com.bnyer.common.core.utils.bean.EntityConvertUtil; +import com.bnyer.common.core.utils.ip.IpUtils; +import com.bnyer.order.api.dto.QueryVipOrderDto; +import com.bnyer.order.api.remote.RemoteVipOrderService; +import com.bnyer.order.api.vo.VipOrderVo; +import com.bnyer.pay.bo.QueryOrderBo; +import com.bnyer.pay.bo.UnifiedOrderBo; +import com.bnyer.pay.constant.KSPayConstants; +import com.bnyer.pay.design.factory.PayFactory; +import com.bnyer.pay.design.strategy.IPayStrategy; +import com.bnyer.pay.dto.AddPayInfoDto; +import com.bnyer.pay.dto.InOrderDto; +import com.bnyer.pay.dto.QueryOrderDto; +import com.bnyer.pay.enums.EnumAliPayStatus; +import com.bnyer.pay.enums.EnumDyPayStatus; +import com.bnyer.pay.enums.EnumKsPayStatus; +import com.bnyer.pay.enums.EnumWxPayStatus; +import com.bnyer.pay.service.PayInfoService; +import com.bnyer.pay.service.UnifiedPayService; +import com.bnyer.pay.vo.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * @author :WXC + * @Date :2023/05/05 + * @description : + */ +@Slf4j +@Service +public class UnifiedPayServiceImpl implements UnifiedPayService { + + @Autowired + private PayInfoService payInfoService; + + @Autowired + private RemoteVipOrderService remoteVipOrderService; + + /** + * 统一下单,并生成支付订单 + * @param dto + * @param request + * @return + */ + @Override + public InOrderVo inOrder(InOrderDto dto, HttpServletRequest request) { + //支付金额 + String payAmount = ""; + //商品类型:快手支付需要 + int goodsType; + //payId + String payId; + EnumSceneCode enumSceneCode = EnumSceneCode.getSceneCodeByCode(dto.getSceneCode()); + switch (enumSceneCode){ + //会员充值场景 + case VIP_RECHARGE: + QueryVipOrderDto queryVipOrderDto = new QueryVipOrderDto(); + queryVipOrderDto.setOrderNo(dto.getOrderNo()); + //查询会员业务订单信息 + R> vipOrderVoListR = remoteVipOrderService.getVipOrderList(queryVipOrderDto); + if (!vipOrderVoListR.isSuccess()){ + throw new ServiceException(vipOrderVoListR.getMsg()); + } + VipOrderVo vipOrderVo = vipOrderVoListR.getData().get(0); + payAmount = vipOrderVo.getPayAmount().toString(); + goodsType = KSPayConstants.GOODS_TYPE_VIP; + payId = OrderUtil.getOrderNo("RV",new Date(), EnumUserClientType.getCodeByType(vipOrderVo.getUserClientType()) + ,String.valueOf(vipOrderVo.getUserId())); + break; + default: + throw new ServiceException("sceneCode未匹配上对应支付场景"); + } + //构建统一下单请求实体 + UnifiedOrderBo unifiedOrderBo = buildUnifiedOrderDto(dto, goodsType,payAmount,payId, request); + //下单,获取第三方返回信息 + IPayStrategy payStrategy = PayFactory.getInstance().getConcreteStrategy(dto.getPayType()); + if (Objects.isNull(payStrategy)){ + throw new ServiceException("暂不支持该支付方式"); + } + ThirdInOrderVo thirdInOrderVo = payStrategy.unifiedOrder(unifiedOrderBo); + //构建支付订单完成入库 + AddPayInfoDto addPayInfoDto = buildPayInfo(thirdInOrderVo, unifiedOrderBo,dto); + payInfoService.addPayInfo(addPayInfoDto); + InOrderVo inOrderVo = EntityConvertUtil.copy(thirdInOrderVo, InOrderVo.class); + return inOrderVo; + } + + /** + * 构建支付订单 + * + * @param thirdInOrderVo + * @param unifiedOrderBo + * @param dto + * @return + */ + private AddPayInfoDto buildPayInfo(ThirdInOrderVo thirdInOrderVo, UnifiedOrderBo unifiedOrderBo, InOrderDto dto) { + AddPayInfoDto addPayInfoDto = new AddPayInfoDto(); + addPayInfoDto.setOrderNo(dto.getOrderNo()); + addPayInfoDto.setSceneCode(dto.getSceneCode()); + addPayInfoDto.setRemark(dto.getRemark()); + addPayInfoDto.setPayType(dto.getPayType()); + addPayInfoDto.setPayId(unifiedOrderBo.getPayId()); + addPayInfoDto.setPayAmount(new BigDecimal(unifiedOrderBo.getPayAmount())); + addPayInfoDto.setAppid(thirdInOrderVo.getAppId()); + addPayInfoDto.setGoodsSubject(unifiedOrderBo.getGoodsSubject()); + addPayInfoDto.setGoodsDesc(unifiedOrderBo.getGoodsDesc()); +// addPayInfoDto.setThirdNo(thirdInOrderVo.getPrepayid()); + addPayInfoDto.setIp(unifiedOrderBo.getIp()); + return addPayInfoDto; + } + + /** + * 构建统一下单请求实体 + * + * @param dto 入参 + * @param goodsType 商品类型 + * @param payAmount 不同支付场景下的支付金额 + * @param payId 内部系统支付单号 + * @param request 请求request用于获取ip地址 + * @return + */ + private UnifiedOrderBo buildUnifiedOrderDto(InOrderDto dto, int goodsType, String payAmount, String payId, HttpServletRequest request) { + //当前时间 + Date currDate = new Date(); + //ip地址 + String ip = IpUtils.getIpAddr(request); + UnifiedOrderBo unifiedOrderBo = EntityConvertUtil.copy(dto, UnifiedOrderBo.class); + unifiedOrderBo.setPayId(payId); + unifiedOrderBo.setIp(ip); + unifiedOrderBo.setCurrDate(currDate); + unifiedOrderBo.setPayAmount(payAmount); + unifiedOrderBo.setGoodsType(goodsType); + return unifiedOrderBo; + } + + /** + * 统一订单查询 + * @param dto + * @return + */ + @Override + public QueryOrderVo queryOrder(QueryOrderDto dto) { + //先查询系统支付单是否已经完成支付,如果因为延迟导致没有及时同步,在调用第三方接口查询支付状态返回 + PayInfoDetailsVo payInfoDetailsVo = payInfoService.queryOrder(dto.getPayId()); + Integer payStatus = payInfoDetailsVo.getPayStatus(); + EnumPayStatus enumPayStatus = EnumPayStatus.getEnumPayStatusByStatus(payStatus); + if (EnumPayStatus.NO_PAY != enumPayStatus){ + QueryOrderVo queryOrderVo = new QueryOrderVo(); + queryOrderVo.setPayId(dto.getPayId()); + queryOrderVo.setPayStatus(enumPayStatus.getStatus()); + return queryOrderVo; + } + //获取第三方订单信息 + IPayStrategy strategy = PayFactory.getInstance().getConcreteStrategy(dto.getPayType()); + if (Objects.isNull(strategy)){ + throw new ServiceException("暂不支持该支付方式"); + } + //构建统一订单查询实体 + QueryOrderBo queryOrderBo = EntityConvertUtil.copy(dto, QueryOrderBo.class); + ThirdQueryOrderVo thirdQueryOrderVo = strategy.queryOrder(queryOrderBo); + QueryOrderVo queryOrderVo = new QueryOrderVo(); + queryOrderVo.setPayId(dto.getPayId()); + queryOrderVo.setPayStatus(getPayStatus(dto.getPayType(), thirdQueryOrderVo.getPayStatus())); + return queryOrderVo; + } + + /** + * 通过第三方系统支付状态构建业务系统对应支付状态 + * @param payType + * @param thirdPayStatus + * @return + */ + private Integer getPayStatus(String payType,String thirdPayStatus){ + EnumPayType enumPayType = EnumPayType.getEnumPayTypeByType(payType); + switch (enumPayType){ + case KS_PAY: + EnumPayStatus enumPayStatusKs = EnumKsPayStatus.getEnumPayStatusByStatus(thirdPayStatus); + return enumPayStatusKs.getStatus(); + case WX_PAY: + EnumPayStatus enumPayStatusWx = EnumWxPayStatus.getEnumPayStatusByStatus(thirdPayStatus); + return enumPayStatusWx.getStatus(); + case DY_PAY: + EnumPayStatus enumPayStatusDy = EnumDyPayStatus.getEnumPayStatusByStatus(thirdPayStatus); + return enumPayStatusDy.getStatus(); + case ALI_PAY: + EnumPayStatus enumPayStatusAli = EnumAliPayStatus.getEnumPayStatusByStatus(thirdPayStatus); + return enumPayStatusAli.getStatus(); + default: + throw new ServiceException("payType 参数参数传递错误"); + } + } + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/WxpayConfigServiceImpl.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/WxpayConfigServiceImpl.java new file mode 100644 index 0000000..09e7776 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/WxpayConfigServiceImpl.java @@ -0,0 +1,15 @@ +package com.bnyer.pay.service.impl; + +import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bnyer.pay.mapper.WxpayConfigMapper; +import com.bnyer.common.core.domain.WxpayConfig; +import com.bnyer.pay.service.WxpayConfigService; +/** + * @author :WXC + * @description : + */ +@Service +public class WxpayConfigServiceImpl extends ServiceImpl implements WxpayConfigService { + +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/DYPayUtil.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/DYPayUtil.java new file mode 100644 index 0000000..f164be4 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/DYPayUtil.java @@ -0,0 +1,138 @@ +package com.bnyer.pay.utils; + +import org.springframework.stereotype.Component; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +/** + * @author :WXC + * @Date :2023/04/23 + * @description :抖音支付工具类 + */ +@Component +public class DYPayUtil { + /** + * 发起请求时的签名 + */ + public String getSign(Map paramsMap,String salt) { + List paramsArr = new ArrayList<>(); + for (Map.Entry entry : paramsMap.entrySet()) { + String key = entry.getKey(); + if (key.equals("other_settle_params")) { + continue; + } + String value = entry.getValue().toString(); + + value = value.trim(); + if (value.startsWith("\"") && value.endsWith("\"") && value.length() > 1) { + value = value.substring(1, value.length() - 1); + } + value = value.trim(); + if (value.equals("") || value.equals("null")) { + continue; + } + switch (key) { + // 字段用于标识身份,不参与签名 + case "app_id": + case "thirdparty_id": + case "sign": + break; + default: + paramsArr.add(value); + break; + } + } + // 支付密钥值 + paramsArr.add(salt); + Collections.sort(paramsArr); + StringBuilder signStr = new StringBuilder(); + String sep = ""; + for (String s : paramsArr) { + signStr.append(sep).append(s); + sep = "&"; + } + return md5FromStr(signStr.toString()); + } + + public String md5FromStr(String inStr) { + MessageDigest md5; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return ""; + } + + byte[] byteArray = inStr.getBytes(StandardCharsets.UTF_8); + byte[] md5Bytes = md5.digest(byteArray); + StringBuilder hexValue = new StringBuilder(); + for (byte md5Byte : md5Bytes) { + int val = ((int) md5Byte) & 0xff; + if (val < 16) { + hexValue.append("0"); + } + hexValue.append(Integer.toHexString(val)); + } + return hexValue.toString(); + } + + /** + * 回调验证签名 + */ + public String getCallbackSignature(int timestamp, String nonce, String msg,String token) { + List sortedString = new ArrayList<>(); + sortedString.add(String.valueOf(timestamp)); + sortedString.add(nonce); + sortedString.add(msg); + sortedString.add(token); + Collections.sort(sortedString); + StringBuilder sb = new StringBuilder(); + sortedString.forEach(sb::append); + return getSha1(sb.toString().getBytes()); + } + + public String getSha1(byte[] input) { + MessageDigest mDigest; + try { + mDigest = MessageDigest.getInstance("SHA1"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return ""; + } + byte[] result = mDigest.digest(input); + StringBuilder sb = new StringBuilder(); + for (byte b : result) { + sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1)); + } + return sb.toString(); + } + + /** + * 手机号登陆信息解密 + */ + public String decrypt(String encryptedData, String sessionKey, String iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException { + Base64.Decoder decoder = Base64.getDecoder(); + byte[] sessionKeyBytes = decoder.decode(sessionKey); + byte[] ivBytes = decoder.decode(iv); + byte[] encryptedBytes = decoder.decode(encryptedData); + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + SecretKeySpec skeySpec = new SecretKeySpec(sessionKeyBytes, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); + cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec); + byte[] ret = cipher.doFinal(encryptedBytes); + return new String(ret); + } +} + diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/KSPayUtil.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/KSPayUtil.java new file mode 100644 index 0000000..4d694d2 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/KSPayUtil.java @@ -0,0 +1,133 @@ +package com.bnyer.pay.utils; + +import com.google.common.base.Joiner; +import com.google.common.base.Strings; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import static javax.crypto.Cipher.DECRYPT_MODE; + +/** + * @author :WXC + * @Date :2023/04/23 + * @description :快手支付工具类 + */ +@Slf4j +@Component +public class KSPayUtil { + + /** + * 快手小程序返回的加密数据的解密函数 + * + * @param sessionKey 有效的sessionKey,通过 login code 置换 + * @param encryptedData 返回的加密数据(base64编码) + * @param iv 返回的加密IV(base64编码) + * @return 返回解密的字符串数据 + */ + public String decrypt(String sessionKey, String encryptedData, String iv) { + // Base64解码数据 + byte[] aesKey = Base64.decodeBase64(sessionKey); + byte[] ivBytes = Base64.decodeBase64(iv); + byte[] cipherBytes = Base64.decodeBase64(encryptedData); + + byte[] plainBytes = decrypt0(aesKey, ivBytes, cipherBytes); + + return new String(plainBytes, StandardCharsets.UTF_8); + } + + /** + * AES解密函数. 使用 AES/CBC/PKCS5Padding 模式 + * + * @param aesKey 密钥,长度16 + * @param iv 偏移量,长度16 + * @param cipherBytes 密文信息 + * @return 明文 + */ + private byte[] decrypt0(byte[] aesKey, byte[] iv, byte[] cipherBytes) { + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + try { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(DECRYPT_MODE, keySpec, ivSpec); + return cipher.doFinal(cipherBytes); + } catch (Exception e) { + log.error("decrypt error:{}", e.getMessage()); + throw new RuntimeException(e); + } + } + + /** + * https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html + *

+ * https://mp.kuaishou.com/docs/develop/server/payment/serverSignature.html + * 支付签名 + */ + + public String buildMd5(Map dataMap, String appSecret) { + String signStr = genSignStr(dataMap); + return DigestUtils.md5Hex(signStr + appSecret); + } + + private String genSignStr(Map data) { + StringBuilder sb = new StringBuilder(); + data.keySet().stream().sorted() + .filter(key -> StringUtils.isNotBlank(key) && StringUtils.isNotEmpty(data.get(key))) + .forEach(key -> { + sb.append(key); + sb.append("="); + sb.append(data.get(key)); + sb.append("&"); + }); + if (sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } + + return sb.toString(); + } + + + /** + * 获取参数 Map 的签名结果 + * https://mp.kuaishou.com/docs/develop/server/epay/appendix.html + * + * @param signParamsMap 含义见上述示例 + * @return 返回签名结果 + */ + public String calcSign(Map signParamsMap, String secret) { + // 去掉 value 为空的 + Map trimmedParamMap = signParamsMap.entrySet() + .stream() + .filter(item -> !Strings.isNullOrEmpty(item.getValue().toString())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + // 按照字母排序 + Map sortedParamMap = trimmedParamMap.entrySet() + .stream() + .sorted(Map.Entry.comparingByKey()) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (oldValue, newValue) -> oldValue, LinkedHashMap::new)); + + // 组装成待签名字符串。(注,引用了guava工具) + String paramStr = Joiner.on("&").withKeyValueSeparator("=").join(sortedParamMap.entrySet()); + String signStr = paramStr + secret; + + // 生成签名返回。(注,引用了commons-codec工具) + return DigestUtils.md5Hex(signStr); + } +} + diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/MoneyUtil.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/MoneyUtil.java new file mode 100644 index 0000000..adfe78b --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/MoneyUtil.java @@ -0,0 +1,131 @@ +package com.bnyer.pay.utils; + + +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; +import java.text.DecimalFormat; + +public class MoneyUtil { + + /** + * 费用:元TO元 + * 四舍五入,保留2位小数 + * 正常情况用不到,但是如果传入的不合规范的超长小数点,则需要规范小数:例如12.26999999999998 + * @param yuan + * @return + */ + public static String yuanToYuan(String yuan){ + return yuanToYuan(yuan,false); + } + + /** + * 费用:元TO元 + * 四舍五入,保留2位小数 + * 正常情况用不到,但是如果传入的不合规范的超长小数点,则需要规范小数:例如12.26999999999998 + * @param yuan + * @param returnZeroFlag + * @return + */ + public static String yuanToYuan(String yuan,boolean returnZeroFlag){ + String ret = "0"; + if(StringUtils.isNotBlank(yuan)){ + try{ + DecimalFormat df = new DecimalFormat("0.00"); + ret = df.format(Float.valueOf(yuan)); + }catch(Exception e){} + }else{ + if(returnZeroFlag) {//空的时候是否返回0 + return ret; + }else{ + return yuan; + } + } + return ret; + } + + /** + * 费用:元TO分 + * @return + */ + public static String yuanToFen(String yuan){ + return yuanToFen(yuan,false); + } + + /** + * + * @param yuan + * @param returnZeroFlag 入参空的是或否是否返回0 + * @return + */ + public static String yuanToFen(String yuan,boolean returnZeroFlag){ + String ret = "0"; + if(StringUtils.isNotBlank(yuan)){ + try{ + + BigDecimal res = new BigDecimal(yuan).multiply(new BigDecimal(100)); + DecimalFormat df = new DecimalFormat("0"); + ret = df.format(res.doubleValue()); + }catch(Exception e){} + }else{ + if(returnZeroFlag) {//空的时候是否返回0 + return ret; + }else{ + return yuan; + } + } + return ret; + } + + /** + * 费用:分TO元 + * 格式化0.00 表示:比如结果0.50 + * #.00表示:比如.50 + * @param fen + * @return + */ + public static String fenToYuan(String fen){ + return fenToYuan(fen,false); + } + + /** + * 费用:分TO元 + * 格式化0.00 表示:比如结果0.50 + * #.00表示:比如.50 + * @param fen + * @param returnZeroFlag + * @return + */ + public static String fenToYuan(String fen,boolean returnZeroFlag){ + String ret = "0.00"; + if(StringUtils.isNotBlank(fen)){ + try{ + BigDecimal res = new BigDecimal(fen).divide(new BigDecimal(100)); + DecimalFormat df = new DecimalFormat("0.00"); + ret = df.format(res.doubleValue()); + + }catch(Exception e){} + }else{ + if(returnZeroFlag){//空的时候是否返回0 + return ret; + }else{ + return fen; + } + } + return ret; + } + + public static void main(String[] args) { + + String[] yuans = new String[]{ + "1.01","12.09","123.08","1234.11","12345.99","123456.11","1234567.98","12345678.02","123456789.01","1234567890.55","12345678901.99" + }; + for(String yuan : yuans){ + String fen = yuanToFen(yuan); + System.out.println(yuan+"元转分"+fen); + System.out.println(fen+"分转元"+fenToYuan(fen)); + } + + + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/PayRestTemplateUtil.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/PayRestTemplateUtil.java new file mode 100644 index 0000000..2022f05 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/PayRestTemplateUtil.java @@ -0,0 +1,107 @@ +package com.bnyer.pay.utils; + +import com.alibaba.fastjson.JSONObject; +import com.bnyer.common.core.domain.KspayConfig; +import com.bnyer.pay.constant.KSPayConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +/** + * @author :WXC + * @Date :2023/04/24 + * @description : + */ +@Slf4j +@Component +public class PayRestTemplateUtil { + + @Autowired + private RestTemplate restTemplate; + + /** + * 快手小程序post请求 + * code2session + */ + public String ksPostRequestUrlencoded(JSONObject jsonObject, String url) { + String result = ""; + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + HttpEntity formEntity = new HttpEntity<>(MessageFormat.format("js_code={0}&app_id={1}&&app_secret={2}", jsonObject.get("js_code"), jsonObject.get("appid"), jsonObject.get("secret")), headers); + result = restTemplate.postForObject(url, formEntity, String.class); + } catch (Exception e) { + log.error("快手小程序post请求异常{}", url); + log.error("post请求异常:{}", e.getMessage()); + e.printStackTrace(); + } + return result; + } + + /** + * 快手小程序获取accessToken + */ + public String ksPostRequestUrlencoded(KspayConfig kspayConfig) { + String result = ""; + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + HttpEntity formEntity = new HttpEntity<>(MessageFormat.format("app_id={0}&app_secret={1}&&grant_type={2}" + , kspayConfig.getAppid(), kspayConfig.getSecret(), "client_credentials"), headers); + result = restTemplate.postForObject(KSPayConstants.GET_ACCESS_TOKEN, formEntity, String.class); + } catch (Exception e) { + log.error("快手小程序post请求异常{}", KSPayConstants.GET_ACCESS_TOKEN); + log.error("post请求异常:{}", e.getMessage()); + e.printStackTrace(); + } + return result; + } + + /** + * 快手 + * 支付 退款 结算 + */ + public String ksPostRequestJson(JSONObject jsonObject, String url, String appId, String accessToken) { + String result = ""; + try { + Map map = new HashMap<>(); + map.put("app_id", appId); + map.put("access_token", accessToken); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity formEntity = new HttpEntity<>(jsonObject, headers); + result = restTemplate.postForObject(url + "/?app_id={app_id}&access_token={access_token}", formEntity, String.class, map); + } catch (Exception e) { + log.error("快手小程序post请求异常{}", url); + log.error("post请求异常:{}", e.getMessage()); + e.printStackTrace(); + } + return result; + } + + /** + * 抖音小程序post请求 + */ + public String dyPostRequest(JSONObject jsonObject, String url) { + String result = ""; + try { + HttpHeaders headers = new HttpHeaders(); + //所有的请求需要用JSON格式发送 + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity formEntity = new HttpEntity<>(jsonObject, headers); + result = restTemplate.postForObject(url, formEntity, String.class); + } catch (Exception e) { + log.error("抖音小程序post请求异常{}", url); + e.printStackTrace(); + } + return result; + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/WXPayUtil.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/WXPayUtil.java new file mode 100644 index 0000000..2d807cb --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/WXPayUtil.java @@ -0,0 +1,71 @@ +package com.bnyer.pay.utils; + +import lombok.extern.slf4j.Slf4j; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +/** + * @author :WXC + * @Date :2023/04/12 + * @description : + */ +@Slf4j +public class WXPayUtil { + + /** + * XML格式字符串转换为Map + * + * @param strXML XML字符串 + * @return XML数据转换后的Map + * @throws Exception + */ + public static Map xmlToMap(String strXML) throws Exception { + Map data = new HashMap(); + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + try{ + //防止XXE攻击,即xml带入一个DTD。 + //解决方案1:这是优先选择. 如果不允许DTDs (doctypes) ,几乎可以阻止所有的XML实体攻击 + String FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; + documentBuilderFactory.setFeature(FEATURE, true); + + //解决方案2:如果不能完全禁用DTDs,最少采取以下措施 +// FEATURE = "http://xml.org/sax/features/external-general-entities"; +// documentBuilderFactory.setFeature(FEATURE, false); +// FEATURE = "http://xml.org/sax/features/external-parameter-entities"; +// documentBuilderFactory.setFeature(FEATURE, false); +// documentBuilderFactory.setXIncludeAware(false); +// documentBuilderFactory.setExpandEntityReferences(false); + }catch (Exception e){ + log.info("=============:微信支付XXE尝试攻击失败============"); + return data; + } + DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder(); + InputStream stream = new ByteArrayInputStream(strXML.getBytes(StandardCharsets.UTF_8)); + org.w3c.dom.Document doc = documentBuilder.parse(stream); + doc.getDocumentElement().normalize(); + NodeList nodeList = doc.getDocumentElement().getChildNodes(); + for (int idx=0; idx + + + + + + + + + + + + + + + + + + + + + id, appid, app_private_key, alipay_public_key, `key_type`, backurl, `status`, remark, + create_time, update_time,sort,is_show + + diff --git a/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/DypayConfigMapper.xml b/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/DypayConfigMapper.xml new file mode 100644 index 0000000..ceae969 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/DypayConfigMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + id, appid,salt, token, backurl, `status`, remark, create_time, update_time,sort,is_show + + diff --git a/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/KspayConfigMapper.xml b/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/KspayConfigMapper.xml new file mode 100644 index 0000000..bfa03d1 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/KspayConfigMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + id, appid, secret, backurl, `status`, remark, create_time, update_time,sort,is_show + + diff --git a/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/PayInfoMapper.xml b/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/PayInfoMapper.xml new file mode 100644 index 0000000..4e12b3d --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/PayInfoMapper.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, pay_id, order_id, pay_status,single_status,single_time, pay_type, pay_channel, pay_no, trade_no, appid, goods_subject, goods_desc, + pay_amount, pay_time, scene_code, ip, third_code, third_msg, third_no, remark, create_time, + update_time,sort,is_show + + + + + update pay_pay_info + + set pay_no = #{payNo}, + + + set trade_no = #{tradeNo}, + + + set pay_time = STR_TO_DATE(#{payTime},'%Y%m%d%H%i%s'), + + + set pay_channel = #{payChannel}, + + set pay_status = 1001, + set single_status = 1003, + set single_time = now() + where pay_id = #{payId} + + diff --git a/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/WxpayConfigMapper.xml b/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/WxpayConfigMapper.xml new file mode 100644 index 0000000..ffd24f4 --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/WxpayConfigMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + id, trade_type, appid, mchid, backurl, `key`, `status`, remark, create_time, update_time, sort, is_show + + diff --git a/pom.xml b/pom.xml index b1d967f..7b9dce0 100644 --- a/pom.xml +++ b/pom.xml @@ -44,8 +44,9 @@ 5.8.0.M3 3.16.2 4.23.21.ALL - 4.2.0 + 4.4.0 7.2.18 + 2.2.2 @@ -250,6 +251,13 @@ ${bnyer.version} + + + com.dimensionalnode + bnyer-common-rocketmq + ${bnyer.version} + + com.dimensionalnode @@ -282,6 +290,11 @@ qiniu-java-sdk ${qiniu.version} + + org.apache.rocketmq + rocketmq-spring-boot-starter + ${rocketmq-springboot.version} +