feat(myInvitation): 调整

master
yang 2025-03-07 13:51:04 +08:00
parent 535b175fab
commit 489db2410d
8 changed files with 328 additions and 304 deletions

View File

@ -1,30 +1,30 @@
package com.mcwl.web.controller.communityCenter; //package com.mcwl.web.controller.communityCenter;
//
//
import com.mcwl.communityCenter.service.AIService; //import com.mcwl.communityCenter.service.AIService;
import com.mcwl.communityCenter.webSocket.ChatWebSocket; //import com.mcwl.communityCenter.webSocket.ChatWebSocket;
import lombok.RequiredArgsConstructor; //import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping; //import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; //import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; //import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux; //import reactor.core.publisher.Flux;
//
@RequiredArgsConstructor //@RequiredArgsConstructor
@RestController //@RestController
@RequestMapping("/chat") //@RequestMapping("/chat")
public class ChatController { //public class ChatController {
//
private final AIService aiService; // private final AIService aiService;
//
//
/** // /**
* ai // * ai
*/ // */
@GetMapping("/ai") // @GetMapping("/ai")
public Flux<String> switchUserMode(String msg) throws Exception { // public Flux<String> switchUserMode(String msg) throws Exception {
return aiService.getDeepSeekResponseStream(msg); // return aiService.getDeepSeekResponseStream(msg);
} // }
//
//
//
} //}

View File

@ -20,10 +20,7 @@ import org.springframework.web.bind.annotation.*;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static com.mcwl.common.core.domain.AjaxResult.success; import static com.mcwl.common.core.domain.AjaxResult.success;
@ -113,6 +110,9 @@ public class InvitationController {
public R<Double> totalAmount() { public R<Double> totalAmount() {
Long userId = SecurityUtils.getUserId(); Long userId = SecurityUtils.getUserId();
Double totalAmount = invitationService.getTotalAmount(userId); Double totalAmount = invitationService.getTotalAmount(userId);
if (Objects.isNull(totalAmount)) {
totalAmount = 0.0;
}
return R.ok(totalAmount); return R.ok(totalAmount);
} }

View File

@ -38,17 +38,17 @@
<!-- <artifactId>dashscope-sdk-java</artifactId>--> <!-- <artifactId>dashscope-sdk-java</artifactId>-->
<!-- <version>2.18.3</version>--> <!-- <version>2.18.3</version>-->
<!-- </dependency>--> <!-- </dependency>-->
<dependency> <!-- <dependency>-->
<groupId>com.alibaba.cloud.ai</groupId> <!-- <groupId>com.alibaba.cloud.ai</groupId>-->
<artifactId>spring-ai-alibaba-starter</artifactId> <!-- <artifactId>spring-ai-alibaba-starter</artifactId>-->
<version>1.0.0-M5.1</version> <!-- <version>1.0.0-M5.1</version>-->
</dependency> <!-- </dependency>-->
<dependency> <!-- <dependency>-->
<groupId>io.projectreactor.netty</groupId> <!-- <groupId>io.projectreactor.netty</groupId>-->
<artifactId>reactor-netty-http</artifactId> <!-- <artifactId>reactor-netty-http</artifactId>-->
<version>1.1.6</version> <!-- 使用与你的 Spring Boot 版本兼容的版本 --> <!-- <version>1.1.6</version> &lt;!&ndash; 使用与你的 Spring Boot 版本兼容的版本 &ndash;&gt;-->
</dependency> <!-- </dependency>-->
</dependencies> </dependencies>

View File

@ -1,32 +1,32 @@
package com.mcwl.communityCenter.config; //package com.mcwl.communityCenter.config;
//
import com.mcwl.communityCenter.webSocket.ChatWebSocket; //import com.mcwl.communityCenter.webSocket.ChatWebSocket;
import com.mcwl.communityCenter.webSocket.HumanWebSocket; //import com.mcwl.communityCenter.webSocket.HumanWebSocket;
import org.springframework.context.annotation.Bean; //import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; //import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket; //import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer; //import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; //import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
//
@Configuration //@Configuration
@EnableWebSocket //@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer { //public class WebSocketConfig implements WebSocketConfigurer {
//
@Override // @Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { // public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatWebSocket(), "/chat") // registry.addHandler(chatWebSocket(), "/chat")
.addHandler(humanWebSocket(), "/chat/human") // .addHandler(humanWebSocket(), "/chat/human")
.setAllowedOrigins("*"); // .setAllowedOrigins("*");
} // }
//
@Bean // @Bean
public ChatWebSocket chatWebSocket() { // public ChatWebSocket chatWebSocket() {
return new ChatWebSocket(); // return new ChatWebSocket();
} // }
//
@Bean // @Bean
public HumanWebSocket humanWebSocket() { // public HumanWebSocket humanWebSocket() {
return new HumanWebSocket(); // return new HumanWebSocket();
} // }
//
} //}

View File

@ -1,84 +1,84 @@
package com.mcwl.communityCenter.service.impl; //package com.mcwl.communityCenter.service.impl;
//
import com.fasterxml.jackson.core.JsonProcessingException; //import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode; //import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; //import com.fasterxml.jackson.databind.ObjectMapper;
import com.mcwl.common.utils.StringUtils; //import com.mcwl.common.utils.StringUtils;
import com.mcwl.communityCenter.domain.DeepSeekRequest; //import com.mcwl.communityCenter.domain.DeepSeekRequest;
import com.mcwl.communityCenter.service.AIService; //import com.mcwl.communityCenter.service.AIService;
import lombok.RequiredArgsConstructor; //import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value; //import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType; //import org.springframework.http.MediaType;
import org.springframework.stereotype.Service; //import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient; //import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux; //import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; //import reactor.core.publisher.Mono;
import reactor.core.publisher.SignalType; //import reactor.core.publisher.SignalType;
//
import java.util.ArrayList; //import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList; //import java.util.concurrent.CopyOnWriteArrayList;
//
@Service //@Service
@RequiredArgsConstructor //@RequiredArgsConstructor
public class AIServiceImpl implements AIService { //public class AIServiceImpl implements AIService {
//
@Value("${spring.ai.dashscope.base-url}") // @Value("${spring.ai.dashscope.base-url}")
private String DEEPSEEK_API_URL; // private String DEEPSEEK_API_URL;
@Value("${spring.ai.dashscope.api-key}") // @Value("${spring.ai.dashscope.api-key}")
private String API_KEY; // private String API_KEY;
@Value("${spring.ai.dashscope.chat.options.model}") // @Value("${spring.ai.dashscope.chat.options.model}")
private String apiModel; // private String apiModel;
//
//
private final ObjectMapper objectMapper; // private final ObjectMapper objectMapper;
//
@Override // @Override
public Flux<String> getDeepSeekResponseStream(String message) { // public Flux<String> getDeepSeekResponseStream(String message) {
WebClient client = WebClient.builder() // WebClient client = WebClient.builder()
.baseUrl(DEEPSEEK_API_URL) // .baseUrl(DEEPSEEK_API_URL)
.defaultHeader("Authorization", "Bearer " + API_KEY) // .defaultHeader("Authorization", "Bearer " + API_KEY)
.build(); // .build();
//
// 构建请求体(推荐使用对象映射) // // 构建请求体(推荐使用对象映射)
DeepSeekRequest request = new DeepSeekRequest(); // DeepSeekRequest request = new DeepSeekRequest();
request.setModel(apiModel); // request.setModel(apiModel);
// 添加对话历史 // // 添加对话历史
request.addMessage("user", message); // request.addMessage("user", message);
request.setMaxTokens(500); // request.setMaxTokens(500);
request.setTemperature(0.7); // request.setTemperature(0.7);
request.setStream(true); // request.setStream(true);
//
return client.post() // return client.post()
.contentType(MediaType.APPLICATION_JSON) // .contentType(MediaType.APPLICATION_JSON)
.bodyValue(request) // .bodyValue(request)
.retrieve() // .retrieve()
.bodyToFlux(String.class) // 原始数据流 // .bodyToFlux(String.class) // 原始数据流
.takeUntil(data -> data.contains("[DONE]")) // 遇到结束标记停止 // .takeUntil(data -> data.contains("[DONE]")) // 遇到结束标记停止
.flatMap(json -> parseContentFromJson(json)) // 解析内容 // .flatMap(json -> parseContentFromJson(json)) // 解析内容
.onErrorResume(e -> Flux.just(""));// 错误处理 // .onErrorResume(e -> Flux.just(""));// 错误处理
//
} // }
//
//
// 辅助方法:从 JSON 中提取 content // // 辅助方法:从 JSON 中提取 content
private Mono<String> parseContentFromJson(String json) { // private Mono<String> parseContentFromJson(String json) {
try { // try {
JsonNode root = objectMapper.readTree(json); // JsonNode root = objectMapper.readTree(json);
String reasoning_content = root.path("choices") // String reasoning_content = root.path("choices")
.get(0) // .get(0)
.path("delta") // .path("delta")
.path("reasoning_content") // .path("reasoning_content")
.asText(""); // .asText("");
String content = root.path("choices") // String content = root.path("choices")
.get(0) // .get(0)
.path("delta") // .path("delta")
.path("content") // .path("content")
.asText(""); // .asText("");
System.out.print(StringUtils.isNotEmpty(reasoning_content) ? reasoning_content : content); // System.out.print(StringUtils.isNotEmpty(reasoning_content) ? reasoning_content : content);
return Mono.just(StringUtils.isNotEmpty(reasoning_content) ? reasoning_content : content); // return Mono.just(StringUtils.isNotEmpty(reasoning_content) ? reasoning_content : content);
} catch (JsonProcessingException e) { // } catch (JsonProcessingException e) {
return Mono.error(e); // return Mono.error(e);
} // }
} // }
//
} //}

View File

@ -1,82 +1,82 @@
package com.mcwl.communityCenter.webSocket; //package com.mcwl.communityCenter.webSocket;
//
import com.mcwl.communityCenter.service.AIService; //import com.mcwl.communityCenter.service.AIService;
import com.mcwl.communityCenter.service.HumanService; //import com.mcwl.communityCenter.service.HumanService;
import lombok.NoArgsConstructor; //import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired; //import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.socket.CloseStatus; //import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage; //import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession; //import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler; //import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import reactor.core.Disposable; //import reactor.core.Disposable;
import reactor.core.publisher.Flux; //import reactor.core.publisher.Flux;
//
import javax.websocket.server.ServerEndpoint; //import javax.websocket.server.ServerEndpoint;
import java.io.IOException; //import java.io.IOException;
import java.util.Map; //import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; //import java.util.concurrent.ConcurrentHashMap;
//
@ServerEndpoint("/chat") //@ServerEndpoint("/chat")
@NoArgsConstructor //@NoArgsConstructor
public class ChatWebSocket extends AbstractWebSocketHandler { //public class ChatWebSocket extends AbstractWebSocketHandler {
private final Map<String, Boolean> userModes = new ConcurrentHashMap<>(); // private final Map<String, Boolean> userModes = new ConcurrentHashMap<>();
//
// 存储会话与订阅的映射关系 // // 存储会话与订阅的映射关系
private final Map<String, Disposable> sessionSubscriptions = new ConcurrentHashMap<>(); // private final Map<String, Disposable> sessionSubscriptions = new ConcurrentHashMap<>();
//
@Autowired // @Autowired
private AIService aiService; // private AIService aiService;
//
// 构造函数注入服务... // // 构造函数注入服务...
//
@Override // @Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String userId = session.getId(); // String userId = session.getId();
String userMessage = message.getPayload(); // String userMessage = message.getPayload();
//
// AI 流式响应模式 // // AI 流式响应模式
Flux<String> responseStream = aiService.getDeepSeekResponseStream(userMessage); // Flux<String> responseStream = aiService.getDeepSeekResponseStream(userMessage);
//
// 订阅响应流并存储 Disposable // // 订阅响应流并存储 Disposable
Disposable disposable = responseStream // Disposable disposable = responseStream
.doOnNext(chunk -> sendText(session, chunk)) // 发送每个数据块到客户端 // .doOnNext(chunk -> sendText(session, chunk)) // 发送每个数据块到客户端
.doOnComplete(() -> sendText(session, "[END]")) // 当流处理完成时,发送结束标记 // .doOnComplete(() -> sendText(session, "[END]")) // 当流处理完成时,发送结束标记
.doOnError(e -> sendText(session, "[ERROR] " + e.getMessage())) // .doOnError(e -> sendText(session, "[ERROR] " + e.getMessage()))
.subscribe(); // .subscribe();
//
sessionSubscriptions.put(userId, disposable); // sessionSubscriptions.put(userId, disposable);
//
} // }
//
@Override // @Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception { // public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session); // super.afterConnectionEstablished(session);
// userModes.put(session.getId(), false); //// userModes.put(session.getId(), false);
session.sendMessage(new TextMessage("[AI] 您好,请问有什么问题?")); // session.sendMessage(new TextMessage("[AI] 您好,请问有什么问题?"));
} // }
//
@Override // @Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { // public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
// 清理订阅资源 // // 清理订阅资源
String sessionId = session.getId(); // String sessionId = session.getId();
Disposable disposable = sessionSubscriptions.remove(sessionId); // Disposable disposable = sessionSubscriptions.remove(sessionId);
if (disposable != null && disposable.isDisposed()) { // if (disposable != null && disposable.isDisposed()) {
disposable.dispose(); // disposable.dispose();
} // }
} // }
//
//
//
// 线程安全的发送方法 // // 线程安全的发送方法
private void sendText(WebSocketSession session, String text) { // private void sendText(WebSocketSession session, String text) {
try { // try {
if (session.isOpen()) { // if (session.isOpen()) {
synchronized (session) { // WebSocketSession 非线程安全 // synchronized (session) { // WebSocketSession 非线程安全
session.sendMessage(new TextMessage(text)); // session.sendMessage(new TextMessage(text));
} // }
} // }
} catch (IOException e) { // } catch (IOException e) {
System.out.println("WebSocket 发送失败: " + e.getMessage()); // System.out.println("WebSocket 发送失败: " + e.getMessage());
} // }
} // }
} //}

View File

@ -1,55 +1,55 @@
package com.mcwl.communityCenter.webSocket; //package com.mcwl.communityCenter.webSocket;
//
import com.mcwl.common.utils.SecurityUtils; //import com.mcwl.common.utils.SecurityUtils;
import com.mcwl.communityCenter.service.HumanService; //import com.mcwl.communityCenter.service.HumanService;
import lombok.NoArgsConstructor; //import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired; //import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.socket.CloseStatus; //import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage; //import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession; //import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler; //import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import reactor.core.Disposable; //import reactor.core.Disposable;
//
import javax.websocket.server.ServerEndpoint; //import javax.websocket.server.ServerEndpoint;
import java.io.IOException; //import java.io.IOException;
import java.util.Map; //import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; //import java.util.concurrent.ConcurrentHashMap;
//
@ServerEndpoint("/chat/human") //@ServerEndpoint("/chat/human")
@NoArgsConstructor //@NoArgsConstructor
public class HumanWebSocket extends AbstractWebSocketHandler { //public class HumanWebSocket extends AbstractWebSocketHandler {
//
@Autowired // @Autowired
private HumanService humanService; // private HumanService humanService;
//
// 构造函数注入服务... // // 构造函数注入服务...
//
@Override // @Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String userId = session.getId(); // String userId = session.getId();
String userMessage = message.getPayload(); // String userMessage = message.getPayload();
//
humanService.handleHumanMessage(userId, userMessage); // humanService.handleHumanMessage(userId, userMessage);
} // }
//
@Override // @Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception { // public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session); // super.afterConnectionEstablished(session);
humanService.transferToHuman(SecurityUtils.getUserId().toString(), session); // humanService.transferToHuman(SecurityUtils.getUserId().toString(), session);
System.out.println("客服:" + SecurityUtils.getUsername() + " 已上线"); // System.out.println("客服:" + SecurityUtils.getUsername() + " 已上线");
} // }
//
//
// 线程安全的发送方法 // // 线程安全的发送方法
private void sendText(WebSocketSession session, String text) { // private void sendText(WebSocketSession session, String text) {
try { // try {
if (session.isOpen()) { // if (session.isOpen()) {
synchronized (session) { // WebSocketSession 非线程安全 // synchronized (session) { // WebSocketSession 非线程安全
session.sendMessage(new TextMessage(text)); // session.sendMessage(new TextMessage(text));
} // }
} // }
} catch (IOException e) { // } catch (IOException e) {
System.out.println("WebSocket 发送失败: " + e.getMessage()); // System.out.println("WebSocket 发送失败: " + e.getMessage());
} // }
} // }
} //}

View File

@ -1,6 +1,8 @@
package com.mcwl.memberCenter.task; package com.mcwl.memberCenter.task;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mcwl.common.constant.QueueConstants; import com.mcwl.common.constant.QueueConstants;
import com.mcwl.common.core.domain.entity.SysUser; import com.mcwl.common.core.domain.entity.SysUser;
import com.mcwl.memberCenter.domain.*; import com.mcwl.memberCenter.domain.*;
@ -131,16 +133,38 @@ public class UserMemberTask {
/** /**
* 0 0 0 0 * * ? * 0 0 0 0 * * ?
*/ */
@Transactional(rollbackFor = Exception.class)
public void checkPromotionExpiredTask() { public void checkPromotionExpiredTask() {
int pageNum = 1;
int pageSize = 1000;
// 根据活动结束日期小于当前时间获取活动id
List<Long> promotionIdList = new ArrayList<>(); List<Long> promotionIdList = new ArrayList<>();
List<Promotion> promotionList = promotionService.lambdaQuery()
while (true) {
Page<Promotion> page = new Page<>(pageNum, pageSize);
promotionService.lambdaQuery()
.lt(Promotion::getEndTime, new Date()) // 活动结束日期小于当前时间 .lt(Promotion::getEndTime, new Date()) // 活动结束日期小于当前时间
.list(); .page(page);
List<Promotion> promotionList = page.getRecords();
if (promotionList.isEmpty()) {
break;
}
for (Promotion promotion : promotionList) { for (Promotion promotion : promotionList) {
promotionIdList.add(promotion.getId()); promotionIdList.add(promotion.getId());
} }
memberPromotionService.lambdaUpdate()
.set(MemberPromotion::getStatus, PromotionEnum.EXPIRED)
.eq(MemberPromotion::getStatus, PromotionEnum.PARTICIPATE)
.in(MemberPromotion::getPromotionId, promotionIdList)
.update();
pageNum++;
promotionIdList.clear();
}
} }