Commit bde11b59 authored by tangtuo's avatar tangtuo

debug

parent c4c9a96b
......@@ -2,6 +2,7 @@
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ArgNamesErrorsInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="BigDecimalMethodWithoutRoundingCalled" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="60" name="Java" />
......
......@@ -12,6 +12,7 @@ import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
/**
......@@ -50,7 +51,7 @@ public class CopyrightApplyController {
@PostMapping(value = "/reject")
@ApiOperation(value = "驳回")
public ResponseModel<Boolean> reject(@RequestParam Integer id, @RequestParam String rejectReason) {
public ResponseModel<Boolean> reject(@RequestParam Integer id, @RequestParam String rejectReason) throws IOException, InterruptedException {
boolean result = copyrightApplyService.reject(id, rejectReason);
return ResponseModel.success(result);
}
......
package com.fzm.admin.controller;
import com.fzm.common.annotation.Authentication;
import com.fzm.common.entity.vo.PaymentVo;
import com.fzm.common.model.ResponseModel;
import com.fzm.common.service.PaymentService;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author tangtuo
* @date 2022/2/8 16:11
*/
@Authentication
@RestController
@RequestMapping("/payment")
@Api(tags = "支付流水管理")
public class PaymentController {
@Resource
private PaymentService paymentService;
@GetMapping("/pages")
@ApiOperation(value = "分页查询")
public ResponseModel<PageInfo<PaymentVo>> pages(@ApiParam(value = "页码", required = true) @RequestParam Integer pageNum,
@ApiParam(value = "每页记录数", required = true) @RequestParam Integer pageSize,
@ApiParam(value = "姓名") @RequestParam(required = false) String name,
@ApiParam(value = "注册手机号") @RequestParam(required = false) String telephone,
@ApiParam(value = "订单名") @RequestParam(required = false) String orderName,
@ApiParam(value = "交易类型") @RequestParam(required = false) String type,
@ApiParam(value = "支付场景 1-nft发行 2-版权申请") @RequestParam(required = false) Integer payScene,
@ApiParam(value = "创建开始日期,yyyy-MM-dd格式") @RequestParam(required = false) String start,
@ApiParam(value = "创建截止日期,yyyy-MM-dd格式") @RequestParam(required = false) String end) {
PageInfo<PaymentVo> pageInfo = paymentService.pages(pageNum, pageSize, name, telephone, orderName, type, payScene, start, end);
return ResponseModel.success(pageInfo);
}
}
......@@ -9,14 +9,18 @@ import cn.hutool.json.JSONUtil;
import com.fzm.common.config.RabbitMQConfig;
import com.fzm.common.entity.CopyrightApply;
import com.fzm.common.entity.Nft;
import com.fzm.common.entity.Order;
import com.fzm.common.entity.dto.CopyrightQueryRequest;
import com.fzm.common.entity.dto.CopyrightQueryResponse;
import com.fzm.common.entity.dto.EvidenceHashMessage;
import com.fzm.common.entity.vo.NftVo;
import com.fzm.common.enums.CopyrightApplyState;
import com.fzm.common.enums.PayScene;
import com.fzm.common.properties.CopyrightProperties;
import com.fzm.common.service.CopyrightApplyService;
import com.fzm.common.service.NftService;
import com.fzm.common.service.OrderService;
import com.fzm.common.service.WxPayService;
import com.fzm.common.utils.CopyrightSignUtil;
import com.fzm.common.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
......@@ -67,6 +71,12 @@ public class CopyrightTask {
@Resource
private RabbitTemplate rabbitTemplate;
@Resource
private WxPayService wxPayService;
@Resource
private OrderService orderService;
/**
* 定时任务更新版权申请状态
*/
......@@ -137,8 +147,12 @@ public class CopyrightTask {
EvidenceHashMessage msg = new EvidenceHashMessage(serial_code, hash);
rabbitTemplate.convertAndSend(RabbitMQConfig.COPYRIGHT_DIRECT, "evidence.hash.query", msg);
} else if (registerState.equals(CopyrightApplyState.FAILED.getCode())) {
// 审核失败,更新驳回原因
// 审核失败,更新驳回原因,主动发起退款
copyrightApply.setRejectReason(copyrightResponse.getRemark());
Order order = orderService.getByPaySceneAndProductId(PayScene.COPYRIGHT.getCode(), copyrightApply.getId());
if (order != null) {
wxPayService.refund(order.getId());
}
}
copyrightApplyService.updateById(copyrightApply);
} catch (Exception e) {
......
......@@ -98,7 +98,21 @@ huaweiyun:
wx-pay:
app-id: wxbdddd81913c795e9
app-secret: aa201717c46a0e07c4c143b1ee73229a
mch-id: 1604477044
api-v3-key: D864DA53FEF8ACD41519064967DC10D2
mch-serial-num: 72A62544B0A08A214FAEC780108692EDC6E7D5FA
private-key-path: apiclient_key.pem
\ No newline at end of file
private-key-path: apiclient_key.pem
sms:
app-key: Yiru
app-secret: mx5oaR^RY8!(ziHn
login-message-codetype: quick
login-email-codetype: quick
login-voice-codetype: quick
send-sms-url: http://118.31.52.32/send/sms2
validate-code-url: http://118.31.52.32/validate/code
transfer-nft-message-codetype: notice_transfer
transfer-nft-email-codetype: notice_transfer
transfer-nft-voice-codetype: notice_transfer
refund-message-codetype: notice_refund
\ No newline at end of file
......@@ -99,7 +99,21 @@ huaweiyun:
wx-pay:
app-id: wxbdddd81913c795e9
app-secret: aa201717c46a0e07c4c143b1ee73229a
mch-id: 1604477044
api-v3-key: D864DA53FEF8ACD41519064967DC10D2
mch-serial-num: 72A62544B0A08A214FAEC780108692EDC6E7D5FA
private-key-path: apiclient_key.pem
\ No newline at end of file
private-key-path: apiclient_key.pem
sms:
app-key: Yiru
app-secret: mx5oaR^RY8!(ziHn
login-message-codetype: quick
login-email-codetype: quick
login-voice-codetype: quick
send-sms-url: http://118.31.52.32/send/sms2
validate-code-url: http://118.31.52.32/validate/code
transfer-nft-message-codetype: notice_transfer
transfer-nft-email-codetype: notice_transfer
transfer-nft-voice-codetype: notice_transfer
refund-message-codetype: notice_refund
\ No newline at end of file
......@@ -99,7 +99,21 @@ huaweiyun:
wx-pay:
app-id: wxbdddd81913c795e9
app-secret: aa201717c46a0e07c4c143b1ee73229a
mch-id: 1604477044
api-v3-key: D864DA53FEF8ACD41519064967DC10D2
mch-serial-num: 72A62544B0A08A214FAEC780108692EDC6E7D5FA
private-key-path: apiclient_key.pem
\ No newline at end of file
private-key-path: apiclient_key.pem
sms:
app-key: Yiru
app-secret: mx5oaR^RY8!(ziHn
login-message-codetype: quick
login-email-codetype: quick
login-voice-codetype: quick
send-sms-url: http://118.31.52.32/send/sms2
validate-code-url: http://118.31.52.32/validate/code
transfer-nft-message-codetype: notice_transfer
transfer-nft-email-codetype: notice_transfer
transfer-nft-voice-codetype: notice_transfer
refund-message-codetype: notice_refund
\ No newline at end of file
......@@ -99,7 +99,21 @@ huaweiyun:
wx-pay:
app-id: wxbdddd81913c795e9
app-secret: aa201717c46a0e07c4c143b1ee73229a
mch-id: 1604477044
api-v3-key: D864DA53FEF8ACD41519064967DC10D2
mch-serial-num: 72A62544B0A08A214FAEC780108692EDC6E7D5FA
private-key-path: apiclient_key.pem
\ No newline at end of file
private-key-path: apiclient_key.pem
sms:
app-key: Yiru
app-secret: mx5oaR^RY8!(ziHn
login-message-codetype: quick
login-email-codetype: quick
login-voice-codetype: quick
send-sms-url: http://118.31.52.32/send/sms2
validate-code-url: http://118.31.52.32/validate/code
transfer-nft-message-codetype: notice_transfer
transfer-nft-email-codetype: notice_transfer
transfer-nft-voice-codetype: notice_transfer
refund-message-codetype: notice_refund
\ No newline at end of file
spring:
profiles:
active: local
active: nj
application:
name: ly-admin
......
......@@ -36,6 +36,9 @@ public class Refund {
@ApiModelProperty("通知参数")
private String content;
@ApiModelProperty("退款发起渠道 1-用户发起 2-后台运营发起")
private Integer channel;
private Date createDate;
private Date updateDate;
......
package com.fzm.common.entity.vo;
import com.fzm.common.entity.Order;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* @author tangtuo
* @date 2022/2/8 17:45
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class PaymentVo extends Order {
@ApiModelProperty("交易类型 1-支付 2-退款")
private String type;
@ApiModelProperty("交易时间")
private Date tradeTime;
@ApiModelProperty("姓名")
private String name;
@ApiModelProperty("手机号")
private String telephone;
}
package com.fzm.common.mapper;
import cn.hutool.core.date.DateTime;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fzm.common.entity.Payment;
import com.fzm.common.entity.vo.PaymentVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author tangtuo
......@@ -10,4 +15,7 @@ import org.apache.ibatis.annotations.Mapper;
*/
@Mapper
public interface PaymentMapper extends BaseMapper<Payment> {
List<PaymentVo> list(@Param("name") String name, @Param("telephone") String telephone, @Param("orderName") String orderName, @Param("type") String type, @Param("payScene") Integer payScene, @Param("startDate") DateTime startDate, @Param("endDate") DateTime endDate);
}
......@@ -44,5 +44,8 @@ public class SmsProperties {
@ApiModelProperty("转让nft语言验证码模板")
private String transferNftVoiceCodetype;
@ApiModelProperty("退款的短信模板")
private String refundMessageCodetype;
}
......@@ -8,6 +8,7 @@ import com.fzm.common.entity.vo.CopyrightCertificateVo;
import com.fzm.common.entity.vo.CopyrightVo;
import com.github.pagehelper.PageInfo;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
......@@ -40,7 +41,7 @@ public interface CopyrightApplyService extends IService<CopyrightApply> {
* @param id
* @return
*/
Boolean withdraw(Integer id);
Boolean withdraw(Integer id) throws IOException, InterruptedException;
/**
......@@ -87,7 +88,7 @@ public interface CopyrightApplyService extends IService<CopyrightApply> {
* @param rejectReason
* @return
*/
boolean reject(Integer id, String rejectReason);
boolean reject(Integer id, String rejectReason) throws IOException, InterruptedException;
/**
* 审核通过
......
......@@ -22,7 +22,7 @@ public interface OrderService extends IService<Order> {
* @param productId
* @return
*/
Order getByPaySceneAndProductId(Integer payScene, Integer productId, Integer orderStatus);
Order getByPaySceneAndProductId(Integer payScene, Integer productId);
/**
* 更新订单状态
......@@ -49,11 +49,11 @@ public interface OrderService extends IService<Order> {
/**
* 取消订单
*
* @param orderId
* @param orderStatus
* @param orderId
* @return
*/
Boolean cancel(Long orderId);
Boolean cancel(Long orderId, OrderStatus orderStatus);
/**
* 获取我的订单
......@@ -72,4 +72,12 @@ public interface OrderService extends IService<Order> {
* @return
*/
Boolean renewOrderStatus(Long orderId) throws IOException, InterruptedException;
/**
* 查询超时的订单
*
* @param hours
* @return
*/
List<Order> getTimeOutOrders(int hours);
}
......@@ -2,6 +2,8 @@ package com.fzm.common.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.fzm.common.entity.Payment;
import com.fzm.common.entity.vo.PaymentVo;
import com.github.pagehelper.PageInfo;
/**
* @author tangtuo
......@@ -10,4 +12,20 @@ import com.fzm.common.entity.Payment;
public interface PaymentService extends IService<Payment> {
Payment getByOrderId(Long orderId);
/**
* 分页查询
*
* @param pageNum
* @param pageSize
* @param name
* @param telephone
* @param orderName
* @param type
* @param payScene
* @param start
* @param end
* @return
*/
PageInfo<PaymentVo> pages(Integer pageNum, Integer pageSize, String name, String telephone, String orderName, String type, Integer payScene, String start, String end);
}
......@@ -8,4 +8,12 @@ import com.fzm.common.entity.Refund;
* @date 2022/1/24 14:26
*/
public interface RefundService extends IService<Refund> {
/**
* 根据订单id查询
*
* @param orderId
* @return
*/
Refund getByOrderId(Long orderId);
}
package com.fzm.common.service;
/**
* @author tangtuo
* @date 2022/2/8 14:12
*/
public interface SmsService {
void sendRefundSms(Long orderId);
}
......@@ -42,7 +42,7 @@ public interface WxPayService {
*
* @param orderId
*/
void refund(Long orderId) throws IOException;
void refund(Long orderId) throws IOException, InterruptedException;
/**
* 退款回调
......
......@@ -18,6 +18,7 @@ import com.fzm.common.entity.vo.CopyrightApplyVo;
import com.fzm.common.entity.vo.CopyrightCertificateVo;
import com.fzm.common.entity.vo.CopyrightVo;
import com.fzm.common.enums.CopyrightApplyState;
import com.fzm.common.enums.PayScene;
import com.fzm.common.enums.ResultCode;
import com.fzm.common.exception.GlobalException;
import com.fzm.common.mapper.CopyrightApplyMapper;
......@@ -37,6 +38,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
......@@ -99,6 +101,12 @@ public class CopyrightApplyServiceImpl extends ServiceImpl<CopyrightApplyMapper,
@Resource
private DraftService draftService;
@Resource
private OrderService orderService;
@Resource
private WxPayService wxPayService;
@Override
public Integer submit(CopyrightDTO copyrightDTO) {
......@@ -168,7 +176,7 @@ public class CopyrightApplyServiceImpl extends ServiceImpl<CopyrightApplyMapper,
}
@Override
public Boolean withdraw(Integer id) {
public Boolean withdraw(Integer id) throws IOException, InterruptedException {
CopyrightApply copyright = getById(id);
if (copyright == null) {
throw GlobalException.newException(ResultCode.DATA_ERROR, "此版权登记记录不存在,请核对后重试");
......@@ -177,7 +185,13 @@ public class CopyrightApplyServiceImpl extends ServiceImpl<CopyrightApplyMapper,
throw GlobalException.newException(ResultCode.OPERATION_FAILED, "此版权申请记录已提交,无法撤回");
}
copyright.setRegisterState(CopyrightApplyState.WITHDRAW.getCode());
return updateById(copyright);
updateById(copyright);
// 用户撤回后,需要主动退款
Order order = orderService.getByPaySceneAndProductId(PayScene.COPYRIGHT.getCode(), id);
if (order != null) {
wxPayService.refund(order.getId());
}
return true;
}
......@@ -187,8 +201,9 @@ public class CopyrightApplyServiceImpl extends ServiceImpl<CopyrightApplyMapper,
if (copyright == null) {
throw GlobalException.newException(ResultCode.DATA_ERROR, "此版权登记记录不存在,请核对后重试");
}
// 只有撤回、驳回和登记失败的才能删除
// 只有待支付、撤回、驳回和登记失败的才能删除
if (CopyrightApplyState.WITHDRAW.getCode() != copyright.getRegisterState() &&
CopyrightApplyState.TO_BE_PAY.getCode() != copyright.getRegisterState() &&
CopyrightApplyState.REJECTED.getCode() != copyright.getRegisterState() &&
CopyrightApplyState.FAILED.getCode() != copyright.getRegisterState()) {
throw GlobalException.newException(ResultCode.OPERATION_FAILED, "当前版权记录不能被删除");
......@@ -325,7 +340,7 @@ public class CopyrightApplyServiceImpl extends ServiceImpl<CopyrightApplyMapper,
}
@Override
public boolean reject(Integer id, String rejectReason) {
public boolean reject(Integer id, String rejectReason) throws IOException, InterruptedException {
CopyrightApply copyright = getById(id);
if (copyright == null) {
throw GlobalException.newException(ResultCode.DATA_ERROR, "此版权登记记录不存在,请核对后重试");
......@@ -335,7 +350,13 @@ public class CopyrightApplyServiceImpl extends ServiceImpl<CopyrightApplyMapper,
}
copyright.setRegisterState(CopyrightApplyState.REJECTED.getCode());
copyright.setRejectReason(rejectReason);
return updateById(copyright);
updateById(copyright);
// 管理员驳回后,需要自动发起退款
Order order = orderService.getByPaySceneAndProductId(PayScene.COPYRIGHT.getCode(), id);
if (order != null) {
wxPayService.refund(order.getId());
}
return true;
}
@Override
......
......@@ -22,7 +22,8 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
/**
......@@ -55,11 +56,11 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
private WxPayService wxPayService;
@Override
public Order getByPaySceneAndProductId(Integer payScene, Integer productId, Integer orderStatus) {
public Order getByPaySceneAndProductId(Integer payScene, Integer productId) {
QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("pay_scene", payScene)
.eq("product_id", productId)
.eq("order_status", orderStatus);
.eq("order_status", OrderStatus.PAYED.getStatus());
return this.getOne(queryWrapper);
}
......@@ -112,7 +113,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
}
@Override
public Boolean cancel(Long orderId) {
public Boolean cancel(Long orderId, OrderStatus orderStatus) {
Order order = this.getById(orderId);
Integer productId = order.getProductId();
// 判断订单的类型
......@@ -124,7 +125,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
copyrightApplyService.delete(productId);
}
// 修改订单状态为取消
this.updateOrderStatus(orderId, OrderStatus.CANCEL);
this.updateOrderStatus(orderId, orderStatus);
return true;
}
......@@ -152,4 +153,13 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
}
return true;
}
@Override
public List<Order> getTimeOutOrders(int hours) {
Instant instant = Instant.now().minus(Duration.ofHours(hours));
QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_status", OrderStatus.PAYING.getStatus());
queryWrapper.le("create_date", instant);
return this.list(queryWrapper);
}
}
package com.fzm.common.service.impl;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fzm.common.entity.Payment;
import com.fzm.common.entity.vo.PaymentVo;
import com.fzm.common.mapper.PaymentMapper;
import com.fzm.common.service.PaymentService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
/**
* @author tangtuo
* @date 2022/1/20 16:40
......@@ -16,10 +25,30 @@ import org.springframework.transaction.annotation.Transactional;
@Transactional(rollbackFor = RuntimeException.class)
public class PaymentServiceImpl extends ServiceImpl<PaymentMapper, Payment> implements PaymentService {
@Resource
private PaymentMapper paymentMapper;
@Override
public Payment getByOrderId(Long orderId) {
QueryWrapper<Payment> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id", orderId);
return getOne(queryWrapper);
}
@Override
public PageInfo<PaymentVo> pages(Integer pageNum, Integer pageSize, String name, String telephone, String orderName, String type, Integer payScene, String start, String end) {
PageHelper.startPage(pageNum, pageSize);
DateTime startDate = null;
DateTime endDate = null;
if (StringUtils.isNotBlank(start)) {
startDate = DateUtil.parse(start + " 00:00:00", "yyyy-MM-dd HH:mm:ss");
}
if (StringUtils.isNotBlank(end)) {
endDate = DateUtil.parse(end + " 23:59:59", "yyyy-MM-dd HH:mm:ss");
}
List<PaymentVo> list = paymentMapper.list(name, telephone, orderName, type, payScene, startDate, endDate);
return new PageInfo<>(list);
}
}
package com.fzm.common.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fzm.common.entity.Refund;
import com.fzm.common.mapper.RefundMapper;
......@@ -14,4 +15,11 @@ import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(rollbackFor = RuntimeException.class)
public class RefundServiceImpl extends ServiceImpl<RefundMapper, Refund> implements RefundService {
@Override
public Refund getByOrderId(Long orderId) {
QueryWrapper<Refund> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id", orderId);
return getOne(queryWrapper);
}
}
package com.fzm.common.service.impl;
import com.fzm.common.entity.Order;
import com.fzm.common.entity.User;
import com.fzm.common.service.OrderService;
import com.fzm.common.service.SmsService;
import com.fzm.common.service.UserService;
import com.fzm.common.utils.SmsUtil;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
/**
* @author tangtuo
* @date 2022/2/8 14:12
*/
@Service
public class SmsServiceImpl implements SmsService {
@Resource
private UserService userService;
@Resource
private SmsUtil smsUtil;
@Resource
private OrderService orderService;
@Override
@Async("threadPoolTaskExecutor")
public void sendRefundSms(Long orderId) {
Order order = orderService.getById(orderId);
User user = userService.getById(order.getUserId());
String fee = BigDecimal.valueOf(order.getFee()).divide(new BigDecimal(100)).toString();
smsUtil.sendRefundSms(user.getTelephone(), fee);
}
}
......@@ -9,12 +9,14 @@ import com.fzm.common.constant.SystemConstant;
import com.fzm.common.entity.Order;
import com.fzm.common.entity.Payment;
import com.fzm.common.entity.Refund;
import com.fzm.common.entity.User;
import com.fzm.common.entity.dto.JsapiPayDto;
import com.fzm.common.entity.dto.OrderProcessMsg;
import com.fzm.common.enums.*;
import com.fzm.common.exception.GlobalException;
import com.fzm.common.properties.WxPayProperties;
import com.fzm.common.service.*;
import com.fzm.common.utils.SmsUtil;
import com.fzm.common.utils.SnowflakeUtil;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
......@@ -37,6 +39,7 @@ import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
......@@ -83,6 +86,9 @@ public class WxPayServiceImpl implements WxPayService {
@Resource
private RefundService refundService;
@Resource
private SmsService smsService;
public Map<String, Object> payJsapi(JsapiPayDto jsapiPayDto) throws Exception {
RLock lock = redisson.getLock("pay-" + jsapiPayDto.getOrderId());
if (!lock.tryLock(10, TimeUnit.SECONDS)) {
......@@ -193,10 +199,10 @@ public class WxPayServiceImpl implements WxPayService {
if (lock.tryLock(10, TimeUnit.SECONDS)) {
Order order = orderService.getById(out_trade_no);
// 如果订单状态不是待支付,直接返回
// if (!OrderStatus.PAYING.getStatus().equals(order.getOrderStatus())) {
// log.warn("当前订单已处理完成, 订单号:==> {}", out_trade_no);
// return;
// }
if (!OrderStatus.PAYING.getStatus().equals(order.getOrderStatus())) {
log.warn("当前订单已处理完成, 订单号:==> {}", out_trade_no);
return;
}
Integer productId = order.getProductId();
// 订单支付成功后,给mq发送一条消息,处理nft发行或版权申请的状态
OrderProcessMsg orderProcessMsg = new OrderProcessMsg(out_trade_no, productId, order.getPayScene());
......@@ -237,45 +243,69 @@ public class WxPayServiceImpl implements WxPayService {
}
@Override
public void refund(Long orderId) throws IOException {
Order order = orderService.getById(orderId);
if (order == null || !order.getOrderStatus().equals(OrderStatus.PAYED.getStatus())) {
throw GlobalException.newException(ResultCode.REFUND_FAILED, "当前订单未支付成功");
public void refund(Long orderId) throws IOException, InterruptedException {
RLock lock = redisson.getLock("refund-" + orderId);
if (!lock.tryLock(10, TimeUnit.SECONDS)) {
throw GlobalException.newException(ResultCode.REFUND_FAILED, "当前订单正在退款中,请勿重复点击");
}
try {
Order order = orderService.getById(orderId);
if (order == null || !order.getOrderStatus().equals(OrderStatus.PAYED.getStatus())) {
throw GlobalException.newException(ResultCode.REFUND_FAILED, "当前订单未支付成功");
}
Refund refund = refundService.getByOrderId(orderId);
Long out_refund_no;
if (refund == null) {
refund = new Refund();
out_refund_no = snowflakeUtil.snowflakeId();
} else {
out_refund_no = refund.getId();
}
String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("out_refund_no", String.valueOf(out_refund_no))
.put("notify_url", wxPayProperties.getRefundNotifyUrl())
.put("out_trade_no", String.valueOf(orderId));
rootNode.putObject("amount")
.put("refund", order.getFee())
.put("total", order.getFee())
.put("currency", "CNY");
String json = objectMapper.writeValueAsString(rootNode);
log.info("退款接口请求参数: {}", json);
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
log.info("退款接口返回参数: {}", bodyAsString);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200 || statusCode == 204) {
// 发起退款成功
JSONObject jsonObject = JSONUtil.parseObj(bodyAsString);
refund.setRefundId(jsonObject.getStr("refund_id"));
refund.setTransactionId(jsonObject.getStr("transaction_id"));
refund.setUserReceivedAccount(jsonObject.getStr("user_received_account"));
refund.setRefundStatus(RefundStatus.REFUNDING.getStatus());
// 修改订单状态为退款中
orderService.updateOrderStatus(orderId, OrderStatus.REFUNDING);
} else {
// 发起退款失败
refund.setRefundStatus(RefundStatus.FAINED.getStatus());
}
// 插入退款信息
refund.setId(out_refund_no);
refund.setFee(order.getFee());
refund.setOrderId(orderId);
refundService.saveOrUpdate(refund);
// 异步发送退款短信通知
smsService.sendRefundSms(orderId);
} finally {
lock.unlock();
}
Long out_refund_no = snowflakeUtil.snowflakeId();
String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("out_refund_no", String.valueOf(out_refund_no))
.put("notify_url", wxPayProperties.getRefundNotifyUrl())
.put("out_trade_no", String.valueOf(orderId));
rootNode.putObject("amount")
.put("refund", order.getFee())
.put("total", order.getFee())
.put("currency", "CNY");
String json = objectMapper.writeValueAsString(rootNode);
log.info("退款接口请求参数: {}", json);
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
log.info("退款接口返回参数: {}", bodyAsString);
JSONObject jsonObject = JSONUtil.parseObj(bodyAsString);
// 修改订单状态为退款中
orderService.updateOrderStatus(orderId, OrderStatus.REFUNDING);
// 插入退款信息
Refund refund = new Refund();
refund.setId(out_refund_no);
refund.setFee(order.getFee());
refund.setOrderId(orderId);
refund.setRefundId(jsonObject.getStr("refund_id"));
refund.setTransactionId(jsonObject.getStr("transaction_id"));
refund.setUserReceivedAccount(jsonObject.getStr("user_received_account"));
refundService.save(refund);
}
@Override
......
......@@ -56,6 +56,8 @@ public class SmsUtil {
}
/**
* 发送短信验证码
*
* @param codetype 模板类型
* @param mobile 手机号
* @return
......@@ -83,6 +85,37 @@ public class SmsUtil {
return true;
}
/**
* 发送退款发起短信通知
*
* @param mobile
* @param fee
* @return
*/
public Boolean sendRefundSms(String mobile, String fee) {
String timestamp = getTimestamp();
HashMap<String, Object> params = new HashMap<>();
params.put("mobile", mobile);
params.put("codetype", smsProperties.getRefundMessageCodetype());
params.put("param", fee);
String paramStr = getStringToSignOfStr(params);
System.out.println(paramStr);
String sign = getSign(smsProperties.getAppKey(), smsProperties.getAppSecret(), paramStr, timestamp);
HttpResponse response = HttpRequest.post(smsProperties.getSendSmsUrl()).
header("FZM-Ca-Timestamp", timestamp).
header("FZM-Ca-AppKey", smsProperties.getAppKey()).
header("FZM-Ca-Signature", sign).form(params).execute();
if (response == null || StringUtils.isBlank(response.body())) {
return false;
}
JSONObject jsonObject = JSONUtil.parseObj(response.body());
if (jsonObject.get("code", Integer.class) != HttpStatus.HTTP_OK) {
throw GlobalException.newException(ResultCode.CODE_ERROR, jsonObject.getStr("message"));
}
return true;
}
/**
* @param codetype 模板类型,和发短信的一样
* @param mobile 手机号
......@@ -114,37 +147,23 @@ public class SmsUtil {
return true;
}
/* public static void main(String[] args) {
public static void main(String[] args) {
String timestamp = getTimestamp();
HashMap<String, Object> params = new HashMap<>();
params.put("mobile", "17620078872");
params.put("codetype", "quick");
params.put("param", "FzmRandom4");
String paramStr = getStringToSignOfStr(params);
System.out.println(paramStr);
String sign = getSign(APP_KEY, APP_SECRET, paramStr, timestamp);
HttpRequest request = HttpRequest.post("http://118.31.52.32/send/sms2").
header("FZM-Ca-Timestamp", timestamp).
header("FZM-Ca-AppKey", APP_KEY).
header("FZM-Ca-OS", "h5").
header("FZM-Ca-Signature", sign).form(params);
System.out.println(request.getUrl());
System.out.println(request.execute().body());
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("t", "sms");
paramMap.put("codetype", "quick");
paramMap.put("code", "8102");
paramMap.put("codetype", "notice_refund");
paramMap.put("guide", "1");
paramMap.put("mobile", "17620078872");
paramMap.put("param", "100");
String str = getStringToSignOfStr(paramMap);
String sign1 = getSign(APP_KEY, APP_SECRET, str, timestamp);
HttpResponse h5 = HttpRequest.post("http://118.31.52.32/validate/code").
String sign1 = getSign("Yiru", "mx5oaR^RY8!(ziHn", str, timestamp);
HttpResponse h5 = HttpRequest.post("http://118.31.52.32/send/sms2").
header("FZM-Ca-Timestamp", timestamp).
header("FZM-Ca-AppKey", APP_KEY).
header("FZM-Ca-AppKey", "Yiru").
header("FZM-Ca-OS", "h5").
header("FZM-Ca-Signature", sign1).form(paramMap).execute();
System.out.println(h5.body());
}*/
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fzm.common.mapper.PaymentMapper">
<select id="list" resultType="com.fzm.common.entity.vo.PaymentVo">
SELECT
t.*,
o.*,
u.telephone,
p.`name`
FROM
(
SELECT
order_id,
update_date as trade_time,
'支付' AS type
FROM
tb_payment UNION ALL
SELECT
order_id,
update_date as trade_time,
'退款' AS type
FROM
tb_refund
WHERE
refund_status = 2
) t
LEFT JOIN tb_order o ON t.order_id = o.id
left join tb_user u on o.user_id = u.id
left join tb_auth_person p on u.id = p.user_id
<where>
<if test="payScene != null">
and o.pay_scene = #{payScene}
</if>
<if test="orderName != null and orderName != ''">
and o.order_name like concat('%',#{orderName},'%')
</if>
<if test="telephone != null and telephone != ''">
and u.telephone = #{telephone}
</if>
<if test="name != null and name != ''">
and p.name = #{name}
</if>
<if test="type != null and type != ''">
and t.type = #{type}
</if>
<if test="startDate != null">
and t.trade_time >= #{startDate}
</if>
<if test="endDate != null">
and t.trade_time &lt;= #{endDate}
</if>
</where>
ORDER BY
t.trade_time DESC
</select>
</mapper>
\ No newline at end of file
......@@ -5,7 +5,9 @@ import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@EnableSwagger2Doc
@EnableCaching
@SpringBootApplication(scanBasePackages = {"com.fzm.portal", "com.fzm.common"})
......
......@@ -19,6 +19,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
......@@ -55,7 +56,7 @@ public class CopyrightApplyController {
@Authentication
@PostMapping(value = "/withdraw")
@ApiOperation(value = "撤回")
public ResponseModel<Boolean> withdraw(@RequestParam Integer id) {
public ResponseModel<Boolean> withdraw(@RequestParam Integer id) throws IOException, InterruptedException {
Boolean result = copyrightApplyService.withdraw(id);
return ResponseModel.success(result);
}
......
package com.fzm.portal.controller;
import com.fzm.common.annotation.Authentication;
import com.fzm.common.entity.Order;
import com.fzm.common.entity.dto.OrderDto;
import com.fzm.common.entity.vo.OrderVo;
import com.fzm.common.enums.OrderStatus;
......@@ -47,7 +46,7 @@ public class OrderController {
@PostMapping("/cancel")
@ApiOperation("取消订单")
public ResponseModel<Boolean> cancelOrder(@RequestParam Long orderId) {
Boolean result = orderService.cancel(orderId);
Boolean result = orderService.cancel(orderId, OrderStatus.CANCEL);
return ResponseModel.success(result);
}
......
......@@ -72,7 +72,7 @@ public class WxPayController {
@GetMapping("/refund/{orderId}")
public ResponseModel<String> refund(@PathVariable Long orderId) throws IOException {
public ResponseModel<String> refund(@PathVariable Long orderId) throws IOException, InterruptedException {
wxPayService.refund(orderId);
return ResponseModel.success("退款成功");
}
......
......@@ -59,8 +59,8 @@ public class NftListener {
* @param msg
*/
@RabbitListener(queues = "nft.publish.queue")
public void listenNftPublish(NftPublishMsg msg) throws IOException {
log.info("收到处理确认nft发行结果的消息: {}", msg);
public void listenNftPublish(NftPublishMsg msg) throws Exception {
log.info("收到确认nft发行结果的消息: {}", msg);
Nft nft = nftService.getById(msg.getId());
try {
User user = userService.getUserByWallet(nft.getPublishAddress());
......@@ -90,7 +90,7 @@ public class NftListener {
// nft发行失败,需要把nft的发行状态改成failed,然后主动发起退款
nft.setPublishStatus(PublishStatus.FAILED.getCode());
nftService.updateById(nft);
Order order = orderService.getByPaySceneAndProductId(PayScene.NFT.getCode(), nft.getId(), OrderStatus.PAYED.getStatus());
Order order = orderService.getByPaySceneAndProductId(PayScene.NFT.getCode(), nft.getId());
if (order != null) {
wxPayService.refund(order.getId());
}
......
......@@ -31,7 +31,7 @@ public class OrderListener {
private WxPayService wxPayService;
@RabbitListener(queues = "order.process.queue")
public void listenProcessOrder(OrderProcessMsg msg) throws IOException {
public void listenProcessOrder(OrderProcessMsg msg) throws Exception {
log.info("收到处理订单的消息: {}", msg);
try {
if (PayScene.NFT.getCode().equals(msg.getPayScene())) {
......
package com.fzm.portal.schedule;
import cn.hutool.core.collection.CollectionUtil;
import com.fzm.common.entity.Order;
import com.fzm.common.enums.OrderStatus;
import com.fzm.common.enums.WxPayStatus;
import com.fzm.common.service.OrderService;
import com.fzm.common.service.RefundService;
import com.fzm.common.service.WxPayService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author tangtuo
* @date 2022/2/8 15:31
*/
@Slf4j
@Component
public class PayTask {
@Resource
private OrderService orderService;
@Resource
private WxPayService wxPayService;
@Resource
private Redisson redisson;
@Resource
private RefundService refundService;
/**
* 定时关单的任务
*/
@Scheduled(cron = "0 */5 * * * ?")
public void closeOrder() throws InterruptedException, IOException {
RLock lock = redisson.getLock("close-order");
// 加锁,避免集群环境下多个节点同事运行此定时任务
if (!lock.tryLock(30, TimeUnit.SECONDS)) {
log.warn("此任务正在运行中");
return;
}
// 查询超时(创建后超过24小时未支付)的订单
List<Order> orderList = orderService.getTimeOutOrders(24);
if (CollectionUtil.isEmpty(orderList)) {
return;
}
for (Order order : orderList) {
// 确认订单状态
String orderStatus = wxPayService.queryOrder(order.getId());
if (WxPayStatus.NOTPAY.getStatus().equals(orderStatus)) {
// 订单未支付,则关闭订单
orderService.cancel(order.getId(), OrderStatus.CLOSED);
}
}
}
}
......@@ -86,6 +86,7 @@ sms:
transfer-nft-message-codetype: notice_transfer
transfer-nft-email-codetype: notice_transfer
transfer-nft-voice-codetype: notice_transfer
refund-message-codetype: notice_refund
chain:
para:
......
......@@ -86,6 +86,7 @@ sms:
transfer-nft-message-codetype: notice_transfer
transfer-nft-email-codetype: notice_transfer
transfer-nft-voice-codetype: notice_transfer
refund-message-codetype: notice_refund
chain:
para:
......
......@@ -87,6 +87,7 @@ sms:
transfer-nft-message-codetype: notice_transfer
transfer-nft-email-codetype: notice_transfer
transfer-nft-voice-codetype: notice_transfer
refund-message-codetype: notice_refund
#chain:
# para:
......
......@@ -88,6 +88,7 @@ sms:
transfer-nft-message-codetype: notice_transfer
transfer-nft-email-codetype: notice_transfer
transfer-nft-voice-codetype: notice_transfer
refund-message-codetype: notice_refund
chain:
para:
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment