package com.cyksj.service.register.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.cyksj.common.exception.BusinessRuntimeException; import com.cyksj.common.snowflake.Sequence; import com.cyksj.common.util.Codec; import com.cyksj.common.util.Jsons; import com.cyksj.common.util.Xmls; import com.cyksj.config.WeChatConfig; import com.cyksj.dto.RedisKey; import com.cyksj.dto.Result; import com.cyksj.enums.GatewayResponse; import com.cyksj.mapper.AppleAutoRegisterDataMapper; import com.cyksj.mapper.RegisterOrderDonMapper; import com.cyksj.mapper.SpotifyUnLimitRecordMapper; import com.cyksj.mapper.UserMapper; import com.cyksj.mapper.channel.ShopConfigMapper; import com.cyksj.mapper.manage.coupon.CouponMapper; import com.cyksj.model.dto.H5JsPayParams; import com.cyksj.model.entity.*; import com.cyksj.model.manage.request.RegisterReq; import com.cyksj.model.request.OrderAttach; import com.cyksj.model.request.WxOrderRefundReq; import com.cyksj.model.views.CouponUserView; import com.cyksj.redis.RedisService; import com.cyksj.service.mange.coupon.CouponCommonService; import com.cyksj.service.register.RegisterOrderDonService; import com.cyksj.service.register.SpotifyService; import com.cyksj.service.sys.SysConfigService; import com.cyksj.service.wechat.WeChatService; import com.cyksj.task.JobManager; import com.ejlchina.searcher.BeanSearcher; import com.ejlchina.searcher.param.Operator; import com.ejlchina.searcher.util.MapUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.Map; import java.util.Optional; /** * @author chan * @date 2022/11/7 2:39 PM */ @Service @Slf4j @RequiredArgsConstructor public class RegisterOrderDonServiceImpl extends ServiceImpl implements RegisterOrderDonService { private static final Sequence SEQUENCE = new Sequence(0); private static final String REGISTER_PRICE_KEY = "register_price"; private final SpotifyService spotifyService; private final RedisService redisService; private final WeChatService weChatService; private final WeChatConfig weChatConfig; private final SysConfigService sysConfigService; private final UserMapper userMapper; private final AppleAutoRegisterDataMapper appleAutoRegisterDataMapper; private final JobManager jobManager; private final RegisterOrderDonMapper registerOrderDonMapper; private final CouponMapper couponMapper; private final CouponCommonService couponCommonService; private final SpotifyUnLimitRecordMapper spotifyUnLimitRecordMapper; private final BeanSearcher beanSearcher; private final ShopConfigMapper shopConfigMapper; private static final Long EXPIRY_TIME = 1000 * 60 * 5L; /** * 判断是否免费 * @author chan * @date 2022-11-07 3:34 PM */ @Override public boolean isFree(long userId, String platform) { int count = count(Wrappers.lambdaQuery(RegisterOrderDon.class) .eq(RegisterOrderDon::getUserId, userId) .ne(RegisterOrderDon::getStatus, RegisterOrderDon.Status.noPayment) .eq(RegisterOrderDon::getIsFree, true)); if(count > 0){ return false; } Long num = redisService.decr(RedisKey.SPOTIFY_FREE_NUM, 1L); if(num >= 0){ return true; }else { redisService.incr(RedisKey.SPOTIFY_FREE_NUM, 1L); } return false; } /** * 保存注册资料 * @author chan * @date 2022-11-07 3:34 PM */ @Override @Transactional(rollbackFor = Throwable.class) public RegisterOrderDon saveOrder(long userId, RegisterReq req, boolean isFree) throws Exception { RegisterOrderDon orderDon = new RegisterOrderDon(); User user = userMapper.selectById(userId); BeanUtil.copyProperties(req,orderDon); SysConfig register_price = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "register_price").last("limit 1")); Map map = Jsons.toMap(register_price.getSysValue()); orderDon.setOrderNo(SEQUENCE.nextId() + ""); orderDon.setUserId(userId); orderDon.setOpenId(user.getOpenId()); orderDon.setIsFree(isFree); orderDon.setMoney(isFree ? BigDecimal.ZERO : BigDecimal.valueOf(("apple".equals(req.getPlatform()) && "auto".equals(req.getAppleRegisterType())) ? map.get("appleAuto") : map.get(req.getPlatform()))); orderDon.setStatus(isFree ? RegisterOrderDon.Status.hasPayment : RegisterOrderDon.Status.noPayment); save(orderDon); //apple handleAppleIdAuto(userId, req, orderDon); return orderDon; } /** * Apple ID自动注册 */ private void handleAppleIdAuto(long userId, RegisterReq req, RegisterOrderDon orderDon) { if ("apple".equals(req.getPlatform()) && "auto".equals(req.getAppleRegisterType())) { //是否有未支付的国家或区域的订单 Integer exists = appleAutoRegisterDataMapper.checkNoPayOrder(userId, req.getCountryOrArea()); if (exists != null) { throw BusinessRuntimeException.getInstance(String.format("您有%s随机生成Apple ID未支付的订单", req.getCountryOrArea().getDesc())); } if (req.getCountryOrArea() == null) { throw BusinessRuntimeException.getInstance("请选择对应的国家或地区"); } //查看是否有空余账号 AppleAutoRegisterData appleAutoRegisterData = appleAutoRegisterDataMapper.selectOne(Wrappers.lambdaQuery(AppleAutoRegisterData.class).eq(AppleAutoRegisterData::getCountryOrArea, req.getCountryOrArea()).eq(AppleAutoRegisterData::getStatus, false).select(AppleAutoRegisterData::getId).last("limit 1")); if (appleAutoRegisterData == null) { throw BusinessRuntimeException.getInstance("自动生成Apple ID库存不足"); } appleAutoRegisterData.setUserId(userId); appleAutoRegisterData.setStatus(true); appleAutoRegisterData.setOrderId(orderDon.getId()); int update = appleAutoRegisterDataMapper.update(appleAutoRegisterData, Wrappers.lambdaUpdate(AppleAutoRegisterData.class).eq(AppleAutoRegisterData::getStatus, false).eq(AppleAutoRegisterData::getId, appleAutoRegisterData.getId())); if (update != 1) { throw BusinessRuntimeException.getInstance("系统异常,请重新操作"); } jobManager.addJob(EXPIRY_TIME, () -> { updateAppleAutoStatus(orderDon.getId()); }); } } @Override public void updateAppleAutoStatus(Long orderId) { log.info("校验appleID自动注册订单 orderId:{}", orderId); RegisterOrderDon registerOrderDon = this.getById(orderId); if (registerOrderDon != null && RegisterOrderDon.Status.noPayment == registerOrderDon.getStatus()) { AppleAutoRegisterData appleAutoRegisterData = appleAutoRegisterDataMapper.selectOne(Wrappers.lambdaQuery(AppleAutoRegisterData.class).eq(AppleAutoRegisterData::getOrderId, orderId).last("limit 1")); if (appleAutoRegisterData != null) { appleAutoRegisterData.setStatus(false); appleAutoRegisterData.setUserId(0l); appleAutoRegisterData.setOrderId(0l); appleAutoRegisterDataMapper.updateById(appleAutoRegisterData); //订单关闭 registerOrderDon.setStatus(RegisterOrderDon.Status.close); registerOrderDonMapper.updateById(registerOrderDon); log.info("未支付appleID自动注册订单:{},修改appleID使用状态", orderId); } } } @Override public CouponUserView spotifyUnLimit(SpotifyUnLimitRecord spotifyUnLimitRecord) { Boolean isFlag = false; Long spotifyUnLimitCouponId = null; //是否配置了优惠券 SysConfig spotify_unLimit_cid = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "spotify_unLimit_cid").last("limit 1")); if (spotify_unLimit_cid != null && StrUtil.isNotBlank(spotify_unLimit_cid.getSysValue())) { spotifyUnLimitCouponId = Long.parseLong(spotify_unLimit_cid.getSysValue()); Coupon coupon = couponMapper.selectById(spotifyUnLimitCouponId); if (coupon != null) { isFlag = couponCommonService.sendCouponToUser(spotifyUnLimitRecord.getUserId(), spotifyUnLimitCouponId, null, null, null, CouponUser.Channel.manual); } } //14天内是否重复提交过该账号 SpotifyUnLimitRecord exists = spotifyUnLimitRecordMapper.selectOne(Wrappers.lambdaQuery(SpotifyUnLimitRecord.class) .eq(SpotifyUnLimitRecord::getUserId, spotifyUnLimitRecord.getUserId()) .eq(SpotifyUnLimitRecord::getAccount, spotifyUnLimitRecord.getAccount()) .last("limit 1")); if (exists != null && exists.getUpdateTime().after(DateUtil.offsetDay(DateTime.now(), -14))) { throw BusinessRuntimeException.getInstance("您已提交过,请勿重复提交"); } if (exists != null) { spotifyUnLimitRecord.setId(exists.getId()); spotifyUnLimitRecord.setIsSend(false); spotifyUnLimitRecordMapper.updateById(spotifyUnLimitRecord); } else { try { spotifyUnLimitRecordMapper.insert(spotifyUnLimitRecord); } catch (DuplicateKeyException e) { log.info("用户:{}重复提交解除限制spotify账号:{}", spotifyUnLimitRecord.getUserId(), spotifyUnLimitRecord.getAccount()); } } return isFlag ? beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder() .field(CouponUserView::getUserId, spotifyUnLimitRecord.getUserId()).op(Operator.Equal) .field(CouponUserView::getCouponId, spotifyUnLimitCouponId).op(Operator.Equal) .build()) : null; } @Override public CouponUserView getAvailableSpotifyCoupon(long userId) { SysConfig spotify_register_success_cid = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "spotify_register_success_cid").last("limit 1")); CouponUserView couponUserView = null; if (spotify_register_success_cid != null && StrUtil.isNotBlank(spotify_register_success_cid.getSysValue())) { DateTime now = DateTime.now(); Long spotifyRegisterSucCid = Long.parseLong(spotify_register_success_cid.getSysValue()); couponUserView = beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder() .field(CouponUserView::getUserId, userId).op(Operator.Equal) .field(CouponUserView::getCouponId, spotifyRegisterSucCid).op(Operator.Equal) .field(CouponUserView::getCouponUserStatus, CouponUser.Status.unused.name()).op(Operator.Equal) .field(CouponUserView::getValidStartTime, now).op(Operator.LessThan) .field(Coupon::getValidEndTime, now).op(Operator.GreaterThan) .build()); } return couponUserView; } @Override public H5JsPayParams pay(Long orderId) throws Exception { log.info("注册 账号调起微信支付[start] orderId:{}", orderId); RegisterOrderDon orderDon = this.getById(orderId); if (orderDon.getStatus() == RegisterOrderDon.Status.close) { throw BusinessRuntimeException.getInstance("订单已关闭"); } OrderAttach attach = new OrderAttach(); attach.setO(orderDon.getId()); //获取支付默认商户 String shopId = sysConfigService.getDefaultShopConfigByType("wx"); String toWechatAttach = Codec.DoBase64.custom() .setData(Jsons.toJson(attach)).encodeAsText(); H5JsPayParams params = weChatService.getJsPayParams( weChatConfig.getAppid(), shopId, orderDon.getOpenId(), orderDon.getMoney().intValue(), orderDon.getOrderNo(), weChatConfig.getDomain() + "applets/register/prepay/notify", toWechatAttach, "注册服务", "JSAPI", null ); log.info("微信支付成功[end]"); return params; } @Override @Transactional(rollbackFor = Throwable.class) public void prepayNotify(Map map) throws Exception { /* 此处处理订单等业务逻辑 */ // 获取订单 和 微信订单号 和 支付的金额 String attach = Codec.DoBase64.custom().setData(map.get("attach")).decodeAsText(); OrderAttach orderAttach = Jsons.parseObject(attach, OrderAttach.class); final Long oid = orderAttach.getO(); //final Long shareId = orderAttach.getShared(); RegisterOrderDon order = this.getById(oid); if (null == order) { throw BusinessRuntimeException.getInstance("订单不存在."); } String appId = map.get("mch_id"); // 校验签名 boolean f = weChatService.checkSignWithMd5(map, shopConfigMapper.getByAppId(appId).getSecret()); if (!f) { log.error("回调数据:{}", map); throw BusinessRuntimeException.getInstance("支付成功回调, 校验签名失败."); } String transactionId = map.get("transaction_id"); if (order.getStatus().equals(RegisterOrderDon.Status.hasPayment)) { log.info("已回调,重复修改状态 id:{}", order.getId()); return; } // 更改订单状态 order.setTransactionId(transactionId); order.setStatus(RegisterOrderDon.Status.hasPayment); AppleAutoRegisterData appleAutoRegisterData = appleAutoRegisterDataMapper.selectOne(Wrappers.lambdaQuery(AppleAutoRegisterData.class).eq(AppleAutoRegisterData::getOrderId, order.getId()).eq(AppleAutoRegisterData::getStatus, true)); if (appleAutoRegisterData != null) { order.setStatus(RegisterOrderDon.Status.complete); } order.setPayTime(DateUtil.parse(map.get("time_end"), "yyyyMMddHHmmss")); if (StringUtils.isBlank(order.getOpenId())) { order.setOpenId(map.get("openid")); } this.updateById(order); } @Override public Result wxRefund(WxOrderRefundReq wxOrderRefundReq) throws Exception { RegisterOrderDon registerOrderDon = this.getById(wxOrderRefundReq.getOrderId()); if (registerOrderDon == null) { throw BusinessRuntimeException.getInstance("该订单不存在"); } if (registerOrderDon.getStatus() == RegisterOrderDon.Status.refund) { throw BusinessRuntimeException.getInstance("该订单已退款"); } if (registerOrderDon.getStatus() != RegisterOrderDon.Status.error && registerOrderDon.getStatus() != RegisterOrderDon.Status.hasPayment) { throw BusinessRuntimeException.getInstance("该订单状态不允许退款"); } if (registerOrderDon.getMoney().compareTo(BigDecimal.ZERO) <= 0) { throw BusinessRuntimeException.getInstance("该订单金额为0,无需退款"); } if (!redisService.setNx(String.format("%s", wxOrderRefundReq.getOrderId()), wxOrderRefundReq.getOrderId(), 10l)) { throw BusinessRuntimeException.getInstance("请勿重复点击,正在退款中.."); } long time = System.currentTimeMillis(); //退款订单号 String outRefundNo = Codec.DoDigest.custom() .setAlgorithm(Codec.DoDigest.Algorithm.MD5) .setStringData(time + String.valueOf(registerOrderDon.getId())) .toHexString(); wxOrderRefundReq.setRefundMoney(registerOrderDon.getMoney()); weChatService.refundWxOrder(weChatConfig.getAppid(), Optional.ofNullable(registerOrderDon.getShopId()).orElse(ShopConfig.WX_DEFAULT_APP_ID), registerOrderDon.getTransactionId(), outRefundNo, registerOrderDon.getMoney(), wxOrderRefundReq.getRefundMoney(), weChatConfig.getDomain() + "manage/register/wx/refund/notify"); //修改订单状态 registerOrderDon.setStatus(RegisterOrderDon.Status.refund); this.updateById(registerOrderDon); return GatewayResponse.SUCCESS.newBuilder().toResult("退款成功"); } @Override public void wxRefundNotify(Map map) throws Exception { // 对退款数据 解密 String md5Key = Codec.DoDigest.custom().setAlgorithm(Codec.DoDigest.Algorithm.MD5).setStringData(shopConfigMapper.getByAppId(map.get("mch_id")).getSecret()).toHexString(); String reqInfoXml = Codec.DoCipher.custom().setAlgorithm(Codec.DoCipher.Trans.AES_ECB_PKCS7Padding).setBase64Data(map.get("req_info")).setStringKey(md5Key).ofDecrypt().toText(); log.info("=== 退款数据解密: \n {}", reqInfoXml); Map reqInfoMap = Xmls.toMap(reqInfoXml); // 退款失败 if (StrUtil.equals("CHANGE", reqInfoMap.get("refund_status"))) { throw BusinessRuntimeException.getInstance("退款异常."); } else if (StrUtil.equals("REFUNDCLOSE", reqInfoMap.get("refund_status"))) { throw BusinessRuntimeException.getInstance("退款关闭."); } /* 此处处理业务逻辑 */ String outRefundNo = reqInfoMap.get("out_refund_no"); String transactionId = reqInfoMap.get("transaction_id"); RegisterOrderDon registerOrderDon = this.getOne(Wrappers.lambdaQuery(RegisterOrderDon.class).eq(RegisterOrderDon::getTransactionId, transactionId).last("limit 1")); if (registerOrderDon == null) { log.error("退款回调,transactionId:{}订单不存在", transactionId); } if (registerOrderDon.getStatus() != RegisterOrderDon.Status.refund) { registerOrderDon.setStatus(RegisterOrderDon.Status.refund); this.updateById(registerOrderDon); } log.info("微信退款: 退款单号[{}] 退款成功.", outRefundNo); } }