Преглед изворни кода

取消订单 检查是否支付

yuhongqi пре 4 дана
родитељ
комит
cca4a80e5c

+ 113 - 0
fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java

@@ -473,6 +473,15 @@ public class MallStoreTask
             log.info("[cancelUnpayOrder] 发现{}个超时未支付订单,开始取消", orderList.size());
             for (FsStoreOrderScrm order : orderList) {
                 try {
+                    StringBuilder bankStatusDesc = new StringBuilder();
+                    if (!isBankOrderPendingPay(order, bankStatusDesc)) {
+                        String remark = String.format("[超时取消跳过 %s] 银行订单非待支付(%s),禁止自动取消",
+                                DateUtils.getTime(), bankStatusDesc);
+                        appendOrderRemark(order, remark);
+                        log.warn("[cancelUnpayOrder] 跳过取消,orderId={}, orderCode={}, bankStatus={}",
+                                order.getId(), order.getOrderCode(), bankStatusDesc);
+                        continue;
+                    }
                     // cancelOrder内部已处理活动库存回滚(refundStock -> refundActivityStock)
                     orderService.cancelOrder(order.getId());
                     log.info("[cancelUnpayOrder] 超时订单取消成功,orderId={}, orderType={}, associatedId={}",
@@ -486,6 +495,110 @@ public class MallStoreTask
         }
     }
 
+    /**
+     * 取消前校验银行侧是否仍为待支付。
+     * 汇付:trans_stat=S/P 视为非待支付;易宝:status=100 且 state=0 视为已支付。
+     */
+    private boolean isBankOrderPendingPay(FsStoreOrderScrm order, StringBuilder bankStatusDesc) {
+        List<FsStorePaymentScrm> payments = fsStorePaymentMapper.selectNoPayByOrderId(order.getId());
+        if (payments == null || payments.isEmpty()) {
+            return true;
+        }
+        boolean hasBankTrade = false;
+        for (FsStorePaymentScrm payment : payments) {
+            if (StringUtils.isEmpty(payment.getTradeNo())) {
+                continue;
+            }
+            hasBankTrade = true;
+            if (StringUtils.isNotEmpty(payment.getAppId())) {
+                if (!checkHuiFuPendingPay(payment, bankStatusDesc)) {
+                    return false;
+                }
+            } else {
+                if (!checkYbPayPendingPay(payment, bankStatusDesc)) {
+                    return false;
+                }
+            }
+        }
+        return !hasBankTrade || bankStatusDesc.length() == 0;
+    }
+
+    private boolean checkHuiFuPendingPay(FsStorePaymentScrm payment, StringBuilder bankStatusDesc) {
+        V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
+        request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
+        request.setOrgHfSeqId(payment.getTradeNo());
+        request.setAppId(payment.getAppId());
+        try {
+            HuiFuQueryOrderResult result = huiFuService.queryOrder(request);
+            if (result == null) {
+                bankStatusDesc.append("汇付无返回");
+                return true;
+            }
+            if (!"00000000".equals(result.getResp_code())) {
+                return true;
+            }
+            if ("S".equals(result.getTrans_stat())) {
+                bankStatusDesc.append("汇付已成功(S)");
+                return false;
+            }
+            if ("P".equals(result.getTrans_stat())) {
+                bankStatusDesc.append("汇付处理中(P)");
+                return false;
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("[cancelUnpayOrder] 汇付查询失败 paymentId={}", payment.getPaymentId(), e);
+            bankStatusDesc.append("汇付查询异常");
+            return false;
+        }
+    }
+
+    private boolean checkYbPayPendingPay(FsStorePaymentScrm payment, StringBuilder bankStatusDesc) {
+        try {
+            OrderQueryDTO query = new OrderQueryDTO();
+            query.setUpOrderId(payment.getTradeNo());
+            OrderResult orderResult = ybPayService.getOrder(query);
+            if (orderResult == null) {
+                if (StringUtils.isNotEmpty(payment.getPayCode())) {
+                    query = new OrderQueryDTO();
+                    query.setLowOrderId("store-" + payment.getPayCode());
+                    orderResult = ybPayService.getOrder(query);
+                }
+            }
+            if (orderResult == null) {
+                return true;
+            }
+            if ("100".equals(orderResult.getStatus()) && "0".equals(orderResult.getState())) {
+                bankStatusDesc.append("易宝已支付");
+                return false;
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("[cancelUnpayOrder] 易宝查询失败 paymentId={}", payment.getPaymentId(), e);
+            bankStatusDesc.append("易宝查询异常");
+            return false;
+        }
+    }
+
+    private void appendOrderRemark(FsStoreOrderScrm order, String appendRemark) {
+        if (order == null || StringUtils.isEmpty(appendRemark)) {
+            return;
+        }
+        String oldRemark = StringUtils.defaultString(order.getRemark());
+        if (oldRemark.contains(appendRemark)) {
+            return;
+        }
+        String newRemark = StringUtils.isEmpty(oldRemark) ? appendRemark : oldRemark + ";" + appendRemark;
+        if (newRemark.length() > 500) {
+            newRemark = newRemark.substring(newRemark.length() - 500);
+        }
+        FsStoreOrderScrm update = new FsStoreOrderScrm();
+        update.setId(order.getId());
+        update.setRemark(newRemark);
+        fsStoreOrderMapper.updateFsStoreOrder(update);
+        order.setRemark(newRemark);
+    }
+
 
     //每天执行一次
     public void userMoneyOp()

+ 188 - 27
fs-user-app/src/main/java/com/fs/app/controller/store/CompanyUserScrmController.java

@@ -7,14 +7,18 @@ import cn.hutool.extra.qrcode.QrCodeUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.fs.aiSoundReplication.VoiceCloneController;
+import com.fs.aiSoundReplication.util.FileToMultipartConverterUtil;
 import com.fs.app.annotation.Login;
 import com.fs.app.controller.AppBaseController;
 import com.fs.app.param.FsBindCompanyUserParam;
 import com.fs.common.config.FSConfig;
+import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
+import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.sign.Md5Utils;
@@ -24,6 +28,7 @@ import com.fs.company.domain.CompanyUserCard;
 import com.fs.company.domain.CompanyUserUser;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.company.param.CompanyUserLoginParam;
+import com.fs.company.param.VcCompanyUser;
 import com.fs.company.param.companyUserAddPrintParam;
 import com.fs.company.service.ICompanyUserCardService;
 import com.fs.company.service.ICompanyUserService;
@@ -34,7 +39,9 @@ import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.vo.AudioVO;
 import com.fs.framework.security.SecurityUtils;
+import com.fs.his.utils.ConfigUtil;
 import com.fs.his.utils.PhoneUtil;
+import com.fs.hisStore.enums.SysConfigEnum;
 import com.fs.sop.domain.QwSopTempVoice;
 import com.fs.sop.service.IQwSopTempVoiceService;
 import com.fs.system.oss.CloudStorageService;
@@ -52,15 +59,19 @@ import org.apache.http.util.EntityUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
@@ -95,6 +106,14 @@ public class CompanyUserScrmController extends AppBaseController {
     private FastgptChatVoiceHomoMapper fastgptChatVoiceHomoMapper;
     @Autowired
     AiHostProper aiHostProper;
+    @Autowired
+    private ConfigUtil configUtil;
+    @Autowired
+    private VoiceCloneController voiceCloneController;
+    private static final String VOICE_CLONE_CACHE_KEY = "voice:clone:company:user:id";
+    private static final Integer CACHE_TTL = 3600;
+
+
 
     @PostMapping("/login")
     public R Login(@RequestBody CompanyUserLoginParam param, HttpServletRequest request){
@@ -298,31 +317,43 @@ public class CompanyUserScrmController extends AppBaseController {
         //更新销售员工声纹
         companyUser.setVoicePrintUrl(wavUrl);
         companyUserMapper.updateCompanyUser(companyUser);
-
-        try {
-            CloseableHttpClient httpClient = HttpClients.createDefault();
-            HttpPost httpPost = new HttpPost(aiHostProper.getCommonApi()+"/app/common/addCompanyAudio");
-            String json = "{\"url\":\""+wavUrl+"\",\"id\":\""+userId+"\"}";
-            StringEntity entity = new StringEntity(json);
-            httpPost.setEntity(entity);
-            httpPost.setHeader("Content-type", "application/json");
-            HttpResponse response = httpClient.execute(httpPost);
-
-            if (response.getStatusLine().getStatusCode() == 200) {
-                String responseBody = EntityUtils.toString(response.getEntity());
-                JSONObject jsonObject = JSON.parseObject(responseBody);
-                Integer code = (Integer)jsonObject.get("code");
-                if (code==200){
-                    voiceService.insertQwSopTempVoiceModel(userId);
-                    return R.ok();
-                }
-            } else {
-                return R.error();
+        JSONObject vcConfig = configUtil.generateConfigByKey(SysConfigEnum.VS_CONFIG.getKey());
+        if (vcConfig != null && !vcConfig.isEmpty() &&
+//                !vcConfig.equals(new JSONObject()) &&
+                "2".equals(vcConfig.getString("type"))) {
+            /*走豆包直接不走原逻辑*/
+            AjaxResult ajaxResult = uploadVoice(userId, wavUrl);
+            if (!ajaxResult.get("code").equals(200)) {
+                return R.error((String) ajaxResult.get("msg"));
             }
+            voiceService.insertQwSopTempVoiceModel(userId);
+            return R.ok(ajaxResult.get("msg").toString());
+        } else {
+            try {
+                CloseableHttpClient httpClient = HttpClients.createDefault();
+                HttpPost httpPost = new HttpPost(aiHostProper.getCommonApi()+"/app/common/addCompanyAudio");
+                String json = "{\"url\":\""+wavUrl+"\",\"id\":\""+userId+"\"}";
+                StringEntity entity = new StringEntity(json);
+                httpPost.setEntity(entity);
+                httpPost.setHeader("Content-type", "application/json");
+                HttpResponse response = httpClient.execute(httpPost);
+
+                if (response.getStatusLine().getStatusCode() == 200) {
+                    String responseBody = EntityUtils.toString(response.getEntity());
+                    JSONObject jsonObject = JSON.parseObject(responseBody);
+                    Integer code = (Integer)jsonObject.get("code");
+                    if (code==200){
+                        voiceService.insertQwSopTempVoiceModel(userId);
+                        return R.ok();
+                    }
+                } else {
+                    return R.error();
+                }
 
-            httpClient.close();
-        } catch (Exception e) {
-            e.printStackTrace();
+                httpClient.close();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
         }
 
         return R.error();
@@ -442,5 +473,135 @@ public class CompanyUserScrmController extends AppBaseController {
         return R.ok().put("data", audioVO);
     }
 
+    /**
+     * 上传音频豆包
+     *
+     * @param voicePrintUrl
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/uploadVoice")
+    public AjaxResult uploadVoice(
+            Long userId,
+            String voicePrintUrl) throws Exception {
+        if (userId == null) userId = 123L;
+        VcCompanyUser vcCompanyUser = companyUserMapper.selectVcCompanyUserByCompanyUserId(userId);
+        if (vcCompanyUser == null) {
+            return AjaxResult.error("用户没有声纹槽位,请联系管理员");
+        }
+        if (vcCompanyUser.getTimes() != null && vcCompanyUser.getTimes() >= 5)
+            return AjaxResult.error("用户已上传声纹达到上限");
+        vcCompanyUser.setUploadUrl(voicePrintUrl);
+        File file = downloadFileFromUrl(voicePrintUrl);
+        /*获取文件时长*/
+        AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
+        AudioFormat format = audioInputStream.getFormat();
+        long frames = audioInputStream.getFrameLength();
+        // 计算时长(秒)= 总帧数 / 帧率
+        Double duration = (double) frames / format.getFrameRate();
+        audioInputStream.close();
+        MultipartFile convert = FileToMultipartConverterUtil.convert(file);
+        R r = voiceCloneController.uploadVoice(vcCompanyUser.getSpeakerId(), convert, vcCompanyUser.getVersionId(), 0);
+        if (!r.get("code").equals(200))
+            return AjaxResult.error("上传声纹失败", r.get("msg"));
+        vcCompanyUser.incrementTimes();
+        vcCompanyUser.setUploadTime(duration);
+        companyUserMapper.updateVcCompanyUser(vcCompanyUser);
+        return AjaxResult.success(String.format("您已上传%d次声纹,总共有5次录入次数", vcCompanyUser.getTimes()));
+    }
+
+    /**
+     * 获取文件扩展名
+     *
+     * @param fileUrl 文件路径
+     * @return 文件扩展名
+     */
+    private static String getFileExtension(String fileUrl) {
+        return fileUrl.substring(fileUrl.lastIndexOf("."));
+    }
+
+
+    private File downloadFileFromUrl(String fileUrl) throws IOException {
+        InputStream inputStream = null;
+        FileOutputStream outputStream = null;
+        try {
+            // 创建 HTTP 连接
+            URL url = new URL(fileUrl);
+            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+            // 设置Referer请求头
+//            connection.setRequestProperty("Referer", "cos.his.cdwjyyh.com");
+            connection.setRequestMethod("GET");
+            connection.connect();
+
+            // 检查是否成功连接
+            if (connection.getResponseCode() != 200) {
+                throw new ServiceException("无法下载音频文件,HTTP 响应码:" + connection.getResponseCode());
+            }
+
+            // 获取输入流
+            inputStream = connection.getInputStream();
+
+            // 创建临时文件,并指定存放地址
+            String tempFileName = "temp_" + UUID.randomUUID() + "_" + getFileExtension(fileUrl);
+            File destinationDirectory = new File("c:\\hook\\");
+
+            // 参照 transferAudioSilk 方法,同步确保目录创建的线程安全
+            synchronized (AudioUtils.class) {
+                if (!destinationDirectory.exists()) {
+                    destinationDirectory.mkdirs();
+                }
+            }
+
+            // 将文件保存到指定路径
+            File tempFile = new File(destinationDirectory, tempFileName);
+
+            // 写入文件
+            outputStream = new FileOutputStream(tempFile);
+            byte[] buffer = new byte[8192];
+            int bytesRead;
+            while ((bytesRead = inputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+
+            return tempFile;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (inputStream != null) inputStream.close();
+                if (outputStream != null) outputStream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    public VcCompanyUser getVcCompanyUser(Long companyUserId) {
+        String cacheKey = VOICE_CLONE_CACHE_KEY + companyUserId;
+        VcCompanyUser vcCompanyUser = redisCache.getCacheObject(cacheKey);
+        if (vcCompanyUser != null) {
+            return vcCompanyUser;
+        }
+        // 使用双重检查锁,防止缓存击穿
+        synchronized (this) {
+            // 再次检查缓存,防止在等待锁的过程中已经被其他线程加载
+            vcCompanyUser = redisCache.getCacheObject(cacheKey);
+            if (vcCompanyUser != null) {
+                return vcCompanyUser;
+            }
+            // 查询数据库
+            vcCompanyUser = companyUserMapper.selectVcCompanyUserByCompanyUserId(companyUserId);
+            if (vcCompanyUser == null) {
+                // 缓存空对象,防止缓存穿透
+                redisCache.setCacheObject(cacheKey, new VcCompanyUser(), CACHE_TTL, TimeUnit.SECONDS);
+                throw new RuntimeException("用户不存在");
+            }
+            // 设置缓存
+            redisCache.setCacheObject(cacheKey, vcCompanyUser, CACHE_TTL,TimeUnit.SECONDS);
+            return vcCompanyUser;
+        }
+    }
+
 
 }