客户管理功能联调
This commit is contained in:
@@ -181,6 +181,10 @@ public class PasswordUtil {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -165,6 +165,10 @@ public class ServiceManager {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
package com.rj.config;
|
package com.rj.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 阿里云配置类
|
* 阿里云配置类
|
||||||
*
|
*
|
||||||
* @author Li Zhonghua
|
* @author rj
|
||||||
* @since 2025-08-22
|
* @date 2025-01-22
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties(prefix = "aliyun")
|
@ConfigurationProperties(prefix = "aliyun")
|
||||||
public class AliyunConfig {
|
public class AliyunConfig {
|
||||||
@@ -27,123 +29,37 @@ public class AliyunConfig {
|
|||||||
* 是否启用阿里云服务
|
* 是否启用阿里云服务
|
||||||
*/
|
*/
|
||||||
private boolean enabled = true;
|
private boolean enabled = true;
|
||||||
|
|
||||||
public String getApiKey() {
|
/**
|
||||||
return apiKey;
|
* API配置
|
||||||
|
*/
|
||||||
|
private ApiConfig api = new ApiConfig();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class ApiConfig {
|
||||||
|
/**
|
||||||
|
* 连接超时时间(毫秒)
|
||||||
|
*/
|
||||||
|
private int connectTimeout = 60000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取超时时间(毫秒)
|
||||||
|
*/
|
||||||
|
private int readTimeout = 300000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大重试次数
|
||||||
|
*/
|
||||||
|
private int maxRetries = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重试延迟时间(毫秒)
|
||||||
|
*/
|
||||||
|
private int retryDelay = 3000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL访问测试超时(毫秒)
|
||||||
|
*/
|
||||||
|
private int urlTestTimeout = 10000;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public void setApiKey(String apiKey) {
|
|
||||||
this.apiKey = apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRegion() {
|
|
||||||
return region;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRegion(String region) {
|
|
||||||
this.region = region;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -4,11 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.rj.common.DataEnrichmentUtil;
|
import com.rj.common.DataEnrichmentUtil;
|
||||||
import com.rj.entity.CustomerManagement;
|
import com.rj.entity.CustomerManagement;
|
||||||
import com.rj.entity.Dealership;
|
|
||||||
import com.rj.entity.SalesManagement;
|
|
||||||
import com.rj.service.ICustomerManagementService;
|
import com.rj.service.ICustomerManagementService;
|
||||||
import com.rj.service.IDealershipService;
|
|
||||||
import com.rj.service.ISalesManagementService;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@@ -17,11 +13,10 @@ import org.springframework.http.ResponseEntity;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -140,7 +135,15 @@ public class CustomerManagementController {
|
|||||||
@Parameter(description = "联系方式(模糊查询)")
|
@Parameter(description = "联系方式(模糊查询)")
|
||||||
@RequestParam(required = false) String contact,
|
@RequestParam(required = false) String contact,
|
||||||
@Parameter(description = "所属门店ID")
|
@Parameter(description = "所属门店ID")
|
||||||
@RequestParam(required = false) String dealershipId) {
|
@RequestParam(required = false) String dealershipId,
|
||||||
|
@Parameter(description = "创建开始时间", example = "2025-01-01 00:00:00")
|
||||||
|
@RequestParam(required = false) String createStartTime,
|
||||||
|
@Parameter(description = "创建结束时间", example = "2025-12-31 23:59:59")
|
||||||
|
@RequestParam(required = false) String createEndTime,
|
||||||
|
@Parameter(description = "修改开始时间", example = "2025-01-01 00:00:00")
|
||||||
|
@RequestParam(required = false) String updateStartTime,
|
||||||
|
@Parameter(description = "修改结束时间", example = "2025-12-31 23:59:59")
|
||||||
|
@RequestParam(required = false) String updateEndTime) {
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
try {
|
try {
|
||||||
Page<CustomerManagement> page = new Page<>(current, size);
|
Page<CustomerManagement> page = new Page<>(current, size);
|
||||||
@@ -160,6 +163,49 @@ public class CustomerManagementController {
|
|||||||
queryWrapper.eq(CustomerManagement::getDealershipId, dealershipId);
|
queryWrapper.eq(CustomerManagement::getDealershipId, dealershipId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加时间范围查询条件
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
if (createStartTime != null && !createStartTime.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
LocalDateTime startTime = LocalDateTime.parse(createStartTime, formatter);
|
||||||
|
queryWrapper.ge(CustomerManagement::getCreateTime, startTime);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "创建开始时间格式错误,请使用格式:yyyy-MM-dd HH:mm:ss");
|
||||||
|
return ResponseEntity.badRequest().body(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (createEndTime != null && !createEndTime.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
LocalDateTime endTime = LocalDateTime.parse(createEndTime, formatter);
|
||||||
|
queryWrapper.le(CustomerManagement::getCreateTime, endTime);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "创建结束时间格式错误,请使用格式:yyyy-MM-dd HH:mm:ss");
|
||||||
|
return ResponseEntity.badRequest().body(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updateStartTime != null && !updateStartTime.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
LocalDateTime startTime = LocalDateTime.parse(updateStartTime, formatter);
|
||||||
|
queryWrapper.ge(CustomerManagement::getUpdateTime, startTime);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "修改开始时间格式错误,请使用格式:yyyy-MM-dd HH:mm:ss");
|
||||||
|
return ResponseEntity.badRequest().body(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updateEndTime != null && !updateEndTime.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
LocalDateTime endTime = LocalDateTime.parse(updateEndTime, formatter);
|
||||||
|
queryWrapper.le(CustomerManagement::getUpdateTime, endTime);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "修改结束时间格式错误,请使用格式:yyyy-MM-dd HH:mm:ss");
|
||||||
|
return ResponseEntity.badRequest().body(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 按创建时间倒序排列
|
// 按创建时间倒序排列
|
||||||
queryWrapper.orderByDesc(CustomerManagement::getUpdateTime);
|
queryWrapper.orderByDesc(CustomerManagement::getUpdateTime);
|
||||||
|
|
||||||
|
|||||||
@@ -403,6 +403,10 @@ public class MenuController {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -373,6 +373,10 @@ public class RoleController {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -379,6 +379,10 @@ public class UserRoleController {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -145,6 +145,10 @@ public class DifyWorkflowResponseDto {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -77,5 +77,17 @@ public class CustomerManagement implements Serializable {
|
|||||||
@TableField("remark")
|
@TableField("remark")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
@Schema(description = "详细地址")
|
||||||
|
@TableField("detailed_address")
|
||||||
|
private String detailedAddress;
|
||||||
|
|
||||||
|
@Schema(description = "销售电话")
|
||||||
|
@TableField("sales_phone")
|
||||||
|
private String salesPhone;
|
||||||
|
|
||||||
|
@Schema(description = "联系客户次数")
|
||||||
|
@TableField("contact_count")
|
||||||
|
private Integer contactCount;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,10 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -144,6 +144,10 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.rj.entity.ImageModel;
|
|||||||
import com.rj.mapper.ImageModelMapper;
|
import com.rj.mapper.ImageModelMapper;
|
||||||
import com.rj.service.IImageModelService;
|
import com.rj.service.IImageModelService;
|
||||||
import com.rj.service.MinIOService;
|
import com.rj.service.MinIOService;
|
||||||
|
import com.rj.config.AliyunConfig;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -27,6 +28,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -59,6 +61,9 @@ public class ImageModelServiceImpl extends ServiceImpl<ImageModelMapper, ImageMo
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ObjectMapper objectMapper;
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AliyunConfig aliyunConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存图像模型记录
|
* 保存图像模型记录
|
||||||
@@ -870,7 +875,7 @@ public class ImageModelServiceImpl extends ServiceImpl<ImageModelMapper, ImageMo
|
|||||||
/**
|
/**
|
||||||
* 下载图片到字节数组
|
* 下载图片到字节数组
|
||||||
*
|
*
|
||||||
* 从指定URL下载图片并返回字节数组
|
* 从指定URL下载图片并返回字节数组,支持超时配置
|
||||||
*
|
*
|
||||||
* @param imageUrl 图片URL
|
* @param imageUrl 图片URL
|
||||||
* @return 图片字节数组
|
* @return 图片字节数组
|
||||||
@@ -881,19 +886,36 @@ public class ImageModelServiceImpl extends ServiceImpl<ImageModelMapper, ImageMo
|
|||||||
log.info("开始下载图片: {}", imageUrl);
|
log.info("开始下载图片: {}", imageUrl);
|
||||||
|
|
||||||
URL url = new URL(imageUrl);
|
URL url = new URL(imageUrl);
|
||||||
try (InputStream inputStream = url.openStream();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
|
||||||
|
// 设置请求头
|
||||||
byte[] buffer = new byte[4096];
|
connection.setRequestMethod("GET");
|
||||||
int bytesRead;
|
connection.setConnectTimeout(aliyunConfig.getApi().getConnectTimeout()); // 使用配置的连接超时
|
||||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
connection.setReadTimeout(aliyunConfig.getApi().getReadTimeout()); // 使用配置的读取超时
|
||||||
outputStream.write(buffer, 0, bytesRead);
|
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
||||||
|
connection.setRequestProperty("Accept", "image/*");
|
||||||
|
connection.setRequestProperty("Accept-Encoding", "identity"); // 禁用压缩
|
||||||
|
|
||||||
|
int responseCode = connection.getResponseCode();
|
||||||
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
|
try (InputStream inputStream = connection.getInputStream();
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
|
||||||
|
byte[] buffer = new byte[8192]; // 增大缓冲区
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] imageBytes = outputStream.toByteArray();
|
||||||
|
log.info("图片下载完成,大小: {} bytes", imageBytes.length);
|
||||||
|
return imageBytes;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
byte[] imageBytes = outputStream.toByteArray();
|
log.error("下载图片失败,HTTP状态码: {}, URL: {}", responseCode, imageUrl);
|
||||||
log.info("图片下载完成,大小: {} bytes", imageBytes.length);
|
throw new IOException("HTTP error code: " + responseCode);
|
||||||
return imageBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("下载图片失败: {}", imageUrl, e);
|
log.error("下载图片失败: {}", imageUrl, e);
|
||||||
throw new IOException("下载图片失败: " + e.getMessage(), e);
|
throw new IOException("下载图片失败: " + e.getMessage(), e);
|
||||||
@@ -1128,20 +1150,46 @@ public class ImageModelServiceImpl extends ServiceImpl<ImageModelMapper, ImageMo
|
|||||||
|
|
||||||
// 2. 上传文件到MinIO并生成临时访问URL
|
// 2. 上传文件到MinIO并生成临时访问URL
|
||||||
log.info("开始上传文件到MinIO,文件名: {}", multipartFile.getOriginalFilename());
|
log.info("开始上传文件到MinIO,文件名: {}", multipartFile.getOriginalFilename());
|
||||||
|
log.info("文件大小: {} bytes", multipartFile.getSize());
|
||||||
|
log.info("文件类型: {}", multipartFile.getContentType());
|
||||||
|
|
||||||
// 生成唯一文件名
|
// 生成唯一文件名
|
||||||
String originalFilename = multipartFile.getOriginalFilename();
|
String originalFilename = multipartFile.getOriginalFilename();
|
||||||
String fileExtension = getFileExtension(originalFilename);
|
String fileExtension = getFileExtension(originalFilename);
|
||||||
String uniqueFileName = generateUniqueFileName(fileExtension);
|
String uniqueFileName = generateUniqueFileName(fileExtension);
|
||||||
|
|
||||||
|
log.info("生成的文件名: {}", uniqueFileName);
|
||||||
|
log.info("文件扩展名: {}", fileExtension);
|
||||||
|
|
||||||
// 上传到MinIO
|
// 上传到MinIO
|
||||||
|
long uploadStartTime = System.currentTimeMillis();
|
||||||
String materialUrl = minIOService.uploadFileWithName(multipartFile, uniqueFileName);
|
String materialUrl = minIOService.uploadFileWithName(multipartFile, uniqueFileName);
|
||||||
|
long uploadEndTime = System.currentTimeMillis();
|
||||||
|
log.info("MinIO上传耗时: {} ms", uploadEndTime - uploadStartTime);
|
||||||
|
|
||||||
String materialTempUrl = minIOService.generateTempUrl(uniqueFileName);
|
String materialTempUrl = minIOService.generateTempUrl(uniqueFileName);
|
||||||
|
|
||||||
log.info("文件上传到MinIO成功:");
|
log.info("文件上传到MinIO成功:");
|
||||||
log.info("永久URL: {}", materialUrl);
|
log.info("永久URL: {}", materialUrl);
|
||||||
log.info("临时访问URL: {}", materialTempUrl);
|
log.info("临时访问URL: {}", materialTempUrl);
|
||||||
|
|
||||||
|
// 验证临时URL是否可访问
|
||||||
|
log.info("开始验证临时URL可访问性...");
|
||||||
|
try {
|
||||||
|
URL testUrl = new URL(materialTempUrl);
|
||||||
|
HttpURLConnection testConnection = (HttpURLConnection) testUrl.openConnection();
|
||||||
|
testConnection.setRequestMethod("HEAD");
|
||||||
|
testConnection.setConnectTimeout(10000);
|
||||||
|
testConnection.setReadTimeout(10000);
|
||||||
|
int testResponseCode = testConnection.getResponseCode();
|
||||||
|
log.info("临时URL访问测试结果: HTTP {}", testResponseCode);
|
||||||
|
if (testResponseCode != 200) {
|
||||||
|
log.warn("临时URL可能无法访问,这可能导致阿里云API超时");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("临时URL访问测试失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
// 输出到控制台
|
// 输出到控制台
|
||||||
System.out.println("=== 图生图文件上传信息 ===");
|
System.out.println("=== 图生图文件上传信息 ===");
|
||||||
System.out.println("原始文件名: " + originalFilename);
|
System.out.println("原始文件名: " + originalFilename);
|
||||||
@@ -1177,11 +1225,38 @@ public class ImageModelServiceImpl extends ServiceImpl<ImageModelMapper, ImageMo
|
|||||||
// 5. 使用MultiModalConversation调用阿里云API
|
// 5. 使用MultiModalConversation调用阿里云API
|
||||||
MultiModalConversation conv = new MultiModalConversation();
|
MultiModalConversation conv = new MultiModalConversation();
|
||||||
|
|
||||||
// 构建多模态消息
|
// 构建多模态消息 - 智能选择图片传递方式
|
||||||
|
Object imageContent;
|
||||||
|
|
||||||
|
// 首先尝试使用临时URL
|
||||||
|
if (isUrlAccessible(materialTempUrl)) {
|
||||||
|
log.info("使用临时URL传递图片: {}", materialTempUrl);
|
||||||
|
imageContent = materialTempUrl;
|
||||||
|
}
|
||||||
|
// 如果临时URL不可访问,尝试永久URL
|
||||||
|
else if (isUrlAccessible(materialUrl)) {
|
||||||
|
log.warn("临时URL不可访问,使用永久URL: {}", materialUrl);
|
||||||
|
imageContent = materialUrl;
|
||||||
|
}
|
||||||
|
// 如果URL都不可访问,使用base64编码
|
||||||
|
else {
|
||||||
|
log.warn("所有URL都不可访问,尝试使用base64编码传递图片");
|
||||||
|
try {
|
||||||
|
byte[] imageBytes = multipartFile.getBytes();
|
||||||
|
String base64Image = "data:image/" + getFileExtension(multipartFile.getOriginalFilename()) + ";base64," +
|
||||||
|
java.util.Base64.getEncoder().encodeToString(imageBytes);
|
||||||
|
imageContent = base64Image;
|
||||||
|
log.info("使用base64编码传递图片,大小: {} bytes", imageBytes.length);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("转换为base64失败,回退到使用临时URL: {}", e.getMessage());
|
||||||
|
imageContent = materialTempUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MultiModalMessage userMessage = MultiModalMessage.builder()
|
MultiModalMessage userMessage = MultiModalMessage.builder()
|
||||||
.role(Role.USER.getValue())
|
.role(Role.USER.getValue())
|
||||||
.content(Arrays.asList(
|
.content(Arrays.asList(
|
||||||
Collections.singletonMap("image", materialTempUrl), // 使用临时访问URL
|
Collections.singletonMap("image", imageContent),
|
||||||
Collections.singletonMap("text", prompt)
|
Collections.singletonMap("text", prompt)
|
||||||
)).build();
|
)).build();
|
||||||
|
|
||||||
@@ -1219,9 +1294,57 @@ public class ImageModelServiceImpl extends ServiceImpl<ImageModelMapper, ImageMo
|
|||||||
System.out.println("参数: " + JsonUtils.toJson(parametersMap));
|
System.out.println("参数: " + JsonUtils.toJson(parametersMap));
|
||||||
System.out.println("=========================");
|
System.out.println("=========================");
|
||||||
|
|
||||||
// 6. 调用阿里云API
|
// 6. 调用阿里云API(带重试机制)
|
||||||
log.info("开始调用阿里云图生图API");
|
log.info("开始调用阿里云图生图API");
|
||||||
MultiModalConversationResult conversationResult = conv.call(param);
|
log.info("API配置 - 连接超时: {} ms, 读取超时: {} ms",
|
||||||
|
aliyunConfig.getApi().getConnectTimeout(),
|
||||||
|
aliyunConfig.getApi().getReadTimeout());
|
||||||
|
log.info("重试配置 - 最大重试次数: {}, 重试延迟: {} ms",
|
||||||
|
aliyunConfig.getApi().getMaxRetries(),
|
||||||
|
aliyunConfig.getApi().getRetryDelay());
|
||||||
|
|
||||||
|
MultiModalConversationResult conversationResult = null;
|
||||||
|
int maxRetries = aliyunConfig.getApi().getMaxRetries();
|
||||||
|
int retryCount = 0;
|
||||||
|
|
||||||
|
while (retryCount < maxRetries) {
|
||||||
|
try {
|
||||||
|
log.info("第{}次尝试调用阿里云API...", retryCount + 1);
|
||||||
|
long apiStartTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
conversationResult = conv.call(param);
|
||||||
|
|
||||||
|
long apiEndTime = System.currentTimeMillis();
|
||||||
|
log.info("阿里云API调用成功,耗时: {} ms", apiEndTime - apiStartTime);
|
||||||
|
break; // 成功则跳出循环
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
retryCount++;
|
||||||
|
log.error("阿里云API调用失败,第{}次尝试,错误详情:", retryCount);
|
||||||
|
log.error("错误代码: {}", e.getMessage());
|
||||||
|
log.error("请求ID: {}", extractRequestId(e.getMessage()));
|
||||||
|
|
||||||
|
if (e.getMessage().contains("DataInspection") || e.getMessage().contains("timeout")) {
|
||||||
|
if (retryCount < maxRetries) {
|
||||||
|
long delayTime = aliyunConfig.getApi().getRetryDelay() * retryCount;
|
||||||
|
log.warn("检测到超时错误,{}ms后进行第{}次重试", delayTime, retryCount + 1);
|
||||||
|
try {
|
||||||
|
Thread.sleep(delayTime);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new RuntimeException("重试被中断", ie);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.error("阿里云API调用失败,已达到最大重试次数: {}", maxRetries);
|
||||||
|
log.error("最终失败原因: {}", e.getMessage());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.error("非超时错误,直接抛出异常: {}", e.getMessage());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 输出完整响应到控制台
|
// 输出完整响应到控制台
|
||||||
System.out.println("=== 阿里云API完整响应 ===");
|
System.out.println("=== 阿里云API完整响应 ===");
|
||||||
@@ -1358,4 +1481,49 @@ public class ImageModelServiceImpl extends ServiceImpl<ImageModelMapper, ImageMo
|
|||||||
return "req_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
|
return "req_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从错误消息中提取请求ID
|
||||||
|
*
|
||||||
|
* @param errorMessage 错误消息
|
||||||
|
* @return 请求ID,如果未找到则返回"未知"
|
||||||
|
*/
|
||||||
|
private String extractRequestId(String errorMessage) {
|
||||||
|
try {
|
||||||
|
if (errorMessage != null && errorMessage.contains("requestId")) {
|
||||||
|
int startIndex = errorMessage.indexOf("\"requestId\":\"") + 13;
|
||||||
|
int endIndex = errorMessage.indexOf("\"", startIndex);
|
||||||
|
if (startIndex > 12 && endIndex > startIndex) {
|
||||||
|
return errorMessage.substring(startIndex, endIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("提取请求ID失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return "未知";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查URL是否可访问
|
||||||
|
*
|
||||||
|
* @param urlString URL字符串
|
||||||
|
* @return 如果URL可访问返回true,否则返回false
|
||||||
|
*/
|
||||||
|
private boolean isUrlAccessible(String urlString) {
|
||||||
|
try {
|
||||||
|
URL url = new URL(urlString);
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setRequestMethod("HEAD");
|
||||||
|
connection.setConnectTimeout(5000); // 5秒连接超时
|
||||||
|
connection.setReadTimeout(5000); // 5秒读取超时
|
||||||
|
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
||||||
|
|
||||||
|
int responseCode = connection.getResponseCode();
|
||||||
|
log.info("URL访问测试: {} -> HTTP {}", urlString, responseCode);
|
||||||
|
return responseCode == 200;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("URL访问测试失败: {} -> {}", urlString, e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -125,6 +125,10 @@ spring:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,18 @@ aliyun:
|
|||||||
region: cn-beijing
|
region: cn-beijing
|
||||||
# 是否启用阿里云服务
|
# 是否启用阿里云服务
|
||||||
enabled: true
|
enabled: true
|
||||||
|
# API超时配置
|
||||||
|
api:
|
||||||
|
# 连接超时时间(毫秒)
|
||||||
|
connect-timeout: 60000
|
||||||
|
# 读取超时时间(毫秒)
|
||||||
|
read-timeout: 300000
|
||||||
|
# 最大重试次数
|
||||||
|
max-retries: 5
|
||||||
|
# 重试延迟时间(毫秒)
|
||||||
|
retry-delay: 3000
|
||||||
|
# URL访问测试超时(毫秒)
|
||||||
|
url-test-timeout: 10000
|
||||||
|
|
||||||
# ASR语音识别配置
|
# ASR语音识别配置
|
||||||
asr:
|
asr:
|
||||||
|
|||||||
@@ -102,6 +102,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,16 @@
|
|||||||
<result column="intended_model" property="intendedModel" />
|
<result column="intended_model" property="intendedModel" />
|
||||||
<result column="info_card" property="infoCard" />
|
<result column="info_card" property="infoCard" />
|
||||||
<result column="remark" property="remark" />
|
<result column="remark" property="remark" />
|
||||||
|
<result column="detailed_address" property="detailedAddress" />
|
||||||
|
<result column="sales_phone" property="salesPhone" />
|
||||||
|
<result column="contact_count" property="contactCount" />
|
||||||
<result column="create_time" property="createTime" />
|
<result column="create_time" property="createTime" />
|
||||||
<result column="update_time" property="updateTime" />
|
<result column="update_time" property="updateTime" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- 通用查询结果列 -->
|
<!-- 通用查询结果列 -->
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
id, customer_name, contact, dealership_id, dealership_name, sales_id, sales_name, recording_count, intended_model, info_card, remark, create_time, update_time
|
id, customer_name, contact, dealership_id, dealership_name, sales_id, sales_name, recording_count, intended_model, info_card, remark, detailed_address, sales_phone, contact_count, create_time, update_time
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -301,6 +301,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
30
src/main/sql/communication_record.sql
Normal file
30
src/main/sql/communication_record.sql
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
-- 沟通记录表建表语句
|
||||||
|
-- 根据沟通记录详情页面字段创建
|
||||||
|
|
||||||
|
CREATE TABLE `communication_record` (
|
||||||
|
`id` varchar(36) NOT NULL COMMENT '主键ID(UUID)',
|
||||||
|
`customer_name` varchar(100) NOT NULL COMMENT '客户姓名',
|
||||||
|
`customer_phone` varchar(50) NOT NULL COMMENT '客户电话',
|
||||||
|
`customer_id` varchar(36) NOT NULL COMMENT '客户ID',
|
||||||
|
`communication_type` varchar(50) NOT NULL COMMENT '沟通类型',
|
||||||
|
`communication_time` datetime NOT NULL COMMENT '沟通时间',
|
||||||
|
`communication_content` text NOT NULL COMMENT '沟通内容',
|
||||||
|
`communication_result` text COMMENT '沟通结果',
|
||||||
|
`owner_name` varchar(100) NOT NULL COMMENT '所属人姓名',
|
||||||
|
`owner_phone` varchar(50) NOT NULL COMMENT '所属人电话',
|
||||||
|
`todo_suggestion` text COMMENT '待办建议',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态:1-有效,0-删除',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_customer_name` (`customer_name`),
|
||||||
|
KEY `idx_customer_phone` (`customer_phone`),
|
||||||
|
KEY `idx_customer_id` (`customer_id`),
|
||||||
|
KEY `idx_communication_time` (`communication_time`),
|
||||||
|
KEY `idx_owner_name` (`owner_name`),
|
||||||
|
KEY `idx_create_time` (`create_time`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='沟通记录表';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -33,6 +33,9 @@ CREATE TABLE `customer_management` (
|
|||||||
`intended_model` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '意向车型',
|
`intended_model` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '意向车型',
|
||||||
`info_card` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '信息卡',
|
`info_card` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '信息卡',
|
||||||
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',
|
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',
|
||||||
|
`detailed_address` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '详细地址',
|
||||||
|
`sales_phone` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '销售电话',
|
||||||
|
`contact_count` int NULL DEFAULT 0 COMMENT '联系客户次数',
|
||||||
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
|||||||
@@ -239,6 +239,10 @@ public class FaceDetectImageCountTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -180,6 +180,10 @@ public class TtsRequestLogShortUrlTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,10 @@ public class VideoSynthesisTempUrlTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,10 @@ public class VideoSynthesisVideoNameTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user