18 changed files with 702 additions and 9 deletions
@ -0,0 +1,64 @@ |
|||||||
|
package com.cloud.kicc.system.api.entity; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||||
|
import com.cloud.kicc.common.data.entity.CommonEntity; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.EqualsAndHashCode; |
||||||
|
import lombok.experimental.Accessors; |
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank; |
||||||
|
import java.time.LocalDateTime; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* IM聊天内容 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author wangxiang4 |
||||||
|
* @since 2023-11-15 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = false) |
||||||
|
@Accessors(chain = true) |
||||||
|
@TableName(value = "sys_im_content", excludeProperty = { "createById", "createByName", "createTime", "updateById", "updateByName", "updateTime", "remarks" }) |
||||||
|
@ApiModel(value = "ImContent对象", description = "IM聊天内容") |
||||||
|
public class ImContent extends CommonEntity { |
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
@ApiModelProperty("主键") |
||||||
|
private String id; |
||||||
|
|
||||||
|
@ApiModelProperty("发送者") |
||||||
|
@NotBlank(message = "发送者ID不能为空") |
||||||
|
private String sendUserId; |
||||||
|
|
||||||
|
@ApiModelProperty("发送时间") |
||||||
|
private LocalDateTime sendTime; |
||||||
|
|
||||||
|
@ApiModelProperty("接收者") |
||||||
|
@NotBlank(message = "接收者ID不能为空") |
||||||
|
private String receiveUserId; |
||||||
|
|
||||||
|
@ApiModelProperty("接收时间") |
||||||
|
private LocalDateTime receiveTime; |
||||||
|
|
||||||
|
@ApiModelProperty("内容") |
||||||
|
private String content; |
||||||
|
|
||||||
|
@ApiModelProperty("内容类型") |
||||||
|
@NotBlank(message = "发送内容类型不能为空") |
||||||
|
private String contentType; |
||||||
|
|
||||||
|
@ApiModelProperty("状态") |
||||||
|
private String status; |
||||||
|
|
||||||
|
@ApiModelProperty("附件") |
||||||
|
private String files; |
||||||
|
|
||||||
|
@ApiModelProperty("撤销标记") |
||||||
|
private String revert; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package com.cloud.kicc.system.api.enums; |
||||||
|
|
||||||
|
import lombok.Getter; |
||||||
|
import lombok.RequiredArgsConstructor; |
||||||
|
|
||||||
|
/** |
||||||
|
*<p> |
||||||
|
* IM消息类型 |
||||||
|
*</p> |
||||||
|
* |
||||||
|
* @Author: wangxiang4 |
||||||
|
* @Since: 2023/11/15 |
||||||
|
*/ |
||||||
|
@Getter |
||||||
|
@RequiredArgsConstructor |
||||||
|
public enum ImMessageTypeEnum { |
||||||
|
|
||||||
|
|
||||||
|
TEXT("0", "文本"), |
||||||
|
|
||||||
|
AUDIO("1", "音频"); |
||||||
|
|
||||||
|
|
||||||
|
private final String value; |
||||||
|
|
||||||
|
|
||||||
|
private final String description; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
package com.cloud.kicc.system.config; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
*<p> |
||||||
|
* ai 配置 |
||||||
|
*</p> |
||||||
|
* |
||||||
|
* @Author: wangxiang4 |
||||||
|
* @Since: 2023/11/15 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@RefreshScope |
||||||
|
@Configuration |
||||||
|
@ConfigurationProperties("openai.api") |
||||||
|
public class OpenAiConfigProperties { |
||||||
|
|
||||||
|
/** |
||||||
|
* change this to an `accessToken` extracted from the ChatGPT site's |
||||||
|
* `<a href="https://chat.openai.com/api/auth/session">...</a>` response |
||||||
|
*/ |
||||||
|
private String token; |
||||||
|
|
||||||
|
/** OpenAI API Base URL - <a href="https://api.openai.com">...</a> */ |
||||||
|
private String baseUrl; |
||||||
|
|
||||||
|
/** OpenAI API Model - <a href="https://platform.openai.com/docs/models">...</a> */ |
||||||
|
private String model; |
||||||
|
|
||||||
|
/** timeout time unit seconds */ |
||||||
|
private int timeout = 10; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
package com.cloud.kicc.system.controller; |
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil; |
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import com.cloud.kicc.common.core.api.R; |
||||||
|
import com.cloud.kicc.common.core.constant.AppConstants; |
||||||
|
import com.cloud.kicc.common.core.exception.CheckedException; |
||||||
|
import com.cloud.kicc.common.security.annotation.Inner; |
||||||
|
import com.cloud.kicc.common.security.util.SecurityUtils; |
||||||
|
import com.cloud.kicc.system.api.entity.ImContent; |
||||||
|
import com.cloud.kicc.system.service.IImContentService; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
import org.springframework.web.multipart.MultipartFile; |
||||||
|
|
||||||
|
import javax.validation.Valid; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
*<p> |
||||||
|
* ChatGpt APi Support |
||||||
|
*</p> |
||||||
|
* |
||||||
|
* @Author: wangxiang4 |
||||||
|
* @Since: 2023/11/15 |
||||||
|
*/ |
||||||
|
@RestController |
||||||
|
@AllArgsConstructor |
||||||
|
@RequestMapping(AppConstants.APP_SYSTEM + "/ai") |
||||||
|
public class ChatGptController { |
||||||
|
|
||||||
|
private final IImContentService iImContentService; |
||||||
|
|
||||||
|
@Inner(false) |
||||||
|
@GetMapping("/chat/message") |
||||||
|
public R list(Page page, ImContent imContent) { |
||||||
|
if (StrUtil.isBlank(imContent.getSendUserId()) || StrUtil.isBlank(imContent.getReceiveUserId())) |
||||||
|
throw new CheckedException("当前发送者ID与接收者ID必填!"); |
||||||
|
// 查询一个会话内的聊天记录
|
||||||
|
SecurityUtils.openInterfaceTemporaryLoginSession(imContent.getSendUserId()); |
||||||
|
IPage<Map<String, Object>> result = iImContentService.listHistoryMessage(page, imContent); |
||||||
|
return R.ok(result.getRecords(), result.getTotal()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Inner(false) |
||||||
|
@PostMapping("/chat/completions") |
||||||
|
public R completions(@Valid @ModelAttribute ImContent imContent, @RequestPart(value = "file", required = false) MultipartFile file) { |
||||||
|
return R.ok(iImContentService.completions(imContent, file)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.cloud.kicc.system.mapper; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import com.cloud.kicc.system.api.entity.ImContent; |
||||||
|
import org.apache.ibatis.annotations.Param; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* IM聊天内容 Mapper 接口 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author wangxiang4 |
||||||
|
* @since 2023-11-15 |
||||||
|
*/ |
||||||
|
public interface ImContentMapper extends BaseMapper<ImContent> { |
||||||
|
|
||||||
|
IPage<Map<String, Object>> listHistoryMessage(Page page, @Param("ew") ImContent imContent); |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.cloud.kicc.system.service; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||||
|
import com.cloud.kicc.system.api.entity.ImContent; |
||||||
|
import org.springframework.web.multipart.MultipartFile; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* IM聊天内容 服务类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author wangxiang4 |
||||||
|
* @since 2023-11-15 |
||||||
|
*/ |
||||||
|
public interface IImContentService extends IService<ImContent> { |
||||||
|
|
||||||
|
IPage<Map<String, Object>> listHistoryMessage(Page page, ImContent imContent); |
||||||
|
|
||||||
|
ImContent completions(ImContent imContent, MultipartFile file); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,195 @@ |
|||||||
|
package com.cloud.kicc.system.service.impl; |
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil; |
||||||
|
import cn.hutool.core.util.IdUtil; |
||||||
|
import cn.hutool.core.util.StrUtil; |
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||||
|
import com.cloud.kicc.common.core.exception.CheckedException; |
||||||
|
import com.cloud.kicc.common.security.util.SecurityUtils; |
||||||
|
import com.cloud.kicc.system.api.entity.ImContent; |
||||||
|
import com.cloud.kicc.system.api.enums.ImMessageTypeEnum; |
||||||
|
import com.cloud.kicc.system.mapper.ImContentMapper; |
||||||
|
import com.cloud.kicc.system.service.FileService; |
||||||
|
import com.cloud.kicc.system.service.IImContentService; |
||||||
|
import com.cloud.kicc.system.util.AiUtil; |
||||||
|
import com.pig4cloud.plugin.oss.OssProperties; |
||||||
|
import com.pig4cloud.plugin.oss.service.OssTemplate; |
||||||
|
import com.theokanning.openai.audio.CreateSpeechRequest; |
||||||
|
import com.theokanning.openai.audio.CreateTranscriptionRequest; |
||||||
|
import com.theokanning.openai.audio.TranscriptionResult; |
||||||
|
import com.theokanning.openai.completion.chat.ChatCompletionChoice; |
||||||
|
import com.theokanning.openai.completion.chat.ChatCompletionResult; |
||||||
|
import com.theokanning.openai.completion.chat.ChatMessage; |
||||||
|
import com.theokanning.openai.completion.chat.ChatMessageRole; |
||||||
|
import com.theokanning.openai.service.OpenAiService; |
||||||
|
import lombok.RequiredArgsConstructor; |
||||||
|
import lombok.SneakyThrows; |
||||||
|
import okhttp3.ResponseBody; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
import org.springframework.transaction.annotation.Transactional; |
||||||
|
import org.springframework.web.multipart.MultipartFile; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.time.LocalDateTime; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.Optional; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* IM聊天内容 服务实现类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author wangxiang4 |
||||||
|
* @since 2023-11-15 |
||||||
|
*/ |
||||||
|
@Service |
||||||
|
@RequiredArgsConstructor |
||||||
|
public class ImContentServiceImpl extends ServiceImpl<ImContentMapper, ImContent> implements IImContentService { |
||||||
|
|
||||||
|
private final FileService fileService; |
||||||
|
|
||||||
|
private final OssProperties ossProperties; |
||||||
|
|
||||||
|
private final OssTemplate ossTemplate; |
||||||
|
|
||||||
|
@Override |
||||||
|
public IPage<Map<String, Object>> listHistoryMessage(Page page, ImContent imContent) { |
||||||
|
return baseMapper.listHistoryMessage(page, imContent); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@Transactional(rollbackFor = Exception.class) |
||||||
|
@SneakyThrows |
||||||
|
public ImContent completions(ImContent imContent, MultipartFile file) { |
||||||
|
SecurityUtils.openInterfaceTemporaryLoginSession(imContent.getSendUserId()); |
||||||
|
if (imContent.getContentType().equals(ImMessageTypeEnum.AUDIO.getValue())) { |
||||||
|
Optional.ofNullable(file).orElseThrow(() -> new CheckedException("当前语音文件为空,请检查后重试!")); |
||||||
|
OpenAiService openAiService = AiUtil.getOpenAiService(); |
||||||
|
String speechName = "speech.mp3"; |
||||||
|
String sendFileName = IdUtil.simpleUUID() + StrUtil.DOT + FileUtil.extName(file.getOriginalFilename()); |
||||||
|
String receiveFileName = IdUtil.simpleUUID() + StrUtil.DOT + FileUtil.extName(speechName); |
||||||
|
|
||||||
|
// 构建发送文件信息进行OSS存储
|
||||||
|
com.cloud.kicc.system.api.entity.File sendOssFile = new com.cloud.kicc.system.api.entity.File() |
||||||
|
.setFileName(sendFileName) |
||||||
|
.setBucketName(ossProperties.getBucketName()) |
||||||
|
.setOriginal(file.getOriginalFilename()) |
||||||
|
.setType(FileUtil.extName(file.getOriginalFilename())) |
||||||
|
.setFileSize(file.getSize()) |
||||||
|
.setMimeType(file.getContentType()); |
||||||
|
ossTemplate.putObject(ossProperties.getBucketName(), sendFileName, file.getContentType(), file.getInputStream()); |
||||||
|
String sendVoiceUrl = ossTemplate.getObjectURL(ossProperties.getBucketName(), sendFileName); |
||||||
|
sendOssFile.setAvailablePath(sendVoiceUrl); |
||||||
|
fileService.save(sendOssFile); |
||||||
|
imContent.setFiles(sendOssFile.getId()); |
||||||
|
|
||||||
|
// 语音转文字
|
||||||
|
File voiceFile = File.createTempFile(sendFileName, null); |
||||||
|
CreateTranscriptionRequest request = CreateTranscriptionRequest.builder() |
||||||
|
.model("whisper-1") |
||||||
|
.language("zh") |
||||||
|
.build(); |
||||||
|
FileUtil.writeBytes(file.getBytes(), voiceFile); |
||||||
|
TranscriptionResult transcriptionResult = openAiService.createTranscription(request, voiceFile); |
||||||
|
if (voiceFile.delete()) { |
||||||
|
System.out.println("已成功删除临时文件!"); |
||||||
|
} |
||||||
|
if (StrUtil.isBlank(transcriptionResult.getText())) { |
||||||
|
throw new CheckedException("Ai语音转文字为空解析失败!"); |
||||||
|
} |
||||||
|
|
||||||
|
imContent.setContent(transcriptionResult.getText()); |
||||||
|
ImContent content = askChatCompletion(imContent); |
||||||
|
content.setContentType(ImMessageTypeEnum.AUDIO.getValue()); |
||||||
|
|
||||||
|
// 文字转语音
|
||||||
|
CreateSpeechRequest createSpeechRequest = CreateSpeechRequest.builder() |
||||||
|
.model("tts-1") |
||||||
|
.input(content.getContent()) |
||||||
|
.voice("nova") |
||||||
|
.responseFormat(FileUtil.extName(speechName)) |
||||||
|
.speed(1.0) |
||||||
|
.build(); |
||||||
|
ResponseBody responseBody = openAiService.createSpeech(createSpeechRequest); |
||||||
|
|
||||||
|
// 构建发送文件信息进行OSS存储
|
||||||
|
com.cloud.kicc.system.api.entity.File receiveOssFile = new com.cloud.kicc.system.api.entity.File() |
||||||
|
.setFileName(receiveFileName) |
||||||
|
.setBucketName(ossProperties.getBucketName()) |
||||||
|
.setOriginal(speechName) |
||||||
|
.setType(FileUtil.extName(speechName)) |
||||||
|
.setFileSize(responseBody.contentLength()) |
||||||
|
.setMimeType(Objects.requireNonNull(responseBody.contentType()).toString()); |
||||||
|
ossTemplate.putObject(ossProperties.getBucketName(), receiveFileName, Objects.requireNonNull(responseBody.contentType()).toString(), responseBody.byteStream()); |
||||||
|
String receiveVoiceUrl = ossTemplate.getObjectURL(ossProperties.getBucketName(), receiveFileName); |
||||||
|
receiveOssFile.setAvailablePath(receiveVoiceUrl); |
||||||
|
fileService.save(receiveOssFile); |
||||||
|
|
||||||
|
// 保存AI回复聊天记录
|
||||||
|
imContent.setFiles(receiveOssFile.getId()); |
||||||
|
super.save(content); |
||||||
|
return content; |
||||||
|
} else { |
||||||
|
// 保存AI回复聊天记录
|
||||||
|
ImContent content = askChatCompletion(imContent); |
||||||
|
super.save(content); |
||||||
|
return content; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private ImContent askChatCompletion(ImContent imContent) { |
||||||
|
if (StrUtil.isBlank(imContent.getContent())) { |
||||||
|
throw new CheckedException("提问内容不能为空请检查!"); |
||||||
|
} |
||||||
|
// 保存用户发送聊天记录
|
||||||
|
imContent.setSendUserId(SecurityUtils.getCasUser().getId()); |
||||||
|
imContent.setSendTime(LocalDateTime.now()); |
||||||
|
imContent.setReceiveUserId(imContent.getReceiveUserId()); |
||||||
|
imContent.setReceiveTime(LocalDateTime.now()); |
||||||
|
super.save(imContent); |
||||||
|
|
||||||
|
// 查询当前历史消息,进行AI上下文消息合并
|
||||||
|
List<ImContent> imContents = super.list(Wrappers.<ImContent>lambdaQuery().and(i -> |
||||||
|
i.eq(ImContent::getSendUserId, imContent.getSendUserId()) |
||||||
|
.eq(ImContent::getReceiveUserId, imContent.getReceiveUserId()) |
||||||
|
).or(i -> |
||||||
|
i.eq(ImContent::getSendUserId, imContent.getReceiveUserId()) |
||||||
|
.eq(ImContent::getReceiveUserId, imContent.getSendUserId()) |
||||||
|
) .orderByAsc(ImContent::getSendTime)); |
||||||
|
|
||||||
|
// 历史上下文消息
|
||||||
|
List<ChatMessage> historyMessages = imContents.stream().map(item -> { |
||||||
|
ChatMessage chatMessage = new ChatMessage(); |
||||||
|
chatMessage.setRole(StrUtil.equals(item.getSendUserId(), imContent.getSendUserId()) ? ChatMessageRole.USER.value() : ChatMessageRole.ASSISTANT.value()); |
||||||
|
chatMessage.setContent(item.getContent()); |
||||||
|
return chatMessage; |
||||||
|
}).collect(Collectors.toList()); |
||||||
|
|
||||||
|
// 提问消息
|
||||||
|
ChatMessage askMessage = new ChatMessage(ChatMessageRole.USER.value(), imContent.getContent()); |
||||||
|
historyMessages.add(askMessage); |
||||||
|
|
||||||
|
ChatCompletionResult chatCompletionResult = AiUtil.getChatCompletion(historyMessages); |
||||||
|
List<ChatCompletionChoice> choiceList = chatCompletionResult.getChoices(); |
||||||
|
|
||||||
|
if (choiceList.isEmpty()) { |
||||||
|
throw new CheckedException("Ai生成解析失败!"); |
||||||
|
} |
||||||
|
|
||||||
|
// 构建AI生成消息
|
||||||
|
return new ImContent() |
||||||
|
.setSendUserId(imContent.getReceiveUserId()) |
||||||
|
.setSendTime(LocalDateTime.now()) |
||||||
|
.setReceiveUserId(SecurityUtils.getCasUser().getId()) |
||||||
|
.setReceiveTime(LocalDateTime.now()) |
||||||
|
.setContent(choiceList.get(0).getMessage().getContent()) |
||||||
|
.setContentType(ImMessageTypeEnum.TEXT.getValue()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
package com.cloud.kicc.system.util; |
||||||
|
|
||||||
|
import cn.hutool.core.map.MapUtil; |
||||||
|
import com.cloud.kicc.common.core.util.SpringContextHolderUtil; |
||||||
|
import com.cloud.kicc.common.security.util.SecurityUtils; |
||||||
|
import com.cloud.kicc.system.config.OpenAiConfigProperties; |
||||||
|
import com.theokanning.openai.client.OpenAiApi; |
||||||
|
import com.theokanning.openai.completion.chat.*; |
||||||
|
import com.theokanning.openai.service.OpenAiService; |
||||||
|
import lombok.experimental.UtilityClass; |
||||||
|
import okhttp3.OkHttpClient; |
||||||
|
import retrofit2.Retrofit; |
||||||
|
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; |
||||||
|
import retrofit2.converter.jackson.JacksonConverterFactory; |
||||||
|
|
||||||
|
import java.time.Duration; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
*<p> |
||||||
|
* Ai 调用工具 |
||||||
|
*</p> |
||||||
|
* |
||||||
|
* @Author: wangxiang4 |
||||||
|
* @Since: 2023/11/16 |
||||||
|
*/ |
||||||
|
@UtilityClass |
||||||
|
public class AiUtil { |
||||||
|
|
||||||
|
OpenAiConfigProperties openAiConfigProperties = SpringContextHolderUtil.getBean(OpenAiConfigProperties.class); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取OpenAiService |
||||||
|
*/ |
||||||
|
public OpenAiService getOpenAiService() { |
||||||
|
OkHttpClient client = OpenAiService.defaultClient(openAiConfigProperties.getToken(), Duration.ofSeconds(openAiConfigProperties.getTimeout())); |
||||||
|
Retrofit retrofit = new Retrofit.Builder() |
||||||
|
.baseUrl(openAiConfigProperties.getBaseUrl()) |
||||||
|
.client(client) |
||||||
|
.addConverterFactory(JacksonConverterFactory.create(OpenAiService.defaultObjectMapper())) |
||||||
|
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) |
||||||
|
.build(); |
||||||
|
OpenAiApi api = retrofit.create(OpenAiApi.class); |
||||||
|
return new OpenAiService(api); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* <a href="https://platform.openai.com/docs/api-reference/chat/create">...</a> |
||||||
|
*/ |
||||||
|
public ChatCompletionResult getChatCompletion(List<ChatMessage> historyMessages) { |
||||||
|
String rule = "是与AI助手的对话。助手乐于助人,富有创造力,聪明每次回答限制在300字以内不得超过300字。"; |
||||||
|
List<ChatMessage> messages = new ArrayList<>(); |
||||||
|
ChatMessage systemMessage = new ChatMessage(ChatMessageRole.SYSTEM.value(), rule); |
||||||
|
messages.add(systemMessage); |
||||||
|
messages.addAll(historyMessages); |
||||||
|
ChatCompletionRequest request = ChatCompletionRequest.builder() |
||||||
|
.model(openAiConfigProperties.getModel()) |
||||||
|
.messages(messages) |
||||||
|
.n(1) |
||||||
|
.frequencyPenalty(1.0) |
||||||
|
.presencePenalty(1.0) |
||||||
|
.maxTokens(160) |
||||||
|
.topP(0.8) |
||||||
|
.user(SecurityUtils.getCasUser().getId()) |
||||||
|
.build(); |
||||||
|
return getOpenAiService().createChatCompletion(request); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||||
|
<mapper namespace="com.cloud.kicc.system.mapper.ImContentMapper"> |
||||||
|
|
||||||
|
<select id="listHistoryMessage" resultType="Map"> |
||||||
|
select |
||||||
|
im.id, |
||||||
|
im.send_user_id, |
||||||
|
im.send_time, |
||||||
|
im.receive_user_id, |
||||||
|
im.receive_time, |
||||||
|
im.content, |
||||||
|
im.content_type, |
||||||
|
im.status, |
||||||
|
im.files, |
||||||
|
im.revert, |
||||||
|
im.tenant_id, |
||||||
|
sf.file_name, |
||||||
|
sf.bucket_name, |
||||||
|
sf.original, |
||||||
|
sf.type, |
||||||
|
sf.file_size, |
||||||
|
sf.available_path, |
||||||
|
sf.duration, |
||||||
|
sf.mime_type |
||||||
|
from |
||||||
|
sys_im_content im left join sys_file sf on im.files = sf.id |
||||||
|
<where> |
||||||
|
im.del_flag = '0' |
||||||
|
and (im.send_user_id = #{ew.sendUserId} and im.receive_user_id = #{ew.receiveUserId}) |
||||||
|
or (im.send_user_id = #{ew.receiveUserId} and im.receive_user_id = #{ew.sendUserId}) |
||||||
|
</where> |
||||||
|
order by im.send_time asc |
||||||
|
</select> |
||||||
|
|
||||||
|
</mapper> |
@ -0,0 +1,102 @@ |
|||||||
|
import com.baomidou.mybatisplus.generator.AutoGenerator; |
||||||
|
import com.baomidou.mybatisplus.generator.config.*; |
||||||
|
import com.cloud.kicc.common.data.entity.CommonEntity; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
|
||||||
|
/** |
||||||
|
*<p> |
||||||
|
* mysql crud代码自动生成 |
||||||
|
*</p> |
||||||
|
* |
||||||
|
* @Author: wangxiang4 |
||||||
|
* @Date: 2022/7/22 |
||||||
|
*/ |
||||||
|
public class SimpleCrudGeneratorTest { |
||||||
|
|
||||||
|
/** 代码生成全局配置 */ |
||||||
|
interface Config { |
||||||
|
|
||||||
|
String url = "jdbc:mysql://139.9.72.189:8052/db_kicc?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true"; |
||||||
|
|
||||||
|
String username = "u_kanglai_account"; |
||||||
|
|
||||||
|
String password = "$kanglai.<2021>#"; |
||||||
|
|
||||||
|
String author = "wangxiang4"; |
||||||
|
|
||||||
|
String rootOutputDir = "/Users/wangxiang/IdeaProjects/org/kicc/kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java"; |
||||||
|
|
||||||
|
String entityOutputDir = "/Users/wangxiang/IdeaProjects/org/kicc/kicc-platform/kicc-platform-api/kicc-system-api/src/main/java/com/cloud/kicc/system/api/entity"; |
||||||
|
|
||||||
|
String mapperXmlOutputDir = "/Users/wangxiang/IdeaProjects/org/kicc/kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/resources/mapper"; |
||||||
|
|
||||||
|
String packageParent = "com.cloud.kicc.system"; |
||||||
|
|
||||||
|
String packageService = "service"; |
||||||
|
|
||||||
|
String packageEntity = "api.entity"; |
||||||
|
|
||||||
|
String packageServiceImpl = "service.impl"; |
||||||
|
|
||||||
|
String packageMapper = "mapper"; |
||||||
|
|
||||||
|
String packageController = "controller"; |
||||||
|
|
||||||
|
String[] includeTable = { "sys_im_content" }; |
||||||
|
|
||||||
|
String[] filterEntityTablePrefix = { "sys_" }; |
||||||
|
|
||||||
|
String[] superEntityColumns = { "createById", "createByName", "createTime", "updateById","updateByName", "updateTime", "remarks", "delFlag" }; |
||||||
|
|
||||||
|
String[] ignoreColumns = { "tenant_id" }; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
/** 运行代码生成 */ |
||||||
|
public void CrudSimpleGenerator() { |
||||||
|
|
||||||
|
AutoGenerator generator = new AutoGenerator(new DataSourceConfig.Builder(Config.url, Config.username, Config.password).build()); |
||||||
|
// 全局配置 参考:https://github.com/baomidou/generator#%E5%85%A8%E5%B1%80%E9%85%8D%E7%BD%AEglobalconfig
|
||||||
|
generator.global(new GlobalConfig.Builder() |
||||||
|
.author(Config.author) |
||||||
|
.enableSwagger() |
||||||
|
.fileOverride() |
||||||
|
.outputDir(Config.rootOutputDir) |
||||||
|
.build() |
||||||
|
); |
||||||
|
// 包路径配置 参考:https://github.com/baomidou/generator#%E5%8C%85%E9%85%8D%E7%BD%AEpackageconfig
|
||||||
|
Map<OutputFile, String> pathInfos = new ConcurrentHashMap(); |
||||||
|
pathInfos.put(OutputFile.entity, Config.entityOutputDir); |
||||||
|
pathInfos.put(OutputFile.mapperXml, Config.mapperXmlOutputDir); |
||||||
|
generator.packageInfo(new PackageConfig.Builder() |
||||||
|
.parent(Config.packageParent) |
||||||
|
.service(Config.packageService) |
||||||
|
.entity(Config.packageEntity) |
||||||
|
.serviceImpl(Config.packageServiceImpl) |
||||||
|
.mapper(Config.packageMapper) |
||||||
|
.controller(Config.packageController) |
||||||
|
.pathInfo(pathInfos) |
||||||
|
.build() |
||||||
|
); |
||||||
|
// 策略配置 参考:https://github.com/baomidou/generator#%E7%AD%96%E7%95%A5%E9%85%8D%E7%BD%AEstrategyconfig
|
||||||
|
generator.strategy(new StrategyConfig.Builder() |
||||||
|
.addInclude(Config.includeTable) |
||||||
|
.addTablePrefix(Config.filterEntityTablePrefix) |
||||||
|
.entityBuilder() |
||||||
|
.enableChainModel() |
||||||
|
.enableLombok() |
||||||
|
.enableRemoveIsPrefix() |
||||||
|
.superClass(CommonEntity.class) |
||||||
|
.addSuperEntityColumns(Config.superEntityColumns) |
||||||
|
.addIgnoreColumns(Config.ignoreColumns) |
||||||
|
.build() |
||||||
|
); |
||||||
|
generator.execute(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue