Compare commits
71 Commits
11aa1b11b1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f08d63278 | |||
| db11d5cb2a | |||
| 735df67c77 | |||
| 0194405957 | |||
| 2820710fa6 | |||
| c932369f69 | |||
| 01ad333483 | |||
| fe32de4791 | |||
| 88043f44e6 | |||
| 10d8980095 | |||
| e4bb4e55e3 | |||
| 5e3ec6ddae | |||
| 90c553a31d | |||
| 5e9a5d9a30 | |||
| fde43cc549 | |||
| 5bface3047 | |||
| 4051c67af5 | |||
| eaca90a521 | |||
| afa6616a6f | |||
| 8483250a65 | |||
| ddb2acf37a | |||
| ed11350ed4 | |||
| 90681d1198 | |||
| 6e09130823 | |||
| 4c6a8f30b0 | |||
| f28ef213b7 | |||
| 1ec2d722eb | |||
| c76ab3e4c9 | |||
| a31fc4cc72 | |||
| deb775ff5c | |||
| c5dcd0b3ce | |||
| 4ad2a85850 | |||
| 0199c91dbd | |||
| 4ff76a3100 | |||
| 497e4f5001 | |||
| 1a45c37c44 | |||
| 82df2b5da9 | |||
| 40d214f80b | |||
| be1213da46 | |||
| 922223c635 | |||
| 461e3adb12 | |||
| cb5835fcaf | |||
| 3822488f26 | |||
| 6385dd163b | |||
| 22d2e40264 | |||
| aaa7d5ec3f | |||
| 77397ff40a | |||
| e04ef92a65 | |||
| 9e4191fc90 | |||
| b61f280567 | |||
| fe9c72ff6c | |||
| 8b072cbcbe | |||
| b77af1ca0d | |||
| 97deb1aef6 | |||
| 7d8a15bcdc | |||
| c2a8b4013b | |||
| 28e4a9983e | |||
| ac12331e8d | |||
| 0fe02ff1a9 | |||
| 6e5455ca25 | |||
| 879bd8c009 | |||
| 5c02fb16ca | |||
| cb5d4d00a7 | |||
| c8f9b6547a | |||
| 905bfa1996 | |||
| 4b06d2d0f9 | |||
| 50a5c5e128 | |||
| 6adda93d5d | |||
| 5f76945039 | |||
| d7e92d9b59 | |||
| 9080ae2e07 |
@@ -110,6 +110,11 @@
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.9.1</version>
|
||||
</dependency>
|
||||
<!-- 健康检查核心依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -79,7 +79,8 @@ public class SysLoginController
|
||||
public AjaxResult appLogin(@RequestBody LoginBody loginBody)
|
||||
{
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
ajax=loginService.appLogin(loginBody);
|
||||
//ajax=loginService.appLogin(loginBody);
|
||||
ajax=loginService.appLoginNew(loginBody);
|
||||
return ajax;
|
||||
}
|
||||
|
||||
@@ -94,6 +95,9 @@ public class SysLoginController
|
||||
if(loginBody==null||StringUtils.isBlank(loginBody.getIdCard())){
|
||||
return AjaxResult.error("请输入有效的身份证号!");
|
||||
}
|
||||
if(StringUtils.isBlank(loginBody.getUsername())){
|
||||
return AjaxResult.error("姓名不能为空!");
|
||||
}
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
ajax=loginService.idCardLogin(loginBody);
|
||||
return ajax;
|
||||
|
||||
@@ -261,7 +261,7 @@ public class SysUserController extends BaseController
|
||||
}
|
||||
|
||||
@ApiOperation("企业资质审核")
|
||||
@PreAuthorize("@ss.hasPermi('app:company:approval:list')")
|
||||
@PreAuthorize("@ss.hasPermi('company:List:review')")
|
||||
@PostMapping("/approval")
|
||||
public AjaxResult approval(@RequestBody Company company)
|
||||
{
|
||||
@@ -277,7 +277,7 @@ public class SysUserController extends BaseController
|
||||
sysUser.setPhonenumber(company1.getContactPersonPhone());
|
||||
sysUser.setNickName(company1.getContactPersonPhone());
|
||||
}else{
|
||||
sysUser.setPassword("123456");
|
||||
sysUser.setPassword("Abcd1234@");
|
||||
sysUser.setUserName(company1.getName());
|
||||
sysUser.setNickName(company1.getName());
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ token:
|
||||
# 令牌自定义标识
|
||||
header: Authorization
|
||||
# 令牌密钥
|
||||
secret: abcdefghijklmnopqrstuvwxyz
|
||||
secret: Abc123!@#Def456$%^Ghi789&*()Jkl0+-=MnoPqrStuVwxYz987$%^654@#$321!@#ZyxWvu
|
||||
# 令牌有效期(默认30分钟)
|
||||
expireTime: 30
|
||||
|
||||
@@ -140,6 +140,13 @@ mybatis-plus:
|
||||
file:
|
||||
upload-dir: /data/file
|
||||
|
||||
#nginx节点健康检查
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health
|
||||
|
||||
#微信小程序
|
||||
wx:
|
||||
appid: wx4aa34488b965a331
|
||||
@@ -183,4 +190,24 @@ oauth:
|
||||
tyQueryUnitInfo: http://10.98.80.146/qxgl_backend/security/get_organization_by_organizationid
|
||||
connect-timeout: 10
|
||||
read-timeout: 30
|
||||
write-timeout: 30
|
||||
write-timeout: 30
|
||||
|
||||
#ai
|
||||
chat:
|
||||
baseUrl: http://192.168.133.200:8080
|
||||
chatUrl: /v1/chat/completions
|
||||
chatDetailUrl: /core/chat/getPaginationRecords
|
||||
chatHistoryUrl: /core/chat/getHistories
|
||||
updateNameUrl: /core/chat/updateHistory
|
||||
stickChatUrl: /core/chat/updateHistory
|
||||
delChatUrl: /core/chat/delHistory
|
||||
delAllChatUrl: /core/chat/clearHistories
|
||||
guestUrl: /v1/chat/completions
|
||||
praiseUrl: /core/chat/feedback/updateUserFeedback
|
||||
appId: 67cd49095e947ae0ca7fadd8
|
||||
apiKey: fastgpt-qMl63276wPZvKAxEkW77bur0sSJpmuC6Ngg9lzyEjufLhsBAurjT55j
|
||||
model: qd-job-turbo
|
||||
|
||||
audioText:
|
||||
asr: http://192.168.133.200:8000/asr/file
|
||||
tts: http://192.168.133.200:19527/synthesize
|
||||
@@ -89,12 +89,12 @@
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
||||
<version>7.14.0</version>
|
||||
<version>7.17.24</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch</artifactId>
|
||||
<version>7.14.0</version>
|
||||
<version>7.17.24</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -111,11 +111,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<!--localDate-->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,296 @@
|
||||
package com.ruoyi.cms.config;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.cms.domain.chat.ChatRequest;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import lombok.var;
|
||||
import okhttp3.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class ChatClient {
|
||||
// 超时设置(单位:秒)
|
||||
private static final int CONNECT_TIMEOUT = 30;
|
||||
private static final int WRITE_TIMEOUT = 30;
|
||||
private static final int READ_TIMEOUT = 300; // 流式响应不设置读取超时
|
||||
|
||||
// 单例 OkHttp 客户端(复用连接池)
|
||||
private static final OkHttpClient client = new OkHttpClient.Builder()
|
||||
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
|
||||
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
|
||||
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
private static final String CHAT_ENDPOINT = "chat";
|
||||
|
||||
private static final String CHAT_HISTORY = "history";
|
||||
public static final List<String> TEXT_FILE_EXTENSIONS= Arrays.asList(".txt", ".md", ".html", ".doc", ".docx", ".pdf", ".ppt", ".pptx", ".csv", ".xls", ".xlsx");
|
||||
public static final List<String> IMAGE_FILE_EXTENSIONS=Arrays.asList(".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp");
|
||||
|
||||
|
||||
private ChatConfig chatConfig;
|
||||
@Autowired
|
||||
public void setChatConfig(ChatConfig chatConfig) {
|
||||
this.chatConfig = chatConfig;
|
||||
}
|
||||
@Value("${spring.profiles.active}")
|
||||
private String env;
|
||||
/**
|
||||
* 发送流式聊天请求
|
||||
* @param chatRequest 查询请求体
|
||||
* @param callback 流式响应回调接口
|
||||
*/
|
||||
public void sendStreamingChat(ChatRequest chatRequest, StreamCallback callback) {
|
||||
String url=chatConfig.getBaseUrl()+chatConfig.getChatUrl();
|
||||
// 构建请求体
|
||||
String jsonBody = buildChatRequestBody(chatRequest, CHAT_ENDPOINT);
|
||||
// 构建请求
|
||||
Request request = null;
|
||||
|
||||
try {
|
||||
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),jsonBody);
|
||||
request = new Request.Builder()
|
||||
.url(url)
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + chatConfig.getApiKey())
|
||||
.post(body).build();
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
callback.onError(new RuntimeException("构建请求失败: " + e.getMessage(), e));
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送异步请求
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
callback.onError(new RuntimeException("请求发送失败: " + e.getMessage(), e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
try {
|
||||
if (!response.isSuccessful()) {
|
||||
String errorBody = response.body() != null ? response.body().string() : "无错误信息";
|
||||
String errorMsg = String.format("API 响应错误: 状态码=%d, 错误信息=%s",
|
||||
response.code(), errorBody);
|
||||
System.err.println(errorMsg); // 打印详细错误
|
||||
callback.onError(new RuntimeException(errorMsg));
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理流式响应
|
||||
ResponseBody body = response.body();
|
||||
if (body == null) {
|
||||
callback.onError(new RuntimeException("响应体为空"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 逐行读取 SSE 格式的响应
|
||||
try (var bufferedSource = body.source()) {
|
||||
while (!bufferedSource.exhausted()) {
|
||||
String chunk = bufferedSource.readUtf8Line();
|
||||
if (chunk != null && !chunk.trim().isEmpty()) {
|
||||
callback.onData(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 通知流结束
|
||||
callback.onComplete();
|
||||
|
||||
} catch (Exception e) {
|
||||
String errorMsg = "处理响应失败: " + e.getMessage();
|
||||
System.err.println(errorMsg);
|
||||
callback.onError(new RuntimeException(errorMsg, e));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建聊天请求的 JSON 体
|
||||
*/
|
||||
/**
|
||||
* 构建聊天请求的 JSON 体
|
||||
* 根据文档 [cite: 162-172] 修改为 Vision API 兼容格式
|
||||
*/
|
||||
private String buildChatRequestBody(ChatRequest chatRequest, String key) {
|
||||
JSONObject chatObject = new JSONObject();
|
||||
|
||||
// 1. 设置会话ID (如果有)
|
||||
if(StringUtils.isNotEmpty(chatRequest.getSessionId())){
|
||||
chatObject.put("chatId", chatRequest.getSessionId());
|
||||
}
|
||||
|
||||
if("chat".equals(key)){
|
||||
// 基础参数设置
|
||||
chatObject.put("stream", true);
|
||||
chatObject.put("model", chatConfig.getModel()); // 需确保为 "qd-job-turbo"
|
||||
chatObject.put("user", chatRequest.getSessionId());
|
||||
|
||||
// 2. 获取历史消息列表,如果为空则初始化
|
||||
JSONArray messages = chatRequest.getMessages();
|
||||
if (messages == null) {
|
||||
messages = new JSONArray();
|
||||
}
|
||||
|
||||
// 3. 构建当前用户的多模态消息内容 (Multimodal Content) [cite: 162]
|
||||
JSONArray contentArray = new JSONArray();
|
||||
|
||||
// 3.1 添加文本内容 [cite: 166, 172]
|
||||
if(StringUtils.isNotEmpty(chatRequest.getData())){
|
||||
JSONObject textPart = new JSONObject();
|
||||
textPart.put("type", "text");
|
||||
textPart.put("text", chatRequest.getData());
|
||||
contentArray.add(textPart);
|
||||
}
|
||||
|
||||
// 3.2 添加文件内容 (图片、PDF、Excel等) [cite: 174, 180, 232]
|
||||
// 文档说明:image_url 字段兼容 PDF, Excel, PPT 等所有 OCR 支持的文件
|
||||
if(!CollectionUtils.isEmpty(chatRequest.getFileUrl())){
|
||||
for(String url : chatRequest.getFileUrl()){
|
||||
String finalUrl = "";
|
||||
if(Objects.equals(env, "pro")){
|
||||
finalUrl = url.replace("https://fw.rc.qingdao.gov.cn/rgpp-api/api/ng", "http://10.213.6.207:19010");
|
||||
}else {
|
||||
finalUrl = url;
|
||||
}
|
||||
// 处理内网/外网地址映射 (保留你原有的逻辑)
|
||||
|
||||
JSONObject filePart = new JSONObject();
|
||||
filePart.put("type", "image_url"); // 固定为 image_url [cite: 174]
|
||||
|
||||
JSONObject imageUrlObj = new JSONObject();
|
||||
imageUrlObj.put("url", finalUrl); // 文件地址 [cite: 172]
|
||||
|
||||
filePart.put("image_url", imageUrlObj);
|
||||
contentArray.add(filePart);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 将当前消息封装为 User Message 对象并加入消息列表 [cite: 151, 163]
|
||||
// 只有当有内容(文本或文件)时才添加
|
||||
if (!contentArray.isEmpty()) {
|
||||
JSONObject currentUserMessage = new JSONObject();
|
||||
currentUserMessage.put("role", "user");
|
||||
currentUserMessage.put("content", contentArray); // content 为数组格式
|
||||
messages.add(currentUserMessage);
|
||||
}
|
||||
|
||||
// 5. 将完整的消息列表放入请求体 [cite: 135]
|
||||
chatObject.put("messages", messages);
|
||||
|
||||
} else {
|
||||
// 非 chat 场景的逻辑保留
|
||||
chatObject.put("appId", chatConfig.getAppId());
|
||||
}
|
||||
|
||||
return chatObject.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的 JSON 转义处理(防止特殊字符破坏 JSON 格式)
|
||||
*/
|
||||
private String escapeJson(String value) {
|
||||
if (value == null) return "";
|
||||
return value
|
||||
.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"")
|
||||
.replace("\b", "\\b")
|
||||
.replace("\f", "\\f")
|
||||
.replace("\n", "\\n")
|
||||
.replace("\r", "\\r")
|
||||
.replace("\t", "\\t");
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送聊天请求并返回完整JSON响应
|
||||
* @param chatRequest 用户输入的查询内容
|
||||
* @return 完整的JSON响应字符串
|
||||
* @throws IOException 网络请求异常
|
||||
*/
|
||||
public String sendChatGuest(ChatRequest chatRequest) throws IOException {
|
||||
String url=chatConfig.getBaseUrl()+chatConfig.getGuestUrl();
|
||||
|
||||
JSONArray array = chatRequest.getMessages();
|
||||
if(array==null||array.isEmpty()||array.size()==0){
|
||||
array = new JSONArray();
|
||||
}
|
||||
JSONObject contentObject = new JSONObject();
|
||||
contentObject.put("content","你是一个岗位招聘专家,请根据用户的问题生成用户下一步想要提出的问题。需要以用户的口吻进行生成。结合上下文中用户提出的问题以及助手回复的答案,需要猜测用户更进一步的需求,例如期望的薪资,期望的工作地点,掌握的技能。生成的问题举例:有没有薪资在9000以上的工作?我的学历是本科。我希望找国企。注意不仅限于这些,还要根据上下文。其次所有的问题应该限定在喀什。并且只生成3到4个。");
|
||||
contentObject.put("role","system");
|
||||
array.add(contentObject);
|
||||
JSONObject jsonBody = new JSONObject();
|
||||
jsonBody.put("stream",false);
|
||||
jsonBody.put("model",chatConfig.getModel());
|
||||
jsonBody.put("messages",array);
|
||||
|
||||
// 构建请求(使用非流式响应模式)
|
||||
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), String.valueOf(jsonBody));
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + chatConfig.getApiKey())
|
||||
.post(requestBody).build();
|
||||
|
||||
// 发送同步请求
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
if (!response.isSuccessful()) {
|
||||
String errorBody = response.body() != null ? response.body().string() : "无错误信息";
|
||||
String errorMsg = String.format("API 响应错误: 状态码=%d, 错误信息=%s",
|
||||
response.code(), errorBody);
|
||||
throw new IOException(errorMsg);
|
||||
}
|
||||
ResponseBody body = response.body();
|
||||
if (body == null) {
|
||||
throw new IOException("响应体为空");
|
||||
}
|
||||
JSONObject object = JSONObject.parseObject(body.string());
|
||||
String choices = object.getString("choices");
|
||||
if (choices != null && !choices.trim().isEmpty()) {
|
||||
JSONArray jsonArray = JSONArray.parseArray(choices);
|
||||
object = JSONObject.parseObject(jsonArray.getString(0));
|
||||
object = object.getJSONObject("message");
|
||||
String content = object.getString("content");// 消息内容
|
||||
|
||||
return content;
|
||||
}
|
||||
return body.string();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 流式响应回调接口
|
||||
*/
|
||||
public interface StreamCallback {
|
||||
/**
|
||||
* 接收分片数据
|
||||
* @param chunk SSE 格式的分片数据
|
||||
*/
|
||||
void onData(String chunk);
|
||||
|
||||
/**
|
||||
* 响应结束
|
||||
*/
|
||||
void onComplete();
|
||||
|
||||
/**
|
||||
* 发生错误
|
||||
* @param e 异常信息
|
||||
*/
|
||||
void onError(Throwable e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.ruoyi.cms.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "chat")
|
||||
public class ChatConfig {
|
||||
private String baseUrl;
|
||||
private String chatUrl;
|
||||
private String chatDetailUrl;
|
||||
private String chatHistoryUrl;
|
||||
private String updateNameUrl;
|
||||
private String delChatUrl;
|
||||
private String delAllChatUrl;
|
||||
private String guestUrl;
|
||||
private String praiseUrl;
|
||||
private String apiKey;
|
||||
private String appId;
|
||||
private String model;
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public String getChatUrl() {
|
||||
return chatUrl;
|
||||
}
|
||||
|
||||
public void setChatUrl(String chatUrl) {
|
||||
this.chatUrl = chatUrl;
|
||||
}
|
||||
|
||||
public String getChatDetailUrl() {
|
||||
return chatDetailUrl;
|
||||
}
|
||||
|
||||
public void setChatDetailUrl(String chatDetailUrl) {
|
||||
this.chatDetailUrl = chatDetailUrl;
|
||||
}
|
||||
|
||||
public String getChatHistoryUrl() {
|
||||
return chatHistoryUrl;
|
||||
}
|
||||
|
||||
public void setChatHistoryUrl(String chatHistoryUrl) {
|
||||
this.chatHistoryUrl = chatHistoryUrl;
|
||||
}
|
||||
|
||||
public String getUpdateNameUrl() {
|
||||
return updateNameUrl;
|
||||
}
|
||||
|
||||
public void setUpdateNameUrl(String updateNameUrl) {
|
||||
this.updateNameUrl = updateNameUrl;
|
||||
}
|
||||
|
||||
public String getDelChatUrl() {
|
||||
return delChatUrl;
|
||||
}
|
||||
|
||||
public void setDelChatUrl(String delChatUrl) {
|
||||
this.delChatUrl = delChatUrl;
|
||||
}
|
||||
|
||||
public String getDelAllChatUrl() {
|
||||
return delAllChatUrl;
|
||||
}
|
||||
|
||||
public void setDelAllChatUrl(String delAllChatUrl) {
|
||||
this.delAllChatUrl = delAllChatUrl;
|
||||
}
|
||||
|
||||
public String getGuestUrl() {
|
||||
return guestUrl;
|
||||
}
|
||||
|
||||
public void setGuestUrl(String guestUrl) {
|
||||
this.guestUrl = guestUrl;
|
||||
}
|
||||
|
||||
public String getPraiseUrl() {
|
||||
return praiseUrl;
|
||||
}
|
||||
|
||||
public void setPraiseUrl(String praiseUrl) {
|
||||
this.praiseUrl = praiseUrl;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,20 @@
|
||||
package com.ruoyi.cms.controller.app;
|
||||
|
||||
import com.ruoyi.cms.util.IdGenerator;
|
||||
import com.ruoyi.cms.util.ProxyServerUtil;
|
||||
import com.ruoyi.common.core.domain.entity.File;
|
||||
import com.ruoyi.cms.service.IFileService;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.SiteSecurityUtils;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@@ -24,7 +28,10 @@ public class AppFileController extends BaseController {
|
||||
@ApiOperation("上传文件")
|
||||
@PostMapping("/upload")
|
||||
public AjaxResult upload(@RequestParam("file") MultipartFile file, @RequestParam(value = "bussinessid",required = false) Long bussinessId) {
|
||||
return fileService.upload(file,bussinessId);
|
||||
if(!(SiteSecurityUtils.isLogin() || SecurityUtils.isLogin())){
|
||||
return AjaxResult.error("未登录,请登录后在上传文件!");
|
||||
}
|
||||
return fileService.upload(file,bussinessId);
|
||||
}
|
||||
|
||||
@ApiOperation("获取附件列表")
|
||||
@@ -44,10 +51,15 @@ public class AppFileController extends BaseController {
|
||||
|
||||
@ApiOperation("上传文件")
|
||||
@PostMapping("/uploadFile")
|
||||
public AjaxResult uploadFile(@RequestParam("file") MultipartFile file, @RequestParam(value = "bussinessid",required = false) Long bussinessId) {
|
||||
public AjaxResult uploadFile(@RequestParam("file") MultipartFile file, @RequestParam(value = "bussinessid",required = false) Long bussinessId, HttpServletRequest request) {
|
||||
String proxyServer = ProxyServerUtil.getProxyServer(request);
|
||||
System.out.println("获取服务器地址======================"+proxyServer);
|
||||
if(!(SiteSecurityUtils.isLogin() || SecurityUtils.isLogin())){
|
||||
return AjaxResult.error("未登录,请登录后在上传文件!");
|
||||
}
|
||||
if(bussinessId==null){
|
||||
bussinessId=idGenerator.generateId();
|
||||
}
|
||||
return fileService.uploadFile(file,bussinessId);
|
||||
return fileService.uploadFile(file,bussinessId,request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,16 @@ package com.ruoyi.cms.controller.app;
|
||||
|
||||
import com.ruoyi.cms.domain.ESJobDocument;
|
||||
import com.ruoyi.cms.domain.Job;
|
||||
import com.ruoyi.cms.domain.JobApply;
|
||||
import com.ruoyi.cms.domain.query.ESJobSearch;
|
||||
import com.ruoyi.cms.service.ICompanyService;
|
||||
import com.ruoyi.cms.service.IESJobSearchService;
|
||||
import com.ruoyi.cms.service.IJobCollectionService;
|
||||
import com.ruoyi.cms.service.IJobService;
|
||||
import com.ruoyi.cms.service.*;
|
||||
import com.ruoyi.cms.util.RoleUtils;
|
||||
import com.ruoyi.cms.util.sensitiveWord.SensitiveWordChecker;
|
||||
import com.ruoyi.common.annotation.BussinessLog;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.utils.SiteSecurityUtils;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
@@ -21,6 +20,7 @@ import org.dromara.easyes.core.biz.EsPageInfo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -42,6 +42,8 @@ public class AppJobController extends BaseController
|
||||
private IESJobSearchService esJobSearchService;
|
||||
@Autowired
|
||||
private SensitiveWordChecker sensitiveWordChecker;
|
||||
@Autowired
|
||||
private IJobApplyService jobApplyService;
|
||||
|
||||
/**
|
||||
* 查询岗位列表
|
||||
@@ -143,14 +145,34 @@ public class AppJobController extends BaseController
|
||||
list.setList(jobList);
|
||||
return getTableDataInfo(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* ai对话模型查询岗位数据(es查询)
|
||||
* @param job
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation("添加模型查询岗位")
|
||||
@GetMapping("/searchModelJobsList")
|
||||
public TableDataInfo searchModelJobsList(ESJobSearch job)
|
||||
{
|
||||
EsPageInfo<ESJobDocument> list = esJobSearchService.nearJob(job);
|
||||
List<ESJobDocument> jobList = list.getList();
|
||||
jobList.forEach(it->it.setAppJobUrl(String.valueOf(it.getJobId())));
|
||||
return getDataTable(jobList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取岗位详细信息
|
||||
*/
|
||||
@ApiOperation("获取岗位详细信息")
|
||||
@GetMapping(value = "/{jobId}")
|
||||
public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
|
||||
public AjaxResult getInfo(@PathVariable("jobId") Long jobId, HttpServletRequest request)
|
||||
{
|
||||
Job job = jobService.selectJobByJobIdApp(jobId);
|
||||
if (jobId == null) {
|
||||
return AjaxResult.error("jobId不能为空");
|
||||
}
|
||||
//Job job = jobService.selectJobByJobIdApp(jobId);
|
||||
Job job = jobService.selectHttpJobByJobIdApp(jobId,request);
|
||||
return success(job);
|
||||
}
|
||||
|
||||
@@ -232,4 +254,33 @@ public class AppJobController extends BaseController
|
||||
}
|
||||
return success(jobService.selectApplyJobUserList(jobId));
|
||||
}
|
||||
|
||||
@ApiOperation("删除岗位申请")
|
||||
@DeleteMapping("/applyJobCencal")
|
||||
public AjaxResult applyJobCencal(@RequestBody JobApply apply){
|
||||
if(apply==null){
|
||||
return AjaxResult.error("参数为空!");
|
||||
}
|
||||
if (apply.getJobId() == null) {
|
||||
return AjaxResult.error("岗位id为空!");
|
||||
}
|
||||
if(!SiteSecurityUtils.isLogin()){
|
||||
return AjaxResult.error("用户未登录!");
|
||||
}
|
||||
if (apply.getUserId() == null) {
|
||||
apply.setUserId(SiteSecurityUtils.getUserId());
|
||||
}
|
||||
return toAjax(jobApplyService.applyJobCencal(apply));
|
||||
}
|
||||
|
||||
@ApiOperation("获取取消岗位岗位详情")
|
||||
@GetMapping("/selectCencalList")
|
||||
public TableDataInfo selectCencalList(){
|
||||
JobApply queryApply = new JobApply();
|
||||
if (queryApply.getUserId() == null) {
|
||||
queryApply.setUserId(SiteSecurityUtils.getUserId());
|
||||
}
|
||||
List<Job> list=jobApplyService.selectCencalList(queryApply);
|
||||
return getDataTable(list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
package com.ruoyi.cms.controller.app;
|
||||
|
||||
import com.alibaba.nls.client.AccessToken;
|
||||
import com.ruoyi.cms.handler.SpeechRecognizerAI;
|
||||
import com.ruoyi.common.annotation.BussinessLog;
|
||||
import com.ruoyi.common.config.AudioTextRequestClient;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import static com.ruoyi.common.enums.BusinessType.OTHER;
|
||||
|
||||
/**
|
||||
* app语音Controller
|
||||
@@ -21,17 +29,49 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@Api(tags = "移动端:用户相关")
|
||||
public class AppSpeechController extends BaseController
|
||||
{
|
||||
private String appKey = "4lFYn2yPsQymwGu8";
|
||||
/*private String appKey = "4lFYn2yPsQymwGu8";
|
||||
private String id = "LTAI5t9hhSqdDHqwH3RjgyYj";
|
||||
private String secret = "ni5aW3vxrWouMwcGqJPfh9Uu56PBuv";
|
||||
private String url = System.getenv().getOrDefault("NLS_GATEWAY_URL", "wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1");
|
||||
@ApiOperation("统计")
|
||||
private String url = System.getenv().getOrDefault("NLS_GATEWAY_URL", AliyunNlsUtils.getNlsUrl()+"/ws/v1/");*/
|
||||
|
||||
@Autowired
|
||||
AudioTextRequestClient audioTextRequestClient;
|
||||
|
||||
@BussinessLog(title = "语音转文字",businessType = OTHER)
|
||||
@ApiOperation("语音转文字")
|
||||
@PostMapping(value = "/asr")
|
||||
public AjaxResult asr(@RequestParam("file") MultipartFile file){
|
||||
try {
|
||||
return AjaxResult.success("请求成功",audioTextRequestClient.getTextFromAudioFile(file));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@BussinessLog(title = "文字转语音",businessType = OTHER)
|
||||
@ApiOperation("文字转语音")
|
||||
@GetMapping(value = "/tts")
|
||||
public ResponseEntity<byte[]> tts(@RequestParam("text") String text) throws UnsupportedEncodingException {
|
||||
byte[] wavData = audioTextRequestClient.getAudioInputStreamFromText(text);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
// WAV音频的标准MIME类型
|
||||
headers.setContentType(MediaType.parseMediaType("audio/wav"));
|
||||
// inline:让浏览器在线播放(而非下载)
|
||||
headers.setContentDispositionFormData("inline", System.currentTimeMillis() + ".wav");
|
||||
// 设置内容长度
|
||||
headers.setContentLength(wavData.length);
|
||||
// 4. 返回音频数据
|
||||
return new ResponseEntity<>(wavData, headers, HttpStatus.OK);
|
||||
}
|
||||
|
||||
/*@ApiOperation("统计")
|
||||
@GetMapping("/getToken")
|
||||
public AjaxResult getToken()
|
||||
{
|
||||
SpeechRecognizerAI recognizerDemo = new SpeechRecognizerAI(appKey, id, secret, url);
|
||||
AccessToken accessToken = recognizerDemo.getAccessToken();
|
||||
String token = "wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1?appkey="+appKey+"&token="+accessToken.getToken();
|
||||
String token = AliyunNlsUtils.getNlsUrl()+"/ws/v1/?appkey="+appKey+"&token="+accessToken.getToken();
|
||||
return AjaxResult.success(token);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -76,6 +76,18 @@ public class AppUserController extends BaseController
|
||||
AppUser appUser = appUserService.selectAppUserByUserId(SiteSecurityUtils.getUserId());
|
||||
return AjaxResult.success(appUser);
|
||||
}
|
||||
|
||||
@ApiOperation("企业查看简历")
|
||||
@GetMapping("/userResume/{id}")
|
||||
public AjaxResult getResume(@PathVariable(name = "id", required = true) Long userId)
|
||||
{
|
||||
if(userId==null){
|
||||
return AjaxResult.error("用户id为空!");
|
||||
}
|
||||
AppUser appUser = appUserService.selectAppUserByUserId(userId);
|
||||
return AjaxResult.success(appUser);
|
||||
}
|
||||
|
||||
@ApiOperation("我的浏览")
|
||||
@GetMapping("/review")
|
||||
public TableDataInfo review(MineJobQuery jobQuery)
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
package com.ruoyi.cms.controller.app;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.cms.domain.ai.AiChatHistory;
|
||||
import com.ruoyi.cms.domain.chat.ChatRequest;
|
||||
import com.ruoyi.cms.service.AiChatHistoryService;
|
||||
import com.ruoyi.cms.util.ChatTextCleanUtil;
|
||||
import com.ruoyi.common.annotation.BussinessLog;
|
||||
import com.ruoyi.cms.config.ChatClient;
|
||||
import com.ruoyi.cms.config.ChatConfig;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.utils.SiteSecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.ruoyi.common.enums.BusinessType.QUERY;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/app/chat")
|
||||
@Slf4j
|
||||
@Api(tags = "移动端:ai对话")
|
||||
public class ChatController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
ChatClient chatClient;
|
||||
@Autowired
|
||||
ChatConfig chatConfig;
|
||||
@Autowired
|
||||
AiChatHistoryService aiChatHistoryService;
|
||||
|
||||
//private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
// 可优化线程池配置,避免无限创建线程
|
||||
private final ExecutorService executor = new ThreadPoolExecutor(
|
||||
2, // 核心线程数
|
||||
10, // 最大线程数
|
||||
30, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(50), // 任务队列
|
||||
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略(避免任务丢失)
|
||||
);
|
||||
|
||||
@BussinessLog(title = "查询用户的聊天历史记录",businessType = QUERY)
|
||||
@ApiOperation("查询用户的聊天历史记录")
|
||||
@GetMapping(value = "/getHistory")
|
||||
public AjaxResult getChatHistoryList(AiChatHistory chatHistory) {
|
||||
return AjaxResult.success(aiChatHistoryService.getList(chatHistory));
|
||||
}
|
||||
|
||||
@BussinessLog(title = "查询用户的聊天详情",businessType = QUERY)
|
||||
@ApiOperation("查询用户的聊天详情")
|
||||
@GetMapping(value = "/detail")
|
||||
public AjaxResult getChatDetail(ChatRequest request) {
|
||||
return AjaxResult.success(aiChatHistoryService.getDetailList(request.getSessionId()));
|
||||
}
|
||||
|
||||
// 处理前端聊天请求,返回 SSE 发射器
|
||||
@ApiOperation("用户AI聊天")
|
||||
@PostMapping("/chat")
|
||||
@ResponseBody
|
||||
@BussinessLog(title = "用户AI聊天",businessType = QUERY)
|
||||
public SseEmitter chatStream(@RequestBody ChatRequest request) {
|
||||
// 设置超时时间(30分钟)
|
||||
SseEmitter emitter = new SseEmitter(1800000L);
|
||||
long userId = 0;
|
||||
if(ObjectUtil.isNotEmpty(request.getUserId())&&request.getUserId()!=0){
|
||||
userId = request.getUserId();
|
||||
}else{
|
||||
try {
|
||||
userId = SiteSecurityUtils.getLoginUser().getUserId();
|
||||
}catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject contentObject = new JSONObject();
|
||||
contentObject.put("content",request.getData());
|
||||
contentObject.put("role","user");
|
||||
JSONArray array = aiChatHistoryService.getChatHistoryData(request.getSessionId());
|
||||
if(array==null||array.isEmpty()||array.size()==0){
|
||||
array = new JSONArray();
|
||||
}
|
||||
array.add(contentObject);
|
||||
request.setMessages(array);
|
||||
|
||||
List<String> answerList = new ArrayList<>();
|
||||
long[] timeStart = {0};
|
||||
// 异步处理请求并推送数据
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
// 2. 调用ChatClient的流式聊天方法,正确传递参数
|
||||
chatClient.sendStreamingChat(
|
||||
request,
|
||||
new ChatClient.StreamCallback() { // 使用内部类完整路径
|
||||
@Override
|
||||
public void onData(String chunk) {
|
||||
try {
|
||||
if(timeStart[0] == 0){
|
||||
timeStart[0] = System.currentTimeMillis();
|
||||
}
|
||||
// 1. 处理SSE协议格式
|
||||
String processedChunk = chunk.trim();
|
||||
// 解析返回的chunk数据
|
||||
String content = parseChatChunk(processedChunk);
|
||||
if (StringUtils.isNotEmpty(content)) {
|
||||
answerList.add(content);
|
||||
}
|
||||
emitter.send(processedChunk, MediaType.TEXT_EVENT_STREAM);
|
||||
} catch (IOException e) {
|
||||
emitter.completeWithError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
emitter.complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
try {
|
||||
emitter.send(SseEmitter.event()
|
||||
.data("{\"status\":\"error\",\"message\":\"" + e.getMessage() + "\"}")
|
||||
.name("error"));
|
||||
} catch (IOException ex) {
|
||||
// 记录日志
|
||||
} finally {
|
||||
emitter.completeWithError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (Exception e) {
|
||||
emitter.completeWithError(e);
|
||||
}
|
||||
});
|
||||
|
||||
// 处理连接关闭
|
||||
long finalUserId = userId;
|
||||
emitter.onCompletion(() -> {
|
||||
// 此处仅做资源清理,不关闭线程池
|
||||
log.info("连接关闭,清理资源");
|
||||
long timeEnd = System.currentTimeMillis();
|
||||
double duration = (timeEnd - timeStart[0]) / 1000.0;
|
||||
AiChatHistory chatHistory = new AiChatHistory();
|
||||
chatHistory.setChatId(request.getSessionId());
|
||||
chatHistory.setUserId((finalUserId==0)?null:finalUserId);
|
||||
chatHistory.setAppId(chatConfig.getAppId());
|
||||
chatHistory.setDataId(request.getDataId());
|
||||
chatHistory.setTitle(request.getData());
|
||||
chatHistory.setAnswerStringList(answerList);
|
||||
chatHistory.setDurationSeconds(duration);
|
||||
aiChatHistoryService.saveChatHistory(chatHistory);
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
|
||||
@BussinessLog(title = "提供推测出的问询建议",businessType = QUERY)
|
||||
@ApiOperation("提供推测出的问询建议")
|
||||
@PostMapping(value = "/guest")
|
||||
public AjaxResult getChatGuest(@RequestBody ChatRequest request) throws IOException {
|
||||
JSONArray array = aiChatHistoryService.getChatHistoryData(request.getSessionId());
|
||||
request.setMessages(array);
|
||||
String result = chatClient.sendChatGuest(request);
|
||||
//去除思维链
|
||||
result = ChatTextCleanUtil.removeThinkChain(result);
|
||||
try {
|
||||
String[] strList = result.split("?");
|
||||
List<String> list = new ArrayList<>();
|
||||
for(String str:strList){
|
||||
if(StringUtils.isNotEmpty(str)){
|
||||
str = str+"?";
|
||||
list.add(str);
|
||||
}
|
||||
}
|
||||
return AjaxResult.success(list);
|
||||
}catch (Exception e) {
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 解析返回的流式数据
|
||||
private String parseChatChunk(String chunk) {
|
||||
try {
|
||||
String processed = chunk.trim();
|
||||
if (processed.startsWith("data:")) {
|
||||
processed = processed.substring("data:".length()).trim();
|
||||
}
|
||||
if (processed.isEmpty() || "[DONE]".equals(processed)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject json = JSONObject.parseObject(processed);
|
||||
String choices = json.getString("choices");
|
||||
if (choices != null && !choices.trim().isEmpty()) {
|
||||
JSONArray jsonArray = JSONArray.parseArray(choices);
|
||||
json = JSONObject.parseObject(jsonArray.getString(0));
|
||||
json = json.getJSONObject("delta");
|
||||
String content = json.getString("content");// 消息内容
|
||||
|
||||
return content;
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
executor.shutdown(); // 应用退出前关闭线程池
|
||||
try {
|
||||
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
executor.shutdownNow(); // 强制关闭未完成的任务
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
executor.shutdownNow();
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
package com.ruoyi.cms.controller.app;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.cms.config.ChatClient;
|
||||
import com.ruoyi.cms.config.ChatConfig;
|
||||
import com.ruoyi.cms.domain.ai.AiChatHistory;
|
||||
import com.ruoyi.cms.domain.chat.ChatRequest;
|
||||
import com.ruoyi.cms.service.AiChatHistoryService;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.websocket.*;
|
||||
import javax.websocket.server.PathParam;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* WebSocket AI 聊天处理器
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ServerEndpoint("/ws/chat/{userId}")
|
||||
public class ChatWebSocketHandler {
|
||||
|
||||
// 存储所有连接的会话
|
||||
private static final Map<String, Session> SESSIONS = new ConcurrentHashMap<>();
|
||||
|
||||
// 由于 WebSocket 是多例的,需要通过 SpringUtils 获取 Bean
|
||||
private ChatClient getChatClient() {
|
||||
return SpringUtils.getBean(ChatClient.class);
|
||||
}
|
||||
|
||||
private ChatConfig getChatConfig() {
|
||||
return SpringUtils.getBean(ChatConfig.class);
|
||||
}
|
||||
|
||||
private AiChatHistoryService getAiChatHistoryService() {
|
||||
return SpringUtils.getBean(AiChatHistoryService.class);
|
||||
}
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam("userId") String userId) {
|
||||
SESSIONS.put(userId + "_" + session.getId(), session);
|
||||
log.info("WebSocket 连接建立,userId: {}, sessionId: {}", userId, session.getId());
|
||||
sendMessage(session, buildResponse("connected", "连接成功", null));
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose(Session session, @PathParam("userId") String userId) {
|
||||
SESSIONS.remove(userId + "_" + session.getId());
|
||||
log.info("WebSocket 连接关闭,userId: {}, sessionId: {}", userId, session.getId());
|
||||
}
|
||||
|
||||
@OnError
|
||||
public void onError(Session session, Throwable error, @PathParam("userId") String userId) {
|
||||
log.error("WebSocket 发生错误,userId: {}, error: {}", userId, error.getMessage());
|
||||
sendMessage(session, buildResponse("error", error.getMessage(), null));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 接收客户端消息
|
||||
* 消息格式:
|
||||
* {
|
||||
* "action": "chat", // chat: 聊天, history: 获取历史, detail: 获取详情, guest: 获取建议
|
||||
* "data": "用户输入的内容",
|
||||
* "sessionId": "会话ID",
|
||||
* "dataId": "数据ID",
|
||||
* "fileUrl": ["文件URL列表"]
|
||||
* }
|
||||
*/
|
||||
@OnMessage
|
||||
public void onMessage(String message, Session session, @PathParam("userId") String userId) {
|
||||
log.info("收到消息,userId: {}, message: {}", userId, message);
|
||||
try {
|
||||
JSONObject json = JSONObject.parseObject(message);
|
||||
String action = json.getString("action");
|
||||
|
||||
if (StringUtils.isEmpty(action)) {
|
||||
action = "chat";
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case "chat":
|
||||
handleChat(json, session, userId);
|
||||
break;
|
||||
case "history":
|
||||
handleHistory(json, session);
|
||||
break;
|
||||
case "detail":
|
||||
handleDetail(json, session);
|
||||
break;
|
||||
case "guest":
|
||||
handleGuest(json, session);
|
||||
break;
|
||||
default:
|
||||
sendMessage(session, buildResponse("error", "未知的操作类型: " + action, null));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理消息失败: {}", e.getMessage(), e);
|
||||
sendMessage(session, buildResponse("error", "处理消息失败: " + e.getMessage(), null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理聊天请求
|
||||
*/
|
||||
private void handleChat(JSONObject json, Session session, String userId) {
|
||||
ChatRequest request = buildChatRequest(json, userId);
|
||||
|
||||
JSONObject contentObject = new JSONObject();
|
||||
contentObject.put("content", request.getData());
|
||||
contentObject.put("role", "user");
|
||||
|
||||
JSONArray array = getAiChatHistoryService().getChatHistoryData(request.getSessionId());
|
||||
if (array == null || array.isEmpty()) {
|
||||
array = new JSONArray();
|
||||
}
|
||||
array.add(contentObject);
|
||||
request.setMessages(array);
|
||||
|
||||
List<String> answerList = new ArrayList<>();
|
||||
long[] timeStart = {0};
|
||||
|
||||
getChatClient().sendStreamingChat(request, new ChatClient.StreamCallback() {
|
||||
@Override
|
||||
public void onData(String chunk) {
|
||||
if (timeStart[0] == 0) {
|
||||
timeStart[0] = System.currentTimeMillis();
|
||||
}
|
||||
String processedChunk = chunk.trim();
|
||||
String content = parseChatChunk(processedChunk);
|
||||
if (StringUtils.isNotEmpty(content)) {
|
||||
answerList.add(content);
|
||||
}
|
||||
// 发送流式数据
|
||||
sendMessage(session, buildResponse("data", null, processedChunk));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
// 保存聊天记录
|
||||
saveChatHistory(request, userId, answerList, timeStart[0]);
|
||||
sendMessage(session, buildResponse("complete", "对话完成", null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
log.error("聊天请求失败: {}", e.getMessage());
|
||||
sendMessage(session, buildResponse("error", e.getMessage(), null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理获取历史记录请求
|
||||
*/
|
||||
private void handleHistory(JSONObject json, Session session) {
|
||||
AiChatHistory chatHistory = new AiChatHistory();
|
||||
if (json.containsKey("userId")) {
|
||||
chatHistory.setUserId(json.getLong("userId"));
|
||||
}
|
||||
JSONObject result = getAiChatHistoryService().getList(chatHistory);
|
||||
sendMessage(session, buildResponse("history", null, result.toJSONString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理获取聊天详情请求
|
||||
*/
|
||||
private void handleDetail(JSONObject json, Session session) {
|
||||
String sessionId = json.getString("sessionId");
|
||||
JSONObject result = getAiChatHistoryService().getDetailList(sessionId);
|
||||
sendMessage(session, buildResponse("detail", null, result.toJSONString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理获取建议请求
|
||||
*/
|
||||
private void handleGuest(JSONObject json, Session session) {
|
||||
try {
|
||||
ChatRequest request = new ChatRequest();
|
||||
request.setSessionId(json.getString("sessionId"));
|
||||
JSONArray array = getAiChatHistoryService().getChatHistoryData(request.getSessionId());
|
||||
request.setMessages(array);
|
||||
|
||||
String result = getChatClient().sendChatGuest(request);
|
||||
String[] strList = result.split("?");
|
||||
List<String> list = new ArrayList<>();
|
||||
for (String str : strList) {
|
||||
if (StringUtils.isNotEmpty(str)) {
|
||||
str = str + "?";
|
||||
list.add(str);
|
||||
}
|
||||
}
|
||||
sendMessage(session, buildResponse("guest", null, JSONObject.toJSONString(list)));
|
||||
} catch (Exception e) {
|
||||
log.error("获取建议失败: {}", e.getMessage());
|
||||
sendMessage(session, buildResponse("error", "获取建议失败: " + e.getMessage(), null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 ChatRequest 对象
|
||||
*/
|
||||
private ChatRequest buildChatRequest(JSONObject json, String userId) {
|
||||
ChatRequest request = new ChatRequest();
|
||||
request.setData(json.getString("data"));
|
||||
request.setSessionId(json.getString("sessionId"));
|
||||
request.setDataId(json.getString("dataId"));
|
||||
|
||||
if (json.containsKey("fileUrl")) {
|
||||
request.setFileUrl(json.getJSONArray("fileUrl").toJavaList(String.class));
|
||||
}
|
||||
|
||||
if (json.containsKey("userId") && json.getLong("userId") != 0) {
|
||||
request.setUserId(json.getLong("userId"));
|
||||
} else if (StringUtils.isNotEmpty(userId) && !"0".equals(userId)) {
|
||||
request.setUserId(Long.parseLong(userId));
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存聊天记录
|
||||
*/
|
||||
private void saveChatHistory(ChatRequest request, String userId, List<String> answerList, long timeStart) {
|
||||
long timeEnd = System.currentTimeMillis();
|
||||
double duration = (timeEnd - timeStart) / 1000.0;
|
||||
|
||||
AiChatHistory chatHistory = new AiChatHistory();
|
||||
chatHistory.setChatId(request.getSessionId());
|
||||
|
||||
Long uid = null;
|
||||
if (request.getUserId() != 0) {
|
||||
uid = request.getUserId();
|
||||
} else if (StringUtils.isNotEmpty(userId) && !"0".equals(userId)) {
|
||||
uid = Long.parseLong(userId);
|
||||
}
|
||||
chatHistory.setUserId(uid);
|
||||
chatHistory.setAppId(getChatConfig().getAppId());
|
||||
chatHistory.setDataId(request.getDataId());
|
||||
chatHistory.setTitle(request.getData());
|
||||
chatHistory.setAnswerStringList(answerList);
|
||||
chatHistory.setDurationSeconds(duration);
|
||||
|
||||
getAiChatHistoryService().saveChatHistory(chatHistory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析流式数据
|
||||
*/
|
||||
private String parseChatChunk(String chunk) {
|
||||
try {
|
||||
String processed = chunk.trim();
|
||||
if (processed.startsWith("data:")) {
|
||||
processed = processed.substring("data:".length()).trim();
|
||||
}
|
||||
if (processed.isEmpty() || "[DONE]".equals(processed)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject json = JSONObject.parseObject(processed);
|
||||
String choices = json.getString("choices");
|
||||
if (choices != null && !choices.trim().isEmpty()) {
|
||||
JSONArray jsonArray = JSONArray.parseArray(choices);
|
||||
json = JSONObject.parseObject(jsonArray.getString(0));
|
||||
json = json.getJSONObject("delta");
|
||||
return json.getString("content");
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建响应消息
|
||||
*/
|
||||
private String buildResponse(String type, String message, String data) {
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("type", type);
|
||||
if (message != null) {
|
||||
response.put("message", message);
|
||||
}
|
||||
if (data != null) {
|
||||
response.put("data", data);
|
||||
}
|
||||
return response.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*/
|
||||
private void sendMessage(Session session, String message) {
|
||||
if (session != null && session.isOpen()) {
|
||||
try {
|
||||
synchronized (session) {
|
||||
session.getBasicRemote().sendText(message);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("发送消息失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定用户发送消息
|
||||
*/
|
||||
public static void sendMessageToUser(String userId, String message) {
|
||||
SESSIONS.forEach((key, session) -> {
|
||||
if (key.startsWith(userId + "_") && session.isOpen()) {
|
||||
try {
|
||||
synchronized (session) {
|
||||
session.getBasicRemote().sendText(message);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("发送消息给用户 {} 失败: {}", userId, e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import com.ruoyi.cms.domain.vo.AppUserLky;
|
||||
import com.ruoyi.cms.service.IAppReviewJobService;
|
||||
import com.ruoyi.cms.util.DateValidateUtil;
|
||||
import com.ruoyi.cms.util.RoleUtils;
|
||||
import com.ruoyi.cms.util.StringUtil;
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.annotation.BussinessLog;
|
||||
import com.ruoyi.common.core.domain.entity.AppUserShow;
|
||||
import com.ruoyi.common.core.domain.model.RegisterBody;
|
||||
@@ -64,6 +66,10 @@ public class CmsAppUserController extends BaseController
|
||||
{
|
||||
startPage();
|
||||
List<AppUser> list = appUserService.selectAppUserList(appUser);
|
||||
//身份证脱敏
|
||||
list.forEach(it->{
|
||||
it.setIdCard(StringUtil.desensitizeIdCard(it.getIdCard()));
|
||||
});
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@@ -166,6 +172,10 @@ public class CmsAppUserController extends BaseController
|
||||
{
|
||||
startPage();
|
||||
List<AppUser> list = appUserService.selectNoTmAppUserList(appUser);
|
||||
//身份证脱敏
|
||||
list.forEach(it->{
|
||||
it.setIdCard(StringUtil.desensitizeIdCard(it.getIdCard()));
|
||||
});
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@@ -264,4 +274,27 @@ public class CmsAppUserController extends BaseController
|
||||
}
|
||||
return toAjax(appReviewJobService.insertAppReviewJob(appReviewJob));
|
||||
}
|
||||
|
||||
/**
|
||||
* 浪潮需要根据用户id查询简历
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation("查看简历")
|
||||
@GetMapping("/userInfoResume/{userId}")
|
||||
@Anonymous
|
||||
public AjaxResult getUserInfoResume(@PathVariable("userId") Long userId)
|
||||
{
|
||||
if(userId==null){
|
||||
return error("用户id为空!");
|
||||
}
|
||||
try {
|
||||
AppUser result=appUserService.selectAppUserByUserId(userId);
|
||||
if (result == null) {
|
||||
return error("暂无该用户的简历信息");
|
||||
}
|
||||
return success(result);
|
||||
} catch (Exception e) {
|
||||
return error("查询简历失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import com.ruoyi.cms.domain.*;
|
||||
import com.ruoyi.cms.domain.query.ESJobSearch;
|
||||
import com.ruoyi.cms.domain.vo.CandidateVO;
|
||||
import com.ruoyi.cms.domain.vo.CompanyVo;
|
||||
import com.ruoyi.cms.domain.vo.JobExcelVo;
|
||||
import com.ruoyi.cms.service.*;
|
||||
import com.ruoyi.cms.util.EasyExcelUtils;
|
||||
import com.ruoyi.cms.util.RoleUtils;
|
||||
import com.ruoyi.cms.util.StringUtil;
|
||||
import com.ruoyi.cms.util.sensitiveWord.SensitiveWordChecker;
|
||||
@@ -24,11 +26,15 @@ import com.ruoyi.common.utils.bean.BeanUtils;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -66,14 +72,15 @@ public class CmsJobController extends BaseController
|
||||
@ApiOperation("查询岗位列表")
|
||||
// @PreAuthorize("@ss.hasPermi('cms:job:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(Job job)
|
||||
public TableDataInfo list(Job job,HttpServletRequest request)
|
||||
{
|
||||
if (RoleUtils.isCompanyAdmin()) {
|
||||
Company company = companyService.queryCodeCompany(RoleUtils.getCurrentUseridCard());
|
||||
job.setCompanyId(Objects.nonNull(company) ? company.getCompanyId() : null);
|
||||
}
|
||||
startPage();
|
||||
List<Job> list = jobService.selectJobList(job);
|
||||
//List<Job> list = jobService.selectJobList(job);
|
||||
List<Job> list = jobService.selectHttpJobList(job,request);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@@ -83,9 +90,13 @@ public class CmsJobController extends BaseController
|
||||
@ApiOperation("获取岗位详细信息")
|
||||
// @PreAuthorize("@ss.hasPermi('bussiness:job:query')")
|
||||
@GetMapping(value = "/{jobId}")
|
||||
public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
|
||||
public AjaxResult getInfo(@PathVariable("jobId") Long jobId, HttpServletRequest request)
|
||||
{
|
||||
return success(jobService.selectJobByJobId(jobId));
|
||||
if (jobId == null) {
|
||||
return AjaxResult.error("jobId不能为空");
|
||||
}
|
||||
//return success(jobService.selectJobByJobId(jobId));
|
||||
return success(jobService.selectHttpJobByJobId(jobId,request));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +180,11 @@ public class CmsJobController extends BaseController
|
||||
esJobSearch.setCode(RoleUtils.getCurrentUseridCard());
|
||||
esJobSearch.setUserType(StringUtil.IS_COMPANY_USER);
|
||||
}
|
||||
esJobSearch.setPageSize(20);
|
||||
//判断pageSize
|
||||
Integer pageSize=esJobSearch.getPageSize();
|
||||
if (pageSize == null || pageSize <= 0) {
|
||||
esJobSearch.setPageSize(20);
|
||||
}
|
||||
List<ESJobDocument> jobList = jobService.sysRecommend(esJobSearch);
|
||||
List<Job> jobs=new ArrayList<>();
|
||||
jobList.stream().forEach(it->{
|
||||
@@ -331,4 +346,99 @@ public class CmsJobController extends BaseController
|
||||
});
|
||||
return success(jobList);
|
||||
}
|
||||
|
||||
@PostMapping("/wechat")
|
||||
@ApiOperation("微信抓取功能调用的新增")
|
||||
public AjaxResult wechatInsert(@RequestBody Job job) {
|
||||
// 不发布
|
||||
job.setIsPublish(0);
|
||||
if (job.getJobContactList() == null) {
|
||||
job.setJobContactList(new ArrayList<>());
|
||||
}
|
||||
Integer total=jobService.getTotals(job);
|
||||
int totalNum = total != null ? total : 0;
|
||||
System.out.println("岗位条数======================"+totalNum);
|
||||
if (totalNum == 0) {
|
||||
return toAjax(jobService.insertJob(job));
|
||||
}
|
||||
return AjaxResult.success("此岗位已存在!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用上传请求(单个)
|
||||
*/
|
||||
@PostMapping("/uploadFile")
|
||||
@ApiOperation("岗位批量上传")
|
||||
public AjaxResult uploadFile(@RequestParam("file") MultipartFile file) throws Exception {
|
||||
if (file.isEmpty()) {
|
||||
return AjaxResult.error("上传文件不能为空");
|
||||
}
|
||||
String fileName = file.getOriginalFilename();
|
||||
if (fileName == null || !fileName.endsWith(".xlsx") && !fileName.endsWith(".xls")) {
|
||||
return AjaxResult.error("请上传Excel格式的文件");
|
||||
}
|
||||
|
||||
try (InputStream inputStream = file.getInputStream()){
|
||||
List<JobExcelVo> allExcelVoList = new ArrayList<>();
|
||||
EasyExcelUtils.readExcelByBatch(inputStream, JobExcelVo.class, 100, list -> {
|
||||
allExcelVoList.addAll(list);
|
||||
});
|
||||
if (CollectionUtils.isEmpty(allExcelVoList)) {
|
||||
throw new Exception("Excel文件中无有效数据");
|
||||
}
|
||||
jobService.uploadFileJob(allExcelVoList);
|
||||
return AjaxResult.success("已上传!");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板下载
|
||||
* @param request
|
||||
* @param response
|
||||
* @throws Exception
|
||||
*/
|
||||
@PostMapping("/downloadModel")
|
||||
public void downloadModel(HttpServletRequest request, HttpServletResponse response)throws Exception{
|
||||
String name = "模板.xlsx";
|
||||
String pathFile="/data/downloadmodel/"+name;
|
||||
File url = new File(pathFile);
|
||||
|
||||
String resMsg = "";
|
||||
try {
|
||||
request.setCharacterEncoding("utf-8");
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
try {
|
||||
name = new String(name.getBytes("gb2312"), "ISO8859-1");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
response.reset();
|
||||
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Content-Disposition", "attachment; filename="+ name);
|
||||
response.setHeader("Pragma", "public");
|
||||
response.setHeader("Cache-Control", "max-age=0");
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(url);
|
||||
} catch (FileNotFoundException e1) {
|
||||
resMsg = "文件未找到";
|
||||
e1.printStackTrace();
|
||||
response.getWriter().write(resMsg + ":" + name);
|
||||
}
|
||||
OutputStream ou = response.getOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int i = -1;
|
||||
while ((i = in.read(buffer)) != -1) {
|
||||
ou.write(buffer, 0, i);
|
||||
}
|
||||
ou.flush();
|
||||
ou.close();
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ public class CmsNoticeController extends BaseController
|
||||
@GetMapping("/noticTotal")
|
||||
public AjaxResult getNoticTotal(Notice notice){
|
||||
if(!SecurityUtils.isLogin()){
|
||||
return AjaxResult.error("未登录!,返回为空");
|
||||
return AjaxResult.success("未登录!,返回为空");
|
||||
}
|
||||
if(notice.getUserId()==null){
|
||||
String idCard= RoleUtils.getCurrentUseridCard();
|
||||
|
||||
@@ -65,12 +65,14 @@ public class CmsSkillController extends BaseController {
|
||||
@Log(title = "技能", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/add")
|
||||
public AjaxResult save(@RequestBody AppSkill appSkill){
|
||||
if(!SecurityUtils.isLogin()){
|
||||
if(SecurityUtils.isLogin()){
|
||||
AppUser appUser=appUserService.selectAppuserByIdcard(RoleUtils.getCurrentUseridCard());
|
||||
if(appUser==null){
|
||||
return AjaxResult.error("未传递userId!");
|
||||
}
|
||||
appSkill.setUserId(appUser.getUserId());
|
||||
}else {
|
||||
return AjaxResult.error("未登录!");
|
||||
}
|
||||
return toAjax(appSkillService.insertAppskill(appSkill));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.ruoyi.cms.controller.cms;
|
||||
|
||||
import com.ruoyi.cms.domain.CommunityUser;
|
||||
import com.ruoyi.cms.service.ICommunityUserService;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/cms/communityUser")
|
||||
@Api(tags = "后台:工作人员管理")
|
||||
public class CommunityUserController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private ICommunityUserService communityUserService;
|
||||
|
||||
|
||||
@ApiOperation("查询工作人员列表")
|
||||
@PreAuthorize("@ss.hasPermi('application:mgmt:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(CommunityUser communityUser) {
|
||||
startPage();
|
||||
List<CommunityUser> list = communityUserService.selectCommunityUserList(communityUser);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@ApiOperation("新增工作人员")
|
||||
@PreAuthorize("@ss.hasPermi('application:mgmt:add')")
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody CommunityUser communityUser) {
|
||||
return toAjax(communityUserService.save(communityUser));
|
||||
}
|
||||
|
||||
@ApiOperation("修改工作人员")
|
||||
@PreAuthorize("@ss.hasPermi('application:mgmt:edit')")
|
||||
@PutMapping
|
||||
public AjaxResult update(@RequestBody CommunityUser communityUser) {
|
||||
return toAjax(communityUserService.updateById(communityUser));
|
||||
}
|
||||
|
||||
@ApiOperation("删除工作人员")
|
||||
@PreAuthorize("@ss.hasPermi('application:mgmt:del')")
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable("ids") Long[] ids) {
|
||||
return toAjax(communityUserService.delCommunityUser(ids));
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package com.ruoyi.cms.controller.cms;
|
||||
|
||||
import com.ruoyi.cms.domain.BussinessDictType;
|
||||
import com.ruoyi.cms.domain.query.Staticsquery;
|
||||
import com.ruoyi.cms.service.StaticsqueryService;
|
||||
import com.ruoyi.cms.util.DateValidateUtil;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@@ -100,4 +100,24 @@ public class StaticsController extends BaseController {
|
||||
Map<String,Object> result = service.educationSalary(staticsquery);
|
||||
return success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业岗位统计
|
||||
* @param staticsquery
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/qygwtjCount")
|
||||
public AjaxResult qygwtjCount(@RequestBody Staticsquery staticsquery)
|
||||
{
|
||||
if(staticsquery==null){
|
||||
return error("参数为空!");
|
||||
}
|
||||
//判断时间
|
||||
String timeError = DateValidateUtil.validateStartAndEndTime(staticsquery.getStartTime(),staticsquery.getEndTime());
|
||||
if (StringUtils.isNotBlank(timeError)) {
|
||||
return error(timeError);
|
||||
}
|
||||
Map<String,Object> result = service.qygwtjCount(staticsquery);
|
||||
return success(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,14 @@ public class SysAreaController {
|
||||
public AjaxResult jobCategory(SysArea sysArea){
|
||||
return AjaxResult.success(sysAreaService.getList(sysArea));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有市
|
||||
* @param sysArea
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/listCity")
|
||||
public AjaxResult listCity(SysArea sysArea){
|
||||
return AjaxResult.success(sysAreaService.getCityList(sysArea));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.ruoyi.cms.controller.cms;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.ruoyi.cms.domain.WechatGroup;
|
||||
import com.ruoyi.cms.domain.vo.WechatGroupVo;
|
||||
import com.ruoyi.cms.service.IWechatGroupService;
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/cms/wechatGroup")
|
||||
@Api(tags = "后台:转发对象管理")
|
||||
public class WechatGroupController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IWechatGroupService wechatGroupService;
|
||||
|
||||
|
||||
@ApiOperation("查询转发对象列表")
|
||||
@PreAuthorize("@ss.hasPermi('application:group:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(WechatGroup wechatGroup) {
|
||||
startPage();
|
||||
List<WechatGroupVo> list = wechatGroupService.selectWechatGroupList(wechatGroup);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@ApiOperation("新增转发对象")
|
||||
@PreAuthorize("@ss.hasPermi('application:group:add')")
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody WechatGroup wechatGroup) {
|
||||
return toAjax(wechatGroupService.save(wechatGroup));
|
||||
}
|
||||
|
||||
@ApiOperation("修改转发对象")
|
||||
@PreAuthorize("@ss.hasPermi('application:group:update')")
|
||||
@PutMapping
|
||||
public AjaxResult update(@RequestBody WechatGroup wechatGroup) {
|
||||
return toAjax(wechatGroupService.updateById(wechatGroup));
|
||||
}
|
||||
|
||||
@ApiOperation("删除转发对象")
|
||||
@PreAuthorize("@ss.hasPermi('application:group:del')")
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable("ids") Long[] ids) {
|
||||
return toAjax(wechatGroupService.removeBatchByIds(CollUtil.newArrayList(ids)));
|
||||
}
|
||||
|
||||
@GetMapping("/enableList")
|
||||
@Anonymous
|
||||
public List<WechatGroupVo> enableList() {
|
||||
WechatGroup wechatGroup = new WechatGroup();
|
||||
wechatGroup.setIsPush(1);
|
||||
return wechatGroupService.selectWechatGroupList(wechatGroup)
|
||||
.stream().peek(e->e.setPhoneNumber(""))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.ruoyi.cms.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 工作人员配置
|
||||
*/
|
||||
@TableName("community_user")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class CommunityUser extends BaseEntity {
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 微信名
|
||||
*/
|
||||
private String wechatName;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String phoneNumber;
|
||||
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class ESJobDocument
|
||||
@ApiModelProperty("用人单位名称")
|
||||
private String companyName;
|
||||
|
||||
@ApiModelProperty("工作地点")
|
||||
@ApiModelProperty("岗位区划")
|
||||
private String jobLocation;
|
||||
|
||||
@ApiModelProperty("工作地点区县字典代码")
|
||||
@@ -150,6 +150,15 @@ public class ESJobDocument
|
||||
@ApiModelProperty("信用代码")
|
||||
private String code;
|
||||
|
||||
@ApiModelProperty("工作地点")
|
||||
private String jobAddress;
|
||||
|
||||
@ApiModelProperty("所属行政区划")
|
||||
private String regionCode;
|
||||
|
||||
@ApiModelProperty("人员类型 1残疾人,2退伍军人")
|
||||
private String staffType;
|
||||
|
||||
@ApiModelProperty("公司信息")
|
||||
@IndexField(fieldType = FieldType.TEXT)
|
||||
private String companyVoJson;
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.ruoyi.cms.domain.vo.CompanyVo;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import com.ruoyi.common.core.domain.entity.AppUser;
|
||||
import com.ruoyi.common.core.domain.entity.Company;
|
||||
import com.ruoyi.common.core.domain.entity.File;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
@@ -59,8 +60,8 @@ public class Job extends BaseEntity
|
||||
@ApiModelProperty("用人单位名称")
|
||||
private String companyName;
|
||||
|
||||
@Excel(name = "工作地点")
|
||||
@ApiModelProperty("工作地点")
|
||||
@Excel(name = "岗位区划")
|
||||
@ApiModelProperty("岗位区划")
|
||||
private String jobLocation;
|
||||
|
||||
@ApiModelProperty("工作地点区县字典代码")
|
||||
@@ -168,6 +169,15 @@ public class Job extends BaseEntity
|
||||
@ApiModelProperty("类型 0常规岗位 1就业见习岗位 2实习实训岗位 3社区实践岗位 4零工 对应字段字典position_type")
|
||||
private String type;
|
||||
|
||||
@ApiModelProperty("工作地点")
|
||||
private String jobAddress;
|
||||
|
||||
@ApiModelProperty("所属行政区划")
|
||||
private String regionCode;
|
||||
|
||||
@ApiModelProperty("人员类型 1残疾人,2退伍军人")
|
||||
private String staffType;
|
||||
|
||||
@TableField(exist = false)
|
||||
@ApiModelProperty("岗位联系人列表")
|
||||
private List<JobContact> jobContactList;
|
||||
@@ -184,4 +194,8 @@ public class Job extends BaseEntity
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@ApiModelProperty("时间(足迹、投简历、收藏)")
|
||||
private String shareTime;
|
||||
|
||||
@TableField(exist = false)
|
||||
@ApiModelProperty("申请人列表")
|
||||
private List<AppUser> applyUsers;
|
||||
}
|
||||
|
||||
@@ -79,5 +79,5 @@ public class JobFair extends BaseEntity
|
||||
|
||||
@TableField(exist = false)
|
||||
@ApiModelProperty("是否收藏")
|
||||
public Integer isCollection;
|
||||
private Integer isCollection;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ruoyi.cms.domain;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class StaticsJob {
|
||||
@ApiModelProperty("总数")
|
||||
private String zs;
|
||||
@ApiModelProperty("高效毕业生岗位")
|
||||
private String gxbysgw;
|
||||
@ApiModelProperty("实时在招岗位数")
|
||||
private String sszzgw;
|
||||
@ApiModelProperty("简历数量")
|
||||
private String jlsl;
|
||||
@ApiModelProperty("名称")
|
||||
private String label;
|
||||
|
||||
@ApiModelProperty("归集岗位合计")
|
||||
private String gjgwhj;
|
||||
@ApiModelProperty("注册企业数")
|
||||
private String zcqys;
|
||||
@ApiModelProperty("求职者实名数")
|
||||
private String qzzsms;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.ruoyi.cms.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 转发对象配置
|
||||
*/
|
||||
@TableName("wechat_group")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class WechatGroup extends BaseEntity {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 群聊名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 工作人员 ID
|
||||
*/
|
||||
private Long communityId;
|
||||
|
||||
/**
|
||||
* 是否启用 0=禁用 1=启用
|
||||
*/
|
||||
private Integer isPush;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.ruoyi.cms.domain.ai;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@ApiModel("ai聊天详情")
|
||||
@TableName("AI_CHAT_DETAIL")
|
||||
public class AiChatDetail {
|
||||
|
||||
@TableField(exist = false)
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id",type = IdType.AUTO)
|
||||
@ApiModelProperty("id")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("会话id")
|
||||
private String chatId;
|
||||
@ApiModelProperty("数据id")
|
||||
private String dataId;
|
||||
@ApiModelProperty("会话类型,Human:用户,AI:大模型")
|
||||
private String obj;
|
||||
@ApiModelProperty("会话内容")
|
||||
private String content;
|
||||
@ApiModelProperty("会话时间")
|
||||
private Date time;
|
||||
@ApiModelProperty("耗时")
|
||||
private Double durationSeconds;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.ruoyi.cms.domain.ai;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ApiModel("ai聊天历史记录")
|
||||
@TableName("AI_CHAT_HISTORY")
|
||||
public class AiChatHistory{
|
||||
|
||||
@TableField(exist = false)
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id",type = IdType.AUTO)
|
||||
@ApiModelProperty("id")
|
||||
private Long id;
|
||||
@ApiModelProperty("用户id")
|
||||
private Long userId;
|
||||
@JSONField(name = "chatId")
|
||||
@ApiModelProperty("会话id")
|
||||
private String chatId;
|
||||
@ApiModelProperty("应用id")
|
||||
private String appId;
|
||||
@JSONField(name = "title")
|
||||
@ApiModelProperty("第一次的问题")
|
||||
private String title;
|
||||
@JSONField(name = "updateTime")
|
||||
@ApiModelProperty("会话时间")
|
||||
private Date updateTime;
|
||||
@ApiModelProperty("是否删除")
|
||||
private String delFlag;
|
||||
|
||||
@TableField(exist = false)
|
||||
private List<String> answerStringList;
|
||||
@TableField(exist = false)
|
||||
private double durationSeconds;
|
||||
@TableField(exist = false)
|
||||
private String customTitle;
|
||||
@TableField(exist = false)
|
||||
private String dataId;
|
||||
@TableField(exist = false)
|
||||
private boolean top=false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.ruoyi.cms.domain.chat;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ChatRequest {
|
||||
private String data;
|
||||
private String dataId;
|
||||
private String sessionId;
|
||||
private long userId;
|
||||
private List<String> fileUrl;
|
||||
|
||||
private JSONArray messages;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.ruoyi.cms.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 岗位对象 job
|
||||
*/
|
||||
@Data
|
||||
public class JobExcelVo
|
||||
{
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 0)
|
||||
@Excel(name = "职位名称")
|
||||
@ApiModelProperty("职位名称")
|
||||
private String jobTitle;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 1)
|
||||
@Excel(name = "最小薪资", readConverterExp = "元=")
|
||||
@ApiModelProperty("最小薪资(元)")
|
||||
private Long minSalary;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 2)
|
||||
@Excel(name = "最大薪资", readConverterExp = "元=")
|
||||
@ApiModelProperty("最大薪资(元)")
|
||||
private Long maxSalary;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 3)
|
||||
@Excel(name = "学历要求 对应字典education")
|
||||
@ApiModelProperty("学历要求 对应字典education")
|
||||
private String education;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 4)
|
||||
@Excel(name = "工作经验要求 对应字典experience")
|
||||
@ApiModelProperty("工作经验要求 对应字典experience")
|
||||
private String experience;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 5)
|
||||
@Excel(name = "用人单位名称")
|
||||
@ApiModelProperty("用人单位名称")
|
||||
private String companyName;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 6)
|
||||
@Excel(name = "招聘人数")
|
||||
@ApiModelProperty("招聘人数")
|
||||
private Long vacancies;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 7)
|
||||
@ApiModelProperty("岗位描述")
|
||||
private String description;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 8)
|
||||
@ApiModelProperty("岗位分类")
|
||||
private String jobCategory;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 9)
|
||||
@ApiModelProperty("岗位类型 0疆内 1疆外")
|
||||
private String jobType;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 10)
|
||||
@ApiModelProperty("类型 0常规岗位 1就业见习岗位 2实习实训岗位 3社区实践岗位 4零工 对应字段字典position_type")
|
||||
private String type;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 11)
|
||||
@ApiModelProperty("工作地点")
|
||||
private String jobAddress;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 12)
|
||||
@Excel(name = "岗位区划")
|
||||
@ApiModelProperty("岗位区划")
|
||||
private String jobLocation;
|
||||
|
||||
@ExcelProperty(value = "联系人", index = 13)
|
||||
@ApiModelProperty("联系人")
|
||||
private String contactPerson;
|
||||
|
||||
@ExcelProperty(value = "联系人电话", index = 14)
|
||||
@ApiModelProperty("联系人电话")
|
||||
private String contactPersonPhone;
|
||||
|
||||
@ApiModelProperty("是否发布 0未发布 1发布")
|
||||
private Integer isPublish;
|
||||
|
||||
@ApiModelProperty("工作地点区县字典代码")
|
||||
private Integer jobLocationAreaCode;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "发布时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty("发布时间")
|
||||
private String postingDate;
|
||||
|
||||
@ApiModelProperty("数据来源")
|
||||
private String dataSource;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.ruoyi.cms.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class WechatAuthVO {
|
||||
private String openid;
|
||||
private String unionid;
|
||||
private String sessionKey;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ruoyi.cms.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
public class WechatGroupVo {
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private Long id;
|
||||
private String name;
|
||||
private Integer isPush;
|
||||
private String wechatNumber;
|
||||
private String phoneNumber;
|
||||
private String wechatName;
|
||||
private Long communityId;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.ruoyi.cms.handler;
|
||||
|
||||
import com.alibaba.nls.client.protocol.asr.SpeechRecognizer;
|
||||
import com.ruoyi.cms.util.AliyunNlsUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.websocket.*;
|
||||
@@ -20,7 +20,7 @@ public class SpeechRecognitionWebSocketHandler {
|
||||
String appKey = "4lFYn2yPsQymwGu8";
|
||||
String id = "LTAI5t9hhSqdDHqwH3RjgyYj";
|
||||
String secret = "ni5aW3vxrWouMwcGqJPfh9Uu56PBuv";
|
||||
String url = System.getenv().getOrDefault("NLS_GATEWAY_URL", "wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1");
|
||||
String url = System.getenv().getOrDefault("NLS_GATEWAY_URL", AliyunNlsUtils.getNlsUrl()+"/ws/v1/");
|
||||
recognizerDemo = new SpeechRecognizerAI(appKey, id, secret, url);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.alibaba.nls.client.protocol.SampleRateEnum;
|
||||
import com.alibaba.nls.client.protocol.asr.SpeechRecognizer;
|
||||
import com.alibaba.nls.client.protocol.asr.SpeechRecognizerListener;
|
||||
import com.alibaba.nls.client.protocol.asr.SpeechRecognizerResponse;
|
||||
import com.ruoyi.cms.util.AliyunNlsUtils;
|
||||
import lombok.Data;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -26,7 +27,12 @@ public class SpeechRecognizerAI {
|
||||
// 获取 AccessToken
|
||||
accessToken = new AccessToken(id, secret);
|
||||
try {
|
||||
accessToken.apply(); // 申请 Token
|
||||
if(AliyunNlsUtils.USE_TEST_ENV){
|
||||
accessToken.apply(); // 申请 Token
|
||||
}else{
|
||||
AliyunNlsTokenUtil.generateToken(id, secret, this.accessToken);
|
||||
}
|
||||
//accessToken.apply(); // 申请 Token
|
||||
logger.info("Token: {}, Expire Time: {}", accessToken.getToken(), accessToken.getExpireTime());
|
||||
|
||||
// 初始化 NlsClient
|
||||
@@ -35,7 +41,7 @@ public class SpeechRecognizerAI {
|
||||
} else {
|
||||
this.client = new NlsClient(url, accessToken.getToken()); // 使用自定义服务地址
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to initialize NlsClient: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.alibaba.nls.client.protocol.SampleRateEnum;
|
||||
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizer;
|
||||
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerListener;
|
||||
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerResponse;
|
||||
import com.ruoyi.cms.util.AliyunNlsUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -20,18 +21,23 @@ import java.nio.ByteBuffer;
|
||||
@ServerEndpoint("/speech-synthesis")
|
||||
public class SpeechSynthesisWebSocketHandler {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SpeechSynthesisWebSocketHandler.class);
|
||||
|
||||
|
||||
private NlsClient client;
|
||||
private String appKey = "mtA2pwmvCeefHT3Y";
|
||||
private String accessKeyId = "LTAI5tRBahK93vPNF1JDVEPA";
|
||||
private String accessKeySecret = "x95OWb4cV6ccQVtbEJ2Gxm2Uwl2thJ";
|
||||
private String url = "wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1";
|
||||
private String url = AliyunNlsUtils.getNlsUrl()+"/ws/v1/";
|
||||
|
||||
public SpeechSynthesisWebSocketHandler() {
|
||||
// Initialize NLS client with token
|
||||
AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
|
||||
try {
|
||||
accessToken.apply();
|
||||
if(AliyunNlsUtils.USE_TEST_ENV){
|
||||
accessToken.apply();
|
||||
}else{
|
||||
AliyunNlsTokenUtil.generateToken(accessKeyId, accessKeySecret, accessToken);
|
||||
}
|
||||
//accessToken.apply();
|
||||
String token = accessToken.getToken();
|
||||
if(url.isEmpty()) {
|
||||
this.client = new NlsClient(token);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.cms.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.cms.domain.ai.AiChatDetail;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface AiChatDetailMapper extends BaseMapper<AiChatDetail> {
|
||||
|
||||
List<AiChatDetail> getList(AiChatDetail aiChatDetail);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.cms.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.cms.domain.ai.AiChatHistory;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface AiChatHistoryMapper extends BaseMapper<AiChatHistory> {
|
||||
|
||||
List<AiChatHistory> getList(AiChatHistory aiChatHistory);
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.ruoyi.common.core.domain.entity.AppUserShow;
|
||||
import com.ruoyi.common.core.domain.entity.MyChart;
|
||||
import com.ruoyi.common.core.domain.entity.AppUser;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* APP用户Mapper接口
|
||||
@@ -26,15 +27,15 @@ public interface AppUserMapper extends BaseMapper<AppUser>
|
||||
|
||||
List<AppUser> selectByJobId(Long jobId);
|
||||
|
||||
AppUser selectByOpenid(String openid);
|
||||
AppUser selectByOpenid(@Param("openid")String openid, @Param("userType") String userType);
|
||||
|
||||
int insertSysUserRole(Map<String,Object> map);
|
||||
|
||||
int insertSysUser(SysUser sysUser);
|
||||
|
||||
MyChart getMyTj(Long userId);
|
||||
MyChart getMyTj(@Param("userId") Long userId);
|
||||
|
||||
SysUser selectSysUserIdcard(String idCard);
|
||||
SysUser selectSysUserIdcard(@Param("idCard") String idCard);
|
||||
|
||||
List<AppUserShow> selectUserApplyList(AppUser appUser);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.ruoyi.cms.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.cms.domain.CommunityUser;
|
||||
|
||||
public interface CommunityUserMapper extends BaseMapper<CommunityUser> {
|
||||
}
|
||||
@@ -29,4 +29,6 @@ public interface CompanyMapper extends BaseMapper<Company>
|
||||
List<Company> selectLikeCompanyList(Company company);
|
||||
|
||||
Company selectCompanyByJobId(Long jobId);
|
||||
|
||||
List<Company> selectByNames(List<String> list);
|
||||
}
|
||||
|
||||
@@ -22,4 +22,8 @@ public interface FileMapper extends BaseMapper<File>
|
||||
public List<File> selectFileList(File file);
|
||||
|
||||
public int updateBussinessids(@Param("longs") List<Long> longs,@Param("newBussinessid") Long bussinessid);
|
||||
|
||||
public List<File> selectFileListByBussinessIds(@Param("longs") List<Long> longs);
|
||||
|
||||
public int updateIds(@Param("longs") List<Long> longs,@Param("newBussinessid") Long bussinessid);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.ruoyi.cms.domain.Job;
|
||||
import com.ruoyi.cms.domain.JobApply;
|
||||
import com.ruoyi.cms.domain.vo.CandidateVO;
|
||||
import com.ruoyi.common.core.domain.entity.AppUser;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 岗位申请Mapper接口
|
||||
@@ -39,4 +40,10 @@ public interface JobApplyMapper extends BaseMapper<JobApply>
|
||||
List<Job> selectJobApplyListJob(JobApply jobApply);
|
||||
|
||||
public int updateJobZphApply(JobApply jobApply);
|
||||
|
||||
public List<Job> selectCencalList(JobApply jobApply);
|
||||
|
||||
public int applyJobCencal(JobApply jobApply);
|
||||
|
||||
int applyCencalCount(@Param("userId") Long userId);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.ruoyi.cms.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.cms.domain.JobContact;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -17,4 +18,6 @@ public interface JobContactMapper extends BaseMapper<JobContact> {
|
||||
List<JobContact> getSelectList(JobContact jobContact);
|
||||
|
||||
int batchInsert(List<JobContact> list);
|
||||
|
||||
List<JobContact> selectByJobIds(@Param("jobIds") List<Long> longs);
|
||||
}
|
||||
|
||||
@@ -58,4 +58,8 @@ public interface JobMapper extends BaseMapper<Job>
|
||||
* @return
|
||||
*/
|
||||
Job getJobInfo(Long jobId);
|
||||
|
||||
Integer getTotals(Job job);
|
||||
|
||||
void updateFileBatchInsert(List<Job> list);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,15 @@ package com.ruoyi.cms.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.cms.domain.Statics;
|
||||
import com.ruoyi.cms.domain.StaticsJob;
|
||||
import com.ruoyi.cms.domain.query.Staticsquery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface StaticsMapper extends BaseMapper<Statics>
|
||||
{
|
||||
|
||||
public StaticsJob qygwtjCount(Staticsquery staticsquery);
|
||||
|
||||
public List<StaticsJob> getGroutCityJobs(Staticsquery staticsquery);
|
||||
}
|
||||
|
||||
@@ -14,4 +14,6 @@ import java.util.List;
|
||||
public interface SysAreaMapper{
|
||||
|
||||
List<SysArea> getList(SysArea sysArea);
|
||||
|
||||
List<SysArea> getCityList(SysArea sysArea);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ruoyi.cms.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.cms.domain.WechatGroup;
|
||||
import com.ruoyi.cms.domain.vo.WechatGroupVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface WechatGroupMapper extends BaseMapper<WechatGroup> {
|
||||
List<WechatGroupVo> selectWechatGroupList(@Param("p") WechatGroup wechatGroup);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ruoyi.cms.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.ruoyi.cms.domain.ai.AiChatHistory;
|
||||
|
||||
public interface AiChatHistoryService extends IService<AiChatHistory> {
|
||||
|
||||
JSONObject getList(AiChatHistory aiChatHistory);
|
||||
|
||||
void saveChatHistory(AiChatHistory aiChatHistory);
|
||||
|
||||
JSONObject getDetailList(String chatId);
|
||||
|
||||
JSONArray getChatHistoryData(String chatId);
|
||||
|
||||
}
|
||||
@@ -58,10 +58,16 @@ public interface IAppUserService
|
||||
|
||||
public AppUser getPhone(String phone);
|
||||
|
||||
AppUser selectByOpenid(String openid);
|
||||
public AppUser getPhoneAndNoRole(String phone);
|
||||
|
||||
public AppUser getPhoneAndUserType(String phone,String userType);
|
||||
|
||||
AppUser selectByOpenid(String openid,String userType);
|
||||
|
||||
public AppUser registerAppUser(RegisterBody registerBody);
|
||||
|
||||
public AppUser registerAppUserNew(RegisterBody registerBody);
|
||||
|
||||
public AppUser selectAppuserByIdcard(String idCard);
|
||||
|
||||
public AppUserLky selectAppUserInfo(AppUser appUser);
|
||||
@@ -75,4 +81,8 @@ public interface IAppUserService
|
||||
public MyChart getMyTj(Long userId);
|
||||
|
||||
public List<AppUserShow> selectUserApplyList(AppUser appUser);
|
||||
|
||||
public AppUser getYtjValidPhone(String phone);
|
||||
|
||||
public AppUser getYtjValidIdcard(String phone);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ruoyi.cms.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.ruoyi.cms.domain.CommunityUser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ICommunityUserService extends IService<CommunityUser> {
|
||||
List<CommunityUser> selectCommunityUserList(CommunityUser communityUser);
|
||||
|
||||
int delCommunityUser(Long[] ids);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.ruoyi.common.core.domain.entity.File;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -56,5 +57,5 @@ public interface IFileService
|
||||
|
||||
AjaxResult upload(MultipartFile file, Long bussinessid);
|
||||
|
||||
AjaxResult uploadFile(MultipartFile file, Long bussinessid);
|
||||
AjaxResult uploadFile(MultipartFile file, Long bussinessid, HttpServletRequest request);
|
||||
}
|
||||
|
||||
@@ -72,4 +72,8 @@ public interface IJobApplyService
|
||||
public List<Job> selectJobApplyListJob(JobApply jobApply);
|
||||
|
||||
public int updateJobZphApply(JobApply jobApply);
|
||||
|
||||
public int applyJobCencal(JobApply jobApply);
|
||||
|
||||
public List<Job> selectCencalList(JobApply jobApply);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ import com.ruoyi.cms.domain.ESJobDocument;
|
||||
import com.ruoyi.cms.domain.Job;
|
||||
import com.ruoyi.cms.domain.query.ESJobSearch;
|
||||
import com.ruoyi.cms.domain.vo.CandidateVO;
|
||||
import com.ruoyi.cms.util.AppWechatEntity;
|
||||
import com.ruoyi.cms.domain.vo.JobExcelVo;
|
||||
import com.ruoyi.common.core.domain.entity.AppUser;
|
||||
import org.dromara.easyes.core.biz.EsPageInfo;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 岗位Service接口
|
||||
*
|
||||
@@ -79,6 +81,8 @@ public interface IJobService
|
||||
|
||||
Job selectJobByJobIdApp(Long jobId);
|
||||
|
||||
Job selectHttpJobByJobIdApp(Long jobId,HttpServletRequest request);
|
||||
|
||||
void importRow(String path);
|
||||
|
||||
List<CandidateVO> candidates(Long jobId);
|
||||
@@ -96,4 +100,17 @@ public interface IJobService
|
||||
List<ESJobDocument> sysRecommend(ESJobSearch esJobSearch);
|
||||
|
||||
List<Job> selectAllJob();
|
||||
|
||||
public Job selectHttpJobByJobId(Long jobId, HttpServletRequest request);
|
||||
|
||||
public List<Job> selectHttpJobList(Job job,HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 获取微信抓取的重复数据条数
|
||||
* @param job
|
||||
* @return
|
||||
*/
|
||||
public Integer getTotals(Job job);
|
||||
|
||||
void uploadFileJob(List<JobExcelVo> list);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.ruoyi.cms.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.ruoyi.cms.domain.WechatGroup;
|
||||
import com.ruoyi.cms.domain.vo.WechatGroupVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IWechatGroupService extends IService<WechatGroup> {
|
||||
List<WechatGroupVo> selectWechatGroupList(WechatGroup wechatGroup);
|
||||
}
|
||||
@@ -14,5 +14,7 @@ import java.util.List;
|
||||
public interface JobContactService{
|
||||
|
||||
List<JobContact> getSelectList(JobContact jobContact);
|
||||
|
||||
int batchInsert(List<JobContact> list);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,6 @@ public interface StaticsqueryService {
|
||||
void educationSalaryGen();
|
||||
|
||||
Map<String, Object> educationSalary(Staticsquery staticsquery);
|
||||
|
||||
Map<String,Object> qygwtjCount(Staticsquery staticsquery);
|
||||
}
|
||||
|
||||
@@ -14,5 +14,7 @@ import java.util.List;
|
||||
public interface SysAreaService{
|
||||
|
||||
List<SysArea> getList(SysArea sysArea);
|
||||
|
||||
List<SysArea> getCityList(SysArea sysArea);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.ruoyi.cms.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.ruoyi.cms.domain.ai.AiChatDetail;
|
||||
import com.ruoyi.cms.domain.ai.AiChatHistory;
|
||||
import com.ruoyi.cms.mapper.AiChatDetailMapper;
|
||||
import com.ruoyi.cms.mapper.AiChatHistoryMapper;
|
||||
import com.ruoyi.cms.service.AiChatHistoryService;
|
||||
import com.ruoyi.common.utils.SiteSecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class AiChatHistoryServiceImpl extends ServiceImpl<AiChatHistoryMapper, AiChatHistory> implements AiChatHistoryService {
|
||||
|
||||
@Autowired
|
||||
private AiChatHistoryMapper aiChatHistoryMapper;
|
||||
@Autowired
|
||||
private AiChatDetailMapper aiChatDetailMapper;
|
||||
|
||||
@Override
|
||||
public JSONObject getList(AiChatHistory aiChatHistory) {
|
||||
JSONObject object = new JSONObject();
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
if(Objects.isNull(aiChatHistory.getUserId())){
|
||||
try {
|
||||
aiChatHistory.setUserId(SiteSecurityUtils.getUserId());
|
||||
}catch (Exception e){
|
||||
object.put("list", jsonArray);
|
||||
return object;
|
||||
}
|
||||
}
|
||||
List<AiChatHistory> list = aiChatHistoryMapper.getList(aiChatHistory);
|
||||
if(list!=null&&!list.isEmpty()){
|
||||
JSONObject jsonObject1 = new JSONObject();
|
||||
for(AiChatHistory history:list){
|
||||
jsonObject1 = JSONObject.parseObject(JSONObject.toJSONString(history));
|
||||
jsonArray.add(jsonObject1);
|
||||
}
|
||||
}
|
||||
object.put("list", jsonArray);
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveChatHistory(AiChatHistory aiChatHistory) {
|
||||
try {
|
||||
AiChatHistory history = new AiChatHistory();
|
||||
history.setChatId(aiChatHistory.getChatId());
|
||||
List<AiChatHistory> list = aiChatHistoryMapper.getList(history);
|
||||
if(list!=null&&!list.isEmpty()){
|
||||
history = list.get(0);
|
||||
history.setUpdateTime(new Date());
|
||||
aiChatHistoryMapper.updateById(history);
|
||||
}else{
|
||||
history.setTitle(aiChatHistory.getTitle());
|
||||
history.setUserId(aiChatHistory.getUserId());
|
||||
history.setAppId(aiChatHistory.getAppId());
|
||||
history.setDelFlag("0");
|
||||
history.setUpdateTime(new Date());
|
||||
aiChatHistoryMapper.insert(history);
|
||||
}
|
||||
AiChatDetail chatDetail = new AiChatDetail();
|
||||
chatDetail.setChatId(aiChatHistory.getChatId());
|
||||
chatDetail.setObj("Human");
|
||||
chatDetail.setTime(new Date());
|
||||
chatDetail.setDataId(IdUtils.fastSimpleUUID());
|
||||
chatDetail.setContent(aiChatHistory.getTitle());
|
||||
aiChatDetailMapper.insert(chatDetail);
|
||||
List<String> answerList = aiChatHistory.getAnswerStringList();
|
||||
if(answerList!=null&&!answerList.isEmpty()){
|
||||
chatDetail = new AiChatDetail();
|
||||
chatDetail.setChatId(aiChatHistory.getChatId());
|
||||
chatDetail.setObj("AI");
|
||||
chatDetail.setTime(new Date());
|
||||
chatDetail.setDataId(aiChatHistory.getDataId());
|
||||
chatDetail.setContent(String.join("",answerList));
|
||||
chatDetail.setDurationSeconds(aiChatHistory.getDurationSeconds());
|
||||
aiChatDetailMapper.insert(chatDetail);
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getDetailList(String chatId) {
|
||||
JSONObject dataList = new JSONObject();
|
||||
JSONArray array = new JSONArray();
|
||||
AiChatDetail detail = new AiChatDetail();
|
||||
detail.setChatId(chatId);
|
||||
List<AiChatDetail> list = aiChatDetailMapper.getList(detail);
|
||||
if(list!=null&&!list.isEmpty()){
|
||||
JSONObject data = new JSONObject();
|
||||
JSONArray value = new JSONArray();
|
||||
JSONObject valueObject = new JSONObject();
|
||||
String dataId = "";
|
||||
List<String> contentList = new ArrayList<>();
|
||||
List<AiChatDetail> details = new ArrayList<>();
|
||||
for(AiChatDetail detail1:list){
|
||||
details = list.stream().filter(fi->fi.getDataId().equals(detail1.getDataId())).collect(Collectors.toList());
|
||||
if(details.size() > 1){
|
||||
contentList = new ArrayList<>();
|
||||
if(StringUtils.isNotEmpty(dataId)){
|
||||
continue;
|
||||
}
|
||||
data = new JSONObject();
|
||||
data.put("dataId", detail1.getDataId());
|
||||
data.put("obj", detail1.getObj());
|
||||
data.put("hideInUI",false);
|
||||
data.put("customFeedbacks",new JSONArray());
|
||||
data.put("time",detail1.getTime());
|
||||
data.put("durationSeconds",detail1.getDurationSeconds());
|
||||
value = new JSONArray();
|
||||
dataId = detail1.getDataId();
|
||||
for(AiChatDetail detail2:details){
|
||||
if(detail2.getContent().contains("```")){
|
||||
contentList.add(detail2.getContent());
|
||||
}else{
|
||||
if(contentList!=null&&!contentList.isEmpty()){
|
||||
valueObject = new JSONObject();
|
||||
valueObject.put("type","text");
|
||||
valueObject.put("text",new JSONObject().fluentPut("content", String.join("",contentList)));
|
||||
value.add(valueObject);
|
||||
}
|
||||
|
||||
valueObject = new JSONObject();
|
||||
valueObject.put("type","text");
|
||||
valueObject.put("text",new JSONObject().fluentPut("content", detail2.getContent()));
|
||||
value.add(valueObject);
|
||||
}
|
||||
}
|
||||
data.put("value",value);
|
||||
array.add(data);
|
||||
}else{
|
||||
dataId = "";
|
||||
data = new JSONObject();
|
||||
data.put("dataId", detail1.getDataId());
|
||||
data.put("obj", detail1.getObj());
|
||||
data.put("hideInUI",false);
|
||||
data.put("customFeedbacks",new JSONArray());
|
||||
data.put("time",detail1.getTime());
|
||||
if("AI".equals(detail1.getObj())){
|
||||
data.put("durationSeconds",detail1.getDurationSeconds());
|
||||
}
|
||||
valueObject = new JSONObject();
|
||||
valueObject.put("type","text");
|
||||
valueObject.put("text",new JSONObject().fluentPut("content", detail1.getContent()));
|
||||
value = new JSONArray();
|
||||
value.add(valueObject);
|
||||
data.put("value",value);
|
||||
array.add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
dataList.put("list",array);
|
||||
return dataList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONArray getChatHistoryData(String chatId) {
|
||||
JSONObject content = null;
|
||||
JSONArray data = new JSONArray();
|
||||
AiChatDetail detail = new AiChatDetail();
|
||||
detail.setChatId(chatId);
|
||||
List<AiChatDetail> list = aiChatDetailMapper.getList(detail);
|
||||
if(list!=null&&!list.isEmpty()){
|
||||
for(AiChatDetail d:list){
|
||||
content = new JSONObject();
|
||||
if("AI".equals(d.getObj())){
|
||||
content.put("role","assistant");
|
||||
}else{
|
||||
content.put("role","user");
|
||||
}
|
||||
content.put("content",d.getContent());
|
||||
data.add(content);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,10 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper,AppUser> imple
|
||||
}
|
||||
//查询企业信息
|
||||
if("0".equals(appUser.getIsCompanyUser())){
|
||||
Company company=companyMapper.selectOne(Wrappers.<Company>lambdaQuery().eq(Company::getCode, appUser.getIdCard()).orderByDesc(Company::getUpdateTime).last("LIMIT 1"));
|
||||
Company company=companyMapper.selectOne(Wrappers.<Company>lambdaQuery()
|
||||
//.eq(Company::getCode, appUser.getIdCard())
|
||||
.apply("UPPER(code) = {0}", StringUtil.toUpperCaseIgnoreBlank(appUser.getIdCard()))
|
||||
.orderByDesc(Company::getUpdateTime).last("LIMIT 1"));
|
||||
appUser.setCompany(company);
|
||||
if(company!=null){
|
||||
CompanyContact contact=new CompanyContact();
|
||||
@@ -75,15 +78,21 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper,AppUser> imple
|
||||
company.setCompanyContactList(companyContactList);
|
||||
}
|
||||
}else if("1".equals(appUser.getIsCompanyUser())){
|
||||
//工作经历
|
||||
UserWorkExperiences workExperiences=new UserWorkExperiences();
|
||||
workExperiences.setUserId(appUser.getUserId());
|
||||
List<UserWorkExperiences> experiences =userWorkExperiencesMapper.getWorkExperiencesList(workExperiences);
|
||||
appUser.setExperiencesList(experiences);
|
||||
|
||||
//技能
|
||||
AppSkill skill=new AppSkill();
|
||||
skill.setUserId(appUser.getUserId());
|
||||
List<AppSkill> skillList=appSkillMapper.getList(skill);
|
||||
appUser.setAppSkillsList(skillList);
|
||||
//附件信息
|
||||
File file=new File();
|
||||
file.setBussinessid(userId);
|
||||
List<File> files=fileMapper.selectFileList(file);
|
||||
appUser.setFileList(files);
|
||||
}
|
||||
return appUser;
|
||||
}
|
||||
@@ -107,8 +116,18 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper,AppUser> imple
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int insertAppUser(AppUser appUser)
|
||||
{
|
||||
if(StringUtils.isNotEmpty(appUser.getYtjPassword())){
|
||||
try {
|
||||
appUser.setYtjPassword(SiteSecurityUtils.encryptPassword(appUser.getYtjPassword()));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("密码加密出错", e);
|
||||
}
|
||||
}else{
|
||||
appUser.setYtjPassword(null);
|
||||
}
|
||||
return appUserMapper.insert(appUser);
|
||||
}
|
||||
|
||||
@@ -119,6 +138,7 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper,AppUser> imple
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int updateAppUser(AppUser appUser)
|
||||
{
|
||||
//工作经历
|
||||
@@ -157,6 +177,10 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper,AppUser> imple
|
||||
});
|
||||
}
|
||||
}
|
||||
//添加一体机密码
|
||||
if(StringUtils.isNotEmpty(appUser.getYtjPassword())){
|
||||
appUser.setYtjPassword(SiteSecurityUtils.encryptPassword(appUser.getYtjPassword()));
|
||||
}
|
||||
return appUserMapper.updateById(appUser);
|
||||
}
|
||||
|
||||
@@ -179,8 +203,20 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper,AppUser> imple
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppUser selectByOpenid(String openid) {
|
||||
return appUserMapper.selectByOpenid(openid);
|
||||
public AppUser getPhoneAndNoRole(String phone) {
|
||||
return appUserMapper.selectOne(new LambdaQueryWrapper<AppUser>()
|
||||
.eq(AppUser::getPhone, phone).eq(AppUser::getDelFlag,"0").isNull(AppUser::getIsCompanyUser).orderByDesc(AppUser::getUpdateTime).last("LIMIT 1"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppUser getPhoneAndUserType(String phone,String userType) {
|
||||
return appUserMapper.selectOne(new LambdaQueryWrapper<AppUser>()
|
||||
.eq(AppUser::getPhone, phone).eq(AppUser::getIsCompanyUser,userType).eq(AppUser::getDelFlag,"0").orderByDesc(AppUser::getUpdateTime).last("LIMIT 1"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppUser selectByOpenid(String openid,String userType) {
|
||||
return appUserMapper.selectByOpenid(openid,userType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,7 +233,7 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper,AppUser> imple
|
||||
}
|
||||
}
|
||||
//角色集合
|
||||
Map mapUserRole=new HashMap<>();
|
||||
Map<String,Object> mapUserRole=new HashMap<>();
|
||||
switch (appUser.getIsCompanyUser()){
|
||||
case StringUtil.IS_COMPANY_USER://企业
|
||||
if(registerBody.getCompany()!=null){
|
||||
@@ -236,32 +272,248 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper,AppUser> imple
|
||||
//保存sys_user
|
||||
SysUser parmUser=appUserMapper.selectSysUserIdcard(appUser.getIdCard());
|
||||
if(parmUser==null){
|
||||
SysUser sysUser=new SysUser();
|
||||
sysUser.setUserName(StringUtil.USER_KEY+appUser.getIdCard());
|
||||
sysUser.setNickName(StringUtils.isEmpty(appUser.getName())?appUser.getPhone():appUser.getName());
|
||||
sysUser.setPassword(SiteSecurityUtils.encryptPassword("123456"));
|
||||
sysUser.setPhonenumber(appUser.getPhone());
|
||||
sysUser.setSex(appUser.getSex());
|
||||
sysUser.setStatus("0");
|
||||
sysUser.setLoginIp(appUser.getLoginIp());
|
||||
sysUser.setLoginDate(appUser.getLoginDate());
|
||||
sysUser.setIdCard(appUser.getIdCard());
|
||||
appUserMapper.insertSysUser(sysUser);
|
||||
//保存sys_user_role
|
||||
mapUserRole.put("userId",sysUser.getUserId());
|
||||
appUserMapper.insertSysUserRole(mapUserRole);
|
||||
registerInsertSysUser(appUser,mapUserRole);
|
||||
}
|
||||
//一体机密码
|
||||
if(StringUtils.isNotEmpty(appUser.getYtjPassword())){
|
||||
appUser.setYtjPassword(SiteSecurityUtils.encryptPassword(appUser.getYtjPassword()));
|
||||
try {
|
||||
appUser.setYtjPassword(SiteSecurityUtils.encryptPassword(appUser.getYtjPassword()));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("密码加密出错", e);
|
||||
}
|
||||
}else{
|
||||
appUser.setYtjPassword(null);
|
||||
}
|
||||
appUserMapper.updateById(appUser);
|
||||
return appUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AppUser registerAppUserNew(RegisterBody registerBody) {
|
||||
// 1. 参数校验
|
||||
validateRegisterParam(registerBody);
|
||||
|
||||
// 2. 获取基础数据
|
||||
AppUser appUser = registerBody.getAppUser();
|
||||
Long loginUserId = SiteSecurityUtils.getUserId();
|
||||
|
||||
// 3. 登录态处理:关联已有用户信息
|
||||
AppUser existingUser = handleLoginUserAssociation(appUser, loginUserId);
|
||||
|
||||
// 4. 差异化业务处理(企业用户/求职者)
|
||||
handleUserTypeSpecificLogic(existingUser, registerBody);
|
||||
|
||||
// 5. 系统用户同步(不存在则创建)
|
||||
syncSysUserIfNecessary(existingUser);
|
||||
|
||||
// 6. 一体机密码加密处理
|
||||
encryptYtjPassword(existingUser);
|
||||
|
||||
// 7. 更新App用户信息
|
||||
appUserMapper.updateById(existingUser);
|
||||
|
||||
return existingUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册参数校验
|
||||
* @param registerBody 注册请求体
|
||||
*/
|
||||
private void validateRegisterParam(RegisterBody registerBody) {
|
||||
if (registerBody == null) {
|
||||
throw new IllegalArgumentException("注册请求参数不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理登录用户关联逻辑
|
||||
* @param appUser 注册传入的用户信息
|
||||
* @param loginUserId 登录用户ID
|
||||
* @return 关联后的用户信息
|
||||
*/
|
||||
private AppUser handleLoginUserAssociation(AppUser appUser, Long loginUserId) {
|
||||
if (SiteSecurityUtils.isLogin() && loginUserId != null) {
|
||||
AppUser dbUser = appUserMapper.selectById(loginUserId);
|
||||
if (dbUser != null) {
|
||||
if (appUser == null) {
|
||||
return dbUser;
|
||||
} else {
|
||||
appUser.setPhone(dbUser.getPhone());
|
||||
appUser.setUserId(dbUser.getUserId());
|
||||
return appUser;
|
||||
}
|
||||
}
|
||||
}
|
||||
return appUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理不同用户类型的差异化业务逻辑
|
||||
* @param appUser 用户信息
|
||||
* @param registerBody 注册请求体
|
||||
*/
|
||||
private void handleUserTypeSpecificLogic(AppUser appUser, RegisterBody registerBody) {
|
||||
String userType = appUser.getIsCompanyUser();
|
||||
switch (userType) {
|
||||
case StringUtil.IS_COMPANY_USER:
|
||||
handleCompanyUserLogic(appUser, registerBody.getCompany());
|
||||
break;
|
||||
default:
|
||||
handleJobSeekerUserLogic(appUser, registerBody);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理企业用户注册逻辑
|
||||
* @param appUser 企业用户信息
|
||||
* @param company 企业信息
|
||||
*/
|
||||
private void handleCompanyUserLogic(AppUser appUser, Company company) {
|
||||
if (company != null) {
|
||||
// 保存企业信息(新增场景)
|
||||
if (company.getCompanyId() == null) {
|
||||
companyMapper.insert(company);
|
||||
// 批量保存企业联系人
|
||||
saveCompanyContacts(company.getCompanyId(), company.getCompanyContactList());
|
||||
}
|
||||
// 关联企业信息到用户
|
||||
appUser.setIdCard(company.getCode());
|
||||
appUser.setName(company.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存企业联系人
|
||||
* @param companyId 企业ID
|
||||
* @param contactList 联系人列表
|
||||
*/
|
||||
private void saveCompanyContacts(Long companyId, List<CompanyContact> contactList) {
|
||||
if (contactList != null && !contactList.isEmpty()) {
|
||||
contactList.forEach(contact -> contact.setCompanyId(companyId));
|
||||
companyContactMapper.batchInsert(contactList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理求职者用户注册逻辑
|
||||
* @param appUser 求职者用户信息
|
||||
* @param registerBody 注册请求体
|
||||
*/
|
||||
private void handleJobSeekerUserLogic(AppUser appUser, RegisterBody registerBody) {
|
||||
Long userId = appUser.getUserId();
|
||||
// 保存工作经历
|
||||
saveUserWorkExperiences(userId, registerBody.getExperiencesList());
|
||||
// 保存技能信息
|
||||
saveUserAppSkills(userId, registerBody.getAppSkillsList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存用户工作经历
|
||||
* @param userId 用户ID
|
||||
* @param experiencesList 工作经历列表
|
||||
*/
|
||||
private void saveUserWorkExperiences(Long userId, List<UserWorkExperiences> experiencesList) {
|
||||
if (experiencesList != null && !experiencesList.isEmpty()) {
|
||||
experiencesList.forEach(experience -> experience.setUserId(userId));
|
||||
userWorkExperiencesMapper.batchInsert(experiencesList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存用户技能信息
|
||||
* @param userId 用户ID
|
||||
* @param skillsList 技能列表
|
||||
*/
|
||||
private void saveUserAppSkills(Long userId, List<AppSkill> skillsList) {
|
||||
if (skillsList != null && !skillsList.isEmpty()) {
|
||||
skillsList.forEach(skill -> skill.setUserId(userId));
|
||||
appSkillMapper.batchInsert(skillsList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步系统用户(不存在则创建)
|
||||
* @param appUser App用户信息
|
||||
*/
|
||||
private void syncSysUserIfNecessary(AppUser appUser) {
|
||||
SysUser sysUser = appUserMapper.selectSysUserIdcard(appUser.getIdCard());
|
||||
if (sysUser == null) {
|
||||
// 构建角色映射
|
||||
Map<String, Object> roleMap = buildUserRoleMap(appUser.getIsCompanyUser());
|
||||
// 创建系统用户及角色关联
|
||||
registerInsertSysUser(appUser, roleMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建用户角色映射
|
||||
* @param userType 用户类型
|
||||
* @return 角色ID映射
|
||||
*/
|
||||
private Map<String, Object> buildUserRoleMap(String userType) {
|
||||
Map<String, Object> roleMap = new HashMap<>();
|
||||
if (StringUtil.IS_COMPANY_USER.equals(userType)) {
|
||||
roleMap.put("roleId", StringUtil.SYS_QY);
|
||||
} else {
|
||||
roleMap.put("roleId", StringUtil.SYS_QZZ);
|
||||
}
|
||||
return roleMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密一体机密码
|
||||
* @param appUser 用户信息
|
||||
*/
|
||||
private void encryptYtjPassword(AppUser appUser) {
|
||||
if (StringUtils.hasText(appUser.getYtjPassword())) {
|
||||
appUser.setYtjPassword(SiteSecurityUtils.encryptPassword(appUser.getYtjPassword()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存系统用户及角色关联
|
||||
* @param appUser App用户信息
|
||||
* @param roleMap 角色映射(包含roleId)
|
||||
*/
|
||||
private void registerInsertSysUser(AppUser appUser, Map<String, Object> roleMap) {
|
||||
// 构建系统用户
|
||||
SysUser sysUser = buildSysUser(appUser);
|
||||
// 插入系统用户
|
||||
appUserMapper.insertSysUser(sysUser);
|
||||
// 关联用户角色
|
||||
roleMap.put("userId", sysUser.getUserId());
|
||||
appUserMapper.insertSysUserRole(roleMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建系统用户对象
|
||||
* @param appUser App用户信息
|
||||
* @return 系统用户对象
|
||||
*/
|
||||
private SysUser buildSysUser(AppUser appUser) {
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUserName(StringUtil.USER_KEY + appUser.getIdCard());
|
||||
sysUser.setNickName(StringUtils.isEmpty(appUser.getName()) ? appUser.getPhone() : appUser.getName());
|
||||
sysUser.setPassword(SiteSecurityUtils.encryptPassword("123456"));
|
||||
sysUser.setPhonenumber(appUser.getPhone());
|
||||
sysUser.setSex(appUser.getSex());
|
||||
sysUser.setStatus("0");
|
||||
sysUser.setLoginIp(appUser.getLoginIp());
|
||||
sysUser.setLoginDate(appUser.getLoginDate());
|
||||
sysUser.setIdCard(appUser.getIdCard());
|
||||
return sysUser;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AppUser selectAppuserByIdcard(String idCard) {
|
||||
return appUserMapper.selectOne(Wrappers.<AppUser>lambdaQuery().eq(AppUser::getIdCard, idCard).eq(AppUser::getDelFlag,"0").orderByDesc(AppUser::getUpdateTime).last("LIMIT 1"));
|
||||
return appUserMapper.selectOne(Wrappers.<AppUser>lambdaQuery()
|
||||
//.eq(AppUser::getIdCard, idCard)
|
||||
.apply("UPPER(id_card) = {0}", StringUtil.toUpperCaseIgnoreBlank(idCard))
|
||||
.eq(AppUser::getDelFlag,"0")
|
||||
.orderByDesc(AppUser::getUpdateTime).last("LIMIT 1"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -393,4 +645,68 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper,AppUser> imple
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppUser getYtjValidPhone(String phone) {
|
||||
return queryPhoneUser(phone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppUser getYtjValidIdcard(String phone) {
|
||||
return queryIdcardUser(phone);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户
|
||||
* @param phone
|
||||
* @return
|
||||
*/
|
||||
private AppUser queryIdcardUser(String phone) {
|
||||
AppUser user = appUserMapper.selectOne(buildBaseIdcardQuery(phone)
|
||||
.isNotNull(AppUser::getIsCompanyUser)
|
||||
.eq(AppUser::getIsCompanyUser, "1"));
|
||||
|
||||
if (user == null) {
|
||||
user = appUserMapper.selectOne(buildBaseIdcardQuery(phone)
|
||||
.isNotNull(AppUser::getIsCompanyUser));
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户
|
||||
* @param phone
|
||||
* @return
|
||||
*/
|
||||
public AppUser queryPhoneUser(String phone) {
|
||||
AppUser user = appUserMapper.selectOne(buildBaseQuery(phone)
|
||||
.isNotNull(AppUser::getIsCompanyUser)
|
||||
.eq(AppUser::getIsCompanyUser, "1"));
|
||||
|
||||
if (user == null) {
|
||||
user = appUserMapper.selectOne(buildBaseQuery(phone)
|
||||
.isNotNull(AppUser::getIsCompanyUser));
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础查询条件
|
||||
* @param phone
|
||||
* @return
|
||||
*/
|
||||
private LambdaQueryWrapper<AppUser> buildBaseQuery(String phone) {
|
||||
return new LambdaQueryWrapper<AppUser>()
|
||||
.eq(AppUser::getPhone, phone)
|
||||
.eq(AppUser::getDelFlag, "0")
|
||||
.orderByDesc(AppUser::getUpdateTime)
|
||||
.last("LIMIT 1");
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<AppUser> buildBaseIdcardQuery(String idCard) {
|
||||
return new LambdaQueryWrapper<AppUser>()
|
||||
.apply("UPPER(id_card) = {0}", idCard)
|
||||
.eq(AppUser::getDelFlag,"0")
|
||||
.orderByDesc(AppUser::getUpdateTime).last("LIMIT 1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.ruoyi.cms.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.ruoyi.cms.domain.CommunityUser;
|
||||
import com.ruoyi.cms.domain.WechatGroup;
|
||||
import com.ruoyi.cms.mapper.CommunityUserMapper;
|
||||
import com.ruoyi.cms.mapper.WechatGroupMapper;
|
||||
import com.ruoyi.cms.service.ICommunityUserService;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class CommunityUserServiceImpl extends ServiceImpl<CommunityUserMapper, CommunityUser>
|
||||
implements ICommunityUserService {
|
||||
|
||||
@Autowired
|
||||
private WechatGroupMapper wechatGroupMapper;
|
||||
|
||||
@Override
|
||||
public List<CommunityUser> selectCommunityUserList(CommunityUser communityUser) {
|
||||
|
||||
return baseMapper.selectList(Wrappers.lambdaQuery(CommunityUser.class)
|
||||
.like(StrUtil.isNotBlank(communityUser.getWechatName()), CommunityUser::getWechatName, communityUser.getWechatName())
|
||||
.like(StrUtil.isNotBlank(communityUser.getPhoneNumber()), CommunityUser::getPhoneNumber, communityUser.getPhoneNumber())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delCommunityUser(Long[] ids) {
|
||||
if (ids == null || ids.length == 0) return 0;
|
||||
Collection<Long> userIds = CollUtil.newArrayList(ids);
|
||||
Long count = wechatGroupMapper.selectCount(Wrappers.lambdaQuery(WechatGroup.class)
|
||||
.in(WechatGroup::getCommunityId, userIds));
|
||||
if (count > 0) {
|
||||
throw new ServiceException("所选工作人员已配置转发对象!");
|
||||
}
|
||||
return baseMapper.deleteBatchIds(userIds);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import com.ruoyi.cms.service.IESJobSearchService;
|
||||
import com.ruoyi.cms.util.ListUtil;
|
||||
import com.ruoyi.cms.util.StringUtil;
|
||||
import com.ruoyi.common.core.domain.entity.Company;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.utils.SiteSecurityUtils;
|
||||
import com.ruoyi.common.utils.bean.BeanUtils;
|
||||
@@ -28,6 +29,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -48,6 +50,16 @@ public class ESJobSearchImpl implements IESJobSearchService
|
||||
private AppUserServiceImpl appUserService;
|
||||
@Autowired
|
||||
private ICompanyService iCompanyService;
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
// 锁的key(唯一标识ES索引初始化)
|
||||
private static final String ES_INIT_LOCK_KEY = "es:job_document:init:lock";
|
||||
// 锁过期时间(30分钟,确保初始化完成)
|
||||
private static final Integer LOCK_EXPIRE_SECONDS = 1800;
|
||||
// 等待锁时间(5分钟,避免无限等待)
|
||||
private static final Integer WAIT_LOCK_SECONDS = 300;
|
||||
|
||||
|
||||
@Autowired
|
||||
private BussinessDictDataServiceImpl bussinessDictDataServicel;
|
||||
@@ -58,7 +70,52 @@ public class ESJobSearchImpl implements IESJobSearchService
|
||||
@PostConstruct
|
||||
public void init()
|
||||
{
|
||||
resetTextCache();
|
||||
boolean isLockAcquired = false;
|
||||
try {
|
||||
isLockAcquired = acquireDistributedLock();
|
||||
if (isLockAcquired) {
|
||||
resetTextCache();
|
||||
} else {
|
||||
logger.info("其他节点正在初始化ES索引,直接复用,无需重复执行");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("ES索引初始化等待锁异常", e);
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
if (isLockAcquired) {
|
||||
releaseDistributedLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于自定义RedisCache实现分布式锁(无getRedisTemplate适配版)
|
||||
*/
|
||||
private boolean acquireDistributedLock() throws InterruptedException {
|
||||
long start = System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() - start < WAIT_LOCK_SECONDS * 1000) {
|
||||
boolean lockExists = redisCache.hasKey(ES_INIT_LOCK_KEY);
|
||||
if (!lockExists) {
|
||||
redisCache.setCacheObject(ES_INIT_LOCK_KEY, "es_init_locked", LOCK_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
||||
logger.info("成功获取ES初始化分布式锁,key:{}", ES_INIT_LOCK_KEY);
|
||||
return true;
|
||||
}
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
}
|
||||
logger.warn("等待{}秒未获取到ES初始化锁,放弃执行", WAIT_LOCK_SECONDS);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放分布式锁(用自定义RedisCache的deleteObject方法)
|
||||
*/
|
||||
private void releaseDistributedLock() {
|
||||
try {
|
||||
redisCache.deleteObject(ES_INIT_LOCK_KEY);
|
||||
logger.info("已释放ES初始化分布式锁,key:{}", ES_INIT_LOCK_KEY);
|
||||
} catch (Exception e) {
|
||||
logger.error("释放ES初始化锁异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,8 +136,24 @@ public class ESJobSearchImpl implements IESJobSearchService
|
||||
public void resetTextCache() {
|
||||
logger.info("正在重新刷新es");
|
||||
// 删除并重新创建索引
|
||||
esJobDocumentMapper.deleteIndex("job_document");
|
||||
esJobDocumentMapper.createIndex();
|
||||
/*esJobDocumentMapper.deleteIndex("job_document");
|
||||
esJobDocumentMapper.createIndex();*/
|
||||
if (esJobDocumentMapper.existsIndex("job_document")) {
|
||||
esJobDocumentMapper.deleteIndex("job_document");
|
||||
logger.info("已删除原有job_document索引");
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("删除索引后休眠异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!esJobDocumentMapper.existsIndex("job_document")) {
|
||||
esJobDocumentMapper.createIndex();
|
||||
logger.info("已创建job_document索引");
|
||||
} else {
|
||||
logger.info("索引已被其他节点创建,直接复用");
|
||||
}
|
||||
|
||||
// 分批次处理数据
|
||||
int batchSize = 1000; // 每批次处理的数据量
|
||||
@@ -104,7 +177,7 @@ public class ESJobSearchImpl implements IESJobSearchService
|
||||
BeanUtils.copyBeanProp(esJobDocument, job);
|
||||
CompanyVo vo=job.getCompanyVo();
|
||||
esJobDocument.setCompanyVoJson(JSON.toJSONString(vo));
|
||||
esJobDocument.setAppJobUrl("https://qd.zhaopinzao8dian.com/app#/packageA/pages/post/post?jobId="+ Base64.getEncoder().encodeToString(String.valueOf(job.getJobId()).getBytes()));
|
||||
esJobDocument.setAppJobUrl("https://www.xjksly.cn/app#/packageA/pages/post/post?jobId="+ Base64.getEncoder().encodeToString(String.valueOf(job.getJobId()).getBytes()));
|
||||
if(!StringUtil.isEmptyOrNull(job.getScale())){
|
||||
esJobDocument.setScale(Integer.valueOf(job.getScale()));
|
||||
}else {
|
||||
@@ -145,65 +218,36 @@ public class ESJobSearchImpl implements IESJobSearchService
|
||||
|
||||
ESJobSearch newSearch = new ESJobSearch();
|
||||
BeanUtils.copyProperties(esJobSearch,newSearch);
|
||||
boolean isCompanyUser = StringUtil.IS_COMPANY_USER.equals(esJobSearch.getUserType());
|
||||
//查询
|
||||
if(SiteSecurityUtils.isLogin()){
|
||||
AppUser appUser = appUserService.selectAppUserByUserId(SiteSecurityUtils.getUserId());
|
||||
if(!ListUtil.isEmptyOrNull(appUser.getJobTitle())){
|
||||
List<String> jobTitle = appUser.getJobTitle();
|
||||
newSearch.setJobTitle(String.join(",", jobTitle));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getJobTitle())){
|
||||
newSearch.setJobTitle(esJobSearch.getJobTitle());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(appUser.getEducation())){
|
||||
newSearch.setEducation(appUser.getEducation());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getEducation())){
|
||||
newSearch.setEducation(esJobSearch.getEducation());
|
||||
}
|
||||
|
||||
if(!StringUtil.isEmptyOrNull(appUser.getArea())){
|
||||
newSearch.setArea(appUser.getArea());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getArea())){
|
||||
newSearch.setArea(esJobSearch.getArea());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(appUser.getWorkExperience())){
|
||||
newSearch.setExperience(appUser.getWorkExperience());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getExperience())){
|
||||
newSearch.setExperience(esJobSearch.getExperience());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(appUser.getSalaryMax())){
|
||||
newSearch.setMaxSalary(Long.valueOf(appUser.getSalaryMax()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(appUser.getSalaryMin())){
|
||||
newSearch.setMinSalary(Long.valueOf(appUser.getSalaryMin()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getJobType())){
|
||||
newSearch.setJobType(esJobSearch.getJobType());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getType())){
|
||||
newSearch.setType(esJobSearch.getType());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getDescription())){
|
||||
newSearch.setDescription(esJobSearch.getDescription());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getCompanyNature())){
|
||||
newSearch.setCompanyNature(esJobSearch.getCompanyNature());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getCode())){
|
||||
if (!StringUtil.isEmptyOrNull(esJobSearch.getCode())) {
|
||||
newSearch.setCode(esJobSearch.getCode());
|
||||
}
|
||||
if (!isCompanyUser) {
|
||||
setJobSeekerParams(appUser, esJobSearch, newSearch);
|
||||
}
|
||||
}
|
||||
|
||||
LambdaEsQueryWrapper<ESJobDocument> wrapper = getWrapper(newSearch,jobIds);
|
||||
|
||||
//todo 暂时
|
||||
wrapper.limit(esJobSearch.getPageSize());
|
||||
if (isCompanyUser) {
|
||||
int current = esJobSearch.getCurrent() == null ? 0 : esJobSearch.getCurrent();
|
||||
current = Math.max(current, 0); // 非负校验
|
||||
int pageSize = esJobSearch.getPageSize() == null ? 10 : esJobSearch.getPageSize();
|
||||
pageSize = Math.min(pageSize, 50); // 限制最大条数
|
||||
int from = current * pageSize;
|
||||
wrapper.orderByAsc(ESJobDocument::getId);
|
||||
wrapper.limit(from, pageSize);
|
||||
}else{
|
||||
wrapper.limit(esJobSearch.getPageSize());
|
||||
}
|
||||
|
||||
List<ESJobDocument> esJobDocuments = esJobDocumentMapper.selectList(wrapper);
|
||||
|
||||
if (esJobDocuments.size() < esJobSearch.getPageSize()) {
|
||||
if (!isCompanyUser &&esJobDocuments.size() < esJobSearch.getPageSize()) {
|
||||
// 定义要逐步放宽的搜索条件字段
|
||||
List<Runnable> relaxConditions = new ArrayList<>();
|
||||
relaxConditions.add(() -> newSearch.setArea(null));
|
||||
@@ -251,6 +295,58 @@ public class ESJobSearchImpl implements IESJobSearchService
|
||||
return esJobDocuments;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼装参数
|
||||
* @param appUser
|
||||
* @param esJobSearch
|
||||
* @param newSearch
|
||||
*/
|
||||
private void setJobSeekerParams(AppUser appUser, ESJobSearch esJobSearch, ESJobSearch newSearch) {
|
||||
if(!ListUtil.isEmptyOrNull(appUser.getJobTitle())){
|
||||
List<String> jobTitle = appUser.getJobTitle();
|
||||
newSearch.setJobTitle(String.join(",", jobTitle));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getJobTitle())){
|
||||
newSearch.setJobTitle(esJobSearch.getJobTitle());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(appUser.getEducation())){
|
||||
newSearch.setEducation(appUser.getEducation());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getEducation())){
|
||||
newSearch.setEducation(esJobSearch.getEducation());
|
||||
}
|
||||
|
||||
if(!StringUtil.isEmptyOrNull(appUser.getArea())){
|
||||
newSearch.setArea(appUser.getArea());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getArea())){
|
||||
newSearch.setArea(esJobSearch.getArea());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(appUser.getWorkExperience())){
|
||||
newSearch.setExperience(appUser.getWorkExperience());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getExperience())){
|
||||
newSearch.setExperience(esJobSearch.getExperience());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(appUser.getSalaryMax())){
|
||||
newSearch.setMaxSalary(Long.valueOf(appUser.getSalaryMax()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(appUser.getSalaryMin())){
|
||||
newSearch.setMinSalary(Long.valueOf(appUser.getSalaryMin()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getJobType())){
|
||||
newSearch.setJobType(esJobSearch.getJobType());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getType())){
|
||||
newSearch.setType(esJobSearch.getType());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getDescription())){
|
||||
newSearch.setDescription(esJobSearch.getDescription());
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getCompanyNature())){
|
||||
newSearch.setCompanyNature(esJobSearch.getCompanyNature());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增全文检索数据
|
||||
@@ -435,12 +531,21 @@ public class ESJobSearchImpl implements IESJobSearchService
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getCode())){
|
||||
wrapper.and(x->x.eq(ESJobDocument::getCode,esJobSearch.getCode()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getType())){
|
||||
wrapper.and(x->x.eq(ESJobDocument::getType,esJobSearch.getType()));
|
||||
}
|
||||
if(esJobSearch.getJobId()!=null){
|
||||
wrapper.and(x->x.eq(ESJobDocument::getJobId,esJobSearch.getJobId()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getJobAddress())){
|
||||
wrapper.and(x->x.like(ESJobDocument::getJobAddress,esJobSearch.getJobAddress()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getJobLocation())){
|
||||
wrapper.and(x->x.like(ESJobDocument::getJobLocation,esJobSearch.getJobLocation()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getRegionCode())){
|
||||
wrapper.and(x->x.like(ESJobDocument::getRegionCode,esJobSearch.getRegionCode()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(esJobSearch.getStaffType())){
|
||||
wrapper.and(x->x.like(ESJobDocument::getStaffType,esJobSearch.getStaffType()));
|
||||
}
|
||||
if(Objects.nonNull(esJobSearch.getOrder())){
|
||||
if(esJobSearch.getOrder()==1){
|
||||
wrapper.orderByDesc(ESJobDocument::getIsHot);
|
||||
@@ -453,9 +558,8 @@ public class ESJobSearchImpl implements IESJobSearchService
|
||||
}
|
||||
}
|
||||
//企业用户排除es去除jobIds
|
||||
boolean needExclude = true;
|
||||
needExclude = !StringUtil.IS_COMPANY_USER.equals(esJobSearch.getUserType());
|
||||
if(needExclude && !ListUtil.isListEmptyOrNull(jobIds)){
|
||||
boolean isCompanyUser = StringUtil.IS_COMPANY_USER.equals(esJobSearch.getUserType());
|
||||
if (!isCompanyUser && !ListUtil.isListEmptyOrNull(jobIds)) {
|
||||
wrapper.not().in(ESJobDocument::getJobId, jobIds);
|
||||
}
|
||||
return wrapper;
|
||||
@@ -519,6 +623,30 @@ public class ESJobSearchImpl implements IESJobSearchService
|
||||
if(!StringUtil.isEmptyOrNull(jobQuery.getType())){
|
||||
wrapper.and(a->a.eq(ESJobDocument::getType,jobQuery.getType()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(jobQuery.getJobType())){
|
||||
wrapper.and(a->a.eq(ESJobDocument::getJobType,jobQuery.getJobType()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(jobQuery.getDescription())){
|
||||
wrapper.and(x->x.like(ESJobDocument::getDescription,jobQuery.getDescription()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(jobQuery.getCode())){
|
||||
wrapper.and(x->x.eq(ESJobDocument::getCode,jobQuery.getCode()));
|
||||
}
|
||||
if(jobQuery.getJobId()!=null){
|
||||
wrapper.and(x->x.eq(ESJobDocument::getJobId,jobQuery.getJobId()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(jobQuery.getJobAddress())){
|
||||
wrapper.and(x->x.like(ESJobDocument::getJobAddress,jobQuery.getJobAddress()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(jobQuery.getJobLocation())){
|
||||
wrapper.and(x->x.like(ESJobDocument::getJobLocation,jobQuery.getJobLocation()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(jobQuery.getRegionCode())){
|
||||
wrapper.and(x->x.like(ESJobDocument::getRegionCode,jobQuery.getRegionCode()));
|
||||
}
|
||||
if(!StringUtil.isEmptyOrNull(jobQuery.getStaffType())){
|
||||
wrapper.and(x->x.like(ESJobDocument::getStaffType,jobQuery.getStaffType()));
|
||||
}
|
||||
if(Objects.nonNull(jobQuery.getOrder())){
|
||||
if (jobQuery.getOrder()==2){
|
||||
wrapper.orderByDesc(ESJobDocument::getPostingDate);
|
||||
@@ -572,7 +700,7 @@ public class ESJobSearchImpl implements IESJobSearchService
|
||||
}
|
||||
|
||||
BeanUtils.copyBeanProp(esJobDocument, job);
|
||||
esJobDocument.setAppJobUrl("https://ks.zhaopinzao8dian.com/app#/packageA/pages/post/post?jobId="+ Base64.getEncoder().encodeToString(String.valueOf(job.getJobId()).getBytes()));
|
||||
esJobDocument.setAppJobUrl("https://www.xjksly.cn/app#/packageA/pages/post/post?jobId="+ Base64.getEncoder().encodeToString(String.valueOf(job.getJobId()).getBytes()));
|
||||
if(!StringUtil.isEmptyOrNull(job.getScale())){
|
||||
esJobDocument.setScale(Integer.valueOf(job.getScale()));
|
||||
}else {
|
||||
|
||||
@@ -19,6 +19,8 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 文件Service业务层处理
|
||||
*
|
||||
@@ -115,7 +117,7 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements IF
|
||||
// 保存文件信息到数据库
|
||||
saveFileInfo(fileName, bussinessid);
|
||||
|
||||
return AjaxResult.success("http://39.98.44.136/file/"+fileName);
|
||||
return AjaxResult.success(StringUtil.PATH_PROXY_50+fileName);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return AjaxResult.error("文件上传失败");
|
||||
@@ -129,7 +131,7 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements IF
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult uploadFile(MultipartFile file, Long bussinessid) {
|
||||
public AjaxResult uploadFile(MultipartFile file, Long bussinessid, HttpServletRequest request) {
|
||||
if (file.isEmpty()) {
|
||||
return AjaxResult.error("文件为空,请选择文件上传");
|
||||
}
|
||||
@@ -149,10 +151,11 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements IF
|
||||
Files.copy(file.getInputStream(), filePath);
|
||||
|
||||
// 保存文件信息到数据库
|
||||
saveFileInfo(fileName, bussinessid);
|
||||
File svFile=saveFileInfo(fileName, bussinessid);
|
||||
AjaxResult ajaxResult=AjaxResult.success();
|
||||
ajaxResult.put("filePath", StringUtil.PATH_DEV+fileName);
|
||||
ajaxResult.put("bussinessid",bussinessid);
|
||||
ajaxResult.put("filePath", StringUtil.getFilePath(request)+fileName);
|
||||
ajaxResult.put("bussinessid",String.valueOf(bussinessid));
|
||||
ajaxResult.put("id",String.valueOf(svFile.getId()));
|
||||
return ajaxResult;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
@@ -160,11 +163,12 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements IF
|
||||
}
|
||||
}
|
||||
|
||||
private void saveFileInfo(String fileName, Long bussinessid) {
|
||||
private File saveFileInfo(String fileName, Long bussinessid) {
|
||||
// 这里假设你已经有了一个FileService来处理数据库操作
|
||||
File file = new File();
|
||||
file.setBussinessid(bussinessid);
|
||||
file.setFileUrl(fileName);
|
||||
this.save(file);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -109,12 +108,14 @@ public class JobApplyServiceImpl extends ServiceImpl<JobApplyMapper,JobApply> im
|
||||
@Override
|
||||
public HashMap<String, Integer> statistics() {
|
||||
Integer applyCount = jobApplyMapper.applyJob(SiteSecurityUtils.getUserId()).size();
|
||||
Integer applyCencalCount=jobApplyMapper.applyCencalCount(SiteSecurityUtils.getUserId());
|
||||
Integer collectionJobCount = jobCollectionMapper.collectionJob(SiteSecurityUtils.getUserId()).size();
|
||||
Integer collectionCompanyCount = companyCollectionMapper.collectionJob(SiteSecurityUtils.getUserId()).size();
|
||||
Integer jobReviewCount = appReviewJobMapper.review(SiteSecurityUtils.getUserId(),new MineJobQuery()).size();
|
||||
Integer fairCollecitonCount = fairCollectionMapper.selectList(Wrappers.<FairCollection>lambdaQuery().eq(FairCollection::getUserId, SiteSecurityUtils.getUserId())).size();
|
||||
HashMap<String, Integer> map = new HashMap<>();
|
||||
map.put("applyCount", applyCount);
|
||||
map.put("applyCencalCount", applyCencalCount);
|
||||
map.put("collectionCount", collectionJobCount+collectionCompanyCount);
|
||||
map.put("jobReviewCount", jobReviewCount);
|
||||
map.put("fairCollecitonCount", fairCollecitonCount);
|
||||
@@ -160,4 +161,14 @@ public class JobApplyServiceImpl extends ServiceImpl<JobApplyMapper,JobApply> im
|
||||
public int updateJobZphApply(JobApply jobApply) {
|
||||
return jobApplyMapper.updateJobZphApply(jobApply);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Job> selectCencalList(JobApply jobApply) {
|
||||
return jobApplyMapper.selectCencalList(jobApply);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int applyJobCencal(JobApply jobApply) {
|
||||
return jobApplyMapper.applyJobCencal(jobApply);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,8 +262,10 @@ public class JobCollectionServiceImpl extends ServiceImpl<JobCollectionMapper,Jo
|
||||
}
|
||||
|
||||
// 4. 地点匹配
|
||||
if (user.getArea() != null && user.getArea().contains(jobLocation) || jobLocation.contains(user.getArea())) {
|
||||
matchScore += 1;
|
||||
if (user.getArea() != null) {
|
||||
if(user.getArea().contains(jobLocation) || jobLocation.contains(user.getArea())){
|
||||
matchScore += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 年龄估算(从生日计算)
|
||||
|
||||
@@ -18,4 +18,9 @@ public class JobContactServiceImpl extends ServiceImpl<JobContactMapper, JobCont
|
||||
public List<JobContact> getSelectList(JobContact jobContact){
|
||||
return jobContactMapper.getSelectList(jobContact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int batchInsert(List<JobContact> list) {
|
||||
return jobContactMapper.batchInsert(list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.ruoyi.cms.service.impl;
|
||||
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
@@ -10,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ruoyi.cms.domain.*;
|
||||
import com.ruoyi.cms.domain.vo.JobExcelVo;
|
||||
import com.ruoyi.cms.util.notice.NoticeUtils;
|
||||
import com.ruoyi.common.core.domain.entity.File;
|
||||
import com.ruoyi.cms.domain.query.ESJobSearch;
|
||||
@@ -25,11 +24,13 @@ import com.ruoyi.common.core.domain.entity.Company;
|
||||
import com.ruoyi.common.core.domain.entity.JobTitle;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.SiteSecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.bean.BeanUtils;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.dromara.easyes.core.biz.EsPageInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -37,6 +38,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.HttpURLConnection;
|
||||
@@ -46,6 +48,8 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 岗位Service业务层处理
|
||||
*
|
||||
@@ -222,13 +226,38 @@ public class JobServiceImpl extends ServiceImpl<JobMapper,Job> implements IJobSe
|
||||
if(contacts!=null){
|
||||
job.setJobContactList(contacts);
|
||||
}
|
||||
//查询附件
|
||||
File file=new File();
|
||||
file.setBussinessid(jobId);
|
||||
List<File> filesList=fileMapper.selectFileList(file);
|
||||
if(filesList!=null){
|
||||
job.setFilesList(filesList);
|
||||
return job;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Job selectHttpJobByJobId(Long jobId, HttpServletRequest request)
|
||||
{
|
||||
Job job = jobMapper.selectById(jobId);
|
||||
//查询公司信息
|
||||
if(Objects.nonNull(job.getCompanyId())){
|
||||
Company company = companyMapper.selectById(job.getCompanyId());
|
||||
job.setCompany(company);
|
||||
}
|
||||
//查询联系人
|
||||
JobContact contact = new JobContact();
|
||||
contact.setJobId(jobId);
|
||||
List<JobContact> contacts = jobContactMapper.getSelectList(contact);
|
||||
job.setJobContactList(contacts == null ? Collections.emptyList() : contacts);
|
||||
//查询附件
|
||||
String baseFilePath = StringUtil.getFilePath(request);
|
||||
//查询附件
|
||||
File queryFile = new File();
|
||||
queryFile.setBussinessid(jobId);
|
||||
List<File> filesList = Optional.ofNullable(fileMapper.selectFileList(queryFile))
|
||||
.orElseGet(Collections::emptyList);
|
||||
//添加路径
|
||||
List<File> processedFiles = filesList.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(file -> file.getFileUrl() != null && !file.getFileUrl().trim().isEmpty())
|
||||
.peek(file -> file.setFileUrl(String.join("", baseFilePath, file.getFileUrl())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
job.setFilesList(processedFiles);
|
||||
|
||||
return job;
|
||||
}
|
||||
@@ -246,6 +275,65 @@ public class JobServiceImpl extends ServiceImpl<JobMapper,Job> implements IJobSe
|
||||
return jobs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Job> selectHttpJobList(Job job,HttpServletRequest request)
|
||||
{
|
||||
List<Job> jobs = jobMapper.selectJobList(job);
|
||||
if (CollectionUtils.isNotEmpty(jobs)) {
|
||||
String baseFilePath = StringUtil.getFilePath(request);
|
||||
baseFilePath = baseFilePath == null ? "" : baseFilePath;
|
||||
|
||||
List<Long> jobIds = jobs.stream().filter(Objects::nonNull)
|
||||
.map(Job::getJobId).filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//查询所有附件
|
||||
List<File> allFiles = CollectionUtils.isNotEmpty(jobIds)
|
||||
? Optional.ofNullable(fileMapper.selectFileListByBussinessIds(jobIds))
|
||||
.orElseGet(Collections::emptyList)
|
||||
: Collections.emptyList();
|
||||
//查询所有联系人
|
||||
List<JobContact> allJobContacts = CollectionUtils.isNotEmpty(jobIds)
|
||||
? Optional.ofNullable(jobContactMapper.selectByJobIds(jobIds)).orElse(Collections.emptyList())
|
||||
: Collections.emptyList();
|
||||
//岗位联系人
|
||||
Map<Long, List<JobContact>> jobContactGroupMap = allJobContacts.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(contact -> Objects.nonNull(contact.getJobId()))
|
||||
.collect(Collectors.groupingBy(
|
||||
JobContact::getJobId,
|
||||
Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)
|
||||
));
|
||||
|
||||
//附件分组
|
||||
String finalBaseFilePath = baseFilePath;
|
||||
Map<Long, List<File>> fileGroupMap = allFiles.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(file -> Objects.nonNull(file.getBussinessid()))
|
||||
.filter(file -> StringUtils.isNotBlank(file.getFileUrl()))
|
||||
.peek(file -> {
|
||||
String fileUrl = file.getFileUrl().trim();
|
||||
String fullFileUrl = StringUtils.join(finalBaseFilePath, fileUrl);
|
||||
file.setFileUrl(fullFileUrl);
|
||||
})
|
||||
.collect(Collectors.groupingBy(
|
||||
File::getBussinessid,
|
||||
Collectors.collectingAndThen(Collectors.toList(),Collections::unmodifiableList)
|
||||
));
|
||||
|
||||
jobs.forEach(jobItem -> {
|
||||
if (Objects.nonNull(jobItem)) {
|
||||
Long jobItemId = jobItem.getJobId();
|
||||
List<File> jobFiles = fileGroupMap.getOrDefault(jobItemId, Collections.emptyList());
|
||||
List<JobContact> jobContent = jobContactGroupMap.getOrDefault(jobItemId, Collections.emptyList());
|
||||
jobItem.setFilesList(jobFiles);
|
||||
jobItem.setJobContactList(jobContent);
|
||||
}
|
||||
});
|
||||
}
|
||||
return jobs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int view(Long jobId) {
|
||||
Job job = jobMapper.selectById(jobId);
|
||||
@@ -263,18 +351,19 @@ public class JobServiceImpl extends ServiceImpl<JobMapper,Job> implements IJobSe
|
||||
* @param job 岗位
|
||||
* @return 结果
|
||||
*/
|
||||
@Transactional
|
||||
@Override
|
||||
public int insertJob(Job job)
|
||||
{
|
||||
int insert = jobMapper.insert(job);
|
||||
//todo 线程池管理
|
||||
Thread thread = new Thread(() -> {
|
||||
VectorJob jobVector = jobMapper.selectVectorJob(job.getJobId());
|
||||
jobVector.setJobUrl("http://39.98.44.136/app#/packageA/pages/post/post?jobId="+job.getJobId());
|
||||
String jsonBody = JSONUtil.toJsonStr(jobVector);
|
||||
HttpUtil.post("http://39.98.44.136:6004/insert_vector", jsonBody);
|
||||
});
|
||||
thread.start();
|
||||
// Thread thread = new Thread(() -> {
|
||||
// VectorJob jobVector = jobMapper.selectVectorJob(job.getJobId());
|
||||
// jobVector.setJobUrl("http://39.98.44.136/app#/packageA/pages/post/post?jobId="+job.getJobId());
|
||||
// String jsonBody = JSONUtil.toJsonStr(jobVector);
|
||||
// HttpUtil.post("http://39.98.44.136:6004/insert_vector", jsonBody);
|
||||
// });
|
||||
// thread.start();
|
||||
if(insert>0){
|
||||
//添加联系人
|
||||
List<JobContact> jobContactList = job.getJobContactList() != null ? job.getJobContactList() : Collections.emptyList();
|
||||
@@ -332,32 +421,75 @@ public class JobServiceImpl extends ServiceImpl<JobMapper,Job> implements IJobSe
|
||||
int i=jobMapper.updateById(job);
|
||||
//修改岗位联系人列表
|
||||
if(i>0){
|
||||
LambdaUpdateWrapper<JobContact> updateWrapper = Wrappers.<JobContact>lambdaUpdate()
|
||||
.eq(JobContact::getJobId, job.getJobId())
|
||||
.set(JobContact::getDelFlag, Constants.Del_FLAG_DELETE);
|
||||
|
||||
JobContact emptyEntity = new JobContact();
|
||||
jobContactMapper.update(emptyEntity, updateWrapper);
|
||||
List<JobContact> jobContactList = job.getJobContactList() != null ? job.getJobContactList() : Collections.emptyList();
|
||||
List<JobContact> insertList = jobContactList.stream()
|
||||
.filter(Objects::nonNull).map(x -> {
|
||||
JobContact jobContact = new JobContact();
|
||||
jobContact.setJobId(job.getJobId());
|
||||
return jobContact;
|
||||
}).collect(Collectors.toList());
|
||||
if (!insertList.isEmpty()) {
|
||||
jobContactMapper.batchInsert(insertList);
|
||||
}
|
||||
// 处理联系人
|
||||
handleJobContact(job);
|
||||
//添加附件
|
||||
List<File> filesList = job.getFilesList() != null ? job.getFilesList() : Collections.emptyList();
|
||||
List<Long> longs = filesList.stream().filter(Objects::nonNull).filter(file -> Objects.isNull(file.getId())).map(File::getBussinessid).collect(Collectors.toList());
|
||||
if(!longs.isEmpty()){
|
||||
fileMapper.updateBussinessids(longs,job.getJobId());
|
||||
}
|
||||
handleJobFile(job);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理联系人
|
||||
* @param job
|
||||
*/
|
||||
private void handleJobContact(Job job) {
|
||||
List<JobContact> jobContactList = Optional.ofNullable(job.getJobContactList()).orElse(Collections.emptyList());
|
||||
if (CollectionUtils.isEmpty(jobContactList)) {
|
||||
return;
|
||||
}
|
||||
List<JobContact> insertList = new ArrayList<>();
|
||||
List<JobContact> updateList = new ArrayList<>();
|
||||
|
||||
for (JobContact originContact : jobContactList) {
|
||||
if (originContact == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JobContact targetContact = new JobContact();
|
||||
targetContact.setJobId(job.getJobId());
|
||||
targetContact.setContactPerson(originContact.getContactPerson());
|
||||
targetContact.setContactPersonPhone(originContact.getContactPersonPhone());
|
||||
targetContact.setPosition(originContact.getPosition());
|
||||
|
||||
if (originContact.getId() == null) {
|
||||
insertList.add(targetContact);
|
||||
} else {
|
||||
targetContact.setId(originContact.getId());
|
||||
updateList.add(targetContact);
|
||||
}
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(insertList)) {
|
||||
jobContactMapper.batchInsert(insertList);
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(updateList)) {
|
||||
updateList.forEach(contact -> {
|
||||
LambdaUpdateWrapper<JobContact> updateWrapper = Wrappers.<JobContact>lambdaUpdate()
|
||||
.eq(JobContact::getId, contact.getId());
|
||||
jobContactMapper.update(contact, updateWrapper);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理附件
|
||||
* @param job
|
||||
*/
|
||||
private void handleJobFile(Job job) {
|
||||
List<File> filesList = Optional.ofNullable(job.getFilesList()).orElse(Collections.emptyList());
|
||||
List<Long> fileIds = filesList.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(File::getBussinessid)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!CollectionUtils.isEmpty(fileIds)) {
|
||||
fileMapper.updateBussinessids(fileIds, job.getJobId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除岗位
|
||||
*
|
||||
@@ -463,6 +595,63 @@ public class JobServiceImpl extends ServiceImpl<JobMapper,Job> implements IJobSe
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Job selectHttpJobByJobIdApp(Long jobId,HttpServletRequest request) {
|
||||
Job job = jobMapper.selectById(jobId);
|
||||
//查询公司信息
|
||||
if(Objects.nonNull(job.getCompanyId())){
|
||||
Company company = companyMapper.selectById(job.getCompanyId());
|
||||
job.setCompany(company);
|
||||
}
|
||||
if(SiteSecurityUtils.isLogin()){
|
||||
//查询申请信息
|
||||
Long applyCount = jobApplyMapper.selectCount(Wrappers.<JobApply>lambdaQuery().eq(JobApply::getJobId, jobId).eq(JobApply::getUserId, SiteSecurityUtils.getUserId()));
|
||||
job.setIsApply(applyCount>0?1:0);
|
||||
//查询收藏信息
|
||||
Long collectionCount = jobCollectionMapper.selectCount(Wrappers.<JobCollection>lambdaQuery().eq(JobCollection::getJobId, jobId).eq(JobCollection::getUserId, SiteSecurityUtils.getUserId()));
|
||||
job.setIsCollection(collectionCount>0?1:0);
|
||||
//todo asyn
|
||||
//保存浏览记录
|
||||
List<AppReviewJob> appReviewJobs = appReviewJobMapper.selectList(Wrappers.<AppReviewJob>lambdaQuery().eq(AppReviewJob::getUserId, SiteSecurityUtils.getUserId()).eq(AppReviewJob::getJobId, jobId));
|
||||
//之前相同岗位的记录删除 保存最新的浏览记录
|
||||
if(!appReviewJobs.isEmpty()){
|
||||
appReviewJobMapper.deleteBatchIds(appReviewJobs.stream().map(AppReviewJob::getId).collect(Collectors.toList()));
|
||||
}
|
||||
AppReviewJob appReviewJob = new AppReviewJob();
|
||||
appReviewJob.setUserId(SiteSecurityUtils.getUserId());
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String formattedDate = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
appReviewJob.setReviewDate(formattedDate);
|
||||
appReviewJob.setJobId(jobId);
|
||||
appReviewJobMapper.insert(appReviewJob);
|
||||
}
|
||||
this.view(jobId);
|
||||
//查询联系人
|
||||
JobContact contact = new JobContact();
|
||||
contact.setJobId(jobId);
|
||||
List<JobContact> contacts = jobContactMapper.getSelectList(contact);
|
||||
job.setJobContactList(contacts == null ? Collections.emptyList() : contacts);
|
||||
//查询附件
|
||||
String baseFilePath = StringUtil.getFilePath(request);
|
||||
File queryFile = new File();
|
||||
queryFile.setBussinessid(jobId);
|
||||
List<File> filesList = Optional.ofNullable(fileMapper.selectFileList(queryFile))
|
||||
.orElseGet(Collections::emptyList);
|
||||
//添加路径
|
||||
List<File> processedFiles = filesList.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(file -> file.getFileUrl() != null && !file.getFileUrl().trim().isEmpty())
|
||||
.peek(file -> file.setFileUrl(String.join("", baseFilePath, file.getFileUrl())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
job.setFilesList(processedFiles);
|
||||
//查询投递人
|
||||
List<AppUser> appUsers=jobMapper.selectApplyJobUserList(jobId);
|
||||
job.setApplyUsers(appUsers == null ? Collections.emptyList() : appUsers);
|
||||
return job;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CandidateVO> candidates(Long jobId) {
|
||||
List<CandidateVO> jobList = jobApplyMapper.candidates(jobId);
|
||||
@@ -882,30 +1071,55 @@ public class JobServiceImpl extends ServiceImpl<JobMapper,Job> implements IJobSe
|
||||
|
||||
@Override
|
||||
public List<ESJobDocument> sysRecommend(ESJobSearch esJobSearch) {
|
||||
Long userId=SecurityUtils.isLogin()?SecurityUtils.getUserId():null;
|
||||
List<Long> jobList=new ArrayList<>();
|
||||
String jobKey="";
|
||||
AppUser appUser=null;
|
||||
if(userId!=null){
|
||||
jobKey=CacheConstants.SYS_JOB_IDS+ userId;
|
||||
RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
|
||||
JSONArray cacheObject = redisCache.getCacheObject(jobKey);
|
||||
jobList = new ArrayList<>();
|
||||
if(Objects.isNull(cacheObject)){
|
||||
ArrayList<Long> longs = new ArrayList<>();
|
||||
jobList =longs;
|
||||
}else {
|
||||
jobList = cacheObject.toList(Long.class);
|
||||
Long userId = SecurityUtils.isLogin() ? SecurityUtils.getUserId() : null;
|
||||
List<Long> viewedJobIds = new ArrayList<>();
|
||||
String jobCacheKey = "";
|
||||
AppUser appUser = null;
|
||||
|
||||
if (userId != null) {
|
||||
jobCacheKey = CacheConstants.SYS_JOB_IDS + userId;
|
||||
try {
|
||||
JSONArray cacheObject = redisCache.getCacheObject(jobCacheKey);
|
||||
if (Objects.isNull(cacheObject)) {
|
||||
cacheObject = new JSONArray();
|
||||
}
|
||||
viewedJobIds = cacheObject.stream()
|
||||
.map(o -> Long.parseLong(o.toString()))
|
||||
.distinct().limit(100).collect(Collectors.toList());
|
||||
|
||||
String idCard = RoleUtils.getCurrentUseridCard();
|
||||
if (StringUtils.isNotEmpty(idCard)) {
|
||||
appUser = appUserService.selectAppuserByIdcard(idCard);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户已查看岗位缓存失败", e);
|
||||
viewedJobIds = new ArrayList<>();
|
||||
}
|
||||
appUser=appUserService.selectAppuserByIdcard(RoleUtils.getCurrentUseridCard());
|
||||
}
|
||||
//从es中查询
|
||||
List<ESJobDocument> jobListResult = iesJobSearchService.selectSysTextListExceptJobId(esJobSearch,jobList,appUser);
|
||||
//存入当前session中查看的岗位 避免重复 todo 定时删除 key上保存用户信息
|
||||
jobList.addAll(jobListResult.stream().map(ESJobDocument::getJobId).collect(Collectors.toList()));
|
||||
if(StringUtils.isNotEmpty(jobKey)){
|
||||
redisCache.setCacheObject(jobKey,jobList);
|
||||
|
||||
List<ESJobDocument> jobListResult = new ArrayList<>();
|
||||
try {
|
||||
jobListResult = iesJobSearchService.selectSysTextListExceptJobId(esJobSearch, viewedJobIds, appUser);
|
||||
//降级策略:如果过滤后无数据,忽略已查看记录重新查询
|
||||
if (CollectionUtils.isEmpty(jobListResult) && !viewedJobIds.isEmpty()) {
|
||||
log.warn("用户{}已查看岗位过多,忽略过滤条件重新查询");
|
||||
jobListResult = iesJobSearchService.selectSysTextListExceptJobId(esJobSearch, new ArrayList<>(), appUser);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("ES推荐岗位查询失败", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (userId != null && !CollectionUtils.isEmpty(jobListResult)) {
|
||||
try {
|
||||
List<Long> newJobIds = jobListResult.stream().map(ESJobDocument::getJobId).distinct().collect(Collectors.toList());
|
||||
List<Long> updatedJobIds = Stream.concat(viewedJobIds.stream(), newJobIds.stream()).distinct().limit(100).collect(Collectors.toList());
|
||||
redisCache.setCacheObject(jobCacheKey, updatedJobIds, 24, TimeUnit.HOURS);
|
||||
} catch (Exception e) {
|
||||
log.error("更新用户已查看岗位缓存失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
List<ESJobDocument> esJobDocuments = sysUserCollection(jobListResult);
|
||||
return esJobDocuments;
|
||||
}
|
||||
@@ -918,4 +1132,171 @@ public class JobServiceImpl extends ServiceImpl<JobMapper,Job> implements IJobSe
|
||||
params.put("offset", offset*batchSize);
|
||||
return jobMapper.selectAllJob(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTotals(Job job) {
|
||||
return jobMapper.getTotals(job);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void uploadFileJob(List<JobExcelVo> list) {
|
||||
//查询所有企业
|
||||
Map<String, Long> companyNameToIdMap = companyNameToIdMap(list);
|
||||
//岗位字典转换
|
||||
List<Job> jobList = buildJobList(list, companyNameToIdMap);
|
||||
//岗位去重
|
||||
List<Job> dedupedJobList = dedupJobList(jobList);
|
||||
//批量保存岗
|
||||
jobMapper.updateFileBatchInsert(dedupedJobList);
|
||||
//构建联系人列表(关联去重后的岗位)
|
||||
List<JobContact> contactList = buildJobContactList(dedupedJobList, list, companyNameToIdMap);
|
||||
//联系人去重(按:企业ID+联系人手机号 去重;企业ID为0/null时,仅按手机号去重)
|
||||
List<JobContact> dedupedContactList = dedupContactList(contactList, dedupedJobList);
|
||||
//批量保存联系人
|
||||
if (CollectionUtils.isNotEmpty(dedupedContactList)) {
|
||||
jobContactMapper.batchInsert(dedupedContactList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业名称查询关联(核心业务逻辑)
|
||||
*/
|
||||
private Map<String, Long> companyNameToIdMap(List<JobExcelVo> allExcelVoList) {
|
||||
Set<String> companyNameSet = allExcelVoList.stream()
|
||||
.map(JobExcelVo::getCompanyName)
|
||||
.filter(StringUtils::hasText)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (companyNameSet.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
List<Company> companies = companyMapper.selectByNames(new ArrayList<>(companyNameSet));
|
||||
if (CollectionUtils.isEmpty(companies)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return companies.stream()
|
||||
.collect(Collectors.toMap(
|
||||
Company::getName,
|
||||
Company::getCompanyId,
|
||||
(v1, v2) -> v1
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典转换 + Job对象构建(业务数据转换)
|
||||
*/
|
||||
private List<Job> buildJobList(List<JobExcelVo> allExcelVoList, Map<String, Long> companyNameToIdMap) {
|
||||
return allExcelVoList.stream().map(it -> {
|
||||
Job job = new Job();
|
||||
BeanUtils.copyProperties(it, job);
|
||||
//字典转换
|
||||
String education = DictUtils.getDictValue("education", it.getEducation());
|
||||
String experience = DictUtils.getDictValue("experience", it.getExperience());
|
||||
String jobType = DictUtils.getDictValue("job_type", it.getJobType());
|
||||
String type = DictUtils.getDictValue("position_type", it.getType());
|
||||
String areaCode = DictUtils.getDictValue("area", it.getJobLocation());
|
||||
// 字段赋值
|
||||
job.setEducation(education);
|
||||
job.setExperience(experience);
|
||||
job.setJobType(jobType);
|
||||
job.setType(type);
|
||||
job.setJobLocation(StringUtils.isBlank(it.getJobAddress()) ? it.getJobLocation() : it.getJobAddress());
|
||||
job.setJobLocationAreaCode(StringUtils.isBlank(areaCode) ? 0 : Integer.valueOf(areaCode));
|
||||
job.setIsPublish(1);
|
||||
job.setDataSource("1");
|
||||
job.setPostingDate(DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS));
|
||||
|
||||
String companyName = it.getCompanyName();
|
||||
job.setCompanyName(companyName);
|
||||
job.setCompanyId(companyNameToIdMap.getOrDefault(companyName, null));
|
||||
|
||||
return job;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 岗位去重(业务规则:企业ID+岗位名称+工作地点唯一)
|
||||
*/
|
||||
private List<Job> dedupJobList(List<Job> jobList) {
|
||||
Map<String, Job> dedupMap = new LinkedHashMap<>();
|
||||
for (Job job : jobList) {
|
||||
String dedupKey = String.format(
|
||||
"%s_%s_%s",
|
||||
Optional.ofNullable(job.getCompanyId()).orElse(null),
|
||||
job.getJobTitle().trim(),
|
||||
job.getJobLocation().trim()
|
||||
);
|
||||
dedupMap.putIfAbsent(dedupKey, job);
|
||||
}
|
||||
return new ArrayList<>(dedupMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建联系人列表(业务关联)
|
||||
*/
|
||||
private List<JobContact> buildJobContactList(List<Job> dedupedJobList, List<JobExcelVo> allExcelVoList, Map<String, Long> companyNameToIdMap) {
|
||||
List<JobContact> contactList = new ArrayList<>();
|
||||
|
||||
Map<String, Job> jobMatchMap = dedupedJobList.stream()
|
||||
.collect(Collectors.toMap(
|
||||
job -> String.format(
|
||||
"%s_%s",
|
||||
job.getJobTitle().trim(),
|
||||
job.getJobLocation().trim()
|
||||
),
|
||||
job -> job
|
||||
));
|
||||
|
||||
for (JobExcelVo vo : allExcelVoList) {
|
||||
String jobTitle = vo.getJobTitle().trim();
|
||||
String jobLocation = StringUtils.isBlank(vo.getJobAddress()) ? vo.getJobLocation().trim() : vo.getJobAddress().trim();
|
||||
String matchKey = String.format("%s_%s", jobTitle, jobLocation);
|
||||
|
||||
Job matchedJob = jobMatchMap.get(matchKey);
|
||||
if (matchedJob == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JobContact contact = new JobContact();
|
||||
contact.setJobId(matchedJob.getJobId());
|
||||
contact.setContactPerson(vo.getContactPerson());
|
||||
contact.setContactPersonPhone(vo.getContactPersonPhone());
|
||||
contactList.add(contact);
|
||||
}
|
||||
|
||||
return contactList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 联系人去重(兼容企业ID为0的情况)
|
||||
*/
|
||||
private List<JobContact> dedupContactList(List<JobContact> contactList, List<Job> dedupedJobList) {
|
||||
if (CollectionUtils.isEmpty(contactList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Set<Long> validJobIds = dedupedJobList.stream().map(Job::getJobId).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
|
||||
Map<String, JobContact> dedupMap = new LinkedHashMap<>();
|
||||
for (JobContact contact : contactList) {
|
||||
if (StringUtils.isBlank(contact.getContactPersonPhone())) {
|
||||
System.out.printf("跳过联系人:姓名=%s(手机号为空)%n", contact.getContactPerson());
|
||||
continue;
|
||||
}
|
||||
|
||||
Long jobId = contact.getJobId();
|
||||
if (jobId == null || !validJobIds.contains(jobId)) {
|
||||
System.out.printf("跳过联系人:姓名=%s(岗位ID无效或不存在)%n", contact.getContactPerson());
|
||||
continue;
|
||||
}
|
||||
|
||||
String dedupKey = String.format("%s_%s",jobId,contact.getContactPersonPhone().trim());
|
||||
|
||||
dedupMap.putIfAbsent(dedupKey, contact);
|
||||
}
|
||||
|
||||
return new ArrayList<>(dedupMap.values());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@ package com.ruoyi.cms.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.ruoyi.cms.domain.CommercialArea;
|
||||
import com.ruoyi.cms.domain.CompanyCardCollection;
|
||||
import com.ruoyi.cms.domain.Statics;
|
||||
import com.ruoyi.cms.domain.StaticsJob;
|
||||
import com.ruoyi.cms.domain.query.Staticsquery;
|
||||
import com.ruoyi.cms.mapper.CompanyCardCollectionMapper;
|
||||
import com.ruoyi.cms.mapper.StaticsMapper;
|
||||
import com.ruoyi.cms.service.ISubwayLineService;
|
||||
import com.ruoyi.cms.service.StaticsqueryService;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -551,4 +549,16 @@ public class StaticsqueryServiceImpl extends ServiceImpl<StaticsMapper, Statics>
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> qygwtjCount(Staticsquery staticsquery) {
|
||||
String eneTime= DateUtils.addOneDay(staticsquery.getEndTime(),DateUtils.YYYY_MM_DD,DateUtils.YYYY_MM_DD);
|
||||
staticsquery.setEndTime(eneTime);
|
||||
HashMap<String, Object> result = new HashMap<>();
|
||||
StaticsJob staticsJob=staticsMapper.qygwtjCount(staticsquery);
|
||||
List<StaticsJob> list=staticsMapper.getGroutCityJobs(staticsquery);
|
||||
result.put("hz",staticsJob);
|
||||
result.put("group",list);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.ruoyi.cms.service.SysAreaService;
|
||||
import com.ruoyi.common.core.domain.entity.SysArea;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@@ -16,4 +17,9 @@ public class SysAreaServiceImpl implements SysAreaService {
|
||||
public List<SysArea> getList(SysArea sysArea){
|
||||
return sysAreaMapper.getList(sysArea);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysArea> getCityList(SysArea sysArea) {
|
||||
return sysAreaMapper.getCityList(sysArea);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ruoyi.cms.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.ruoyi.cms.domain.WechatGroup;
|
||||
import com.ruoyi.cms.domain.vo.WechatGroupVo;
|
||||
import com.ruoyi.cms.mapper.WechatGroupMapper;
|
||||
import com.ruoyi.cms.service.IWechatGroupService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class WechatGroupServiceImpl extends ServiceImpl<WechatGroupMapper, WechatGroup>
|
||||
implements IWechatGroupService {
|
||||
|
||||
@Override
|
||||
public List<WechatGroupVo> selectWechatGroupList(WechatGroup wechatGroup) {
|
||||
return baseMapper.selectWechatGroupList(wechatGroup);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.ruoyi.cms.util;
|
||||
|
||||
/**
|
||||
* 阿里云配置
|
||||
*/
|
||||
public class AliyunNlsUtils {
|
||||
|
||||
/**
|
||||
* 标志
|
||||
*/
|
||||
public static final boolean USE_TEST_ENV=true;
|
||||
/**
|
||||
* 测试nls
|
||||
*/
|
||||
public static final String NLS_TEST_URL="wss://nls-gateway-cn-shanghai.aliyuncs.com";
|
||||
/**
|
||||
* 测试微信获取oppenid链接
|
||||
*/
|
||||
public static final String WX_TEST_URL="https://api.weixin.qq.com/sns/jscode2session";
|
||||
/**
|
||||
* 正式nls
|
||||
*/
|
||||
public static final String NLS_FORMAL_URL="http://192.168.2.102:10044";
|
||||
|
||||
/**
|
||||
* 根据环境判断正式还是测试
|
||||
* @return
|
||||
*/
|
||||
public static String getNlsUrl(){
|
||||
String url = USE_TEST_ENV ? NLS_TEST_URL : NLS_FORMAL_URL;
|
||||
System.out.println("nls当前环境:" + (USE_TEST_ENV ? "测试" : "正式") + ",WebSocket地址:" + url);
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信授权登录
|
||||
* @return
|
||||
*/
|
||||
public static String getWXoppenidUrl(){
|
||||
String url = USE_TEST_ENV ? WX_TEST_URL : NLS_FORMAL_URL+"/weixin";
|
||||
System.out.println("微信授权登录当前环境:" + (USE_TEST_ENV ? "测试" : "正式") + ",获取oppenid地址:" + url);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.ruoyi.cms.util;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 文本内容清理工具类:移除思维链、提取有效业务文本
|
||||
* 核心能力:
|
||||
* 1. 优先提取 <file_upload> 标签内的有效文本(自动剔除标签外的思维链)
|
||||
* 2. 无 file_upload 标签时,移除 标签及内部的思维链
|
||||
* 3. 统一清理文本格式(多余换行/空格、末尾问号)
|
||||
*/
|
||||
public class ChatTextCleanUtil {
|
||||
|
||||
// 正则常量:匹配 <file_upload> 标签及内部内容(提取标签内文本)
|
||||
private static final String PATTERN_FILE_UPLOAD = "\\<file_upload\\>(.*?)\\</file_upload\\>";
|
||||
// 正则常量:匹配 标签及内部的思维链内容
|
||||
private static final String PATTERN_THINK_CHAIN = "\\<think\\>.*?\\</think\\>";
|
||||
// 正则常量:匹配多个换行符
|
||||
private static final String PATTERN_MULTI_NEWLINE = "\\n+";
|
||||
// 正则常量:匹配多个空白字符(空格/制表符等)
|
||||
private static final String PATTERN_MULTI_SPACE = "\\s+";
|
||||
// 正则常量:匹配末尾的问号
|
||||
private static final String PATTERN_TRAILING_QUESTION = "\\?$";
|
||||
|
||||
/**
|
||||
* 清理文本中的思维链,提取有效业务内容
|
||||
*
|
||||
* @param content 原始文本内容(可能包含思维链、file_upload标签等)
|
||||
* @return 清理后的纯业务文本,空值/空文本返回原内容
|
||||
*/
|
||||
public static String removeThinkChain(String content) {
|
||||
// 空值/空文本直接返回,避免后续处理
|
||||
if (!StringUtils.hasText(content)) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// 提取有效文本(优先处理file_upload标签)
|
||||
String validContent = extractFileUploadContent(content);
|
||||
// 若未提取到file_upload内容,移除think标签的思维链
|
||||
if (!StringUtils.hasText(validContent)) {
|
||||
validContent = cleanThinkChainContent(content);
|
||||
}
|
||||
|
||||
// 统一格式化文本(清理多余换行/空格、末尾问号)
|
||||
return formatText(validContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取 <file_upload> 标签内的文本内容
|
||||
*
|
||||
* @param content 原始文本
|
||||
* @return 标签内的文本(无标签返回空字符串)
|
||||
*/
|
||||
private static String extractFileUploadContent(String content) {
|
||||
Pattern pattern = Pattern.compile(PATTERN_FILE_UPLOAD, Pattern.DOTALL);
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
if (matcher.find()) {
|
||||
// 提取分组1的内容(标签内文本)并去除首尾空格
|
||||
return matcher.group(1).trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除 标签及内部的思维链内容
|
||||
*
|
||||
* @param content 原始文本
|
||||
* @return 移除思维链后的文本,空值返回空字符串
|
||||
*/
|
||||
private static String cleanThinkChainContent(String content) {
|
||||
if (!StringUtils.hasText(content)) {
|
||||
return "";
|
||||
}
|
||||
Pattern pattern = Pattern.compile(PATTERN_THINK_CHAIN, Pattern.DOTALL);
|
||||
// 替换think标签及内容为空,再去除首尾空格
|
||||
return pattern.matcher(content).replaceAll("").trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文本:清理多余换行、空格,移除末尾问号
|
||||
*
|
||||
* @param content 需要格式化的文本
|
||||
* @return 格式化后的文本,空值返回空字符串
|
||||
*/
|
||||
private static String formatText(String content) {
|
||||
if (!StringUtils.hasText(content)) {
|
||||
return "";
|
||||
}
|
||||
// 多个换行符替换为单个空格
|
||||
String formatted = content.replaceAll(PATTERN_MULTI_NEWLINE, " ");
|
||||
//多个空白字符替换为单个空格
|
||||
formatted = formatted.replaceAll(PATTERN_MULTI_SPACE, " ");
|
||||
//移除末尾的问号
|
||||
formatted = formatted.replaceAll(PATTERN_TRAILING_QUESTION, "");
|
||||
//最终再trim一次,确保无首尾空格
|
||||
return formatted.trim();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ruoyi.cms.util;
|
||||
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
@@ -12,6 +14,8 @@ public class DateValidateUtil {
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
private static final String ERROR_MSG = "生日格式错误!不为空时必须填写 yyyy-MM-dd 完整格式(如 1991-09-01)";
|
||||
|
||||
private static final String DEFAULT_DATE_FORMAT_TIP = "请使用 yyyy-MM-dd 格式";
|
||||
|
||||
// 改为校验 String 类型
|
||||
public static String validateBirthDate(String birthDateStr) {
|
||||
if (birthDateStr == null || birthDateStr.trim().isEmpty()) {
|
||||
@@ -26,4 +30,73 @@ public class DateValidateUtil {
|
||||
return ERROR_MSG; // 格式错误返回提示
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验开始时间和结束时间的合法性
|
||||
* @param startTime 开始时间字符串
|
||||
* @param endTime 结束时间字符串
|
||||
* @return 校验不通过返回错误信息,校验通过返回null
|
||||
*/
|
||||
public static String validateStartAndEndTime(String startTime, String endTime) {
|
||||
StringBuilder errorMsg = new StringBuilder();
|
||||
|
||||
if (StringUtils.isBlank(startTime)) {
|
||||
errorMsg.append("开始时间不能为空!");
|
||||
} else {
|
||||
if (!isValidDate(startTime)) {
|
||||
errorMsg.append(StringUtils.isNotBlank(errorMsg.toString()) ? " " : "")
|
||||
.append("开始时间格式错误,").append(DEFAULT_DATE_FORMAT_TIP);
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(endTime)) {
|
||||
errorMsg.append(StringUtils.isNotBlank(errorMsg.toString()) ? " " : "")
|
||||
.append("结束时间不能为空!");
|
||||
} else {
|
||||
if (!isValidDate(endTime)) {
|
||||
errorMsg.append(StringUtils.isNotBlank(errorMsg.toString()) ? " " : "")
|
||||
.append("结束时间格式错误,").append(DEFAULT_DATE_FORMAT_TIP);
|
||||
} else if (StringUtils.isNotBlank(startTime) && isValidDate(startTime)) {
|
||||
if (!isEndTimeAfterStartTime(startTime, endTime)) {
|
||||
errorMsg.append(StringUtils.isNotBlank(errorMsg.toString()) ? " " : "")
|
||||
.append("结束时间不能早于开始时间!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errorMsg.length() > 0 ? errorMsg.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验单个日期字符串是否符合 yyyy-MM-dd 格式
|
||||
* @param dateStr 日期字符串
|
||||
* @return 合法返回true,否则false
|
||||
*/
|
||||
public static boolean isValidDate(String dateStr) {
|
||||
if (StringUtils.isBlank(dateStr)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
LocalDate.parse(dateStr); // 默认解析 yyyy-MM-dd 格式
|
||||
return true;
|
||||
} catch (DateTimeParseException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验结束时间是否晚于开始时间(前提:两个时间格式都合法)
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 结束时间晚于开始时间返回true,否则false
|
||||
*/
|
||||
public static boolean isEndTimeAfterStartTime(String startTime, String endTime) {
|
||||
try {
|
||||
LocalDate startDate = LocalDate.parse(startTime);
|
||||
LocalDate endDate = LocalDate.parse(endTime);
|
||||
return !endDate.isBefore(startDate); // 结束时间 >= 开始时间 返回true
|
||||
} catch (DateTimeParseException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.ruoyi.cms.util;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
public class ProxyServerUtil {
|
||||
private static final String PROXY_HEADER = "X-Proxy-Server";
|
||||
|
||||
/**
|
||||
* 从请求头中获取代理服务器标识(B或C)
|
||||
* @param request HttpServletRequest
|
||||
* @return 代理标识(如"proxy-b"、"proxy-c"),无则返回null
|
||||
*/
|
||||
public static String getProxyServer(HttpServletRequest request) {
|
||||
// 从请求头中获取自定义标识
|
||||
return request.getHeader(PROXY_HEADER);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.ruoyi.cms.util;
|
||||
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -11,11 +14,24 @@ public class StringUtil {
|
||||
/*1101(求职者)、1102(招聘者)、1103(网格员)、1104(内部工作者)*/
|
||||
public static final Long COMPANY_ADMIN_ROLE_KEY = 1102L;
|
||||
|
||||
/************************移动端角色开始***************************/
|
||||
/**
|
||||
* 企业用户
|
||||
* 移动端-企业用户
|
||||
*/
|
||||
public static final String IS_COMPANY_USER = "0";
|
||||
|
||||
/**
|
||||
* 移动端-求职者
|
||||
*/
|
||||
public static final String IS_JOB_REQUEST_USER = "1";
|
||||
/**
|
||||
* 移动端-网格员
|
||||
*/
|
||||
public static final String IS_GRID_USER = "2";
|
||||
/**
|
||||
* 移动端-内部工作者
|
||||
*/
|
||||
public static final String IS_INTERNAL_USER = "3";
|
||||
/************************移动端角色结束***************************/
|
||||
/**
|
||||
* pc端-求职者
|
||||
*/
|
||||
@@ -33,7 +49,7 @@ public class StringUtil {
|
||||
/**
|
||||
* 岗位互联网
|
||||
*/
|
||||
public static final String BASE_WW_GW="http://http://222.80.110.161:11111/kashi/job-portal/detail/:";
|
||||
public static final String BASE_WW_GW="https://www.xjksly.cn/kashi/job-portal/detail/";
|
||||
|
||||
/**
|
||||
*录用
|
||||
@@ -48,7 +64,10 @@ public class StringUtil {
|
||||
*录用-招聘会
|
||||
*/
|
||||
public static final String HIRE_SOURCE_ZPH="1";
|
||||
|
||||
/**
|
||||
* 标记
|
||||
*/
|
||||
public static final boolean PATH_TEST_ENV = true;
|
||||
/**
|
||||
* 测试环境附件地址
|
||||
*/
|
||||
@@ -56,7 +75,15 @@ public class StringUtil {
|
||||
/**
|
||||
* 正式环境环境地址
|
||||
*/
|
||||
public static final String PATH_PRO = "http://10.98.80.37/file/";
|
||||
public static final String PATH_PROXY_37 = "http://10.98.80.37/file/";
|
||||
/**
|
||||
* 互联网
|
||||
*/
|
||||
public static final String PATH_PROXY_50="https://www.xjksly.cn/file/";
|
||||
/**
|
||||
* 经办端
|
||||
*/
|
||||
public static final String PATH_PROXY_146="http://10.98.80.146/file/";
|
||||
|
||||
/**
|
||||
* 身份证规则
|
||||
@@ -113,15 +140,93 @@ public class StringUtil {
|
||||
.collect(Collectors.toList()); // 收集为List
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取附件地址
|
||||
* @return
|
||||
*/
|
||||
public static String getFilePath(HttpServletRequest request){
|
||||
String proxyServer = getProxyServer(request);
|
||||
if ("proxy-50".equals(proxyServer)) {
|
||||
return PATH_PROXY_50;
|
||||
} else if ("proxy-146".equals(proxyServer)) {
|
||||
return PATH_PROXY_146;
|
||||
}
|
||||
return !PATH_TEST_ENV ? PATH_PROXY_37 : PATH_DEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* 原方法-不传request
|
||||
* @return
|
||||
*/
|
||||
public static String getFilePath(){
|
||||
return !PATH_TEST_ENV ? PATH_PROXY_37 : PATH_DEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取nginx地址
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private static String getProxyServer(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
return request.getHeader("X-Proxy-Server");
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号脱敏
|
||||
* @param phone
|
||||
* @return
|
||||
*/
|
||||
public static String desensitizePhone(String phone) {
|
||||
if (StringUtils.isEmpty(phone) || phone.length() != 11) {
|
||||
return phone;
|
||||
}
|
||||
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
|
||||
}
|
||||
|
||||
/**
|
||||
* 脱敏逻辑:前4位 + ***+ 后4位
|
||||
* @param idCard
|
||||
* @return
|
||||
*/
|
||||
public static String desensitizeIdCard(String idCard) {
|
||||
if (idCard == null || idCard.length() != 18) {
|
||||
return idCard; // 非标准身份证号不脱敏(或按规则处理)
|
||||
if (StringUtils.isEmpty(idCard)) {
|
||||
return null;
|
||||
}
|
||||
return idCard.substring(0, 4) + "***" + idCard.substring(14);
|
||||
// 处理18位身份证(支持末尾X/x)
|
||||
if (idCard.matches("\\d{17}[\\dXx]")) {
|
||||
return idCard.replaceAll("(\\d{6})\\d{8}([\\dXx]{4})", "$1********$2");
|
||||
}
|
||||
// 处理15位身份证
|
||||
else if (idCard.matches("\\d{15}")) {
|
||||
return idCard.replaceAll("(\\d{6})\\d{6}(\\d{3})", "$1******$2");
|
||||
}
|
||||
// 非标准格式(如16位、含特殊字符):返回部分脱敏(前4位+****+后2位),避免明文暴露
|
||||
else {
|
||||
int len = idCard.length();
|
||||
if (len >= 6) {
|
||||
return idCard.substring(0, 4) + "****" + idCard.substring(len - 2);
|
||||
}
|
||||
// 长度过短(<6位):直接返回***,避免明文
|
||||
return "***";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转大写
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static String toUpperCaseIgnoreBlank(String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmedStr = str.trim();
|
||||
if (trimmedStr.isEmpty()) {
|
||||
return str;
|
||||
}
|
||||
return str.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ public class WechatUtil {
|
||||
try {
|
||||
System.out.println("appid==============="+appid);
|
||||
System.out.println("secret================"+secret);
|
||||
String response = getAccessData("https://api.weixin.qq.com/sns/jscode2session?appid="+appid+"&secret="+secret+"&js_code="+code+"&grant_type=authorization_code");
|
||||
String response = getAccessData(AliyunNlsUtils.getWXoppenidUrl()+"?appid="+appid+"&secret="+secret+"&js_code="+code+"&grant_type=authorization_code");
|
||||
JSONObject result = JSONObject.parseObject(response);
|
||||
// 微信返回错误码处理
|
||||
if (result.containsKey("errcode") && result.getInteger("errcode") != 0) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.cms.mapper.AiChatDetailMapper">
|
||||
|
||||
<select id="getList" parameterType="com.ruoyi.cms.domain.ai.AiChatDetail" resultType="com.ruoyi.cms.domain.ai.AiChatDetail">
|
||||
select * from ai_chat_detail where chat_id=#{chatId} order by time
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.cms.mapper.AiChatHistoryMapper">
|
||||
|
||||
<select id="getList" parameterType="com.ruoyi.cms.domain.ai.AiChatHistory" resultType="com.ruoyi.cms.domain.ai.AiChatHistory">
|
||||
select * from ai_chat_history where del_flag='0'
|
||||
<if test="userId != null and userId != ''">
|
||||
and user_id = #{userId}
|
||||
</if>
|
||||
<if test="chatId != null and chatId != ''">
|
||||
and chat_id = #{chatId}
|
||||
</if>
|
||||
<if test="appId != null and appId != ''">
|
||||
and app_id = #{appId}
|
||||
</if>
|
||||
<if test="title != null and title != ''">
|
||||
and title like CONCAT('%',#{title},'%')
|
||||
</if>
|
||||
<if test="updateTime != null">
|
||||
and to_char(update_time,'yyyy-mm-dd') = to_char(#{updateTime},'yyyy-mm-dd')
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -31,10 +31,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="idCard" column="id_card" />
|
||||
<result property="workExperience" column="work_experience" />
|
||||
<result property="isCompanyUser" column="is_company_user" />
|
||||
<result property="dwUserid" column="dw_userid" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectAppUserVo">
|
||||
select user_id, name, age, sex, birth_date, education, political_affiliation, phone, avatar, salary_min, salary_max, area, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark,job_title_id,is_recommend,id_card,work_experience,is_company_user from app_user
|
||||
select user_id, name, age, sex, birth_date, education, political_affiliation, phone, avatar, salary_min, salary_max, area, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark,job_title_id,is_recommend,id_card,work_experience,is_company_user,dw_userid from app_user
|
||||
</sql>
|
||||
|
||||
<select id="selectAppUserList" parameterType="AppUser" resultMap="AppUserResult">
|
||||
@@ -67,7 +68,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</select>
|
||||
|
||||
<select id="selectByOpenid" resultType="com.ruoyi.common.core.domain.entity.AppUser">
|
||||
<include refid="selectAppUserVo"/> WHERE DEL_FLAG = '0' and openid=#{openid} LIMIT 1
|
||||
<include refid="selectAppUserVo"/> WHERE DEL_FLAG = '0' and openid=#{openid} and is_company_user=#{userType} LIMIT 1
|
||||
</select>
|
||||
|
||||
<insert id="insertSysUserRole" parameterType="java.util.Map">
|
||||
@@ -109,10 +110,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</insert>
|
||||
|
||||
<select id="getMyTj" resultType="com.ruoyi.common.core.domain.entity.MyChart">
|
||||
SELECT t1.yzj,t2.ysc,t3.ytd,0 AS yyy FROM
|
||||
SELECT t1.yzj,t2.ysc,t3.ytd,0 AS yyy,t4.yqx FROM
|
||||
(SELECT COUNT(user_id) AS yzj FROM app_review_job WHERE user_id = #{userId} AND del_flag = '0') t1
|
||||
CROSS JOIN (SELECT COUNT(user_id) AS ysc FROM job_collection WHERE user_id = #{userId} AND del_flag = '0') t2
|
||||
CROSS JOIN (SELECT COUNT(user_id) AS ytd FROM job_apply WHERE user_id = #{userId} AND del_flag = '0') t3
|
||||
CROSS JOIN (SELECT COUNT(user_id) AS yqx FROM job_apply WHERE user_id = #{userId} AND del_flag = '2') t4
|
||||
</select>
|
||||
|
||||
<select id="selectSysUserIdcard" resultType="com.ruoyi.common.core.domain.entity.SysUser">
|
||||
|
||||
@@ -106,7 +106,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</select>
|
||||
|
||||
<select id="selectCompanyByJobId" resultType="com.ruoyi.common.core.domain.entity.Company">
|
||||
select a.* from COMPANY a inner join job b on a.company_id=b.company_id and b.job_id=#{jobId} limit 1
|
||||
select a.* from COMPANY a inner join job b on a.company_id=b.company_id and a.del_flag = '0' and b.del_flag = '0' and b.job_id=#{jobId} limit 1
|
||||
</select>
|
||||
|
||||
<select id="selectByNames" resultType="com.ruoyi.common.core.domain.entity.Company" parameterType="java.util.List">
|
||||
SELECT company_id, name FROM company
|
||||
WHERE del_flag = '0' and name IN
|
||||
<foreach collection="list" item="name" open="(" separator="," close=")">
|
||||
#{name}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -39,7 +39,42 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
where
|
||||
<if test="longs != null and longs.size() > 0">
|
||||
bussinessid in (
|
||||
<foreach collection="longs" item="oldId" open="(" close=")" separator=",">
|
||||
<foreach collection="longs" item="oldId" separator=",">
|
||||
#{oldId}
|
||||
</foreach>
|
||||
)
|
||||
</if>
|
||||
<if test="longs == null or longs.size() == 0">
|
||||
1 = 2
|
||||
</if>
|
||||
</update>
|
||||
|
||||
<select id="selectFileListByBussinessIds" resultMap="FileResult">
|
||||
<include refid="selectFileVo"/>
|
||||
<where> del_flag = '0'
|
||||
<if test="longs != null and longs.size() > 0">
|
||||
and bussinessid in (
|
||||
<foreach collection="longs" item="oldId" separator=",">
|
||||
#{oldId}
|
||||
</foreach>
|
||||
)
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<update id="updateIds">
|
||||
update file
|
||||
<if test="newBussinessid != null">
|
||||
set bussinessid = #{newBussinessid}
|
||||
</if>
|
||||
<if test="newBussinessid == null">
|
||||
set bussinessid = bussinessid
|
||||
where 1 = 2
|
||||
</if>
|
||||
where
|
||||
<if test="longs != null and longs.size() > 0">
|
||||
id in (
|
||||
<foreach collection="longs" item="oldId" separator=",">
|
||||
#{oldId}
|
||||
</foreach>
|
||||
)
|
||||
|
||||
@@ -130,4 +130,20 @@
|
||||
update job_apply set update_time=sysdate(),update_by=#{updateBy},hire=#{hire},hire_source=#{hireSource} where user_id=#{userId} AND job_id=#{jobId}
|
||||
</update>
|
||||
|
||||
<update id="applyJobCencal" parameterType="JobApply">
|
||||
update job_apply set del_flag='2',update_time=sysdate(),update_by=#{updateBy} where user_id=#{userId} and job_id=#{jobId}
|
||||
</update>
|
||||
|
||||
<select id="selectCencalList" parameterType="JobApply" resultType="com.ruoyi.cms.domain.Job">
|
||||
select b.*,a.create_time as shareTime from job_apply a inner join job b on a.job_id=b.job_id and b.del_flag='0'
|
||||
<where> a.del_flag = '2'
|
||||
<if test="jobId != null "> and a.job_id = #{jobId}</if>
|
||||
<if test="userId != null "> and a.user_id = #{userId}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="applyCencalCount" resultType="java.lang.Integer">
|
||||
select count(user_id) from job_apply where del_flag = '2' and user_id=#{userId}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -55,4 +55,22 @@
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<select id="selectByJobIds" parameterType="java.util.List" resultMap="jobContactResult">
|
||||
<include refid="JobContactVo"/>
|
||||
<where> del_flag = '0'
|
||||
<if test="jobIds != null and jobIds.size() > 0">
|
||||
AND job_id IN (
|
||||
<foreach collection="jobIds" item="jobId" separator=",">
|
||||
<if test="jobId != null">
|
||||
#{jobId}
|
||||
</if>
|
||||
</foreach>
|
||||
)
|
||||
</if>
|
||||
<if test="jobIds == null or jobIds.size() == 0">
|
||||
AND 1 = 0
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
<result property="explainUrl" column="explain_url" />
|
||||
<result property="cover" column="cover" />
|
||||
<result property="jobType" column="job_type" />
|
||||
<result property="jobAddress" column="job_address" />
|
||||
<result property="regionCode" column="region_code" />
|
||||
<result property="staffType" column="staff_type" />
|
||||
|
||||
</resultMap>
|
||||
|
||||
@@ -77,6 +80,9 @@
|
||||
<result property="scale" column="scale" />
|
||||
<result property="companyNature" column="company_nature" />
|
||||
<result property="code" column="code" />
|
||||
<result property="jobAddress" column="job_address" />
|
||||
<result property="regionCode" column="region_code" />
|
||||
<result property="staffType" column="staff_type" />
|
||||
|
||||
<association property="companyVo" resultMap="CompanyResult"/>
|
||||
</resultMap>
|
||||
@@ -95,7 +101,7 @@
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectJobVo">
|
||||
select job_id, job_title, min_salary, max_salary, education, experience, company_name, job_location, posting_date, vacancies, del_flag, create_by, create_time, update_by, update_time, remark, latitude, longitude, "view", company_id , is_hot ,apply_num,is_publish, description,job_location_area_code,data_source,job_url,job_category,is_explain,explain_url,cover,job_type from job
|
||||
select job_id, job_title, min_salary, max_salary, education, experience, company_name, job_location, posting_date, vacancies, del_flag, create_by, create_time, update_by, update_time, remark, latitude, longitude, "view", company_id , is_hot ,apply_num,is_publish, description,job_location_area_code,data_source,job_url,job_category,is_explain,explain_url,cover,job_type,job_address,region_code,staff_type from job
|
||||
</sql>
|
||||
<insert id="insertBatchRowWork">
|
||||
INSERT INTO row_work (
|
||||
@@ -125,7 +131,7 @@
|
||||
job_title, min_salary, max_salary, education, experience, company_name, job_location,
|
||||
job_location_area_code, posting_date, vacancies, latitude, longitude, "view", company_id,
|
||||
is_hot, apply_num, description, is_publish, data_source, job_url, remark, del_flag,
|
||||
create_by, create_time, row_id, job_category,jobType
|
||||
create_by, create_time, row_id, job_category,job_type,type,job_address,region_code,staff_type
|
||||
) VALUES
|
||||
<foreach collection="list" item="job" separator=",">
|
||||
(
|
||||
@@ -134,7 +140,8 @@
|
||||
#{job.vacancies}, #{job.latitude}, #{job.longitude}, #{job.view}, #{job.companyId},
|
||||
#{job.isHot}, #{job.applyNum}, #{job.description}, #{job.isPublish}, #{job.dataSource},
|
||||
#{job.jobUrl}, #{job.remark}, #{job.delFlag}, #{job.createBy}, #{job.createTime},
|
||||
#{job.rowId}, #{job.jobCategory},#{job.jobType}
|
||||
#{job.rowId}, #{job.jobCategory},#{job.jobType},#{job.type},#{job.jobAddress},
|
||||
#{job.regionCode},#{job.staffType}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
@@ -275,7 +282,8 @@
|
||||
<select id="selectAllJob" resultMap="JobEsResult">
|
||||
SELECT j.*,c.industry,c.scale,c.nature as company_nature,c.code,c.description as company_description,c.name,c.company_id,t.contact_person,t.contact_person_phone FROM job as j
|
||||
left join company as c on c.company_id = j.company_id and j.del_flag='0' and c.del_flag='0'
|
||||
left join company_contact as t on t.company_id=j.company_id and t.del_flag='0' limit #{offset},#{batchSize}
|
||||
LEFT JOIN (SELECT t1.*, ROW_NUMBER() OVER (PARTITION BY t1.company_id ORDER BY t1.id) AS rn FROM company_contact AS t1 WHERE t1.del_flag = '0' ) AS t
|
||||
ON t.company_id = j.company_id AND t.rn = 1 WHERE j.del_flag = '0' limit #{offset},#{batchSize}
|
||||
</select>
|
||||
<select id="selectAllInsertRowWork" resultType="com.ruoyi.cms.domain.RowWork">
|
||||
select Id, TaskId, TaskName, Std_class, SF, ZCMC, Aca112, Acb22a, Aac011, Acb240,
|
||||
@@ -292,7 +300,7 @@
|
||||
</select>
|
||||
<select id="selectVectorJob" resultType="com.ruoyi.cms.domain.VectorJob">
|
||||
SELECT
|
||||
ANY_VALUE(job_id) as job_id,
|
||||
j.job_id,
|
||||
j.job_title,
|
||||
ed.dict_label as education,
|
||||
ex.dict_label as experience,
|
||||
@@ -302,15 +310,15 @@
|
||||
ar.dict_label as area,
|
||||
ab.dict_label as nature,
|
||||
ac.dict_label as scale,
|
||||
concat(j.min_salary,"元-",j.max_salary,"元") as salary
|
||||
concat(j.min_salary,'元-',j.max_salary,'元') as salary
|
||||
FROM
|
||||
job as j
|
||||
inner join company as c on c.company_id = j.company_id
|
||||
inner join qd.bussiness_dict_data as ed on ed.dict_type = 'education' and ed.dict_value = j.education
|
||||
inner join qd.bussiness_dict_data as ex on ex.dict_type = 'experience' and ex.dict_value = j.experience
|
||||
left join qd.bussiness_dict_data as ar on ar.dict_type = 'area' and ar.dict_value = j.job_location_area_code
|
||||
left join qd.bussiness_dict_data as ab on ab.dict_type = 'company_nature' and ab.dict_value = c.nature
|
||||
left join qd.bussiness_dict_data as ac on ac.dict_type = 'scale' and ac.dict_value = c.scale
|
||||
inner join bussiness_dict_data as ed on ed.dict_type = 'education' and ed.dict_value = j.education
|
||||
inner join bussiness_dict_data as ex on ex.dict_type = 'experience' and ex.dict_value = j.experience
|
||||
left join bussiness_dict_data as ar on ar.dict_type = 'area' and ar.dict_value = j.job_location_area_code
|
||||
left join bussiness_dict_data as ab on ab.dict_type = 'company_nature' and ab.dict_value = c.nature
|
||||
left join bussiness_dict_data as ac on ac.dict_type = 'scale' and ac.dict_value = c.scale
|
||||
where job_id =#{jobId}
|
||||
</select>
|
||||
|
||||
@@ -328,4 +336,42 @@
|
||||
left join company as c on c.company_id = j.company_id and j.del_flag='0' and j.job_id=#{jobId}
|
||||
and c.del_flag='0' limit 1
|
||||
</select>
|
||||
|
||||
<select id="getTotals" parameterType="Job" resultType="java.lang.Integer">
|
||||
select count(job_id) as total_count from job
|
||||
<where> del_flag = '0'
|
||||
<if test="companyName != null and companyName != ''"> and company_name =#{companyName}</if>
|
||||
<if test="dataSource != null and dataSource!='' "> and data_source = #{dataSource}</if>
|
||||
<if test="description != null and description!='' "> and description = #{description}</if>
|
||||
<if test="education != null and education != ''"> and education = #{education}</if>
|
||||
<if test="experience != null and experience != ''"> and experience = #{experience}</if>
|
||||
<if test="isPublish != null "> and is_publish = #{isPublish}</if>
|
||||
<if test="jobLocation != null and jobLocation != ''"> and job_location = #{jobLocation}</if>
|
||||
<if test="jobTitle != null and jobTitle != ''"> and job_title = #{jobTitle}</if>
|
||||
<if test="minSalary != null "> and min_salary = #{minSalary}</if>
|
||||
<if test="maxSalary != null "> and max_salary = #{maxSalary}</if>
|
||||
<if test="vacancies != null "> and vacancies = #{vacancies}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<insert id="updateFileBatchInsert" useGeneratedKeys="true" keyProperty="jobId" parameterType="java.util.List">
|
||||
INSERT INTO job (
|
||||
job_title, min_salary, max_salary, education, experience, company_name, job_location,
|
||||
job_location_area_code, posting_date, vacancies, latitude, longitude, "view", company_id,
|
||||
is_hot, apply_num, description, is_publish, data_source, job_url, remark, del_flag,
|
||||
create_by, create_time, row_id, job_category,job_type,type,job_address,region_code,staff_type
|
||||
) VALUES
|
||||
<foreach collection="list" item="job" separator=",">
|
||||
(
|
||||
#{job.jobTitle}, #{job.minSalary}, #{job.maxSalary}, #{job.education}, #{job.experience},
|
||||
#{job.companyName}, #{job.jobLocation}, #{job.jobLocationAreaCode}, #{job.postingDate},
|
||||
#{job.vacancies}, #{job.latitude}, #{job.longitude}, #{job.view}, #{job.companyId},
|
||||
#{job.isHot}, #{job.applyNum}, #{job.description}, #{job.isPublish}, #{job.dataSource},
|
||||
#{job.jobUrl}, #{job.remark}, #{job.delFlag}, #{job.createBy}, #{job.createTime},
|
||||
#{job.rowId}, #{job.jobCategory},#{job.jobType},#{job.type},#{job.jobAddress},
|
||||
#{job.regionCode},#{job.staffType}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.ruoyi.cms.mapper.StaticsMapper">
|
||||
|
||||
<select id="qygwtjCount" parameterType="com.ruoyi.cms.domain.query.Staticsquery" resultType="com.ruoyi.cms.domain.StaticsJob">
|
||||
WITH time_params AS (
|
||||
SELECT
|
||||
CAST(#{startTime} AS timestamp) AS start_time,
|
||||
CAST(#{endTime} AS timestamp) AS end_time
|
||||
),
|
||||
job_stats AS (
|
||||
SELECT COUNT(*) AS 归集岗位合计 FROM job, time_params tp
|
||||
WHERE posting_date >= tp.start_time AND posting_date < tp.end_time
|
||||
),
|
||||
company_stats AS (
|
||||
SELECT COUNT(company_id) AS 注册企业数 FROM company, time_params tp
|
||||
WHERE del_flag='0' AND create_time >= tp.start_time AND create_time < tp.end_time
|
||||
),
|
||||
user_stats AS (
|
||||
SELECT COUNT(*) AS 求职者实名数 FROM app_user, time_params tp
|
||||
WHERE del_flag='0' AND is_company_user='1' AND id_card IS NOT NULL
|
||||
AND create_time >= tp.start_time AND create_time < tp.end_time
|
||||
),
|
||||
apply_stats AS (
|
||||
SELECT COUNT(id) AS 简历投递数量 FROM job_apply, time_params tp
|
||||
WHERE del_flag='0' AND create_time >= tp.start_time AND create_time < tp.end_time
|
||||
)
|
||||
SELECT
|
||||
js.归集岗位合计 gjgwhj, cs.注册企业数 zcqys,
|
||||
us.求职者实名数 qzzsms, as2.简历投递数量 jlsl
|
||||
FROM job_stats js, company_stats cs, user_stats us, apply_stats as2;
|
||||
</select>
|
||||
|
||||
<select id="getGroutCityJobs" parameterType="com.ruoyi.cms.domain.query.Staticsquery" resultType="com.ruoyi.cms.domain.StaticsJob">
|
||||
WITH job_stats AS (
|
||||
SELECT job_location_area_code, COUNT(job_id) AS zs,
|
||||
SUM(CASE WHEN education >= 3 THEN 1 ELSE 0 END) AS gxbysgw,
|
||||
SUM(CASE WHEN del_flag = '0' THEN 1 ELSE 0 END) AS sszzgw
|
||||
FROM job WHERE job_location_area_code IS NOT NULL
|
||||
<if test="startTime!=null and startTime!='' ">
|
||||
<![CDATA[ AND posting_date >= CAST(#{startTime} AS timestamp) ]]>
|
||||
</if>
|
||||
<if test="endTime!=null and endTime!='' ">
|
||||
<![CDATA[ AND posting_date <= CAST(#{endTime} AS timestamp) ]]>
|
||||
</if>
|
||||
GROUP BY job_location_area_code
|
||||
),apply_stats AS (
|
||||
SELECT j.job_location_area_code, COUNT(ja.id) AS jlsl
|
||||
FROM job j INNER JOIN job_apply ja ON j.job_id = ja.job_id
|
||||
WHERE j.job_location_area_code IS NOT NULL
|
||||
GROUP BY j.job_location_area_code )
|
||||
SELECT js.zs, js.gxbysgw, js.sszzgw, COALESCE(as2.jlsl, 0) AS jlsl,
|
||||
bdd.dict_label as label FROM job_stats js
|
||||
LEFT JOIN bussiness_dict_data bdd ON js.job_location_area_code = bdd.dict_value AND bdd.dict_type = 'area'
|
||||
LEFT JOIN apply_stats as2 ON js.job_location_area_code = as2.job_location_area_code
|
||||
ORDER BY bdd.dict_sort
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -24,4 +24,13 @@
|
||||
order by code
|
||||
</select>
|
||||
|
||||
<select id="getCityList" resultType="com.ruoyi.common.core.domain.entity.SysArea" parameterType="com.ruoyi.common.core.domain.entity.SysArea">
|
||||
WITH top_area AS (
|
||||
SELECT code, name, (name LIKE '%市%') AS is_municipality FROM sys_area WHERE del_flag='0' and parent_code IS NULL
|
||||
)
|
||||
SELECT sa.*,get_name_first_pinyin(sa.name) AS zm FROM sys_area sa INNER JOIN top_area ta ON sa.code = ta.code WHERE ta.is_municipality = TRUE
|
||||
UNION ALL
|
||||
SELECT sa.*,get_name_first_pinyin(sa.name) AS zm FROM sys_area sa INNER JOIN top_area ta ON sa.parent_code = ta.code WHERE ta.is_municipality = FALSE
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.ruoyi.cms.mapper.WechatGroupMapper">
|
||||
|
||||
<select id="selectWechatGroupList" resultType="com.ruoyi.cms.domain.vo.WechatGroupVo">
|
||||
select g.create_time, g.id, g.name, g.is_push, u.wechat_name, u.phone_number, u.id communityId
|
||||
from wechat_group g join community_user u on g.community_id = u.id
|
||||
where g.del_flag = 0 and u.del_flag = 0
|
||||
<if test="p.name != null and p.name != ''">
|
||||
and g.name like '%' || #{p.name} || '%'
|
||||
</if>
|
||||
<if test="p.isPush != null">
|
||||
and g.is_push = #{p.isPush}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -120,6 +120,22 @@
|
||||
<artifactId>UserAgentUtils</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--奇安信密码机-->
|
||||
<!--<dependency>
|
||||
<groupId>org.quickssl</groupId>
|
||||
<artifactId>quickapi-client-java</artifactId>
|
||||
<version>1.5.11-SNAPSHOT</version>
|
||||
<classifier>shaded</classifier>
|
||||
<scope>compile</scope>
|
||||
</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>1.59</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- servlet包 -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
@@ -142,6 +158,12 @@
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.7.22</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.ruoyi.common.config;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
@Component
|
||||
public class AudioTextRequestClient {
|
||||
|
||||
@Value("${audioText.asr}")
|
||||
private String asr;
|
||||
|
||||
// 1. 读取 TTS 服务地址
|
||||
@Value("${audioText.tts}")
|
||||
private String tts;
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
|
||||
public String getTextFromAudioFile(MultipartFile file) throws IOException {
|
||||
HttpRequest request = HttpRequest.post(asr);
|
||||
request.header(HttpHeaders.CONTENT_TYPE,MediaType.MULTIPART_FORM_DATA_VALUE);
|
||||
request.form("file",file.getBytes(),file.getOriginalFilename());
|
||||
HttpResponse response = request.execute();
|
||||
if(response.isOk()){
|
||||
String body = response.body();
|
||||
JSONObject jsonObject = JSONObject.parseObject(body);
|
||||
if("0".equals(jsonObject.getString("code")) || "200".equals(jsonObject.getString("code"))){
|
||||
return jsonObject.getString("text");
|
||||
}else{
|
||||
throw new RuntimeException("语音转文字接口调用失败: " + jsonObject.getString("error"));
|
||||
}
|
||||
}else{
|
||||
throw new RuntimeException("语音转文字接口调用失败: " + response.body());
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getAudioInputStreamFromText(String audioText) throws UnsupportedEncodingException {
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("text", audioText);
|
||||
object.put("speaker_id", 0);
|
||||
object.put("length_scale", 1.0);
|
||||
object.put("noise_scale", 0.667);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<String> entity = new HttpEntity<>(object.toJSONString(), headers);
|
||||
ResponseEntity<byte[]> response = restTemplate.postForEntity(tts, entity, byte[].class);
|
||||
if (!response.getStatusCode().is2xxSuccessful()){
|
||||
String body = new String(response.getBody(),"UTF-8");
|
||||
JSONObject jsonObject = JSONObject.parseObject(body);
|
||||
throw new RuntimeException("文字转语音接口调用失败: " + jsonObject.getString("error"));
|
||||
}else{
|
||||
return response.getBody();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.ruoyi.common.constant;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 加解密配置常量
|
||||
*/
|
||||
public class EncryptConstants {
|
||||
|
||||
/**
|
||||
* 是否开启加解密功能
|
||||
*/
|
||||
public static final boolean ENCRYPT_ENABLED = true;
|
||||
|
||||
/**
|
||||
* 需要加解密的URL路径模式
|
||||
*/
|
||||
public static final List<String> URL_PATTERNS = Arrays.asList(
|
||||
/*"/app/login",
|
||||
"/app/user/resume",
|
||||
"/app/user/experience/edit",
|
||||
"/app/user/experience/delete",
|
||||
"/app/user/experience/getSingle/*",
|
||||
"/app/user/experience/list",
|
||||
"/login",
|
||||
"/system/user/resetPwd",
|
||||
"/system/user/list",
|
||||
"/system/user",
|
||||
"/cms/appUser/list",
|
||||
"/cms/appUser/getResumeList",
|
||||
"/cms/appUser/getResumeDetail/*",
|
||||
"/app/alipay/scanLogin",
|
||||
"/app/user/cert"*/
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.ruoyi.common.constant;
|
||||
|
||||
public class SM4Constants {
|
||||
|
||||
public static final String SM4_KET = "86C63180C1306ABC4D8F989E0A0BC9F3";
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.ruoyi.common.core.domain.entity;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@@ -111,7 +110,7 @@ public class AppUser extends BaseEntity
|
||||
/**
|
||||
* 是否企业用户 0是,1否改为(0企业,1求职者,2网格员)
|
||||
*/
|
||||
@ApiModelProperty("app角色:0企业,1求职者,2网格员 3内部政府人员")
|
||||
@ApiModelProperty("app角色:0企业,1求职者,2网格员 3内部政府人员 4其他(浪潮用)")
|
||||
private String isCompanyUser;
|
||||
|
||||
@TableField(exist = false)
|
||||
@@ -135,6 +134,9 @@ public class AppUser extends BaseEntity
|
||||
@ApiModelProperty("工作经验")
|
||||
private String workExperience;
|
||||
|
||||
@ApiModelProperty("机构类型 0用人单位 1培训机构 2评价机构 3人力资源机构")
|
||||
private String orgType;
|
||||
|
||||
@TableField(exist = false)
|
||||
@ApiModelProperty("公司信息")
|
||||
private Company company;
|
||||
@@ -162,4 +164,7 @@ public class AppUser extends BaseEntity
|
||||
|
||||
@ApiModelProperty("户籍地址")
|
||||
private String domicileAddress;
|
||||
|
||||
@ApiModelProperty("地纬userid")
|
||||
private String dwUserid;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MyChart {
|
||||
private String ytd;
|
||||
private String ysc;
|
||||
private String yzj;
|
||||
private String yyy;
|
||||
private String ytd;//投递
|
||||
private String ysc;//收藏
|
||||
private String yzj;//足迹
|
||||
private String yyy;//已预约
|
||||
private String yqx;//已取消
|
||||
}
|
||||
|
||||
@@ -43,4 +43,7 @@ public class SysArea extends BaseEntity {
|
||||
@ApiModelProperty("父级地区编码(顶级为NULL)")
|
||||
private String parentCode;
|
||||
|
||||
@TableField(exist = false)
|
||||
@ApiModelProperty("字母")
|
||||
private String zm;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,11 @@ public class LoginBody
|
||||
*/
|
||||
private String idCard;
|
||||
|
||||
/**
|
||||
* 企业类型
|
||||
*/
|
||||
public String orgType;
|
||||
|
||||
public String getUsername()
|
||||
{
|
||||
return username;
|
||||
@@ -115,4 +120,12 @@ public class LoginBody
|
||||
public void setIdCard(String idCard) {
|
||||
this.idCard = idCard;
|
||||
}
|
||||
|
||||
public String getOrgType() {
|
||||
return orgType;
|
||||
}
|
||||
|
||||
public void setOrgType(String orgType) {
|
||||
this.orgType = orgType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
package com.ruoyi.common.core.redis;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* JDK 1.8 兼容版分布式锁工具类(基于 Redis)
|
||||
*/
|
||||
@Component
|
||||
public class DistributedLockUtil {
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
// 锁默认配置
|
||||
private static final long DEFAULT_EXPIRE_SECONDS = 30;
|
||||
private static final long DEFAULT_ACQUIRE_TIMEOUT_SECONDS = 5;
|
||||
private static final long RENEW_INTERVAL_SECONDS = DEFAULT_EXPIRE_SECONDS / 3;
|
||||
|
||||
// 续期线程池
|
||||
private final ScheduledExecutorService renewExecutor = Executors.newScheduledThreadPool(
|
||||
Runtime.getRuntime().availableProcessors(),
|
||||
new ThreadFactory() {
|
||||
private final AtomicBoolean init = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread thread = new Thread(r, "distributed-lock-renewer");
|
||||
thread.setDaemon(true);
|
||||
if (init.compareAndSet(false, true)) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
renewExecutor.shutdown();
|
||||
}
|
||||
}));
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 释放锁 Lua 脚本(原子操作)
|
||||
private static final String RELEASE_LOCK_LUA_SCRIPT =
|
||||
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
|
||||
" return redis.call('del', KEYS[1]) " +
|
||||
"else " +
|
||||
" return 0 " +
|
||||
"end";
|
||||
private final DefaultRedisScript<Long> releaseScript = new DefaultRedisScript<>();
|
||||
|
||||
// 初始化 Lua 脚本
|
||||
{
|
||||
releaseScript.setScriptText(RELEASE_LOCK_LUA_SCRIPT);
|
||||
releaseScript.setResultType(Long.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取锁(带自动续期)
|
||||
*/
|
||||
public String acquireLockWithRenewal(String lockKey) {
|
||||
return acquireLockWithRenewal(lockKey, DEFAULT_EXPIRE_SECONDS, DEFAULT_ACQUIRE_TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义参数获取锁
|
||||
*/
|
||||
public String acquireLockWithRenewal(String lockKey, long expireSeconds, long acquireTimeoutSeconds) {
|
||||
String identifier = UUID.randomUUID().toString();
|
||||
long endTime = System.currentTimeMillis() + acquireTimeoutSeconds * 1000;
|
||||
|
||||
while (System.currentTimeMillis() < endTime) {
|
||||
boolean locked = tryLockOnce(lockKey, identifier, expireSeconds);
|
||||
if (locked) {
|
||||
startRenewal(lockKey, identifier, expireSeconds);
|
||||
return identifier;
|
||||
}
|
||||
|
||||
// 指数退避重试
|
||||
try {
|
||||
long sleepMs = calculateBackoffSleep(System.currentTimeMillis() - (endTime - acquireTimeoutSeconds * 1000));
|
||||
Thread.sleep(sleepMs);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 原子释放锁
|
||||
*/
|
||||
public boolean releaseLockSafely(String lockKey, String identifier) {
|
||||
if (identifier == null) {
|
||||
return false;
|
||||
}
|
||||
Long result = (Long) redisCache.redisTemplate.execute(
|
||||
releaseScript,
|
||||
Collections.singletonList(lockKey),
|
||||
identifier
|
||||
);
|
||||
return result != null && result > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取一次锁
|
||||
*/
|
||||
private boolean tryLockOnce(String lockKey, String identifier, long expireSeconds) {
|
||||
try {
|
||||
if (!redisCache.hasKey(lockKey)) {
|
||||
redisCache.setCacheObject(lockKey, identifier, (int) expireSeconds, TimeUnit.SECONDS);
|
||||
String storedId = redisCache.getCacheObject(lockKey);
|
||||
return identifier.equals(storedId);
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动锁自动续期
|
||||
*/
|
||||
private void startRenewal(final String lockKey, final String identifier, final long expireSeconds) {
|
||||
final AtomicReference<ScheduledFuture<?>> futureRef = new AtomicReference<>();
|
||||
Runnable renewalTask = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
String storedId = redisCache.getCacheObject(lockKey);
|
||||
if (identifier.equals(storedId)) {
|
||||
redisCache.expire(lockKey, expireSeconds);
|
||||
} else {
|
||||
cancelFuture();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
cancelFuture();
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelFuture() {
|
||||
ScheduledFuture<?> future = futureRef.get();
|
||||
if (future != null && !future.isCancelled()) {
|
||||
future.cancel(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ScheduledFuture<?> future = renewExecutor.scheduleAtFixedRate(
|
||||
renewalTask,
|
||||
RENEW_INTERVAL_SECONDS,
|
||||
RENEW_INTERVAL_SECONDS,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
futureRef.set(future);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指数退避算法(计算重试间隔)
|
||||
*/
|
||||
private long calculateBackoffSleep(long elapsedMs) {
|
||||
int retryCount = (int) (elapsedMs / 100);
|
||||
long sleepMs = 100L * (1 << Math.min(retryCount, 10));
|
||||
return Math.min(sleepMs, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动释放锁工具(支持 try-with-resources)
|
||||
*/
|
||||
public static class AutoReleaseLock implements AutoCloseable {
|
||||
private final DistributedLockUtil lockUtil;
|
||||
private final String lockKey;
|
||||
private final String identifier;
|
||||
|
||||
public AutoReleaseLock(DistributedLockUtil lockUtil, String lockKey, String identifier) {
|
||||
this.lockUtil = lockUtil;
|
||||
this.lockKey = lockKey;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return identifier != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (isLocked()) {
|
||||
lockUtil.releaseLockSafely(lockKey, identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化锁使用
|
||||
*/
|
||||
public AutoReleaseLock tryLock(String lockKey) {
|
||||
String identifier = acquireLockWithRenewal(lockKey);
|
||||
return new AutoReleaseLock(this, lockKey, identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁+超时时间
|
||||
* @param lockKey
|
||||
* @param timeout
|
||||
* @param unit
|
||||
* @return
|
||||
*/
|
||||
public AutoReleaseLock tryLock(String lockKey, long timeout, TimeUnit unit) {
|
||||
// 1. 校验参数合法性(防御性编程)
|
||||
if (lockKey == null || lockKey.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("锁key不能为空");
|
||||
}
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("超时时间不能为负数");
|
||||
}
|
||||
if (unit == null) {
|
||||
throw new IllegalArgumentException("时间单位不能为空");
|
||||
}
|
||||
|
||||
// 2. 带超时获取锁(核心逻辑,需实现 acquireLockWithTimeout 方法)
|
||||
String identifier = acquireLockWithTimeout(lockKey, timeout, unit);
|
||||
|
||||
// 3. 返回自动释放锁(复用原有 AutoReleaseLock,无需修改)
|
||||
return new AutoReleaseLock(this, lockKey, identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 带超时时间获取锁(支持自定义超时单位)
|
||||
* @param lockKey 锁键
|
||||
* @param timeout 获取锁的超时时间
|
||||
* @param unit 时间单位
|
||||
* @return 锁标识(获取失败返回null)
|
||||
*/
|
||||
public String acquireLockWithTimeout(String lockKey, long timeout, TimeUnit unit) {
|
||||
// 转换超时时间为秒(向上取整避免精度丢失)
|
||||
long acquireTimeoutSeconds = (long) Math.ceil((double) unit.toMillis(timeout) / 1000);
|
||||
// 使用默认的锁过期时间(30秒),也可根据需要改为让调用方传入
|
||||
return acquireLockWithRenewal(lockKey, DEFAULT_EXPIRE_SECONDS, acquireTimeoutSeconds);
|
||||
}
|
||||
}
|
||||
@@ -56,4 +56,8 @@ public enum BusinessType
|
||||
* 清空数据
|
||||
*/
|
||||
CLEAN,
|
||||
/**
|
||||
* 查询
|
||||
*/
|
||||
QUERY
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.ruoyi.common.filter;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ruoyi.common.constant.EncryptConstants;
|
||||
import com.ruoyi.common.constant.SM4Constants;
|
||||
import com.ruoyi.common.utils.EncryptHttpServletResponseWrapper;
|
||||
import com.ruoyi.common.utils.SM4Utils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class EncryptResponseFilter implements Filter {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
// 检查是否开启加解密功能
|
||||
if (!EncryptConstants.ENCRYPT_ENABLED) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否是SSE流式响应 - 不处理
|
||||
if (isSseResponse(httpRequest, httpResponse)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查URL是否需要加密响应
|
||||
if (!needProcess(httpRequest)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 包装响应
|
||||
EncryptHttpServletResponseWrapper responseWrapper = new EncryptHttpServletResponseWrapper(httpResponse);
|
||||
chain.doFilter(request, responseWrapper);
|
||||
|
||||
// 获取原始响应内容
|
||||
byte[] content = responseWrapper.getContent();
|
||||
String originalResponse = new String(content, StandardCharsets.UTF_8);
|
||||
|
||||
// 加密响应内容
|
||||
if (StringUtils.isNotBlank(originalResponse) && !originalResponse.trim().isEmpty()) {
|
||||
String encryptedResponse = SM4Utils.encryptEcb(SM4Constants.SM4_KET, originalResponse);
|
||||
|
||||
// 构建加密响应
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("encrypted", true);
|
||||
result.put("encryptedData", encryptedResponse);
|
||||
result.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
String finalResponse = objectMapper.writeValueAsString(result);
|
||||
|
||||
// 设置响应
|
||||
byte[] encryptedBytes = finalResponse.getBytes(StandardCharsets.UTF_8);
|
||||
httpResponse.setContentLength(encryptedBytes.length);
|
||||
httpResponse.setContentType("application/json;charset=UTF-8");
|
||||
httpResponse.getOutputStream().write(encryptedBytes);
|
||||
} else {
|
||||
// 空响应直接返回空
|
||||
httpResponse.getOutputStream().write(new byte[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断请求是否需要处理
|
||||
*/
|
||||
private boolean needProcess(HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
|
||||
// 检查URL是否匹配需要加解密的模式
|
||||
for (String pattern : EncryptConstants.URL_PATTERNS) {
|
||||
if (pathMatcher.match(pattern, requestURI)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是SSE流式响应
|
||||
*/
|
||||
private boolean isSseResponse(HttpServletRequest request, HttpServletResponse response) {
|
||||
String path = request.getRequestURI();
|
||||
String method = request.getMethod();
|
||||
|
||||
// 检查是否是聊天接口
|
||||
if ("/app/chat/chat".equals(path) && "POST".equalsIgnoreCase(method)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查Accept头是否包含text/event-stream
|
||||
String acceptHeader = request.getHeader("Accept");
|
||||
if (acceptHeader != null && acceptHeader.contains("text/event-stream")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查响应头是否已经设置为text/event-stream
|
||||
String contentType = response.getContentType();
|
||||
if (contentType != null && contentType.contains("text/event-stream")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package com.ruoyi.common.filter;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ruoyi.common.constant.EncryptConstants;
|
||||
import com.ruoyi.common.constant.SM4Constants;
|
||||
import com.ruoyi.common.utils.EncryptHttpServletRequestWrapper;
|
||||
import com.ruoyi.common.utils.EncryptHttpServletResponseWrapper;
|
||||
import com.ruoyi.common.utils.SM4Utils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 请求包装过滤器 - 处理请求解密和包装
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RequestWrapperFilter implements Filter {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final PathMatcher pathMatcher = new AntPathMatcher();
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
// 检查是否开启加解密功能
|
||||
if (!EncryptConstants.ENCRYPT_ENABLED) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否是SSE流式响应 - 不处理
|
||||
if (isSseResponse(httpRequest, httpResponse)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查URL是否需要解密
|
||||
if (needProcess(httpRequest)) {
|
||||
// 处理请求解密
|
||||
String method = httpRequest.getMethod();
|
||||
if("POST".equalsIgnoreCase(method)) {
|
||||
HttpServletRequest processedRequest = processBodyRequest(httpRequest);
|
||||
chain.doFilter(processedRequest, response);
|
||||
}else{
|
||||
EncryptHttpServletResponseWrapper responseWrapper = new EncryptHttpServletResponseWrapper(httpResponse);
|
||||
String forwardUrl = buildGetRequestURI(httpRequest);
|
||||
if (StringUtils.isNotBlank(forwardUrl)) {
|
||||
log.info("GET请求解密后转发URL:{}", forwardUrl);
|
||||
// 服务器内部转发
|
||||
request.setAttribute("ENCRYPT_PROCESSED", Boolean.TRUE);
|
||||
RequestDispatcher dispatcher = request.getRequestDispatcher(forwardUrl);
|
||||
dispatcher.forward(request, responseWrapper);
|
||||
|
||||
// 2. 监听/处理转发后的响应(核心逻辑:从包装器中获取响应数据)
|
||||
byte[] content = responseWrapper.getContent();
|
||||
String originalResponse = new String(content, StandardCharsets.UTF_8);
|
||||
if (StringUtils.isNotBlank(originalResponse) && !originalResponse.trim().isEmpty()) {
|
||||
String encryptedResponse = SM4Utils.encryptEcb(SM4Constants.SM4_KET, originalResponse);
|
||||
|
||||
// 构建加密响应
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("encrypted", true);
|
||||
result.put("encryptedData", encryptedResponse);
|
||||
result.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
String finalResponse = objectMapper.writeValueAsString(result);
|
||||
|
||||
// 设置响应
|
||||
byte[] encryptedBytes = finalResponse.getBytes(StandardCharsets.UTF_8);
|
||||
httpResponse.setContentLength(encryptedBytes.length);
|
||||
httpResponse.setContentType("application/json;charset=UTF-8");
|
||||
httpResponse.getOutputStream().write(encryptedBytes);
|
||||
} else {
|
||||
// 空响应直接返回空
|
||||
httpResponse.getOutputStream().write(new byte[0]);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 不需要解密,直接放行
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断请求是否需要处理
|
||||
*/
|
||||
private boolean needProcess(HttpServletRequest request) {
|
||||
if (Boolean.TRUE.equals(request.getAttribute("ENCRYPT_PROCESSED"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String requestURI = request.getRequestURI();
|
||||
|
||||
// 检查URL是否匹配需要加解密的模式
|
||||
for (String pattern : EncryptConstants.URL_PATTERNS) {
|
||||
if (pathMatcher.match(pattern, requestURI)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是SSE流式响应
|
||||
*/
|
||||
private boolean isSseResponse(HttpServletRequest request, HttpServletResponse response) {
|
||||
String path = request.getRequestURI();
|
||||
String method = request.getMethod();
|
||||
|
||||
// 检查是否是聊天接口
|
||||
if ("/app/chat/chat".equals(path) && "POST".equalsIgnoreCase(method)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查Accept头是否包含text/event-stream
|
||||
String acceptHeader = request.getHeader("Accept");
|
||||
if (acceptHeader != null && acceptHeader.contains("text/event-stream")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查响应头是否已经设置为text/event-stream
|
||||
String contentType = response.getContentType();
|
||||
if (contentType != null && contentType.contains("text/event-stream")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private HttpServletRequest processBodyRequest(HttpServletRequest request) throws IOException {
|
||||
String body = getRequestBody(request);
|
||||
log.info("过滤器 - 原始请求体: " + body);
|
||||
|
||||
if (StringUtils.isNotBlank(body)) {
|
||||
try {
|
||||
// 解析请求体,提取encryptedData字段
|
||||
JsonNode jsonNode = objectMapper.readTree(body);
|
||||
JsonNode encryptedDataNode = jsonNode.get("encryptedData");
|
||||
|
||||
if (encryptedDataNode != null && encryptedDataNode.isTextual()) {
|
||||
String encryptedData = encryptedDataNode.asText();
|
||||
// 解密 encryptedData
|
||||
String decryptedData = SM4Utils.decryptEcb(SM4Constants.SM4_KET, encryptedData);
|
||||
|
||||
// 使用解密后的数据创建包装请求
|
||||
return new EncryptHttpServletRequestWrapper(request, decryptedData);
|
||||
} else {
|
||||
log.info("请求体中未找到encryptedData字段,直接返回原始请求");
|
||||
return request;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("POST请求体解密失败: " + e.getMessage());
|
||||
// 解密失败时返回原始请求,让业务层处理
|
||||
return request;
|
||||
}
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private String getRequestBody(HttpServletRequest request) throws IOException {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
BufferedReader bufferedReader = null;
|
||||
try {
|
||||
InputStream inputStream = request.getInputStream();
|
||||
if (inputStream != null) {
|
||||
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
char[] charBuffer = new char[128];
|
||||
int bytesRead;
|
||||
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
|
||||
stringBuilder.append(charBuffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (bufferedReader != null) {
|
||||
bufferedReader.close();
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private String buildGetRequestURI(HttpServletRequest request) {
|
||||
String baseUrl = request.getRequestURI(); // 完整基础URL
|
||||
String encryptedData = request.getParameter("encryptedData");
|
||||
|
||||
if (StringUtils.isBlank(encryptedData)) {
|
||||
log.info("GET请求未携带encryptedData参数,返回原始URL");
|
||||
return null; // 无加密参数,不处理
|
||||
}
|
||||
|
||||
try {
|
||||
// 解密参数(增加异常捕获,避免解密失败导致URL拼接异常)
|
||||
String decryptedData = SM4Utils.decryptEcb(SM4Constants.SM4_KET, encryptedData);
|
||||
if (StringUtils.isBlank(decryptedData)) {
|
||||
log.error("GET请求encryptedData解密后为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
JSONObject object = JSONObject.parseObject(decryptedData);
|
||||
if (object.isEmpty()) {
|
||||
log.info("解密后的参数为空,返回原始URL");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 拼接参数(用StringBuilder,避免字符串频繁拼接)
|
||||
StringBuilder params = new StringBuilder();
|
||||
for (Map.Entry<String, Object> entry : object.entrySet()) { // 用entrySet更高效
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
// 跳过空值和空key
|
||||
if (StringUtils.isBlank(key) || Objects.isNull(value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 处理参数值:数组/嵌套JSON转为JSON字符串,普通类型直接转字符串
|
||||
String paramValue = String.valueOf(value);
|
||||
|
||||
// URL编码(处理中文、特殊字符,如空格、&、=等)
|
||||
String encodedValue = URLEncoder.encode(paramValue, "UTF-8");
|
||||
if (params.length() > 0) {
|
||||
params.append("&");
|
||||
}
|
||||
params.append(key).append("=").append(encodedValue);
|
||||
}
|
||||
|
||||
// 拼接完整URL(仅当有参数时才加?)
|
||||
if (params.length() > 0) {
|
||||
return baseUrl + "?" + params.toString();
|
||||
} else {
|
||||
log.info("解密后的参数均为空,返回原始URL");
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("GET请求参数解密/拼接失败", e);
|
||||
return null; // 异常时返回null,走原始请求
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
@@ -219,4 +220,30 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加1天
|
||||
* @param dateStr
|
||||
* @param inputFormat
|
||||
* @param outputFormat
|
||||
* @return
|
||||
*/
|
||||
public static String addOneDay(String dateStr, String inputFormat, String outputFormat) {
|
||||
if (dateStr == null || dateStr.trim().isEmpty()
|
||||
|| inputFormat == null || inputFormat.trim().isEmpty()
|
||||
|| outputFormat == null || outputFormat.trim().isEmpty()) {
|
||||
System.err.println("参数错误:dateStr/inputFormat/outputFormat 不能为空");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern(inputFormat.trim());
|
||||
DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern(outputFormat.trim());
|
||||
LocalDate date = LocalDate.parse(dateStr.trim(), inputFormatter);
|
||||
LocalDate nextDay = date.plusDays(1);
|
||||
return nextDay.format(outputFormatter);
|
||||
} catch (Exception e) {
|
||||
System.err.println("日期解析失败:输入字符串[" + dateStr + "] 与输入格式[" + inputFormat + "]不匹配");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user