Prechádzať zdrojové kódy

Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java

GHH 11 hodín pred
rodič
commit
a621594a3b
71 zmenil súbory, kde vykonal 1139 pridanie a 155 odobranie
  1. 12 0
      fs-admin/src/main/java/com/fs/his/controller/FsPackageController.java
  2. 13 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java
  3. 61 0
      fs-common/src/main/java/com/fs/common/core/redis/RedisCache.java
  4. 42 8
      fs-qw-voice/src/main/java/com/fs/app/controller/CommonController.java
  5. 10 10
      fs-qw-voice/src/main/java/com/fs/app/exception/FSException.java
  6. 99 12
      fs-qw-voice/src/main/java/com/fs/app/mq/RocketMQConsumerService.java
  7. 10 10
      fs-qw-voice/src/main/java/com/fs/app/task/Task.java
  8. 13 3
      fs-qw-voice/src/main/java/com/fs/framework/config/DataSourceConfig.java
  9. 4 4
      fs-qw-voice/src/main/java/com/fs/framework/config/DruidConfig.java
  10. 3 0
      fs-service/src/main/java/com/fs/config/ai/AiHostProper.java
  11. 35 34
      fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java
  12. 314 29
      fs-service/src/main/java/com/fs/fastgptApi/util/AudioUtils.java
  13. 4 0
      fs-service/src/main/java/com/fs/fastgptApi/vo/AudioVO.java
  14. 1 0
      fs-service/src/main/java/com/fs/his/enums/FsStoreOrderLogEnum.java
  15. 6 3
      fs-service/src/main/java/com/fs/his/mapper/FsPackageMapper.java
  16. 1 1
      fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderMapper.java
  17. 4 0
      fs-service/src/main/java/com/fs/his/service/IFsPackageService.java
  18. 21 0
      fs-service/src/main/java/com/fs/his/service/impl/FsPackageServiceImpl.java
  19. 23 4
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java
  20. 3 0
      fs-service/src/main/java/com/fs/hisStore/config/MedicalMallConfig.java
  21. 2 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductScrmMapper.java
  22. 2 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreProductScrmService.java
  23. 129 26
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  24. 5 1
      fs-service/src/main/java/com/fs/sop/domain/QwSopTempVoice.java
  25. 2 0
      fs-service/src/main/java/com/fs/sop/mapper/QwSopTempContentMapper.java
  26. 11 0
      fs-service/src/main/java/com/fs/sop/mapper/QwSopTempVoiceMapper.java
  27. 2 0
      fs-service/src/main/java/com/fs/sop/service/IQwSopTempContentService.java
  28. 10 0
      fs-service/src/main/java/com/fs/sop/service/IQwSopTempVoiceService.java
  29. 6 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempContentServiceImpl.java
  30. 53 4
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempVoiceServiceImpl.java
  31. 4 1
      fs-service/src/main/java/com/fs/wxwork/service/WxWorkServiceImpl.java
  32. 1 0
      fs-service/src/main/resources/application-config-bly.yml
  33. 1 0
      fs-service/src/main/resources/application-config-dev-yjb.yml
  34. 1 0
      fs-service/src/main/resources/application-config-dev.yml
  35. 1 0
      fs-service/src/main/resources/application-config-druid-bjczwh.yml
  36. 1 0
      fs-service/src/main/resources/application-config-druid-bnkc.yml
  37. 1 0
      fs-service/src/main/resources/application-config-druid-drk-test.yml
  38. 1 0
      fs-service/src/main/resources/application-config-druid-drk.yml
  39. 1 0
      fs-service/src/main/resources/application-config-druid-fby.yml
  40. 1 0
      fs-service/src/main/resources/application-config-druid-hcl.yml
  41. 1 0
      fs-service/src/main/resources/application-config-druid-hdt.yml
  42. 1 0
      fs-service/src/main/resources/application-config-druid-hst.yml
  43. 1 0
      fs-service/src/main/resources/application-config-druid-hyt.yml
  44. 1 0
      fs-service/src/main/resources/application-config-druid-hzyy.yml
  45. 1 0
      fs-service/src/main/resources/application-config-druid-jkj.yml
  46. 3 3
      fs-service/src/main/resources/application-config-druid-jnlzjk.yml
  47. 1 0
      fs-service/src/main/resources/application-config-druid-jnmy.yml
  48. 1 0
      fs-service/src/main/resources/application-config-druid-jzzx.yml
  49. 1 0
      fs-service/src/main/resources/application-config-druid-kyt.yml
  50. 1 0
      fs-service/src/main/resources/application-config-druid-nmgyt.yml
  51. 1 0
      fs-service/src/main/resources/application-config-druid-qdtst.yml
  52. 1 0
      fs-service/src/main/resources/application-config-druid-sft.yml
  53. 1 0
      fs-service/src/main/resources/application-config-druid-sxjz.yml
  54. 1 0
      fs-service/src/main/resources/application-config-druid-syysy.yml
  55. 1 0
      fs-service/src/main/resources/application-config-druid-whhm.yml
  56. 1 0
      fs-service/src/main/resources/application-config-druid-xfk.yml
  57. 1 0
      fs-service/src/main/resources/application-config-druid-xzt.yml
  58. 1 0
      fs-service/src/main/resources/application-config-druid-yjb.yml
  59. 1 0
      fs-service/src/main/resources/application-config-druid-yzt.yml
  60. 1 0
      fs-service/src/main/resources/application-config-druid-zsjk.yml
  61. 1 0
      fs-service/src/main/resources/application-config-druid.yml
  62. 1 0
      fs-service/src/main/resources/application-config-myhk.yml
  63. 1 0
      fs-service/src/main/resources/application-config-zkzh.yml
  64. 1 0
      fs-service/src/main/resources/application-druid-lmjy-test.yml
  65. 1 0
      fs-service/src/main/resources/application-druid-lmjy.yml
  66. 8 0
      fs-service/src/main/resources/mapper/his/FsPackageMapper.xml
  67. 7 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml
  68. 6 2
      fs-service/src/main/resources/mapper/qw/QwUserVoiceLogMapper.xml
  69. 4 0
      fs-service/src/main/resources/mapper/sop/QwSopTempContentMapper.xml
  70. 25 0
      fs-service/src/main/resources/mapper/sop/QwSopTempVoiceMapper.xml
  71. 148 0
      fs-user-app/src/main/java/com/fs/app/controller/CompanyUserController.java

+ 12 - 0
fs-admin/src/main/java/com/fs/his/controller/FsPackageController.java

@@ -147,6 +147,18 @@ public class FsPackageController extends BaseController
         return toAjax(fsPackageService.deleteFsPackageByPackageIds(packageIds));
     }
 
+
+    /**
+     * 批量复制套餐包
+     */
+    @PreAuthorize("@ss.hasPermi('his:package:bulkCopy')")
+    @Log(title = "套餐包", businessType = BusinessType.DELETE)
+    @GetMapping("/bulkCopy/{packageIds}")
+    public AjaxResult bulkCopy(@PathVariable Long[] packageIds)
+    {
+        return toAjax(fsPackageService.bulkCopyFsPackageByPackage(packageIds));
+    }
+
     /**
      * 查询套餐包列表
      */

+ 13 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java

@@ -179,6 +179,19 @@ public class FsStoreProductScrmController extends BaseController
         return toAjax(fsStoreProductService.deleteFsStoreProductByIds(productIds));
     }
 
+
+    /**
+     * 批量复制商品
+     */
+    @PreAuthorize("@ss.hasPermi('store:storeProduct:bulkCopy')")
+    @Log(title = "商品管理", businessType = BusinessType.UPDATE,isStoreLog = true,
+            logParamExpression ="#p0.length>1?new String[]{'商品','批量复制商品信息'}: new String[]{'商品','复制商品信息'}" )
+    @GetMapping("/bulkCopy/{productIds}")
+    public R bulkCopy(@PathVariable Long[] productIds)
+    {
+        return fsStoreProductService.bulkCopyFsStoreProductByIds(productIds);
+    }
+
     @ApiOperation(value = "生成属性")
     @PostMapping(value = "/genFormatAttr/{productId}")
     public ResponseEntity genFormatAttr(@PathVariable Long productId, @RequestBody String jsonStr,Long[] stores){

+ 61 - 0
fs-common/src/main/java/com/fs/common/core/redis/RedisCache.java

@@ -240,4 +240,65 @@ public class RedisCache
     public Long incr(final String key,final Long delta) {
         return redisTemplate.opsForValue().increment(key, delta);
     }
+
+    /**
+     * 获得list对象的value
+     *
+     * @param key 缓存的键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> T getVoiceAllList(final String key)
+    {
+        return (T) redisTemplate.opsForList().range(key,0,-1);
+    }
+    /**
+     * 获得缓存的对象id
+     *
+     * @param key 缓存的键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> T popVoiceKey(final String key)
+    {
+        if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
+            return (T) redisTemplate.opsForList().leftPop(key);
+        }
+        return null;
+    }
+    /**
+     * 删除缓存Map
+     *
+     * @param key
+     * @param hKey
+     */
+    public <T> void deleteCacheMap(final String key, final String hKey)
+    {
+        if (hKey != null) {
+            redisTemplate.opsForHash().delete(key, hKey);
+        }
+    }
+
+    /**
+     * 添加List消息数据
+     *
+     * @param key 缓存的键值
+     * @param dataList 待缓存的List消息数据
+     * @return 缓存的对象
+     */
+    public <T> long setVoiceList(final String key, final List<T> dataList)
+    {
+        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
+        return count == null ? 0 : count;
+    }
+
+    /**
+     * 添加List消息数据的key
+     *
+     * @param key 缓存的键值
+     * @param value 待缓存的值
+     * @return 缓存的对象
+     */
+    public <T> void setVoice(final String key, final T value)
+    {
+        redisTemplate.opsForList().rightPush(key, value);
+    }
 }

+ 42 - 8
fs-qw-voice/src/main/java/com/fs/app/controller/CommonController.java

@@ -1,20 +1,24 @@
 package com.fs.app.controller;
 
 
-import com.fs.ad.enums.AdUploadType;
-import com.fs.ad.service.IAdHtmlClickLogService;
+import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.R;
-import com.fs.company.service.ICompanyWxChatService;
+import com.fs.common.core.page.TableDataInfo;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.vo.AudioVO;
-import com.fs.wxUser.service.ICompanyWxUserService;
+import com.fs.sop.domain.QwSopTempVoice;
+import com.fs.sop.service.IQwSopTempVoiceService;
 import io.swagger.annotations.Api;
 import jdk.nashorn.internal.ir.annotations.Ignore;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
 
 
 @Slf4j
@@ -23,7 +27,8 @@ import org.springframework.web.bind.annotation.RestController;
 @AllArgsConstructor
 @Ignore
 @RequestMapping(value="/app/common")
-public class CommonController {
+public class CommonController extends BaseController {
+
 
     @GetMapping("/test")
     public R testSend(String voice, Long id){
@@ -31,4 +36,33 @@ public class CommonController {
         return R.ok().put("data", audioVO);
     }
 
+    @GetMapping("/voice")
+    public R voice(String voice, Long id){
+        AudioVO audioVO = AudioUtils.transferAudioSilkFromText(voice,id,  false);
+        return R.ok().put("data", audioVO);
+    }
+
+
+    /**
+     * 当只有模板文字text时,生成表中对应条的voice_url和user_voice_url
+     * @param id            qw_sop_temp_voice的id
+     * @return
+     */
+    @GetMapping("/createUserUrlAndUrl")
+    public R createUserUrlAndUrl(@Param("id") Long id,@Param("voiceTxt") String voiceTxt){
+        AudioVO audioVO = AudioUtils.transferCompanyIdAudioSilkFromText(voiceTxt,id,false);
+        return R.ok().put("data", audioVO);
+    }
+
+    /**
+     * 当只有user_voice_url时,生成表中对应条的voice_url
+     * @param userVoiceUrl  wav格式的语音文件
+     * @param id            qw_sop_temp_voice的id
+     * @return
+     */
+    @GetMapping("/createVoiceUrl")
+    public R createVoiceUrl( @RequestParam("id") Long id,@RequestParam("userVoiceUrl") String userVoiceUrl){
+        AudioVO audioVO = AudioUtils.transferAudioSilkFromUrl(userVoiceUrl,  false);
+        return R.ok().put("data", audioVO);
+    }
 }

+ 10 - 10
fs-qw-voice/src/main/java/com/fs/app/exception/FSException.java

@@ -5,26 +5,26 @@ package com.fs.app.exception;
  */
 public class FSException extends RuntimeException {
 	private static final long serialVersionUID = 1L;
-	
-    private String msg;
-    private int code = 500;
-    
-    public FSException(String msg) {
+
+	private String msg;
+	private int code = 500;
+
+	public FSException(String msg) {
 		super(msg);
 		this.msg = msg;
 	}
-	
+
 	public FSException(String msg, Throwable e) {
 		super(msg, e);
 		this.msg = msg;
 	}
-	
+
 	public FSException(String msg, int code) {
 		super(msg);
 		this.msg = msg;
 		this.code = code;
 	}
-	
+
 	public FSException(String msg, int code, Throwable e) {
 		super(msg, e);
 		this.msg = msg;
@@ -46,6 +46,6 @@ public class FSException extends RuntimeException {
 	public void setCode(int code) {
 		this.code = code;
 	}
-	
-	
+
+
 }

+ 99 - 12
fs-qw-voice/src/main/java/com/fs/app/mq/RocketMQConsumerService.java

@@ -1,29 +1,25 @@
 package com.fs.app.mq;
 
 import com.alibaba.fastjson.JSON;
-import com.fs.ad.service.IAdHtmlClickLogService;
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.annotation.DataSource;
 import com.fs.common.core.redis.RedisCacheT;
 import com.fs.common.enums.DataSourceType;
-import com.fs.common.utils.PubFun;
-import com.fs.qw.result.QwFilterSopCustomersResult;
-import com.fs.qw.vo.AdUploadVo;
+import com.fs.qw.service.IQwUserService;
+import com.fs.qw.vo.QwUserVO;
 import com.fs.sop.domain.QwSop;
 import com.fs.sop.domain.QwSopTempContent;
-import com.fs.sop.domain.QwSopTempDay;
-import com.fs.sop.mapper.QwSopMapper;
-import com.fs.sop.mapper.QwSopTempContentMapper;
-import com.fs.sop.params.QwSopTagsParam;
+import com.fs.sop.domain.QwSopTempVoice;
+import com.fs.sop.service.IQwSopService;
+import com.fs.sop.service.IQwSopTempContentService;
 import com.fs.sop.service.IQwSopTempDayService;
 import com.fs.sop.service.IQwSopTempVoiceService;
 import com.fs.sop.vo.VoiceVo;
-import com.fs.voice.utils.StringUtil;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.commons.beanutils.ConvertUtils;
 import org.apache.rocketmq.spring.core.RocketMQListener;
 import org.springframework.stereotype.Service;
-import org.springframework.web.bind.annotation.GetMapping;
 
 import java.util.Arrays;
 import java.util.List;
@@ -32,10 +28,14 @@ import java.util.stream.Collectors;
 @Slf4j
 @Service
 @AllArgsConstructor
-@RocketMQMessageListener(topic = "${rocketmq.consumer.topic}", consumerGroup = "${rocketmq.consumer.group}")
+//@RocketMQMessageListener(topic = "${rocketmq.consumer.topic}", consumerGroup = "${rocketmq.consumer.group}")
 public class RocketMQConsumerService implements RocketMQListener<String> {
     private final RedisCacheT<VoiceVo> redisCache;
     public final static String VOICE_CACHE_KEY = "voice:cache:";
+    private final IQwSopTempVoiceService qwSopTempVoiceService;
+    private final IQwSopTempContentService qwSopTempContentService;
+    private final IQwUserService qwUserService;
+    private final IQwSopService qwSopService;
 
     @Override
     @DataSource(DataSourceType.SOP)
@@ -44,6 +44,93 @@ public class RocketMQConsumerService implements RocketMQListener<String> {
         VoiceVo vo = JSON.parseObject(message, VoiceVo.class);
         vo.setGenerated(0);
         redisCache.setCacheObject(VOICE_CACHE_KEY + vo.getType() + ":" + vo.getId(), vo);
+
+        /*VoiceVo vo = JSON.parseObject(message, VoiceVo.class);
+        if(vo.getId() != null){
+            List<QwSopTempContent> contentList = qwSopTempContentService.selectQwSopTempContentByTempId(vo.getId());
+            if(contentList != null && !contentList.isEmpty()){
+                updateTempVoiceInfo(contentList);
+            }
+        }*/
     }
 
+    private void updateTempVoiceInfo(List<QwSopTempContent> voiceList) {
+        for (QwSopTempContent qwSopTempContent : voiceList) {
+            String tempId = qwSopTempContent.getTempId();
+            String content = qwSopTempContent.getContent();
+            JSONObject jsonObject = JSONObject.parseObject(content);
+            String text = jsonObject.getString("value");
+            List<QwSop> qwSopList = qwSopService.selectQwSopByTempId(tempId);//通过tempId查询出所有sop
+            if(qwSopList != null && !qwSopList.isEmpty()){
+                for (QwSop qwSop : qwSopList) {
+                    if(qwSop != null && qwSop.getQwUserIds() != null){
+                        //查询出所有的tempContent来筛选文字
+                        List<QwSopTempContent> qwSopTempContentList = qwSopTempContentService.selectQwSopTempContentByTempId(tempId);
+                        if(qwSopTempContentList != null && !qwSopTempContentList.isEmpty()){
+                            for (QwSopTempContent qwSopTemp : qwSopTempContentList) {
+                                if(qwSopTemp != null && qwSopTemp.getContentType() == 7){
+                                    String[] split = qwSop.getQwUserIds().split(",");
+                                    Long[] qwUserIds = (Long[]) ConvertUtils.convert(split, Long.class);
+                                    List<QwUserVO> qwUserVOS = qwUserService.selectQwUserVOByIds(qwUserIds);
+                                    if(qwUserVOS != null){
+                                        for (QwUserVO qwUserVO : qwUserVOS) {
+                                            Long companyUserId = qwUserVO.getCompanyUserId();
+                                            QwSopTempVoice qwSopTempVoice = qwSopTempVoiceService.selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(companyUserId,text);
+                                            if(qwSopTempVoice == null){
+                                                QwSopTempVoice sopTempVoice = new QwSopTempVoice();
+                                                sopTempVoice.setCompanyUserId(companyUserId);
+                                                sopTempVoice.setVoiceTxt(text);
+                                                sopTempVoice.setTempId(tempId);
+                                                sopTempVoice.setRecordType(0);
+                                                qwSopTempVoiceService.insertQwSopTempVoice(sopTempVoice);
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        /*if(qwSop != null && qwSop.getQwUserIds() != null){
+            String tempId = qwSop.getTempId();
+            List<QwSopTempContent> voiceList = qwSopTempContentService.selectQwSopTempContentByTempId(tempId);
+            if(voiceList != null && !voiceList.isEmpty()){
+                for (QwSopTempContent qwSopTempContent : voiceList) {
+                    String content = qwSopTempContent.getContent();
+                    JSONObject jsonObject = JSONObject.parseObject(content);
+                    String text = jsonObject.getString("value");
+                    //查询出所有的tempContent来筛选文字
+                    List<QwSopTempContent> qwSopTempContentList = qwSopTempContentService.selectQwSopTempContentByTempId(tempId);
+                    if(qwSopTempContentList != null && !qwSopTempContentList.isEmpty()){
+                        for (QwSopTempContent qwSopTemp : qwSopTempContentList) {
+                            if(qwSopTemp != null && qwSopTemp.getContentType() == 7){
+                                String[] split = qwSop.getQwUserIds().split(",");
+                                Long[] qwUserIds = Arrays.stream(split)
+                                        .filter(s -> !s.isEmpty())
+                                        .map(Long::valueOf)
+                                        .toArray(Long[]::new);
+                                List<QwUserVO> qwUserVOS = qwUserService.selectQwUserVOByIds(qwUserIds);
+                                if(qwUserVOS != null){
+                                    for (QwUserVO qwUserVO : qwUserVOS) {
+                                        Long companyUserId = qwUserVO.getCompanyUserId();
+                                        QwSopTempVoice qwSopTempVoice = qwSopTempVoiceService.selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(companyUserId,text);
+                                        if(qwSopTempVoice == null){
+                                            QwSopTempVoice sopTempVoice = new QwSopTempVoice();
+                                            sopTempVoice.setCompanyUserId(companyUserId);
+                                            sopTempVoice.setVoiceTxt(text);
+                                            sopTempVoice.setTempId(tempId);
+                                            sopTempVoice.setRecordType(0);
+                                            qwSopTempVoiceService.insertQwSopTempVoice(sopTempVoice);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }*/
+    }
 }

+ 10 - 10
fs-qw-voice/src/main/java/com/fs/app/task/Task.java

@@ -38,14 +38,14 @@ public class Task {
 
     @Scheduled(cron = "0 * * * * *")
     @DataSource(DataSourceType.SOP)
-    public void scheduled(){
+    public void scheduled() {
         List<VoiceVo> list = redisCache.getCacheListByPattern(VOICE_CACHE_KEY + "*");
-        if(list.isEmpty()) return;
+        if (list.isEmpty()) return;
         VoiceVo vo = list.get(0);
-        if(vo.getType() == 0){
+        if (vo.getType() == 0) {
             synchronousTemp(Long.parseLong(vo.getId()));
         }
-        if(vo.getType() == 1){
+        if (vo.getType() == 1) {
             synchronousSop(vo.getId());
         }
         redisCache.deleteObject(VOICE_CACHE_KEY + vo.getType() + ":" + vo.getId());
@@ -53,19 +53,19 @@ public class Task {
 
 
     @DataSource(DataSourceType.SOP)
-    public void synchronousTemp(Long dayId){
+    public void synchronousTemp(Long dayId) {
         QwSopTempDay day = qwSopTempDayService.getById(dayId);
         List<QwSopTempContent> contents = qwSopTempContentMapper.listByTempAndDay(day.getTempId(), dayId);
         qwSopTempVoiceService.synchronousTemp(contents, day);
     }
 
     @DataSource(DataSourceType.SOP)
-    public void synchronousSop(String sopId){
+    public void synchronousSop(String sopId) {
         QwSop qwSop = qwSopMapper.selectQwSopById(sopId);
         QwSopTagsParam qwSopTagsParam = new QwSopTagsParam();
         //成员筛选
-        if (!StringUtil.strIsNullOrEmpty(qwSop.getQwUserIds())){
-            qwSopTagsParam.setUserIdsSelectList(Arrays.asList( qwSop.getQwUserIds().split(",")));
+        if (!StringUtil.strIsNullOrEmpty(qwSop.getQwUserIds())) {
+            qwSopTagsParam.setUserIdsSelectList(Arrays.asList(qwSop.getQwUserIds().split(",")));
         }
 
         //标过滤类型
@@ -73,13 +73,13 @@ public class Task {
 
 
         //标签
-        if (!StringUtil.strIsNullOrEmpty(qwSop.getTags())){
+        if (!StringUtil.strIsNullOrEmpty(qwSop.getTags())) {
             qwSopTagsParam.setTagsIdsSelectList(Arrays.asList(qwSop.getTags().split(",")));
         }
 
         //排除标签
         String excludeTags = qwSop.getExcludeTags();
-        if (!StringUtil.strIsNullOrEmpty(excludeTags)){
+        if (!StringUtil.strIsNullOrEmpty(excludeTags)) {
             qwSopTagsParam.setOutTagsIdsSelectList(Arrays.asList(excludeTags.split(",")));
         }
         qwSopTagsParam.setCropId(qwSop.getCorpId());

+ 13 - 3
fs-qw-voice/src/main/java/com/fs/framework/config/DataSourceConfig.java

@@ -21,7 +21,6 @@ import java.util.Map;
 
 @Configuration
 public class DataSourceConfig {
-
     @Bean
     @ConfigurationProperties(prefix = "spring.datasource.sop.druid.master")
     public DataSource sopDataSource() {
@@ -34,12 +33,23 @@ public class DataSourceConfig {
         return new DruidDataSource();
     }
 
+    @Bean
+    @ConfigurationProperties(prefix = "spring.datasource.mysql.druid.slave")
+    public DataSource slaveDataSource() {
+        return new DruidDataSource();
+    }
+
 
 
     @Bean
     @Primary
-    public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("sopDataSource") DataSource sopDataSource) {
+    public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
+                                        @Qualifier("sopDataSource") DataSource sopDataSource,
+                                        @Qualifier("slaveDataSource") DataSource slaveDataSource) {
         Map<Object, Object> targetDataSources = new HashMap<>();
+        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
+
+        targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource);
         targetDataSources.put(DataSourceType.SOP.name(), sopDataSource);
         return new DynamicDataSource(masterDataSource, targetDataSources);
     }
@@ -49,7 +59,7 @@ public class DataSourceConfig {
      */
     @SuppressWarnings({ "rawtypes", "unchecked" })
     @Bean
-    @ConditionalOnProperty(name = "spring.datasource.mysql.druid.statViewServlet.enabled", havingValue = "true")
+    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
     public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
     {
         // 获取web监控页面的参数

+ 4 - 4
fs-qw-voice/src/main/java/com/fs/framework/config/DruidConfig.java

@@ -30,7 +30,7 @@ package com.fs.framework.config;//package com.fs.framework.config;
 //public class DruidConfig
 //{
 //    @Bean
-//    @ConfigurationProperties("spring.datasource.mysql.druid.master")
+//    @ConfigurationProperties("spring.datasource.druid.master")
 //    public DataSource masterDataSource(DruidProperties druidProperties)
 //    {
 //        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
@@ -38,8 +38,8 @@ package com.fs.framework.config;//package com.fs.framework.config;
 //    }
 //
 //    @Bean
-//    @ConfigurationProperties("spring.datasource.mysql.druid.slave")
-//    @ConditionalOnProperty(prefix = "spring.datasource.mysql.druid.slave", name = "enabled", havingValue = "true")
+//    @ConfigurationProperties("spring.datasource.druid.slave")
+//    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
 //    public DataSource slaveDataSource(DruidProperties druidProperties)
 //    {
 //        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
@@ -80,7 +80,7 @@ package com.fs.framework.config;//package com.fs.framework.config;
 //     */
 //    @SuppressWarnings({ "rawtypes", "unchecked" })
 //    @Bean
-//    @ConditionalOnProperty(name = "spring.datasource.mysql.druid.statViewServlet.enabled", havingValue = "true")
+//    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
 //    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
 //    {
 //        // 获取web监控页面的参数

+ 3 - 0
fs-service/src/main/java/com/fs/config/ai/AiHostProper.java

@@ -13,4 +13,7 @@ public class AiHostProper {
     @Value("${ipad.aiApi}")
     private String aiApi;
 
+    @Value("${ipad.voiceApi}")
+    private String voiceApi;
+
 }

+ 35 - 34
fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java

@@ -267,24 +267,11 @@ public class DfOrderServiceImpl implements IErpOrderService
             String response = client.execute(RequestUrlEnum.ORDER_DELIVERY_STATUS, map, sfAccountIndex);
             DFApiResponse dfApiResponse = JSON.parseObject(response, DFApiResponse.class);
             if ("运单不存在".equals(dfApiResponse.getMsg())){
-                //取消订单
-                FsStoreOrderSalesParam afterSalesParam = new FsStoreOrderSalesParam();
-                //修改订单状态 方便后续重新发货
-                order.setStatus(FsStoreOrderStatusEnum.STATUS_2.getValue());
-                order.setExtendOrderId("");
-                fsStoreOrderMapper.updateFsStoreOrder(order);
-                afterSalesParam.setOrderId(order.getOrderId());
-                afterSalesParam.setReasons("代服管家取消订单");
-                afterSalesParam.setOperator("代服管家");
-                fsStoreOrderService.afterSales(afterSalesParam);
-                FsStoreOrderDf df = new FsStoreOrderDf();
-                df.setOrderId(order.getOrderId());
-                df.setStatus(2);
-                df.setUpdateTime(new Date());
-                fsStoreOrderDfMapper.updateFsStoreOrderDf(df);
-                fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.REFUND_ORDER_DF.getValue(),
-                        "运单不存在,"+FsStoreOrderLogEnum.REFUND_ORDER_DF.getDesc());
-                log.info("代服管家 订单取消成功: {}", response);
+
+                //查看原来物流状态
+                cancelOrder(order);
+                log.info("代服管家 getOrderDeliveryStatus: {}", response);
+                return;
             }
             //3.处理请求结果
             if (dfApiResponse != null && "ok".equals(dfApiResponse.getCode())) {
@@ -350,22 +337,7 @@ public class DfOrderServiceImpl implements IErpOrderService
                         List<FsStoreOrder> fsStoreOrders = fsStoreOrderMapper.selectFsStoreOrderListByDeliverySn(mailNumber);
                         if (fsStoreOrders != null && !fsStoreOrders.isEmpty()) {
                             fsStoreOrders.forEach(tempOrder -> {
-                                FsStoreOrderSalesParam afterSalesParam = new FsStoreOrderSalesParam();
-                                //修改订单状态 方便后续重新发货
-                                order.setStatus(FsStoreOrderStatusEnum.STATUS_2.getValue());
-                                order.setExtendOrderId("");
-                                fsStoreOrderMapper.updateFsStoreOrder(order);
-                                afterSalesParam.setOrderId(tempOrder.getOrderId());
-                                afterSalesParam.setReasons("代服管家取消订单");
-                                afterSalesParam.setOperator("代服管家");
-                                fsStoreOrderService.afterSales(afterSalesParam);
-                                FsStoreOrderDf df = new FsStoreOrderDf();
-                                df.setOrderId(order.getOrderId());
-                                df.setStatus(2);
-                                df.setUpdateTime(new Date());
-                                fsStoreOrderDfMapper.updateFsStoreOrderDf(df);
-                                fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.REFUND_ORDER_DF.getValue(),
-                                        FsStoreOrderLogEnum.REFUND_ORDER_DF.getDesc());
+                                cancelOrder(order);
                                 log.info("代服管家 订单取消成功: {}", response);
                             });
                         }
@@ -380,6 +352,35 @@ public class DfOrderServiceImpl implements IErpOrderService
         }
     }
 
+    private void cancelOrder(FsStoreOrder order) {
+        Integer deliveryStatus = order.getDeliveryStatus();
+        if (deliveryStatus == null || deliveryStatus == 0) {
+            //没有物流信息
+            //修改订单状态 方便后续重新发货
+            order.setStatus(FsStoreOrderStatusEnum.STATUS_2.getValue());
+            order.setExtendOrderId("");
+            order.setDeliverySn("");
+            fsStoreOrderMapper.updateFsStoreOrder(order);
+            fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.UPDATE_ORDER_DF.getValue(),
+                    "运单不存在,"+FsStoreOrderLogEnum.UPDATE_ORDER_DF.getDesc());
+        } else {
+            //有物流信息->售后处理
+            //取消订单
+            FsStoreOrderSalesParam afterSalesParam = new FsStoreOrderSalesParam();
+            afterSalesParam.setOrderId(order.getOrderId());
+            afterSalesParam.setReasons("代服管家取消订单");
+            afterSalesParam.setOperator("代服管家");
+            fsStoreOrderService.afterSales(afterSalesParam);
+            fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.REFUND_ORDER_DF.getValue(),
+                    "运单不存在,"+FsStoreOrderLogEnum.REFUND_ORDER_DF.getDesc());
+        }
+        FsStoreOrderDf df = new FsStoreOrderDf();
+        df.setOrderId(order.getOrderId());
+        df.setStatus(2);
+        df.setUpdateTime(new Date());
+        fsStoreOrderDfMapper.updateFsStoreOrderDf(df);
+    }
+
     /**
      * 获取erp推送参数
      *

+ 314 - 29
fs-service/src/main/java/com/fs/fastgptApi/util/AudioUtils.java

@@ -1,16 +1,25 @@
 package com.fs.fastgptApi.util;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.base.BaseException;
+import com.fs.config.ai.AiHostProper;
+import com.fs.fastGpt.domain.FastgptChatVoiceHomo;
+import com.fs.fastGpt.service.IFastGptChatMsgService;
 import com.fs.fastgptApi.vo.AudioVO;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
 
 import javax.sound.sampled.AudioInputStream;
 import javax.sound.sampled.AudioSystem;
@@ -20,16 +29,83 @@ import java.net.HttpURLConnection;
 import java.net.URL;
 import java.nio.file.Files;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
 
+@Slf4j
+@Component
 public class AudioUtils {
+
+    private static AiHostProper staticAiHostProper;
+
+    @Autowired
+    public void setAiHostProper(AiHostProper service) {
+        AudioUtils.staticAiHostProper = service;
+    }
+
     /**
      * 工具地址
      **/
     static String path = "c:\\";
     static String destinationDir = "c:\\hook\\";
+    public static AudioVO createVoiceUrl(Long id,String userVoiceUrl){
+        String fileUrl = staticAiHostProper.getVoiceApi() + "/app/common/createVoiceUrl?id=" + id + "&userVoiceUrl=" + userVoiceUrl;
+
+        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+            HttpGet httpGet = new HttpGet(fileUrl);
+            HttpResponse response = httpClient.execute(httpGet);
+
+            // 检查响应状态码
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                String responseBody = EntityUtils.toString(response.getEntity());
+
+                // 使用JSON工具解析响应
+                ObjectMapper mapper = new ObjectMapper();
+                JsonNode jsonNode = mapper.readTree(responseBody);
+                JsonNode dataNode = jsonNode.get("data");
+                return mapper.treeToValue(dataNode, AudioVO.class);
+
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static AudioVO createUserUrlAndUrl(List<FastgptChatVoiceHomo> homos,Long id,String voiceTxt){
+        String voiceHomo = voiceHomo(homos,voiceTxt);
+        String fileUrl = staticAiHostProper.getVoiceApi() + "/app/common/createUserUrlAndUrl?id=" + id + "&voiceTxt=" + voiceHomo;
+
+        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+            HttpGet httpGet = new HttpGet(fileUrl);
+            HttpResponse response = httpClient.execute(httpGet);
+
+            // 检查响应状态码
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                String responseBody = EntityUtils.toString(response.getEntity());
+
+                // 使用JSON工具解析响应
+                ObjectMapper mapper = new ObjectMapper();
+                JsonNode jsonNode = mapper.readTree(responseBody);
+                JsonNode dataNode = jsonNode.get("data");
+                return mapper.treeToValue(dataNode, AudioVO.class);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private static String voiceHomo(List<FastgptChatVoiceHomo> homos,String content){
+        for (FastgptChatVoiceHomo homo : homos) {
+            if (content.contains(homo.getContent())) {
+                // 如果包含目标字段,则替换
+                content= content.replace(homo.getContent(), homo.getChangeCount());
+            } else {
+            }
+        }
+        return content;
+    }
 
     /**
      * 从网络 URL 转换 MP3/WAV到SILk格式
@@ -69,13 +145,39 @@ public class AudioUtils {
             if (tempFile == null) {
                 throw new ServiceException("下载文件失败");
             }
-
-
             Integer durations = getDurations(destinationDir+tempFile);
             String silkUrl = transferAudioSilk(destinationDir, tempFile, isSource);
             AudioVO audioVO = new AudioVO();
             audioVO.setDuration(durations);
             audioVO.setUrl(silkUrl);
+            File wavFile = new File(destinationDir+tempFile);
+            if (wavFile.exists()) {
+                wavFile.delete();
+            }
+            // 调用原来的转换方法
+            return audioVO;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static AudioVO transferCompanyIdAudioSilkFromText(String voiceText,Long companyUserId, boolean isSource) {
+        try {
+            // 下载文件到本地临时路径
+            String tempFile = downloadFileFromText(voiceText,companyUserId);
+            if (tempFile == null) {
+                throw new ServiceException("下载文件失败");
+            }
+            Integer durations = getDurations(destinationDir+tempFile);
+            AudioVO  audioVO = transferCompanyAudioSilk(destinationDir, tempFile, isSource);
+
+            audioVO.setDuration(durations);
+
+            File wavFile = new File(destinationDir+tempFile);
+            if (wavFile.exists()) {
+                wavFile.delete();
+            }
             // 调用原来的转换方法
             return audioVO;
         } catch (Exception e) {
@@ -84,6 +186,7 @@ public class AudioUtils {
         return null;
     }
 
+
     public static AudioVO transferAudioSilkFromTextNew(String Text,Long id, boolean isSource) {
         try {
             // 下载文件到本地临时路径
@@ -132,7 +235,7 @@ public class AudioUtils {
                     path + "ffmpeg.exe",
                     "-i", audioFilePath,WAVPath
             };
-            System.out.println(command[2]);
+            log.info(command[2]);
             // 启动进程执行命令
             ProcessBuilder processBuilder = new ProcessBuilder(command);
             processBuilder.redirectErrorStream(true); // 合并标准输出和错误输出
@@ -167,11 +270,11 @@ public class AudioUtils {
             while ((line = reader.readLine()) != null) {
                 // 打印或解析输出信息
                 if (line.contains("Duration")) {
-                    System.out.println(line);
+                    log.info(line);
                     // 查找并解析时长信息
                     String duration = line.substring(line.indexOf("Duration: ") +10, line.indexOf(","));
                     int i = convertDurationToSeconds(duration);
-                    System.out.println("音频时长: " + i);
+                    log.info("音频时长: " + i);
                     durations=i;
                 }
             }
@@ -198,7 +301,7 @@ public class AudioUtils {
         }
     }
     /**
-//     * MP3/WAV转SILk格式
+     //     * MP3/WAV转SILk格式
      *
      * @param path 文件路径 例:D:\\file\\
      * @param name 文件名称 例:audio.mp3/audio.wav
@@ -219,16 +322,17 @@ public class AudioUtils {
                 throw new Exception("文件不存在!");
             }
             // 文件名时拼接
-            SimpleDateFormat ttime = new SimpleDateFormat("yyyyMMddhhmmss");
-            String time = ttime.format(new Date());
+            // 使用 UUID + 临时目录隔离
+            File tempDir = Files.createTempDirectory("audioSilk_").toFile();
+            String uuid = UUID.randomUUID().toString().replace("-", "");
             // 导出的pcm格式路径
-            String pcmPath = path + "PCM_" + time + ".pcm";
+            String pcmPath = tempDir.getAbsolutePath() + File.separator + "PCM_" + uuid + ".pcm";
             // 先将mp3/wav转换成pcm格式
-            transferAudioPcm(filePath, pcmPath);
+            transferAudioPcmSecond(filePath, pcmPath);
             // 导出的silk格式路径
-            String silkPath = path + "SILK_" + time + ".silk";
+            String silkPath = tempDir.getAbsolutePath() + File.separator + "SILK_" + uuid + ".silk";
             // 转换成silk格式
-            transferPcmSilk(pcmPath, silkPath);
+            transferPcmSilkSecond(pcmPath, silkPath);
             // 删除pcm文件
             File pcmFile = new File(pcmPath);
             if (pcmFile.exists()) {
@@ -252,12 +356,86 @@ public class AudioUtils {
             if (silkFile.exists()) {
                 silkFile.delete();
             }
+            //最后删除文件夹
+            if (tempDir.exists()) {
+                tempDir.delete();
+            }
             return silkUrl;
         } catch (Exception e) {
             e.printStackTrace();
         }
         return null;
     }
+
+    public static AudioVO transferCompanyAudioSilk(String path, String name, boolean isSource) {
+        AudioVO audioVO = new AudioVO();
+        try {
+            // 判断后缀格式
+            String suffix = name.split("\\.")[1];
+            if (!suffix.toLowerCase().equals("mp3") && !suffix.toLowerCase().equals("wav")) {
+                throw new ServiceException("文件格式必须是mp3/wav");
+            }
+            String filePath = path + name;
+            File file = new File(filePath);
+            if (!file.exists()) {
+                throw new Exception("文件不存在!");
+            }
+            File wavFile = new File(filePath);
+            //上传silk文件
+            String wavPathSuffix = filePath.split("\\.")[1];
+            CloudStorageService wavStorage = OSSFactory.build();
+            byte[] wavBytes = Files.readAllBytes(wavFile.toPath());
+            String wavUrl = wavStorage.uploadSuffix(wavBytes, "."+wavPathSuffix);
+            audioVO.setWavUrl(wavUrl);
+
+            // 文件名时拼接
+            // 使用 UUID + 临时目录隔离
+            File tempDir = Files.createTempDirectory("audio_").toFile();
+            String uuid = UUID.randomUUID().toString().replace("-", "");
+            // 导出的pcm格式路径
+            String pcmPath = tempDir.getAbsolutePath() + File.separator + "PCM_" + uuid + ".pcm";
+            // 先将mp3/wav转换成pcm格式
+            transferAudioPcmSecond(filePath, pcmPath);
+            // 导出的silk格式路径
+            String silkPath = tempDir.getAbsolutePath() + File.separator + "SILK_" + uuid + ".silk";
+            // 转换成silk格式
+            transferPcmSilkSecond(pcmPath, silkPath);
+            // 删除pcm文件
+            File pcmFile = new File(pcmPath);
+            if (pcmFile.exists()) {
+                pcmFile.delete();
+            }
+            if (isSource) {
+                File audioFile = new File(filePath);
+
+                if (audioFile.exists()) {
+                    audioFile.delete();
+                }
+            }
+
+            File silkFile = new File(silkPath);
+            //上传silk文件
+            String silkPathSuffix = silkPath.split("\\.")[1];
+            CloudStorageService storage = OSSFactory.build();
+            byte[] fileBytes = Files.readAllBytes(silkFile.toPath());
+            String silkUrl = storage.uploadSuffix(fileBytes, "."+silkPathSuffix);
+
+            if (silkFile.exists()) {
+                silkFile.delete();
+            }
+            //最后删除文件夹
+            if (tempDir.exists()) {
+                tempDir.delete();
+            }
+            audioVO.setUrl(silkUrl);
+            return audioVO;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return audioVO;
+    }
+
+
     public static String transferAudioSilkNew(String path, String name, boolean isSource) {
         try {
             // 判断后缀格式
@@ -334,7 +512,7 @@ public class AudioUtils {
      * @param target
      */
     private static void transferAudioPcm(String fpath, String target) {
-        System.out.println("pcm地址"+fpath);
+        log.info("pcm地址"+fpath);
         List<String> commend = new ArrayList<String>();
         commend.add("cmd");
         commend.add("/c");
@@ -354,7 +532,7 @@ public class AudioUtils {
         try {
             ProcessBuilder builder = new ProcessBuilder();
             builder.command(commend);
-           // builder.inheritIO();
+            // builder.inheritIO();
             Process p = builder.start();
             p.waitFor();
             p.destroy();
@@ -363,6 +541,66 @@ public class AudioUtils {
         }
     }
 
+    /**
+     * mp3/wav 通用
+     * @param fpath
+     * @param target
+     */
+    private static void transferAudioPcmSecond(String fpath, String target) {
+        Process process = null;
+        try {
+            List<String> command = new ArrayList<String>();
+            command.add(path + "ffmpeg.exe");
+            command.add("-y");
+            command.add("-i");
+            command.add(fpath);
+            command.add("-f");
+            command.add("s16le");
+            command.add("-ar");
+            command.add("24000");
+            command.add("-ac");
+            command.add("1");
+            command.add(target);
+
+            ProcessBuilder builder = new ProcessBuilder();
+            builder.command(command);
+            builder.redirectErrorStream(true);
+
+            process = builder.start();
+
+            // 添加超时机制,避免进程无限挂起
+            boolean finished = process.waitFor(10, TimeUnit.SECONDS);
+            if (!finished) {
+                process.destroyForcibly();
+                throw new ServiceException("PCM转换超时");
+            }
+
+            int exitCode = process.exitValue();
+            if (exitCode != 0) {
+                throw new ServiceException("PCM转换失败,退出码:" + exitCode);
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new ServiceException("PCM转换被中断"+ e);
+        } catch (IOException e) {
+            throw new ServiceException("PCM转换IO异常:" + e.getMessage() + e);
+        } catch (Exception e) {
+            throw new ServiceException("PCM转换异常:" + e.getMessage() + e);
+        } finally {
+            if (process != null) {
+                try {
+                    process.destroy();
+                } catch (Exception e) {
+                    // 记录但不抛出异常,避免掩盖主异常
+                    System.err.println("销毁进程时出错:" + e.getMessage());
+                }
+            }
+        }
+    }
+
+
+
+
 
     /**
      * silk_v3_encoder.exe,转成Silk格式
@@ -412,6 +650,52 @@ public class AudioUtils {
         }
     }
 
+    /**
+     * 解决语音较长时转换不完全的问题
+     * @param pcmPath
+     * @param target
+     */
+    public static void transferPcmSilkSecond(String pcmPath, String target) {
+        Process process = null;
+        try {
+            // 使用 ProcessBuilder 替代 Runtime.exec 提高可靠性
+            List<String> command = new ArrayList<>();
+            command.add("cmd");
+            command.add("/c");
+            command.add("start");
+            command.add("/wait");  // 等待程序执行完毕才退出
+            command.add(path + "silk_v3_encoder.exe");
+            command.add(pcmPath);
+            command.add(target);
+            command.add("-tencent");
+
+            ProcessBuilder builder = new ProcessBuilder(command);
+            builder.redirectErrorStream(true); // 合并标准输出和错误输出
+
+            process = builder.start();
+
+            // 可选:读取输出以确认执行状态
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    log.info(line);  // 打印日志便于调试
+                }
+            }
+
+            int exitCode = process.waitFor();  // 等待执行完成
+            if (exitCode != 0) {
+                System.err.println("silk_v3_encoder exited with code: " + exitCode);
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (process != null) {
+                process.destroy();
+            }
+        }
+    }
+
     public static void transferPcmSilkNew(String pcmPath, String target) {
         Process process = null;
         try {
@@ -446,6 +730,8 @@ public class AudioUtils {
             // 创建 HTTP 连接
             URL url = new URL(fileUrl);
             HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+            // 设置Referer请求头
+            connection.setRequestProperty("Referer", "cos.his.cdwjyyh.com");
             connection.setRequestMethod("GET");
             connection.connect();
 
@@ -458,12 +744,14 @@ public class AudioUtils {
             inputStream = connection.getInputStream();
 
             // 创建临时文件,并指定存放地址
-            String tempFileName = "temp_" + System.currentTimeMillis() + "_" + getFileExtension(fileUrl);
+            String tempFileName = "temp_" + UUID.randomUUID() + "_" + getFileExtension(fileUrl);
             File destinationDirectory = new File(destinationDir);
 
-            // 确保目标目录存在
-            if (!destinationDirectory.exists()) {
-                destinationDirectory.mkdirs();
+            // 参照 transferAudioSilk 方法,同步确保目录创建的线程安全
+            synchronized (AudioUtils.class) {
+                if (!destinationDirectory.exists()) {
+                    destinationDirectory.mkdirs();
+                }
             }
 
             // 将文件保存到指定路径
@@ -501,13 +789,13 @@ public class AudioUtils {
                 .replaceAll("[a-zA-Z]", "")
                 .replaceAll("\\s", "");
         try {
-            String fileUrl = "http://118.24.209.192:9881/?text="+text+"&ref_audio=./参考音频/companyUser"+userId+".wav&ref_text=在这片神奇的森林里,小鸟轻快地唱着歌,仿佛在诉说着春天的故事。"; // 替换为要下载的文件URL
+            String fileUrl = "http://127.0.0.1:9880/?text="+text+"&ref_audio=./参考音频/companyUser"+userId+".wav&ref_text=在这片神奇的森林里,小鸟轻快地唱着歌,仿佛在诉说着春天的故事。"; // 替换为要下载的文件URL
             try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                 HttpGet httpGet = new HttpGet(fileUrl);
                 HttpResponse response = httpClient.execute(httpGet);
                 HttpEntity entity = response.getEntity();https://hf-mirror.com
                 if (entity != null) {
-                     inputStream = entity.getContent();
+                    inputStream = entity.getContent();
                     byte[] fileBytes = convertInputStreamToByteArray(inputStream);
                     // 对文件字节数组进行进一步处理
                     String filePath = destinationDir+tempFileName; // 文件将被保存到 C 盘根目录下,文件名为 output_file.wav
@@ -548,13 +836,13 @@ public class AudioUtils {
                 .replaceAll("[a-zA-Z]", "")
                 .replaceAll("\\s", "");
         try {
-            String fileUrl = "http://127.0.0.1:9881/?text="+text+"&ref_audio=./参考音频/companyUser"+userId+".wav&ref_text=在这片神奇的森林里,小鸟轻快地唱着歌,仿佛在诉说着春天的故事。"; // 替换为要下载的文件URL
+            String fileUrl = "http://127.0.0.1:9880/?text="+text+"&ref_audio=./参考音频/companyUser"+userId+".wav&ref_text=在这片神奇的森林里,小鸟轻快地唱着歌,仿佛在诉说着春天的故事。"; // 替换为要下载的文件URL
             try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                 HttpGet httpGet = new HttpGet(fileUrl);
                 HttpResponse response = httpClient.execute(httpGet);
                 HttpEntity entity = response.getEntity();https://hf-mirror.com
                 if (entity != null) {
-                     inputStream = entity.getContent();
+                    inputStream = entity.getContent();
                     byte[] fileBytes = convertInputStreamToByteArray(inputStream);
                     // 对文件字节数组进行进一步处理
                     String filePath = destinationDir+tempFileName; // 文件将被保存到 C 盘根目录下,文件名为 output_file.wav
@@ -655,7 +943,7 @@ public class AudioUtils {
             Double v = clip.getMicrosecondLength() / 1000000D;
             return v;
         } catch (Exception e) {
-            System.out.println("cuo");
+            log.info("cuo");
             return null;
         } finally {
             if(clip!=null){
@@ -674,8 +962,5 @@ public class AudioUtils {
         }
         return byteOutput.toByteArray();
     }
-    // 省略其他方法的实现
-
-
 
 }

+ 4 - 0
fs-service/src/main/java/com/fs/fastgptApi/vo/AudioVO.java

@@ -4,6 +4,10 @@ import lombok.Data;
 
 @Data
 public class AudioVO {
+    private Long id;
     private Integer duration;//秒
+    private String voiceTxt;//文本
     private String url;
+    private String wavUrl;
+    private Integer recordType;
 }

+ 1 - 0
fs-service/src/main/java/com/fs/his/enums/FsStoreOrderLogEnum.java

@@ -15,6 +15,7 @@ public enum FsStoreOrderLogEnum {
     REFUND_ORDER_APPLY("apply_refund","用户申请退款"),
     REFUND_ORDER_PLATFORM("REFUND_ORDER_PLATFORM","平台申请退款"),
     REFUND_ORDER_DF("refund_order_df","代服取消订单,申请退款"),
+    UPDATE_ORDER_DF("update_order_df","代服取消订单,需要重新发货"),
     TAKE_ORDER_DELIVERY("user_take_delivery","用户已收货"),
     PAY_ORDER_SUCCESS("pay_success","用户付款成功"),
     PAY_REMAIN_ORDER_SUCCESS("pay_remain_success","用户付款尾款成功"),

+ 6 - 3
fs-service/src/main/java/com/fs/his/mapper/FsPackageMapper.java

@@ -83,7 +83,7 @@ public interface FsPackageMapper
             "            <if test=\"maps.diseaseType != null \"> and disease_type = #{maps.diseaseType}</if>\n" +
             "            <if test=\"maps.isShow != null \"> and is_show = #{maps.isShow}</if>\n" +
             "            <if test=\"maps.solarTerm != null \"> and solar_term = #{maps.solarTerm}</if>\n" +
-            "            <if test=\"maps.appId != null and maps.appId != ''\"> and (FIND_IN_SET(#{maps.appId}, app_ids) > 0) or app_ids is null or app_ids = ''</if>\n" +
+            "            <if test=\"maps.appId != null and maps.appId != ''\"> and ((FIND_IN_SET(#{maps.appId}, app_ids) > 0) or app_ids is null or app_ids = '')</if>\n" +
             "        order by sort,package_id desc"+
             "</script>"})
 
@@ -126,10 +126,10 @@ public interface FsPackageMapper
             "and p.is_show = #{maps.isShow} " +
             "</if>" +
             "<if test = 'maps.appId != null and maps.appId != \"\"'> " +
-            "and (FIND_IN_SET(#{maps.appId}, app_ids) > 0) or app_ids is null or app_ids = ''" +
+            "and ((FIND_IN_SET(#{maps.appId}, app_ids) > 0) or app_ids is null or app_ids = '')" +
             "</if>" +
             "<if test = 'maps.appId == null or maps.appId == \"\"'> " +
-            "and app_ids is null or app_ids = ''" +
+            "and (app_ids is null or app_ids = '')" +
             "</if>" +
             " order by p.sort desc,package_id  "+
             "</script>"})
@@ -152,4 +152,7 @@ public interface FsPackageMapper
 
     @Update("UPDATE fs_package SET solar_term = NULL WHERE package_id = #{packageId}")
     void setSolarTermIsNullById(@Param("packageId") Long packageId);
+
+    List<FsPackage> selectFsPackageListByIds(Long[] packageIds);
+
 }

+ 1 - 1
fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderMapper.java

@@ -1156,7 +1156,7 @@ public interface FsStoreOrderMapper
     @Update("UPDATE fs_store_order SET delivery_code = #{deliveryCode} WHERE extend_order_id = #{extendOrderId}")
     int updateFsStoreOrderByExtendOrderId(@Param("extendOrderId")String extendOrderId,@Param("deliveryCode")String deliveryCode);
 
-    @Select("select * from fs_store_order where  `status`=3 and extend_order_id is not null and delivery_sn is not null")
+    @Select("select * from fs_store_order where  `status`=3 and (extend_order_id is not null or extend_order_id != '') and (delivery_sn is not null or delivery_sn != '')")
     List<FsStoreOrder> selectShippedOrder();
 
     List<FsStoreOrderListVO> selectFsStoreOrderListVOByErpAccount(@Param("maps") FsStoreOrderParam fsStoreOrder);

+ 4 - 0
fs-service/src/main/java/com/fs/his/service/IFsPackageService.java

@@ -86,4 +86,8 @@ public interface IFsPackageService
      * @return icdName
      */
     List<String> selectIcdNameByPackageId(Long packageId);
+
+    List<FsPackage> selectFsPackageListByIds(Long[] packageIds);
+
+    int bulkCopyFsPackageByPackage(Long[] packageIds);
 }

+ 21 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsPackageServiceImpl.java

@@ -323,5 +323,26 @@ public class FsPackageServiceImpl implements IFsPackageService {
     public List<String> selectIcdNameByPackageId(Long packageId) {
         return fsPackageMapper.selectIcdNameByPackageId(packageId);
     }
+
+    @Override
+    public List<FsPackage> selectFsPackageListByIds(Long[] packageIds) {
+        return fsPackageMapper.selectFsPackageListByIds(packageIds);
+    }
+
+    @Override
+    @Transactional
+    public int bulkCopyFsPackageByPackage(Long[] packageIds) {
+        List<FsPackage> list = fsPackageMapper.selectFsPackageListByIds(packageIds);
+        if(list != null && !list.isEmpty()){
+            try {
+                for (FsPackage fsPackage : list) {
+                    fsPackageMapper.insertFsPackage(fsPackage);
+                }
+            } catch (Exception e) {
+                return 0;
+            }
+        }
+        return 1;
+    }
 }
 

+ 23 - 4
fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java

@@ -3415,13 +3415,32 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
                 FsStoreOrder fsStoreOrder = fsStoreOrderMapper.selectFsStoreOrderByOrderCode(orderNumber);
                 FsStoreOrder tempOrder = new FsStoreOrder();
                 tempOrder.setOrderId(fsStoreOrder.getOrderId());
-                tempOrder.setExtendOrderId("");
-                fsStoreOrderMapper.updateFsStoreOrder(tempOrder);
+                Integer deliveryStatus = fsStoreOrder.getDeliveryStatus();
+                if (deliveryStatus == null || deliveryStatus == 0) {
+                    //没有物流信息
+                    //修改订单状态 方便后续重新发货
+                    tempOrder.setStatus(FsStoreOrderStatusEnum.STATUS_2.getValue());
+                    tempOrder.setExtendOrderId("");
+                    tempOrder.setDeliverySn("");
+                    fsStoreOrderMapper.updateFsStoreOrder(tempOrder);
+                    fsStoreOrderLogsService.create(tempOrder.getOrderId(), FsStoreOrderLogEnum.UPDATE_ORDER_DF.getValue(),
+                            "运单不存在,"+FsStoreOrderLogEnum.UPDATE_ORDER_DF.getDesc());
+                } else {
+                    //有物流信息->售后处理
+                    //取消订单
+                    FsStoreOrderSalesParam afterSalesParam = new FsStoreOrderSalesParam();
+                    afterSalesParam.setOrderId(tempOrder.getOrderId());
+                    afterSalesParam.setReasons("代服管家取消订单");
+                    afterSalesParam.setOperator("代服管家");
+                    fsStoreOrderService.afterSales(afterSalesParam);
+                    fsStoreOrderLogsService.create(tempOrder.getOrderId(), FsStoreOrderLogEnum.REFUND_ORDER_DF.getValue(),
+                            "运单不存在,"+FsStoreOrderLogEnum.REFUND_ORDER_DF.getDesc());
+                }
                 FsStoreOrderDf df = new FsStoreOrderDf();
-                df.setOrderId(fsStoreOrder.getOrderId());
+                df.setOrderId(tempOrder.getOrderId());
                 df.setStatus(0); //回到默认
-                df.setFailMsg(failMsg); //失败消息
                 df.setUpdateTime(new Date());
+                df.setFailMsg(failMsg); //失败消息
                 fsStoreOrderDfMapper.updateFsStoreOrderDf(df);
                 //推送失败消息
                 return R.ok("接收成功").put("code", "ok");

+ 3 - 0
fs-service/src/main/java/com/fs/hisStore/config/MedicalMallConfig.java

@@ -28,6 +28,8 @@ public class MedicalMallConfig {
     private boolean isAudit;
     // 资源配置
     private boolean isResource;
+    // 多店铺
+    private boolean isStores;
     @PostConstruct
     public void init() {
         JSONObject jsonObject = configUtil.generateConfigByKey("medicalMall.func.switch");
@@ -36,6 +38,7 @@ public class MedicalMallConfig {
             this.isStatics = Objects.equals(jsonObject.getString("statics"), "1");
             this.isAudit = Objects.equals(jsonObject.getString("isAudit"), "1");
             this.isResource = Objects.equals(jsonObject.getString("isResource"), "1");
+            this.isStores = Objects.equals(jsonObject.getString("isStores"), "1");
         }
     }
 

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductScrmMapper.java

@@ -398,4 +398,6 @@ public interface FsStoreProductScrmMapper
             "where find_in_set(p.product_id,#{ids})  " +
             "</script>"})
     List<FsStoreProductActivityListVO> selectFsStoreProductByIdsAudit(String productIds,@Param("config") MedicalMallConfig  config);
+
+    List<FsStoreProductScrm> bulkCopyFsStoreProductByIds(Long[] productIds);
 }

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreProductScrmService.java

@@ -135,4 +135,6 @@ public interface IFsStoreProductScrmService
     FsStoreProductScrm selectFsStoreProductByIdAudit(Long productId);
 
     List<FsStoreProductActivityListVO> selectFsStoreProductByIdsAudit(String productIds);
+
+    R bulkCopyFsStoreProductByIds(Long[] productIds);
 }

+ 129 - 26
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -17,6 +17,7 @@ import com.fs.common.exception.CustomException;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.DateUtils;
 import com.fs.company.cache.ICompanyCacheService;
+import com.fs.config.cloud.CloudHostProper;
 import com.fs.erp.domain.ErpGoods;
 import com.fs.erp.service.IErpGoodsService;
 import com.fs.his.config.FsSysConfig;
@@ -94,6 +95,9 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
     @Autowired
     private MedicalMallConfig medicalMallConfig;
 
+    @Autowired
+    private CloudHostProper cloudHostProper;
+
     /**
      * 查询商品
      *
@@ -334,34 +338,37 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
         product.setStoreId(param.getStoreId());
         product.setIsDrug(param.getIsDrug().toString());
         //校验店铺资质信息
-        //获取店铺
-        FsStoreScrm store = fsStoreScrmService.selectFsStoreByStoreId(product.getStoreId());
-        if(store == null || 1 != store.getStatus()){
-            return R.error("店铺不存在或未启用");
-        }else{
-            //验证资质
-            switch (product.getProductType()){
-                case 1://非处方
-                    break;
-                case 2://处方
-                    if("".equals(store.getDrugLicense()) ||  LocalDate.now().isBefore(store.getDrugLicenseExpiryEnd())){
-                        return R.error("店铺药品资质为空或已过期,请完善后再添加");
-                    }
-                    break;
-                case 3://食品
-                    if("".equals(store.getFoodLicense()) ||  LocalDate.now().isBefore(store.getFoodLicenseExpiryEnd())){
-                        return R.error("店铺食品资质为空或已过期,请完善后再添加");
-                    }
-                    break;
-                case 4://器械
-                    if("".equals(store.getMedicalDevice3()) ||  LocalDate.now().isBefore(store.getMedicalDevice3ExpiryEnd())){
-                        return R.error("店铺器械资质为空或已过期,请完善后再添加");
-                    }
-                    break;
-                default:
-                    return R.error("商品类型错误");
+        if(!("益善缘".equals(cloudHostProper.getCompanyName()))){
+            //获取店铺
+            FsStoreScrm store = fsStoreScrmService.selectFsStoreByStoreId(product.getStoreId());
+            if(store == null || 1 != store.getStatus()){
+                return R.error("店铺不存在或未启用");
+            }else{
+                //验证资质
+                switch (product.getProductType()){
+                    case 1://非处方
+                        break;
+                    case 2://处方
+                        if("".equals(store.getDrugLicense()) ||  LocalDate.now().isBefore(store.getDrugLicenseExpiryEnd())){
+                            return R.error("店铺药品资质为空或已过期,请完善后再添加");
+                        }
+                        break;
+                    case 3://食品
+                        if("".equals(store.getFoodLicense()) ||  LocalDate.now().isBefore(store.getFoodLicenseExpiryEnd())){
+                            return R.error("店铺食品资质为空或已过期,请完善后再添加");
+                        }
+                        break;
+                    case 4://器械
+                        if("".equals(store.getMedicalDevice3()) ||  LocalDate.now().isBefore(store.getMedicalDevice3ExpiryEnd())){
+                            return R.error("店铺器械资质为空或已过期,请完善后再添加");
+                        }
+                        break;
+                    default:
+                        return R.error("商品类型错误");
+                }
             }
         }
+
         if(param.getProductId() != null && param.getProductId() > 0){
             //对已上架的商品进行修改需要重新审核
             if(1 == product.getIsShow() && "1".equals(product.getIsAudit())){
@@ -1037,4 +1044,100 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
     public List<FsStoreProductActivityListVO> selectFsStoreProductByIdsAudit(String productIds) {
         return fsStoreProductMapper.selectFsStoreProductByIdsAudit(productIds,medicalMallConfig);
     }
+
+    @Override
+    @Transactional
+    public R bulkCopyFsStoreProductByIds(Long[] productIds) {
+        storeAuditLogUtil.addBatchAuditArray(productIds, "", "");
+        List<FsStoreProductScrm> list = fsStoreProductMapper.bulkCopyFsStoreProductByIds(productIds);
+        if(list != null && !list.isEmpty()){
+            for (FsStoreProductScrm product : list) {
+                List<FsStoreProductAttrValueScrm> param = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueByProductId(product.getProductId());
+                //获取店铺
+                FsStoreScrm store = fsStoreScrmService.selectFsStoreByStoreId(product.getStoreId());
+                if(store == null || 1 != store.getStatus()){
+                    return R.error("ID为:" + product.getProductId() + "的店铺不存在或未启用");
+                }else{
+                    //验证资质
+                    switch (product.getProductType()){
+                        case 1://非处方
+                            break;
+                        case 2://处方
+                            if("".equals(store.getDrugLicense()) ||  LocalDate.now().isBefore(store.getDrugLicenseExpiryEnd())){
+                                return R.error("ID为:" + product.getProductId() + "的店铺药品资质为空或已过期,请完善后再添加");
+                            }
+                            break;
+                        case 3://食品
+                            if("".equals(store.getFoodLicense()) ||  LocalDate.now().isBefore(store.getFoodLicenseExpiryEnd())){
+                                return R.error("ID为:" + product.getProductId() + "的店铺食品资质为空或已过期,请完善后再添加");
+                            }
+                            break;
+                        case 4://器械
+                            if("".equals(store.getMedicalDevice3()) ||  LocalDate.now().isBefore(store.getMedicalDevice3ExpiryEnd())){
+                                return R.error("ID为:" + product.getProductId() + "的店铺器械资质为空或已过期,请完善后再添加");
+                            }
+                            break;
+                        default:
+                            return R.error("ID为:" + product.getProductId() + "的商品类型错误");
+                    }
+                }
+
+                fsStoreProductMapper.insertFsStoreProduct(product);
+                Long fsStoreProductId = product.getProductId();
+
+                storeAuditLogUtil.addOperLog(product.getProductId());
+
+                if (product.getSpecType().equals(0)) {
+                    ProductArrtDTO fromatDetailDto = ProductArrtDTO.builder()
+                            .value("规格")
+                            .detail(ListUtil.toList("默认"))
+                            .build();
+                    List<ProductArrtDTO> items=new ArrayList<>();
+                    items.add(fromatDetailDto);
+                    param.get(0).setSku("默认");
+                    addProductAttr(product.getProductId(),items,param);
+                } else {
+                    List<FsStoreProductAttrScrm> items = fsStoreProductAttrMapper.selectFsStoreProductAttrByProductId(product.getProductId());
+
+                    //清空attr
+                    fsStoreProductAttrMapper.clear(fsStoreProductId);
+                    //清空values
+                    //查出商品属性所有ID;
+                    List<FsStoreProductAttrValueScrm> attrValues=fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueByProductId(fsStoreProductId);
+                    fsStoreProductAttrValueMapper.deleteFsStoreProductAttrValueByProductId(fsStoreProductId);
+
+                    //写入attr
+                    for(FsStoreProductAttrScrm vo:items){
+                        vo.setProductId(fsStoreProductId);
+                        fsStoreProductAttrMapper.insertFsStoreProductAttr(vo);
+                    }
+                    Map<String,Object> map = new LinkedHashMap<>();
+                    map.put("attr",items);
+                    // map.put("value",values);
+
+                    for(FsStoreProductAttrValueScrm val: param){
+                        //更新套餐商品属性ID  获取套餐
+                        Long id=val.getId();
+                        if(val.getDetail()!=null){
+                            List<String> stringList = new ArrayList<>(val.getDetail().values());
+                            Collections.sort(stringList);
+                            val.setSku(StrUtil.join(",",stringList));
+                        }
+                        val.setProductId(fsStoreProductId);
+                        fsStoreProductAttrValueMapper.insertFsStoreProductAttrValue(val);
+                        if(attrValues!=null && !attrValues.isEmpty()){
+                            for(FsStoreProductAttrValueScrm attrValue:attrValues){
+                                if(attrValue.getId().equals(id)){
+                                    fsStoreProductGroupMapper.updateProducts(attrValue.getId(),val.getId());
+                                    fsStoreProductPackageMapper.updateProducts(attrValue.getId(),val.getId());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            return R.ok();
+        }
+        return R.ok();
+    }
 }

+ 5 - 1
fs-service/src/main/java/com/fs/sop/domain/QwSopTempVoice.java

@@ -49,12 +49,16 @@ public class QwSopTempVoice{
     @Excel(name = "语音文件路径")
     private String voiceUrl;
 
+    /** 用户语音文件路径 */
+    private String userVoiceUrl;
+
     /** 语音文件路径 */
     @Excel(name = "秒")
     private Integer duration;
-    /** 语音文件路径 */
+
     @Excel(name = "秒")
     private LocalDateTime createTime;
+
     private LocalDateTime updateTime;
 
     //@Excel(name = "是否录制完成")

+ 2 - 0
fs-service/src/main/java/com/fs/sop/mapper/QwSopTempContentMapper.java

@@ -102,4 +102,6 @@ public interface QwSopTempContentMapper extends BaseMapper<QwSopTempContent>{
             "left join qw_sop_temp_rules tr on  tc.rules_id=tr.id " +
             "where tc.temp_id=#{tempId}")
     List<QwSopTempContent> selectQwSopTempContentByTempIdAndRules(@Param("tempId") String tempId);
+
+    List<QwSopTempContent> selectQwSopTempContentByTempId(@Param("tempId") String tempId);
 }

+ 11 - 0
fs-service/src/main/java/com/fs/sop/mapper/QwSopTempVoiceMapper.java

@@ -79,4 +79,15 @@ public interface QwSopTempVoiceMapper extends BaseMapper<QwSopTempVoice>{
     List<QwSopTempVoice> selectAllByUserIds(@Param("userIdList") List<Long> userIdList);
 
     QwSopTempVoice selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(@Param("companyUserId") Long companyUserId,@Param("voiceTxt") String voiceTxt);
+
+    QwSopTempVoice selectQwSopTempVoiceByCompanyUserIdAndQwUserId(@Param("companyUserId") Long companyUserId,@Param("qwUserId") Long qwUserId);
+
+    List<QwSopTempVoice> selectQwSopTempVoiceListLimit(QwSopTempVoice sopTempVoice);
+
+    @DataSource(DataSourceType.SOP)
+    QwSopTempVoice selectQwSopTempVoiceByIdAndUserVoiceUrl(@Param("id")Long id);
+
+    @DataSource(DataSourceType.SOP)
+    List<QwSopTempVoice> selectQwSopTempVoiceNewList(QwSopTempVoice sopTempVoice);
+
 }

+ 2 - 0
fs-service/src/main/java/com/fs/sop/service/IQwSopTempContentService.java

@@ -82,4 +82,6 @@ public interface IQwSopTempContentService extends IService<QwSopTempContent>{
     List<QwSopTempContent> listByTempAndDay(String tempId, Long dayId);
 
     void updateDay(QwSopTempContent content);
+
+    List<QwSopTempContent> selectQwSopTempContentByTempId(String tempId);
 }

+ 10 - 0
fs-service/src/main/java/com/fs/sop/service/IQwSopTempVoiceService.java

@@ -79,4 +79,14 @@ public interface IQwSopTempVoiceService extends IService<QwSopTempVoice>{
     List<QwSopTempVoice> listByTempIdAndCompanyId(String tempId, List<Long> longs);
 
     QwSopTempVoice selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(Long companyUserId, String voiceTxt);
+
+    QwSopTempVoice selectQwSopTempVoiceByCompanyUserIdAndQwUserId(Long companyUserId, Long qwUserId);
+
+    List<QwSopTempVoice> selectQwSopTempVoiceListLimit(QwSopTempVoice sopTempVoice);
+
+    void insertQwSopTempVoiceModel(Long userId);
+
+    QwSopTempVoice selectQwSopTempVoiceByIdAndUserVoiceUrl(Long id);
+
+    List<QwSopTempVoice> selectQwSopTempVoiceNewList(QwSopTempVoice sopTempVoice);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempContentServiceImpl.java

@@ -142,4 +142,10 @@ public class QwSopTempContentServiceImpl extends ServiceImpl<QwSopTempContentMap
     public void updateDay(QwSopTempContent content) {
         baseMapper.updateDay(content.getId(), content.getDayId());
     }
+
+    @Override
+    @DataSource(DataSourceType.SOP)
+    public List<QwSopTempContent> selectQwSopTempContentByTempId(String tempId) {
+        return baseMapper.selectQwSopTempContentByTempId(tempId);
+    }
 }

+ 53 - 4
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempVoiceServiceImpl.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.common.annotation.DataSource;
+import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.enums.DataSourceType;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.PubFun;
@@ -26,12 +27,11 @@ import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 import java.util.stream.Collectors;
 
+import static com.fs.common.utils.DictUtils.getDictCache;
+
 /**
  * 模板对应的销售语音文件Service业务层处理
  *
@@ -280,4 +280,53 @@ public class QwSopTempVoiceServiceImpl extends ServiceImpl<QwSopTempVoiceMapper,
     public QwSopTempVoice selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(Long companyUserId, String voiceTxt) {
         return qwSopTempVoiceMapper.selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(companyUserId,voiceTxt);
     }
+    @Override
+    @DataSource(DataSourceType.SOP)
+    public QwSopTempVoice selectQwSopTempVoiceByCompanyUserIdAndQwUserId(Long companyUserId, Long qwUserId) {
+        return qwSopTempVoiceMapper.selectQwSopTempVoiceByCompanyUserIdAndQwUserId(companyUserId,qwUserId);
+    }
+
+    @Override
+    @DataSource(DataSourceType.SOP)
+    public List<QwSopTempVoice> selectQwSopTempVoiceListLimit(QwSopTempVoice sopTempVoice) {
+        return qwSopTempVoiceMapper.selectQwSopTempVoiceListLimit(sopTempVoice);
+    }
+
+    /**
+     * 在用户上传声纹时生成语音模板
+     * @param userId
+     */
+    @Override
+    public void insertQwSopTempVoiceModel(Long userId) {
+        List<SysDictData> dictCache = getDictCache("sys_fastgpt_voice_model");
+        if(dictCache != null){
+            List<QwSopTempVoice> qwUserIdList = new ArrayList<>();
+
+            for (SysDictData sysDictData : dictCache) {
+                QwSopTempVoice qwSopTempVoice = new QwSopTempVoice();
+                qwSopTempVoice.setVoiceTxt(sysDictData.getDictLabel());
+                qwSopTempVoice.setCompanyUserId(userId);
+                QwSopTempVoice sopTempVoice = qwSopTempVoiceMapper.selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(userId, sysDictData.getDictLabel());
+                if(sopTempVoice == null){
+                    qwSopTempVoice.setRecordType(2);
+                    qwUserIdList.add(qwSopTempVoice);
+                }
+            }
+            if(!qwUserIdList.isEmpty()){
+                qwSopTempVoiceMapper.insertBatch(qwUserIdList);
+            }
+        }
+    }
+
+    @Override
+    @DataSource(DataSourceType.SOP)
+    public QwSopTempVoice selectQwSopTempVoiceByIdAndUserVoiceUrl(Long id) {
+        return qwSopTempVoiceMapper.selectQwSopTempVoiceByIdAndUserVoiceUrl(id);
+    }
+
+    @Override
+    @DataSource(DataSourceType.SOP)
+    public List<QwSopTempVoice> selectQwSopTempVoiceNewList(QwSopTempVoice sopTempVoice) {
+        return qwSopTempVoiceMapper.selectQwSopTempVoiceNewList(sopTempVoice);
+    }
 }

+ 4 - 1
fs-service/src/main/java/com/fs/wxwork/service/WxWorkServiceImpl.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.TypeReference;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
+import com.fs.config.ai.AiHostProper;
 import com.fs.qw.domain.QwIpadServer;
 import com.fs.qw.service.IQwIpadServerService;
 import com.fs.wxwork.dto.*;
@@ -28,6 +29,8 @@ public class WxWorkServiceImpl implements WxWorkService {
     RedisCache redisCache;
     @Autowired
     IQwIpadServerService qwIpadServerService;
+    @Autowired
+    AiHostProper aiHostProper;
     public String getUrl(Long serverId) {
         String url = redisCache.getCacheObject("serverId:" + serverId);
         if (url != null && !url.isEmpty()) {
@@ -273,7 +276,7 @@ public class WxWorkServiceImpl implements WxWorkService {
 
     @Override
     public WxwSilkVoceDTO getSilkVoice(String param, Long companyUserId) {
-        String  url="http://162.14.193.126:8009/app/common/voice?voice="+param+"&id="+companyUserId;
+        String  url= aiHostProper.getVoiceApi() + "/app/common/voice?voice="+param+"&id="+companyUserId;
         String json = WxWorkHttpUtil.get(url);
         WxwSilkVoceDTO wxwSilkVoceDTO = JSON.parseObject(json, WxwSilkVoceDTO.class);
         return wxwSilkVoceDTO;

+ 1 - 0
fs-service/src/main/resources/application-config-bly.yml

@@ -132,6 +132,7 @@ nuonuo:
 ipad:
   ipadUrl:
   aiApi:
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id: VXEvKaGNPFuJmhWK9O_QPrTZxe9umDCukq-maI8Vdek
   inquiry_temp_id: 9POPYeqhI48LOPvq-Rfoklze7H-9SlunJKh10Qt4_2I

+ 1 - 0
fs-service/src/main/resources/application-config-dev-yjb.yml

@@ -106,6 +106,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi: http://152.136.202.157:3000/api
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-dev.yml

@@ -105,6 +105,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi: http://152.136.202.157:3000/api
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-bjczwh.yml

@@ -85,6 +85,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi: 1212121212
+  voiceApi:
 
 wx_miniapp_temp:
   pay_order_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-bnkc.yml

@@ -84,6 +84,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi: 1212121212
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-drk-test.yml

@@ -84,6 +84,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi: 1212121212
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-drk.yml

@@ -84,6 +84,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi: 1212121212
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-fby.yml

@@ -105,6 +105,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.jiuzhouzaixian.com
   aiApi:
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id: VXEvKaGNPFuJmhWK9O_QPrTZxe9umDCukq-maI8Vdek
   inquiry_temp_id: 9POPYeqhI48LOPvq-Rfoklze7H-9SlunJKh10Qt4_2I

+ 1 - 0
fs-service/src/main/resources/application-config-druid-hcl.yml

@@ -90,6 +90,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi:
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-hdt.yml

@@ -86,6 +86,7 @@ ipad:
   ipadUrl: http://ipad.hebeihdt.com
 #  aiApi: http://152.136.202.157:3000/api
   aiApi: http://49.232.181.28:3000/api
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-hst.yml

@@ -82,6 +82,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.schstyl.cn
   aiApi:
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-hyt.yml

@@ -77,6 +77,7 @@ headerImg:
 ipad:
   ipadUrl:
   aiApi:
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id: -SjnK9K6cNKASa6AD9Q_c0YT7J1lPTEpPIpqbMJF8F0
   inquiry_temp_id: hwFXVh0AWqeasBsZpa0-urb3CrPeYEwBiy3P6AMMGFQ

+ 1 - 0
fs-service/src/main/resources/application-config-druid-hzyy.yml

@@ -90,6 +90,7 @@ headerImg:
 ipad:
   ipadUrl: http://139.159.133.223:8667
   aiApi: http://1.95.196.10:3000/api
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-jkj.yml

@@ -83,6 +83,7 @@ headerImg:
   imgUrl: https://jkj-1323137866.cos.ap-chongqing.myqcloud.com/fs/logo/jkj.png
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 3 - 3
fs-service/src/main/resources/application-config-druid-jnlzjk.yml

@@ -83,9 +83,9 @@ headerImg:
   imgUrl:
 
 ipad:
-  ipadUrl: http://ipad.cdwjyyh.com
-  aiApi: 1212121212
-
+  ipadUrl: http://ipadjnlzjk.ylrztop.com
+  aiApi: http://49.232.181.28:3000/
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-jnmy.yml

@@ -87,6 +87,7 @@ headerImg:
 ipad:
   ipadUrl: http://qwipad.jnmyunl.com
   aiApi: http://49.232.181.28:3000/api
+  voiceApi: http://162.14.193.126:8009
 wx_miniapp_temp:
   pay_order_temp_id: -SjnK9K6cNKASa6AD9Q_c0YT7J1lPTEpPIpqbMJF8F0
   inquiry_temp_id: hwFXVh0AWqeasBsZpa0-urb3CrPeYEwBiy3P6AMMGFQ

+ 1 - 0
fs-service/src/main/resources/application-config-druid-jzzx.yml

@@ -89,6 +89,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.jiuzhouzaixian.com
   aiApi: http://1.95.196.10:3000/api
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id: VXEvKaGNPFuJmhWK9O_QPrTZxe9umDCukq-maI8Vdek
   inquiry_temp_id: 9POPYeqhI48LOPvq-Rfoklze7H-9SlunJKh10Qt4_2I

+ 1 - 0
fs-service/src/main/resources/application-config-druid-kyt.yml

@@ -82,6 +82,7 @@ headerImg:
 ipad:
   ipadUrl: http://kytIpad.ylrzcloud.com
   aiApi:
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-nmgyt.yml

@@ -85,6 +85,7 @@ headerImg:
 ipad:
   ipadUrl: http://qwipad.nmghysmytdyf.com
   aiApi: http://127.0.0.1:3000/api
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id: V
   inquiry_temp_id: 9

+ 1 - 0
fs-service/src/main/resources/application-config-druid-qdtst.yml

@@ -90,6 +90,7 @@ headerImg:
 ipad:
   ipadUrl:
   aiApi:
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-sft.yml

@@ -82,6 +82,7 @@ headerImg:
 ipad:
   ipadUrl: http://qwipad.cqsft.vip
   aiApi:
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id: V
   inquiry_temp_id: 9

+ 1 - 0
fs-service/src/main/resources/application-config-druid-sxjz.yml

@@ -86,6 +86,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.xintaihl.cn
   aiApi: http://1.95.196.10:3000/api
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-syysy.yml

@@ -82,6 +82,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.ysya.top
   aiApi: http://49.232.181.28:3000/api
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-whhm.yml

@@ -84,6 +84,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi: 1212121212
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-xfk.yml

@@ -89,6 +89,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi:
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id: VXEvKaGNPFuJmhWK9O_QPrTZxe9umDCukq-maI8Vdek
   inquiry_temp_id: 9POPYeqhI48LOPvq-Rfoklze7H-9SlunJKh10Qt4_2I

+ 1 - 0
fs-service/src/main/resources/application-config-druid-xzt.yml

@@ -84,6 +84,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi: 1212121212
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-yjb.yml

@@ -85,6 +85,7 @@ headerImg:
 ipad:
   ipadUrl: http://ipad.bjyjbao.com
   aiApi: http://152.136.202.157:3000/api
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-yzt.yml

@@ -81,6 +81,7 @@ headerImg:
   imgUrl: https://yztcourse-1325300895.cos.ap-guangzhou.myqcloud.com/yztcourse/20250523/e04871a98cc84be39a7f60c084698e21.jpg
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-zsjk.yml

@@ -70,6 +70,7 @@ headerImg:
   imgUrl: https://zs-1362480099.cos.ap-beijing.myqcloud.com/fs/20250618/4839e2ff3bdb4908b459abea45a04f4b.png
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid.yml

@@ -69,6 +69,7 @@ headerImg:
   imgUrl: https://jz-cos-1356808054.cos.ap-chengdu.myqcloud.com/fs/20250515/0877754b59814ea8a428fa3697b20e68.png
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-myhk.yml

@@ -88,6 +88,7 @@ ipad:
   ipadUrl: http://qwipad.muyi88.com
 #  aiApi: http://152.136.202.157:3000/api
   aiApi: http://49.232.181.28:3000/api
+  voiceApi:
 
 wx_miniapp_temp:
   pay_order_temp_id: VXEvKaGNPFuJmhWK9O_QPrTZxe9umDCukq-maI8Vdek

+ 1 - 0
fs-service/src/main/resources/application-config-zkzh.yml

@@ -140,6 +140,7 @@ nuonuo:
 ipad:
   ipadUrl: http://qwipad.muyi88.com
   aiApi: http://152.136.202.157:3000/api
+  voiceApi:
 wx_miniapp_temp:
   pay_order_temp_id: VXEvKaGNPFuJmhWK9O_QPrTZxe9umDCukq-maI8Vdek
   inquiry_temp_id: 9POPYeqhI48LOPvq-Rfoklze7H-9SlunJKh10Qt4_2I

+ 1 - 0
fs-service/src/main/resources/application-druid-lmjy-test.yml

@@ -151,6 +151,7 @@ rocketmq:
 ipad:
     ipadUrl:
     aiApi:
+    voiceApi:
 openIM:
     secret:
     userID:

+ 1 - 0
fs-service/src/main/resources/application-druid-lmjy.yml

@@ -151,6 +151,7 @@ rocketmq:
 ipad:
     ipadUrl:
     aiApi:
+    voiceApi:
 openIM:
     secret:
     userID:

+ 8 - 0
fs-service/src/main/resources/mapper/his/FsPackageMapper.xml

@@ -78,6 +78,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         WHERE package_id = #{packageId};
     </select>
 
+    <select id="selectFsPackageListByIds" resultType="com.fs.his.domain.FsPackage">
+        <include refid="selectFsPackageVo"/>
+        where package_id in
+        <foreach item="packageId" collection="array" open="(" separator="," close=")">
+            #{packageId}
+        </foreach>
+    </select>
+
     <insert id="insertFsPackage" parameterType="FsPackage" useGeneratedKeys="true" keyProperty="packageId">
         insert into fs_package
         <trim prefix="(" suffix=")" suffixOverrides=",">

+ 7 - 0
fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml

@@ -445,4 +445,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </if>
         and fsp.is_best=1 and fsp.is_display=1 order by fsp.sort desc,fsp.product_id desc
     </select>
+    <select id="bulkCopyFsStoreProductByIds" resultMap="FsStoreProductResult">
+        <include refid="selectFsStoreProductVo"/>
+        where product_id in
+        <foreach item="productId" collection="array" open="(" separator="," close=")">
+            #{productId}
+        </foreach>
+    </select>
 </mapper>

+ 6 - 2
fs-service/src/main/resources/mapper/qw/QwUserVoiceLogMapper.xml

@@ -29,6 +29,10 @@
             <id column="qw_user_id" property="qwUserId"></id>
             <result column="qw_user_name" property="qwUserName"></result>
         </association>
+        <association property="qwExternalContact" javaType="com.fs.qw.domain.QwExternalContact" autoMapping="true">
+            <id column="id" property="id"></id>
+            <result column="name" property="name"></result>
+        </association>
     </resultMap>
 
     <resultMap type="com.fs.qw.vo.QwUserVoiceLogVo" id="QwUserVoiceLogVoResult" extends="QwUserVoiceLogResult">
@@ -57,7 +61,7 @@
     </sql>
 
     <select id="selectQwUserVoiceLogList" resultMap="QwUserVoiceLogVoResult">
-        select uvl.id, ext_id, uvl.qw_user_id, duration, title, uvl.status, uvl.corp_id, uvl.company_id, uvl.company_user_id, uvl.create_time,qec.`name`,qec.tag_ids,c.company_name,cu.user_name,qu.qw_user_name
+        select uvl.id, ext_id, uvl.qw_user_id, duration, title, uvl.status, uvl.corp_id, uvl.company_id, uvl.company_user_id, uvl.create_time,qec.`name`,qec.tag_ids tagIds,c.company_name,cu.user_name,qu.qw_user_name
         from qw_user_voice_log uvl
         left join qw_external_contact qec on uvl.ext_id = qec.id
         left join company c on uvl.company_id = c.company_id
@@ -98,7 +102,7 @@
     </select>
     <select id="selectQwUserVoiceLogTotalList" resultMap="QwUserVoiceLogVoTotalResult">
         SELECT uvl.id, ext_id, uvl.qw_user_id, duration, title, uvl.status, uvl.corp_id,
-               uvl.company_id, uvl.company_user_id, uvl.create_time,qec.`name`,qec.tag_ids,qu.qw_user_name,
+               uvl.company_id, uvl.company_user_id, uvl.create_time,qec.`name`,qec.tag_ids tagIds,qu.qw_user_name,
         SUM(duration) duration,
         COUNT(CASE WHEN uvl.status=1 THEN 1 END) AS connectCount,
         COUNT(CASE WHEN uvl.status=2 THEN 1 END) AS noConnectCount

+ 4 - 0
fs-service/src/main/resources/mapper/sop/QwSopTempContentMapper.xml

@@ -46,6 +46,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="listByTempAndDay" resultType="com.fs.sop.domain.QwSopTempContent">
         SELECT  a.*  FROM qw_sop_temp_content a WHERE a.temp_id = #{tempId} and a.day_id = #{dayId} AND a.content_type = 7
     </select>
+    <select id="selectQwSopTempContentByTempId" resultType="com.fs.sop.domain.QwSopTempContent">
+        <include refid="selectQwSopTempContentVo"/>
+        where temp_id = #{tempId}
+    </select>
 
     <insert id="insertQwSopTempContent" parameterType="QwSopTempContent">
         insert into qw_sop_temp_content

+ 25 - 0
fs-service/src/main/resources/mapper/sop/QwSopTempVoiceMapper.xml

@@ -124,4 +124,29 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         select qw_user_id qwUserId,voice_url voiceUrl,voice_txt voiceTxt,user_voice_url userVoiceUrl,company_user_id companyUserId,
                record_type recordType,duration from qw_sop_temp_voice where company_user_id = #{companyUserId} and voice_txt = #{voiceTxt}
     </select>
+    <select id="selectQwSopTempVoiceByCompanyUserIdAndQwUserId" resultType="com.fs.sop.domain.QwSopTempVoice">
+        select id,company_user_id companyUserId,voice_url voiceUrl,voice_txt voiceTxt,duration
+        from qw_sop_temp_voice
+        where company_user_id = #{companyUserId} and qw_user_id = #{qwUserId}
+          and voice_url  is not null
+            limit 1
+    </select>
+    <select id="selectQwSopTempVoiceListLimit" resultType="com.fs.sop.domain.QwSopTempVoice">
+        select id,voice_txt voiceTxt,company_user_id companyUserId,voice_url voiceUrl,duration,record_type recordType,
+               user_voice_url userVoiceUrl from qw_sop_temp_voice
+        where record_type = #{recordType} and company_user_id is not null
+        order by date_format(create_time,'%y%m%d') desc,company_user_id desc limit 10
+    </select>
+    <select id="selectQwSopTempVoiceByIdAndUserVoiceUrl" resultType="com.fs.sop.domain.QwSopTempVoice">
+        select id,company_user_id companyUserId from qw_sop_temp_voice where id = #{id} and record_type in (0,1,2) limit 1
+    </select>
+    <select id="selectQwSopTempVoiceNewList" resultType="com.fs.sop.domain.QwSopTempVoice">
+        select id,voice_txt voiceTxt,company_user_id companyUserId,voice_url voiceUrl,duration,record_type recordType,
+        user_voice_url userVoiceUrl from qw_sop_temp_voice
+        <where>
+            <if test="companyUserId != null"> and company_user_id = #{companyUserId}</if>
+            <if test="recordType != null"> and record_type = #{recordType}</if>
+        </where>
+        order by update_time desc,id desc
+    </select>
 </mapper>

+ 148 - 0
fs-user-app/src/main/java/com/fs/app/controller/CompanyUserController.java

@@ -10,6 +10,7 @@ import com.fs.app.annotation.Login;
 import com.fs.app.param.FsBindCompanyUserParam;
 import com.fs.common.config.FSConfig;
 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.file.OssException;
@@ -24,9 +25,14 @@ import com.fs.company.param.companyUserAddPrintParam;
 import com.fs.company.service.ICompanyUserCardService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyUserUserService;
+import com.fs.fastGpt.domain.FastgptChatVoiceHomo;
+import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
 import com.fs.fastgptApi.util.AudioUtils;
+import com.fs.fastgptApi.vo.AudioVO;
 import com.fs.his.param.FsCouponReceiveParam;
 import com.fs.his.param.FsHealthTongueUParam;
+import com.fs.sop.domain.QwSopTempVoice;
+import com.fs.sop.service.IQwSopTempVoiceService;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 import io.swagger.annotations.Api;
@@ -38,6 +44,7 @@ import org.apache.http.entity.StringEntity;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.EntityUtils;
+import org.apache.ibatis.annotations.Param;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -47,6 +54,7 @@ import javax.servlet.http.HttpServletRequest;
 import java.io.*;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 
 @Api("销售接口")
@@ -63,6 +71,12 @@ public class CompanyUserController extends  AppBaseController {
     private CompanyUserMapper companyUserMapper;
     @Autowired
     private ICompanyUserUserService companyUserUserService;
+    @Autowired
+    private IQwSopTempVoiceService voiceService;
+    @Autowired
+    private FastgptChatVoiceHomoMapper fastgptChatVoiceHomoMapper;
+
+    public static final String SOP_TEMP_VOICE_KEY = "sop:tempVoice";
     @PostMapping("/login")
     public R Login(@RequestBody CompanyUserLoginParam param, HttpServletRequest request){
         try {
@@ -158,6 +172,7 @@ public class CompanyUserController extends  AppBaseController {
                 JSONObject jsonObject = JSON.parseObject(responseBody);
                 Integer code = (Integer)jsonObject.get("code");
                 if (code==200){
+                    voiceService.insertQwSopTempVoiceModel(userId);
                     return R.ok();
                 }
             } else {
@@ -196,4 +211,137 @@ public class CompanyUserController extends  AppBaseController {
         return companyUserService.getBindInfo(companyUserId);
 
     }
+
+    /**
+     * 当只有模板文字text时,生成表中对应条的voice_url和user_voice_url
+     * @param id            qw_sop_temp_voice的id
+     * @return
+     */
+    @GetMapping("/companyUserVoice")
+    public R companyUserVoice(@Param("id") Long id){
+        AudioVO audioVO = new AudioVO();
+        Long companyUserId = getCompanyUserId();
+        List<QwSopTempVoice> sopTempVoices = redisCache.getVoiceAllList(SOP_TEMP_VOICE_KEY + ":" + companyUserId);
+        if(sopTempVoices != null && !sopTempVoices.isEmpty()){
+            List<Long> collect = sopTempVoices.stream().map(QwSopTempVoice::getId).collect(Collectors.toList());
+            if (collect.contains(id)){
+                return R.ok().put("code",202).put("msg","该语音已进入转换,请完成后再试。");
+            }
+        }
+
+        if(companyUserId != null){
+            CompanyUser companyUser = companyUserMapper.selectCompanyUserByCompanyUserId(companyUserId);
+            if(companyUser != null && companyUser.getVoicePrintUrl() == null){
+                return R.ok().put("code",201).put("msg","账号未录制声纹,请录制后再试!");
+            }
+        }
+
+        QwSopTempVoice qwSopTempVoice = voiceService.selectQwSopTempVoiceById(id);
+        if(qwSopTempVoice != null && qwSopTempVoice.getCompanyUserId() != null){
+            List<FastgptChatVoiceHomo> homos = fastgptChatVoiceHomoMapper.selectFastgptChatVoiceHomoList(new FastgptChatVoiceHomo());
+            audioVO = AudioUtils.createUserUrlAndUrl(homos,qwSopTempVoice.getCompanyUserId(), qwSopTempVoice.getVoiceTxt().replace(" ",""));
+            if(audioVO != null && audioVO.getWavUrl() != null &&  audioVO.getUrl() != null){
+                qwSopTempVoice.setVoiceUrl(audioVO.getUrl());
+                qwSopTempVoice.setUserVoiceUrl(audioVO.getWavUrl());
+                qwSopTempVoice.setDuration(audioVO.getDuration());
+                qwSopTempVoice.setRecordType(1);
+                voiceService.updateQwSopTempVoice(qwSopTempVoice);
+            }
+        }
+        return R.ok().put("data", audioVO);
+    }
+
+    /**
+     * 当只有user_voice_url时,生成表中对应条的voice_url
+     * @param userVoiceUrl  wav格式的语音文件
+     * @param id            qw_sop_temp_voice的id
+     * @return
+     */
+    @GetMapping("/companyUserVoiceNew")
+    public R companyUserVoiceNew( @RequestParam("id") Long id,@RequestParam("userVoiceUrl") String userVoiceUrl){
+
+        AudioVO audioVO = new AudioVO();
+        Long companyUserId = getCompanyUserId();
+        List<QwSopTempVoice> sopTempVoices = redisCache.getVoiceAllList(SOP_TEMP_VOICE_KEY + ":" + companyUserId);
+        if(sopTempVoices != null && !sopTempVoices.isEmpty()){
+            List<Long> collect = sopTempVoices.stream().map(QwSopTempVoice::getId).collect(Collectors.toList());
+            if (collect.contains(id)){
+                return R.ok().put("code",202).put("msg","该语音已进入转换,请完成后再试。");
+            }
+        }
+
+        QwSopTempVoice qwSopTempVoice = voiceService.selectQwSopTempVoiceByIdAndUserVoiceUrl(id);
+        if(qwSopTempVoice != null && qwSopTempVoice.getId() != null){
+            audioVO = AudioUtils.createVoiceUrl(qwSopTempVoice.getCompanyUserId(), userVoiceUrl);
+            if(audioVO != null && audioVO.getUrl() != null){
+                qwSopTempVoice.setVoiceUrl(audioVO.getUrl());
+                qwSopTempVoice.setUserVoiceUrl(userVoiceUrl);
+                qwSopTempVoice.setDuration(audioVO.getDuration());
+                qwSopTempVoice.setRecordType(1);
+                voiceService.updateQwSopTempVoice(qwSopTempVoice);
+            }
+        }
+        return R.ok().put("data", audioVO);
+    }
+
+
+
+    @GetMapping("/query/{id}")
+    public R querySopVoiceById(@PathVariable("id") Long id){
+        QwSopTempVoice tempVoice = voiceService.selectQwSopTempVoiceById(id);
+        AudioVO audioVO = new AudioVO();
+        if(tempVoice != null){
+            audioVO.setId(tempVoice.getId());
+            audioVO.setVoiceTxt(tempVoice.getVoiceTxt());
+            audioVO.setUrl(tempVoice.getVoiceUrl());
+            audioVO.setWavUrl(tempVoice.getUserVoiceUrl());
+            audioVO.setDuration(tempVoice.getDuration());
+            audioVO.setRecordType(tempVoice.getRecordType());
+        }
+        return R.ok().put("data", audioVO);
+    }
+
+    @GetMapping("/querySopVoiceList")
+    public TableDataInfo querySopVoiceList(@Param("recordType") Integer recordType){
+        startPage();
+        QwSopTempVoice sopTempVoice = new QwSopTempVoice();
+        sopTempVoice.setRecordType(recordType);
+        sopTempVoice.setCompanyUserId(getCompanyUserId());
+        List<QwSopTempVoice> sopTempVoices = voiceService.selectQwSopTempVoiceNewList(sopTempVoice);
+        return getDataTable(sopTempVoices);
+    }
+
+    /**
+     * 一键转换
+     * @return
+     */
+    @GetMapping("/createUserAllVoice")
+    public R createUserAllVoice(){
+        QwSopTempVoice sopTempVoice = new QwSopTempVoice();
+        sopTempVoice.setRecordType(0);
+        Long companyUserId = getCompanyUserId();
+
+
+        if(companyUserId != null){
+            CompanyUser companyUser = companyUserMapper.selectCompanyUserByCompanyUserId(companyUserId);
+            if(companyUser != null && companyUser.getVoicePrintUrl() == null){
+                return R.ok().put("code",201).put("msg","账号未录制声纹,请录制后再试!");
+            }
+        }
+
+        sopTempVoice.setCompanyUserId(companyUserId);
+        List<QwSopTempVoice> sopTempVoices = voiceService.selectQwSopTempVoiceNewList(sopTempVoice);
+        if(sopTempVoices != null && !sopTempVoices.isEmpty()){
+            List<Long> newCompanyUserId = redisCache.getVoiceAllList(SOP_TEMP_VOICE_KEY);
+            if(newCompanyUserId != null && newCompanyUserId.contains(companyUserId)){
+                return R.error().put("code",202).put("msg","语音还未转换完成,请完成后再添加!");
+            }else{
+                redisCache.setVoice(SOP_TEMP_VOICE_KEY,companyUserId);
+                sopTempVoices.forEach(m -> m.setVoiceTxt(m.getVoiceTxt().replace(" ","")));
+                redisCache.setVoiceList(SOP_TEMP_VOICE_KEY + ":" + companyUserId, sopTempVoices);
+                return R.ok().put("msg","语音已加入队列进行转换,请耐心等待!");
+            }
+        }
+        return null;
+    }
 }