This commit is contained in:
jack ning
2024-06-12 16:32:58 +08:00
parent 2460e34436
commit 15a4e3e6bf
772 changed files with 14165 additions and 6473 deletions

BIN
.DS_Store vendored

Binary file not shown.

Binary file not shown.

BIN
modules/.DS_Store vendored

Binary file not shown.

BIN
modules/ai/.DS_Store vendored

Binary file not shown.

View File

@@ -17,42 +17,48 @@
<properties>
<!-- https://docs.spring.io/spring-ai/reference/getting-started.html -->
<spring-ai.version>0.8.1-SNAPSHOT</spring-ai.version>
<!-- <spring-ai.version>1.0.0-M1</spring-ai.version> -->
<spring-ai.version>1.0.0-SNAPSHOT</spring-ai.version>
</properties>
<dependencies>
<!-- https://docs.spring.io/spring-ai/reference/api/clients/ollama.html -->
<!-- <dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store-spring-boot-starter</artifactId>
</dependency> -->
<!-- https://docs.spring.io/spring-ai/reference/api/embeddings/ollama-embeddings.html -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<!-- <dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency> -->
<!-- <dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama</artifactId>
<version>${spring-ai.version}</version>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency> -->
<!-- <dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-transformers-spring-boot-starter</artifactId>
<version>${spring-ai.version}</version>
<artifactId>spring-ai-postgresml-spring-boot-starter</artifactId>
</dependency> -->
<!-- https://docs.spring.io/spring-ai/reference/api/vectordbs/redis.html -->
<!-- <dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis</artifactId>
<version>${spring-ai.version}</version>
<artifactId>spring-ai-redis-store-spring-boot-starter</artifactId>
</dependency> -->
<!-- <dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.1.0</version>
</dependency> -->
<!-- https://docs.spring.io/spring-ai/reference/api/embeddings/zhipuai-embeddings.html -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId>
</dependency>
<!-- ///////////////////////////////////////////////////////////////////////////// -->
@@ -63,7 +69,35 @@
<scope>provided</scope>
</dependency>
<!-- ///////////////////////////////////////////////////////////////////////////// -->
<!-- https://open.bigmodel.cn/dev/api#sdk_install -->
<!-- https://github.com/MetaGLM/zhipuai-sdk-java-v4 -->
<dependency>
<groupId>cn.bigmodel.openapi</groupId>
<artifactId>oapi-java-sdk</artifactId>
<version>release-V4-2.1.0</version>
</dependency>
<!-- ///////////////////////////////////////////////////////////////////////////// -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>

View File

@@ -1,6 +1,24 @@
<!--
* @Author: jackning 270580156@qq.com
* @Date: 2024-02-02 09:32:36
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-31 09:58:27
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
-->
# ai
## docs
- [spring-ai](https://docs.spring.io/spring-ai/reference/)
- [spring-ai-github](https://github.com/spring-projects/spring-ai)
```bash
ollama --help
```

Binary file not shown.

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:21:15
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-15 09:20:13
* @LastEditTime: 2024-05-27 11:59:13
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -16,6 +16,7 @@ package com.bytedesk.ai.doc;
import com.bytedesk.ai.file.KbFile;
import com.bytedesk.core.base.BaseEntity;
import com.bytedesk.core.constant.TypeConsts;
import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
@@ -43,15 +44,12 @@ import lombok.experimental.Accessors;
@NoArgsConstructor
@Table(name = "ai_kb_doc")
public class KbDoc extends BaseEntity {
private static final long serialVersionUID = 1L;
// @Column(name = "did", unique = true, nullable = false)
// private String did;
@Column(columnDefinition = "LONGTEXT")
private static final long serialVersionUID = 1L;
@Column(columnDefinition = TypeConsts.COLUMN_TYPE_TEXT)
private String content;
private String meta;
/**
@@ -60,11 +58,20 @@ public class KbDoc extends BaseEntity {
@JoinColumn(name = "kb_file_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
private KbFile kbFile;
/**
* belong to org
*/
// @JsonIgnore
// @ManyToOne(fetch = FetchType.LAZY)
// private Organization organization;
private String orgUid;
/**
* 所属用户
*/
// @JsonIgnore
// @ManyToOne(fetch = FetchType.LAZY)
// @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
// @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "none", value =
// ConstraintMode.NO_CONSTRAINT))
// private User user;
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:59:55
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-08 09:15:09
* @LastEditTime: 2024-05-31 07:40:45
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,14 +14,13 @@
*/
package com.bytedesk.ai.doc;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bytedesk.core.utils.JsonResult;
import com.bytedesk.core.base.BaseController;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import lombok.AllArgsConstructor;
/**
@@ -30,74 +29,39 @@ import lombok.AllArgsConstructor;
@RestController
@RequestMapping("/api/v1/kb/doc")
@AllArgsConstructor
public class KbDocController {
public class KbDocController extends BaseController<KbDocRequest> {
private final KbDocService kbDocService;
/**
* query
*
* @param kbDocRequest
* @return
*/
@GetMapping("/query")
public JsonResult<?> query(KbDocRequest kbDocRequest) {
return JsonResult.success(kbDocService.query(kbDocRequest));
@Override
public ResponseEntity<?> queryByOrg(KbDocRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'queryByOrg'");
}
/**
* create
*
* @param kbDocRequest kb
* @return json
*/
@PostMapping("/create")
public JsonResult<?> create(@RequestBody KbDocRequest kbDocRequest) {
// return kbService.create(kbDocRequest);
return JsonResult.success();
@Override
public ResponseEntity<?> query(KbDocRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'query'");
}
/**
* update
*
* @param kbDocRequest kb
* @return json
*/
@PostMapping("/update")
public JsonResult<?> update(@RequestBody KbDocRequest kbDocRequest) {
//
return new JsonResult<>("update success", 200, false);
@Override
public ResponseEntity<?> create(@RequestBody KbDocRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'create'");
}
/**
* delete
*
* @param kbDocRequest kb
* @return json
*/
@PostMapping("/delete")
public JsonResult<?> delete(@RequestBody KbDocRequest kbDocRequest) {
//
return new JsonResult<>("delete success", 200, true);
@Override
public ResponseEntity<?> update(@RequestBody KbDocRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'update'");
}
/**
* filter
*
* @return json
*/
@GetMapping("/filter")
public JsonResult<?> filter(KbDocRequest filterParam) {
//
//
return new JsonResult<>("filter success", 200, false);
@Override
public ResponseEntity<?> delete(@RequestBody KbDocRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'delete'");
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 17:00:07
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:55:40
* @LastEditTime: 2024-05-27 11:55:19
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,36 +14,98 @@
*/
package com.bytedesk.ai.doc;
import org.modelmapper.ModelMapper;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import com.bytedesk.core.base.BaseService;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class KbDocService {
public class KbDocService extends BaseService<KbDoc, KbDocRequest, KbDocResponse> {
private final KbDocRepository kbDocRepository;
// private final KbDocRepository kbDocRepository;
private final ModelMapper modelMapper;
// private final ModelMapper modelMapper;
public Page<KbDocResponse> query(KbDocRequest kbDocRequest) {
Pageable pageable = PageRequest.of(kbDocRequest.getPageNumber(), kbDocRequest.getPageSize(),
Sort.Direction.DESC,
"id");
Page<KbDoc> kbDocPage = kbDocRepository.findAll(pageable);
return kbDocPage.map(this::convertToDocResponse);
@Override
public Page<KbDocResponse> queryByOrg(KbDocRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'queryByOrg'");
}
@Override
public Page<KbDocResponse> queryByUser(KbDocRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'queryByUser'");
}
@Override
public Optional<KbDoc> findByUid(String uid) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'findByUid'");
}
@Override
public KbDocResponse create(KbDocRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'create'");
}
@Override
public KbDocResponse update(KbDocRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'update'");
}
@Override
public KbDoc save(KbDoc entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'save'");
}
@Override
public void deleteByUid(String uid) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'deleteByUid'");
}
@Override
public void delete(KbDoc entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'delete'");
}
@Override
public void handleOptimisticLockingFailureException(ObjectOptimisticLockingFailureException e, KbDoc entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'handleOptimisticLockingFailureException'");
}
@Override
public KbDocResponse convertToResponse(KbDoc entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'convertToResponse'");
}
public KbDocResponse convertToDocResponse(KbDoc kbDoc) {
return modelMapper.map(kbDoc, KbDocResponse.class);
}
// public Page<KbDocResponse> query(KbDocRequest kbDocRequest) {
// Pageable pageable = PageRequest.of(kbDocRequest.getPageNumber(), kbDocRequest.getPageSize(),
// Sort.Direction.DESC,
// "id");
// Page<KbDoc> kbDocPage = kbDocRepository.findAll(pageable);
// return kbDocPage.map(this::convertToDocResponse);
// }
// public KbDocResponse convertToDocResponse(KbDoc kbDoc) {
// return modelMapper.map(kbDoc, KbDocResponse.class);
// }
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:23:35
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:56:57
* @LastEditTime: 2024-05-27 11:59:22
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -49,18 +49,12 @@ public class KbFile extends BaseEntity {
private static final long serialVersionUID = 1L;
// @Column(name = "fid", unique = true, nullable = false)
// private String fid;
private String loader;
private String splitter;
private Integer docsCount;
/**
*
*/
@JoinColumn(name = "upload_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
private Upload upload;
@@ -72,6 +66,14 @@ public class KbFile extends BaseEntity {
@JoinColumn(name = "kb_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
private Kb kb;
/**
* belong to org
*/
// @JsonIgnore
// @ManyToOne(fetch = FetchType.LAZY)
// private Organization organization;
private String orgUid;
/**
* 所属用户
*/

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:48:26
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-08 09:15:24
* @LastEditTime: 2024-05-31 07:40:57
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,14 +14,13 @@
*/
package com.bytedesk.ai.file;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bytedesk.core.utils.JsonResult;
import com.bytedesk.core.base.BaseController;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import lombok.AllArgsConstructor;
/**
@@ -30,74 +29,40 @@ import lombok.AllArgsConstructor;
@RestController
@RequestMapping("/api/v1/kb/file")
@AllArgsConstructor
public class KbFileController {
public class KbFileController extends BaseController<KbFileRequest> {
private final KbFileService kbService;
/**
* query
*
* @param kbFileRequest
* @return
*/
@GetMapping("/query")
public JsonResult<?> query(KbFileRequest kbFileRequest) {
return JsonResult.success(kbService.query(kbFileRequest));
@Override
public ResponseEntity<?> queryByOrg(KbFileRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'queryByOrg'");
}
/**
* create
*
* @param kbFileRequest kb
* @return json
*/
@PostMapping("/create")
public JsonResult<?> create(@RequestBody KbFileRequest kbFileRequest) {
// return kbService.create(kbFileRequest);
return JsonResult.success();
@Override
public ResponseEntity<?> query(KbFileRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'query'");
}
/**
* update
*
* @param kbFileRequest kb
* @return json
*/
@PostMapping("/update")
public JsonResult<?> update(@RequestBody KbFileRequest kbFileRequest) {
//
return new JsonResult<>("update success", 200, false);
@Override
public ResponseEntity<?> create(@RequestBody KbFileRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'create'");
}
/**
* delete
*
* @param kbFileRequest kb
* @return json
*/
@PostMapping("/delete")
public JsonResult<?> delete(@RequestBody KbFileRequest kbFileRequest) {
//
return new JsonResult<>("delete success", 200, true);
@Override
public ResponseEntity<?> update(@RequestBody KbFileRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'update'");
}
/**
* filter
*
* @return json
*/
@GetMapping("/filter")
public JsonResult<?> filter(KbFileRequest filterParam) {
//
//
return new JsonResult<>("filter success", 200, false);
@Override
public ResponseEntity<?> delete(@RequestBody KbFileRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'delete'");
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:13:38
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:58:05
* @LastEditTime: 2024-06-11 11:41:42
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -15,6 +15,7 @@
package com.bytedesk.ai.kb;
import com.bytedesk.core.base.BaseEntity;
import com.bytedesk.core.constant.I18Consts;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@@ -41,9 +42,6 @@ public class Kb extends BaseEntity {
private static final long serialVersionUID = 1L;
// @Column(name = "kid", unique = true, nullable = false)
// private String kid;
private String name;
private String vectorStore;
@@ -51,8 +49,20 @@ public class Kb extends BaseEntity {
private String embeddings;
// is_published or not
// private boolean published;
@Builder.Default
private boolean published = true;
@Builder.Default
private String language = I18Consts.ZH_CN;
/**
* belong to org
*/
// @JsonIgnore
// @ManyToOne(fetch = FetchType.LAZY)
// private Organization organization;
private String orgUid;
/**
* 所属用户
*/

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:46:14
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-08 09:15:35
* @LastEditTime: 2024-05-31 07:41:03
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,14 +14,13 @@
*/
package com.bytedesk.ai.kb;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bytedesk.core.utils.JsonResult;
import com.bytedesk.core.base.BaseController;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import lombok.AllArgsConstructor;
/**
@@ -30,73 +29,39 @@ import lombok.AllArgsConstructor;
@RestController
@RequestMapping("/api/v1/kb")
@AllArgsConstructor
public class KbController {
public class KbController extends BaseController<KbRequest> {
private final KbService kbService;
/**
* query
*
* @param kbRequest
* @return
*/
@GetMapping("/query")
public JsonResult<?> query(KbRequest kbRequest) {
return JsonResult.success(kbService.query(kbRequest));
@Override
public ResponseEntity<?> queryByOrg(KbRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'queryByOrg'");
}
/**
* create
*
* @param kbRequest kb
* @return json
*/
@PostMapping("/create")
public JsonResult<?> create(@RequestBody KbRequest kbRequest) {
return kbService.create(kbRequest);
@Override
public ResponseEntity<?> query(KbRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'query'");
}
/**
* update
*
* @param kbRequest kb
* @return json
*/
@PostMapping("/update")
public JsonResult<?> update(@RequestBody KbRequest kbRequest) {
//
return new JsonResult<>("update success", 200, false);
@Override
public ResponseEntity<?> create(@RequestBody KbRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'create'");
}
/**
* delete
*
* @param kbRequest kb
* @return json
*/
@PostMapping("/delete")
public JsonResult<?> delete(@RequestBody KbRequest kbRequest) {
//
return new JsonResult<>("delete success", 200, true);
@Override
public ResponseEntity<?> update(@RequestBody KbRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'update'");
}
/**
* filter
*
* @return json
*/
@GetMapping("/filter")
public JsonResult<?> filter(KbRequest filterParam) {
//
//
return new JsonResult<>("filter success", 200, false);
@Override
public ResponseEntity<?> delete(@RequestBody KbRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'delete'");
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:46:24
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:58:11
* @LastEditTime: 2024-05-27 14:48:36
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -61,13 +61,14 @@ public class KbService {
return modelMapper.map(kb, KbResponse.class);
}
public Kb getKb(String name) {
public Kb getKb(String name, String orgUid) {
Kb kb = Kb.builder()
// .kid(uidUtils.getCacheSerialUid())
.name(name)
.vectorStore("redis")
.embeddings("m3e-base")
.orgUid(orgUid)
.build();
kb.setUid(uidUtils.getCacheSerialUid());

View File

@@ -1,61 +0,0 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-25 12:08:16
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:59:23
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.llm;
import org.springframework.stereotype.Service;
import com.bytedesk.core.uid.UidUtils;
import com.bytedesk.core.utils.JsonResult;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class LlmService {
private LlmRepository llmRepository;
private final UidUtils uidUtils;
public JsonResult<?> create(LlmRequest llmRequest) {
return JsonResult.success();
}
public Llm getLlm(String type) {
Llm llm = new Llm();
llm.setUid(uidUtils.getCacheSerialUid());
llm.setName("智谱AI");
llm.setDescription("对接智谱API");
llm.setType(type);
llm.setEmbeddings("m3e-base");
llm.setTopK(3);
llm.setTemperature(0.9);
llm.setScoreThreshold(0.5);
llm.setPrompt("你是一个聪明、对人类有帮助的人工智能,你可以对人类提出的问题给出有用、详细、礼貌的回答");
return llmRepository.save(llm);
}
public void initData() {
if (llmRepository.count() > 0) {
return;
}
//
getLlm("system");
}
}

View File

@@ -0,0 +1,59 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-31 10:24:39
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-31 11:11:28
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.ollama;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.OllamaEmbeddingModel;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* https://docs.spring.io/spring-ai/reference/api/embeddings/ollama-embeddings.html
*/
@Configuration
public class OllamaConfig {
@Bean
OllamaApi ollamaApi() {
return new OllamaApi("http://localhost:11434");
}
// https://docs.spring.io/spring-ai/reference/api/chatclient.html
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
return builder.defaultSystem("You are a friendly chat bot that answers question")
.build();
}
@Bean
OllamaChatModel chatModel() {
return new OllamaChatModel(ollamaApi(),
OllamaOptions.create()
// .withModel(OllamaOptions.DEFAULT_MODEL)
.withModel("qwen:7b")
.withTemperature(0.9f));
}
// https://docs.spring.io/spring-ai/reference/api/embeddings/ollama-embeddings.html
@Bean
OllamaEmbeddingModel ollamaEmbeddingModel() {
return new OllamaEmbeddingModel(ollamaApi());
}
}

View File

@@ -0,0 +1,99 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-31 09:50:56
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-31 11:12:19
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.ollama;
import java.util.List;
import java.util.Map;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.embedding.EmbeddingRequest;
import org.springframework.ai.embedding.EmbeddingResponse;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.OllamaEmbeddingModel;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.bytedesk.core.utils.JsonResult;
import lombok.AllArgsConstructor;
import reactor.core.publisher.Flux;
// https://docs.spring.io/spring-ai/reference/api/chat/ollama-chat.html
@RestController
@RequestMapping("/visitor/api/v1/ai/ollama")
@AllArgsConstructor
public class OllamaController {
private final OllamaEmbeddingModel ollamaEmbeddingModel;
private final ChatClient chatClient;
private final OllamaChatModel chatModel;
// public OllamaController(EmbeddingModel embeddingModel, ChatClient.Builder chatClientbBuilder) {
// this.embeddingModel = embeddingModel;
// this.chatClient = chatClientbBuilder.build();
// }
// http://localhost:9003/visitor/api/v1/ai/ollama/chat?input=hello
@GetMapping("/chat")
public ResponseEntity<?> generation(@RequestParam("input") String input) {
String content = this.chatClient.prompt()
.user(input)
.call()
.content();
return ResponseEntity.ok(JsonResult.success(content));
}
// http://localhost:9003/visitor/api/v1/ai/ollama/simple
@GetMapping("/simple")
public Map<String, String> completion(
@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
return Map.of("completion", chatClient.prompt().user(message).call().content());
}
// http://localhost:9003/visitor/api/v1/ai/ollama/generate
@GetMapping("/generate")
public Map<?, ?> generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
return Map.of("generation", chatModel.call(message));
}
// http://localhost:9003/visitor/api/v1/ai/ollama/generateStream
@GetMapping("/generateStream")
public Flux<ChatResponse> generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
Prompt prompt = new Prompt(new UserMessage(message));
return chatModel.stream(prompt);
}
/*
* http://localhost:9003/visitor/api/v1/ai/ollama/embedding
*/
@GetMapping("/embedding")
public Map<?, ?> embed(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
// EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(List.of(message));
//
EmbeddingResponse embeddingResponse = ollamaEmbeddingModel.call(new EmbeddingRequest(List.of("Hello World", "World is big and salvation is near"),
OllamaOptions.create().withModel("qwen:7b")));
return Map.of("embedding", embeddingResponse);
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:16:26
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:59:40
* @LastEditTime: 2024-06-12 07:19:14
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -15,14 +15,19 @@
package com.bytedesk.ai.robot;
import com.bytedesk.ai.kb.Kb;
import com.bytedesk.ai.llm.Llm;
import com.bytedesk.core.base.BaseEntity;
import com.bytedesk.core.rbac.user.User;
import com.bytedesk.core.constant.AvatarConsts;
import com.bytedesk.core.constant.I18Consts;
import com.bytedesk.core.constant.TypeConsts;
import com.fasterxml.jackson.annotation.JsonIgnore;
// import jakarta.persistence.AssociationOverride;
// import jakarta.persistence.AssociationOverrides;
import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.JoinColumn;
@@ -47,36 +52,46 @@ import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners({ RobotEntityListener.class })
@Table(name = "ai_robot")
public class Robot extends BaseEntity {
private static final long serialVersionUID = 1L;
// @Column(name = "rid", unique = true, nullable = false)
// private String rid;
private String nickname;
private String name;
private String avatar;
@Builder.Default
private String avatar = AvatarConsts.DEFAULT_AVATAR_URL;
private String description;
@Builder.Default
private String description = I18Consts.I18N_ROBOT_DESCRIPTION;
private String welcome;
@Embedded
@Builder.Default
private RobotServiceSettings serviceSettings = new RobotServiceSettings();
@Embedded
@Builder.Default
private RobotLlm llm = new RobotLlm();
// 客服机器人、问答机器人、闲聊
// service、ask、chat
@Column(name = "by_type")
private String type;
@Builder.Default
@Column(name = TypeConsts.COLUMN_NAME_TYPE)
// private String type = TypeConsts.ROBOT_TYPE_SERVICE;
private RobotTypeEnum type = RobotTypeEnum.SERVICE;
// is_published or not
private boolean published;
/**
* llm
*/
@ManyToOne()
@JoinColumn(name = "llm_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
private Llm llm;
@Builder.Default
private boolean published = false;
// /**
// * llm
// */
// @ManyToOne()
// @JoinColumn(name = "llm_id", foreignKey = @ForeignKey(name = "none", value =
// ConstraintMode.NO_CONSTRAINT))
// private Llm llm;
/**
* 知识库
@@ -87,10 +102,13 @@ public class Robot extends BaseEntity {
private Kb kb;
/**
* 所属用户
* belong to org
*/
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
private User user;
// @JsonIgnore
// @ManyToOne(fetch = FetchType.LAZY)
// private Organization organization;
private String orgUid;
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:37:01
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-08 09:15:46
* @LastEditTime: 2024-06-05 10:19:17
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,14 +14,18 @@
*/
package com.bytedesk.ai.robot;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bytedesk.core.action.ActionAnnotation;
import com.bytedesk.core.base.BaseController;
import com.bytedesk.core.utils.JsonResult;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
@@ -32,74 +36,54 @@ import lombok.AllArgsConstructor;
@RequestMapping("/api/v1/robot")
@AllArgsConstructor
@Tag(name = "robot - 机器人", description = "robot description")
public class RobotController {
public class RobotController extends BaseController<RobotRequest> {
private final RobotService robotService;
/**
* query
*
* @param robotRequest
* @return
*/
@GetMapping("/query")
public JsonResult<?> query(RobotRequest robotRequest) {
return JsonResult.success(robotService.query(robotRequest));
}
/**
* create
*
* @param robotRequest robot
* @return json
*/
@PostMapping("/create")
public JsonResult<?> create(@RequestBody RobotRequest robotRequest) {
return robotService.create(robotRequest);
}
/**
* update
*
* @param robotRequest robot
* @return json
*/
@PostMapping("/update")
public JsonResult<?> update(@RequestBody RobotRequest robotRequest) {
//
return new JsonResult<>("update success", 200, false);
}
/**
* delete
*
* @param robotRequest robot
* @return json
*/
@PostMapping("/delete")
public JsonResult<?> delete(@RequestBody RobotRequest robotRequest) {
//
return new JsonResult<>("delete success", 200, true);
}
/**
* filter
*
* @return json
*/
@GetMapping("/filter")
public JsonResult<?> filter(RobotRequest filterParam) {
//
@GetMapping("/query/org")
@Override
public ResponseEntity<?> queryByOrg(RobotRequest request) {
//
return new JsonResult<>("filter success", 200, false);
Page<RobotResponse> page = robotService.queryByOrg(request);
return ResponseEntity.ok(JsonResult.success(page));
}
@GetMapping("/query/user")
@Override
public ResponseEntity<?> query(RobotRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'query'");
}
@ActionAnnotation(title = "robot", action = "create", description = "create robot")
@PostMapping("/create")
@Override
public ResponseEntity<?> create(@RequestBody RobotRequest request) {
RobotResponse robot = robotService.create(request);
return ResponseEntity.ok(JsonResult.success(robot));
}
@ActionAnnotation(title = "robot", action = "update", description = "update robot")
@PostMapping("/update")
@Override
public ResponseEntity<?> update(@RequestBody RobotRequest request) {
RobotResponse robotResponse = robotService.update(request);
return ResponseEntity.ok(JsonResult.success(robotResponse));
}
@ActionAnnotation(title = "robot", action = "delete", description = "delete robot")
@PostMapping("/delete")
@Override
public ResponseEntity<?> delete(@RequestBody RobotRequest request) {
robotService.deleteByUid(request.getUid());
return ResponseEntity.ok(JsonResult.success(request));
}
}

View File

@@ -0,0 +1,47 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-06 13:08:36
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-06 13:08:39
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.robot;
public enum RobotEmbedingEnum {
M3E_BASE("m3e-base");
// private final String name;
private final String value;
RobotEmbedingEnum(String value) {
// this.name = name;
this.value = value;
}
// public String getName() {
// return name;
// }
// 获取枚举常量的整型值
public String getValue() {
return value;
}
// 根据整型值查找对应的枚举常量
public static RobotEmbedingEnum fromValue(String value) {
for (RobotEmbedingEnum type : RobotEmbedingEnum.values()) {
if (type.getValue().equalsIgnoreCase(value)) {
return type;
}
}
throw new IllegalArgumentException("No RobotEmbedingEnum constant with value " + value);
}
}

View File

@@ -1,8 +1,8 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-12 15:06:27
* @Date: 2024-06-12 07:17:00
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-12 15:09:56
* @LastEditTime: 2024-06-12 07:22:52
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -12,16 +12,18 @@
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.service.customer;
package com.bytedesk.ai.robot;
/**
* https://docs.spring.io/spring-data/jpa/reference/repositories/projections.html
*/
public interface CustomerSummary {
String getName();
AddressSummary getAddress();
import org.springframework.stereotype.Component;
interface AddressSummary {
String getCity();
import jakarta.persistence.PostPersist;
@Component
public class RobotEntityListener {
@PostPersist
public void onPostPersist(Robot robot) {
System.out.println("RobotListener: onPostPersist");
}
}

View File

@@ -0,0 +1,71 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-12 07:17:13
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-12 09:45:24
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.robot;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.bytedesk.ai.kb.Kb;
import com.bytedesk.ai.kb.KbService;
import com.bytedesk.core.constant.AvatarConsts;
import com.bytedesk.core.constant.I18Consts;
import com.bytedesk.core.rbac.organization.Organization;
import com.bytedesk.core.rbac.organization.OrganizationCreateEvent;
// import com.bytedesk.core.rbac.user.User;
import com.bytedesk.core.uid.UidUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@AllArgsConstructor
public class RobotEventListener {
private final RobotService robotService;
private final KbService kbService;
private final UidUtils uidUtils;
@Order(2)
@EventListener
public void onOrganizationCreateEvent(OrganizationCreateEvent event) {
Organization organization = (Organization) event.getSource();
// User user = organization.getUser();
log.info("robot - organization created: {}", organization.getName());
//
Kb kb = kbService.getKb(I18Consts.I18N_ROBOT_NICKNAME, organization.getUid());
RobotLlm llm = RobotLlm.builder().build();
Robot robot = Robot.builder()
// .nickname(I18Consts.I18N_ROBOT_NICKNAME)
.description(I18Consts.I18N_ROBOT_DESCRIPTION)
.type(RobotTypeEnum.SERVICE)
.orgUid(organization.getUid())
.kb(kb)
.llm(llm)
.build();
robot.setUid(uidUtils.getCacheSerialUid());
robot.setNickname(I18Consts.I18N_ROBOT_NICKNAME);
robot.setAvatar(AvatarConsts.DEFAULT_AVATAR_URL);
//
robotService.save(robot);
}
}

View File

@@ -0,0 +1,72 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-05 10:02:51
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-11 11:52:16
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.robot;
import com.bytedesk.core.constant.I18Consts;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
// https://docs.spring.io/spring-data/jpa/reference/repositories/projections.html
@Embeddable
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RobotLlm {
// 默认不起用llm问答需要管理后台手动启动
@Builder.Default
@Column(name = "is_enabled")
private boolean enabled = false;
@Builder.Default
private int topK = 3;
@Builder.Default
private Double scoreThreshold = 0.5;
@Builder.Default
private RobotModelEnum model = RobotModelEnum.ZHIPUAI_GLM_3_TURBO;
@Builder.Default
// private String embeddings = "m3e-base";
private RobotEmbedingEnum embeddings = RobotEmbedingEnum.M3E_BASE;
@Builder.Default
private Float temperature = 0.9f;
@Builder.Default
private Float topP = 0.7f;
@Builder.Default
private String prompt = I18Consts.I18N_ROBOT_LLM_PROMPT_ZH_CN;
// 默认是true表示使用自定义模型, false表示云服务需要在配置文件中配置相关参数
@Builder.Default
@Column(name = "is_custom")
private boolean custom = true;
private String apiKey;
private String apiSecret;
private String apiUrl;
}

View File

@@ -0,0 +1,53 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-05 12:41:13
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-06 12:09:38
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.robot;
import lombok.Builder;
import lombok.Data;
/**
* 转化为json存储到message表中content字段
* @{com.bytedesk.core.message.Message}
*/
@Data
@Builder
public class RobotMessage {
//
private String question;
private String answer;
public void clearAnswer() {
this.answer = "";
}
public void appendAnswer(String answer) {
this.answer += answer;
}
/**
* 参考:
* @{com.zhipu.oapi.service.v4.model.Usage}
*/
@Builder.Default
private Integer promptTokens = 0;
@Builder.Default
private Integer completionTokens = 0;
@Builder.Default
private Integer totalTokens = 0;
}

View File

@@ -0,0 +1,61 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-05 10:42:05
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-06 13:12:14
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.robot;
public enum RobotModelEnum {
ZHIPUAI_GLM_3_TURBO("glm-3-turbo"),
ZHIPUAI_GLM_4("GLM-4"),
ZHIPUAI_CogView("cogview"),
ZHIPUAI_GLM4V("glm-4v"),
OLLAMA("ollama"),
OPENAI("openai");
// private final String name;
private final String value;
RobotModelEnum(String value) {
// this.name = name;
this.value = value;
}
// public String getName() {
// return name;
// }
// 获取枚举常量的整型值
public String getValue() {
return value;
}
// 根据整型值查找对应的枚举常量
public static RobotModelEnum fromValue(String value) {
for (RobotModelEnum type : RobotModelEnum.values()) {
if (type.getValue().equalsIgnoreCase(value)) {
return type;
}
}
throw new IllegalArgumentException("No RobotModelEnum constant with value " + value);
}
// public static RobotModelEnum fromString(String typeStr) {
// // 使用try-catch处理可能的异常
// try {
// return RobotModelEnum.valueOf(typeStr);
// } catch (IllegalArgumentException e) {
// // 处理错误,例如记录日志或抛出更具体的异常
// throw new IllegalArgumentException("Invalid robot type: " + typeStr, e);
// }
// }
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:44:54
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-03-26 15:12:12
* @LastEditTime: 2024-06-12 09:10:15
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,26 +14,25 @@
*/
package com.bytedesk.ai.robot;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import com.bytedesk.core.rbac.user.User;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 机器人
*
*/
@Repository
@Tag(name = "robot info - 机器人")
@Tag(name = "robot info")
// @PreAuthorize("hasRole('ROLE_ADMIN')")
public interface RobotRepository extends JpaRepository<Robot, Long>, JpaSpecificationExecutor<Robot> {
Page<Robot> findByUser(User user, Pageable pageable);
Boolean existsByUserAndName(User user, String name);
Optional<Robot> findByUid(String uid);
// Page<Robot> findByOrgUidAndDeleted(String orgUid, Boolean deleted, Pageable pageable);
Boolean existsByNicknameAndOrgUidAndDeleted(String nickname, String orgUid, Boolean deleted);
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:45:07
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-16 12:01:55
* @LastEditTime: 2024-06-11 12:27:10
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -16,6 +16,7 @@ package com.bytedesk.ai.robot;
import com.bytedesk.core.base.BaseRequest;
import jakarta.validation.constraints.NotBlank;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -27,18 +28,24 @@ import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = false)
public class RobotRequest extends BaseRequest {
private String name;
private String nickname;
private String avatar;
private String description;
private String welcome;
// private boolean published;
// @Builder.Default
// private RobotTypeEnum type = RobotTypeEnum.SERVICE;
//
@Builder.Default
private Boolean published = false;
@Builder.Default
private RobotServiceSettingsRequest serviceSettings = new RobotServiceSettingsRequest();
@Builder.Default
private RobotLlm llm = new RobotLlm();
@NotBlank
private String orgUid;
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:45:18
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:59:48
* @LastEditTime: 2024-06-11 11:59:34
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -23,21 +23,24 @@ import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@EqualsAndHashCode(callSuper=false)
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class RobotResponse extends BaseResponse {
// private String rid;
private String name;
private String nickname;
private String avatar;
private String description;
private String welcome;
// private String type;
private RobotTypeEnum type;
private boolean published;
private Boolean published;
private RobotServiceSettings serviceSettings;
private RobotLlm llm;
}

View File

@@ -0,0 +1,27 @@
package com.bytedesk.ai.robot;
import com.bytedesk.core.base.BaseResponse;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Builder
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class RobotResponseSimple extends BaseResponse {
private static final long serialVersionUID = 1L;
private String nickname;
private String avatar;
private RobotLlm llm;
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-03-22 16:44:41
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-17 23:57:32
* @LastEditTime: 2024-06-12 07:16:00
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -13,115 +13,200 @@
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.robot;
import java.util.Iterator;
import java.util.Optional;
import org.modelmapper.ModelMapper;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import com.bytedesk.ai.kb.Kb;
import com.bytedesk.ai.kb.KbService;
import com.bytedesk.ai.llm.LlmService;
import com.bytedesk.core.base.BaseService;
import com.bytedesk.core.constant.AvatarConsts;
import com.bytedesk.core.rbac.auth.AuthService;
import com.bytedesk.core.rbac.user.User;
import com.bytedesk.core.rbac.user.UserService;
import com.bytedesk.core.constant.I18Consts;
import com.bytedesk.core.constant.UserConsts;
import com.bytedesk.core.quick_button.QuickButton;
import com.bytedesk.core.quick_button.QuickButtonService;
import com.bytedesk.core.uid.UidUtils;
import com.bytedesk.core.utils.JsonResult;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class RobotService {
public class RobotService extends BaseService<Robot, RobotRequest, RobotResponse> {
private final RobotRepository robotRepository;
private final AuthService authService;
private final LlmService llmService;
private final KbService kbService;
private final UserService userService;
private final QuickButtonService quickButtonService;
private final ModelMapper modelMapper;
private final UidUtils uidUtils;
public Page<RobotResponse> query(RobotRequest robotRequest) {
@Override
public Page<RobotResponse> queryByOrg(RobotRequest request) {
User user = authService.getCurrentUser();
Pageable pageable = PageRequest.of(robotRequest.getPageNumber(), robotRequest.getPageSize(), Sort.Direction.DESC,
"id");
//
Page<Robot> robotPage = robotRepository.findByUser(user, pageable);
return robotPage.map(robot -> modelMapper.map(robot, RobotResponse.class));
}
public JsonResult<?> create(RobotRequest robotRequest) {
User user = authService.getCurrentUser();
if (robotRepository.existsByUserAndName(user, robotRequest.getName())) {
return JsonResult.error("机器人名称已存在");
}
//
Robot robot = modelMapper.map(robotRequest, Robot.class);
//
String rid = uidUtils.getCacheSerialUid();
robot.setUid(rid);
robot.setAvatar(AvatarConsts.DEFAULT_AVATAR_URL);
robot.setDescription("default robot description");
robot.setWelcome("您好,有什么可以帮您的?");
// robot.setType(null)
robot.setPublished(false);
robot.setLlm(llmService.getLlm("user"));
robot.setKb(kbService.getKb(rid));
robot.setUser(user);
Robot result = robotRepository.save(robot);
return JsonResult.success(result);
}
// @SuppressWarnings("null")
public void initData() {
Pageable pageable = PageRequest.of(request.getPageNumber(), request.getPageSize(), Direction.ASC,
"updatedAt");
Specification<Robot> specification = RobotSpecification.search(request);
Page<Robot> page = robotRepository.findAll(specification, pageable);
// Page<Robot> page = robotRepository.findByOrgUidAndDeleted(request.getOrgUid(), false, pageable);
return page.map(robot -> modelMapper.map(robot, RobotResponse.class));
}
@Override
public Page<RobotResponse> queryByUser(RobotRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'queryByUser'");
}
@Override
public RobotResponse create(RobotRequest request) {
if (existsByNicknameAndOrgUidAndDeleted(request.getNickname(), request.getOrgUid())) {
throw new RuntimeException("robot name already exists, please find another name");
}
Kb kb = kbService.getKb(request.getNickname(), request.getOrgUid());
RobotLlm llm = RobotLlm.builder().build();
//
Robot robot = Robot.builder()
// .nickname(request.getNickname())
.type(RobotTypeEnum.fromString(request.getType()))
.orgUid(request.getOrgUid())
.kb(kb)
.llm(llm)
.build();
robot.setUid(uidUtils.getCacheSerialUid());
robot.setNickname(request.getNickname());
robot.setAvatar(AvatarConsts.DEFAULT_AVATAR_URL);
return convertToResponse(save(robot));
}
@Override
public RobotResponse update(RobotRequest robotRequest) {
Optional<Robot> robotOptional = findByUid(robotRequest.getUid());
if (!robotOptional.isPresent()) {
throw new RuntimeException("robot " + robotRequest.getUid() + " not found");
}
//
Robot robot = robotOptional.get();
robot.setNickname(robotRequest.getNickname());
robot.setAvatar(robotRequest.getAvatar());
robot.setDescription(robotRequest.getDescription());
robot.setPublished(robotRequest.getPublished());
//
// robot.setServiceSettings(request.getServiceSettings());
RobotServiceSettings serviceSettings = modelMapper.map(
robotRequest.getServiceSettings(), RobotServiceSettings.class);
if (robotRequest.getServiceSettings().getQuickButtonUids() != null
&& robotRequest.getServiceSettings().getQuickButtonUids().size() > 0) {
Iterator<String> iterator = robotRequest.getServiceSettings().getQuickButtonUids().iterator();
while (iterator.hasNext()) {
String quickButtonUid = iterator.next();
Optional<QuickButton> quickButtonOptional = quickButtonService.findByUid(quickButtonUid);
if (quickButtonOptional.isPresent()) {
QuickButton quickButtonEntity = quickButtonOptional.get();
serviceSettings.getQuickButtons().add(quickButtonEntity);
}
}
}
robot.setServiceSettings(serviceSettings);
robot.setLlm(robotRequest.getLlm());
//
Robot updateRobot = save(robot);
if (updateRobot == null) {
throw new RuntimeException("update robot failed");
}
// TODO: 更新当前进行中会话的agent字段
return convertToResponse(updateRobot);
}
@Cacheable(value = "robot", key = "#uid", unless = "#result == null")
@Override
public Optional<Robot> findByUid(String uid) {
return robotRepository.findByUid(uid);
}
@Override
public Robot save(Robot entity) {
try {
return robotRepository.save(entity);
} catch (ObjectOptimisticLockingFailureException e) {
handleOptimisticLockingFailureException(e, entity);
}
return null;
}
@Override
public void deleteByUid(String uid) {
Optional<Robot> robotOptional = findByUid(uid);
robotOptional.ifPresent(robot -> {
robot.setDeleted(true);
save(robot);
});
}
@Override
public void delete(Robot entity) {
deleteByUid(entity.getUid());
}
@Override
public void handleOptimisticLockingFailureException(ObjectOptimisticLockingFailureException e, Robot entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'handleOptimisticLockingFailureException'");
}
@Override
public RobotResponse convertToResponse(Robot entity) {
return modelMapper.map(entity, RobotResponse.class);
}
private Boolean existsByNicknameAndOrgUidAndDeleted(String name, String orgUid) {
return robotRepository.existsByNicknameAndOrgUidAndDeleted(name, orgUid, false);
}
public void initData() {
if (robotRepository.count() > 0) {
return;
}
//
Optional<User> adminOptional = userService.getAdmin();
if (adminOptional.isPresent()) {
//
String rid = uidUtils.getCacheSerialUid();
Robot robot = Robot.builder()
// .rid(rid)
.name("客服机器人")
.avatar(AvatarConsts.DEFAULT_AVATAR_URL)
.description("客服机器人")
.welcome("欢迎使用客服机器人")
.published(false)
.type("service")
.llm(llmService.getLlm("user"))
.kb(kbService.getKb(rid))
.user(adminOptional.get())
.build();
robot.setUid(rid);
robotRepository.save(robot);
}
Kb kb = kbService.getKb(I18Consts.I18N_ROBOT_NICKNAME, UserConsts.DEFAULT_ORGANIZATION_UID);
RobotLlm llm = RobotLlm.builder().build();
Robot robot = Robot.builder()
// .nickname(I18Consts.I18N_ROBOT_NICKNAME)
.description(I18Consts.I18N_ROBOT_DESCRIPTION)
.type(RobotTypeEnum.SERVICE)
.orgUid(UserConsts.DEFAULT_ORGANIZATION_UID)
.kb(kb)
.llm(llm)
.build();
robot.setUid(UserConsts.DEFAULT_ROBOT_UID);
robot.setNickname(I18Consts.I18N_ROBOT_NICKNAME);
robot.setAvatar(AvatarConsts.DEFAULT_AVATAR_URL);
//
save(robot);
}
}

View File

@@ -0,0 +1,91 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-04 17:16:54
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-12 10:40:16
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.robot;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.bytedesk.core.constant.I18Consts;
import com.bytedesk.core.quick_button.QuickButton;
import com.bytedesk.core.thread.ThreadTypeEnum;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Builder
@Embeddable
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class RobotServiceSettings {
@Builder.Default
private String language = I18Consts.ZH_CN;
@Builder.Default
@Column(name = "is_auto_pop")
private boolean autoPop = false;
/**
* TODO: set different tips for different lang
*/
@Builder.Default
private boolean showTopTip = false;
@Builder.Default
@Column(length = 512)
private String topTip = I18Consts.I18N_TOP_TIP;
@Builder.Default
private String welcomeTip = I18Consts.I18N_WELCOME_TIP;
@Builder.Default
private String leavemsgTip = I18Consts.I18N_LEAVEMSG_TIP;
/** auto close time in min - 默认自动关闭时间,单位分钟 */
@Builder.Default
private Double autoCloseMin = Double.valueOf(25);
@Builder.Default
@OneToMany(fetch = FetchType.LAZY)
private List<QuickButton> quickButtons = new ArrayList<>();
// 是否允许转人工
@Builder.Default
private boolean allowTransferToAgent = true;
// 限制仅允许workgroup、appointed
@Builder.Default
private ThreadTypeEnum transferType = ThreadTypeEnum.WORKGROUP;
// agentUid or workgroupUid
private String transferToUid;
//
@Builder.Default
private boolean showLogo = true;
// 有效日期
private Date validateUntil;
}

View File

@@ -0,0 +1,91 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-04 17:16:54
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-11 12:29:46
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.robot;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.bytedesk.core.constant.I18Consts;
import com.bytedesk.core.thread.ThreadTypeEnum;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Builder
@Embeddable
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class RobotServiceSettingsRequest {
@Builder.Default
private String language = I18Consts.ZH_CN;
@Builder.Default
@Column(name = "is_auto_pop")
private boolean autoPop = false;
/**
* TODO: set different tips for different lang
*/
@Builder.Default
private boolean showTopTip = false;
@Builder.Default
@Column(length = 512)
private String topTip = I18Consts.I18N_TOP_TIP;
@Builder.Default
private String welcomeTip = I18Consts.I18N_WELCOME_TIP;
@Builder.Default
private String leavemsgTip = I18Consts.I18N_LEAVEMSG_TIP;
/** auto close time in min - 默认自动关闭时间,单位分钟 */
@Builder.Default
private Double autoCloseMin = Double.valueOf(25);
@Builder.Default
@OneToMany(fetch = FetchType.LAZY)
private List<String> quickButtonUids = new ArrayList<>();
// 是否允许转人工
@Builder.Default
private boolean allowTransferToAgent = true;
// 限制仅允许workgroup、appointed
@Builder.Default
private ThreadTypeEnum transferType = ThreadTypeEnum.WORKGROUP;
// agentUid or workgroupUid
private String transferToUid;
//
@Builder.Default
private boolean showLogo = true;
// private String logoUrl;
// 有效日期
private Date validateUntil;
}

View File

@@ -0,0 +1,92 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-04 17:16:54
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-12 10:40:36
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.robot;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.bytedesk.core.constant.I18Consts;
import com.bytedesk.core.quick_button.QuickButton;
import com.bytedesk.core.thread.ThreadTypeEnum;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Builder
@Embeddable
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class RobotServiceSettingsResponse {
@Builder.Default
private String language = I18Consts.ZH_CN;
@Builder.Default
@Column(name = "is_auto_pop")
private boolean autoPop = false;
/**
* TODO: set different tips for different lang
*/
@Builder.Default
private boolean showTopTip = false;
@Builder.Default
@Column(length = 512)
private String topTip = I18Consts.I18N_TOP_TIP;
@Builder.Default
private String welcomeTip = I18Consts.I18N_WELCOME_TIP;
@Builder.Default
private String leavemsgTip = I18Consts.I18N_LEAVEMSG_TIP;
/** auto close time in min - 默认自动关闭时间,单位分钟 */
@Builder.Default
private Double autoCloseMin = Double.valueOf(25);
@Builder.Default
@OneToMany(fetch = FetchType.LAZY)
private List<QuickButton> quickButtons = new ArrayList<>();
// 是否允许转人工
@Builder.Default
private boolean allowTransferToAgent = true;
// 限制仅允许workgroup、appointed
@Builder.Default
private ThreadTypeEnum transferType = ThreadTypeEnum.WORKGROUP;
// agentUid or workgroupUid
private String transferToUid;
//
@Builder.Default
private boolean showLogo = true;
// private String logoUrl;
// 有效日期
private Date validateUntil;
}

View File

@@ -0,0 +1,39 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-12 09:07:53
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-12 09:08:56
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.robot;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.jpa.domain.Specification;
import com.bytedesk.core.base.BaseSpecification;
import jakarta.persistence.criteria.Predicate;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class RobotSpecification extends BaseSpecification {
public static Specification<Robot> search(RobotRequest request) {
log.info("request: {}", request);
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
predicates.addAll(getBasicPredicates(root, criteriaBuilder, request.getOrgUid()));
//
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}

View File

@@ -0,0 +1,40 @@
package com.bytedesk.ai.robot;
public enum RobotTypeEnum {
SERVICE(0), // 客服机器人
MARKETING(1); // 营销机器人
//
private final int value;
// 枚举构造器,每个枚举常量都有一个与之关联的整型值
RobotTypeEnum(int value) {
this.value = value;
}
// 获取枚举常量的整型值
public int getValue() {
return value;
}
// 根据整型值查找对应的枚举常量
public static RobotTypeEnum fromValue(int value) {
for (RobotTypeEnum type : RobotTypeEnum.values()) {
if (type.getValue() == value) {
return type;
}
}
throw new IllegalArgumentException("No enum constant with value " + value);
}
public static RobotTypeEnum fromString(String typeStr) {
// 使用try-catch处理可能的异常
try {
return RobotTypeEnum.valueOf(typeStr);
} catch (IllegalArgumentException e) {
// 处理错误,例如记录日志或抛出更具体的异常
throw new IllegalArgumentException("Invalid robot type: " + typeStr, e);
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-06 11:28:01
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-06 11:29:36
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.utils;
import org.modelmapper.ModelMapper;
import com.bytedesk.ai.robot.Robot;
import com.bytedesk.ai.robot.RobotResponse;
import com.bytedesk.ai.robot.RobotResponseSimple;
public class ConvertAiUtils {
private ConvertAiUtils() {
}
public static RobotResponse convertToRobotResponse(Robot entity) {
return new ModelMapper().map(entity, RobotResponse.class);
}
public static RobotResponseSimple convertToRobotResponseSimple(Robot entity) {
return new ModelMapper().map(entity, RobotResponseSimple.class);
}
}

View File

@@ -0,0 +1,83 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-31 10:53:11
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-31 11:17:06
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.zhipuai;
import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingModel;
import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingOptions;
import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zhipu.oapi.ClientV4;
/**
* https://docs.spring.io/spring-ai/reference/api/embeddings/zhipuai-embeddings.html
*/
@Configuration
public class ZhipuAiConfig {
@Value("${spring.ai.zhipu.api-key}")
String zhiPuAiApiKey;
@Bean
ZhiPuAiApi zhipuaiApi() {
// return new ZhiPuAiApi(System.getenv("ZHIPU_AI_API_KEY"));
return new ZhiPuAiApi(zhiPuAiApiKey);
}
@Bean
ZhiPuAiEmbeddingModel zhipuaiEmbeddingModel() {
return new ZhiPuAiEmbeddingModel(zhipuaiApi());
}
// https://open.bigmodel.cn/overview
@Bean
ZhiPuAiEmbeddingOptions ZhiPuAiEmbeddingOptions() {
return ZhiPuAiEmbeddingOptions.builder()
.withModel(ZhiPuAiApi.ChatModel.GLM_3_Turbo.getValue())
// .withModel("GLM-4")
.build();
}
@Bean
ZhiPuAiChatModel zhipuaiChatModel() {
return new ZhiPuAiChatModel(zhipuaiApi(), ZhiPuAiChatOptions.builder()
.withModel(ZhiPuAiApi.ChatModel.GLM_3_Turbo.getValue())
.withTemperature(0.4f)
.withMaxTokens(200)
.build());
}
@Bean
ZhiPuAiImageApi zhipuaiImageApi() {
return new ZhiPuAiImageApi(zhiPuAiApiKey);
}
@Bean
ZhiPuAiImageModel zhiPuAiImageModel() {
return new ZhiPuAiImageModel(zhipuaiImageApi());
}
@Bean
ClientV4 client() {
return new ClientV4.Builder(zhiPuAiApiKey).build();
}
}

View File

@@ -0,0 +1,138 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-31 11:00:20
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-06 10:48:32
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.zhipuai;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.image.ImagePrompt;
import org.springframework.ai.image.ImageResponse;
import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import com.bytedesk.core.utils.JsonResult;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
/**
* https://open.bigmodel.cn/dev/api#sdk_install
* https://github.com/MetaGLM/zhipuai-sdk-java-v4
*
* https://docs.spring.io/spring-ai/reference/api/chat/zhipuai-chat.html
*/
@Slf4j
@RestController
@RequestMapping("/visitor/api/v1/zhipuai")
@AllArgsConstructor
public class ZhipuAiController {
private final ZhiPuAiChatModel chatModel;
private final ZhiPuAiImageModel zhiPuAiImageModel;
private final ZhipuAiService zhipuAiService;
// http://localhost:9003/visitor/api/v1/zhipuai/chat
@GetMapping("/chat")
public ResponseEntity<?> generation() {
ChatResponse response = chatModel.call(
new Prompt(
"Generate the names of 5 famous pirates.",
ZhiPuAiChatOptions.builder()
.withModel(ZhiPuAiApi.ChatModel.GLM_3_Turbo.getValue())
.withTemperature(0.5f)
.build()));
return ResponseEntity.ok(JsonResult.success(response));
}
// http://localhost:9003/visitor/api/v1/zhipuai/generate?content=hello
@GetMapping("/generate")
public Map<?, ?> generate(@RequestParam(value = "content", defaultValue = "讲个笑话") String content) {
return Map.of("generation", chatModel.call(content));
}
// http://localhost:9003/visitor/api/v1/zhipuai/generateStream?content=讲个笑话
@GetMapping("/generateStream")
public Flux<ChatResponse> generateStream(
@RequestParam(value = "content", defaultValue = "讲个笑话") String content) {
var prompt = new Prompt(new UserMessage(content));
return chatModel.stream(prompt);
}
// http://localhost:9003/visitor/api/v1/zhipuai/image
@GetMapping("/image")
public ResponseEntity<?> image() {
ImageResponse response = zhiPuAiImageModel.call(new ImagePrompt("A light cream colored mini golden doodle"));
return ResponseEntity.ok(JsonResult.success(response));
}
//
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
// http://localhost:9003/visitor/api/v1/zhipuai/sse?uid=&sid=&content=hi
@GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public ResponseEntity<SseEmitter> sseEndpoint(
@RequestParam(value = "uid", required = true) String uid,
@RequestParam(value = "sid", required = true) String sid,
@RequestParam(value = "q", defaultValue = "讲个笑话") String question) {
// TODO: 根据uid和ip判断此visitor是否骚扰用户如果骚扰则拒绝响应
log.info("sseEndpoint sid: {}, uid: {}, question {}", sid, uid, question);
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
executorService.submit(() -> {
try {
// 调用你的服务方法来获取SSE数据
zhipuAiService.getSseAnswer(uid, sid, question, emitter);
//
// 将数据作为SSE事件发送
// emitter.send(SseEmitter.event().data(sseData));
// 完成后完成SSE流
// emitter.complete();
} catch (Exception e) {
// 如果发生错误,则发送错误事件
// emitter.send(SseEmitter.event().error(e));
emitter.completeWithError(e);
} finally {
// 确保清理资源
emitter.complete();
}
});
return ResponseEntity.ok().body(emitter);
}
// 销毁时关闭ExecutorService
public void destroy() {
executorService.shutdown();
}
}

View File

@@ -0,0 +1,210 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-05 15:39:22
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-11 11:43:13
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.ai.zhipuai;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import com.alibaba.fastjson2.JSON;
import com.bytedesk.ai.robot.RobotMessage;
import com.bytedesk.ai.robot.RobotResponseSimple;
// import com.bytedesk.ai.robot.RobotResponseSimple;
import com.bytedesk.core.enums.ClientEnum;
import com.bytedesk.core.thread.Thread;
import com.bytedesk.core.message.Message;
import com.bytedesk.core.message.MessageService;
import com.bytedesk.core.message.MessageStatusEnum;
import com.bytedesk.core.message.MessageTypeEnum;
import com.bytedesk.core.rbac.user.UserResponseSimple;
import com.bytedesk.core.thread.ThreadService;
import com.bytedesk.core.uid.UidUtils;
import com.bytedesk.core.utils.JsonResult;
import com.bytedesk.core.utils.JsonResultCodeEnum;
import com.zhipu.oapi.ClientV4;
// import com.zhipu.oapi.Constants;
import com.zhipu.oapi.service.v4.model.ChatCompletionRequest;
import com.zhipu.oapi.service.v4.model.ChatMessage;
import com.zhipu.oapi.service.v4.model.ChatMessageAccumulator;
import com.zhipu.oapi.service.v4.model.ChatMessageRole;
import com.zhipu.oapi.service.v4.model.Choice;
import com.zhipu.oapi.service.v4.model.ModelApiResponse;
import com.zhipu.oapi.service.v4.model.ModelData;
import io.reactivex.Flowable;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* https://open.bigmodel.cn/dev/api#sdk_install
* https://github.com/MetaGLM/zhipuai-sdk-java-v4
*
* https://docs.spring.io/spring-ai/reference/api/chat/zhipuai-chat.html
*/
@Slf4j
@Service
@AllArgsConstructor
public class ZhipuAiService {
private final ClientV4 client;
private final UidUtils uidUtils;
private final ThreadService threadService;
private final ModelMapper modelMapper;
private final MessageService messageService;
/**
* sse调用
*/
public void getSseAnswer(String uid, String sid, String question, SseEmitter emitter) {
String topic = sid + "/" + uid;
Thread thread = threadService.findByTopic(topic).orElseThrow(() -> new RuntimeException("thread with topic: " + topic+ " not found"));
RobotMessage robotMessage = RobotMessage.builder().question(question).build();
RobotResponseSimple robotSimple = JSON.parseObject(thread.getAgent(), RobotResponseSimple.class);
log.info("robotSimple {}", robotSimple);
UserResponseSimple user = modelMapper.map(thread.getAgent(), UserResponseSimple.class);
//
String messageUid = uidUtils.getCacheSerialUid();
Message message = Message.builder()
.type(MessageTypeEnum.QA)
.status(MessageStatusEnum.SENT)
.client(ClientEnum.SYSTEM)
.orgUid(thread.getOrgUid())
.user(JSON.toJSONString(user))
.build();
message.setUid(messageUid);
//
message.getThreads().add(thread);
if (!robotSimple.getLlm().isEnabled()) {
// 机器人未开启
message.setContent(JsonResultCodeEnum.ROBOT_DISABLED.getName());
try {
emitter.send(SseEmitter.event()
.data(JsonResult.success(
JsonResultCodeEnum.ROBOT_DISABLED.getName(), JsonResultCodeEnum.ROBOT_DISABLED.getValue(), message)));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 完成后完成SSE流
emitter.complete();
// TODO: 关键词匹配
return;
}
//
List<ChatMessage> messages = new ArrayList<>();
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), question);
messages.add(chatMessage);
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
// 模型名称
// .model(Constants.ModelChatGLM3TURBO)
.model(robotSimple.getLlm().getModel().getValue())
.temperature(robotSimple.getLlm().getTemperature())
.topP(robotSimple.getLlm().getTopP())
.stream(Boolean.TRUE)
.messages(messages)
.requestId(messageUid)
.build();
//
ModelApiResponse sseModelApiResp = client.invokeModelApi(chatCompletionRequest);
if (sseModelApiResp.isSuccess()) {
AtomicBoolean isFirst = new AtomicBoolean(true);
List<Choice> choices = new ArrayList<>();
ChatMessageAccumulator chatMessageAccumulator = mapStreamToAccumulator(sseModelApiResp.getFlowable())
.doOnNext(accumulator -> {
{
if (isFirst.getAndSet(false)) {
log.info("answer start: ");
robotMessage.clearAnswer();
message.setContent(JsonResultCodeEnum.ROBOT_ANSWER_START.getName());
emitter.send(SseEmitter.event().data(JsonResult.success(
JsonResultCodeEnum.ROBOT_ANSWER_START.getName(), JsonResultCodeEnum.ROBOT_ANSWER_START.getValue(), message)));
}
if (accumulator.getDelta() != null && accumulator.getDelta().getTool_calls() != null) {
String jsonString = JSON.toJSONString(accumulator.getDelta().getTool_calls());
log.info("tool_calls: " + jsonString);
}
if (accumulator.getDelta() != null && accumulator.getDelta().getContent() != null) {
String answerContent = accumulator.getDelta().getContent();
// delta {"role":"assistant","content":"告诉我","tool_calls":[]} answerContent 告诉我
robotMessage.appendAnswer(answerContent);
message.setContent(JSON.toJSONString(robotMessage));
log.info("delta {} answerContent {}", accumulator.getDelta().toString(), answerContent);
emitter.send(SseEmitter.event().data(JsonResult.success(
JsonResultCodeEnum.ROBOT_ANSWER_CONTINUE.getName(), JsonResultCodeEnum.ROBOT_ANSWER_CONTINUE.getValue(), message)));
}
}
})
.doOnComplete(() -> {
log.info("answer end");
message.setContent(JsonResultCodeEnum.ROBOT_ANSWER_END.getName());
emitter.send(SseEmitter.event().data(JsonResult.success(
JsonResultCodeEnum.ROBOT_ANSWER_END.getName(), JsonResultCodeEnum.ROBOT_ANSWER_END.getValue(), message)));
// 完成后完成SSE流
emitter.complete();
})
.lastElement()
.blockingGet();
ModelData data = new ModelData();
data.setChoices(choices);
data.setUsage(chatMessageAccumulator.getUsage());
data.setId(chatMessageAccumulator.getId());
data.setCreated(chatMessageAccumulator.getCreated());
data.setRequestId(chatCompletionRequest.getRequestId());
sseModelApiResp.setFlowable(null);// 打印前置空
sseModelApiResp.setData(data);
// 存储到数据库
robotMessage.setPromptTokens(chatMessageAccumulator.getUsage().getPromptTokens());
robotMessage.setCompletionTokens(chatMessageAccumulator.getUsage().getCompletionTokens());
robotMessage.setTotalTokens(chatMessageAccumulator.getUsage().getTotalTokens());
//
message.setContent(JSON.toJSONString(robotMessage));
messageService.save(message);
}
String result = JSON.toJSONString(sseModelApiResp);
// sse output:
// {"code":200,"data":{"array":false,"bigDecimal":false,"bigInteger":false,"binary":false,"boolean":false,"choices":[],
// "containerNode":true,"created":1717580268,"double":false,"empty":false,"float":false,"floatingPointNumber":false,
// "id":"8718088513501356715","int":false,"integralNumber":false,"long":false,"missingNode":false,"nodeType":"OBJECT",
// "null":false,"number":false,"object":true,"pojo":false,"request_id":"bytedesk-1717580267785","short":false,
// "textual":false,"usage":{"completion_tokens":28,"prompt_tokens":6,"total_tokens":34},"valueNode":false},"success":true}
log.info("sse output:" + result);
}
public static Flowable<ChatMessageAccumulator> mapStreamToAccumulator(Flowable<ModelData> flowable) {
return flowable.map(chunk -> {
return new ChatMessageAccumulator(chunk.getChoices().get(0).getDelta(), null, chunk.getChoices().get(0),
chunk.getUsage(), chunk.getCreated(), chunk.getId());
});
}
}

View File

@@ -1 +1,2 @@
spring.application.name=ai

BIN
modules/core/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-25 15:31:38
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 11:37:26
* @LastEditTime: 2024-06-07 14:44:36
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,9 +14,19 @@
*/
package com.bytedesk.core.action;
import com.bytedesk.core.base.BaseEntity;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
import com.bytedesk.core.base.BaseEntity;
import com.bytedesk.core.constant.BdConstants;
import com.bytedesk.core.constant.TypeConsts;
import com.bytedesk.core.rbac.user.User;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -31,7 +41,7 @@ import lombok.experimental.Accessors;
@Data
@Entity
@Builder
@EqualsAndHashCode(callSuper=false)
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@@ -39,20 +49,35 @@ import lombok.experimental.Accessors;
public class Action extends BaseEntity {
private static final long serialVersionUID = 1L;
// @NotBlank
// @Column(unique = true, nullable = false)
// private String aid;
private String title;
private String action;
private String description;
//
private String userUid;
private String ip;
// according to ip address
private String ipLocation;
@Builder.Default
@Column(name = TypeConsts.COLUMN_NAME_TYPE)
private String type = TypeConsts.ACTION_TYPE_LOG;
// action failed object
@Builder.Default
@Column(columnDefinition = TypeConsts.COLUMN_TYPE_JSON)
@JdbcTypeCode(SqlTypes.JSON)
private String extra = BdConstants.EMPTY_JSON_STRING;
// private String userUid;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
private User user;
private String orgUid;
@Builder.Default
private String platform = BdConstants.PLATFORM_BYTEDESK;
}

View File

@@ -20,8 +20,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.bytedesk.core.constant.TypeConsts;
/**
* 自定义操作日志记录注解
* record for action and failed operations
*
* @author jackning
*
@@ -29,11 +31,13 @@ import java.lang.annotation.Target;
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ActionLogAnnotation {
public @interface ActionAnnotation {
public String title() default "";
public String action() default "";
public String description() default "";
public String type() default TypeConsts.ACTION_TYPE_LOG;
}

View File

@@ -0,0 +1,123 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-17 16:53:12
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-30 14:03:30
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.action;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.bytedesk.core.ip.IpService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* 操作日志记录处理
*
* 注意:不能在类上注解@Async否则会获取不到 HttpServletRequestattributes为空。
* 如果不需要HttpServletRequest可以添加@Async注解
*
* @author jackning
*/
@Slf4j
// @Async
@Aspect
@Component
@AllArgsConstructor
public class ActionAspect {
private final ActionService actionService;
private final IpService ipService;
/**
* 处理请求前执行
*/
@Before(value = "@annotation(actionAnnotation)")
public void doBefore(JoinPoint joinPoint, ActionAnnotation actionAnnotation) {
log.debug("actionLog before: model {}, action {}", actionAnnotation.title(), actionAnnotation.action());
}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "@annotation(actionAnnotation)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, ActionAnnotation actionAnnotation, Object jsonResult) {
log.debug("actionLog after returning: title {}, action {}", actionAnnotation.title(), actionAnnotation.action());
//
ActionRequest actionRequest = ActionRequest.builder()
.title(actionAnnotation.title())
.action(actionAnnotation.action())
.description(actionAnnotation.description())
.build();
actionRequest.setType(actionAnnotation.type());
//
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取方法参数列表
Object[] args = joinPoint.getArgs();
// 遍历参数
for (int i = 0; i < args.length; i++) {
// 获取参数名
String paramName = signature.getParameterNames()[i];
// 获取参数值
Object paramValue = args[i];
// 参数名: authRequest, 参数值: AuthRequest(username=admin@email.com, password=admin,
// mobile=null, email=null, code=null, platform=bytedesk)
log.debug("TODO: 参数名: {}, 参数值: {}", paramName, paramValue);
}
//
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
// 注意:不能在类上注解@Async否则会获取不到 HttpServletRequestattributes为空
HttpServletRequest request = attributes.getRequest();
String ipAddress = request.getRemoteAddr();
String ip = ipService.getIp(request);
String ipLocation = ipService.getIpLocation(ip);
log.info("ipAddress {}, ip {}, ipLocation {}", ipAddress, ip, ipLocation);
actionRequest.setIp(ip);
actionRequest.setIpLocation(ipLocation);
// 接下来的操作...
} else {
// 处理非Web请求情况比如记录日志或者直接返回
}
//
actionService.create(actionRequest);
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param e 异常
*/
@AfterThrowing(value = "@annotation(actionAnnotation)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, ActionAnnotation actionAnnotation, Exception e) {
log.info("actionLog after throwing: model {}, action {}", actionAnnotation.title(), actionAnnotation.action());
// handleLog(joinPoint, actionAnnotation, e, null);
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-25 15:40:19
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-25 15:40:21
* @LastEditTime: 2024-05-31 07:39:58
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,6 +14,56 @@
*/
package com.bytedesk.core.action;
public class ActionController {
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bytedesk.core.base.BaseController;
import com.bytedesk.core.utils.JsonResult;
import lombok.AllArgsConstructor;
@RestController
@AllArgsConstructor
@RequestMapping("/api/v1/action")
public class ActionController extends BaseController<ActionRequest> {
private final ActionService actionService;
@GetMapping("/query/org")
@Override
public ResponseEntity<?> queryByOrg(ActionRequest request) {
Page<ActionResponse> page = actionService.queryByOrg(request);
return ResponseEntity.ok(JsonResult.success(page));
}
@Override
public ResponseEntity<?> query(ActionRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'query'");
}
@Override
public ResponseEntity<?> create(ActionRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'create'");
}
@Override
public ResponseEntity<?> update(ActionRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'update'");
}
@Override
public ResponseEntity<?> delete(ActionRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'delete'");
}
}

View File

@@ -0,0 +1,35 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-23 09:53:53
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-23 10:03:03
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.action;
import org.springframework.context.ApplicationEvent;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class ActionEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L;
private final ActionRequest actionRequest;
public ActionEvent(Object source, ActionRequest actionRequest) {
super(source);
this.actionRequest = actionRequest;
}
}

View File

@@ -0,0 +1,37 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-23 10:00:44
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-23 10:04:09
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.action;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@AllArgsConstructor
public class ActionListener {
private final ActionService actionService;
@EventListener
public void onActionEvent(ActionEvent event) {
log.info("Received event: {}", event.getActionRequest().getTitle());
// do something
actionService.create(event.getActionRequest());
}
}

View File

@@ -1,88 +0,0 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-17 16:53:12
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 12:33:32
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.action;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* 操作日志记录处理
*
* @author jackning
*/
@Slf4j
@Async
@Aspect
@Component
@AllArgsConstructor
public class ActionLogAspect {
private final ActionService actionService;
/**
* 处理请求前执行
*/
@Before(value = "@annotation(controllerLog)")
public void doBefore(JoinPoint joinPoint, ActionLogAnnotation controllerLog) {
log.debug("actionLog before: model {}, action {}", controllerLog.title(), controllerLog.action());
}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, ActionLogAnnotation controllerLog, Object jsonResult) {
log.debug("actionLog after returning: model {}, action {}, jsonResult {}", controllerLog.title(),
controllerLog.action(), jsonResult);
// handleLog(joinPoint, controllerLog, null, jsonResult);
// TODO: 记录具体用户
ActionRequest actionRequest = ActionRequest.builder()
.title(controllerLog.title())
.action(controllerLog.action())
.description(controllerLog.description())
.build();
actionService.create(actionRequest);
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param e 异常
*/
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, ActionLogAnnotation controllerLog, Exception e) {
log.info("actionLog after throwing: model {}, action {}", controllerLog.title(), controllerLog.action());
// handleLog(joinPoint, controllerLog, e, null);
}
// protected void handleLog(final JoinPoint joinPoint, ActionLog controllerLog, final Exception e, Object jsonResult) {
// log.debug("actionLog handleLog: model {}, action {}", controllerLog.title(), controllerLog.action());
// // TODO: write to db
// }
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-25 15:40:53
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-25 15:40:55
* @LastEditTime: 2024-05-30 16:10:00
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -15,8 +15,8 @@
package com.bytedesk.core.action;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface ActionRepository extends JpaRepository<Action, Long> {
public interface ActionRepository extends JpaRepository<Action, Long>, JpaSpecificationExecutor<Action> {
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-25 15:40:29
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 11:49:01
* @LastEditTime: 2024-05-23 23:37:53
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -15,6 +15,7 @@
package com.bytedesk.core.action;
import com.bytedesk.core.base.BaseRequest;
import com.bytedesk.core.constant.BdConstants;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -30,13 +31,24 @@ import lombok.experimental.Accessors;
public class ActionRequest extends BaseRequest {
private static final long serialVersionUID = 1L;
// private String aid;
private String title;
private String action;
private String description;
private String ip;
// according to ip address
private String ipLocation;
private String extra;
private String orgUid;
private String nickname;
@Builder.Default
private String platform = BdConstants.PLATFORM_BYTEDESK;
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-25 15:40:39
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 11:49:07
* @LastEditTime: 2024-05-28 11:05:24
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,26 +14,44 @@
*/
package com.bytedesk.core.action;
import com.bytedesk.core.base.BaseResponse;
import java.util.Date;
import com.bytedesk.core.base.BaseResponse;
import com.bytedesk.core.rbac.user.UserResponseSimple;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Builder
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@NoArgsConstructor
public class ActionResponse extends BaseResponse {
private static final long serialVersionUID = 1L;
// private String aid;
private String title;
private String action;
private String description;
private String ip;
// according to ip address
private String ipLocation;
private String type;
private String extra;
private UserResponseSimple user;
public Date createdAt;
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-25 15:41:47
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 11:37:38
* @LastEditTime: 2024-05-30 16:02:31
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,16 +14,28 @@
*/
package com.bytedesk.core.action;
import java.util.Optional;
import org.modelmapper.ModelMapper;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import com.bytedesk.core.base.BaseService;
import com.bytedesk.core.constant.UserConsts;
import com.bytedesk.core.rbac.auth.AuthService;
import com.bytedesk.core.rbac.user.User;
import com.bytedesk.core.uid.UidUtils;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class ActionService {
public class ActionService extends BaseService<Action, ActionRequest, ActionResponse> {
private final ActionRepository actionRepository;
@@ -31,20 +43,77 @@ public class ActionService {
private final UidUtils uidUtils;
public Action create(ActionRequest actionRequest) {
private final AuthService authService;
public ActionResponse create(ActionRequest actionRequest) {
Action action = modelMapper.map(actionRequest, Action.class);
action.setUid(uidUtils.getCacheSerialUid());
return save(action);
//
User user = authService.getCurrentUser();
if (user != null) {
action.setUser(user);
action.setOrgUid(user.getOrgUid());
} else {
action.setOrgUid(UserConsts.DEFAULT_ORGANIZATION_UID);
}
//
return convertToResponse(save(action));
}
public Action save(Action action) {
return actionRepository.save(action);
}
public ActionResponse convertToActionResponse(Action action) {
public ActionResponse convertToResponse(Action action) {
return modelMapper.map(action, ActionResponse.class);
}
@Override
public Page<ActionResponse> queryByOrg(ActionRequest request) {
Pageable pageable = PageRequest.of(request.getPageNumber(), request.getPageSize(), Direction.DESC, "updatedAt");
//
Specification<Action> spec = ActionSpecs.search(request);
Page<Action> page = actionRepository.findAll(spec, pageable);
return page.map(action -> convertToResponse(action));
}
@Override
public Page<ActionResponse> queryByUser(ActionRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'queryByUser'");
}
@Override
public Optional<Action> findByUid(String uid) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'findByUid'");
}
@Override
public ActionResponse update(ActionRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'update'");
}
@Override
public void deleteByUid(String uid) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'deleteByUid'");
}
@Override
public void delete(Action entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'delete'");
}
@Override
public void handleOptimisticLockingFailureException(ObjectOptimisticLockingFailureException e, Action entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'handleOptimisticLockingFailureException'");
}
}

View File

@@ -0,0 +1,60 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-30 15:49:34
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-05 23:10:25
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.action;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;
import com.bytedesk.core.base.BaseSpecification;
import jakarta.persistence.criteria.Predicate;
public class ActionSpecs extends BaseSpecification {
public static Specification<Action> search(ActionRequest request) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
predicates.addAll(getBasicPredicates(root, criteriaBuilder, request.getOrgUid()));
//
if (StringUtils.hasText(request.getTitle())) {
predicates.add(criteriaBuilder.like(root.get("title"), "%" + request.getTitle() + "%"));
}
if (StringUtils.hasText(request.getAction())) {
predicates.add(criteriaBuilder.like(root.get("action"), "%" + request.getAction() + "%"));
}
if (StringUtils.hasText(request.getDescription())) {
predicates.add(criteriaBuilder.like(root.get("description"), "%" + request.getDescription() + "%"));
}
if (StringUtils.hasText(request.getIp())) {
predicates.add(criteriaBuilder.like(root.get("ip"), "%" + request.getIp() + "%"));
}
if (StringUtils.hasText(request.getIpLocation())) {
predicates.add(criteriaBuilder.like(root.get("ipLocation"), "%" + request.getIpLocation() + "%"));
}
if (StringUtils.hasText(request.getType())) {
predicates.add(criteriaBuilder.like(root.get("type"), "%" + request.getType() + "%"));
}
if (StringUtils.hasText(request.getNickname())) {
predicates
.add(criteriaBuilder.like(root.get("user").get("nickname"), "%" + request.getNickname() + "%"));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-26 20:32:23
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:46:28
* @LastEditTime: 2024-06-07 14:52:08
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -15,6 +15,9 @@
package com.bytedesk.core.asistant;
import com.bytedesk.core.base.BaseEntity;
import com.bytedesk.core.constant.AvatarConsts;
import com.bytedesk.core.constant.I18Consts;
import com.bytedesk.core.constant.TypeConsts;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -38,25 +41,24 @@ import lombok.experimental.Accessors;
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners({ AsistantListener.class })
// @DiscriminatorValue("Asistant")
@Table(name = "core_asistant")
public class Asistant extends BaseEntity {
private static final long serialVersionUID = 1L;
// @NotBlank
// @Column(unique = true, nullable = false, length = 127)
// private String aid;
private String topic;
@Column(name = "by_type")
@Column(name = TypeConsts.COLUMN_NAME_TYPE)
private String type;
private String name;
private String avatar;
private String description;
private String nickname;
@Builder.Default
private String avatar = AvatarConsts.DEFAULT_AVATAR_URL;
@Builder.Default
private String description = I18Consts.I18N_USER_DESCRIPTION;
/** belong to org */
private String orgUid;

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-27 12:09:59
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-27 13:00:43
* @LastEditTime: 2024-06-04 15:45:47
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -23,11 +23,11 @@ import lombok.extern.slf4j.Slf4j;
@Component
public class AsistantListener {
@PostPersist
public void onPostPersist(Asistant asistant) {
log.debug("AsistantListener: onPostPersist {}", asistant.getName());
log.debug("AsistantListener: onPostPersist {}", asistant.getNickname());
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-26 21:05:09
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:45:56
* @LastEditTime: 2024-06-04 15:31:02
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -31,7 +31,7 @@ public class AsistantRequest extends BaseRequest {
private String topic;
private String name;
private String nickname;
private String avatar;

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-26 21:05:21
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:42:21
* @LastEditTime: 2024-06-04 15:30:55
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -33,7 +33,7 @@ public class AsistantResponse extends BaseResponse {
private String type;
private String name;
private String nickname;
private String avatar;

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-26 21:04:54
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-08 10:25:53
* @LastEditTime: 2024-06-04 15:31:23
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -81,50 +81,17 @@ public class AsistantService {
return;
}
// Optional<User> adminOptional = userService.getAdmin();
AsistantRequest asistantRequest = AsistantRequest.builder()
.topic(TopicConsts.TOPIC_FILE_ASISTANT)
.name(I18Consts.I18N_FILE_ASISTANT_NAME)
.nickname(I18Consts.I18N_FILE_ASISTANT_NAME)
.avatar(AvatarConsts.DEFAULT_FILE_ASISTANT_AVATAR_URL)
.description(I18Consts.I18N_FILE_ASISTANT_DESCRIPTION)
// .orgUid(adminOptional.get().getOrgUid())
.orgUid(UserConsts.DEFAULT_ORGANIZATION_UID)
.build();
asistantRequest.setUid(UserConsts.DEFAULT_FILE_ASISTANT_UID);
asistantRequest.setType(TypeConsts.TYPE_SYSTEM);
create(asistantRequest);
// 方便测试,默认给每个初始用户生成一个跟 文件助手 的对话
// UserRequest userRequest = new UserRequest();
// userRequest.setPageNumber(0);
// userRequest.setPageSize(10);
// //
// Page<User> userPage = userService.query(userRequest);
// userPage.forEach(user -> {
// //
// UserResponseSimple userSimple = UserResponseSimple.builder()
// // .uid(asistantRequest.getAid())
// .nickname(asistantRequest.getName())
// .avatar(asistantRequest.getAvatar())
// .build();
// userSimple.setUid(asistantRequest.getUid());
// //
// Thread thread = Thread.builder()
// // .tid(uidUtils.getCacheSerialUid())
// .type(ThreadTypeConsts.ASISTANT)
// .topic(TopicConsts.TOPIC_FILE_ASISTANT + "/" + user.getUid())
// .status(StatusConsts.THREAD_STATUS_INIT)
// .client(TypeConsts.TYPE_SYSTEM)
// .user(JSON.toJSONString(userSimple))
// .owner(user)
// .orgUid(asistantRequest.getOrgUid())
// .build();
// thread.setUid(uidUtils.getCacheSerialUid());
// threadService.save(thread);
// });
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-10 12:16:43
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-14 11:06:53
* @LastEditTime: 2024-05-31 07:39:48
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -29,7 +29,7 @@ public abstract class BaseController<T> {
* @param request
* @return
*/
@GetMapping("/org")
@GetMapping("/query/org")
abstract public ResponseEntity<?> queryByOrg(T request);
/**
@@ -67,11 +67,4 @@ public abstract class BaseController<T> {
@PostMapping("/delete")
abstract public ResponseEntity<?> delete(@RequestBody T request);
/**
* filter
*
* @return json
*/
@GetMapping("/filter")
abstract public ResponseEntity<?> filter(T filterParam);
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-01-29 16:21:24
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-11 15:36:45
* @LastEditTime: 2024-05-24 21:25:26
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -23,7 +23,7 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public abstract class BaseRequest implements Serializable {
private String uid;
public String uid;
public int pageNumber;

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-10 12:13:37
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-15 14:19:12
* @LastEditTime: 2024-05-24 15:48:02
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -36,7 +36,7 @@ public abstract class BaseService<T, TRequest, TResponse> {
abstract public T save(T entity);
abstract public void deleteByUid(TRequest request);
abstract public void deleteByUid(String uid);
abstract public void delete(T entity);

View File

@@ -0,0 +1,32 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-30 15:59:30
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-07 15:03:22
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.base;
import java.util.ArrayList;
import java.util.List;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
public abstract class BaseSpecification {
public static List<Predicate> getBasicPredicates(Root<?> root, CriteriaBuilder criteriaBuilder, String orgUid) {
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.equal(root.get("orgUid"), orgUid));
predicates.add(criteriaBuilder.equal(root.get("deleted"), false));
return predicates;
}
}

View File

@@ -0,0 +1,37 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-31 12:53:03
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-04 16:18:07
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.base;
import com.bytedesk.core.constant.AvatarConsts;
import jakarta.persistence.MappedSuperclass;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper=false)
// @Entity
// @Table(name = "core_base_user")
// @Inheritance()
// @DiscriminatorColumn(name = "user_type")
@MappedSuperclass
public abstract class BaseUser extends BaseEntity {
private String nickname;
private String avatar = AvatarConsts.DEFAULT_AVATAR_URL;
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-03 18:13:55
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-20 10:01:28
* @LastEditTime: 2024-06-11 11:52:28
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -19,6 +19,7 @@ import java.util.ArrayList;
import com.bytedesk.core.base.BaseEntity;
import com.bytedesk.core.constant.BdConstants;
import com.bytedesk.core.constant.TypeConsts;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.CascadeType;
@@ -50,9 +51,9 @@ public class Category extends BaseEntity {
// private String description;
@Column(name = "by_type")
@Column(name = TypeConsts.COLUMN_NAME_TYPE)
private String type;
private String icon;
// @Column(unique = true)
@@ -62,7 +63,7 @@ public class Category extends BaseEntity {
* 排序
*/
@Builder.Default
private Integer orderNo = 0;
private int orderNo = 0;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@@ -75,7 +76,7 @@ public class Category extends BaseEntity {
private List<Category> children = new ArrayList<>();
@Builder.Default
private String platform = BdConstants.PLATFORM_BYTEDESK;
private String platform = BdConstants.PLATFORM_BYTEDESK;
/** belong to org */
private String orgUid;

View File

@@ -0,0 +1,14 @@
package com.bytedesk.core.category;
public class CategoryConsts {
private CategoryConsts() {
}
//
public static final String CATEGORY_TYPE_QUICK_REPLY = "quick_reply";
public static final String CATEGORY_TYPE_FAQ = "faq";
//
public static final String CATEGORY_TYPE_HELP_DOC = "help_doc";
public static final String CATEGORY_TYPE_ROBOT_KB = "robot_kb";
public static final String CATEGORY_TYPE_BLOG = "blog";
public static final String CATEGORY_TYPE_EMAIL = "email";
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-11 18:21:26
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-20 12:51:23
* @LastEditTime: 2024-06-08 16:47:17
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,11 +14,16 @@
*/
package com.bytedesk.core.category;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bytedesk.core.base.BaseController;
import com.bytedesk.core.utils.JsonResult;
import lombok.AllArgsConstructor;
@@ -27,12 +32,15 @@ import lombok.AllArgsConstructor;
@RequestMapping("/api/v1/category")
public class CategoryController extends BaseController<CategoryRequest> {
// private final CategoryService categoryService;
private final CategoryService categoryService;
@GetMapping("/query/org")
@Override
public ResponseEntity<?> queryByOrg(CategoryRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'queryByOrg'");
Page<CategoryResponse> page = categoryService.queryByOrg(request);
return ResponseEntity.ok(JsonResult.success(page));
}
@Override
@@ -41,31 +49,32 @@ public class CategoryController extends BaseController<CategoryRequest> {
throw new UnsupportedOperationException("Unimplemented method 'query'");
}
@PostMapping("/create")
@Override
public ResponseEntity<?> create(CategoryRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'create'");
public ResponseEntity<?> create(@RequestBody CategoryRequest request) {
CategoryResponse response = categoryService.create(request);
return ResponseEntity.ok(JsonResult.success(response));
}
@PostMapping("/update")
@Override
public ResponseEntity<?> update(CategoryRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'update'");
public ResponseEntity<?> update(@RequestBody CategoryRequest request) {
CategoryResponse response = categoryService.update(request);
return ResponseEntity.ok(JsonResult.success(response));
}
@PostMapping("/delete")
@Override
public ResponseEntity<?> delete(CategoryRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'delete'");
public ResponseEntity<?> delete(@RequestBody CategoryRequest request) {
categoryService.deleteByUid(request.getUid());
return ResponseEntity.ok(JsonResult.success("delete success", request.getUid()));
}
@Override
public ResponseEntity<?> filter(CategoryRequest filterParam) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'filter'");
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-11 18:21:36
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-20 14:01:05
* @LastEditTime: 2024-06-08 13:29:49
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -18,12 +18,10 @@ import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface CategoryRepository extends JpaRepository<Category, Long> {
public interface CategoryRepository extends JpaRepository<Category, Long>, JpaSpecificationExecutor<Category> {
Optional<Category> findByUid(String uid);
List<Category> findByParentAndPlatformOrderByOrderNoAsc(Category parent, String platform);
Boolean existsByPlatform(String platform);
// Boolean existsByPlatform(String platform);
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-11 18:21:44
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-18 18:11:14
* @LastEditTime: 2024-06-08 15:24:07
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,9 +14,11 @@
*/
package com.bytedesk.core.category;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
import com.bytedesk.core.base.BaseRequest;
import com.bytedesk.core.constant.BdConstants;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -37,9 +39,15 @@ public class CategoryRequest extends BaseRequest {
private String icon;
private Integer orderNo;
@Builder.Default
private Integer orderNo = 0;
private String platform;
@Builder.Default
private String platform = BdConstants.PLATFORM_BYTEDESK;
private Set<String> children;
@Builder.Default
private List<String> children = new ArrayList<>();
/** belong to org */
private String orgUid;
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-11 18:21:53
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-18 18:15:43
* @LastEditTime: 2024-06-08 16:54:49
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,8 +14,6 @@
*/
package com.bytedesk.core.category;
import java.util.List;
import com.bytedesk.core.base.BaseResponse;
import lombok.AllArgsConstructor;
@@ -41,7 +39,5 @@ public class CategoryResponse extends BaseResponse {
private Integer orderNo;
// private String platform;
private List<CategoryResponse> children;
// private List<CategoryResponse> children;
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-11 18:22:04
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-18 18:19:19
* @LastEditTime: 2024-06-08 16:54:59
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -21,10 +21,15 @@ import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import com.bytedesk.core.base.BaseService;
import com.bytedesk.core.uid.UidUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -37,6 +42,8 @@ public class CategoryService extends BaseService<Category, CategoryRequest, Cate
private final CategoryRepository categoryRepository;
private final ModelMapper modelMapper;
private final UidUtils uidUtils;
public List<CategoryResponse> findByNullParent(String platform) {
// 一级分类
@@ -57,8 +64,15 @@ public class CategoryService extends BaseService<Category, CategoryRequest, Cate
@Override
public Page<CategoryResponse> queryByOrg(CategoryRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'queryByOrg'");
Pageable pageable = PageRequest.of(request.getPageNumber(), request.getPageSize(), Sort.Direction.ASC,
"updatedAt");
Specification<Category> specs = CategorySpecification.search(request);
Page<Category> page = categoryRepository.findAll(specs, pageable);
return page.map(this::convertToResponse);
}
@Override
@@ -74,25 +88,56 @@ public class CategoryService extends BaseService<Category, CategoryRequest, Cate
@Override
public CategoryResponse create(CategoryRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'create'");
Category category = modelMapper.map(request, Category.class);
category.setUid(uidUtils.getCacheSerialUid());
Category newCategory = save(category);
return convertToResponse(newCategory);
}
@Override
public CategoryResponse update(CategoryRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'update'");
Optional<Category> category = findByUid(request.getUid());
if (!category.isPresent()) {
throw new RuntimeException("category not found");
}
Category entity = category.get();
// modelMapper.map(request, entity);
entity.setName(request.getName());
entity.setIcon(request.getIcon());
entity.setType(request.getType());
// entity.setPlatform(request.getPlatform());
// TODO: children
Category newCategory = save(entity);
if (newCategory == null) {
throw new RuntimeException("category save error");
}
return convertToResponse(newCategory);
}
@Override
public Category save(Category entity) {
return categoryRepository.save(entity);
try {
return categoryRepository.save(entity);
} catch (ObjectOptimisticLockingFailureException e) {
handleOptimisticLockingFailureException(e, entity);
}
return null;
}
@Override
public void deleteByUid(CategoryRequest request) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'deleteByUid'");
public void deleteByUid(String uid) {
Optional<Category> category = findByUid(uid);
if (category.isPresent()) {
category.get().setDeleted(true);
save(category.get());
}
}
@Override
@@ -113,7 +158,8 @@ public class CategoryService extends BaseService<Category, CategoryRequest, Cate
log.info("{} children length {}", entity.getName(), entity.getChildren().size());
response.setChildren(convertToResponseList(entity.getChildren()));
// response.setChildren(convertToResponseList(entity.getChildren()));
return response;
}
@@ -126,9 +172,9 @@ public class CategoryService extends BaseService<Category, CategoryRequest, Cate
// }
//
public Boolean existsByPlatform(String platform) {
return categoryRepository.existsByPlatform(platform);
}
// public Boolean existsByPlatform(String platform) {
// return categoryRepository.existsByPlatform(platform);
// }
}

View File

@@ -0,0 +1,47 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-08 12:49:00
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-08 12:50:11
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.category;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;
import com.bytedesk.core.base.BaseSpecification;
import jakarta.persistence.criteria.Predicate;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CategorySpecification extends BaseSpecification {
public static Specification<Category> search(CategoryRequest request) {
log.info("request: {}", request);
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
predicates.addAll(getBasicPredicates(root, criteriaBuilder, request.getOrgUid()));
//
if (StringUtils.hasText(request.getName())) {
predicates.add(criteriaBuilder.like(root.get("name"), "%" + request.getName() + "%"));
}
if (StringUtils.hasText(request.getType())) {
predicates.add(criteriaBuilder.like(root.get("type"), "%" + request.getType() + "%"));
}
//
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-26 20:34:52
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:47:12
* @LastEditTime: 2024-06-07 14:52:21
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -15,6 +15,9 @@
package com.bytedesk.core.channel;
import com.bytedesk.core.base.BaseEntity;
import com.bytedesk.core.constant.AvatarConsts;
import com.bytedesk.core.constant.I18Consts;
import com.bytedesk.core.constant.TypeConsts;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -37,28 +40,27 @@ import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners({ChannelListener.class})
@EntityListeners({ ChannelListener.class })
// @DiscriminatorValue("Channel")
@Table(name = "core_channel")
public class Channel extends BaseEntity {
private static final long serialVersionUID = 1L;
// @Column(unique = true, nullable = false, length = 127)
// private String cid;
private String topic;
@Column(name = "by_type")
@Column(name = TypeConsts.COLUMN_NAME_TYPE)
private String type;
private String name;
private String avatar;
private String description;
private String nickname;
@Builder.Default
private String avatar = AvatarConsts.DEFAULT_AVATAR_URL;
@Builder.Default
private String description = I18Consts.I18N_USER_DESCRIPTION;
/** belong to org */
private String orgUid;
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-28 11:19:41
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-28 11:19:44
* @LastEditTime: 2024-06-04 15:31:53
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -25,7 +25,7 @@ public class ChannelListener {
@PostPersist
public void onPostPersist(Channel channel) {
log.info("onPostPersist: {}", channel.getName());
log.info("onPostPersist: {}", channel.getNickname());
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-26 21:07:10
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-04 10:47:25
* @LastEditTime: 2024-06-04 15:31:58
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -31,7 +31,7 @@ public class ChannelRequest extends BaseRequest {
private String topic;
private String name;
private String nickname;
private String avatar;

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-26 21:07:22
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-28 10:55:42
* @LastEditTime: 2024-06-04 15:32:03
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -33,7 +33,7 @@ public class ChannelResponse extends BaseResponse {
private String type;
private String name;
private String nickname;
private String avatar;

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-26 21:06:12
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-08 10:16:34
* @LastEditTime: 2024-06-04 15:32:11
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -73,22 +73,16 @@ public class ChannelService {
if (channelRepository.count() > 0) {
return;
}
// Optional<User> adminOptional = userService.getAdmin();
ChannelRequest channelRequest = ChannelRequest.builder()
.topic(TopicConsts.TOPIC_SYSTEM_NOTIFICATION)
.name(I18Consts.I18N_SYSTEM_NOTIFICATION_NAME)
.nickname(I18Consts.I18N_SYSTEM_NOTIFICATION_NAME)
.avatar(AvatarConsts.DEFAULT_SYSTEM_NOTIFICATION_AVATAR_URL)
.description(I18Consts.I18N_SYSTEM_NOTIFICATION_DESCRIPTION)
// .orgUid(adminOptional.get().getOrgUid())
.orgUid(UserConsts.DEFAULT_ORGANIZATION_UID)
.build();
channelRequest.setType(TypeConsts.TYPE_SYSTEM);
create(channelRequest);
//
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-01-30 09:14:39
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-10 15:37:48
* @LastEditTime: 2024-06-03 12:08:11
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,6 +14,9 @@
*/
package com.bytedesk.core.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@@ -24,20 +27,28 @@ import lombok.Data;
@ConfigurationProperties(prefix = "bytedesk")
public class BytedeskProperties {
private boolean debug = true;
private Boolean debug;
private String username;
private String password;
private String nickname;
// private String username;
private String email;
private String password;
private String passwordDefault;
private String nickname;
private String mobile;
private String company;
private List<String> mobileWhitelist = new ArrayList<>();
private String mobileCode;
private String organizationName;
private String organizationCode;
// private String timezone;
// cors
@@ -57,4 +68,9 @@ public class BytedeskProperties {
private String uploadDir;
private String uploadUrl;
//
public Boolean isInWhitelist(String mobile) {
return this.mobileWhitelist.contains(mobile);
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-16 18:19:59
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-19 21:51:00
* @LastEditTime: 2024-05-23 20:51:18
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -30,7 +30,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
import static org.springframework.security.config.Customizer.withDefaults;
import com.bytedesk.core.rbac.auth.AuthEntryPoint;
import com.bytedesk.core.rbac.auth.AuthJwtTokenFilter;
import com.bytedesk.core.rbac.auth.AuthTokenFilter;
import com.bytedesk.core.rbac.user.UserDetailsServiceImpl;
/**
@@ -66,7 +66,7 @@ public class SecurityConfig {
.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin().disable()))
.authenticationProvider(authenticationProvider())
// .oauth2ResourceServer((oauth2) -> oauth2.jwt(withDefaults()))
.addFilterBefore(authJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
.addFilterBefore(authTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//
return http.build();
}
@@ -77,8 +77,8 @@ public class SecurityConfig {
}
@Bean
public AuthJwtTokenFilter authJwtTokenFilter() {
return new AuthJwtTokenFilter();
public AuthTokenFilter authTokenFilter() {
return new AuthTokenFilter();
}
/**

View File

@@ -26,8 +26,7 @@ public class BdConstants {
* Path separator.
*/
public static final String FILE_SEPARATOR = File.separator;
public static final String DEFAULT_USER_DESCRIPTION = "user default description";
// public static final String DEFAULT_USER_DESCRIPTION = "user default description";
/**
* 是否私有部署

View File

@@ -1,109 +1,109 @@
package com.bytedesk.core.constant;
/**
*
* @author xiaper.io
*/
public class ClientConsts {
// /**
// *
// * @author xiaper.io
// */
// public class ClientConsts {
// Prevents instantiation
private ClientConsts() {}
// // Prevents instantiation
// private ClientConsts() {}
// 访客端
/**
* 访客pc网站
*/
public static String CLIENT_WEB = "web";
/**
* 访客手机网站
*/
public static String CLIENT_WAP = "wap";
/**
* 访客安卓
*/
public static String CLIENT_ANDROID = "android";
/**
* 访客苹果
*/
public static String CLIENT_IOS = "ios";
/**
* uniapp
*/
public static String CLIENT_UNIAPP = "uniapp";
/**
* Flutter客户端
*/
public static String CLIENT_FLUTTER = "flutter";
//
public static String CLIENT_FLUTTER_ANDROID = "flutter_android";
public static String CLIENT_FLUTTER_IOS = "flutter_ios";
public static String CLIENT_FLUTTER_WEB = "flutter_web";
//
public static String CLIENT_FLUTTER_ANDROID_SCHOOL = "flutter_android_school";
public static String CLIENT_FLUTTER_IOS_SCHOOL = "flutter_ios_school";
public static String CLIENT_FLUTTER_WEB_SCHOOL = "flutter_web_school";
/**
* 微信
*/
public static String CLIENT_WECHAT = "wechat";
/**
* 企业微信
*/
public static String CLIENT_WECHAT_WORK = "wechat_work";
/**
* 访客小程序
*/
public static String CLIENT_WECHAT_MINI = "wechat_mini";
/**
* 访客微信公众号
*/
public static String CLIENT_WECHAT_MP = "wechat_mp";
/**
* 微信客服
*/
public static String CLIENT_WECHAT_KEFU = "wechat_kefu";
/**
* 访客微信自定义菜单
*/
public static String CLIENT_WECHAT_URL = "wechat_url";
// // 访客端
// /**
// * 访客pc网站
// */
// public static String CLIENT_WEB = "web";
// /**
// * 访客手机网站
// */
// public static String CLIENT_WAP = "wap";
// /**
// * 访客安卓
// */
// public static String CLIENT_ANDROID = "android";
// /**
// * 访客苹果
// */
// public static String CLIENT_IOS = "ios";
// /**
// * uniapp
// */
// public static String CLIENT_UNIAPP = "uniapp";
// /**
// * Flutter客户端
// */
// public static String CLIENT_FLUTTER = "flutter";
// //
// public static String CLIENT_FLUTTER_ANDROID = "flutter_android";
// public static String CLIENT_FLUTTER_IOS = "flutter_ios";
// public static String CLIENT_FLUTTER_WEB = "flutter_web";
// //
// public static String CLIENT_FLUTTER_ANDROID_SCHOOL = "flutter_android_school";
// public static String CLIENT_FLUTTER_IOS_SCHOOL = "flutter_ios_school";
// public static String CLIENT_FLUTTER_WEB_SCHOOL = "flutter_web_school";
// /**
// * 微信
// */
// public static String CLIENT_WECHAT = "wechat";
// /**
// * 企业微信
// */
// public static String CLIENT_WECHAT_WORK = "wechat_work";
// /**
// * 访客小程序
// */
// public static String CLIENT_WECHAT_MINI = "wechat_mini";
// /**
// * 访客微信公众号
// */
// public static String CLIENT_WECHAT_MP = "wechat_mp";
// /**
// * 微信客服
// */
// public static String CLIENT_WECHAT_KEFU = "wechat_kefu";
// /**
// * 访客微信自定义菜单
// */
// public static String CLIENT_WECHAT_URL = "wechat_url";
// 客服端
/**
* Windwow客服端
*/
public static String CLIENT_WINDOW_ADMIN = "window_admin";
/**
* MAC客服端
*/
public static String CLIENT_MAC_ADMIN = "mac_admin";
/**
* 安卓手机客服端
*/
public static String CLIENT_ANDROID_ADMIN = "android_admin";
/**
* 苹果手机客服端
*/
public static String CLIENT_IOS_ADMIN = "ios_admin";
/**
* web客服端
*/
public static String CLIENT_WEB_ADMIN = "web_admin";
/**
* 小程序客服端
*/
public static String CLIENT_WECHAT_MINI_ADMIN = "wechat_mini_admin";
/**
* 系统端
*/
public static String CLIENT_SYSTEM = "system";
/**
* 小程序非官方接口,自定义接口
*/
public static String CLIENT_WECHAT_MINI_STOMP = "wechat_mini_stomp";
// // 客服端
// /**
// * Windwow客服端
// */
// public static String CLIENT_WINDOW_ADMIN = "window_admin";
// /**
// * MAC客服端
// */
// public static String CLIENT_MAC_ADMIN = "mac_admin";
// /**
// * 安卓手机客服端
// */
// public static String CLIENT_ANDROID_ADMIN = "android_admin";
// /**
// * 苹果手机客服端
// */
// public static String CLIENT_IOS_ADMIN = "ios_admin";
// /**
// * web客服端
// */
// public static String CLIENT_WEB_ADMIN = "web_admin";
// /**
// * 小程序客服端
// */
// public static String CLIENT_WECHAT_MINI_ADMIN = "wechat_mini_admin";
// /**
// * 系统端
// */
// public static String CLIENT_SYSTEM = "system";
// /**
// * 小程序非官方接口,自定义接口
// */
// public static String CLIENT_WECHAT_MINI_STOMP = "wechat_mini_stomp";
}
// }

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-04-26 22:25:47
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-10 15:56:06
* @LastEditTime: 2024-06-12 07:07:56
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -16,16 +16,17 @@ package com.bytedesk.core.constant;
// 国际化常量
public class I18Consts {
private I18Consts() {
}
// public static final String EN = "en";
public static final String EN = "en";
// public static final String ZH = "zh";
//
public static final String EN_US = "en-US";
public static final String ZH_CN = "zh-CN";
public static final String ZH_TW = "zh-TW";
public static final String I18N_PREFIX = "i18n_";
// "文件助手"
public static final String I18N_FILE_ASISTANT_NAME = I18N_PREFIX + "file_asistant";
@@ -34,9 +35,37 @@ public class I18Consts {
// 系统通知
public static final String I18N_SYSTEM_NOTIFICATION_NAME = I18N_PREFIX + "system_notification";
public static final String I18N_SYSTEM_NOTIFICATION_DESCRIPTION = I18N_PREFIX + "system_notification_description";
//
//
public static final String I18N_THREAD_CONTENT_IMAGE = I18N_PREFIX + "thread_content_image";
public static final String I18N_THREAD_CONTENT_FILE = I18N_PREFIX + "thread_content_file";
//
public static final String I18N_WELCOME_TIP = I18N_PREFIX + "welcome_tip";
public static final String I18N_TOP_TIP = I18N_PREFIX + "top_tip";
public static final String I18N_LEAVEMSG_TIP = I18N_PREFIX + "leavemsg_tip";
//
public static final String I18N_WORKGROUP_NICKNAME = I18N_PREFIX + "workgroup_nickname";
public static final String I18N_WORKGROUP_DESCRIPTION = I18N_PREFIX + "workgroup_description";
//
public static final String I18N_AGENT_NICKNAME = I18N_PREFIX + "agent_nickname";
public static final String I18N_AGENT_DESCRIPTION = I18N_PREFIX + "agent_description";
public static final String I18N_AGENT_OFFLINE = I18N_PREFIX + "agent_offline";
public static final String I18N_AGENT_UNAVAILABLE = I18N_PREFIX + "agent_unavailable";
public static final String I18N_AGENT_AVAILABLE = I18N_PREFIX + "agent_available";
//
public static final String I18N_USER_NICKNAME = I18N_PREFIX + "user_nickname";
public static final String I18N_USER_DESCRIPTION = I18N_PREFIX + "user_description";
//
public static final String I18N_DESCRIPTION = I18N_PREFIX + "description";
// "你是一个聪明、对人类有帮助的人工智能,你可以对人类提出的问题给出有用、详细、礼貌的回答"
// "智能问答机器人"
public static final String I18N_ROBOT_NICKNAME = I18N_PREFIX + "robot_nickname";
public static final String I18N_ROBOT_DESCRIPTION = I18N_PREFIX + "robot_description";
//
public static final String I18N_ROBOT_LLM_PROMPT_ZH_CN = "你是一个聪明、对人类有帮助的人工智能,你可以对人类提出的问题给出有用、详细、礼貌的回答";
//
public static final String I18N_ADMIN = I18N_PREFIX + "admin";
public static final String I18N_ADMIN_DESCRIPTION = I18N_PREFIX + "admin_description";
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-01-27 10:55:25
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-16 12:39:23
* @LastEditTime: 2024-06-07 14:47:38
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.

View File

@@ -1,62 +0,0 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-02-26 12:20:17
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-02-26 12:20:22
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.constant;
/**
*
* @author xiaper.io
*/
public class RouteConsts {
// Prevents instantiation
private RouteConsts() {
}
/**
* 轮询分配
* Round-robin
*/
public static final String ROUTE_TYPE_ROBIN = "robin";
/**
* 当日内按照接待个数平均分配,少者优先
* 0点清空前一天并开始新的一天计数
*/
public static final String ROUTE_TYPE_AVERAGE = "average";
/**
* 当前饱和度,饱和度越低,空缺位置越多,优先分配
*/
public static final String ROUTE_TYPE_IDLE = "idle";
/**
* 当前正在进行中对话数量,少者优先
*/
public static final String ROUTE_TYPE_LESS = "less";
/**
* 广播给所有客服,客服抢单
*/
public static final String ROUTE_TYPE_BOARDCAST = "broadcast";
/**
* 熟客优先,最近会话优先分配
*/
// public static final String ROUTE_TYPE_RECENT = "recent";
/**
* TODO: 智能分配,还没有想清楚,待后续完善
*/
// public static final String ROUTE_TYPE_SMART = "smart";
/**
* TODO: 根据客服满意度评分等其他因素自动分配
*/
// public static final String ROUTE_TYPE_RATE = "rate";
}

View File

@@ -1,255 +0,0 @@
package com.bytedesk.core.constant;
/**
*
* @author xiaper.io
*/
public class StatusConsts {
// Prevents instantiation
private StatusConsts() {}
/**
* app、网站已经上线
*/
public static final String APP_STATUS_RELEASE = "release";
/**
* 开发对接中...
*/
public static final String APP_STATUS_DEBUG = "debug";
/**
* 已上线,开发新版本中
*/
public static final String APP_STATUS_VERSION = "version";
/**
* 消息发送状态:
*
* 1. 发送中
*/
public static final String MESSAGE_STATUS_SENDING = "sending";
/**
* 2. 已经存储到服务器
*/
public static final String MESSAGE_STATUS_STORED = "stored";
/**
* 3. 对方已收到
*/
public static final String MESSAGE_STATUS_RECEIVED = "received";
/**
* 4. 对方已读
*/
public static final String MESSAGE_STATUS_READ = "read";
/**
* 5. 发送错误
*/
public static final String MESSAGE_STATUS_ERROR = "error";
/**
* 6. 阅后即焚已销毁
*/
public static final String MESSAGE_STATUS_DESTROYED = "destroyed";
/**
* 7. 消息撤回
*/
public static final String MESSAGE_STATUS_RECALL = "recall";
/**
* 8. 对方拒收
*/
public static final String MESSAGE_STATUS_REJECT = "reject";
/**
* 用户在线状态:
*/
/**
* 跟服务器建立长连接
*/
public static final String USER_STATUS_CONNECTED = "connected";
/**
* 断开长连接
*/
public static final String USER_STATUS_DISCONNECTED = "disconnected";
/**
* 在线状态
*/
public static final String USER_STATUS_ONLINE = "online";
/**
* 离线状态
*/
public static final String USER_STATUS_OFFLINE = "offline";
/**
* 忙
*/
public static final String USER_STATUS_BUSY = "busy";
/**
* 离开
*/
public static final String USER_STATUS_AWAY = "away";
/**
* 登出
*/
public static final String USER_STATUS_LOGOUT = "logout";
/**
* 登录
*/
public static final String USER_STATUS_LOGIN = "login";
/**
* 离开
*/
public static final String USER_STATUS_LEAVE = "leave";
/**
* 话后
*/
public static final String USER_STATUS_AFTER = "after";
/**
* 就餐
*/
public static final String USER_STATUS_EAT = "eat";
/**
* 小休
*/
public static final String USER_STATUS_REST = "rest";
/**
* 签入
*/
public static final String USER_STATUS_SIGN_IN = "sign_in";
/**
* 签出
*/
public static final String USER_STATUS_SIGN_OUT = "sign_out";
/**
* 排队状态: 排队中
*/
public static final String QUEUE_STATUS_QUEUING = "queuing";
/**
* 已接入
*/
public static final String QUEUE_STATUS_ACCEPTED = "accepted";
/**
* 已忽略
*/
public static final String QUEUE_STATUS_IGNORED = "ignored";
/**
* 已离开
*/
public static final String QUEUE_STATUS_LEAVED = "leaved";
/**
* 超时
*/
public static final String QUEUE_STATUS_TIMEOUT = "timeout";
/**
* 留言状态: 未被领取
*/
public static final String LEAVE_MESSAGE_STATUS_UNCLAIMED = "unclaimed";
/**
* 已领取
*/
public static final String LEAVE_MESSAGE_STATUS_CLAIMED = "claimed";
/**
* 已小结
*/
public static final String LEAVE_MESSAGE_STATUS_SUMMARIZED = "summarized";
/**
* 支付:等待支付、支付成功、支付失败
*/
public static final String PAY_STATUS_WAITING = "waiting";
public static final String PAY_STATUS_SUCCESS = "success";
public static final String PAY_STATUS_FAILED = "failed";
/**
* iOS app status
*/
public static final String IOS_BUILD_DEBUG = "debug";
public static final String IOS_BUILD_RELEASE = "release";
/**
* 工单状态(客服角度): 待认领、待回复、处理中、待评价、已解决、已忽略、已关闭
*/
public static final String TICKET_STATUS_UNCLAIM = "unclaim";
// public static final String TICKET_STATUS_WAITING = "waiting";
public static final String TICKET_STATUS_TO_BE_REPLIED = "to_be_replied";
public static final String TICKET_STATUS_PROCESSING = "processing";
// public static final String TICKET_STATUS_TO_BE_RATED = "to_be_rated";
public static final String TICKET_STATUS_DONE = "done"; // 客服解决
public static final String TICKET_STATUS_IGNORE = "ignore"; // 客服忽略
public static final String TICKET_STATUS_CLOSED = "closed"; // 访客关闭
/**
* 瀑布状态:发布、审核中、下线
*/
public static final String WATERFALL_STATUS_ON = "on";
public static final String WATERFALL_STATUS_WAITING = "waiting";
public static final String WATERFALL_STATUS_OFF = "off";
/**
* 学校状态:审核中、被拒绝、审核通过
*/
public static final String SCHOOL_STATUS_WAITING = "waiting";
public static final String SCHOOL_STATUS_REJECT = "reject";
public static final String SCHOOL_STATUS_APPROVE = "approve";
/**
* 课程状态:在售、待售、下架
*/
public static final String COURSE_STATUS_ON = "on";
public static final String COURSE_STATUS_WAITING = "waiting";
public static final String COURSE_STATUS_OFF = "off";
/**
* 教师状态
*/
public static final String TEACHER_STATUS_ON = "on";
public static final String TEACHER_STATUS_OFF = "off";
/**
* 预约状态: 提交待确认,已确认,已取消
*/
public static final String APPOINT_STATUS_PENDING = "pending";
public static final String APPOINT_STATUS_CONFIRM = "confirmed";
public static final String APPOINT_STATUS_CANCEL = "canceled";
/**
* 好友关系:未添加、已发送待对方同意、好友、已删除、已拉黑
*/
public static final String FRIEND_STATUS_NO = "no";
public static final String FRIEND_STATUS_SEND = "send";
public static final String FRIEND_STATUS_OK = "ok";
public static final String FRIEND_STATUS_DELETE = "delete";
public static final String FRIEND_STATUS_BLOCK = "block";
/**
* 来源:附近、通讯录、扫一扫、搜索
*/
public static final String FRIEND_SOURCE_NEARBY = "nearby";
public static final String FRIEND_SOURCE_ADDRESS = "address";
public static final String FRIEND_SOURCE_SCAN = "scan";
public static final String FRIEND_SOURCE_SEARCH = "search";
/**
* FAQ问答状态已发布、未发布
*/
public static final String ANSWER_STATUS_PUBLISHED = "published";
public static final String ANSWER_STATUS_UNPUBLISHED = "unpublished";
// 同事和群组会话初始值为init
public static final String THREAD_STATUS_INIT = "init";
// 只有客服会话会有open
public static final String THREAD_STATUS_OPEN = "open";
public static final String THREAD_STATUS_CLOSED_AUTO = "closed_auto";
public static final String THREAD_STATUS_CLOSED_AGENT = "closed_agent";
// 发送验证码状态
public static final String CODE_STATUS_PENDING = "pending";
public static final String CODE_STATUS_CONFIRM = "confirmed";
public static final String CODE_STATUS_EXPIRED = "expired";
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-02-22 16:01:14
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-30 14:43:37
* @LastEditTime: 2024-05-29 16:44:02
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,22 +14,22 @@
*/
package com.bytedesk.core.constant;
public class ThreadTypeConsts {
// public class ThreadTypeConsts {
private ThreadTypeConsts() {
}
// private ThreadTypeConsts() {
// }
/**
* thread type
*/
public static final String APPOINTED = "appointed";
public static final String WORKGROUP = "workgroup";
public static final String MEMBER = "member";
public static final String GROUP = "group";
public static final String ROBOT = "robot";
public static final String LEAVEMSG = "leavemsg";
public static final String FEEDBACK = "feedback";
public static final String ASISTANT = "assistant";
public static final String CHANNEL = "channel";
public static final String LOCAL = "local";
}
// /**
// * thread type
// */
// public static final String APPOINTED = "appointed";
// public static final String WORKGROUP = "workgroup";
// public static final String MEMBER = "member";
// public static final String GROUP = "group";
// public static final String ROBOT = "robot";
// public static final String LEAVEMSG = "leavemsg";
// public static final String FEEDBACK = "feedback";
// public static final String ASISTANT = "assistant";
// public static final String CHANNEL = "channel";
// public static final String LOCAL = "local";
// }

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-01-29 16:21:24
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-16 11:56:53
* @LastEditTime: 2024-06-07 14:51:02
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -23,66 +23,57 @@ public class TypeConsts {
public static final String TYPE_MOBILE = "mobile";
public static final String TYPE_EMAIL = "email";
//
public static final String SUPER = "SUPER";
public static final String ADMIN = "ADMIN";
public static final String HR = "HR";
public static final String ORG = "ORG";
public static final String IT = "IT";
public static final String MONEY = "MONEY";
public static final String MARKETING = "MARKETING";
public static final String SALES = "SALES";
public static final String CUSTOMER_SERVICE = "CS";
// ROLES - 角色
public static final String ROLE_PREFIX = "ROLE_";
// super - 超级管理员
public static final String ROLE_SUPER = "ROLE_SUPER";
public static final String AUTHORITY_SUPER = "SUPER";
public static final String ROLE_SUPER = ROLE_PREFIX + SUPER;
// admin - 管理员
public static final String ROLE_ADMIN = "ROLE_ADMIN";
public static final String AUTHORITY_ADMIN = "ADMIN";
public static final String ROLE_ADMIN = ROLE_PREFIX + ADMIN;
// hr - 人事
public static final String ROLE_HR = "ROLE_HR";
public static final String AUTHORITY_HR = "HR";
public static final String ROLE_HR = ROLE_PREFIX + HR;
// org - 行政
public static final String ROLE_ORG = "ROLE_ORG";
public static final String AUTHORITY_ORG = "ORG";
public static final String ROLE_ORG = ROLE_PREFIX + ORG;
// it - IT
public static final String ROLE_IT = "ROLE_IT";
public static final String AUTHORITY_IT = "IT";
public static final String ROLE_IT = ROLE_PREFIX + IT;
// money - 财务
public static final String ROLE_MONEY = "ROLE_MONEY";
public static final String AUTHORITY_MONEY = "MONEY";
public static final String ROLE_MONEY = ROLE_PREFIX + MONEY;
// marketing - 市场
public static final String ROLE_MARKETING = "ROLE_MARKETING";
public static final String AUTHORITY_MARKETING = "MARKETING";
public static final String ROLE_MARKETING = ROLE_PREFIX + MARKETING;
// sales - 销售
public static final String ROLE_SALES = "ROLE_SALES";
public static final String AUTHORITY_SALES = "SALES";
public static final String ROLE_SALES = ROLE_PREFIX + SALES;
// customer service - 客服
public static final String ROLE_CUSTOMER_SERVICE = "ROLE_CS";
public static final String AUTHORITY_CUSTOMER_SERVICE = "CS";
public static final String ROLE_CUSTOMER_SERVICE = ROLE_PREFIX + CUSTOMER_SERVICE;
/// 部门
// 管理员
public static final String DEPT_PREFIX = "DEPT_";
public static final String DEPT_ADMIN = DEPT_PREFIX + ADMIN;
// hr - 人事
public static final String DEPT_HR = "DEPT_HR";
public static final String DEPT_HR = DEPT_PREFIX + HR;
// org - 行政
public static final String DEPT_ORG = "DEPT_ORG";
public static final String DEPT_ORG = DEPT_PREFIX + ORG;
// it - IT
public static final String DEPT_IT = "DEPT_IT";
public static final String DEPT_IT = DEPT_PREFIX + IT;
// money - 财务
public static final String DEPT_MONEY = "DEPT_MONEY";
public static final String DEPT_MONEY = DEPT_PREFIX + MONEY;
// marketing - 市场
public static final String DEPT_MARKETING = "DEPT_MARKETING";
public static final String DEPT_MARKETING = DEPT_PREFIX + MARKETING;
// sales - 销售
public static final String DEPT_SALES = "DEPT_SALES";
public static final String DEPT_SALES = DEPT_PREFIX + SALES;
// customer service - 客服
public static final String DEPT_CUSTOMER_SERVICE = "DEPT_CS";
public static final String DEPT_CUSTOMER_SERVICE = DEPT_PREFIX + CUSTOMER_SERVICE;
//
public static final String SEND_MOBILE_CODE_TYPE_LOGIN = "login";
@@ -90,7 +81,6 @@ public class TypeConsts {
public static final String SEND_MOBILE_CODE_TYPE_FORGET = "forget";
public static final String SEND_MOBILE_CODE_TYPE_VERIFY = "verify";
/**
* region类型, 代码长度分别为:省 2、市 4、区/县 6、镇 9
*/
@@ -99,5 +89,16 @@ public class TypeConsts {
public static final String REGION_TYPE_COUNTY = "county";
public static final String REGION_TYPE_TOWN = "town";
//
public static final String COLUMN_TYPE_TEXT = "TEXT"; // length = 65534
public static final String COLUMN_TYPE_JSON = "json"; //
public static final String COLUMN_NAME_TYPE = "by_type";
public static final String COLUMN_NAME_USER = "by_user";
//
// public static final String ROBOT_TYPE_SERVICE = "service";
//
public static final String ACTION_TYPE_FAILED = "failed";
public static final String ACTION_TYPE_LOG = "log";
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-02-26 12:21:02
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-05-08 10:24:18
* @LastEditTime: 2024-06-05 12:57:52
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -52,8 +52,11 @@ public class UserConsts {
// 默认组织uid
public static final String DEFAULT_ORGANIZATION_UID = "default_organization_uid";
public static final String DEFAULT_AGENT_UID = "default_agent_uid";
public static final String DEFAULT_WORKGROUP_UID = "default_wg_uid";
public static final String DEFAULT_ROBOT_UID = "default_robot_uid";
public static final String DEFAULT_FILE_ASISTANT_UID = "default_file_asistant_uid";
public static final String DEFAULT_SYSTEM_NOTIFICATION_UID = "default_system_notification_uid";
}

View File

@@ -0,0 +1,54 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-05-25 13:07:20
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-06 11:40:46
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.enums;
public enum ClientEnum {
SYSTEM("system"),
WEB("web"),
H5("h5"),
IOS("ios"),
ANDROID("android"),
FLUTTER_WEB("flutter_web"),
FLUTTER_ANDROID("flutter_android"),
FLUTTER_IOS("flutter_ios"),
UNI_WEB("uni_web"),
UNI_ANDROID("uni_android"),
UNI_IOS("uni_ios"),
WECHAT_MINI("wechat_mini"),
WECHAT_MP("wechat_mp"),
WECHAT_WORK("wechat_work"),
WECHAT_KEFU("wechat_kefu");
private final String value;
ClientEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
// 根据字符串查找对应的枚举常量
public static ClientEnum fromValue(String value) {
for (ClientEnum type : ClientEnum.values()) {
if (type.getValue().equalsIgnoreCase(value)) {
return type;
}
}
throw new IllegalArgumentException("No enum constant with value: " + value);
}
}

View File

@@ -0,0 +1,41 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-07 14:19:24
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-07 14:20:31
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.enums;
public enum LanguageEnum {
EN("en"),
ZH_CN("zh-cn"),
ZH_TW("zh-tw");
private final String value;
LanguageEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
// 根据字符串查找对应的枚举常量
public static LanguageEnum fromValue(String value) {
for (LanguageEnum type : LanguageEnum.values()) {
if (type.getValue().equalsIgnoreCase(value)) {
return type;
}
}
throw new IllegalArgumentException("No enum constant with value: " + value);
}
}

View File

@@ -0,0 +1,31 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-04 12:59:46
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-04 13:00:20
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.event;
import org.springframework.context.event.EventListener;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import com.bytedesk.core.utils.JsonResult;
@Component
public class BytedeskEventListener {
@EventListener
public ResponseEntity<?> onEmailAlreadyExistsEvent(EmailAlreadyExistsEvent event) {
return ResponseEntity.ok().body(JsonResult.error("Email " + event.getEmail() + " already exists, please login or use another one"));
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-02-23 14:42:58
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-23 08:55:55
* @LastEditTime: 2024-06-04 13:02:44
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -15,10 +15,20 @@
package com.bytedesk.core.event;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Async;
// import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import com.bytedesk.core.action.ActionEvent;
import com.bytedesk.core.action.ActionRequest;
import com.bytedesk.core.message.MessageBytesEvent;
import com.bytedesk.core.message.MessageJsonEvent;
import com.bytedesk.core.quartz.QuartzFiveSecondEvent;
import com.bytedesk.core.rbac.organization.Organization;
import com.bytedesk.core.rbac.organization.OrganizationCreateEvent;
import com.bytedesk.core.thread.Thread;
import com.bytedesk.core.thread.ThreadCreateEvent;
import com.bytedesk.core.thread.ThreadUpdateEvent;
import lombok.AllArgsConstructor;
@Component
@@ -27,50 +37,63 @@ public class BytedeskEventPublisher {
private final ApplicationEventPublisher applicationEventPublisher;
@Async
public void publishMessageBytesEvent(byte[] messageBytes) {
applicationEventPublisher.publishEvent(new MessageBytesEvent(this, messageBytes));
}
@Async
public void publishMessageJsonEvent(String json) {
applicationEventPublisher.publishEvent(new MessageJsonEvent(this, json));
}
@Async
public void publishQuartzFiveSecondEvent() {
applicationEventPublisher.publishEvent(new QuartzFiveSecondEvent(this));
}
@Async
public void publishMqttConnectedEvent(String client) {
applicationEventPublisher.publishEvent(new MqttConnectedEvent(this, client));
}
@Async
public void publishMqttDisconnectedEvent(String client) {
applicationEventPublisher.publishEvent(new MqttDisconnectedEvent(this, client));
}
// @Async
// public void publishMqttSubscribeEvent(String uid, String topic) {
// applicationEventPublisher.publishEvent(new MqttSubscribeEvent(this, uid, topic));
// }
// @Async
// public void publishMqttUnsubscribeEvent(String uid, String topic) {
// applicationEventPublisher.publishEvent(new MqttUnsubscribeEvent(this, uid, topic));
// }
public void publishMqttSubscribeEvent(String topic, String clientId) {
applicationEventPublisher.publishEvent(new MqttSubscribeEvent(this, topic, clientId));
}
public void publishMqttUnsubscribeEvent(String topic, String clientId) {
applicationEventPublisher.publishEvent(new MqttUnsubscribeEvent(this, topic, clientId));
}
@Async
public void publishThreadCreateEvent(Thread thread) {
applicationEventPublisher.publishEvent(new ThreadCreateEvent(this, thread));
}
@Async
public void publishThreadUpdateEvent(Thread thread) {
applicationEventPublisher.publishEvent(new ThreadUpdateEvent(this, thread));
}
public void publishActionEvent(ActionRequest actionRequest) {
applicationEventPublisher.publishEvent(new ActionEvent(this, actionRequest));
}
public void publishOrganizationCreateEvent(Organization organization) {
applicationEventPublisher.publishEvent(new OrganizationCreateEvent(organization));
}
public void publishEmailAlreadyExistsEvent(String email) {
applicationEventPublisher.publishEvent(new EmailAlreadyExistsEvent(this, email));
}
}

View File

@@ -0,0 +1,33 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2024-06-04 12:58:38
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-06-04 12:58:40
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
* 仅支持企业内部员工自用严禁私自用于销售、二次销售或者部署SaaS方式销售
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
* contact: 270580156@qq.com
* 联系270580156@qq.com
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.core.event;
import org.springframework.context.ApplicationEvent;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class EmailAlreadyExistsEvent extends ApplicationEvent {
private final String email;
public EmailAlreadyExistsEvent(Object source, String email) {
super(source);
this.email = email;
}
}

View File

@@ -2,7 +2,7 @@
* @Author: jackning 270580156@qq.com
* @Date: 2024-02-23 14:43:57
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2024-04-16 10:16:12
* @LastEditTime: 2024-06-01 10:17:40
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
* Please be aware of the BSL license restrictions before installing Bytedesk IM
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
@@ -14,22 +14,21 @@
*/
package com.bytedesk.core.event;
// import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEvent;
// import lombok.Data;
// import lombok.EqualsAndHashCode;
import lombok.Data;
import lombok.EqualsAndHashCode;
// @Data
// @EqualsAndHashCode(callSuper = false)
// public class MqttSubscribeEvent extends ApplicationEvent {
@Data
@EqualsAndHashCode(callSuper = false)
public class MqttSubscribeEvent extends ApplicationEvent {
// private String uid;
// private String topic;
private String topic;
private String clientId;
// public MqttSubscribeEvent(Object source, String uid, String topic) {
// super(source);
// this.uid = uid;
// this.topic = topic;
// }
// }
public MqttSubscribeEvent(Object source, String topic, String clientId) {
super(source);
this.topic = topic;
this.clientId = clientId;
}
}

Some files were not shown because too many files have changed in this diff Show More