| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- package com.zsElectric.boot.business.service.impl;
- import cn.hutool.core.collection.CollUtil;
- import cn.hutool.core.date.DateUtil;
- import cn.hutool.core.util.ObjectUtil;
- import com.baomidou.mybatisplus.core.toolkit.Wrappers;
- import com.google.gson.JsonObject;
- import com.zsElectric.boot.business.mapper.RechargeLevelMapper;
- import com.zsElectric.boot.business.model.entity.RechargeLevel;
- import com.zsElectric.boot.business.model.form.applet.LevelOrderForm;
- import com.zsElectric.boot.business.model.form.applet.UserPayForm;
- import com.zsElectric.boot.business.model.vo.UserInfoVO;
- import com.zsElectric.boot.business.service.RechargeLevelService;
- import com.zsElectric.boot.business.service.UserInfoService;
- import com.zsElectric.boot.common.constant.SystemConstants;
- import com.zsElectric.boot.core.pay.WXPayUtility;
- import com.zsElectric.boot.core.pay.WechatConstants;
- import com.zsElectric.boot.core.pay.WechatPayV3Utils;
- import com.zsElectric.boot.core.pay.WechatUrlConstants;
- import com.zsElectric.boot.security.util.SecurityUtils;
- import jakarta.servlet.http.HttpServletRequest;
- import jakarta.servlet.http.HttpServletResponse;
- import lombok.RequiredArgsConstructor;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Service;
- import com.baomidou.mybatisplus.core.metadata.IPage;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.zsElectric.boot.business.mapper.UserOrderInfoMapper;
- import com.zsElectric.boot.business.service.UserOrderInfoService;
- import com.zsElectric.boot.business.model.entity.UserOrderInfo;
- import com.zsElectric.boot.business.model.form.UserOrderInfoForm;
- import com.zsElectric.boot.business.model.query.UserOrderInfoQuery;
- import com.zsElectric.boot.business.model.vo.UserOrderInfoVO;
- import com.zsElectric.boot.business.converter.UserOrderInfoConverter;
- import java.math.BigDecimal;
- import java.math.RoundingMode;
- import java.text.DecimalFormat;
- import java.text.SimpleDateFormat;
- import java.time.LocalDateTime;
- import java.util.*;
- import java.util.concurrent.ThreadLocalRandom;
- import java.util.concurrent.locks.ReentrantLock;
- import java.util.stream.Collectors;
- import cn.hutool.core.lang.Assert;
- import cn.hutool.core.util.StrUtil;
- /**
- * 用户支付订单信息服务实现类
- *
- * @author zsElectric
- * @since 2025-12-16 16:25
- */
- @Slf4j
- @Service
- @RequiredArgsConstructor
- public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, UserOrderInfo> implements UserOrderInfoService {
- private final UserOrderInfoConverter userOrderInfoConverter;
- private final UserInfoService userInfoService;
- private final RechargeLevelMapper rechargeLevelMapper;
- private final WechatPayV3Utils wechatPayV3Utils;
- // 声明一个可重入锁
- private final ReentrantLock lock = new ReentrantLock();
- /**
- * 获取用户支付订单信息分页列表
- *
- * @param queryParams 查询参数
- * @return {@link IPage<UserOrderInfoVO>} 用户支付订单信息分页列表
- */
- @Override
- public IPage<UserOrderInfoVO> getUserOrderInfoPage(UserOrderInfoQuery queryParams) {
- Page<UserOrderInfoVO> pageVO = this.baseMapper.getUserOrderInfoPage(
- new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
- queryParams
- );
- return pageVO;
- }
- /**
- * 获取用户支付订单信息表单数据
- *
- * @param id 用户支付订单信息ID
- * @return 用户支付订单信息表单数据
- */
- @Override
- public UserOrderInfoForm getUserOrderInfoFormData(Long id) {
- UserOrderInfo entity = this.getById(id);
- return userOrderInfoConverter.toForm(entity);
- }
- /**
- * 新增用户支付订单信息
- *
- * @param formData 用户支付订单信息表单对象
- * @return 是否新增成功
- */
- @Override
- public boolean saveUserOrderInfo(UserOrderInfoForm formData) {
- UserOrderInfo entity = userOrderInfoConverter.toEntity(formData);
- return this.save(entity);
- }
- /**
- * 更新用户支付订单信息
- *
- * @param id 用户支付订单信息ID
- * @param formData 用户支付订单信息表单对象
- * @return 是否修改成功
- */
- @Override
- public boolean updateUserOrderInfo(Long id, UserOrderInfoForm formData) {
- UserOrderInfo entity = userOrderInfoConverter.toEntity(formData);
- return this.updateById(entity);
- }
- /**
- * 删除用户支付订单信息
- *
- * @param ids 用户支付订单信息ID,多个以英文逗号(,)分割
- * @return 是否删除成功
- */
- @Override
- public boolean deleteUserOrderInfos(String ids) {
- Assert.isTrue(StrUtil.isNotBlank(ids), "删除的用户支付订单信息数据为空");
- // 逻辑删除
- List<Long> idList = Arrays.stream(ids.split(","))
- .map(Long::parseLong)
- .toList();
- return this.removeByIds(idList);
- }
- @Override
- public UserPayForm createOrder(LevelOrderForm levelOrderForm) {
- Long userId = SecurityUtils.getUserId();
- String userOpenId = userInfoService.getCurrentUserInfo().getOpenid();
- String orderNo = createOrderNo("SP",userId);
- //创建订单
- UserOrderInfo orderInfo = new UserOrderInfo();
- orderInfo.setUserId(userId);
- orderInfo.setOrderNo(orderNo);
- orderInfo.setLevelId(levelOrderForm.getLevelId());
- orderInfo.setOpenid(userOpenId);
- this.save(orderInfo);
- //构建支付表单返回给前端支撑JsApi支付调用
- UserPayForm payForm = new UserPayForm();
- payForm.setOrderId(orderInfo.getId()).setOrderNo(orderNo);
- //查询档位
- RechargeLevel level = rechargeLevelMapper.selectById(levelOrderForm.getLevelId());
- Map<String, Object> result = payment(userOpenId,orderNo,level.getMoney());
- payForm.setParams(result);
- return payForm;
- }
- @Override
- public UserPayForm payOrder(String orderId) {
- UserOrderInfo orderInfo = this.getById(orderId);
- //构建支付表单
- UserPayForm payForm = new UserPayForm();
- payForm.setOrderId(orderInfo.getId()).setOrderNo(orderInfo.getOrderNo());
- //查询档位
- RechargeLevel level = rechargeLevelMapper.selectById(orderInfo.getLevelId());
- Map<String, Object> result = payment(orderInfo.getOpenid(),orderInfo.getOrderNo(),level.getMoney());
- payForm.setParams(result);
- return payForm;
- }
- @Override
- public String orderQuery(String orderNo) {
- //查询订单
- UserOrderInfo orderInfo = this.getById(Wrappers.<UserOrderInfo>lambdaQuery().eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1"));
- if (ObjectUtil.isEmpty(orderInfo)) {
- throw new RuntimeException("当前订单不存在");
- }
- //null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败
- JsonObject res = orderQueryByOutTradeNo(orderNo, WechatConstants.WECHAT_MCH_ID);
- String s = res == null ? null : res.get("trade_state").getAsString();
- // String s = "SUCCESS";
- if ("SUCCESS".equals(s)) {
- if (ObjectUtil.isNotEmpty(orderInfo) && Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_ONE)) {
- orderInfo.setOrderStatus(SystemConstants.STATUS_TWO);
- orderInfo.setPayTime(LocalDateTime.now());
- orderInfo.setOutTradeNo(res.get("transaction_id").getAsString());
- this.updateById(orderInfo);
- }
- return "100001";//支付成功
- }
- if (s == null) {
- //查询订单
- return "100002";//查询失败
- }
- if ("USERPAYING".equals(s) || "ACCEPT".equals(s)) {
- //查询订单
- return "100003";//查询中
- }
- return "100004";//支付失败
- }
- @Override
- public Map<String, String> wechatPayNotify(HttpServletRequest request, HttpServletResponse response) {
- Map<String, String> result = new HashMap<>(2);
- //验签及解析返回数据
- JsonObject res = wechatPayV3Utils.getCallbackData(request);
- if (res == null) {
- result.put("code", "FAIL");
- result.put("message", "失败");
- return result;
- }
- log.info("最终拿到的微信支付通知数据:" + res);
- String orderNo = res.get("out_trade_no").getAsString();
- if (lock.tryLock()) {
- // 处理支付成功后的业务 例如 将订单状态修改为已支付 具体参数键值可参考文档 注意!!! 微信可能会多次发送重复的通知 因此要判断业务是否已经处理过了 避免重复处理
- try {
- //查询订单,判断是否已修改为已支付状态
- UserOrderInfo orderInfo = this.getOne(Wrappers.<UserOrderInfo>lambdaQuery().eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1"));
- if (ObjectUtil.isNotEmpty(orderInfo)) {
- if (Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_TWO)) {
- result.put("code", "SUCCESS");
- result.put("message", "OK");
- return result;
- }
- if (Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_ONE)) {
- orderInfo.setOrderStatus(SystemConstants.STATUS_TWO);
- orderInfo.setOrderType(SystemConstants.STATUS_ONE);
- orderInfo.setTransactionId(res.get("transaction_id").getAsString());
- orderInfo.setPayTime(LocalDateTime.now());
- // orderInfo.setPayTime(dealDateFormat(res.getString("success_time")));
- this.updateById(orderInfo);
- //todo 发送延迟消息 15分钟超时未支付
- //todo 异步增加积分
- }
- }
- result.put("code", "SUCCESS");
- result.put("message", "OK");
- return result;
- } catch (Exception e) {
- log.error("微信支付回调异常:" + e.getMessage());
- result.put("code", "FAIL");
- result.put("message", "失败");
- return result;
- }finally {
- lock.unlock();
- }
- }else {
- result.put("code", "FAIL");
- result.put("message", "失败");
- return result;
- }
- }
- /**
- * 通过商户订单号查询订单在微信侧支付状态
- *
- * @param out_trade_no 发起支付时创建的商户订单号
- * @return null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败
- */
- public JsonObject orderQueryByOutTradeNo(String out_trade_no, String subMchId) {
- String url = WechatUrlConstants.PAY_V3_QUERY_OUT;
- url = url.replace("{out_trade_no}", WXPayUtility.urlEncode(out_trade_no));
- Map<String, Object> args = new HashMap<>();
- args.put("sub_mchid", subMchId);
- url = url + "?" + WXPayUtility.urlEncode(args);
- return wechatPayV3Utils.sendGet(url);
- }
- /**
- * 构建支付表单返回给前端支撑JsApi支付调用
- * @param openId
- * @param orderNo
- * @param amount
- * @return
- */
- private Map<String, Object> payment(String openId, String orderNo,BigDecimal amount) {
- //15分钟超时限制
- Calendar calendar = Calendar.getInstance();
- calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) + 15);
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
- //构建微信支付参数
- Map<String, Object> params = new HashMap<>();
- params.put("appid", WechatConstants.WECHAT_MP_APPID); //小程序appid
- params.put("mchid", WechatConstants.WECHAT_MCH_ID); //商户号
- params.put("description", "订单业务"); //商品描述
- params.put("out_trade_no", orderNo); //商户订单号
- params.put("time_expire", sdf.format(calendar.getTime())); //交易结束时间 选填 时间到了之后将不能再支付 遵循rfc3339标准格式
- params.put("attach", orderNo); //附加数据 选填
- // 在查询API和支付通知中原样返回 可作为自定义参数使用
- params.put("notify_url", WechatUrlConstants.PAY_V3_NOTIFY); //支付结果异步通知接口
- //订单金额信息
- Map<String, Object> amount_json = new HashMap<>();
- //支付金额 单位:分
- // amount_json.put("total", Integer.parseInt(amount_fee(Double.valueOf("0.1"))));测试用例
- amount_json.put("total", Integer.parseInt(amount_fee(amount)));
- params.put("amount", amount_json);
- //支付者信息
- Map<String, Object> payer = new HashMap<>();
- //用户在小程序侧的openid
- payer.put("openid", openId);
- params.put("payer", payer);
- return params;
- }
- /**
- * 创建商户订单号
- * 要求 32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
- * 组成 两位前缀 + 17位时间戳 + 9位id补零 + 4位随机数 合计32位
- *
- * @param head 例如 商品-SP 退款-TK 等等
- * @param id 用户id
- * @return
- */
- public String createOrderNo(String head, Long id) {
- StringBuilder uid = new StringBuilder(id.toString());
- Date date = new Date();
- SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
- int length = uid.length();
- for (int i = 0; i < 9 - length; i++) {
- uid.insert(0, "0");
- }
- return head + sdf.format(date) + uid + (int) ((Math.random() * 9 + 1) * 1000);
- }
- /**
- * 金额元转分字符串
- *
- * @param cny 元
- * @return
- */
- public String amount_fee(BigDecimal cny) {
- BigDecimal b2 = new BigDecimal("100");
- return cny.multiply(b2).setScale(0, RoundingMode.DOWN).toString();
- }
- public static String amount_fee(double price) {
- DecimalFormat df = new DecimalFormat("#.00");
- price = Double.valueOf(df.format(price));
- int money = (int) (price * 100);
- return money + "";
- }
- /**
- * 分转元,转换为bigDecimal在转成double
- *
- * @return
- */
- public static double changeF2Y3(int price) {
- return BigDecimal.valueOf(Long.valueOf(price)).divide(new BigDecimal("100")).doubleValue();
- }
- }
|