Merge remote-tracking branch 'origin/dev/dev' into dev/dev_shb

This commit is contained in:
shahaibo
2024-03-27 13:31:08 +08:00
71 changed files with 2960 additions and 232 deletions

View File

@@ -22,7 +22,7 @@ import java.util.Objects;
@Slf4j
@Component
public class MQConsumer {
public class GenerateConsumer {
@Resource
private GenerateService generateService;
@@ -30,13 +30,13 @@ public class MQConsumer {
@Resource
private RedisUtil redisUtil;
@Value("${redis.key.consumptionOrder}")
@Value("${redis.key.orderForGenerate}")
private String consumptionOrderKey;
@Value("${redis.key.cancelSet}")
@Value("${redis.key.generateCancelSet}")
private String cancelSetKey;
@Value("${redis.key.exceptionMap}")
@Value("${redis.key.generateExceptionMap}")
private String exceptionMapKey;
@Value("${redis.key.resultMap}")

View File

@@ -1,24 +1,23 @@
package com.ai.da.common.RabbitMQ;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
@Configuration
public class MQConfig {
public static final String GENERATE_EXCHANGE_FANOUT = "generate-exchange";
// public static final String GENERATE_QUEUE = "generate-queue-prod";
// public static final String GENERATE_QUEUE = "generate-queue-prod";
// public static final String GENERATE_QUEUE = "generate-queue-test";
public static final String GENERATE_QUEUE = "generate-queue-dev";
// public static final String GENERATE_QUEUE = "generate-queue";
public static final String SR_QUEUE = "SR-queue-dev";
// public static final String SR_QUEUE = "SR-queue-local";
// public static final String SR_RESULT_QUEUE = "SuperResolution-local";
public static final String SR_RESULT_QUEUE = "SuperResolution-dev";
public MQConfig() {
}
@@ -32,10 +31,20 @@ public class MQConfig {
* 创建队列,使用工作模式,不用定义交换机
*/
@Bean
public Queue queueRasa() {
public Queue generateQueue() {
return new Queue(GENERATE_QUEUE);
}
@Bean
public Queue SRQueue() {
return new Queue(SR_QUEUE);
}
@Bean
public Queue SRResultQueue() {
return new Queue(SR_RESULT_QUEUE);
}
/**
* 将队列绑定到交换机上【队列订阅交换机】
*/

View File

@@ -18,7 +18,11 @@ public class MQPublisher {
public void sendGenerateMessage(String mm) {
log.info("send message:" + mm);
amqpTemplate.convertAndSend(MQConfig.GENERATE_QUEUE, mm);
}
public void sendSRMessage(String mm) {
log.info("send message:" + mm);
amqpTemplate.convertAndSend(MQConfig.SR_QUEUE, mm);
}
}

View File

@@ -0,0 +1,203 @@
package com.ai.da.common.RabbitMQ;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.model.dto.SuperResolutionDTO;
import com.ai.da.model.dto.TaskDTO;
import com.ai.da.service.SuperResolutionService;
import com.ai.da.service.TaskListService;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.HashMap;
@Slf4j
@Component
public class SRConsumer {
@Resource
private RedisUtil redisUtil;
@Resource
private TaskListService taskListService;
@Value("${redis.key.orderForSR}")
private String consumptionOrderKey;
@Value("${redis.key.SRCancelSet}")
private String cancelSetKey;
@Value("${redis.key.SRExceptionMap}")
private String exceptionMapKey;
@Value("${redis.key.taskList}")
private String taskListKey;
@Resource
private SuperResolutionService superResolutionService;
/**
* 请求超分处理
*/
public void superResolution(Message msg, Channel channel, String consumerName) {
log.info("============SR start listening==========");
long start = System.currentTimeMillis();
SuperResolutionDTO superResolutionDTO;
String uniqueId = null;
try {
superResolutionDTO = JSONObject.parseObject(msg.getBody(), SuperResolutionDTO.class);
uniqueId = superResolutionDTO.getUniqueId();
log.info("From " + consumerName + " : " + uniqueId);
superResolutionService.updateSROutput(uniqueId, "Executing", null);
taskListService.updateTaskStatusOrOutputRedis(uniqueId, "Executing", null);
/*try {
Thread.sleep(2 * 60 * 1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}*/
// 2、判断当前消息是否在取消列表中
Boolean isMember = redisUtil.isElementExistsInSet(cancelSetKey, uniqueId);
if (isMember) {
try {
// 2.1 手动确认该消息
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
} catch (IOException ex) {
log.error("手动确认,不返回队列重新消费");
}
} else {
// 请求python端进行超分
superResolutionService.SR(superResolutionDTO);
}
} catch (BusinessException e) {
log.error(e.getMsg());
superResolutionDTO = JSONObject.parseObject(msg.getBody(), SuperResolutionDTO.class);
// channel.basicNack() 为不确认deliveryTag对应的消息第二个参数是否应用于多消息第三个参数是否requeue
setErrorMessage(msg, channel, e.getMsg(), superResolutionDTO);
} catch (JSONException e) {
log.error(e.getMessage());
setErrorMessage(msg, channel, e.getMessage(), null);
} catch (Exception e) {
log.error(e.getMessage());
superResolutionDTO = JSONObject.parseObject(msg.getBody(), SuperResolutionDTO.class);
setErrorMessage(msg, channel, e.getMessage(), superResolutionDTO);
}
long end = System.currentTimeMillis();
log.info(" task_id " + uniqueId + "----------" + consumerName + " 执行时长:" + (end - start) + "毫秒");
log.info("=============SR end listening===========");
}
/**
* 获取超分结果
*/
public void getSRResult(Message msg, Channel channel, String consumerName) {
log.info("============SRResult start listening==========");
long start = System.currentTimeMillis();
JSONObject result = null;
String taskId = null;
try {
result = JSONObject.parseObject(msg.getBody(), JSONObject.class);
taskId = result.get("tasks_id").toString();
} catch (JSONException e) {
log.error("SRResult 返回数据格式不合规范");
log.error(e.getMessage());
setErrorMessage(msg, channel, e.getMessage(), null);
}
try {
// channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
// 2、判断状态是否成功
if ("SUCCESS".equals(result.get("status").toString())) {
String output = result.get("data").toString();
superResolutionService.setSRResult(taskId, output, "success");
taskListService.updateTaskStatusOrOutputRedis(taskId, "success", output);
} else {
superResolutionService.setSRResult(taskId, null, "fail");
taskListService.updateTaskStatusOrOutputRedis(taskId, "fail", null);
HashMap<String, String> exceptionInfo = new HashMap<>();
// 获取输入信息
String task = redisUtil.getFromString(taskListKey + taskId + taskId.substring(taskId.lastIndexOf("-") + 1));
Gson gson = new Gson();
Type type = new TypeToken<TaskDTO<SuperResolutionDTO>>() {
}.getType();
TaskDTO<SuperResolutionDTO> taskDTO = gson.fromJson(task, type);
// 将输入信息和报错信息均存入redis todo 加判空
exceptionInfo.put(taskId, "Input ==> " + taskDTO.getInputParam() + "Fail Message ==> " + result.get("message").toString());
// 将报错信息存入redis
redisUtil.addToMap(exceptionMapKey, exceptionInfo);
}
} catch (Exception e) {
log.error(e.getMessage());
// channel.basicNack() 为不确认deliveryTag对应的消息第二个参数是否应用于多消息第三个参数是否requeue
try {
// 第二个参数是否批量确认消息当传false时只确认当前 deliveryTag对应的消息;当传true时会确认当前及之前所有未确认的消息。
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
} catch (IOException exception) {
log.error("手动确认,取消返回队列,不再重新消费");
}
}
long end = System.currentTimeMillis();
log.info(" task_id " + taskId + "----------" + consumerName + " 执行时长:" + (end - start) + "毫秒");
log.info("=============SRResult end listening===========");
}
private void setErrorMessage(Message msg, Channel channel, String message, SuperResolutionDTO superResolutionDTO) {
String uniqueId;
try {
// 第二个参数是否批量确认消息当传false时只确认当前 deliveryTag对应的消息;当传true时会确认当前及之前所有未确认的消息。
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
uniqueId = superResolutionDTO.getUniqueId();
// 将消息从redis排队队列中删除,需保证被消费的消息存储到db之后再从redis删除
redisUtil.removeFromZSet(consumptionOrderKey, uniqueId);
} catch (Exception exception) {
log.error("手动确认,取消返回队列,不再重新消费");
throw new BusinessException("发生错误,手动确认消息");
}
// 将入参和错误信息存入redis
String exceptionMessage = JSONObject.toJSONString(superResolutionDTO) +
" Exception message " + message;
// " Exception message " + e.getMessage();
HashMap<String, String> exceptionInfo = new HashMap<>();
uniqueId = superResolutionDTO.getUniqueId();
exceptionInfo.put(String.valueOf(uniqueId), exceptionMessage);
// 存redis
redisUtil.addToMap(exceptionMapKey, exceptionInfo);
taskListService.updateTaskStatusOrOutputRedis(uniqueId, "fail", null);
}
@RabbitListener(queues = MQConfig.SR_QUEUE)
@RabbitHandler
public void SRConsumer1(Message msg, Channel channel) {
superResolution(msg, channel, "consumer 1");
}
@RabbitListener(queues = MQConfig.SR_RESULT_QUEUE)
@RabbitHandler
public void SRResultConsumer1(Message msg, Channel channel) {
getSRResult(msg, channel, "consumer 1");
}
}

View File

@@ -0,0 +1,51 @@
package com.ai.da.common.config;
import com.paypal.core.PayPalEnvironment;
import com.paypal.core.PayPalHttpClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import java.util.Iterator;
@Configuration
@Slf4j
@PropertySource("classpath:paypal-sandbox.properties")
public class PayPalClient {
public PayPalHttpClient client(String mode, String clientId, String clientSecret) {
log.info("mode={}, clientId={}, clientSecret={}", mode, clientId, clientSecret);
PayPalEnvironment environment = mode.equals("live") ? new PayPalEnvironment.Live(clientId, clientSecret) : new PayPalEnvironment.Sandbox(clientId, clientSecret);
return new PayPalHttpClient(environment);
}
/**
* @param jo
* @param pre
* @return
*/
/*public String prettyPrint(JSONObject jo, String pre) {
Iterator<?> keys = jo.keys();
StringBuilder pretty = new StringBuilder();
while (keys.hasNext()) {
String key = (String) keys.next();
pretty.append(String.format("%s%s: ", pre, StringUtils.capitalize(key)));
if (jo.get(key) instanceof JSONObject) {
pretty.append(prettyPrint(jo.getJSONObject(key), pre + "\t"));
} else if (jo.get(key) instanceof JSONArray) {
int sno = 1;
for (Object jsonObject : jo.getJSONArray(key)) {
pretty.append(String.format("\n%s\t%d:\n", pre, sno++));
pretty.append(prettyPrint((JSONObject) jsonObject, pre + "\t\t"));
}
} else {
pretty.append(String.format("%s\n", jo.getString(key)));
}
}
return pretty.toString();
}*/
}

View File

@@ -0,0 +1,182 @@
package com.ai.da.common.constant;
public class PayPalCheckoutConstant {
// public static String MODE = "sandbox";
public static String MODE = "live";
public static final String CAPTURE = "CAPTURE";
/**
* 该标签将覆盖PayPal网站上PayPal帐户中的公司名称
*/
public static final String BRANDNAME = "AIDA";
/**
* LOGIN。当客户单击PayPal Checkout时客户将被重定向到页面以登录PayPal并批准付款。
* BILLING。当客户单击PayPal Checkout时客户将被重定向到一个页面以输入信用卡或借记卡以及完成购买所需的其他相关账单信息
* NO_PREFERENCE。当客户单击“ PayPal Checkout”时将根据其先前的交互方式将其重定向到页面以登录PayPal并批准付款或重定向至页面以输入信用卡或借记卡以及完成购买所需的其他相关账单信息使用PayPal。
* 默认值NO_PREFERENCE
*/
public static final String LANDINGPAGE = "NO_PREFERENCE";
/**
* CONTINUE。将客户重定向到PayPal付款页面后将出现“ 继续”按钮。当结帐流程启动时最终金额未知时,请使用此选项,并且您想将客户重定向到商家页面而不处理付款。
* PAY_NOW。将客户重定向到PayPal付款页面后出现“ 立即付款”按钮。当启动结帐时知道最终金额并且您要在客户单击“ 立即付款”时立即处理付款时,请使用此选项。
*/
public static final String USERACTION = "PAY_NOW";
/**
* GET_FROM_FILE。使用贝宝网站上客户提供的送货地址。
* NO_SHIPPING。从PayPal网站编辑送货地址。推荐用于数字商品
* SET_PROVIDED_ADDRESS。使用商家提供的地址。客户无法在PayPal网站上更改此地址
*/
// public static final String SHIPPINGPREFERENCE = "SET_PROVIDED_ADDRESS";
public static final String SHIPPINGPREFERENCE = "NO_SHIPPING";
/**
* 交易异常
*/
public static final String FAILURE = "failure";
/**
* 交易成功
*/
public static final String SUCCESS = "success";
/**
* ipn回调。支付成功
*/
public static final String PAYMENT_STATUS_COMPLETED = "Completed";
/**
* ipn回调。退款成功
*/
public static final String PAYMENT_STATUS_REFUNDED = "Refunded";
/**
* ipn回调。待定
*/
public static final String PAYMENT_STATUS_PENDING = "Pending";
/**
* ipn回调付款因退款或其他类型的冲销而被冲销。资金已从您的帐户余额中删除并退还给买方
*/
public static final String PAYMENT_STATUS_REVERSED = "Reversed";
/**
* ipn回调, 撤销已被取消。例如,您赢得了与客户的纠纷,并且撤回的交易资金已退还给您
*/
public static final String PAYMENT_STATUS_CANCELED_REVERSAL = "Canceled_Reversal";
/**
* ipn回调付款被拒绝
*/
public static final String PAYMENT_STATUS_DENIED = "Denied";
/**
* ipn回调 此授权已过期,无法捕获
*/
public static final String PAYMENT_STATUS_EXPIRED = "Expired";
/**
* ipn回调 德国的ELV付款是通过Express Checkout进行的
*/
public static final String PAYMENT_STATUS_CREATED = "Created";
/**
* ipn回调 付款失败。仅当付款是通过您客户的银行帐户进行的。
*/
public static final String PAYMENT_STATUS_FAILED = "Failed";
/**
* ipn回调付款已被接受
*/
public static final String PAYMENT_STATUS_PROCESSED = "Processed";
/**
* ipn回调此授权已失效
*/
public static final String PAYMENT_STATUS_VOIDED = "Voided";
//订单状态
/**
* 1、支付完成;捕获的付款的资金已记入收款人的PayPal帐户
* 2、退款完成;该交易的资金已记入客户的帐户
*/
public static final String STATE_COMPLETED = "COMPLETED";
/**
* 部分退款;少于所捕获付款金额的金额已部分退还给付款人。
*/
public static final String STATE_PARTIALLY_REFUNDED = "PARTIALLY_REFUNDED";
/**
* 1、支付待定;捕获的付款资金尚未记入收款人的PayPal帐户。有关更多信息请参见status.details。
* 2、退款待定;有关更多信息请参见status_details.reason。
*/
/**
* 支付待定:
* capture_status_details
* reason 枚举
* 捕获的付款状态为PENDING或DENIED的原因。可能的值为
* BUYER_COMPLAINT。付款人与贝宝PayPal对此捕获的付款提出了争议。
* CHARGEBACK。响应于付款人与用于支付此已捕获付款的金融工具的发行人对此已捕获的付款提出异议已收回的资金被撤回。
* ECHECK。由尚未结清的电子支票支付的付款人。
* INTERNATIONAL_WITHDRAWAL。访问您的在线帐户。在您的“帐户概览”中接受并拒绝此笔付款。
* OTHER。无法提供其他特定原因。有关此笔付款的更多信息请在线访问您的帐户或联系PayPal。
* PENDING_REVIEW。捕获的付款正在等待人工审核。
*手动收取RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION。收款人尚未为其帐户设置适当的接收首选项。有关如何接受或拒绝此付款的更多信息请在线访问您的帐户。通常在某些情况下提供此原因例如当所捕获付款的货币与收款人的主要持有货币不同时。
* REFUNDED。收回的资金已退还。
* TRANSACTION_APPROVED_AWAITING_FUNDING。付款人必须将这笔付款的资金汇出。通常此代码适用于手动EFT。
* UNILATERAL。收款人没有PayPal帐户。
* VERIFICATION_REQUIRED。收款人的PayPal帐户未通过验证。
*/
/**
* 退款待定
* 退款具有“PENDING”或“FAILED”状态的原因。 可能的值为:
* ECHECK。客户的帐户通过尚未结清的eCheck进行注资。
*/
public static final String STATE_PENDING = "PENDING";
/**
* 退款;大于或等于此捕获的付款金额的金额已退还给付款人
*/
public static final String STATE_REFUNDED = "REFUNDED";
/**
* 支付拒绝
*/
public static final String STATE_DENIED = "DENIED";
/**
* 退款失败
*/
public static final String STATE_FAILED = "FAILED";
/**
* 争议状态
*/
public static final String BUYER_COMPLAINT = "BUYER_COMPLAINT";
/**
* 沙箱环境请求网关地址
*/
public static final String SANDBOX = "https://api.sandbox.paypal.com";
/**
* 生产环境请求网关地址
*/
public static final String LIVE = "https://api.paypal.com";
/**
* 添加物流信息请求路径
*/
public static final String ADD_TRACK_URL = "/v1/shipping/trackers-batch";
/**
* 修改物流信息请求路径
*/
public static final String UPDATE_TRACK_URL = "/v1/shipping/trackers/";
public final static String CMD_NOTIFY_VALIDATE = "_notify-validate";
// public final static String WEBHOOK_ID = "31797347YC028794L";
// kim-sandbox
// public final static String WEBHOOK_ID = "1WH327112B602422N";
// kim-live
public final static String WEBHOOK_ID = "41L14847MC833625B";
public final static String PAYPAL_TOKEN_KEY = "PayPalAccessToken";
}

View File

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

View File

@@ -0,0 +1,40 @@
package com.ai.da.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
public enum CurrencyCodesEnum {
AUSTRALIAN_DOLLAR("AUD"),
BRAZILIAN_REAL("BRL"),
CANADIAN_DOLLAR("CAD"),
CHINESE_RENMENBI("CNY"),
CZECH_KORUNA("CZK"),
DANISH_KRONE("DKK"),
EURO("EUR"),
HONG_KONG_DOLLAR("HKD"),
HUNGARIAN_FORINT("HUF"),
ISRAELI_NEW_SHEKEL("ILS"),
JAPANESE_YEN("JPY"),
MALAYSIAN_RINGGIT("MYR"),
MEXICAN_PESO("MXN"),
NEW_TAIWAN_DOLLAR("TWD"),
NEW_ZEALAND_DOLLAR("NZD"),
NORWEGIAN_KRONE("NOK"),
PHILIPPINE_PESO("PHP"),
POLISH_ZLOTY("PLN"),
POUND_STERLING("GBP"),
RUSSIAN_RUBLE("RUB"),
SINGAPORE_DOLLAR("SGD"),
SWEDISH_KRONA("SEK"),
SWISS_FRANC("CHF"),
THAI_BAHT("THB"),
UNITED_STATES_DOLLAR("USD");
private String code;
CurrencyCodesEnum(String code) {
this.code = code;
}
}

View File

@@ -20,7 +20,7 @@ public enum OrderStatusEnum {
/**
* 已关闭
*/
CLOSED("超时已关闭"),
TIMEOUT_CLOSED("超时已关闭"),
/**
* 已取消

View File

@@ -0,0 +1,24 @@
package com.ai.da.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum PayPalOrderStatusEnum {
CREATED("CREATED"),
SAVED("SAVED"),
APPROVED("APPROVED"),
VOIDED("VOIDED"),
COMPLETED("COMPLETED"),
PAYER_ACTION_REQUIRED("PAYER_ACTION_REQUIRED");
private final String status;
}

View File

@@ -11,11 +11,15 @@ public enum PayTypeEnum {
*/
WXPAY("微信"),
/**
* 支付宝
*/
ALIPAY("支付宝");
ALIPAY("支付宝"),
/**
* PayPal
*/
PAYPAL("PayPal");
/**
* 类型

View File

@@ -48,7 +48,7 @@ public class AuthenticationFilter extends OncePerRequestFilter {
"/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew",
"/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
// "/api/python/chatStream",
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify"
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back"
);
@Override

View File

@@ -29,7 +29,7 @@ public class AliPayTask {
log.info("orderConfirm 被执行......");
List<OrderInfo> orderInfoList = orderInfoService.getNoPayOrderByDuration(1, PayTypeEnum.ALIPAY.getType());
List<OrderInfo> orderInfoList = orderInfoService.getNoPayOrderByDuration(5, PayTypeEnum.ALIPAY.getType());
for (OrderInfo orderInfo : orderInfoList) {
String orderNo = orderInfo.getOrderNo();

View File

@@ -14,6 +14,7 @@ import java.util.concurrent.*;
@Component
public class AsyncCallerUtil {
// 存放状态 表示当前任务是否需要继续等待,默认持续等待
public static Map<String, Boolean> waitingStatus = new HashMap<>();
private static PythonService pythonService;
@@ -58,7 +59,7 @@ public class AsyncCallerUtil {
return null;
} catch (InterruptedException | ExecutionException | BusinessException e) {
// 处理异常
log.error("发生错误 " + e);
log.error("发生错误 " + e, e);
// 取消定时任务
assert timeoutTask != null;
timeoutTask.cancel(true);

View File

@@ -7,8 +7,10 @@ import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
@@ -17,6 +19,10 @@ public class RedisUtil {
@Resource
private RedisTemplate<String, String> redisTemplate;
public Boolean hasKey(String key){
return redisTemplate.hasKey(key);
}
//- - - - - - - - - - - - - - - - - - - - - ZSet类型 - - - - - - - - - - - - - - - - - - - -
/**
@@ -64,10 +70,15 @@ public class RedisUtil {
/**
* 获取当前ZSet中数据量的总和
*/
public Long getZSetTotal(String key) {
public Long getZSetTotalCount(String key) {
return redisTemplate.opsForZSet().zCard(key);
}
public Set<String> getZSetTotalData(String key){
return redisTemplate.opsForZSet().range(key, 0, -1);
}
//- - - - - - - - - - - - - - - - - - - - - set类型 - - - - - - - - - - - - - - - - - - - -
/**
@@ -123,4 +134,26 @@ public class RedisUtil {
public Long removeFromMap(String key, String hashKeys) {
return redisTemplate.opsForHash().delete(key, hashKeys);
}
//- - - - - - - - - - - - - - - - - - - - - String类型 - - - - - - - - - - - - - - - - - - - -
public void addToString(String key, String value, Long expiresIn){
redisTemplate.opsForValue().set(key,value,expiresIn, TimeUnit.SECONDS);
}
public String getFromString(String key){
return redisTemplate.opsForValue().get(key);
}
public Set<String> getKeysFromString(String key){
return redisTemplate.keys(key);
}
public List<String> getMultiValue(Set<String> keys){
return redisTemplate.opsForValue().multiGet(keys);
}
public Long getExpire(String key){
return redisTemplate.getExpire(key);
}
}

View File

@@ -0,0 +1,30 @@
package com.ai.da.common.utils.paypalRequest;
import com.alibaba.fastjson.JSONObject;
import com.paypal.http.HttpRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@Component
public class AuthenticationRequest extends HttpRequest<HashMap> {
public AuthenticationRequest() {
super("/v1/oauth2/token", "POST", HashMap.class);
this.header("Content-Type", "application/x-www-form-urlencoded");
body();
}
public AuthenticationRequest authorization(String clientId, String clientSecret) {
this.header(clientId, clientSecret);
return this;
}
public AuthenticationRequest body() {
HashMap<String, String> body = new HashMap<>();
body.put("grant_type", "client_credentials");
this.requestBody(body);
return this;
}
}

View File

@@ -0,0 +1,26 @@
package com.ai.da.common.utils.paypalRequest;
import com.ai.da.model.dto.WebhookVerifyDTO;
import com.alibaba.fastjson.JSONObject;
import com.paypal.http.HttpRequest;
import com.paypal.orders.OrdersCreateRequest;
import java.io.Serializable;
import java.util.HashMap;
public class WebhookVerifyRequest extends HttpRequest<HashMap> {
public WebhookVerifyRequest() {
super("/v1/notifications/verify-webhook-signature", "POST", HashMap.class);
this.header("Content-Type", "application/json");
}
public WebhookVerifyRequest authorization(String authorization) {
this.header("Authorization", "Bearer " + String.valueOf(authorization));
return this;
}
public WebhookVerifyRequest requestBody(HashMap<String,String> webhookVerify) {
super.requestBody(webhookVerify);
return this;
}
}

View File

@@ -1,20 +1,13 @@
package com.ai.da.controller;
import com.ai.da.common.response.Response;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.service.AliPayService;
import com.ai.da.service.OrderInfoService;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConstants;
import com.alipay.api.internal.util.AlipaySignature;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Map;
@CrossOrigin
@@ -27,22 +20,13 @@ public class AliPayController {
@Resource
private AliPayService aliPayService;
@Resource
private Environment config;
@Resource
private OrderInfoService orderInfoService;
@ApiOperation("统一收单下单并支付页面接口的调用")
@PostMapping("/trade/page/pay/{productId}")
public Response<String> tradePagePay(@PathVariable Long productId,@RequestParam String returnUrl){
System.out.println(productId + " " + 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);
@@ -51,80 +35,7 @@ public class AliPayController {
@ApiOperation("支付通知")
@PostMapping("/trade/notify")
public String tradeNotify(@RequestParam Map<String, String> params){
log.info("支付通知正在执行");
log.info("通知参数 ===> {}", params);
String result = "failure";
try {
//异步通知验签
boolean signVerified = AlipaySignature.rsaCheckV1(
params,
config.getProperty("alipay.alipay-public-key"),
AlipayConstants.CHARSET_UTF8,
AlipayConstants.SIGN_TYPE_RSA2); //调用SDK验证签名
if(!signVerified){
//验签失败则记录异常日志并在response中返回failure.
log.error("支付成功异步通知验签失败!");
return result;
}
// 验签成功后
log.info("支付成功异步通知验签成功!");
//按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,
//1 商户需要验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号
String outTradeNo = params.get("out_trade_no");
OrderInfo order = orderInfoService.getOrderByOrderNo(outTradeNo);
if(order == null){
log.error("订单不存在");
return result;
}
//2 判断 total_amount 是否确实为该订单的实际金额(即商户订单创建时的金额)
String totalAmount = params.get("total_amount");
int totalAmountInt = new BigDecimal(totalAmount).multiply(new BigDecimal("100")).intValue();
int totalFeeInt = order.getTotalFee().intValue();
if(totalAmountInt != totalFeeInt){
log.error("金额校验失败");
return result;
}
//3 校验通知中的 seller_id或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方
String sellerId = params.get("seller_id");
String sellerIdProperty = config.getProperty("alipay.seller-id");
if(!sellerId.equals(sellerIdProperty)){
log.error("商家pid校验失败");
return result;
}
//4 验证 app_id 是否为该商户本身
String appId = params.get("app_id");
String appIdProperty = config.getProperty("alipay.app-id");
if(!appId.equals(appIdProperty)){
log.error("appid校验失败");
return result;
}
//在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS时
// 支付宝才会认定为买家付款成功。
String tradeStatus = params.get("trade_status");
if(!"TRADE_SUCCESS".equals(tradeStatus)){
log.error("支付未成功");
return result;
}
//处理业务 修改订单状态 记录支付日志
aliPayService.processOrder(params);
//校验成功后在response中返回success并继续商户自身业务处理校验失败返回failure
result = "success";
} catch (AlipayApiException e) {
e.printStackTrace();
}
return result;
return aliPayService.tradeNotify(params);
}
/**
@@ -135,7 +46,6 @@ public class AliPayController {
@ApiOperation("用户取消订单")
@PostMapping("/trade/close/{orderNo}")
public Response<String> cancel(@PathVariable String orderNo){
log.info("取消订单");
aliPayService.cancelOrder(orderNo);
return Response.success("订单已取消");
@@ -149,16 +59,14 @@ public class AliPayController {
@ApiOperation("查询订单:测试订单状态用")
@GetMapping("/trade/query/{orderNo}")
public Response<String> queryOrder(@PathVariable String orderNo) {
log.info("查询订单");
String result = aliPayService.queryOrder(orderNo);
return Response.success(result);
}
/**
* 申请退款
* 不在页面提供申请退款接口
* @param orderNo
* @param reason
* @return
@@ -166,7 +74,6 @@ public class AliPayController {
@ApiOperation("申请退款")
@PostMapping("/trade/refund/{orderNo}/{reason}")
public Response<String> refunds(@PathVariable String orderNo, @PathVariable String reason){
log.info("申请退款");
aliPayService.refund(orderNo, reason);
return Response.success();
@@ -180,10 +87,8 @@ public class AliPayController {
*/
@ApiOperation("查询退款:测试用")
@GetMapping("/trade/fastpay/refund/{orderNo}")
public Response<String> queryRefund(@PathVariable String orderNo) throws Exception {
public Response<String> queryRefund(@PathVariable String orderNo) {
log.info("查询退款");
String result = aliPayService.queryRefund(orderNo);
return Response.success(result);
}

View File

@@ -0,0 +1,54 @@
package com.ai.da.controller;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.response.Response;
import com.ai.da.mapper.primary.DesignMapper;
import com.ai.da.mapper.primary.TrialOrderMapper;
import com.ai.da.mapper.primary.entity.TrialOrder;
import com.ai.da.model.dto.UserDesignStatisticDTO;
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;
import java.util.List;
@Api(tags = "便利查询")
@Slf4j
@RestController
@RequestMapping("/api/inquiry")
public class ConvenientInquiryController {
@Resource
private TrialOrderMapper trialOrderMapper;
@Resource
private DesignMapper designMapper;
@ApiOperation("获取当前所有试用用户")
@GetMapping("/getTrial")
public Response<List<TrialOrder>> getTrial(){
Long accountId = UserContext.getUserHolder().getId();
if (accountId.equals(31L) || accountId.equals(87L) || accountId.equals(83L)){
List<TrialOrder> trialOrders = trialOrderMapper.selectList(null);
return Response.success(trialOrders);
}else {
return Response.fail("Sorry, you don't have permission");
}
}
@ApiOperation("获取指定时间区间内所有用户design的使用情况")
@GetMapping("/getDesignStatistic")
public Response<List<UserDesignStatisticDTO>> getDesignStatistic(@RequestParam String startTime,@RequestParam String endTime){
Long accountId = UserContext.getUserHolder().getId();
if (accountId.equals(31L) || accountId.equals(87L) || accountId.equals(83L)){
List<UserDesignStatisticDTO> designStatistic = designMapper.getDesignStatistic(startTime, endTime);
return Response.success(designStatistic);
}else {
return Response.fail("Sorry, you don't have permission");
}
}
}

View File

@@ -0,0 +1,42 @@
package com.ai.da.controller;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.Response;
import com.ai.da.mapper.primary.entity.CreditsDetail;
import com.ai.da.model.dto.QueryIncomeOrExpenditureDTO;
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;
import javax.validation.Valid;
@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(UserContext.getUserHolder().getId());
return Response.success(credits);
}
@ApiOperation("获取积分详细")
@PostMapping("/getCreditsDetail")
public Response<PageBaseResponse<CreditsDetail>> getCreditsDetail(@Valid @RequestBody QueryIncomeOrExpenditureDTO queryPageByTimeDTO) {
PageBaseResponse<CreditsDetail> credits = creditsService.queryCreditsDetailsPage(queryPageByTimeDTO);
return Response.success(credits);
}
}

View File

@@ -1,15 +1,17 @@
package com.ai.da.controller;
import com.ai.da.common.enums.OrderStatusEnum;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.Response;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.model.dto.QueryPageByTimeDTO;
import com.ai.da.service.OrderInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import javax.validation.Valid;
@CrossOrigin //开放前端的跨域访问
@Api(tags = "商品订单管理")
@@ -21,11 +23,11 @@ public class OrderInfoController {
private OrderInfoService orderInfoService;
@ApiOperation("订单列表")
@GetMapping("/list")
public Response<List<OrderInfo>> list(){
List<OrderInfo> list = orderInfoService.listOrderByCreateTimeDesc();
return Response.success(list);
@PostMapping("/list")
public Response<PageBaseResponse<OrderInfo>> list(@Valid @RequestBody QueryPageByTimeDTO queryPageByTimeDTO){
PageBaseResponse<OrderInfo> orderByAccountId = orderInfoService.getOrderByPage(queryPageByTimeDTO);
// List<OrderInfo> list = orderInfoService.listOrderByCreateTimeDesc();
return Response.success(orderByAccountId);
}
/**
@@ -36,15 +38,11 @@ public class OrderInfoController {
@ApiOperation("查询本地订单状态")
@GetMapping("/query-order-status/{orderNo}")
public Response<String> queryOrderStatus(@PathVariable String orderNo){
String orderStatus = orderInfoService.getOrderStatus(orderNo);
if(OrderStatusEnum.SUCCESS.getType().equals(orderStatus)){
return Response.success("支付成功"); //支付成功
}
return Response.success(101,"支付中......");
}
}

View File

@@ -0,0 +1,77 @@
package com.ai.da.controller;
import com.ai.da.common.response.Response;
import com.ai.da.service.CallBackService;
import com.ai.da.service.PayPalCheckoutService;
import com.paypal.http.HttpResponse;
import com.paypal.http.exceptions.SerializeException;
import com.paypal.orders.Order;
import com.paypal.payments.Refund;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
@RestController
@Api(tags = "PayPalCheckout接口")
@RequestMapping("/api/paypal")
public class PayPalCheckoutController {
@Resource
private PayPalCheckoutService payPalCheckoutService;
@ApiOperation(value = "创建订单")
@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);
}
@ApiOperation(value = "ipn异步回调")
@PostMapping(value = "/ipn/back")
public Response<String> callback(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Boolean result = payPalCheckoutService.doPost(request, response);
if (result){
return Response.success(200,"success");
}else {
return Response.fail(500,"failure");
}
}
@ApiOperation(value = "查询指定订单")
@PostMapping(value = "/trade/query/{orderNo}")
public Response<String> queryOrder(@PathVariable String orderNo) throws SerializeException {
String s = payPalCheckoutService.queryOrder(orderNo);
return Response.success(s);
}
/** 不提供退款接口 */
@ApiOperation("申请退款")
@PostMapping("/trade/refund/{orderNo}/{reason}")
public Response<HttpResponse<Refund>> refund(@PathVariable String orderNo, @PathVariable String reason) throws IOException {
Boolean response = payPalCheckoutService.refundOrder(orderNo,reason);
if (response){
return Response.success();
}else {
return Response.fail("Request for refund failed.");
}
}
@ApiOperation("执行扣款")
@PostMapping("/trade/capture/{orderNo}")
public Response<com.paypal.orders.Order> captureOrder(@PathVariable String orderNo) throws IOException {
Order response = payPalCheckoutService.captureOrder(orderNo);
return Response.success(response);
}
}

View File

@@ -7,12 +7,14 @@ import com.ai.da.mapper.primary.entity.Library;
import com.ai.da.model.dto.ChatFlushDTO;
import com.ai.da.model.dto.ChatRobotLibraryDTO;
import com.ai.da.model.dto.ChatSendDTO;
import com.ai.da.model.dto.SuperResolutionDTO;
import com.ai.da.model.vo.ChatRobotVO;
import com.ai.da.model.vo.PythonLibraryVo;
import com.ai.da.model.vo.SysFileVO;
import com.ai.da.python.PythonService;
import com.ai.da.service.ChatRobotService;
import com.ai.da.service.LibraryService;
import com.ai.da.service.SuperResolutionService;
import com.ai.da.service.SysFileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -41,10 +43,12 @@ public class PythonController {
private SysFileService sysFileService;
@Resource
private LibraryService libraryService;
@Resource
private ChatRobotService chatRobotService;
@Resource
private SuperResolutionService superResolutionService;
@ApiOperation(value = "python服务保存图片到java服务")
@PostMapping("/saveGeneratePicture")
public Response<String> upload(@RequestParam("file") MultipartFile file,
@@ -109,4 +113,10 @@ public class PythonController {
return Response.success(chatRobotService.chatBufferFlush(chatFlushDTO));
}
@ApiOperation(value = "超分辨率")
@PostMapping("/prepareForSR")
public Response<List<String>> superResolution(@RequestBody List<SuperResolutionDTO> superResolutionDTO) {
return Response.success(superResolutionService.prepareForSR(superResolutionDTO));
}
}

View File

@@ -0,0 +1,40 @@
package com.ai.da.controller;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.Response;
import com.ai.da.model.dto.QueryTaskHistoryDTO;
import com.ai.da.model.dto.SuperResolutionDTO;
import com.ai.da.model.dto.TaskDTO;
import com.ai.da.model.vo.TaskVO;
import com.ai.da.service.TaskListService;
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;
import javax.validation.Valid;
import java.util.List;
@Api(tags = "任务列表模块")
@Slf4j
@RestController
@RequestMapping("/api/tasks")
public class TaskListController {
@Resource
private TaskListService taskListService;
@PostMapping("/getList")
@ApiOperation("获取未执行完的任务")
public Response<List<TaskDTO<SuperResolutionDTO>>> getTaskList(@Valid @RequestBody List<String> taskIdList) {
return Response.success(taskListService.getExecTask(taskIdList));
}
@PostMapping("/getAllTask")
@ApiOperation("获取所有任务")
public Response<PageBaseResponse<TaskVO>> getAllTask(@Valid @RequestBody QueryTaskHistoryDTO queryTaskHistoryDTO) {
return Response.success(taskListService.getAllTask(queryTaskHistoryDTO));
}
}

View File

@@ -0,0 +1,7 @@
package com.ai.da.mapper.primary;
import com.ai.da.mapper.primary.entity.CreditsDetail;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface CreditsDetailMapper extends BaseMapper<CreditsDetail> {
}

View File

@@ -1,6 +1,10 @@
package com.ai.da.mapper.primary;
import com.ai.da.common.config.mybatis.plus.CommonMapper;
//import com.ai.da.mapper.entity.Design;
import com.ai.da.model.dto.UserDesignStatisticDTO;
import java.util.List;
import com.ai.da.mapper.primary.entity.Design;
/**
@@ -13,4 +17,6 @@ public interface DesignMapper extends CommonMapper<Design> {
//返回插入数据后生成的主键
Long insertDesign(Design design);
List<UserDesignStatisticDTO> getDesignStatistic(String startTime, String endTime);
}

View File

@@ -0,0 +1,7 @@
package com.ai.da.mapper.primary;
import com.ai.da.mapper.primary.entity.TaskList;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface TaskListMapper extends BaseMapper<TaskList> {
}

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

@@ -6,6 +6,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Date;
@Data
@@ -23,12 +24,12 @@ public class BaseEntity implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
/**
* 更新时间
*/
private Date updateTime;
private LocalDateTime updateTime;
/**
* 是否已删除

View File

@@ -0,0 +1,21 @@
package com.ai.da.mapper.primary.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("t_credits_detail")
public class CreditsDetail extends BaseEntity {
/** 用户id */
private Long accountId;
/** 积分变更事件 */
private String changeEvent;
/** 变更积分 ( + 表示加,- 表示减) */
private String changedCredits;
/** 当前积分 */
private BigDecimal credits;
}

View File

@@ -11,11 +11,11 @@ public class OrderInfo extends BaseEntity{
private String orderNo;//商户订单编号
private Long userId;//用户id
private Long accountId;//用户id
private Long productId;//支付产品id
private Integer totalFee;//订单金额()
private Integer totalFee;//订单金额()
private String codeUrl;//订单二维码连接

View File

@@ -11,7 +11,7 @@ public class RefundInfo extends BaseEntity{
private String refundNo;//退款单编号
private String refundId;//支付系统退款单号
private String refundId;//支付系统退款单号(微信)
private Integer totalFee;//原订单金额(分)

View File

@@ -0,0 +1,27 @@
package com.ai.da.mapper.primary.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("t_task_list")
public class TaskList extends BaseEntity{
private Long accountId;
private String taskType;
private String inputUrl;
private Integer scale;
/* 可选状态 成功success 失败:fail */
private String status;
private String outputUrl;
private String taskId;
}

View File

@@ -0,0 +1,13 @@
package com.ai.da.model.dto;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel("查积分的收支详情")
public class QueryIncomeOrExpenditureDTO extends QueryPageByTimeDTO{
private Boolean isIncome;
}

View File

@@ -0,0 +1,19 @@
package com.ai.da.model.dto;
import com.ai.da.model.vo.PageQueryBaseVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel("分页查询,限制时间区间")
public class QueryPageByTimeDTO extends PageQueryBaseVo {
@ApiModelProperty("开始时间 yyyy-mm-dd hh:mm:ss 可以不要时分秒")
private String startTime;
@ApiModelProperty("结束时间 yyyy-mm-dd hh:mm:ss 可以不要时分秒")
private String endTime;
}

View File

@@ -0,0 +1,18 @@
package com.ai.da.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotEmpty;
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel("按分类分页查询历史任务")
public class QueryTaskHistoryDTO extends QueryPageByTimeDTO {
@NotEmpty(message = "type cannot be empty")
@ApiModelProperty("可选类型 SR")
private String type;
}

View File

@@ -0,0 +1,25 @@
package com.ai.da.model.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SuperResolutionDTO {
@NotBlank(message = "You have to select at least one image")
@ApiModelProperty("图片")
private String images;
@NotBlank(message = "You must choose the magnification")
@ApiModelProperty("放大倍数")
private Integer scale;
@ApiModelProperty("唯一id用于保持消息唯一性")
private String uniqueId;
}

View File

@@ -0,0 +1,46 @@
package com.ai.da.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TaskDTO<T> {
private String taskId;
// 可选type : SR GENERATE
private String type;
// 输入的图片名
private String imageName;
private T inputParam;
private String outputImage;
// 任务状态,暂定状态:排队中、执行中、成功/失败
private String status;
// 当前任务的创建时间
private String createDate;
public TaskDTO(String taskId, String type, String imageName, T inputParam, String status, String createDate) {
this.taskId = taskId;
this.type = type;
this.imageName = imageName;
this.inputParam = inputParam;
this.status = status;
this.createDate = createDate;
}
public TaskDTO(String taskId, String type, T inputParam, String status, String createDate) {
this.taskId = taskId;
this.type = type;
this.inputParam = inputParam;
this.status = status;
this.createDate = createDate;
}
}

View File

@@ -0,0 +1,17 @@
package com.ai.da.model.dto;
import lombok.Data;
@Data
public class UserDesignStatisticDTO {
private Long accountId;
private Long useDesignTimes;
private String userEmail;
private String userName;
private String isTrial;
}

View File

@@ -0,0 +1,35 @@
package com.ai.da.model.dto;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WebhookVerifyDTO implements Serializable {
private String transmission_id;
private String transmission_time;
private String cert_url;
private String auth_algo;
private String transmission_sig;
private String webhook_id;
private Object webhook_event;
}

View File

@@ -0,0 +1,22 @@
package com.ai.da.model.vo;
import lombok.Data;
@Data
public class TaskVO {
// 图片名
private String imageName;
private String inputImage;
private String outputImage;
private String otherInput;
private String status;
private String taskId;
private String createDate;
}

View File

@@ -65,6 +65,8 @@ public class PythonService {
private String accessPythonIp;
@Value("${access.python.port:''}")
private String accessPythonPort;
@Value("${access.python.sr}")
private String srPythonPort;
@Resource
private PythonTAllInfoService pythonTAllInfoService;
@@ -290,7 +292,7 @@ public class PythonService {
List<CollectionElement> pinData = getPinData(elementVO);
if (CollectionUtil.isNotEmpty(pinData)) {
return CurrentDesignPictureTypeEnum.PIN;
}else {
} else {
if (sysSketchNum == 0 && noPinSketchNum == 0) {
sysSketchNum = systemScale.multiply(BigDecimal.valueOf(8 - pinSketchNum)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
noPinSketchNum = 8 - pinSketchNum - sysSketchNum;
@@ -605,6 +607,7 @@ public class PythonService {
@Resource
private AttributeRetrievalMapper attributeRetrievalMapper;
private List<CollectionElement> getSystemSketchPool(JSONObject attributeRecognition, String styleCategory, String modelSex, int poolNum) {
/**
* female trousers->female_pants
@@ -847,6 +850,7 @@ public class PythonService {
private CollocationMapper collocationMapper;
@Resource
private DressingMapper dressingMapper;
private List<String> getOtherSketchCategoryList(String modelSex, DesignPythonItem designPythonItem) {
List<Collocation> collocationList = collocationMapper.getCollocationListBySketch(modelSex, designPythonItem.getType());
if (CollectionUtil.isNotEmpty(collocationList)) {
@@ -2003,10 +2007,10 @@ public class PythonService {
if (elementVO.getSingleOverall().equals(SingleOverallEnum.SINGLE.getRealName())) {
if (!CollectionUtils.isEmpty(pinData)) {
return pinData.stream().filter(o -> o.getLevel2Type().equals(elementVO.getSwitchCategory())).collect(Collectors.toList());
}else {
} else {
return pinData;
}
}else {
} else {
return pinData;
}
}
@@ -2016,10 +2020,10 @@ public class PythonService {
if (elementVO.getSingleOverall().equals(SingleOverallEnum.SINGLE.getRealName())) {
if (!CollectionUtils.isEmpty(pinData)) {
return pinData.stream().filter(o -> o.getLevel2Type().equals(elementVO.getSwitchCategory())).collect(Collectors.toList());
}else {
} else {
return pinData;
}
}else {
} else {
return pinData;
}
}
@@ -2042,8 +2046,8 @@ public class PythonService {
return sketchBoardPins;
}
public final static List<String> FEMALE_CATEGORY = Arrays.asList("Dress","Blouse","Skirt","Trousers","Outwear");
public final static List<String> MALE_CATEGORY = Arrays.asList("Tops","Bottoms","Outwear");
public final static List<String> FEMALE_CATEGORY = Arrays.asList("Dress", "Blouse", "Skirt", "Trousers", "Outwear");
public final static List<String> MALE_CATEGORY = Arrays.asList("Tops", "Bottoms", "Outwear");
private List<CollectionElement> getPinData(List<CollectionElement> sketchBoardElements, List<String> hasUseMd5List, String modelSex) {
if (CollectionUtils.isEmpty(sketchBoardElements)) {
@@ -2070,7 +2074,7 @@ public class PythonService {
return null;
}
return noPinData.stream().filter(o -> o.getLevel2Type().equals(elementVO.getSwitchCategory())).collect(Collectors.toList());
}else {
} else {
return getNoPinData(elementVO.getSketchBoardElements(), elementVO.getModelSex());
}
}
@@ -2929,6 +2933,7 @@ public class PythonService {
Boolean result = JSON.parseObject(JSON.toJSONString(response)).getBoolean("successful");
if (result && jsonObject.get("code").equals(200)) {
log.info("Generate##responseObject###{}", jsonObject);
return setGenerateImageList(jsonObject.getJSONObject("data"));
}
log.info("generateSketchOrPrintPrint失败###{}", jsonObject);
@@ -3074,7 +3079,7 @@ public class PythonService {
// .addHeader("Authorization", "Basic YWlkbGFiOjEyMw==")
// .addHeader("Content-Type", "application/json")
.build();
Response response;
Response response = null;
try {
log.info("cancelGenerateTask请求入参content###{}", taskId);
response = client.newCall(request).execute();
@@ -3082,8 +3087,10 @@ public class PythonService {
log.error("PythonService##cancelGenerateTask异常###{}", ExceptionUtil.getThrowableList(ioException));
return null;
}
int responseCode = response.code();
response.close();
if (response.code() != HttpURLConnection.HTTP_OK) {
if (responseCode != HttpURLConnection.HTTP_OK) {
log.info("generate-python 取消请求失败");
return Boolean.FALSE;
}
@@ -3091,4 +3098,45 @@ public class PythonService {
return Boolean.TRUE;
}
public Boolean superResolution(SuperResolutionDTO superResolutionDTO) throws BusinessException {
OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(30, TimeUnit.SECONDS)
.pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒)
.readTimeout(60, TimeUnit.SECONDS)//读取超时(单位:秒)
.writeTimeout(60, TimeUnit.SECONDS)//写入超时(单位:秒)
.build();
MediaType mediaType = MediaType.parse("application/json");
HashMap<String, String> content = new HashMap<>();
content.put("sr_image_url", superResolutionDTO.getImages());
content.put("sr_xn", superResolutionDTO.getScale().toString());
content.put("sr_tasks_id", superResolutionDTO.getUniqueId());
String jsonString = JSON.toJSONString(content, SerializerFeature.WriteNullStringAsEmpty);
RequestBody body = RequestBody.create(mediaType, jsonString);
Request request = new Request.Builder()
.url(srPythonPort + "/api/super_resolution")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.build();
Response response = null;
try {
log.info("superResolution请求入参content###{}", jsonString);
response = client.newCall(request).execute();
} catch (IOException ioException) {
log.error("PythonService##superResolution异常###{}", ExceptionUtil.getThrowableList(ioException));
return null;
}
int responseCode = response.code();
response.close();
if (responseCode != HttpURLConnection.HTTP_OK) {
// 基本不会有除200以外的code
log.info("superResolution 请求超分操作失败。 Response code " + response.code());
throw new BusinessException("superResolution 请求超分操作失败。 Response code " + response.code());
}
log.info("superResolution 请求超分操作成功");
return Boolean.TRUE;
}
}

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,9 @@ 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);
void processOrder(Map<String, String> params);

View File

@@ -0,0 +1,12 @@
package com.ai.da.service;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public interface CallBackService {
Boolean doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException;
}

View File

@@ -0,0 +1,29 @@
package com.ai.da.service;
import com.ai.da.common.enums.CreditsEventsEnum;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.CreditsDetail;
import com.ai.da.model.dto.QueryIncomeOrExpenditureDTO;
import com.baomidou.mybatisplus.extension.service.IService;
public interface CreditsService extends IService<CreditsDetail> {
void initCredits();
Boolean buyCredits(Long accountId, Integer quantity);
void creditsIncrease(Long accountId, String event);
void creditsDecrease(Long accountId, String event);
String getCredits(Long accountId);
void creditsRefund(Long accountId, Integer quantity);
void insertToCreditsDetail(Long accountId, String changeEvent, String credits, String changeType);
PageBaseResponse<CreditsDetail> queryCreditsDetailsPage(QueryIncomeOrExpenditureDTO queryPageByTimeDTO);
Boolean checkCredits(Long accountId, CreditsEventsEnum event, Integer num);
}

View File

@@ -2,14 +2,16 @@ package com.ai.da.service;
import com.ai.da.common.enums.OrderStatusEnum;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.model.dto.QueryPageByTimeDTO;
import com.baomidou.mybatisplus.extension.service.IService;
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);
@@ -22,4 +24,8 @@ public interface OrderInfoService extends IService<OrderInfo> {
List<OrderInfo> getNoPayOrderByDuration(int minutes, String paymentType);
OrderInfo getOrderByOrderNo(String orderNo);
PageBaseResponse<OrderInfo> getOrderByPage(QueryPageByTimeDTO queryPageByTimeDTO);
void updateOrderNoById(Long id, String orderNo);
}

View File

@@ -0,0 +1,32 @@
package com.ai.da.service;
import com.paypal.http.exceptions.SerializeException;
import com.paypal.orders.Order;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public interface PayPalCheckoutService {
HashMap<String, String> createOrder(Integer amount,String returnUrl) throws SerializeException;
// String callback(@SuppressWarnings("rawtypes") Map map);
Boolean doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException;
String queryOrder(String orderNo) throws SerializeException;
Order captureOrder(String orderId) throws IOException;
Boolean refundOrder(String orderId, String reason) throws IOException;
String getOAuth();
void processOrder(String orderId);
}

View File

@@ -1,5 +1,7 @@
package com.ai.da.service;
import com.paypal.orders.Order;
import java.util.Map;
public interface PaymentInfoService {
@@ -7,4 +9,6 @@ public interface PaymentInfoService {
void createPaymentInfo(String plainText);
void createPaymentInfoForAliPay(Map<String, String> params);
void createPaymentInfoForPayPal(Order order);
}

View File

@@ -5,7 +5,9 @@ import org.springframework.stereotype.Service;
@Service
public interface RabbitMQService {
void publishMessage(String message);
void publishMessageToGenerate(String message);
void publishMessageToSR(String message);
Integer getMessageCount(String queueUrl);
}

View File

@@ -17,4 +17,6 @@ public interface RefundInfoService extends IService<RefundInfo> {
RefundInfo createRefundByOrderNoForAliPay(String orderNo, String reason);
void updateRefundForAliPay(String refundNo, String content, String refundStatus);
void updateRefundForPayPal(Long id, String refundId, String content, String refundStatus);
}

View File

@@ -0,0 +1,22 @@
package com.ai.da.service;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.TaskList;
import com.ai.da.model.dto.QueryTaskHistoryDTO;
import com.ai.da.model.dto.SuperResolutionDTO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface SuperResolutionService extends IService<TaskList> {
List<String> prepareForSR(List<SuperResolutionDTO> superResolutionDTO);
void SR(SuperResolutionDTO superResolutionDTO) throws Exception;
void setSRResult(String taskId, String output, String status);
void updateSROutput(String taskId, String status, String output);
PageBaseResponse<TaskList> getTaskHistoryPage(QueryTaskHistoryDTO queryTaskHistoryDTO);
}

View File

@@ -0,0 +1,22 @@
package com.ai.da.service;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.TaskList;
import com.ai.da.model.dto.QueryTaskHistoryDTO;
import com.ai.da.model.dto.SuperResolutionDTO;
import com.ai.da.model.dto.TaskDTO;
import com.ai.da.model.vo.TaskVO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface TaskListService extends IService<TaskList> {
List<TaskDTO<SuperResolutionDTO>> getExecTask(List<String> taskIdList);
void addToTaskListRedis(TaskDTO<SuperResolutionDTO> taskDTO);
void updateTaskStatusOrOutputRedis(String taskId, String status, String output);
PageBaseResponse<TaskVO> getAllTask(QueryTaskHistoryDTO queryTaskHistoryDTO);
}

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;
@@ -488,6 +489,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
trialOrder.setStatus(0);
trialOrderMapper.insert(trialOrder);
SendEmailUtil.sendCustomEmail("1023316923@qq.com", null, trialOrder,1);
SendEmailUtil.sendCustomEmail("calvinwong@aidlab.hk", null, trialOrder,1);
SendEmailUtil.sendCustomEmail("kaicpang.pang@connect.polyu.hk", null, trialOrder,1);
// 判断当前的试用订单是否自动批准
if (AutoApproved.getStatus()) {
// 改变试用订单状态,新增试用用户
@@ -500,7 +503,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
account.setIsTrial(1);
account.setIsBeginner(1);
account.setValidStartTime(System.currentTimeMillis());
account.setValidEndTime(Instant.now().plus(3, ChronoUnit.DAYS).toEpochMilli());
account.setValidEndTime(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli());
accountMapper.updateById(account);
}else {
account.setUserName(trialOrder.getUserName());
@@ -508,7 +511,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
account.setUserEmail(trialOrder.getEmail());
account.setLanguage(Language.ENGLISH.name());
account.setValidStartTime(System.currentTimeMillis());
account.setValidEndTime(Instant.now().plus(3, ChronoUnit.DAYS).toEpochMilli());
account.setValidEndTime(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli());
account.setCreateDate(new Date());
account.setIsTrial(1);
account.setIsBeginner(1);
@@ -516,6 +519,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
}
// 发送邮件提醒用户试用用户已创建
SendEmailUtil.sendCustomEmail("1023316923@qq.com", null, trialOrder,2);
SendEmailUtil.sendCustomEmail("calvinwong@aidlab.hk", null, trialOrder,2);
SendEmailUtil.sendCustomEmail("kaicpang.pang@connect.polyu.hk", null, trialOrder,2);
SendEmailUtil.sendCustomEmail(account.getUserEmail(), null, trialOrder, 3);
}
return Boolean.TRUE;
@@ -548,7 +553,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
account.setIsTrial(1);
account.setIsBeginner(1);
account.setValidStartTime(System.currentTimeMillis());
account.setValidEndTime(Instant.now().plus(3, ChronoUnit.DAYS).toEpochMilli());
account.setValidEndTime(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli());
accountMapper.updateById(account);
}else {
account.setUserName(trialOrder.getUserName());
@@ -556,7 +561,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
account.setUserEmail(trialOrder.getEmail());
account.setLanguage(Language.ENGLISH.name());
account.setValidStartTime(System.currentTimeMillis());
account.setValidEndTime(Instant.now().plus(3, ChronoUnit.DAYS).toEpochMilli());
account.setValidEndTime(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli());
account.setCreateDate(new Date());
account.setIsTrial(1);
account.setIsBeginner(1);
@@ -564,6 +569,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
}
// 发送邮件提醒用户试用用户已创建
SendEmailUtil.sendCustomEmail("1023316923@qq.com", null, trialOrder,2);
SendEmailUtil.sendCustomEmail("calvinwong@aidlab.hk", null, trialOrder,2);
SendEmailUtil.sendCustomEmail("kaicpang.pang@connect.polyu.hk", null, trialOrder,2);
SendEmailUtil.sendCustomEmail(account.getUserEmail(), null, trialOrder, 3);
}
return Boolean.TRUE;
@@ -885,4 +892,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

@@ -1,17 +1,18 @@
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;
import com.alipay.api.AlipayConstants;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.google.gson.Gson;
@@ -46,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();
@@ -69,9 +73,10 @@ public class AliPayServiceImpl implements AliPayService {
//组装当前业务方法的请求参数
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderInfo.getOrderNo());
BigDecimal total = new BigDecimal(orderInfo.getTotalFee().toString()).divide(new BigDecimal("100"));
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());
@@ -84,14 +89,93 @@ public class AliPayServiceImpl implements AliPayService {
return response.getBody();
} else {
log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> " + response.getMsg());
throw new RuntimeException("创建支付交易失败");
throw new BusinessException("Order creation failed");
}
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("创建支付交易失败");
throw new BusinessException("Order creation failed");
}
}
@Override
public String tradeNotify(Map<String, String> params) {
log.info("支付通知正在执行");
log.info("通知参数 ===> {}", params);
String result = "failure";
try {
//异步通知验签
boolean signVerified = AlipaySignature.rsaCheckV1(
params,
config.getProperty("alipay.alipay-public-key"),
AlipayConstants.CHARSET_UTF8,
AlipayConstants.SIGN_TYPE_RSA2); //调用SDK验证签名
if(!signVerified){
//验签失败则记录异常日志并在response中返回failure.
log.error("支付成功异步通知验签失败!");
return result;
}
// 验签成功后
log.info("支付成功异步通知验签成功!");
//按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,
//1 商户需要验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号
String outTradeNo = params.get("out_trade_no");
OrderInfo order = orderInfoService.getOrderByOrderNo(outTradeNo);
if(order == null){
log.error("订单不存在");
return result;
}
//2 判断 total_amount 是否确实为该订单的实际金额(即商户订单创建时的金额)
String totalAmount = params.get("total_amount");
int totalAmountInt = new BigDecimal(totalAmount).intValue();
int totalFeeInt = order.getTotalFee().intValue();
if(totalAmountInt != totalFeeInt){
log.error("金额校验失败");
return result;
}
//3 校验通知中的 seller_id或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方
String sellerId = params.get("seller_id");
String sellerIdProperty = config.getProperty("alipay.seller-id");
if(!sellerId.equals(sellerIdProperty)){
log.error("商家pid校验失败");
return result;
}
//4 验证 app_id 是否为该商户本身
String appId = params.get("app_id");
String appIdProperty = config.getProperty("alipay.app-id");
if(!appId.equals(appIdProperty)){
log.error("appid校验失败");
return result;
}
//在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS时
// 支付宝才会认定为买家付款成功。
String tradeStatus = params.get("trade_status");
if(!"TRADE_SUCCESS".equals(tradeStatus)){
log.error("支付未成功");
return result;
}
//处理业务 修改订单状态 记录支付日志
processOrder(params);
//校验成功后在response中返回success并继续商户自身业务处理校验失败返回failure
result = "success";
} catch (AlipayApiException e) {
e.printStackTrace();
}
return result;
}
/**
* 处理订单
* @param params
@@ -104,6 +188,7 @@ public class AliPayServiceImpl implements AliPayService {
//获取订单号
String orderNo = params.get("out_trade_no");
String totalAmount = params.get("total_amount");
/*在对业务数据进行状态检查和处理之前,
要采用数据锁进行并发控制,
@@ -112,26 +197,31 @@ public class AliPayServiceImpl implements AliPayService {
// 成功获取则立即返回true获取失败则立即返回false。不必一直等待锁的释放
if(lock.tryLock()) {
try {
//处理重复通知
//接口调用的幂等性:无论接口被调用多少次,以下业务执行一次
String orderStatus = orderInfoService.getOrderStatus(orderNo);
if (!OrderStatusEnum.NOT_PAY.getType().equals(orderStatus)) {
// 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;
}
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
//记录支付日志
paymentInfoService.createPaymentInfoForAliPay(params);
// 添加积分变更记录
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
CreditsEventsEnum.BUY_CREDITS.getName() + "--Alipay",
CreditsEventsEnum.BUY_CREDITS.getValue(),
"positive");
// 更新积分
creditsService.buyCredits(orderByOrderNo.getAccountId(),Integer.parseInt(totalAmount) / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
} finally {
//要主动释放锁
lock.unlock();
}
}
}
/**
@@ -176,7 +266,7 @@ public class AliPayServiceImpl implements AliPayService {
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("查单接口的调用失败");
throw new BusinessException("查单接口的调用失败");
}
}
@@ -198,7 +288,7 @@ public class AliPayServiceImpl implements AliPayService {
if(result == null){
log.warn("核实订单未创建 ===> {}", orderNo);
//更新本地订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.CLOSED);
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.TIMEOUT_CLOSED);
}
//解析查单响应结果
@@ -207,24 +297,28 @@ 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);
//如果订单未支付,则调用关单接口关闭订单
this.closeOrder(orderNo);
// 并更新商户端订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.CLOSED);
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.TIMEOUT_CLOSED);
}
if(AliPayTradeStateEnum.SUCCESS.getType().equals(tradeStatus)){
log.warn("核实订单已支付 ===> {}", orderNo);
//如果订单已支付,则更新商户端订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
//并记录支付日志
paymentInfoService.createPaymentInfoForAliPay(alipayTradeQueryResponse);
// 添加积分变更记录
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
CreditsEventsEnum.BUY_CREDITS.getName() + "--Alipay",
CreditsEventsEnum.BUY_CREDITS.getValue(),
"positive");
// 更新积分
creditsService.buyCredits(orderByOrderNo.getAccountId(),orderByOrderNo.getTotalFee() / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
}
}
@@ -252,8 +346,8 @@ public class AliPayServiceImpl implements AliPayService {
}
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("关单接口的调用失败");
log.error("关单失败,原因 ===> {}",e.getMessage());
throw new BusinessException("关单接口的调用失败");
}
}
@@ -268,17 +362,14 @@ public class AliPayServiceImpl implements AliPayService {
try {
log.info("调用退款API");
//创建退款单
RefundInfo refundInfo = refundsInfoService.createRefundByOrderNoForAliPay(orderNo, reason);
//调用统一收单交易退款接口
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest ();
//组装当前业务方法的请求参数
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderNo);//订单编号
BigDecimal refund = new BigDecimal(refundInfo.getRefund().toString()).divide(new BigDecimal("100"));
BigDecimal refund = new BigDecimal(refundInfo.getRefund().toString());
//BigDecimal refund = new BigDecimal("2").divide(new BigDecimal("100"));
bizContent.put("refund_amount", refund);//退款金额:不能大于支付金额
bizContent.put("refund_reason", reason);//退款原因(可选)
@@ -290,33 +381,30 @@ public class AliPayServiceImpl implements AliPayService {
if(response.isSuccess()){
log.info("调用成功,返回结果 ===> " + response.getBody());
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.REFUND_SUCCESS);
//更新退款单
refundsInfoService.updateRefundForAliPay(
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());
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.REFUND_ABNORMAL);
//更新退款单
refundsInfoService.updateRefundForAliPay(
refundInfo.getRefundNo(),
response.getBody(),
AliPayTradeStateEnum.REFUND_ERROR.getType()); //退款失败
}
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("创建退款申请失败");
throw new BusinessException("创建退款申请失败");
}
}
@@ -349,7 +437,7 @@ public class AliPayServiceImpl implements AliPayService {
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("查单接口的调用失败");
throw new BusinessException("查单接口的调用失败");
}
}
@@ -363,7 +451,6 @@ public class AliPayServiceImpl implements AliPayService {
public String queryBill(String billDate, String type) {
try {
AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("bill_type", type);
@@ -383,13 +470,11 @@ public class AliPayServiceImpl implements AliPayService {
return billDownloadUrl;
} else {
log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> " + response.getMsg());
throw new RuntimeException("申请账单失败");
throw new BusinessException("申请账单失败");
}
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("申请账单失败");
log.error("申请账单失败,原因 ===> {}",e.getMessage());
throw new BusinessException("申请账单失败");
}
}
}

View File

@@ -0,0 +1,159 @@
package com.ai.da.service.impl;
import com.ai.da.common.config.PayPalClient;
import com.ai.da.common.constant.PayPalCheckoutConstant;
import com.ai.da.common.utils.paypalRequest.WebhookVerifyRequest;
import com.ai.da.service.CallBackService;
import com.ai.da.service.PayPalCheckoutService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.paypal.api.payments.Event;
import com.paypal.base.Constants;
import com.paypal.base.SDKUtil;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.PayPalRESTException;
import com.paypal.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.ai.da.common.constant.PayPalCheckoutConstant.MODE;
// #Validate Webhook Sample
//
// This sample code demonstrates how to validate a webhook received on your
// web server. This sample assumes that you use the java servlet, which returns
// the HttpServletRequest object. However, you can modify this code to
// your specific case.
//
@Slf4j
@Service
public class CallBackServiceImpl implements CallBackService {
@Value("${paypal.client-id}")
private String clientId;
@Value("${paypal.client-secret}")
private String clientSecret;
@Resource
private PayPalClient payPalClient;
@Resource
private PayPalCheckoutService payPalCheckoutService;
@Override
public Boolean doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
return doPost(req, resp);
}
// ##Validate Webhook
protected Boolean doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
String body = getBody(req);
Map webhookEvent = new ObjectMapper().readValue(body, Map.class);
HashMap<String, Object> webhookRequest = new HashMap<>();
webhookRequest.put("auth_algo",SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-AUTH-ALGO"));
webhookRequest.put("cert_url",SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-CERT-URL"));
webhookRequest.put("transmission_id",SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-TRANSMISSION-ID"));
webhookRequest.put("transmission_sig",SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-TRANSMISSION-SIG"));
webhookRequest.put("transmission_time",SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-TRANSMISSION-TIME"));
webhookRequest.put("webhook_id",PayPalCheckoutConstant.WEBHOOK_ID);
webhookRequest.put("webhook_event",webhookEvent);
WebhookVerifyRequest webhookVerifyRequest = new WebhookVerifyRequest();
webhookVerifyRequest.authorization(payPalCheckoutService.getOAuth());
webhookVerifyRequest.requestBody(webhookRequest);
// 验签
HttpResponse<HashMap> verified = payPalClient.client(MODE, clientId, clientSecret).execute(webhookVerifyRequest);
boolean verifyResult = verified.result().get("verification_status").toString().equals("SUCCESS");
if (verifyResult){
// ### Api Context
APIContext apiContext = new APIContext(clientId, clientSecret, PayPalCheckoutConstant.MODE);
// Set the webhookId that you received when you created this webhook.
apiContext.addConfiguration(Constants.PAYPAL_WEBHOOK_ID, PayPalCheckoutConstant.WEBHOOK_ID);
Boolean result = Event.validateReceivedEvent(apiContext, getHeadersInfo(
req), body);
log.info("Webhook Validated: " + result);
if (result){
// 处理订单数据
LinkedHashMap<String,LinkedHashMap<String,String>> webhookEventMap = (LinkedHashMap<String,LinkedHashMap<String,String>>) webhookEvent;
String orderId = webhookEventMap.get("resource").get("id");
payPalCheckoutService.processOrder(orderId);
return Boolean.TRUE;
}
}
} catch (PayPalRESTException | InvalidKeyException | NoSuchAlgorithmException | SignatureException e) {
log.error(e.getMessage());
}
return Boolean.FALSE;
}
// Simple helper method to help you extract the headers from HttpServletRequest object.
private static Map<String, String> getHeadersInfo(HttpServletRequest request) {
Map<String, String> map = new HashMap<String, String>();
@SuppressWarnings("rawtypes")
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
return map;
}
// Simple helper method to fetch request data as a string from HttpServletRequest object.
private static String getBody(HttpServletRequest request) throws IOException {
String body;
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = stringBuilder.toString();
log.info("回调参数 ===> {}", body);
return body;
}
}

View File

@@ -0,0 +1,182 @@
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.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.AccountMapper;
import com.ai.da.mapper.primary.CreditsDetailMapper;
import com.ai.da.mapper.primary.entity.Account;
import com.ai.da.mapper.primary.entity.CreditsDetail;
import com.ai.da.model.dto.QueryIncomeOrExpenditureDTO;
import com.ai.da.service.AccountService;
import com.ai.da.service.CreditsService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.netty.util.internal.StringUtil;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
@Service
public class CreditsServiceImpl extends ServiceImpl<CreditsDetailMapper, CreditsDetail> 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;
case "Super Resolution":
event = CreditsEventsEnum.SUPER_RESOLUTION;
break;
default:
throw new BusinessException("UNKNOWN TYPE");
}
BigDecimal existingCredits = accountMapper.selectById(accountId).getCredits();
BigDecimal add = new BigDecimal(event.getValue()).add(existingCredits);
accountService.updateCredits(accountId, add.toString());
}
@Override
public void creditsDecrease(Long accountId, String creditsEvent) {
CreditsEventsEnum event;
switch (creditsEvent) {
case "Super Resolution":
event = CreditsEventsEnum.SUPER_RESOLUTION;
break;
case "Other":
event = CreditsEventsEnum.OTHER;
break;
default:
log.error("UNKNOWN TYPE");
throw new BusinessException("UNKNOWN TYPE");
}
BigDecimal existingCredits = accountMapper.selectById(accountId).getCredits();
BigDecimal subtract = existingCredits.subtract(new BigDecimal(event.getValue()));
accountService.updateCredits(accountId, subtract.toString());
}
@Override
public String getCredits(Long accountId) {
Account account = accountMapper.selectById(accountId);
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());
}
/**
* 向积分变更详细表添加记录
*
* @param changeEvent 导致积分变更的事件
* @param credits 变更的积分
* @param changeType 变更类型 positive->增 negative->减
*/
@Override
public void insertToCreditsDetail(Long accountId, String changeEvent, String credits, String changeType) {
CreditsDetail creditsDetail = new CreditsDetail();
Account account = accountMapper.selectById(accountId);
// BigDecimal finalCredits;
String changeCredits;
if ("positive".equals(changeType)) {
// finalCredits = account.getCredits().add(new BigDecimal(credits));
changeCredits = "+" + credits;
} else {
// finalCredits = account.getCredits().subtract(new BigDecimal(credits));
changeCredits = "-" + credits;
}
creditsDetail.setAccountId(accountId);
creditsDetail.setChangeEvent(changeEvent);
creditsDetail.setChangedCredits(changeCredits);
// creditsDetail.setCredits(finalCredits);
creditsDetail.setCredits(account.getCredits());
creditsDetail.setCreateTime(LocalDateTime.now());
baseMapper.insert(creditsDetail);
}
@Override
public PageBaseResponse<CreditsDetail> queryCreditsDetailsPage(QueryIncomeOrExpenditureDTO queryPageByTimeDTO) {
QueryWrapper<CreditsDetail> qw = new QueryWrapper<>();
qw.eq("account_id", UserContext.getUserHolder().getId());
String startTime = queryPageByTimeDTO.getStartTime();
String endTime = queryPageByTimeDTO.getEndTime();
if (StringUtil.isNullOrEmpty(startTime)) {
startTime = "2024-03-01 00:00:00";
}
if (StringUtil.isNullOrEmpty(endTime)) {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
endTime = now.format(dateTimeFormatter);
}
if (!Objects.isNull(queryPageByTimeDTO.getIsIncome())) {
if (queryPageByTimeDTO.getIsIncome()) {
qw.likeRight("changed_credits", "+");
} else {
qw.likeRight("changed_credits", "-");
}
}
qw.between("create_time", startTime, endTime);
qw.orderByDesc("create_time");
Page<CreditsDetail> pageInfo = new Page<>(queryPageByTimeDTO.getPage(), queryPageByTimeDTO.getSize());
Page<CreditsDetail> orderInfo = baseMapper.selectPage(pageInfo, qw);
if (CollectionUtils.isEmpty(orderInfo.getRecords())) {
return PageBaseResponse.success(new Page<>());
}
return PageBaseResponse.success(orderInfo);
}
public Boolean checkCredits(Long accountId, CreditsEventsEnum event, Integer num) {
String credits = getCredits(accountId);
if (new BigDecimal(credits)
.subtract(new BigDecimal(event.getValue())
.multiply(new BigDecimal(num)))
.compareTo(BigDecimal.ZERO) < 0) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
}

View File

@@ -68,13 +68,13 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
@Resource
private GenerateCancelMapper generateCancelMapper;
@Value("${redis.key.consumptionOrder}")
@Value("${redis.key.orderForGenerate}")
private String consumptionOrderKey;
@Value("${redis.key.cancelSet}")
@Value("${redis.key.generateCancelSet}")
private String cancelSetKey;
@Value("${redis.key.exceptionMap}")
@Value("${redis.key.generateExceptionMap}")
private String exceptionMapKey;
@Value("${redis.key.resultMap}")
@@ -368,7 +368,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
redisUtil.addToZSet(consumptionOrderKey, uuid, maxScore);
// 4、将消息发布到MQ消息队列
rabbitMQService.publishMessage(jsonString);
rabbitMQService.publishMessageToGenerate(jsonString);
// 5、返回唯一id
return new PrepareForGenerateVO(uuid, 2 - trialsCount);

View File

@@ -1,21 +1,30 @@
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.response.PageBaseResponse;
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.dto.QueryPageByTimeDTO;
import com.ai.da.model.vo.AuthPrincipalVo;
import com.ai.da.service.OrderInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Service
@@ -25,27 +34,27 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
@Resource
private ProductMapper productMapper;
/*@Resource
private OrderInfoMapper orderInfoMapper;*/
@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.setTitle(product.getTitle());
orderInfo.setOrderNo(OrderNoUtils.getOrderNo()); //订单号
orderInfo.setProductId(productId);
orderInfo.setTotalFee(product.getPrice()); //分
OrderInfo orderInfo = new OrderInfo();
orderInfo.setAccountId(accountId);
orderInfo.setTitle("积分购买 X" + amount );
orderInfo.setOrderNo(OrderNoUtils.getOrderNo()); //订单号 ??
// orderInfo.setProductId(amount);
orderInfo.setTotalFee(Integer.parseInt(CreditsEventsEnum.PRICE.getValue()) * amount); // 元 HKD
orderInfo.setOrderStatus(OrderStatusEnum.NOT_PAY.getType()); //未支付
orderInfo.setPaymentType(paymentType);
baseMapper.insert(orderInfo);
@@ -169,4 +178,38 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
OrderInfo orderInfo = baseMapper.selectOne(queryWrapper);
return orderInfo;
}
@Override
public PageBaseResponse<OrderInfo> getOrderByPage(QueryPageByTimeDTO queryPageByTimeDTO){
QueryWrapper<OrderInfo> qw = new QueryWrapper<>();
qw.eq("account_id",UserContext.getUserHolder().getId());
String startTime = queryPageByTimeDTO.getStartTime();
String endTime = queryPageByTimeDTO.getEndTime();
if (StringUtil.isNullOrEmpty(startTime)){
startTime = "2024-02-01 00:00:00";
}
if (StringUtil.isNullOrEmpty(endTime)){
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
endTime = now.format(dateTimeFormatter);
}
qw.between("create_time", startTime, endTime);
qw.orderByDesc("create_time");
Page<OrderInfo> pageInfo = new Page<>(queryPageByTimeDTO.getPage(), queryPageByTimeDTO.getSize());
Page<OrderInfo> orderInfo = baseMapper.selectPage(pageInfo, qw);
if (CollectionUtils.isEmpty(orderInfo.getRecords())) {
return PageBaseResponse.success(new Page<>());
}
return PageBaseResponse.success(orderInfo);
}
public void updateOrderNoById(Long id, String orderNo){
OrderInfo orderInfo = new OrderInfo();
orderInfo.setId(id);
orderInfo.setOrderNo(orderNo);
baseMapper.updateById(orderInfo);
}
}

View File

@@ -0,0 +1,597 @@
package com.ai.da.service.impl;
import cn.hutool.core.convert.Convert;
import com.ai.da.common.config.PayPalClient;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.PayPalCheckoutConstant;
import com.ai.da.common.enums.*;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.utils.paypalRequest.AuthenticationRequest;
import com.ai.da.common.utils.paypalRequest.WebhookVerifyRequest;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.mapper.primary.entity.RefundInfo;
import com.ai.da.service.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.paypal.api.payments.Event;
import com.paypal.base.Constants;
import com.paypal.base.SDKUtil;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.PayPalRESTException;
import com.paypal.http.HttpResponse;
import com.paypal.http.exceptions.SerializeException;
import com.paypal.http.serializer.Json;
import com.paypal.orders.*;
import com.paypal.payments.CapturesGetRequest;
import com.paypal.payments.CapturesRefundRequest;
import com.paypal.payments.RefundRequest;
import com.paypal.payments.RefundsGetRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.*;
import static com.ai.da.common.constant.PayPalCheckoutConstant.*;
@Slf4j
@Service
//@RefreshScope
public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
@Value("${paypal.receiver.email}")
private String receiverEmail;
@Value("${paypal.client-id}")
private String clientId;
@Value("${paypal.client-secret}")
private String clientSecret;
@Resource
private PayPalClient payPalClient;
@Resource
private OrderInfoService orderInfoService;
@Resource
private PaymentInfoService paymentInfoService;
@Resource
private RefundInfoService refundsInfoService;
@Resource
private CreditsService creditsService;
@Resource
private RedisUtil redisUtil;
/**
* 创建订单的方法
*/
@Override
@Transactional(rollbackFor = Exception.class)
public HashMap<String, String> createOrder(Integer amount, String returnUrl) throws SerializeException {
// 生成订单
log.info("生成订单");
OrderInfo orderInfo = orderInfoService.createOrderByProductId(amount, PayTypeEnum.PAYPAL.getType());
OrdersCreateRequest request = new OrdersCreateRequest();
request.header("prefer", "return=representation");
request.requestBody(buildRequestBody(String.valueOf(orderInfo.getTotalFee()), returnUrl));
HttpResponse<Order> response = null;
try {
response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
} catch (Exception e) {
log.error("调用paypal订单创建失败失败原因 ===> {}", e.getMessage());
throw new BusinessException("Order creation failed");
}
String approve = "";
assert response != null;
if (response.statusCode() == 201) {
log.info("Status Code = {}, Status = {}, OrderID = {}, Intent = {}", response.statusCode(), response.result().status(), response.result().id(), response.result().checkoutPaymentIntent());
for (LinkDescription link : response.result().links()) {
log.info("Links-{}: {} \tCall Type: {}", link.rel(), link.href(), link.method());
if (link.rel().equals("approve")) {
approve = link.href();
}
}
String totalAmount = response.result().purchaseUnits().get(0).amountWithBreakdown().currencyCode() + ":" + response.result().purchaseUnits().get(0).amountWithBreakdown().value();
log.info("Total Amount: {}", totalAmount);
String json = new JSONObject(new Json().serialize(response.result())).toString(4);
log.info("createOrder response body: {}", json);
}
String orderId = response.result().id();
orderInfoService.updateOrderNoById(orderInfo.getId(), orderId);
HashMap<String, String> returnData = new HashMap<>();
returnData.put("approve", approve);
returnData.put("orderNo", orderId);
// 需要返回地址和订单id
return returnData;
}
// ##Validate Webhook
public Boolean doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
String body = getBody(req);
Map webhookEvent = new ObjectMapper().readValue(body, Map.class);
HashMap<String, Object> webhookRequest = new HashMap<>();
webhookRequest.put("auth_algo", SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-AUTH-ALGO"));
webhookRequest.put("cert_url", SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-CERT-URL"));
webhookRequest.put("transmission_id", SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-TRANSMISSION-ID"));
webhookRequest.put("transmission_sig", SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-TRANSMISSION-SIG"));
webhookRequest.put("transmission_time", SDKUtil.validateAndGet(getHeadersInfo(req), "PAYPAL-TRANSMISSION-TIME"));
webhookRequest.put("webhook_id", PayPalCheckoutConstant.WEBHOOK_ID);
webhookRequest.put("webhook_event", webhookEvent);
WebhookVerifyRequest webhookVerifyRequest = new WebhookVerifyRequest();
webhookVerifyRequest.authorization(getOAuth());
webhookVerifyRequest.requestBody(webhookRequest);
// 验签
HttpResponse<HashMap> verified = payPalClient.client(MODE, clientId, clientSecret).execute(webhookVerifyRequest);
boolean verifyResult = verified.result().get("verification_status").toString().equals("SUCCESS");
if (verifyResult) {
// ### Api Context
APIContext apiContext = new APIContext(clientId, clientSecret, PayPalCheckoutConstant.MODE);
// Set the webhookId that you received when you created this webhook.
apiContext.addConfiguration(Constants.PAYPAL_WEBHOOK_ID, PayPalCheckoutConstant.WEBHOOK_ID);
Boolean result = Event.validateReceivedEvent(apiContext, getHeadersInfo(
req), body);
log.info("Webhook Validated: " + result);
if (result) {
// 处理订单数据
LinkedHashMap<String, LinkedHashMap<String, String>> webhookEventMap = (LinkedHashMap<String, LinkedHashMap<String, String>>) webhookEvent;
String orderId = webhookEventMap.get("resource").get("id");
processOrder(orderId);
return Boolean.TRUE;
}
} else {
log.error("Paypal 验签失败");
}
} catch (PayPalRESTException | InvalidKeyException | NoSuchAlgorithmException | SignatureException e) {
log.error(e.getMessage());
}
return Boolean.FALSE;
}
// Simple helper method to help you extract the headers from HttpServletRequest object.
private static Map<String, String> getHeadersInfo(HttpServletRequest request) {
Map<String, String> map = new HashMap<String, String>();
@SuppressWarnings("rawtypes")
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
return map;
}
// Simple helper method to fetch request data as a string from HttpServletRequest object.
private static String getBody(HttpServletRequest request) throws IOException {
String body;
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = stringBuilder.toString();
log.info("回调参数 ===> {}", body);
return body;
}
/**
* 生成订单主体信息
*/
private OrderRequest buildRequestBody(String price, String returnUrl) {
OrderRequest orderRequest = new OrderRequest();
orderRequest.checkoutPaymentIntent(CAPTURE);
ApplicationContext applicationContext = new ApplicationContext()
.brandName(BRANDNAME)
.landingPage(LANDINGPAGE)
.cancelUrl(returnUrl).returnUrl(returnUrl)
.userAction(USERACTION)
.shippingPreference(SHIPPINGPREFERENCE);
orderRequest.applicationContext(applicationContext);
List<PurchaseUnitRequest> purchaseUnitRequests = new ArrayList<PurchaseUnitRequest>();
PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest()
.amountWithBreakdown(new AmountWithBreakdown()
.amountBreakdown(new AmountBreakdown())
.currencyCode(CurrencyCodesEnum.HONG_KONG_DOLLAR.getCode())
.value(price));
purchaseUnitRequests.add(purchaseUnitRequest);
orderRequest.purchaseUnits(purchaseUnitRequests);
return orderRequest;
}
/*@Override
public String callback(@SuppressWarnings("rawtypes") Map map) {
log.info("paypal支付通知正在执行");
log.info("通知参数 ===> {}", map);
// log.info(map.toString());
String outTradeNo = (String) map.get("invoice");
String paymentStatus = (String) map.get("payment_status");
String amount = (String) map.get("mc_gross");
String currency = (String) map.get("mc_currency");
String paymentId = (String) map.get("txn_id");
String parentPaymentId = (String) map.get("parent_txn_id");
log.info("商家订单号 = {}", outTradeNo);
log.info("订单状态 = {}", paymentStatus);
log.info("金额 = {}", amount);
log.info("币种 = {}", currency);
log.info("流水号 = {}", paymentId);
log.info("父流水号 = {}", parentPaymentId);
if (!receiverEmail.equals(map.get("receiver_email"))) {
log.info("FAIL = 商户id错误, outTradeNo = {}", outTradeNo);
return "failure";
}
if ("Completed".equals(paymentStatus)) {
//进行数据库操作
//
//
log.info("支付成功,状态为=COMPLETED");
return "success";
}
if ("Refunded".equals(paymentStatus)) {
//进行数据库操作
//
//
log.info("退款成功");
return "success";
}
if ("Pending".equals(paymentStatus) && StringUtils.isEmpty(parentPaymentId)) {
String pendingReason = String.valueOf(map.get("pending_reason"));
//进行数据库操作
//
//
log.info("订单支付成功,状态为=PENDING产生此状态的原因是 {}", pendingReason);
return "success";
}
if (StringUtils.isEmpty(parentPaymentId)) {
if (PayPalCheckoutConstant.PAYMENT_STATUS_REVERSED.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_CANCELED_REVERSAL.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_DENIED.equals(paymentStatus)) {
String reasonCode = String.valueOf(map.get("reason_code"));
//进行数据库操作(状态修改)
//
//
log.info("订单异常,请尽快查看处理,状态为={},产生此状态的原因是 {} ", paymentStatus, reasonCode);
return PayPalCheckoutConstant.SUCCESS;
}
if (PayPalCheckoutConstant.PAYMENT_STATUS_EXPIRED.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_CREATED.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_FAILED.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_PROCESSED.equals(paymentStatus)
|| PayPalCheckoutConstant.PAYMENT_STATUS_VOIDED.equals(paymentStatus)) {
//进行数据库操作(状态修改)
//
//
log.info("其他订单状态,订单异常,请尽快查看处理, 状态={}", paymentStatus);
return PayPalCheckoutConstant.SUCCESS;
}
}
return "failure";
}*/
/**
* 查询订单信息
*
* @param orderNo
* @return
* @throws SerializeException
*/
public String queryOrder(String orderNo) throws SerializeException {
OrdersGetRequest request = new OrdersGetRequest(orderNo);
HttpResponse<Order> response = null;
try {
response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
} catch (Exception e) {
log.error("paypal订单查询失败失败原因 ===> {}", e.getMessage());
}
System.out.println("Status Code: " + response.statusCode());
System.out.println("Status: " + response.result().status());
System.out.println("Order id: " + response.result().id());
if (response.result().purchaseUnits().get(0).payments() != null) {
List<Capture> captures = response.result().purchaseUnits().get(0).payments().captures();
if (captures != null) {
for (Capture capture : captures) {
System.out.println("\t订单编号= " + capture.invoiceId() + "\tCapture Id= " + capture.id() + "\tCapture status= " + capture.status() + "\tCapture amount= " + capture.amount().currencyCode() + ":" + capture.amount().value());
}
}
List<Refund> refunds = response.result().purchaseUnits().get(0).payments().refunds();
if (refunds != null) {
for (Refund refund : refunds) {
System.out.println("\t售后编号= " + refund.invoiceId() + "\tRefund Id= " + refund.id() + "\tRefund status= " + refund.status() + "\tRefund amount= " + refund.amount().currencyCode() + ":" + refund.amount().value());
}
}
}
System.out.println("Links: ");
for (com.paypal.orders.LinkDescription link : response.result().links()) {
System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
}
System.out.println("Full response body:");
String json = new JSONObject(new Json().serialize(response.result())).toString(4);
System.out.println(json);
return null;
}
/**
* 用户授权支付成功,进行扣款操作
*/
@Transactional(rollbackFor = Exception.class)
public Order captureOrder(String orderId) {
OrdersCaptureRequest request = new OrdersCaptureRequest(orderId);
request.requestBody(new OrderRequest());
PayPalClient payPalClient = new PayPalClient();
HttpResponse<Order> response;
try {
response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
} catch (Exception e) {
log.error("调用paypal扣款失败失败原因 ===> {}", e.getMessage());
throw new BusinessException("Order deduction failed.");
}
log.info("Status Code = {}, Status = {}, OrderID = {}", response.statusCode(), response.result().status(), response.result().id());
for (LinkDescription link : response.result().links()) {
log.info("Links-{}: {} \tCall Type: {}", link.rel(), link.href(), link.method());
}
for (PurchaseUnit purchaseUnit : response.result().purchaseUnits()) {
for (Capture capture : purchaseUnit.payments().captures()) {
log.info("Capture id: {}", capture.id());
log.info("status: {}", capture.status());
log.info("invoice_id: {}", capture.invoiceId());
if ("COMPLETED".equals(capture.status())) {
//进行数据库操作修改订单状态为已支付成功尽快发货配合回调和CapturesGet查询确定成功
log.info("支付成功,状态为=COMPLETED");
}
if ("PENDING".equals(capture.status())) {
log.info("status_details: {}", capture.captureStatusDetails().reason());
String reason = "PENDING";
if (capture.captureStatusDetails() != null && capture.captureStatusDetails().reason() != null) {
reason = capture.captureStatusDetails().reason();
}
//进行数据库操作修改订单状态为已支付成功但触发了人工审核请审核通过后再发货配合回调和CapturesGet查询确定成功
log.info("支付成功,状态为=PENDING : {}", reason);
}
}
}
Payer buyer = response.result().payer();
log.info("Buyer Email Address: {}", buyer.email());
log.info("Buyer Name: {} {}", buyer.name().givenName(), buyer.name().surname());
// String jsonString = JSON.toJSONString(response.result());
String json = null;
try {
json = new JSONObject(new Json().serialize(response.result())).toString(4);
} catch (SerializeException e) {
log.warn("response序列化出错具体信息 ===> {}", e.getMessage());
}
log.info("captureOrder response body: {}", json);
// return Convert.toStr(new Json().serialize(response.result()));
return response.result();
}
/**
* 查询扣款
*/
public String queryCapture(String orderNo) throws IOException {
CapturesGetRequest request = new CapturesGetRequest("扣款id CaptureOrder生成");
HttpResponse<com.paypal.payments.Capture> response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
System.out.println("Status Code: " + response.statusCode());
System.out.println("Status: " + response.result().status());
System.out.println("Capture ids: " + response.result().id());
System.out.println("Links: ");
for (com.paypal.payments.LinkDescription link : response.result().links()) {
System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
}
System.out.println("Full response body:");
System.out.println(new JSONObject(new Json().serialize(response.result())).toString(4));
return null;
}
/**
* 申请退款
*/
@Transactional(rollbackFor = Exception.class)
public Boolean refundOrder(String orderId, String reason) throws IOException {
RefundInfo refundByOrderNo = refundsInfoService.createRefundByOrderNo(orderId, reason);
OrdersGetRequest ordersGetRequest = new OrdersGetRequest(orderId);
PayPalClient payPalClient = new PayPalClient();
HttpResponse<com.paypal.orders.Order> ordersGetResponse = null;
ordersGetRequest.authorization("Bearer " + getOAuth());
boolean result;
try {
ordersGetResponse = payPalClient.client(MODE, clientId, clientSecret).execute(ordersGetRequest);
} catch (Exception e) {
log.error("调用paypal订单查询失败失败原因 ===> {}", e.getMessage());
throw new BusinessException("Order query failed");
}
String captureId = ordersGetResponse.result().purchaseUnits().get(0).payments().captures().get(0).id();
CapturesRefundRequest request = new CapturesRefundRequest(captureId);
request.authorization("Bearer " + getOAuth());
request.prefer("return=representation");
OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderId);
request.requestBody(buildRefundRequestBody(String.valueOf(orderInfo.getTotalFee()), reason));
HttpResponse<com.paypal.payments.Refund> response = null;
try {
response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
} catch (IOException e) {
log.error("调用paypal退款申请失败失败原因 {}", e.getMessage());
throw new BusinessException("Request for refund failed");
}
log.info("Status Code = {}, Status = {}, RefundID = {}", response.statusCode(), response.result().status(), response.result().id());
if ("COMPLETED".equals(response.result().status())) {
//进行数据库操作,修改状态为已退款(配合回调和退款查询确定退款成功)
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.REFUND_SUCCESS);
refundsInfoService.updateRefundForPayPal(
refundByOrderNo.getId(),
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 {
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.REFUND_ABNORMAL);
//更新退款单
refundsInfoService.updateRefundForPayPal(
refundByOrderNo.getId(),
response.result().id(),
new Gson().toJson(response.result(), com.paypal.payments.Refund.class),
AliPayTradeStateEnum.REFUND_ERROR.getType()); //退款失败
result = Boolean.FALSE;
}
for (com.paypal.payments.LinkDescription link : response.result().links()) {
log.info("Links-{}: {} \tCall Type: {}", link.rel(), link.href(), link.method());
}
String json = new JSONObject(new Json().serialize(response.result())).toString(4);
log.info("refundOrder response body: {}", json);
return result;
}
public RefundRequest buildRefundRequestBody(String price, String reason) {
RefundRequest refundRequest = new RefundRequest();
com.paypal.payments.Money money = new com.paypal.payments.Money();
money.currencyCode(CurrencyCodesEnum.HONG_KONG_DOLLAR.getCode());
money.value(price);
refundRequest.amount(money);
// refundRequest.invoiceId("P2020052514440001");
refundRequest.noteToPayer(reason);
try {
log.info("refund order body : {}", Convert.toStr(new Json().serialize(refundRequest)));
} catch (SerializeException e) {
throw new RuntimeException(e);
}
return refundRequest;
}
/**
* 查询退款
*/
public String queryRefund(String orderNo) throws IOException {
RefundsGetRequest request = new RefundsGetRequest("退款id RefundOrder生成");
HttpResponse<com.paypal.payments.Refund> response = payPalClient.client(MODE, clientId, clientSecret).execute(request);
System.out.println("Status Code: " + response.statusCode());
System.out.println("Status: " + response.result().status());
System.out.println("Refund Id: " + response.result().id());
System.out.println("Links: ");
for (com.paypal.payments.LinkDescription link : response.result().links()) {
System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
}
System.out.println("Full response body:");
System.out.println(new JSONObject(new Json().serialize(response.result())).toString(4));
return null;
}
public String getOAuth() {
// 1、判断缓存区是否有该token
Boolean hasKey = redisUtil.hasKey(PAYPAL_TOKEN_KEY);
if (hasKey) {
return redisUtil.getFromString(PAYPAL_TOKEN_KEY);
// return "A21AAKnpozur9r9omqQ2ge5aXHBBdEERi5F8FIgNYOjhhO2N7rjmkz2irh2lScpBO3s3Cqukw3eZkpYZ4YWE7rIacjv7MHmow";
}
// 2、无或者过期重新获取token返回
AuthenticationRequest authenticationRequest = new AuthenticationRequest();
authenticationRequest.authorization(clientId, clientSecret);
try {
HttpResponse<HashMap> authResult = payPalClient.client(MODE, clientId, clientSecret).execute(authenticationRequest);
String accessToken = authResult.result().get("access_token").toString();
long expiresIn = Long.parseLong(authResult.result().get("expires_in").toString());
// 3、存redis
redisUtil.addToString(PAYPAL_TOKEN_KEY, accessToken, expiresIn);
return accessToken;
} catch (IOException e) {
log.error("获取paypal token失败失败原因 ===> {}", e.getMessage());
throw new BusinessException(e);
}
}
// 处理当前订单
@Transactional(rollbackFor = Exception.class)
public void processOrder(String orderId) {
// 1、确定当前订单是否已经被扣款
OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderId);
if (orderInfo.getOrderStatus().equals(OrderStatusEnum.SUCCESS.getType())) {
// 直接返回
return;
}
// 发起扣款请求
Order capturedOrder = captureOrder(orderId);
// 业务处理
if (PayPalOrderStatusEnum.COMPLETED.getStatus().equals(capturedOrder.status())) {
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.SUCCESS);
//记录支付日志
paymentInfoService.createPaymentInfoForPayPal(capturedOrder);
// 添加积分变更记录
creditsService.insertToCreditsDetail(orderInfo.getAccountId(),
CreditsEventsEnum.BUY_CREDITS.getName() + "--PayPal",
CreditsEventsEnum.BUY_CREDITS.getValue(),
"positive");
// 更新积分
creditsService.buyCredits(orderInfo.getAccountId(), orderInfo.getTotalFee() / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
}
}
}

View File

@@ -6,6 +6,7 @@ import com.ai.da.mapper.primary.entity.PaymentInfo;
import com.ai.da.service.PaymentInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.paypal.orders.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -70,8 +71,8 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
String tradeStatus = params.get("trade_status");
//交易金额
String totalAmount = params.get("total_amount");
int totalAmountInt = new BigDecimal(totalAmount).multiply(new BigDecimal("100")).intValue();
// int totalAmountInt = new BigDecimal(totalAmount).multiply(new BigDecimal("100")).intValue();
int totalAmountInt = new BigDecimal(totalAmount).intValue();
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.setOrderNo(orderNo);
@@ -87,4 +88,27 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
baseMapper.insert(paymentInfo);
}
@Override
public void createPaymentInfoForPayPal(Order order) {
log.info("记录支付日志");
// int totalAmountInt = new BigDecimal(totalAmount).multiply(new BigDecimal("100")).intValue();
int totalAmountInt = new BigDecimal(order.purchaseUnits().get(0).payments().captures().get(0).amount().value()).intValue();
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.setOrderNo(order.id());
paymentInfo.setPaymentType(PayTypeEnum.PAYPAL.getType());
paymentInfo.setTransactionId(order.id());
paymentInfo.setTradeType("电脑网站支付");
paymentInfo.setTradeState(order.status());
paymentInfo.setPayerTotal(totalAmountInt);
Gson gson = new Gson();
String json = gson.toJson(order, Order.class);
paymentInfo.setContent(json);
baseMapper.insert(paymentInfo);
}
}

View File

@@ -27,10 +27,14 @@ public class RabbitMQServiceImpl implements RabbitMQService {
private MQPublisher mqPublisher;
@Override
public void publishMessage(String message) {
public void publishMessageToGenerate(String message) {
mqPublisher.sendGenerateMessage(message);
}
@Override
public void publishMessageToSR(String message) {
mqPublisher.sendSRMessage(message);
}
@Override
public Integer getMessageCount(String queueUrl) {

View File

@@ -40,8 +40,8 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
RefundInfo refundInfo = new RefundInfo();
refundInfo.setOrderNo(orderNo);//订单编号
refundInfo.setRefundNo(OrderNoUtils.getRefundNo());//退款单编号
refundInfo.setTotalFee(orderInfo.getTotalFee());//原订单金额()
refundInfo.setRefund(orderInfo.getTotalFee());//退款金额()
refundInfo.setTotalFee(orderInfo.getTotalFee());//原订单金额()
refundInfo.setRefund(orderInfo.getTotalFee());//退款金额()
refundInfo.setReason(reason);//退款原因
//保存退款订单
@@ -50,7 +50,6 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
return refundInfo;
}
/**
* 记录退款记录
* @param content
@@ -68,7 +67,6 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
//设置要修改的字段
RefundInfo refundInfo = new RefundInfo();
refundInfo.setRefundId(resultMap.get("refund_id"));//微信支付退款单号
//查询退款和申请退款中的返回参数
@@ -120,8 +118,8 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
refundInfo.setOrderNo(orderNo);//订单编号
refundInfo.setRefundNo(OrderNoUtils.getRefundNo());//退款单编号
refundInfo.setTotalFee(orderInfo.getTotalFee());//原订单金额()
refundInfo.setRefund(orderInfo.getTotalFee());//退款金额()
refundInfo.setTotalFee(orderInfo.getTotalFee());//原订单金额()
refundInfo.setRefund(orderInfo.getTotalFee());//退款金额()
refundInfo.setReason(reason);//退款原因
//保存退款订单
@@ -153,4 +151,23 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
}
@Override
public void updateRefundForPayPal(Long id, String refundId,String content, String refundStatus) {
//根据退款单编号修改退款单
// QueryWrapper<RefundInfo> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("order_no", orderNo);
//设置要修改的字段
RefundInfo refundInfo = new RefundInfo();
refundInfo.setId(id);
refundInfo.setRefundNo(null);
refundInfo.setRefundId(refundId);
refundInfo.setRefundStatus(refundStatus);//退款状态
refundInfo.setContentReturn(content);//将全部响应结果存入数据库的content字段
//更新退款单
// baseMapper.update(refundInfo, queryWrapper);
baseMapper.updateById(refundInfo);
}
}

View File

@@ -0,0 +1,204 @@
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.common.response.PageBaseResponse;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.mapper.primary.TaskListMapper;
import com.ai.da.mapper.primary.entity.TaskList;
import com.ai.da.model.dto.QueryTaskHistoryDTO;
import com.ai.da.model.dto.SuperResolutionDTO;
import com.ai.da.model.dto.TaskDTO;
import com.ai.da.python.PythonService;
import com.ai.da.service.CreditsService;
import com.ai.da.service.RabbitMQService;
import com.ai.da.service.SuperResolutionService;
import com.ai.da.service.TaskListService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Slf4j
@Service
public class SuperResolutionServiceImpl extends ServiceImpl<TaskListMapper, TaskList> implements SuperResolutionService {
public static final Integer creditsConsumption = 100;
@Resource
private CreditsService creditsService;
@Resource
private RabbitMQService rabbitMQService;
@Resource
private PythonService pythonService;
@Resource
private TaskListService taskListService;
@Resource
private RedisUtil redisUtil;
@Value("${redis.key.orderForSR}")
private String orderForSR;
@Value("${redis.key.resultMap}")
private String resultMapKey;
@Value("${minio.bucketName.users}")
private String usersBucket;
@Value("${minio.endpoint}")
private String endpoint;
@Override
@Transactional(rollbackFor = Exception.class)
public List<String> prepareForSR(List<SuperResolutionDTO> superResolutionDTOList) {
Long accountId = UserContext.getUserHolder().getId();
// 1、判断用户当前积分是否够本次超分消耗
Boolean credits = creditsService.checkCredits(accountId, CreditsEventsEnum.SUPER_RESOLUTION, superResolutionDTOList.size());
// todo 积分扣除待升级
if (credits) {
// 先扣除积分,后失败后再加上
creditsService.creditsDecrease(accountId, CreditsEventsEnum.SUPER_RESOLUTION.getName());
} else {
throw new BusinessException("Not enough Credits");
}
ArrayList<String> uuidList = new ArrayList<>();
for (SuperResolutionDTO superResolutionDTO : superResolutionDTOList) {
// 2、生成唯一id 使用uuid
String uuid = UUID.randomUUID().toString();
int num = 1;
// 判断已经正常生成结果的uuid或正在排队的uuid中是否有相同的id
while ((redisUtil.isElementExistsInMap(resultMapKey, uuid) ||
redisUtil.isElementExistsInZSet(orderForSR, uuid))
&& num < 10) {
uuid = UUID.randomUUID().toString();
num++;
}
uuid += "-" + accountId;
uuidList.add(uuid);
// 3、截取minio地址 (前提是该url是由minio地址转换来的)
String inputString = superResolutionDTO.getImages();
String replace = inputString.replace(endpoint + "/", "");
String minioPath = replace.substring(0, replace.indexOf("?"));
// log.info(minioPath);
superResolutionDTO.setImages(minioPath);
superResolutionDTO.setUniqueId(uuid);
String jsonString = JSON.toJSONString(superResolutionDTO);
// 4、将数据存到数据库
TaskList taskList = new TaskList();
taskList.setAccountId(accountId);
taskList.setTaskType("SR");
taskList.setInputUrl(superResolutionDTO.getImages());
taskList.setScale(superResolutionDTO.getScale());
taskList.setStatus("Waiting");
taskList.setTaskId(superResolutionDTO.getUniqueId());
taskList.setCreateTime(LocalDateTime.now());
baseMapper.insert(taskList);
// 5、加入任务列表 设置状态为 等待中
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String name = superResolutionDTO.getImages();
taskListService.addToTaskListRedis(new TaskDTO<>(uuid, "SR", name.substring(name.lastIndexOf("/") + 1), superResolutionDTO, "Waiting", LocalDateTime.now().format(dateTimeFormatter)));
// 6、将消息发布到MQ消息队列
log.info("发送消息到SR_QUEUE参数 {}", jsonString);
rabbitMQService.publishMessageToSR(jsonString);
}
// 6、返回唯一id列表
return uuidList;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void SR(SuperResolutionDTO superResolutionDTO) throws Exception {
// 1、向模型发起请求
pythonService.superResolution(superResolutionDTO);
// 2、更新状态
updateSROutput(superResolutionDTO.getUniqueId(), "Executing", null);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void setSRResult(String taskId, String output, String status) {
Long accountId = Long.parseLong(taskId.substring(taskId.lastIndexOf("-") + 1));
// 获取结果后更新
updateSROutput(taskId, status, output);
if ("success".equals(status)) {
// 3、记录积分变更
creditsService.insertToCreditsDetail(accountId,
CreditsEventsEnum.SUPER_RESOLUTION.getName(),
CreditsEventsEnum.SUPER_RESOLUTION.getValue(),
"negative");
} else {
// 将之前扣除的积分返还
creditsService.creditsIncrease(accountId, CreditsEventsEnum.SUPER_RESOLUTION.getName());
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateSROutput(String taskId, String status, String output) {
UpdateWrapper<TaskList> uw = new UpdateWrapper<>();
uw.eq("task_id", taskId);
uw.set("output_url", output);
uw.set("status", status);
baseMapper.update(null, uw);
}
// 分页查询
@Override
public PageBaseResponse<TaskList> getTaskHistoryPage(QueryTaskHistoryDTO queryTaskHistoryDTO) {
QueryWrapper<TaskList> qw = new QueryWrapper<>();
qw.eq("account_id", UserContext.getUserHolder().getId());
String startTime = queryTaskHistoryDTO.getStartTime();
String endTime = queryTaskHistoryDTO.getEndTime();
if (StringUtil.isNullOrEmpty(startTime)) {
startTime = "2024-03-01 00:00:00";
}
if (StringUtil.isNullOrEmpty(endTime)) {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
endTime = now.format(dateTimeFormatter);
}
qw.between("create_time", startTime, endTime);
qw.orderByDesc("create_time");
Page<TaskList> pageInfo = new Page<>(queryTaskHistoryDTO.getPage(), queryTaskHistoryDTO.getSize());
Page<TaskList> orderInfo = baseMapper.selectPage(pageInfo, qw);
if (CollectionUtils.isEmpty(orderInfo.getRecords())) {
return PageBaseResponse.success(new Page<>());
}
return PageBaseResponse.success(orderInfo);
}
}

View File

@@ -0,0 +1,123 @@
package com.ai.da.service.impl;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.utils.MinioUtil;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.mapper.primary.TaskListMapper;
import com.ai.da.mapper.primary.entity.TaskList;
import com.ai.da.model.dto.QueryTaskHistoryDTO;
import com.ai.da.model.dto.SuperResolutionDTO;
import com.ai.da.model.dto.TaskDTO;
import com.ai.da.model.vo.TaskVO;
import com.ai.da.service.SuperResolutionService;
import com.ai.da.service.TaskListService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.lang.reflect.Type;
import java.time.format.DateTimeFormatter;
import java.util.*;
@Service
@Slf4j
public class TaskListServiceImpl extends ServiceImpl<TaskListMapper, TaskList> implements TaskListService {
@Resource
private RedisUtil redisUtil;
@Value("${redis.key.taskList}")
private String taskListKey;
@Resource
private MinioUtil minioUtil;
@Resource
private SuperResolutionService superResolutionService;
@Override
@Transactional(rollbackFor = Exception.class)
public List<TaskDTO<SuperResolutionDTO>> getExecTask(List<String> taskIdList) {
Long id = UserContext.getUserHolder().getId();
// Set<String> keys = redisUtil.getKeysFromString(taskListKey + ":" + id + ":*");
ArrayList<TaskDTO<SuperResolutionDTO>> taskDTOS = new ArrayList<>();
taskIdList.forEach(taskId -> {
String taskDetail = redisUtil.getFromString(taskListKey + ":" + id + ":" + taskId);
Gson gson = new Gson();
Type type = new TypeToken<TaskDTO<SuperResolutionDTO>>() {
}.getType();
TaskDTO<SuperResolutionDTO> taskDTO = gson.fromJson(taskDetail, type);
if (Objects.isNull(taskDTO)) {
// 未执行成功的taskId在redis被删除将该条数据在db中的状态改为失败
superResolutionService.updateSROutput(taskId, "fail", null);
taskDTOS.add(new TaskDTO<>());
} else {
SuperResolutionDTO inputParam = taskDTO.getInputParam();
inputParam.setImages(minioUtil.getPresignedUrl(inputParam.getImages(), 24 * 60));
taskDTO.setOutputImage(StringUtil.isNullOrEmpty(taskDTO.getOutputImage()) ? null : minioUtil.getPresignedUrl(taskDTO.getOutputImage(), 24 * 60));
taskDTOS.add(taskDTO);
}
});
return taskDTOS;
}
public void addToTaskListRedis(TaskDTO<SuperResolutionDTO> taskDTO) {
String key = taskListKey + ":" + UserContext.getUserHolder().getId() + ":" + taskDTO.getTaskId();
redisUtil.addToString(key, new Gson().toJson(taskDTO), 24L * 60 * 60 * 3);
}
// 3、更新任务状态
@Override
public void updateTaskStatusOrOutputRedis(String taskId, String status, String output) {
String key = taskListKey + ":" + taskId.substring(taskId.lastIndexOf("-") + 1) + ":" + taskId;
String taskDetail = redisUtil.getFromString(key);
Gson gson = new Gson();
Type type = new TypeToken<TaskDTO<SuperResolutionDTO>>() {
}.getType();
TaskDTO<SuperResolutionDTO> taskDTO = gson.fromJson(taskDetail, type);
taskDTO.setStatus(status);
if (!StringUtil.isNullOrEmpty(output)) {
taskDTO.setOutputImage(output);
}
Long expire = redisUtil.getExpire(key);
redisUtil.addToString(key, new Gson().toJson(taskDTO), expire);
}
@Override
public PageBaseResponse<TaskVO> getAllTask(QueryTaskHistoryDTO queryTaskHistoryDTO) {
PageBaseResponse<TaskVO> response = new PageBaseResponse<>();
ArrayList<TaskVO> taskLists = new ArrayList<>();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
switch (queryTaskHistoryDTO.getType()) {
case "SR":
PageBaseResponse<TaskList> taskHistoryPage = superResolutionService.getTaskHistoryPage(queryTaskHistoryDTO);
List<TaskList> srHistory = taskHistoryPage.getContent();
srHistory.forEach(s -> {
// 成功失败的都返回
TaskVO task = new TaskVO();
task.setImageName(s.getInputUrl().substring(s.getInputUrl().lastIndexOf("/") + 1));
task.setInputImage(minioUtil.getPresignedUrl(s.getInputUrl(), 24 * 60));
task.setOutputImage(StringUtil.isNullOrEmpty(s.getOutputUrl()) ? null : minioUtil.getPresignedUrl(s.getOutputUrl(), 24 * 60));
task.setStatus(s.getStatus());
task.setTaskId(s.getTaskId());
task.setCreateDate(s.getCreateTime().format(dateTimeFormatter));
task.setOtherInput("×" + s.getScale());
taskLists.add(task);
});
BeanUtils.copyProperties(taskHistoryPage, response);
response.setContent(taskLists);
break;
case "GENERATE":
log.info("未知任务类型--GENERATE");
}
return response;
}
}