加入积分系统,将充值与积分关联

This commit is contained in:
2024-03-06 20:56:22 +08:00
parent ee96759832
commit e84d800ba0
16 changed files with 234 additions and 34 deletions

View File

@@ -0,0 +1,30 @@
package com.ai.da.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum CreditsEventsEnum {
PRICE("price","2"),
INIT("init", "1000"),
DAILY_CHECKIN("Daily Check-In", "50"),
SOCIAL_MEDIA_SHARING("Social Media Sharing","50"),
BUY_CREDITS("Buy Credits","2000"),
SUPER_RESOLUTION("Super Resolution","300"),
OTHER("Other","10");
private String name;
/**
* 对应事件需要消耗or获得的积分
*/
private String value;
}

View File

@@ -21,12 +21,12 @@ public class AliPayController {
private AliPayService aliPayService;
@ApiOperation("统一收单下单并支付页面接口的调用")
@PostMapping("/trade/page/pay/{productId}")
public Response<String> tradePagePay(@PathVariable Long productId,@RequestParam String returnUrl){
@PostMapping("/trade/page/pay/{amount}")
public Response<String> tradePagePay(@PathVariable Integer amount, @RequestParam String returnUrl){
log.info("统一收单下单并支付页面接口的调用");
//支付宝开放平台接受 request 请求对象后
// 会为开发者生成一个html 形式的 form表单包含自动提交的脚本
String formStr = aliPayService.tradeCreate(productId, returnUrl);
String formStr = aliPayService.tradeCreate(amount, returnUrl);
//我们将form表单字符串返回给前端程序之后前端将会调用自动提交脚本进行表单的提交
//此时表单会自动提交到action属性所指向的支付宝开放平台中从而为用户展示一个支付页面
return Response.success(formStr);

View File

@@ -0,0 +1,28 @@
package com.ai.da.controller;
import com.ai.da.common.response.Response;
import com.ai.da.service.CreditsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@CrossOrigin
@RestController
@RequestMapping("/api/credits")
@Api(tags = "积分")
@Slf4j
public class CreditsController {
@Resource
private CreditsService creditsService;
@ApiOperation("获取积分")
@GetMapping("/getCredits")
public Response<String> getCredits(){
String credits = creditsService.getCredits();
return Response.success(credits);
}
}

View File

@@ -30,9 +30,9 @@ public class PayPalCheckoutController {
private CallBackService callBackService;
@ApiOperation(value = "创建订单")
@PostMapping(value = "/trade/{productId}")
public Response<HashMap<String, String>> createOrder(@PathVariable Long productId,@RequestParam String returnUrl) throws SerializeException {
HashMap<String, String> approvalUrl = payPalCheckoutService.createOrder(productId,returnUrl);
@PostMapping(value = "/trade/{amount}")
public Response<HashMap<String, String>> createOrder(@PathVariable Integer amount, @RequestParam String returnUrl) throws SerializeException {
HashMap<String, String> approvalUrl = payPalCheckoutService.createOrder(amount,returnUrl);
return Response.success(approvalUrl);
}
@@ -41,7 +41,7 @@ public class PayPalCheckoutController {
public Response<String> callback(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Boolean result = callBackService.doGet(request, response);
if (result){
return Response.success();
return Response.success(200,"success");
}else {
return Response.fail(500,"failure");
}

View File

@@ -8,6 +8,7 @@ import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
@@ -80,4 +81,9 @@ public class Account implements Serializable {
private Integer isBeginner;
private String browserIdentifiers;
/**
* 积分
*/
private BigDecimal credits;
}

View File

@@ -128,4 +128,6 @@ public interface AccountService extends IService<Account> {
void upgradeNotification();
void moveLibraryDate();
void updateCredits(Long accountId, String value);
}

View File

@@ -3,7 +3,7 @@ package com.ai.da.service;
import java.util.Map;
public interface AliPayService {
String tradeCreate(Long productId,String returnUrl);
String tradeCreate(Integer amount,String returnUrl);
String tradeNotify(Map<String, String> params);

View File

@@ -0,0 +1,16 @@
package com.ai.da.service;
public interface CreditsService {
void initCredits();
Boolean buyCredits(Long accountId, Integer quantity);
void creditsIncrease(Long accountId, String event);
void creditsDecrease(Long accountId, String event);
String getCredits();
void creditsRefund(Long accountId, Integer quantity);
}

View File

@@ -9,7 +9,7 @@ import java.util.List;
public interface OrderInfoService extends IService<OrderInfo> {
OrderInfo createOrderByProductId(Long productId, String paymentType);
OrderInfo createOrderByProductId(Integer productId, String paymentType);
void saveCodeUrl(String orderNo, String codeUrl);

View File

@@ -9,7 +9,7 @@ import java.util.Map;
public interface PayPalCheckoutService {
HashMap<String, String> createOrder(Long productId,String returnUrl) throws SerializeException;
HashMap<String, String> createOrder(Integer amount,String returnUrl) throws SerializeException;
/**
* 回调
* @param map

View File

@@ -36,6 +36,7 @@ import org.springframework.util.Assert;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
@@ -885,4 +886,11 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
// 未迁移过的进行迁移,注意模特数据迁移打点信息以及转换模特格式
}
public void updateCredits(Long accountId, String value){
Account account = new Account();
account.setId(accountId);
account.setCredits(new BigDecimal(value));
accountMapper.updateById(account);
}
}

View File

@@ -2,14 +2,12 @@ package com.ai.da.service.impl;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.enums.AliPayTradeStateEnum;
import com.ai.da.common.enums.CreditsEventsEnum;
import com.ai.da.common.enums.OrderStatusEnum;
import com.ai.da.common.enums.PayTypeEnum;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.mapper.primary.entity.RefundInfo;
import com.ai.da.service.AliPayService;
import com.ai.da.service.OrderInfoService;
import com.ai.da.service.PaymentInfoService;
import com.ai.da.service.RefundInfoService;
import com.ai.da.service.*;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
@@ -49,16 +47,19 @@ public class AliPayServiceImpl implements AliPayService {
@Resource
private RefundInfoService refundsInfoService;
@Resource
private CreditsService creditsService;
private final ReentrantLock lock = new ReentrantLock();
@Transactional(rollbackFor = Exception.class)
@Override
public String tradeCreate(Long productId, String returnUrl) {
public String tradeCreate(Integer amount, String returnUrl) {
try {
//生成订单
log.info("生成订单");
OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId, PayTypeEnum.ALIPAY.getType());
OrderInfo orderInfo = orderInfoService.createOrderByProductId(amount, PayTypeEnum.ALIPAY.getType());
//调用支付宝接口
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
@@ -74,7 +75,8 @@ public class AliPayServiceImpl implements AliPayService {
bizContent.put("out_trade_no", orderInfo.getOrderNo());
BigDecimal total = new BigDecimal(orderInfo.getTotalFee().toString());
bizContent.put("total_amount", total);
bizContent.put("subject", orderInfo.getTitle());
// bizContent.put("subject", orderInfo.getTitle());
bizContent.put("subject", "积分购买");
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
request.setBizContent(bizContent.toString());
@@ -186,6 +188,7 @@ public class AliPayServiceImpl implements AliPayService {
//获取订单号
String orderNo = params.get("out_trade_no");
String totalAmount = params.get("total_amount");
/*在对业务数据进行状态检查和处理之前,
要采用数据锁进行并发控制,
@@ -196,7 +199,9 @@ public class AliPayServiceImpl implements AliPayService {
try {
//处理重复通知
//接口调用的幂等性:无论接口被调用多少次,以下业务执行一次
String orderStatus = orderInfoService.getOrderStatus(orderNo);
// String orderStatus = orderInfoService.getOrderStatus(orderNo);
OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderNo);
String orderStatus = orderByOrderNo.getOrderStatus();
// 当订单状态处于未支付或超时已关闭时,更新订单状态
if (!OrderStatusEnum.NOT_PAY.getType().equals(orderStatus) || !OrderStatusEnum.TIMEOUT_CLOSED.getType().equals(orderStatus)) {
return;
@@ -205,6 +210,8 @@ public class AliPayServiceImpl implements AliPayService {
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
//记录支付日志
paymentInfoService.createPaymentInfoForAliPay(params);
// 更新积分
creditsService.buyCredits(orderByOrderNo.getAccountId(),Integer.parseInt(totalAmount) / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
} finally {
//要主动释放锁
lock.unlock();
@@ -285,6 +292,7 @@ public class AliPayServiceImpl implements AliPayService {
LinkedTreeMap alipayTradeQueryResponse = resultMap.get("alipay_trade_query_response");
String tradeStatus = (String)alipayTradeQueryResponse.get("trade_status");
OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderNo);
if(AliPayTradeStateEnum.NOTPAY.getType().equals(tradeStatus)){
log.warn("核实订单未支付 ===> {}", orderNo);
//如果订单未支付,则调用关单接口关闭订单
@@ -299,6 +307,8 @@ public class AliPayServiceImpl implements AliPayService {
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
//并记录支付日志
paymentInfoService.createPaymentInfoForAliPay(alipayTradeQueryResponse);
// 更新积分
creditsService.buyCredits(orderByOrderNo.getAccountId(),orderByOrderNo.getTotalFee() / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
}
}
@@ -368,6 +378,9 @@ public class AliPayServiceImpl implements AliPayService {
refundInfo.getRefundNo(),
response.getBody(),
AliPayTradeStateEnum.REFUND_SUCCESS.getType()); //退款成功
// 更新积分状态
OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderNo);
creditsService.creditsRefund(orderByOrderNo.getAccountId(), orderByOrderNo.getTotalFee() / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
} else {
log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> " + response.getMsg());

View File

@@ -0,0 +1,91 @@
package com.ai.da.service.impl;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.CreditsEventsEnum;
import com.ai.da.mapper.primary.AccountMapper;
import com.ai.da.mapper.primary.entity.Account;
import com.ai.da.service.AccountService;
import com.ai.da.service.CreditsService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
@Service
public class CreditsServiceImpl implements CreditsService {
@Resource
private AccountService accountService;
@Resource
private AccountMapper accountMapper;
@Override
public void initCredits() {
accountService.updateCredits(UserContext.getUserHolder().getId(), CreditsEventsEnum.INIT.getValue());
}
@Override
public Boolean buyCredits(Long accountId, Integer quantity) {
BigDecimal existingCredits = accountMapper.selectById(accountId).getCredits();
BigDecimal newCredits = new BigDecimal(CreditsEventsEnum.BUY_CREDITS.getValue()).multiply(new BigDecimal(quantity));
BigDecimal added = existingCredits.add(newCredits);
accountService.updateCredits(accountId, added.toString());
return Boolean.TRUE;
}
@Override
public void creditsIncrease(Long accountId, String creditsEvent) {
CreditsEventsEnum event = null;
switch (creditsEvent){
case "Daily Check-In":
event = CreditsEventsEnum.DAILY_CHECKIN;
break;
case "Social Media Sharing":
event = CreditsEventsEnum.SOCIAL_MEDIA_SHARING;
break;
case "Other":
event = CreditsEventsEnum.OTHER;
break;
default:
throw new BusinessException("UNKNOWN TYPE");
}
BigDecimal existingCredits = accountMapper.selectById(UserContext.getUserHolder().getId()).getCredits();
BigDecimal add = new BigDecimal(event.getValue()).add(existingCredits);
accountService.updateCredits(UserContext.getUserHolder().getId(), add.toString());
}
@Override
public void creditsDecrease(Long accountId, String creditsEvent) {
CreditsEventsEnum event = null;
switch (creditsEvent){
case "Super Resolution":
event = CreditsEventsEnum.DAILY_CHECKIN;
break;
case "Other":
event = CreditsEventsEnum.OTHER;
break;
default:
throw new BusinessException("UNKNOWN TYPE");
}
BigDecimal existingCredits = accountMapper.selectById(UserContext.getUserHolder().getId()).getCredits();
BigDecimal subtract = existingCredits.subtract(new BigDecimal(event.getValue()));
accountService.updateCredits(UserContext.getUserHolder().getId(), subtract.toString());
}
@Override
public String getCredits() {
Account account = accountMapper.selectById(UserContext.getUserHolder().getId());
return account.getCredits().toString();
}
public void creditsRefund(Long accountId, Integer quantity){
BigDecimal existingCredits = accountMapper.selectById(accountId).getCredits();
BigDecimal newCredits = new BigDecimal(CreditsEventsEnum.BUY_CREDITS.getValue()).multiply(new BigDecimal(quantity));
BigDecimal subtracted = existingCredits.subtract(newCredits);
accountService.updateCredits(accountId, subtracted.toString());
}
}

View File

@@ -2,12 +2,12 @@ package com.ai.da.service.impl;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.CreditsEventsEnum;
import com.ai.da.common.enums.OrderStatusEnum;
import com.ai.da.common.utils.OrderNoUtils;
import com.ai.da.mapper.primary.OrderInfoMapper;
import com.ai.da.mapper.primary.ProductMapper;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.mapper.primary.entity.Product;
import com.ai.da.model.vo.AuthPrincipalVo;
import com.ai.da.service.OrderInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -28,26 +28,26 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
private ProductMapper productMapper;
@Override
public OrderInfo createOrderByProductId(Long productId, String paymentType) {
public OrderInfo createOrderByProductId(Integer amount, String paymentType) {
//查找已存在但未支付的订单
OrderInfo orderInfo = this.getNoPayOrderByProductId(productId, paymentType);
/*OrderInfo orderInfo = this.getNoPayOrderByProductId(amount, paymentType);
if( orderInfo != null){
return orderInfo;
}
}*/
//获取商品信息
Product product = productMapper.selectById(productId);
// Product product = productMapper.selectById(amount);
AuthPrincipalVo userHolder = UserContext.getUserHolder();
Long accountId = userHolder.getId();
//生成订单
orderInfo = new OrderInfo();
OrderInfo orderInfo = new OrderInfo();
orderInfo.setAccountId(accountId);
orderInfo.setTitle(product.getTitle());
orderInfo.setTitle("积分购买 X" + amount );
orderInfo.setOrderNo(OrderNoUtils.getOrderNo()); //订单号 ??
orderInfo.setProductId(productId);
orderInfo.setTotalFee(product.getPrice()); // 元
// orderInfo.setProductId(amount);
orderInfo.setTotalFee(Integer.parseInt(CreditsEventsEnum.PRICE.getValue()) * amount); // 元 HKD
orderInfo.setOrderStatus(OrderStatusEnum.NOT_PAY.getType()); //未支付
orderInfo.setPaymentType(paymentType);
baseMapper.insert(orderInfo);

View File

@@ -9,10 +9,7 @@ import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.utils.paypalRequest.AuthenticationRequest;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.mapper.primary.entity.RefundInfo;
import com.ai.da.service.OrderInfoService;
import com.ai.da.service.PayPalCheckoutService;
import com.ai.da.service.PaymentInfoService;
import com.ai.da.service.RefundInfoService;
import com.ai.da.service.*;
import com.google.gson.Gson;
import com.paypal.http.HttpResponse;
import com.paypal.http.exceptions.SerializeException;
@@ -60,6 +57,9 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
@Resource
private RefundInfoService refundsInfoService;
@Resource
private CreditsService creditsService;
@Resource
private RedisUtil redisUtil;
@@ -67,10 +67,10 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
* 创建订单的方法
*/
@Override
public HashMap<String, String> createOrder(Long productId,String returnUrl) throws SerializeException {
public HashMap<String, String> createOrder(Integer amount, String returnUrl) throws SerializeException {
// 生成订单
log.info("生成订单");
OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId, PayTypeEnum.PAYPAL.getType());
OrderInfo orderInfo = orderInfoService.createOrderByProductId(amount, PayTypeEnum.PAYPAL.getType());
OrdersCreateRequest request = new OrdersCreateRequest();
request.header("prefer","return=representation");
@@ -372,6 +372,10 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
response.result().id(),
new Gson().toJson(response.result(), com.paypal.payments.Refund.class),
AliPayTradeStateEnum.REFUND_SUCCESS.getType()); //退款成功
// 更新积分状态
OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderId);
creditsService.creditsRefund(orderByOrderNo.getAccountId(), orderByOrderNo.getTotalFee() / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
log.info("退款成功");
result = Boolean.TRUE;
}else {
@@ -469,6 +473,8 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.SUCCESS);
//记录支付日志
paymentInfoService.createPaymentInfoForPayPal(capturedOrder);
// 更新积分
creditsService.buyCredits(orderInfo.getAccountId(), orderInfo.getTotalFee() / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
}
}
}