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 a34c2a5..f84a53a 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 @@ -8,7 +8,9 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.Calendar; import java.util.Date; + import org.apache.commons.lang3.time.DateFormatUtils; /** @@ -176,4 +178,22 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); return Date.from(zdt.toInstant()); } + + /** + * 给指定日期添加指定添加的天数 返回添加后的天数 + * + * @param d 指定的日期 + * @param day 指定添加的天数 + * @return 添加后的天数 + */ + public static Date getDateAfter(Date d, int day) { + Calendar now = Calendar.getInstance(); + now.setTime(d); + now.set(Calendar.DATE, now.get(Calendar.DATE) + day); + return now.getTime(); + } + + public static void main(String[] args) { + System.out.println(getDateAfter(new Date(), 365)); + } } diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/config/AlipayConfig.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/config/AlipayConfig.java index 09928e0..ea0d7b1 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/config/AlipayConfig.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/config/AlipayConfig.java @@ -27,6 +27,9 @@ public class AlipayConfig{ @Value("${alipay.privateKey}") private String privateKey; + @Value("${alipay.publicKey}") + private String publicKey; + @Value("${alipay.certPath}") private String certPath; @@ -35,4 +38,17 @@ public class AlipayConfig{ @Value("${alipay.rootPath}") private String rootPath; + + @Value("${alipay.notifyUrl}") + private String notifyUrl; + + @Value("${alipay.returnUrl}") + private String returnUrl; + + @Value("${alipay.signType}") + private String signType; + + @Value("${alipay.charset}") + private String charset; + } diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/RedisKeyConstant.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/RedisKeyConstant.java index e5c7bd3..ea07ae0 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/RedisKeyConstant.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/RedisKeyConstant.java @@ -101,4 +101,9 @@ public class RedisKeyConstant { * 艺术家上传键 */ public static final String CREATOR_UPLOAD_KEY="bnyer.img.creator.upload:"; + + /** + * 用户会员vip锁键 + */ + public static final String PAY_USER_VIP_LOCK_KEY = "bnyer.img.userVip.lock:"; } diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipOrderStatusConstant.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipOrderStatusConstant.java new file mode 100644 index 0000000..1a09232 --- /dev/null +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipOrderStatusConstant.java @@ -0,0 +1,29 @@ +package com.bnyer.img.constants; + +/** + * 用户会员vip支付常量 + * @author chengkun + * @date 2022/4/21 18:12 + */ +public class UserVipOrderStatusConstant { + + /** + * 待支付 + */ + public static final String UN_PAY = "0"; + + /** + * 已支付 + */ + public static final String PAYED = "1"; + + /** + * 支付失败 + */ + public static final String PAY_FAIL = "2"; + + /** + * 支付失败 + */ + public static final String PAY_EXCEPTION = "3"; +} 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 new file mode 100644 index 0000000..fd0fa76 --- /dev/null +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipTypeConstant.java @@ -0,0 +1,24 @@ +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 86182b0..c3c653d 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 @@ -64,6 +64,12 @@ public class FhMiniController extends BaseController { @Autowired private BzDataService bzDataService; + @Autowired + private UserVipService userVipService; + + @Autowired + private UserVipRecordService userVipRecordService; + //@TokenCheck @ApiOperation(value="查询banner列表") @GetMapping(value = "/listBanner") @@ -290,4 +296,24 @@ public class FhMiniController extends BaseController { } return AjaxResult.error(); } + + //@TokenCheck + @ApiOperation(value="获取用户会员vip列表") + @GetMapping(value = "/queryUserVipList") + public AjaxResult queryUserVipList(){ + return AjaxResult.success(userVipService.queryFront()); + } + + //@TokenCheck + @ApiOperation(value="支付购买用户会员vip") + @PostMapping(value = "/payUserVip") + public AjaxResult payUserVip(@Validated @RequestBody @ApiParam("购买会员vip对象") PayUserVipDto dto){ + log.debug("【微信图文小程序】支付购买用户会员vip参数为:{}", JSON.toJSONString(dto)); + boolean b = userVipRecordService.payUserVip(dto); + if(b){ + return AjaxResult.success("购买支付成功!"); + }else{ + return AjaxResult.error("购买支付失败!"); + } + } } 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 026a4d9..fd0ee53 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 @@ -65,6 +65,9 @@ public class TiktokMiniController extends BaseController { @Autowired private BzDataService bzDataService; + @Autowired + private UserVipService userVipService; + //@TokenCheck @ApiOperation(value="查询banner列表") @GetMapping(value = "/listBanner") @@ -293,4 +296,11 @@ public class TiktokMiniController extends BaseController { } return AjaxResult.error(); } + + //@TokenCheck + @ApiOperation(value="获取用户会员vip列表") + @GetMapping(value = "/queryUserVipList") + public AjaxResult queryUserVipList(){ + return AjaxResult.success(userVipService.queryFront()); + } } 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 47dfb91..32841ce 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 @@ -64,6 +64,12 @@ public class WxMiniController extends BaseController { @Autowired private BzDataService bzDataService; + @Autowired + private UserVipService userVipService; + + @Autowired + private UserVipRecordService userVipRecordService; + //@TokenCheck @ApiOperation(value="查询banner列表") @GetMapping(value = "/listBanner") @@ -290,4 +296,24 @@ public class WxMiniController extends BaseController { } return AjaxResult.error(); } + + //@TokenCheck + @ApiOperation(value="获取用户会员vip列表") + @GetMapping(value = "/queryUserVipList") + public AjaxResult queryUserVipList(){ + return AjaxResult.success(userVipService.queryFront()); + } + + //@TokenCheck + @ApiOperation(value="支付购买用户会员vip") + @PostMapping(value = "/payUserVip") + public AjaxResult payUserVip(@Validated @RequestBody @ApiParam("购买会员vip对象") PayUserVipDto dto){ + log.debug("【微信图文小程序】支付购买用户会员vip参数为:{}", JSON.toJSONString(dto)); + boolean b = userVipRecordService.payUserVip(dto); + if(b){ + return AjaxResult.success("购买支付成功!"); + }else{ + return AjaxResult.error("购买支付失败!"); + } + } } diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/domain/UserVipRecord.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/domain/UserVipRecord.java index 3b542ca..ce81907 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/domain/UserVipRecord.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/domain/UserVipRecord.java @@ -4,9 +4,11 @@ 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.io.Serializable; +import java.math.BigDecimal; import java.util.Date; import lombok.AllArgsConstructor; import lombok.Getter; @@ -29,6 +31,13 @@ public class UserVipRecord extends BaseDomain { @ApiModelProperty(value="主键id") private Long id; + /** + * 订单id + */ + @TableField(value = "order_id") + @ApiModelProperty(value="订单id") + private String orderId; + /** * 用户手机号 */ @@ -47,6 +56,7 @@ public class UserVipRecord extends BaseDomain { * 开始时间 */ @TableField(value = "start_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @ApiModelProperty(value="开始时间") private Date startTime; @@ -54,8 +64,30 @@ public class UserVipRecord extends BaseDomain { * 到期时间 */ @TableField(value = "end_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @ApiModelProperty(value="到期时间") private Date endTime; + /** + * 支付金额 + */ + @TableField(value = "price") + @ApiModelProperty(value="支付金额") + private BigDecimal price; + + /** + * vip类型状态(0->月卡;1->季卡;2->年卡) + */ + @TableField(value = "type") + @ApiModelProperty(value="vip类型状态(0->月卡;1->季卡;2->年卡)") + private String type; + + /** + * 支付状态(0->待支付;1->已支付;2->支付失败;3->支付异常) + */ + @TableField(value = "status") + @ApiModelProperty(value="支付状态(0->待支付;1->已支付;2->支付失败;3->支付异常)") + private String status; + private static final long serialVersionUID = 1L; -} \ No newline at end of file +} diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/dto/PayUserVipDto.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/dto/PayUserVipDto.java new file mode 100644 index 0000000..fe3d6c2 --- /dev/null +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/dto/PayUserVipDto.java @@ -0,0 +1,33 @@ +package com.bnyer.img.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + + +@Getter +@Setter +@ApiModel("用户会员vip支付接收类") +public class PayUserVipDto implements Serializable { + + @NotBlank(message = "手机号不能为空!") + @ApiModelProperty(value="手机号") + private String phone; + + @NotNull(message = "用户vipId不能为空!") + @ApiModelProperty(value="用户vip表id") + private Long userVipId; + + @NotBlank(message = "vip类型状态不能为空!") + @ApiModelProperty(value="vip类型状态(0->月卡;1->季卡;2->年卡)") + private String type; + + @NotBlank(message = "支付金额不能为空!") + @ApiModelProperty(value="支付金额") + private String price; +} 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 new file mode 100644 index 0000000..b00305f --- /dev/null +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipRecordService.java @@ -0,0 +1,15 @@ +package com.bnyer.img.service; + +import com.bnyer.img.domain.UserVipRecord; +import com.bnyer.img.dto.PayUserVipDto; + +public interface UserVipRecordService { + + + /** + * 下单并支付会员vip + * @param param 用户vip参数对象 + * @return - + */ + boolean payUserVip(PayUserVipDto param); +} 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 new file mode 100644 index 0000000..c03585c --- /dev/null +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java @@ -0,0 +1,98 @@ +package com.bnyer.img.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.DateUtils; +import com.bnyer.common.core.utils.StringUtils; +import com.bnyer.common.core.utils.uuid.IdUtils; +import com.bnyer.common.redis.service.RedissonService; +import com.bnyer.img.constants.RedisKeyConstant; +import com.bnyer.img.constants.UserVipOrderStatusConstant; +import com.bnyer.img.constants.UserVipTypeConstant; +import com.bnyer.img.domain.UserVip; +import com.bnyer.img.domain.UserVipRecord; +import com.bnyer.img.dto.PayUserVipDto; +import com.bnyer.img.dto.UserVipPageDto; +import com.bnyer.img.mapper.UserVipMapper; +import com.bnyer.img.mapper.UserVipRecordMapper; +import com.bnyer.img.service.UserVipRecordService; +import com.bnyer.img.service.UserVipService; +import com.bnyer.img.vo.UserVipVo; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Service +@Slf4j +public class UserVipServiceRecordImpl implements UserVipRecordService { + + @Autowired + private UserVipRecordMapper userVipRecordMapper; + + @Autowired + private RedissonService redissonService; + + + @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支付操作执行完毕,释放锁成功!"); + } + } + return false; + } +} 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 d5a7036..b98c13d 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,10 +5,14 @@ + + + + @@ -16,7 +20,7 @@ - id, phone, user_vip_id, start_time, end_time, is_show, create_time, update_time, + id,order_id, phone,price,status, user_vip_id, start_time, end_time,type, is_show, create_time, update_time, sort - \ No newline at end of file +