[Core] Support chatgpt

This commit is contained in:
qianmoQ 2023-03-09 22:48:57 +08:00
parent dc36186d30
commit e289057d25
16 changed files with 294 additions and 1 deletions

1
.java-version Normal file
View File

@ -0,0 +1 @@
11.0

View File

@ -91,6 +91,11 @@
<artifactId>UserAgentUtils</artifactId>
<version>1.21</version>
</dependency>
<dependency>
<groupId>com.unfbx</groupId>
<artifactId>chatgpt-java</artifactId>
<version>1.0.4</version>
</dependency>
<dependency>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap-spi</artifactId>

View File

@ -0,0 +1,16 @@
package io.edurt.datacap.server.body;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class UserQuestionBody
{
private String question;
private String type;
}

View File

@ -3,6 +3,7 @@ package io.edurt.datacap.server.controller.user;
import io.edurt.datacap.server.body.FilterBody;
import io.edurt.datacap.server.body.UserNameBody;
import io.edurt.datacap.server.body.UserPasswordBody;
import io.edurt.datacap.server.body.UserQuestionBody;
import io.edurt.datacap.server.common.Response;
import io.edurt.datacap.server.entity.PageEntity;
import io.edurt.datacap.server.entity.UserEntity;
@ -18,6 +19,8 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping(value = "/api/v1/user")
public class UserController
@ -49,9 +52,21 @@ public class UserController
return this.userService.changeUsername(configure);
}
@PutMapping(value = "changeThirdConfigure")
public Response<Long> changeThirdConfigure(@Validated @RequestBody Map<String, Map<String, Object>> configure)
{
return this.userService.changeThirdConfigure(configure);
}
@PostMapping(value = "log")
public Response<PageEntity<UserLogEntity>> getAllLogByFilter(@RequestBody FilterBody filter)
{
return this.userLogService.getAllByFilter(filter);
}
@PostMapping(value = "startChat")
public Response<Object> startChat(@RequestBody UserQuestionBody configure)
{
return this.userService.startChat(configure);
}
}

View File

@ -66,6 +66,9 @@ public class UserEntity
@Column(name = "password")
private String password;
@Column(name = "third_configure")
private String thirdConfigure;
@Column(name = "create_time", columnDefinition = "datetime(5) default CURRENT_TIMESTAMP()")
private Timestamp createTime;

View File

@ -2,10 +2,13 @@ package io.edurt.datacap.server.service;
import io.edurt.datacap.server.body.UserNameBody;
import io.edurt.datacap.server.body.UserPasswordBody;
import io.edurt.datacap.server.body.UserQuestionBody;
import io.edurt.datacap.server.common.JwtResponse;
import io.edurt.datacap.server.common.Response;
import io.edurt.datacap.server.entity.UserEntity;
import java.util.Map;
public interface UserService
{
Response<UserEntity> saveOrUpdate(UserEntity configure);
@ -17,4 +20,8 @@ public interface UserService
Response<Long> changePassword(UserPasswordBody configure);
Response<Long> changeUsername(UserNameBody configure);
Response<Long> changeThirdConfigure(Map<String, Map<String, Object>> configure);
Response<Object> startChat(UserQuestionBody configure);
}

View File

@ -1,8 +1,14 @@
package io.edurt.datacap.server.service.impl;
import com.unfbx.chatgpt.OpenAiClient;
import com.unfbx.chatgpt.entity.chat.ChatCompletion;
import com.unfbx.chatgpt.entity.chat.ChatCompletionResponse;
import com.unfbx.chatgpt.entity.chat.Message;
import io.edurt.datacap.server.audit.AuditUserLog;
import io.edurt.datacap.server.body.UserNameBody;
import io.edurt.datacap.server.body.UserPasswordBody;
import io.edurt.datacap.server.body.UserQuestionBody;
import io.edurt.datacap.server.common.JSON;
import io.edurt.datacap.server.common.JwtResponse;
import io.edurt.datacap.server.common.Response;
import io.edurt.datacap.server.common.ServiceState;
@ -14,6 +20,7 @@ import io.edurt.datacap.server.security.JwtService;
import io.edurt.datacap.server.security.UserDetailsService;
import io.edurt.datacap.server.service.UserService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
@ -21,8 +28,11 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@ -134,4 +144,54 @@ public class UserServiceImpl
this.userRepository.save(user);
return Response.success(user.getId());
}
@Override
public Response<Long> changeThirdConfigure(Map<String, Map<String, Object>> configure)
{
Optional<UserEntity> userOptional = this.userRepository.findById(UserDetailsService.getUser().getId());
if (!userOptional.isPresent()) {
return Response.failure(ServiceState.USER_NOT_FOUND);
}
UserEntity user = userOptional.get();
user.setThirdConfigure(JSON.toJSON(configure));
this.userRepository.save(user);
return Response.success(user.getId());
}
@Override
public Response<Object> startChat(UserQuestionBody configure)
{
Optional<UserEntity> userOptional = this.userRepository.findById(UserDetailsService.getUser().getId());
if (!userOptional.isPresent()) {
return Response.failure(ServiceState.USER_NOT_FOUND);
}
if (!configure.getType().equals("ChatGPT")) {
return Response.failure("Not supported");
}
UserEntity user = userOptional.get();
List<String> content = new ArrayList<>();
if (StringUtils.isNotEmpty(user.getThirdConfigure())) {
Map<String, Object> configureMap = JSON.toMap(user.getThirdConfigure());
if (configureMap.containsKey("chatgpt")) {
Map<String, Object> chatGPTMap = (Map<String, Object>) configureMap.get("chatgpt");
if (chatGPTMap.containsKey("token")) {
String token = (String) chatGPTMap.get("token");
if (StringUtils.isNotEmpty(token)) {
OpenAiClient openAiClient = OpenAiClient.builder()
.apiKey(token)
.build();
Message message = Message.builder()
.role(Message.Role.USER)
.content(configure.getQuestion())
.build();
ChatCompletion chatCompletion = ChatCompletion.builder().messages(Arrays.asList(message)).build();
ChatCompletionResponse chatCompletionResponse = openAiClient.chatCompletion(chatCompletion);
chatCompletionResponse.getChoices().forEach(e -> content.add(e.getMessage().getContent()));
}
}
}
}
return Response.success(content);
}
}

View File

@ -0,0 +1,2 @@
alter table users
add third_configure text;

View File

@ -0,0 +1 @@
source schema/users.sql;

View File

@ -80,5 +80,8 @@ export default {
ssl: 'SSL',
id: 'ID',
authority: 'Authority',
code: 'Code'
code: 'Code',
chatgpt: 'ChatGPT',
send: 'Send',
token: 'Token'
}

View File

@ -81,4 +81,7 @@ export default {
id: 'ID',
authority: '权限',
code: '代码',
chatgpt: 'ChatGPT',
send: '发送',
token: '密钥'
}

View File

@ -1,5 +1,17 @@
export interface User
{
username: string;
thirdConfigure: { chatgpt: ThirdConfigure };
createTime?: string;
}
export class UserQuestion
{
question: string;
type: string;
}
export class ThirdConfigure
{
token = '';
}

View File

@ -182,6 +182,13 @@ const routes: Array<RouteRecordRaw> = [
},
component: () => import("../views/pages/profile/ProfileLog.vue")
},
{
path: 'chatgpt',
meta: {
roles: ['Admin', 'User']
},
component: () => import("../views/pages/profile/ProfileChatGPT.vue")
},
{
path: 'account',
meta: {

View File

@ -3,6 +3,7 @@ import {ResponseModel} from "@/model/ResponseModel";
import {UserPassword} from "@/model/UserPassword";
import {UserName} from "@/model/UserName";
import {Filter} from "@/model/Filter";
import {UserQuestion} from "@/model/User";
const baseUrl = "/api/v1/user";
@ -23,6 +24,16 @@ class UserService
return new HttpCommon().put(baseUrl + '/changeUsername', configure);
}
changeThirdConfigure(configure: any): Promise<ResponseModel>
{
return new HttpCommon().put(baseUrl + '/changeThirdConfigure', configure);
}
startChat(configure: UserQuestion): Promise<ResponseModel>
{
return new HttpCommon().post(baseUrl + '/startChat', configure);
}
getLogs(filter: Filter): Promise<ResponseModel>
{
return new HttpCommon().post(baseUrl + '/log', filter);

View File

@ -0,0 +1,142 @@
<template>
<div>
<Card style="width:100%; minHeight: 150px;">
<template #title>
{{ $t('common.chatgpt') }}
</template>
<template #extra>
<Tooltip :content="$t('common.settings')" transfer>
<Button type="primary" size="small" shape="circle" icon="ios-cog" @click="handlerVisibleDetail(true)"></Button>
</Tooltip>
</template>
<div>
<Layout>
<Content style="padding: 24px 50px;">
<Input v-model="userQuestionResponse" :disabled="!userInfo?.thirdConfigure?.chatgpt?.token" type="textarea" :rows="10"/>
</Content>
<Footer>
<Row>
<Col span="20">
<Input v-model="userQuestion.question" :disabled="!userInfo?.thirdConfigure?.chatgpt?.token" type="textarea" :autosize="{minRows: 2,maxRows: 5}"/>
</Col>
<Col span="2" offset="1">
<Button :disabled="!userInfo?.thirdConfigure?.chatgpt?.token" type="primary" icon="md-send"
:loading="startChatLoading" @click="handlerStartChat()">{{ $t('common.send') }}
</Button>
</Col>
</Row>
</Footer>
</Layout>
</div>
<Spin size="large" fix :show="loading"></Spin>
</Card>
<Modal v-if="visibleModel" v-model="visibleModel" :title="$t('common.chatgpt') +' ' + $t('common.settings')"
:mask-closable="false" :closable="false">
<Form :label-width="80">
<FormItem :label="$t('common.token')">
<Input v-model="userInfo.thirdConfigure.chatgpt.token" type="text" placeholder="example: sk-xxxx"/>
</FormItem>
</Form>
<template #footer>
<Space>
<Button type="error" @click="handlerVisibleDetail(false)">{{ $t('common.cancel') }}</Button>
<Button type="primary" icon="md-document" @click="handlerSave()">{{ $t('common.save') }}</Button>
</Space>
</template>
</Modal>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import UserService from "@/services/UserService";
import {ThirdConfigure, User, UserQuestion} from '@/model/User';
const userQuestion = new UserQuestion();
userQuestion.type = 'ChatGPT';
export default defineComponent({
setup()
{
return {
userQuestion
}
},
created()
{
this.handlerInitialize()
},
data()
{
return {
loading: false,
visibleModel: false,
startChatLoading: false,
userInfo: null as User,
userQuestionResponse: null
}
},
methods: {
handlerInitialize()
{
this.loading = true;
UserService.getInfo()
.then(response => {
if (response.status) {
this.userInfo = response.data;
// If the initialization property has not been set
if (!this.userInfo.thirdConfigure) {
this.userInfo.thirdConfigure = {
chatgpt: new ThirdConfigure()
}
}
else {
this.userInfo.thirdConfigure = JSON.parse(this.userInfo.thirdConfigure);
}
}
})
.finally(() => {
this.loading = false;
});
},
handlerVisibleDetail(value: boolean)
{
this.visibleModel = value;
if (!value) {
this.handlerInitialize();
}
},
handlerSave()
{
UserService.changeThirdConfigure(this.userInfo.thirdConfigure)
.then(response => {
this.$Message.success(response.message);
this.handlerVisibleDetail(false);
})
.finally(() => {
this.loading = false;
});
},
handlerStartChat()
{
this.startChatLoading = true;
UserService.startChat(this.userQuestion)
.then(response => {
if (response.status) {
this.userQuestionResponse = response.data;
}
else {
this.$Message.error(response.message);
}
})
.finally(() => {
this.startChatLoading = false;
});
}
}
});
</script>
<style scoped>
.content {
background-color: #FFFFFF;
}
</style>

View File

@ -20,6 +20,11 @@
<Icon type="md-egg"/>
{{ $t('setting.log') }}
</MenuItem>
<MenuItem name="setting_chatgpt" to="/profile/chatgpt">
<Icon type="md-chatbubbles"/>
{{ $t('common.chatgpt') }}
<Badge text="new"></Badge>
</MenuItem>
<MenuItem name="security" to="/profile/account">
<Icon type="ios-contact"/>
{{ $t('setting.accountSetting') }}