Merge branch 'dev/dev_xp' into dev/dev

This commit is contained in:
2024-09-06 09:32:40 +08:00
44 changed files with 1703 additions and 195 deletions

View File

@@ -59,4 +59,10 @@ public class CommonConstant {
public static final List<String> IS_SUBSCRIBE = Arrays.asList("yes", "no");
public static final String DEFAULT_AVATAR = "aida-users/87/avatar/default.jpg";
/* 截止至2024/08/26在Code-Create DB中pmr_users表中最大的用户id */
public static final Long MAXIMUM_USER_ID = 704L;
// public static final Long MAXIMUM_USER_ID = 225L;
}

View File

@@ -1,133 +1,134 @@
package com.ai.da.common.security.filter;
import cn.hutool.core.util.StrUtil;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.security.config.SecurityProperties;
import com.ai.da.common.security.jwt.JWTTokenHelper;
import com.ai.da.common.utils.LocalCacheUtils;
import com.ai.da.common.utils.MultiReadHttpServletRequest;
import com.ai.da.common.utils.MultiReadHttpServletResponse;
import com.ai.da.common.utils.RequestInfoUtil;
import com.ai.da.model.vo.AuthPrincipalVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.security.sasl.AuthenticationException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* @author: dangweijian
* @description: 认证拦截器
* @create: 2020-07-10 16:50
**/
@Slf4j
@Configuration
public class AuthenticationFilter extends OncePerRequestFilter {
@Resource
private JWTTokenHelper jwtTokenHelper;
@Resource
private SecurityProperties properties;
private static final List<String> FILTER_URL =
Arrays.asList("/favicon.ico", "/doc.html", "api/account/login", "api/account/preLogin", "api/account/sendEmail","api/account/noLoginRequired",
"/webjars/", "/swagger-resources", "/v2/api-docs", "api/account/resetPwd",
"/api/python/saveGeneratePicture", "/api/python/getLibraryByUserId",
"/api/third/party/addUser","/api/third/party/addTrialUser", "/api/third/party/editUser", "/api/element/initDefaultSysFile",
"/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew",
"/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
"/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify"
);
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain) throws ServletException, IOException {
String requestURI = httpServletRequest.getRequestURI();
if (calculateUrl(requestURI) || hasAuthorizationToken(httpServletRequest)) {
StopWatch stopWatch = new StopWatch();
HttpServletRequest wrappedRequest = httpServletRequest;
HttpServletResponse wrappedResponse = httpServletResponse;
try {
stopWatch.start();
if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
extracted(wrappedRequest);
filterChain.doFilter(wrappedRequest, wrappedResponse);
} else {
wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
extracted(wrappedRequest);
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
} catch (Exception e) {
SecurityContextHolder.clearContext();
throw e;
} finally {
stopWatch.stop();
}
} else {
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
private Boolean calculateUrl(String requestURI) {
String filterUrl = FILTER_URL.stream().filter(url -> requestURI.contains(url)).findFirst().orElse(null);
return null == filterUrl ? Boolean.TRUE : Boolean.FALSE;
}
private boolean hasAuthorizationToken(HttpServletRequest request) {
String authorizationHeader = request.getHeader("Authorization");
return authorizationHeader != null && authorizationHeader.startsWith("Bearer");
}
private void extracted(HttpServletRequest request) throws AuthenticationException {
String jwtToken = request.getHeader(properties.getJwtTokenHeader());
// log.debug("后台检查令牌:{}", jwtToken);
if (StrUtil.isBlank(jwtToken)) {
String ipAddress = RequestInfoUtil.getIpAddress(request);
log.info("本次请求的ip为 " + ipAddress);
throw new RuntimeException("请传入token");
}
if(jwtToken.equals("Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA")){
//写死 暂时放行
return;
}
// 检查token
boolean validate = jwtTokenHelper.validateToken(jwtToken);
if (validate) {
AuthPrincipalVo principal = jwtTokenHelper.parserToUser(jwtToken);
if (principal == null) {
throw new RuntimeException("TOKEN已过期请重新登录");
}
//先清空当前线程变量,防止上一个线程遗留
UserContext.delete();
//存取用户信息到缓存
UserContext.setUserHolder(principal);
//校验token
String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId()));
if(StringUtils.isEmpty(cacheToken)){
throw new RuntimeException("TOKEN已过期请重新登录");
}
if(!cacheToken.equals(jwtToken) ){
throw new RuntimeException("TOKEN已过期请重新登录");
}
// UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null);
// SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
package com.ai.da.common.security.filter;
import cn.hutool.core.util.StrUtil;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.security.config.SecurityProperties;
import com.ai.da.common.security.jwt.JWTTokenHelper;
import com.ai.da.common.utils.LocalCacheUtils;
import com.ai.da.common.utils.MultiReadHttpServletRequest;
import com.ai.da.common.utils.MultiReadHttpServletResponse;
import com.ai.da.common.utils.RequestInfoUtil;
import com.ai.da.model.vo.AuthPrincipalVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.security.sasl.AuthenticationException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* @author: dangweijian
* @description: 认证拦截器
* @create: 2020-07-10 16:50
**/
@Slf4j
@Configuration
public class AuthenticationFilter extends OncePerRequestFilter {
@Resource
private JWTTokenHelper jwtTokenHelper;
@Resource
private SecurityProperties properties;
private static final List<String> FILTER_URL =
Arrays.asList("/favicon.ico", "/doc.html", "api/account/login", "api/account/preLogin", "api/account/sendEmail","api/account/noLoginRequired",
"/webjars/", "/swagger-resources", "/v2/api-docs", "api/account/resetPwd",
"/api/python/saveGeneratePicture", "/api/python/getLibraryByUserId",
"/api/third/party/addUser","/api/third/party/addTrialUser", "/api/third/party/editUser", "/api/element/initDefaultSysFile",
"/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew",
"/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
"/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
"/notification"
);
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain) throws ServletException, IOException {
String requestURI = httpServletRequest.getRequestURI();
if (calculateUrl(requestURI) || hasAuthorizationToken(httpServletRequest)) {
StopWatch stopWatch = new StopWatch();
HttpServletRequest wrappedRequest = httpServletRequest;
HttpServletResponse wrappedResponse = httpServletResponse;
try {
stopWatch.start();
if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
extracted(wrappedRequest);
filterChain.doFilter(wrappedRequest, wrappedResponse);
} else {
wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
extracted(wrappedRequest);
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
} catch (Exception e) {
SecurityContextHolder.clearContext();
throw e;
} finally {
stopWatch.stop();
}
} else {
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
private Boolean calculateUrl(String requestURI) {
String filterUrl = FILTER_URL.stream().filter(url -> requestURI.contains(url)).findFirst().orElse(null);
return null == filterUrl ? Boolean.TRUE : Boolean.FALSE;
}
private boolean hasAuthorizationToken(HttpServletRequest request) {
String authorizationHeader = request.getHeader("Authorization");
return authorizationHeader != null && authorizationHeader.startsWith("Bearer");
}
private void extracted(HttpServletRequest request) throws AuthenticationException {
String jwtToken = request.getHeader(properties.getJwtTokenHeader());
// log.debug("后台检查令牌:{}", jwtToken);
if (StrUtil.isBlank(jwtToken)) {
String ipAddress = RequestInfoUtil.getIpAddress(request);
log.info("本次请求的ip为 " + ipAddress);
throw new RuntimeException("请传入token");
}
if(jwtToken.equals("Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA")){
//写死 暂时放行
return;
}
// 检查token
boolean validate = jwtTokenHelper.validateToken(jwtToken);
if (validate) {
AuthPrincipalVo principal = jwtTokenHelper.parserToUser(jwtToken);
if (principal == null) {
throw new RuntimeException("TOKEN已过期请重新登录");
}
//先清空当前线程变量,防止上一个线程遗留
UserContext.delete();
//存取用户信息到缓存
UserContext.setUserHolder(principal);
//校验token
String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId()));
if(StringUtils.isEmpty(cacheToken)){
throw new RuntimeException("TOKEN已过期请重新登录");
}
if(!cacheToken.equals(jwtToken) ){
throw new RuntimeException("TOKEN已过期请重新登录");
}
// UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null);
// SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}

View File

@@ -57,4 +57,12 @@ public class AccountTask {
accountService.toVisitor(account);
}
}
/**
* 将Code-Create上注册的用户添加为AiDA的游客
*/
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
public void registerUserToVisitor(){
accountService.registerUserToVisitor();
}
}

View File

@@ -216,4 +216,17 @@ public class RedisUtil {
return redisTemplate.opsForValue().increment(key, 0);
}
public final static String PERSONAL_HOMEPAGE_VIEW_KEY = "PersonalHomepage:view:";
public void increasePersonalHomepageViewCount(Long accountId) {
String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId;
redisTemplate.opsForValue().increment(key);
}
public Long getPersonalHomepageViewCount(Long accountId) {
String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId;
return redisTemplate.opsForValue().increment(key, 0);
}
}

View File

@@ -590,4 +590,40 @@ public class SendEmailUtil {
throw new BusinessException("failed.to.send.mail");
}
}
private final static Long NEW_USER_REGISTER_NOTIFICATION_EN = 126919L;
public static void notificationForRegisterUser(String receiverAddress){
try{
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey此处还需注意密钥对的保密
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露并威胁账号下所有资源的安全性。以下代码示例仅供参考建议采用更安全的方式来使用密钥请参见https://cloud.tencent.com/document/product/1278/85305
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
Credential cred = new Credential(SECRET_ID, SECRET_KEy);
// 实例化一个http选项可选的没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("ses.tencentcloudapi.com");
// 实例化一个client选项可选的没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
SesClient client = new SesClient(cred, "ap-hongkong", clientProfile);
// 实例化一个请求对象,每个接口都会对应一个request对象
SendEmailRequest req = new SendEmailRequest();
req.setFromEmailAddress(SEND_ADDRESS);
req.setDestination(new String[]{receiverAddress});
req.setSubject("Welcome to AiDa");
Template template = new Template();
template.setTemplateID(NEW_USER_REGISTER_NOTIFICATION_EN);
template.setTemplateData("{\"email\":\" " + receiverAddress + "\"}");
req.setTemplate(template);
// 返回的resp是一个SendEmailResponse的实例与请求对象对应
SendEmailResponse resp = client.SendEmail(req);
log.info("短信发送结果res###{}", SendEmailResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
log.info("邮件发送失败###{}", e.toString());
throw new BusinessException("failed.to.send.mail");
}
}
}

View File

@@ -0,0 +1,43 @@
package com.ai.da.common.websocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint(value = "/notification")
@Component
@Slf4j
public class NotificationConnection {
static Map<String,Session> sessionMap = new ConcurrentHashMap<>();
//连接建立时执行的操作
@OnOpen
public void onOpen(Session session){
sessionMap.put(session.getId(),session);
log.info("websocket is open");
}
//收到了客户端消息执行的操作
@OnMessage
public void onMessage(String text){
log.info("收到了一条消息:"+text);
// return "已收到你的消息";
}
//连接关闭的时候执行的操作
@OnClose
public void onClose(Session session){
sessionMap.remove(session.getId());
log.info("websocket is close");
}
public void sendMsg(String message) throws IOException {
for(String key:sessionMap.keySet()){
sessionMap.get(key).getBasicRemote().sendText(message);
}
}
}

View File

@@ -0,0 +1,18 @@
package com.ai.da.common.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* Configuration of WebSocket
*
* @author db1995
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

View File

@@ -1,18 +1,21 @@
package com.ai.da.controller;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.Response;
import com.ai.da.mapper.primary.entity.Account;
import com.ai.da.mapper.primary.entity.TrialOrder;
import com.ai.da.model.dto.*;
import com.ai.da.model.vo.AccountLoginVO;
import com.ai.da.model.vo.AccountPreLoginVO;
import com.ai.da.model.vo.PersonalHomepageVO;
import com.ai.da.service.AccountService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@@ -194,4 +197,26 @@ public class AccountController {
public Response<List<Long>> setUserValidToDayEnd(){
return Response.success(accountService.setUserValidToDayEnd());
}
// 用户上传头像
@ApiOperation(value = "上传头像")
@PostMapping(path = "/uploadAvatar")
public Response<String> uploadAvatar(@RequestParam("file") MultipartFile file) {
if (null == file || StringUtils.isEmpty(file.getOriginalFilename())) {
throw new BusinessException("file.cannot.be.empty");
}
return Response.success(accountService.uploadAvatar(file));
}
@ApiOperation(value = "个人主页浏览量增加")
@GetMapping("/viewsIncrease")
public Response<Boolean> viewsGet(@RequestParam("id") Long id) {
return Response.success(accountService.viewsIncrease(id));
}
@ApiOperation(value = "获取个人主页信息")
@GetMapping("/personalHomepage")
public Response<PersonalHomepageVO> getPersonalHomepage(@RequestParam("id") Long id){
return Response.success(accountService.getPersonalHomepage(id));
}
}

View File

@@ -51,7 +51,8 @@ public class ConvenientInquiryController {
@ApiOperation("获取指定时间区间内所有用户design的使用情况")
@GetMapping("/getDesignStatistic")
public Response<List<UserDesignStatisticDTO>> getDesignStatistic(@RequestParam String startTime, @RequestParam String endTime) {
public Response<List<UserDesignStatisticDTO>> getDesignStatistic(@RequestParam String startTime, @RequestParam String endTime,
@RequestParam(required = false) List<Long> ids, @RequestParam(required = false) String email) {
Long accountId = UserContext.getUserHolder().getId();
if (accountId.equals(31L) || accountId.equals(87L) || accountId.equals(83L) || accountId.equals(6L) || accountId.equals(4L) || accountId.equals(73L)) {
if (StringUtil.isNullOrEmpty(startTime)) startTime = "2024-02-01 00:00:00";
@@ -60,7 +61,10 @@ public class ConvenientInquiryController {
Date date = new Date();
endTime = simpleDateFormat.format(date);
}
List<UserDesignStatisticDTO> designStatistic = designMapper.getDesignStatistic(startTime, endTime);
if (!StringUtil.isNullOrEmpty(email)){
email = email.trim();
}
List<UserDesignStatisticDTO> designStatistic = designMapper.getDesignStatistic(startTime, endTime, ids, email);
return Response.success(designStatistic);
} else {
return Response.fail("Sorry, you don't have permission");

View File

@@ -0,0 +1,63 @@
package com.ai.da.controller;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.Response;
import com.ai.da.model.dto.GetNotificationDTO;
import com.ai.da.model.vo.NotificationVO;
import com.ai.da.model.dto.PublishSysNotificationDTO;
import com.ai.da.service.MessageCenterService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
@Api(tags = "消息中心模块")
@Slf4j
@RestController
@RequestMapping("/api/message")
public class MessageCenterController {
@Resource
private MessageCenterService messageCenterService;
// 获取未读消息总数
@ApiOperation(value = "获取未读消息数")
@GetMapping("/getUnreadCount")
public Response<Map<String, Long>> getUnreadMessage(){
return Response.success(messageCenterService.getAllTypeMessageUnreadCount());
}
// 获取历史消息
@ApiOperation(value = "获取历史消息")
@PostMapping("/getHistoryNotification")
public Response<PageBaseResponse<NotificationVO>> getHistoryNotification(@Valid @RequestBody GetNotificationDTO getNotificationDTO) {
return Response.success(messageCenterService.getHistoryNotification(getNotificationDTO));
}
// 已读消息
@ApiOperation(value = "设置消息状态为已读")
@PostMapping("/setReadStatus")
public Response<Boolean> setReadStatus(@RequestParam("notificationIdList") List<Long> notificationIdList, @RequestParam("type") String type) {
return Response.success(messageCenterService.setReadStatus(notificationIdList, type));
}
// 发布系统消息
@ApiOperation(value = "发布系统消息")
@PostMapping("/publishSysMessage")
public Response<String> publishSysMessage(@Valid @RequestBody PublishSysNotificationDTO message) {
messageCenterService.publishSystemNotification(message);
return Response.success("success");
}
@ApiOperation(value = "一键已读")
@PostMapping("/oneClickRead")
public Response<String> setReadAll(@RequestParam("type") String type) {
messageCenterService.setReadAll(type);
return Response.success("success");
}
}

View File

@@ -1,12 +1,11 @@
package com.ai.da.controller;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.Response;
import com.ai.da.mapper.primary.entity.Account;
import com.ai.da.model.dto.*;
import com.ai.da.model.vo.CommentVO;
import com.ai.da.model.vo.PortfolioVO;
import com.ai.da.model.vo.UserLikeChooseVO;
import com.ai.da.model.vo.UserLikeGroupVO;
import com.ai.da.model.vo.*;
import com.ai.da.service.PortfolioService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -16,6 +15,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
@Api(tags = "Portfolio模块")
@Slf4j
@@ -117,4 +117,31 @@ public class PortfolioController {
public Response<Boolean> commentDelete(@Valid @RequestBody CommentDTO commentDTO) {
return Response.success(portfolioService.commentDelete(commentDTO));
}
@ApiOperation(value = "关注")
@GetMapping("/follow")
public Response<String> follow(@RequestParam("followeeId") Long followeeId) {
portfolioService.follow(followeeId);
return Response.success(BusinessException.getMessageFromResource("subscription.success"));
}
@ApiOperation(value = "取消关注")
@GetMapping("/cancelFollow")
public Response<String> cancelFollow(@RequestParam("followeeId") Long followeeId) {
portfolioService.cancelFollow(followeeId);
return Response.success(BusinessException.getMessageFromResource("unsubscribe.success"));
}
@ApiOperation(value = "获取关注列表")
@PostMapping("/getFolloweeList")
public Response<List<AccountFollowVO>> getFolloweeList(@Valid @RequestBody GetFollowListDTO getFollowListDTO) {
return Response.success(portfolioService.getFolloweeList(getFollowListDTO));
}
@ApiOperation(value = "获取粉丝列表")
@PostMapping("/getFollowerList")
public Response<List<AccountFollowVO>> getFollowerList(@Valid @RequestBody GetFollowListDTO getFollowListDTO) {
return Response.success(portfolioService.getFollowerList(getFollowListDTO));
}
}

View File

@@ -18,5 +18,5 @@ public interface DesignMapper extends CommonMapper<Design> {
//返回插入数据后生成的主键
Long insertDesign(Design design);
List<UserDesignStatisticDTO> getDesignStatistic(String startTime, String endTime);
List<UserDesignStatisticDTO> getDesignStatistic(String startTime, String endTime, List<Long> ids, String email);
}

View File

@@ -0,0 +1,24 @@
package com.ai.da.mapper.primary;
import com.ai.da.common.config.mybatis.plus.CommonMapper;
import com.ai.da.mapper.primary.entity.Notification;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
public interface NotificationMapper extends CommonMapper<Notification> {
List<Map<String, Object>> getTypeCount(Long receiverId);
/** 解决mybatis-plus自动过滤 is_deleted为1的数据 问题 */
Notification getUniqueLikeAndFollow(String type, Long senderId, Long receiverId, Long portfolioId);
void updateUniqueLikeAndFollow(Long id, LocalDateTime time);
void deleteNotification(Long id, LocalDateTime time);
void setPersonalNotificationAllRead(String type, Long receiverId, LocalDateTime time);
List<Long> getUnreadSysNotification(Long accountId);
}

View File

@@ -0,0 +1,7 @@
package com.ai.da.mapper.primary;
import com.ai.da.common.config.mybatis.plus.CommonMapper;
import com.ai.da.mapper.primary.entity.SysNotificationReadStatus;
public interface SysNotificationReadStatusMapper extends CommonMapper<SysNotificationReadStatus> {
}

View File

@@ -0,0 +1,19 @@
package com.ai.da.mapper.primary;
import com.ai.da.common.config.mybatis.plus.CommonMapper;
import com.ai.da.mapper.primary.entity.UserFollow;
import com.ai.da.model.vo.AccountFollowVO;
import java.util.List;
public interface UserFollowMapper extends CommonMapper<UserFollow> {
List<AccountFollowVO> getFolloweeListByFollower(Long followerAccountId, Integer limit, Integer offset, String order);
List<AccountFollowVO> getFollowerListByFollowee(Long followeeAccountId, Integer limit, Integer offset, String order);
List<AccountFollowVO> getFolloweeListByName(String name, Long followerId);
List<AccountFollowVO> getFollowerListByName(String name, Long followeeId);
}

View File

@@ -99,4 +99,9 @@ public class Account implements Serializable {
* 4 参加活动获取30天有效期和6000个积分的用户
*/
private Integer systemUser;
/**
* 头像
*/
private String avatar;
}

View File

@@ -0,0 +1,73 @@
package com.ai.da.mapper.primary.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@TableName("t_notification")
@Data
public class Notification extends BaseEntity{
/**
* 操作类型 system/like/comment/follow
*/
private String type;
/**
* 发起操作者用户id
*/
private Long senderId;
/**
* 被操作对象用户id
*/
private Long receiverId;
/**
* 点赞评论时的作品id
*/
private Long portfolioId;
/**
* 消息内容
*/
private String content;
/**
* 评论id
*/
private Long commentId;
/**
* 个人消息已读状态
*/
private Integer isRead;
/**
* 系统消息发布状态
*/
private Integer publishFlag;
/**
* 是否被删除
*/
private Integer isDeleted;
public Notification() {
}
public Notification(String type, Long senderId, Long receiverId) {
this.type = type;
this.senderId = senderId;
this.receiverId = receiverId;
}
public Notification(String type, Long senderId, Long receiverId, Long portfolioId) {
this.type = type;
this.senderId = senderId;
this.receiverId = receiverId;
this.portfolioId = portfolioId;
}
public Notification(String type, Long senderId, Long receiverId, Long portfolioId, String content, Long commentId) {
this.type = type;
this.senderId = senderId;
this.receiverId = receiverId;
this.portfolioId = portfolioId;
this.content = content;
this.commentId = commentId;
}
}

View File

@@ -0,0 +1,26 @@
package com.ai.da.mapper.primary.entity;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@EqualsAndHashCode(callSuper = true)
@TableName("t_sys_notification_read_status")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysNotificationReadStatus extends BaseEntity{
/**
* 系统消息id
*/
private Long system_notification_id;
/**
* 已读当前消息的用户id将被存储
*/
private Long account_id;
}

View File

@@ -0,0 +1,26 @@
package com.ai.da.mapper.primary.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@EqualsAndHashCode(callSuper = true)
@TableName("t_user_follow")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserFollow extends BaseEntity{
/**
* 被关注者用户id
*/
private Long followeeId;
/**
* 关注者用户id
*/
private Long followerId;
}

View File

@@ -0,0 +1,19 @@
package com.ai.da.model.dto;
import com.ai.da.model.vo.PageQueryBaseVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel("按条件分页查询关注列表")
public class GetFollowListDTO extends PageQueryBaseVo {
@ApiModelProperty("查找指定用户名")
private String searchByName;
@ApiModelProperty("按关注时间排序 DESC 降序 || ASC 升序")
private String order;
}

View File

@@ -0,0 +1,16 @@
package com.ai.da.model.dto;
import com.ai.da.model.vo.PageQueryBaseVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@ApiModel
@Data
public class GetNotificationDTO extends PageQueryBaseVo {
@ApiModelProperty("system/like/comment/follow/newPosted")
private String type;
}

View File

@@ -0,0 +1,19 @@
package com.ai.da.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("发布系统消息")
public class PublishSysNotificationDTO {
@ApiModelProperty("系统消息标题")
private String title;
@ApiModelProperty("系统消息内容")
private String content;
@ApiModelProperty("系统消息 活动链接")
private String link;
}

View File

@@ -14,4 +14,6 @@ public class QueryPortfolioPageDTO extends PageQueryBaseVo {
private Integer getMyPortfolio;
private Integer getLikePortfolio;
private Long accountId;
}

View File

@@ -0,0 +1,33 @@
package com.ai.da.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AccountFollowVO {
/**
* userId
*/
private Long senderId;
private String userName;
private String avatar;
/**
* followTime
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private LocalDateTime createTime;
/**
* mutualFollowing 互粉
*/
private Integer isFollow;
}

View File

@@ -34,4 +34,10 @@ public class AccountLoginVO {
private Integer systemUser;
private String avatar;
private Long followeeCount;
private Long followerCount;
}

View File

@@ -0,0 +1,29 @@
package com.ai.da.model.vo;
import com.ai.da.mapper.primary.entity.Notification;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class NotificationVO extends Notification {
/**
* senderUserName
*/
private String userName;
// private String senderUserAvatar;
private String portfolioName;
/**
* sender头像
*/
private String avatar;
private Integer isFollow;
private String canvas;
}

View File

@@ -0,0 +1,31 @@
package com.ai.da.model.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("个人主页返回信息")
public class PersonalHomepageVO {
@ApiModelProperty("用户名")
private String userName;
@ApiModelProperty("用户头像")
private String avatar;
@ApiModelProperty("用户作品总数")
private Long portfolioCount;
@ApiModelProperty("粉丝总数")
private Long followerCount;
@ApiModelProperty("关注者总数")
private Long followeeCount;
@ApiModelProperty("个人主页总浏览量")
private Long homepageViewCount;
@ApiModelProperty("是否关注了主页用户")
private Integer isFollow;
}

View File

@@ -28,4 +28,8 @@ public class PortfolioVO extends Portfolio {
private Integer selected;
private Integer jumpable;
private Integer isFollow;
private String avatar;
}

View File

@@ -3369,6 +3369,7 @@ public class PythonService {
}
} catch (IOException e) {
log.error("promptTranslation 用户输入翻译失败; error message => " + e.getMessage());
response.close();
throw new RuntimeException(e);
} finally {

View File

@@ -5,8 +5,10 @@ import com.ai.da.mapper.primary.entity.TrialOrder;
import com.ai.da.model.dto.*;
import com.ai.da.model.vo.AccountLoginVO;
import com.ai.da.model.vo.AccountPreLoginVO;
import com.ai.da.model.vo.PersonalHomepageVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@@ -157,4 +159,12 @@ public interface AccountService extends IService<Account> {
IPage<Account> getPageByIds(List<Long> ids, int pageNum, int size);
List<Account> getByIds(List<Long> ids);
String uploadAvatar(MultipartFile file);
PersonalHomepageVO getPersonalHomepage(Long accountId);
Boolean viewsIncrease(Long id);
void registerUserToVisitor();
}

View File

@@ -0,0 +1,28 @@
package com.ai.da.service;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.Notification;
import com.ai.da.model.dto.GetNotificationDTO;
import com.ai.da.model.vo.NotificationVO;
import com.ai.da.model.dto.PublishSysNotificationDTO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
import java.util.Map;
public interface MessageCenterService extends IService<Notification> {
Map<String, Long> getAllTypeMessageUnreadCount();
PageBaseResponse<NotificationVO> getHistoryNotification(GetNotificationDTO getNotificationDTO);
void prePushMessage(Notification notification);
void cancelPushMessage(String type, Long senderId, Long receiverId, Long portfolioId, Long commentId);
Boolean setReadStatus(List<Long> notificationIdList, String type);
void setReadAll(String type);
void publishSystemNotification(PublishSysNotificationDTO message);
}

View File

@@ -1,14 +1,15 @@
package com.ai.da.service;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.Account;
import com.ai.da.mapper.primary.entity.Portfolio;
import com.ai.da.model.dto.*;
import com.ai.da.model.vo.CommentVO;
import com.ai.da.model.vo.PortfolioVO;
import com.ai.da.model.vo.UserLikeChooseVO;
import com.ai.da.model.vo.*;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
public interface PortfolioService extends IService<Portfolio> {
Boolean publish(MultipartFile canvas, String data);
@@ -41,4 +42,22 @@ public interface PortfolioService extends IService<Portfolio> {
Boolean delete(Long id);
Portfolio getByIdAll(Long originalPortfolioId);
void follow(Long followeeId);
void cancelFollow(Long followeeId);
Long getFolloweeCount(Long accountId);
List<AccountFollowVO> getFolloweeList(GetFollowListDTO getFollowListDTO);
Long getFollowerCount(Long accountId);
List<AccountFollowVO> getFollowerList(GetFollowListDTO getFollowListDTO);
Integer getIfFollowed(Long followeeId, Long followerId);
Long getPortfolioCount(Long accountId);
List<Long> getFolloweeList(Long accountId);
}

View File

@@ -2,6 +2,7 @@ package com.ai.da.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.AuthenticationOperationTypeEnum;
import com.ai.da.common.enums.CreditsEventsEnum;
@@ -16,10 +17,7 @@ import com.ai.da.mapper.primary.entity.*;
import com.ai.da.model.dto.*;
import com.ai.da.model.enums.AutoApproved;
import com.ai.da.model.enums.Language;
import com.ai.da.model.vo.AccountLoginVO;
import com.ai.da.model.vo.AccountPreLoginVO;
import com.ai.da.model.vo.AuthPrincipalVo;
import com.ai.da.model.vo.QuestionnaireVO;
import com.ai.da.model.vo.*;
import com.ai.da.service.*;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -32,9 +30,11 @@ import com.zaxxer.hikari.HikariDataSource;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@@ -83,6 +83,15 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
@Resource
private CreditsService creditsService;
@Resource
private MinioUtil minioUtil;
@Value("${minio.bucketName.users}")
private String userBucket;
@Resource
private RedisUtil redisUtil;
@Override
@Transactional(rollbackFor = Exception.class)
public AccountPreLoginVO preLogin(AccountPreLoginDTO accountDTO) {
@@ -137,6 +146,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
return new AccountPreLoginVO(account.getId());
}
@Resource
private PortfolioService portfolioService;
@Transactional(rollbackFor = Exception.class)
@Override
public AccountLoginVO login(AccountLoginDTO accountLoginDTO, HttpServletRequest request) {
@@ -189,6 +201,16 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
}
response.setUserId(account.getId());
response.setSystemUser(account.getSystemUser());
// 设置头像
String avatar;
if (StringUtil.isNullOrEmpty(account.getAvatar())){
avatar = CommonConstant.DEFAULT_AVATAR;
}else {
avatar = account.getAvatar();
}
response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
//判断是否常用ip 不是则发邮件提示
calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account);
return response;
@@ -300,6 +322,19 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("BINARY user_email", email);
List<Account> accountList = accountMapper.selectList(queryWrapper);
log.info(accountList.toString());
if (CollectionUtil.isEmpty(accountList)) {
throw new BusinessException("email.does.not.exist", ResultEnum.PROMPT.getCode());
}
return accountList.get(0);
}
// 忽略大小写
private Account getAccountByEmail(String email) {
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_email", email);
List<Account> accountList = accountMapper.selectList(queryWrapper);
log.info(accountList.toString());
if (CollectionUtil.isEmpty(accountList)) {
throw new BusinessException("email.does.not.exist", ResultEnum.PROMPT.getCode());
}
@@ -1011,7 +1046,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
account.setIsTrial(1);
account.setIsBeginner(1);
account.setCreateDate(new Date());
account.setCredits(BigDecimal.valueOf(500));
account.setCredits(BigDecimal.valueOf(100));
accountMapper.insert(account);
AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
response.setEmail(account.getUserEmail());
@@ -1044,6 +1079,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
config.setJdbcUrl("jdbc:mysql://code-create.com.hk:3306/db1nfvsgmjp3b8");
config.setUsername("uafqtz4gsvfrw");
config.setPassword("aida123456.");
// config.setJdbcUrl("jdbc:mysql://localhost:3306/code-create-local?serverTimezone=UTC");
// config.setUsername("root");
// config.setPassword("root");
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
@@ -1068,6 +1106,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
int orderId = queryOrderResultSet.getInt("order_id");
int customerId = queryOrderResultSet.getInt("customer_id");
double totalSales = queryOrderResultSet.getDouble("total_sales");
log.info("Code-Create 订单:{} 顾客id:{}, 付款金额:{}",orderId, customerId, totalSales);
String email = "";
String userName = "";
// 为什么一般没有值
@@ -1094,7 +1133,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
Boolean flag = Boolean.FALSE;
try {
// 不是新用户 直接延长使用期限
userInfo = getOneByEmail(email);
userInfo = getAccountByEmail(email);
} catch (BusinessException e) {
// 通过邮箱找不到用户 说明是新用户 => 创建用户
flag = Boolean.TRUE;
@@ -1106,23 +1145,24 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
Account account = extendValidity(validEndTime, totalSales);
int systemUserType = 0;
// 不管是不是新用户 都要更新用户角色和积分
String credits = "0";
if (totalSales == 5000.0){
log.info("年付用户初始积分6000");
credits = CreditsEventsEnum.INIT_MONTHLY.getValue();
systemUserType = 1;
}else if (totalSales == 500.0){
log.info("月付用户初始积分5000");
credits = CreditsEventsEnum.INIT_MONTHLY.getValue();
systemUserType = 2;
}else if (totalSales == 0.0){
log.info("测试用户初始积分10");
credits = "10";
systemUserType = 3;
}
if (flag) {
// 是新用户 => 新增一条数据
String credits = "0";
if (totalSales == 5000.0){
log.info("年付用户初始积分6000");
credits = CreditsEventsEnum.INIT_MONTHLY.getValue();
systemUserType = 1;
}else if (totalSales == 500.0){
log.info("月付用户初始积分5000");
credits = CreditsEventsEnum.INIT_MONTHLY.getValue();
systemUserType = 2;
}else if (totalSales == 0.0){
log.info("测试用户初始积分10");
credits = "10";
systemUserType = 3;
}
Boolean b = addUser(new AccountAddDTO(email,
StringUtil.isNullOrEmpty(userName) ? email.substring(0, email.indexOf("@")) : userName,
country,
@@ -1131,6 +1171,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
if (b) log.info("付费新用户 {} 新增成功!", email);
} else {
userInfo.setValidEndTime(toDayEnd(account.getValidEndTime()));
userInfo.setCredits(new BigDecimal(credits));
userInfo.setSystemUser(systemUserType);
baseMapper.updateById(userInfo);
log.info("付费用户 {} 续订成功", email);
}
@@ -1196,6 +1238,86 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
return account;
}
private static final String QUERY_MAXIMUM_USERID = "SELECT MAX(ID) AS max_id FROM pmr_users;";
private static final String QUERY_NEW_USER_EMAIL = "SELECT user_email FROM pmr_users " +
"WHERE ID > ? ";
@Value("${redis.key.maximumUserId}")
private String maximumUserIdKey;
/**
* 将Code-Create上注册的用户添加为AiDA的游客
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void registerUserToVisitor(){
ArrayList<String> newUserEmails = new ArrayList<>();
long maxUserId = CommonConstant.MAXIMUM_USER_ID;
try (Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(QUERY_MAXIMUM_USERID)) {
try (ResultSet queryOrderResultSet = preparedStatement.executeQuery()) {
while (queryOrderResultSet.next()) {
// 获取最新的最大用户id
maxUserId = queryOrderResultSet.getLong("max_id");
// 获取历史最大用户id
long maxUserIdHistory = StringUtil.isNullOrEmpty(redisUtil.getFromString(maximumUserIdKey)) ? CommonConstant.MAXIMUM_USER_ID : Long.parseLong(redisUtil.getFromString(maximumUserIdKey));
if (maxUserId > maxUserIdHistory){
// 查出新增用户的邮箱
PreparedStatement newUserEmail = connection.prepareStatement(QUERY_NEW_USER_EMAIL);
// 填充参数 - 历史最大用户ID
newUserEmail.setLong(1, maxUserIdHistory);
try (ResultSet queryEmailResultSet = newUserEmail.executeQuery()) {
if (queryEmailResultSet.next()) {
String email = queryEmailResultSet.getString("user_email");
newUserEmails.add(email);
} /*else {
log.error("未知错误。code-create的用户表中没有付费用户的信息");
throw new BusinessException("user info missing");
}*/
}
}
}
}
} catch (Exception e) {
// 记录异常并处理
e.printStackTrace();
// return null;
}
if (!newUserEmails.isEmpty()){
// 查询这些邮箱在aida上是否有账号
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
queryWrapper.in("user_email", newUserEmails).select("user_email");
List<String> collect = baseMapper.selectList(queryWrapper).stream().map(Account::getUserEmail).collect(Collectors.toList());
if (!collect.isEmpty()){
// 移除Code-Create新增用户中在AiDA已有账号的邮箱
newUserEmails.removeAll(collect);
}
// 将新增用户添加到AiDA身份为游客
if (!newUserEmails.isEmpty()){
newUserEmails.forEach(email -> {
Account account = new Account();
account.setUserEmail(email);
account.setUserName(email);
account.setUserPassword("Third-000000");
account.setLanguage(Language.ENGLISH.name());
account.setCreateDate(new Date());
account.setIsTrial(0);
account.setIsBeginner(1);
account.setCredits(new BigDecimal(0));
account.setSystemUser(0);
baseMapper.insert(account);
// 邮件通知用户
SendEmailUtil.notificationForRegisterUser(email);
});
}
// 记录当前最大的用户id
redisUtil.addToString(maximumUserIdKey, String.valueOf(maxUserId));
}
}
// 收集调查问卷的信息
@Override
@Transactional(rollbackFor = Exception.class)
@@ -1404,7 +1526,63 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
return baseMapper.selectList(queryWrapper);
}
public String uploadAvatar(MultipartFile file){
Long accountId = UserContext.getUserHolder().getId();
// 1、上传图片到minio
String avatarPath = minioUtil.upload(userBucket, accountId.toString() + "/avatar", file);
// 2、查询该用户之前的头像
String avatar = baseMapper.selectById(accountId).getAvatar();
if (!StringUtil.isNullOrEmpty(avatar) && !avatar.equals(CommonConstant.DEFAULT_AVATAR)){
minioUtil.deleteObject(avatar);
}
// 3、替换新的头像
Account account = new Account();
account.setId(accountId);
account.setAvatar(avatarPath);
baseMapper.updateById(account);
return minioUtil.getPreSignedUrl(avatarPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
}
public PersonalHomepageVO getPersonalHomepage(Long accountId){
// 需要返回 用户头像 用户名 作品总量 粉丝量 关注量 主页访问量 当前用户是否被查看者关注
Long currentUserId = UserContext.getUserHolder().getId();
PersonalHomepageVO personalHomepageVO = new PersonalHomepageVO();
Account account = baseMapper.selectById(accountId);
personalHomepageVO.setUserName(account.getUserName());
String avatar = StringUtil.isNullOrEmpty(account.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : account.getAvatar();
personalHomepageVO.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
personalHomepageVO.setPortfolioCount(portfolioService.getPortfolioCount(accountId));
personalHomepageVO.setFolloweeCount(portfolioService.getFolloweeCount(accountId));
personalHomepageVO.setFollowerCount(portfolioService.getFollowerCount(accountId));
personalHomepageVO.setHomepageViewCount(viewPersonalHomepageCount(0L));
if (accountId.equals(currentUserId)){
personalHomepageVO.setIsFollow(0);
Long viewCount = viewPersonalHomepageCount(accountId);
// 只有本人才能看到个人主页浏览量
personalHomepageVO.setHomepageViewCount(viewCount == null ? 0 : viewCount);
}else {
personalHomepageVO.setIsFollow(portfolioService.getIfFollowed(accountId, currentUserId));
// 非本人浏览主页时增加浏览量
viewsIncrease(accountId);
}
return personalHomepageVO;
}
@Override
public Boolean viewsIncrease(Long id) {
redisUtil.increasePersonalHomepageViewCount(id);
return Boolean.TRUE;
}
private Long viewPersonalHomepageCount(Long accountId) {
redisUtil.getPersonalHomepageViewCount(accountId);
return null;
}
}

View File

@@ -27,6 +27,7 @@ import com.mysql.cj.util.StringUtils;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
@@ -434,11 +435,20 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
// 新增用户
@Transactional(rollbackFor = Exception.class)
public Boolean addUser(AccountAddDTO accountAddDTO) {
// 需要给的数据 用户邮箱、用户名、账号有效期截止时间、账号类型
Account account = CopyUtil.copyObject(accountAddDTO, Account.class);
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_email", account.getUserEmail());
Account existsAccount = accountMapper.selectOne(queryWrapper);
if (!Objects.isNull(existsAccount)){
throw new BusinessException("The email address already exists. One email address can only register one AiDA account");
}
// 添加正式用户
assert accountAddDTO != null;
if (Objects.isNull(accountAddDTO.getSystemUser())) {
throw new BusinessException("you have to choose user type");
} else {

View File

@@ -401,6 +401,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
designPythonOutfitDetail.setUserId(userId);
designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(Integer.parseInt(jsonObject.getString("priority"))))));
designPythonOutfitDetail.setPriority((Integer) jsonObject.get("priority"));
designPythonOutfitDetail.setCreateDate(LocalDateTime.now());
list.add(designPythonOutfitDetail);
}
return list;

View File

@@ -744,7 +744,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
Long accountId = Long.parseLong(taskId.substring(taskId.lastIndexOf("-") + 1));
if (!status.equals("Invalid")){
if (!status.equals("Invalid")) {
// 4、扣除积分
Boolean b = creditsService.taskCreditsDeduction(accountId, taskId);
// 3、记录积分变更

View File

@@ -0,0 +1,358 @@
package com.ai.da.service.impl;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.utils.CopyUtil;
import com.ai.da.common.utils.MinioUtil;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.websocket.NotificationConnection;
import com.ai.da.mapper.primary.CanvasMapper;
import com.ai.da.mapper.primary.NotificationMapper;
import com.ai.da.mapper.primary.SysNotificationReadStatusMapper;
import com.ai.da.mapper.primary.entity.Account;
import com.ai.da.mapper.primary.entity.Notification;
import com.ai.da.mapper.primary.entity.Portfolio;
import com.ai.da.mapper.primary.entity.SysNotificationReadStatus;
import com.ai.da.model.dto.GetNotificationDTO;
import com.ai.da.model.dto.PublishSysNotificationDTO;
import com.ai.da.model.vo.NotificationVO;
import com.ai.da.service.AccountService;
import com.ai.da.service.MessageCenterService;
import com.ai.da.service.PortfolioService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.mysql.cj.util.StringUtils;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Slf4j
public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, Notification> implements MessageCenterService {
@Resource
private NotificationConnection notificationConnection;
@Resource
private SysNotificationReadStatusMapper sysNotificationReadStatusMapper;
@Resource
private AccountService accountService;
@Resource
private MinioUtil minioUtil;
@Resource
private PortfolioService portfolioService;
@Resource
private CanvasMapper canvasMapper;
@Resource
private RedisUtil redisUtil;
@Value("${redis.key.newPosted}")
private String lastViewNewPostedTimeKey;
@Override
public Map<String, Long> getAllTypeMessageUnreadCount() {
Long accountId = UserContext.getUserHolder().getId();
List<Map<String, Object>> typeCount = baseMapper.getTypeCount(accountId);
Map<String, Long> msgTypeCount = typeCount.stream()
.collect(Collectors.toMap(
map -> (String) map.get("type"),
map -> Objects.isNull(map.get("count")) ? 0L : (Long) map.get("count")));
msgTypeCount.put("system", getUnreadSystemNotification());
msgTypeCount.put("newPosted", getNewPostedCount(accountId));
log.info(msgTypeCount.toString());
// 整理数据 加上系统消息未读数
return msgTypeCount;
}
// 获取历史消息 可指定消息类型 分页查询
@Override
public PageBaseResponse<NotificationVO> getHistoryNotification(GetNotificationDTO getNotificationDTO) {
log.info("获取历史消息parameter => {}", getNotificationDTO);
if (StringUtil.isNullOrEmpty(getNotificationDTO.getType())){
throw new BusinessException("type cannot be empty");
}
Long accountId = UserContext.getUserHolder().getId();
// 查动态
if (!StringUtils.isNullOrEmpty(getNotificationDTO.getType()) && getNotificationDTO.getType().equals("newPosted")){
return getNewPosted(accountId, getNotificationDTO.getPage(), getNotificationDTO.getSize());
}
QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
if (!StringUtils.isNullOrEmpty(getNotificationDTO.getType())) {
queryWrapper.eq("type", getNotificationDTO.getType());
}
if (!getNotificationDTO.getType().equals("system")){
queryWrapper.eq("receiver_id", accountId);
}
Page<Notification> notificationPage = baseMapper.selectPage(new Page<>(getNotificationDTO.getPage(), getNotificationDTO.getSize()), queryWrapper);
List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId);
IPage<NotificationVO> convert = notificationPage.convert(o -> {
NotificationVO notificationVO = CopyUtil.copyObject(o, NotificationVO.class);
Account senderAccount = accountService.getById(notificationVO.getSenderId());
notificationVO.setUserName(senderAccount.getUserName());
// notificationVO.setSenderUserAvatar(StringUtils.isNullOrEmpty(senderAccount.getAvatar()) ? null : minioUtil.getPreSignedUrl(senderAccount.getAvatar(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
notificationVO.setPortfolioName(Objects.isNull(notificationVO.getPortfolioId()) ? null : portfolioService.getById(notificationVO.getPortfolioId()).getPortfolioName());
// 设置单个人 系统消息是否已读
if (notificationVO.getType().equals("system")){
if (unreadSysNotificationIds.contains(notificationVO.getId())){
notificationVO.setIsRead(0);
}else {
notificationVO.setIsRead(1);
}
}else {
String avatar = StringUtil.isNullOrEmpty(senderAccount.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : senderAccount.getAvatar();
notificationVO.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
}
return notificationVO;
});
return PageBaseResponse.success(convert);
}
public void prePushMessage(Notification notification){
// 先判断点赞、关注记录是不是唯一
if (!getUniqueLikeAndFollow(notification)){
// 1、将数据存数据库
if (!notification.getType().equals("system")) {
notification.setIsRead(0);
}
notification.setIsDeleted(0);
notification.setCreateTime(LocalDateTime.now());
save(notification);
}
// 推送消息
pushMessage(notification.getType(), notification.getSenderId());
}
/**
* 只记录唯一点赞和关注
* 重复点赞、关注只会记录最新的一次操作
* @param notification
* @return
*/
public Boolean getUniqueLikeAndFollow(Notification notification){
Notification existNotification= null;
// 对单个作品的点赞和对某个人的关注 具有唯一性
if (notification.getType().equals("like") || notification.getType().equals("follow")){
existNotification = baseMapper.getUniqueLikeAndFollow(notification.getType(), notification.getSenderId(),
notification.getReceiverId(), notification.getPortfolioId());
}
if (!Objects.isNull(existNotification)){
// 有记录,则更新 1、删除状态 为0 2、已读状态 为0 3、创建时间 为最新时间 4、更新时间 为最新时间
baseMapper.updateUniqueLikeAndFollow(existNotification.getId(), LocalDateTime.now());
return Boolean.TRUE;
}
return Boolean.FALSE;
}
// 消息推送 只需要返回当前操作类型和该操作未读消息数量
public void pushMessage(String type, Long receiverId) {
// 推送消息到前端
ArrayList<Map<String, Object>> resp = new ArrayList<>();
HashMap<String, Object> data = new HashMap<>();
Long count;
if (!type.equals("system")) {
// 个人未读消息
count = getUnreadCountByType(type, receiverId);
} else {
// 系统未读消息
count = getUnreadSystemNotification();
}
data.put(type, count);
resp.add(data);
String jsonString = JSON.toJSONString(resp);
log.info("消息推送 {}", jsonString);
try {
notificationConnection.sendMsg(jsonString);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 取消点赞、删除评论、取消关注
public void cancelPushMessage(String type, Long senderId, Long receiverId, Long portfolioId, Long commentId) {
QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("type", type)
.eq("sender_id", senderId)
.eq("receiver_id", receiverId)
.eq("is_deleted", 0);
if (!type.equals("follow")){
queryWrapper.eq("portfolio_id", portfolioId);
}
if (type.equals("comment")) {
queryWrapper.eq("comment_id", commentId);
}
Notification notification = baseMapper.selectOne(queryWrapper);
if (!Objects.isNull(notification)) {
baseMapper.deleteNotification(notification.getId(), LocalDateTime.now());
}
// 推送消息
pushMessage(type, senderId);
}
// 获取个人指定消息类型未读消息数量
private Long getUnreadCountByType(String type, Long receiverId) {
QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("type", type)
.eq("is_read", 0)
.eq("receiver_id", receiverId);
return baseMapper.selectCount(queryWrapper);
}
private Long getUnreadSystemNotification() {
// 计算总的系统通知数量
QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("type", "system");
Long totalSysCount = baseMapper.selectCount(queryWrapper);
// 计算单个用户读了多少条系统数据
QueryWrapper<SysNotificationReadStatus> wrapper = new QueryWrapper<>();
wrapper.eq("account_id", UserContext.getUserHolder().getId());
Long readCount = sysNotificationReadStatusMapper.selectCount(wrapper);
// 计算差
return totalSysCount - readCount > 0 ? totalSysCount - readCount : 0;
}
// 设置个人消息的已读状态 (允许一次已读多条个人消息)
public Boolean setReadStatus(List<Long> notificationIdList, String type) {
if (type.equals("system")) {
setReadStatusSystem(notificationIdList);
} else {
UpdateWrapper<Notification> updateWrapper = new UpdateWrapper<>();
updateWrapper.in("id", notificationIdList)
// .eq("type", type)
.set("is_read", 1)
.set("update_time", LocalDateTime.now());
baseMapper.update(null, updateWrapper);
}
pushMessage(type, UserContext.getUserHolder().getId());
return Boolean.TRUE;
}
// 设置系统消息的已读状态 (允许一次已读多条系统消息)
public void setReadStatusSystem(List<Long> notificationIdList) {
Long id = UserContext.getUserHolder().getId();
for (Long notificationId : notificationIdList) {
SysNotificationReadStatus sysNotificationReadStatus = new SysNotificationReadStatus(notificationId, id);
sysNotificationReadStatus.setCreateTime(LocalDateTime.now());
sysNotificationReadStatusMapper.insert(sysNotificationReadStatus);
}
}
// 一键已读
public void setReadAll(String type){
Long accountId = UserContext.getUserHolder().getId();
// 指定某个用户的某种类型的数据,将未读数据全部已读
if (!type.equals("system")){
// 个人消息已读
baseMapper.setPersonalNotificationAllRead(type, accountId, LocalDateTime.now());
}else {
// 系统消息已读
// 1、先确定当前用户未读的系统消息有哪些
List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId);
// 2、将未读的设为已读
if (!unreadSysNotificationIds.isEmpty()) setReadStatusSystem(unreadSysNotificationIds);
}
pushMessage(type, accountId);
}
// 发布系统消息
public void publishSystemNotification(PublishSysNotificationDTO message) {
Notification notification = new Notification();
notification.setType("system");
notification.setSenderId(UserContext.getUserHolder().getId());
notification.setContent(new Gson().toJson(message));
// todo 是否需要定时发送系统通知
prePushMessage(notification);
}
public Long getNewPostedCount(Long accountId){
// 1.1 获取我关注的所有用户
List<Long> followeeList = portfolioService.getFolloweeList(accountId);
// 1.2 查询我关注的用户在我上次查看动态之后发布的作品数量
String lastViewTime = redisUtil.getFromString(lastViewNewPostedTimeKey + ":" + accountId);
QueryWrapper<Portfolio> queryWrapper = new QueryWrapper<>();
if (!followeeList.isEmpty()){
queryWrapper.in("account_id", followeeList);
}else {
return 0L;
}
if (!StringUtil.isNullOrEmpty(lastViewTime)){
queryWrapper.gt("create_date", lastViewTime);
}else {
return 0L;
}
return portfolioService.getBaseMapper().selectCount(queryWrapper);
}
/**
* 获取关注用户发布的新作品 分页查询
*/
public PageBaseResponse<NotificationVO> getNewPosted(Long accountId, Integer page, Integer size){
// 1、获取关注用户发布的所有作品
// 1.1 获取我关注的所有用户
List<Long> followeeList = portfolioService.getFolloweeList(accountId);
// 1.2 分页查询我关注的用户发布的作品
QueryWrapper<Portfolio> queryWrapper = new QueryWrapper<>();
if (!followeeList.isEmpty()){
queryWrapper.in("account_id", followeeList);
}else {
return new PageBaseResponse<>(new ArrayList<>(), page, size, 0, 0);
}
queryWrapper.orderByDesc("create_date");
Page<Portfolio> portfolioPage = portfolioService.getBaseMapper().selectPage(new Page<>(page, size), queryWrapper);
// 2、组装返回的数据
IPage<NotificationVO> convert = portfolioPage.convert(o -> {
NotificationVO notificationVO = CopyUtil.copyObject(o, NotificationVO.class);
notificationVO.setPortfolioId(o.getId());
notificationVO.setSenderId(o.getAccountId());
notificationVO.setReceiverId(accountId);
notificationVO.setCreateTime(o.getCreateDate());
notificationVO.setType("newPosted");
Account account = accountService.getById(o.getAccountId());
notificationVO.setUserName(account.getUserName());
String avatar = StringUtil.isNullOrEmpty(account.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : account.getAvatar();
notificationVO.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
notificationVO.setPortfolioName(o.getPortfolioName());
String canvas = canvasMapper.selectById(o.getCanvasId()).getUrl();
notificationVO.setCanvas(minioUtil.getPreSignedUrl(canvas, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 增加访问时间记录
redisUtil.addToString(lastViewNewPostedTimeKey + ":" + accountId, format);
return notificationVO;
});
return PageBaseResponse.success(convert);
}
}

View File

@@ -1,6 +1,7 @@
package com.ai.da.service.impl;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.utils.CopyUtil;
@@ -8,6 +9,7 @@ import com.ai.da.common.utils.MinioUtil;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.mapper.primary.*;
import com.ai.da.mapper.primary.entity.*;
import com.ai.da.mapper.primary.entity.Collection;
import com.ai.da.model.dto.*;
import com.ai.da.model.enums.Position;
import com.ai.da.model.enums.Sex;
@@ -20,20 +22,18 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Function;
import io.netty.util.internal.StringUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import sun.security.krb5.internal.crypto.Des;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio> implements PortfolioService {
@@ -60,8 +60,8 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
@Resource
private UserLikeMapper userLikeMapper;
@Resource
@Resource
private TDesignPythonOutfitMapper designPythonOutfitMapper;
@Resource
@@ -76,7 +76,6 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
@Resource
private DesignItemDetailPrintMapper designItemDetailPrintMapper;
@Resource
private MinioUtil minioUtil;
@@ -110,6 +109,9 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
@Resource
private WorkspaceRelStyleMapper workspaceRelStyleMapper;
@Resource
private UserFollowMapper userFollowMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean publish(MultipartFile file, String data) {
@@ -170,7 +172,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
portfolio.setOriginal(0);
portfolio.setOriginalAccountId(userLikeGroup.getOriginalAccountId());
portfolio.setOriginalPortfolioId(userLikeGroup.getOriginalPortfolioId());
}else {
} else {
portfolio.setOriginal(1);
// portfolio.setOriginalAccountId(authPrincipalVo.getId());
}
@@ -188,7 +190,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
portfolio.setPortfolioDes(portfolioDTO.getPortfolioDes());
if (!CollectionUtils.isEmpty(portfolios)) {
portfolioMapper.updateById(portfolio);
}else {
} else {
portfolioMapper.insert(portfolio);
}
@@ -262,7 +264,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
portfolio.setOriginal(0);
portfolio.setOriginalAccountId(userLikeGroup.getOriginalAccountId());
portfolio.setOriginalPortfolioId(userLikeGroup.getOriginalPortfolioId());
}else {
} else {
portfolio.setOriginal(1);
// portfolio.setOriginalAccountId(authPrincipalVo.getId());
}
@@ -279,7 +281,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
portfolio.setPortfolioDes(portfolioDTO.getPortfolioDes());
if (!CollectionUtils.isEmpty(portfolios)) {
portfolioMapper.updateById(portfolio);
}else {
} else {
portfolioMapper.insert(portfolio);
}
}
@@ -423,17 +425,19 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
QueryWrapper<Portfolio> qw = new QueryWrapper<>();
if (query.getGetMyPortfolio() == 1) {
qw.lambda().eq(Portfolio::getAccountId, userHolder.getId());
} else if (!Objects.isNull(query.getAccountId())) {
qw.lambda().eq(Portfolio::getAccountId, query.getAccountId());
}
if (query.getGetLikePortfolio() == 1) {
List<Long> likedPortfolioIdList = redisUtil.getLikedPortfolios(userHolder.getId());
if (!CollectionUtils.isEmpty(likedPortfolioIdList)) {
qw.lambda().in(Portfolio::getId, likedPortfolioIdList);
}else {
} else {
return PageBaseResponse.success(new Page<>());
}
}
qw.lambda().orderByDesc(Portfolio::getUpdateDate);
IPage<Portfolio> page = portfolioMapper.selectPage(new Page<>(query.getPage(), query.getSize()),qw);
IPage<Portfolio> page = portfolioMapper.selectPage(new Page<>(query.getPage(), query.getSize()), qw);
IPage<PortfolioVO> convert = page.convert((Function<Portfolio, PortfolioVO>) portfolio -> {
if (portfolio != null) {
PortfolioVO vo = CopyUtil.copyObject(portfolio, PortfolioVO.class);
@@ -479,19 +483,28 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
Canvas canvas = canvasMapper.selectById(vo.getCanvasId());
vo.setCanvasUrl(minioUtil.getPreSignedUrl(canvas.getUrl(), 24 * 60));
vo.setLikeNum(redisUtil.getLikeCount(vo.getId()));
String avatar;
Account account = accountMapper.selectById(vo.getAccountId());
if (userHolder == null) {
vo.setIsLike(0);
}else {
vo.setIsFollow(0);
avatar = CommonConstant.DEFAULT_AVATAR;
} else {
boolean postLikedByUser = redisUtil.isPostLikedByUser(portfolioDTO.getId(), userHolder.getId());
if (postLikedByUser) {
vo.setIsLike(1);
}else {
} else {
vo.setIsLike(0);
}
// 设置当前用户是否关注了所查看作品的作者
Integer ifFollowed = getIfFollowed(portfolio.getAccountId(), userHolder.getId());
vo.setIsFollow(ifFollowed);
avatar = StringUtil.isNullOrEmpty(account.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : account.getAvatar();
}
vo.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
redisUtil.increaseViewCount(portfolioDTO.getId());
vo.setViewNums(redisUtil.getViewCount(portfolioDTO.getId()));
vo.setUserName(accountMapper.selectById(vo.getAccountId()).getUserName());
vo.setUserName(account.getUserName());
if (vo.getOriginal() == 0) {
vo.setOriginalUserName(accountMapper.selectById(vo.getOriginalAccountId()).getUserName());
Portfolio byId = portfolioMapper.getByIdAll(vo.getOriginalPortfolioId());
@@ -500,7 +513,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
vo.setOriginalPortfolioName(portfolioName);
if (byId.getIsDeleted() == 0) {
vo.setJumpable(1);
}else {
} else {
vo.setJumpable(0);
}
}
@@ -508,11 +521,11 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
if (userHolder == null) {
vo.setIsMine(0);
vo.setSelected(0);
}else {
} else {
if (Objects.equals(vo.getAccountId(), userHolder.getId()) || Objects.equals(vo.getOriginalAccountId(), userHolder.getId())) {
vo.setIsMine(1);
vo.setSelected(1);
}else {
} else {
vo.setIsMine(0);
QueryWrapper<UserLikeGroup> getSelectedQw = new QueryWrapper<>();
getSelectedQw.lambda().eq(UserLikeGroup::getAccountId, userHolder.getId());
@@ -520,7 +533,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
List<UserLikeGroup> userLikeGroups = userLikeGroupMapper.selectList(getSelectedQw);
if (CollectionUtils.isEmpty(userLikeGroups)) {
vo.setSelected(0);
}else {
} else {
vo.setSelected(1);
}
}
@@ -547,12 +560,12 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
if (portfolio.getOriginal() == 1) {
if (Objects.equals(portfolio.getAccountId(), authPrincipalVo.getId())) {
userLikeGroupNew.setOriginal(1);
}else {
} else {
userLikeGroupNew.setOriginal(0);
userLikeGroupNew.setOriginalAccountId(portfolio.getAccountId());
userLikeGroupNew.setOriginalPortfolioId(portfolio.getId());
}
}else {
} else {
userLikeGroupNew.setOriginal(0);
userLikeGroupNew.setOriginalAccountId(portfolio.getOriginalAccountId());
userLikeGroupNew.setOriginalPortfolioId(portfolio.getId());
@@ -572,7 +585,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
if (workspace.getSex().equals(Sex.FEMALE.getValue())) {
design.setTemplateId(workspace.getMannequinFemaleId());
design.setModelType(workspace.getMannequinFemaleType());
}else {
} else {
design.setTemplateId(workspace.getMannequinMaleId());
design.setModelType(workspace.getMannequinMaleType());
}
@@ -580,7 +593,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
if (workspace.getPosition().equals(Position.OVERALL.getValue())) {
design.setSingleOverall("overall");
design.setSwitchCategory("");
}else {
} else {
design.setSingleOverall("single");
design.setSwitchCategory(workspace.getPosition());
}
@@ -698,7 +711,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
workspaceNew.setSystemDesignerPercentage((design1.getSystemScale().multiply(BigDecimal.valueOf(100)).intValue()));
if (design1.getSingleOverall().equals("overall")) {
workspaceNew.setPosition("Overall");
}else {
} else {
workspaceNew.setPosition(design1.getSwitchCategory());
}
workspaceMapper.insert(workspaceNew);
@@ -727,7 +740,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
workspaceNew.setMannequinMaleId(anotherOne.getId());
workspaceNew.setMannequinMaleType("System");
}
}else {
} else {
workspaceNew.setSex("Male");
workspaceNew.setMannequinMaleId(design1.getTemplateId());
workspaceNew.setMannequinMaleType("System");
@@ -751,7 +764,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
workspaceNew.setMannequinFemaleType("System");
}
}
}else {
} else {
Library library = libraryMapper.selectById(design1.getTemplateId());
if (library.getLevel2Type().equals("Female")) {
workspaceNew.setSex("Female");
@@ -766,7 +779,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
workspaceNew.setMannequinMaleId(anotherOne.getId());
workspaceNew.setMannequinMaleType("System");
}
}else {
} else {
workspaceNew.setSex("Male");
workspaceNew.setMannequinMaleId(design1.getTemplateId());
workspaceNew.setMannequinMaleType("Library");
@@ -795,15 +808,22 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
@Resource
private RedisUtil redisUtil;
@Resource
private MessageCenterService messageCenterService;
@Override
public Boolean like(Long id) {
AuthPrincipalVo userHolder = UserContext.getUserHolder();
redisUtil.likePost(id, userHolder.getId());
Portfolio byIdAll = baseMapper.getByIdAll(id);
messageCenterService.prePushMessage(new Notification("like", userHolder.getId(), byIdAll.getAccountId(), id));
return Boolean.TRUE;
}
@Resource
private CommentMapper commentMapper;
@Override
public Boolean comment(CommentDTO commentDTO) {
AuthPrincipalVo userHolder = UserContext.getUserHolder();
@@ -811,6 +831,8 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
comment.setCreateTime(LocalDateTime.now());
comment.setAccountId(userHolder.getId());
commentMapper.insert(comment);
Portfolio portfolio = baseMapper.selectById(comment.getPortfolioId());
messageCenterService.prePushMessage(new Notification("comment", userHolder.getId(), portfolio.getAccountId(), portfolio.getId(), commentDTO.getComment(), comment.getId()));
return Boolean.TRUE;
}
@@ -837,6 +859,8 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
public Boolean unlike(Long id) {
AuthPrincipalVo userHolder = UserContext.getUserHolder();
redisUtil.unLikePost(id, userHolder.getId());
Portfolio portfolio = baseMapper.selectById(id);
messageCenterService.cancelPushMessage("like", userHolder.getId(), portfolio.getAccountId(), portfolio.getId(), null);
return Boolean.TRUE;
}
@@ -869,6 +893,15 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
throw new BusinessException("You do not have the permission to delete this comment");
}
}
// 获取消息接收者id即该操作的接收者)
Long receiverId;
if (!comment.getParentLevel1Id().equals(0L)) {
receiverId = commentMapper.selectById(comment.getParentLevel1Id()).getAccountId();
} else {
receiverId = portfolio.getAccountId();
}
// 删除主评论
commentMapper.deleteById(id);
@@ -879,6 +912,7 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
.eq(Comment::getParentLevel2Id, id);
commentMapper.delete(lambdaQueryWrapper);
messageCenterService.cancelPushMessage("comment", userHolder.getId(), receiverId, portfolio.getId(), id);
return Boolean.TRUE;
}
@@ -925,4 +959,143 @@ public class PortfolioServiceImpl extends ServiceImpl<PortfolioMapper, Portfolio
// }
return resultList;
}
public void follow(Long followeeId) {
Long accountId = UserContext.getUserHolder().getId();
// 1、不能关注自己
if (Objects.equals(followeeId, accountId)) {
throw new BusinessException("you.cannot.follow.yourself", 1);
}
// 2、不能重复关注
QueryWrapper<UserFollow> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("followee_id", followeeId).eq("follower_id", accountId);
UserFollow userFollow = userFollowMapper.selectOne(queryWrapper);
if (!Objects.isNull(userFollow)) {
throw new BusinessException("you.have.already.followed.this.user", 1);
}
// 3、添加新follower到关注列表
UserFollow newFollower = new UserFollow(followeeId, accountId);
newFollower.setCreateTime(LocalDateTime.now());
userFollowMapper.insert(newFollower);
// 4、推送消息
messageCenterService.prePushMessage(new Notification("follow", accountId, followeeId));
}
// 取消关注
public void cancelFollow(Long followeeId) {
Long accountId = UserContext.getUserHolder().getId();
// 1、确定是否关注了该用户
QueryWrapper<UserFollow> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("followee_id", followeeId).eq("follower_id", accountId);
UserFollow userFollow = userFollowMapper.selectOne(queryWrapper);
// 2、删除关注记录
if (!Objects.isNull(userFollow)) {
userFollowMapper.deleteById(userFollow.getId());
// 3、逻辑删除关注消息
messageCenterService.cancelPushMessage("follow", accountId, followeeId, null, null);
} else {
throw new BusinessException("you.have.not.followed.the.current.user", 1);
}
}
public Long getFolloweeCount(Long accountId) {
QueryWrapper<UserFollow> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("follower_id", accountId).select("followee_id");
return userFollowMapper.selectCount(queryWrapper);
}
// 获取某个用户的关注列表 + 按名字查询
public List<AccountFollowVO> getFolloweeList(GetFollowListDTO getFollowListDTO) {
Long accountId = UserContext.getUserHolder().getId();
// 1、判断是否有按用户名查询
List<AccountFollowVO> followeeList;
if (!StringUtil.isNullOrEmpty(getFollowListDTO.getSearchByName())) {
followeeList = userFollowMapper.getFolloweeListByName(getFollowListDTO.getSearchByName(), accountId);
}else {
// 2、查全部 分页查询
String order = StringUtil.isNullOrEmpty(getFollowListDTO.getOrder()) ? "DESC" : getFollowListDTO.getOrder().equals("DESC") ? "DESC" : "ASC";
Integer limit= getFollowListDTO.getSize() > 0 ? getFollowListDTO.getSize() : 20;
Integer offset = getFollowListDTO.getPage() > 0 ? (getFollowListDTO.getPage() - 1) * getFollowListDTO.getSize() : 0;
followeeList = userFollowMapper.getFolloweeListByFollower(accountId, limit, offset, order);
}
if (!followeeList.isEmpty()){
followeeList.forEach(followee -> {
String avatar = StringUtil.isNullOrEmpty(followee.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : followee.getAvatar();
followee.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
});
return followeeList;
}
return new ArrayList<>();
}
public Long getFollowerCount(Long accountId) {
QueryWrapper<UserFollow> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("followee_id", accountId).select("follower_id");
return userFollowMapper.selectCount(queryWrapper);
}
// 获取某个用户的粉丝列表 + 按名字查询 需返回是否关注该粉丝
public List<AccountFollowVO> getFollowerList(GetFollowListDTO getFollowListDTO) {
Long accountId = UserContext.getUserHolder().getId();
// 获取当前用户的所有粉丝
QueryWrapper<UserFollow> qw = new QueryWrapper<>();
qw.eq("follower_id", accountId).select("followee_id","create_time");
List<UserFollow> userFollows = userFollowMapper.selectList(qw);
Map<Long, LocalDateTime> followeeMap = userFollows.stream().collect(Collectors.toMap(UserFollow::getFolloweeId, UserFollow::getCreateTime));
List<AccountFollowVO> followerList;
// 1、判断是否有按用户名查询粉丝
if (!StringUtil.isNullOrEmpty(getFollowListDTO.getSearchByName())) {
followerList = userFollowMapper.getFollowerListByName(getFollowListDTO.getSearchByName(), accountId);
}else {
// 2、查全部 分页查询
String order = StringUtil.isNullOrEmpty(getFollowListDTO.getOrder()) ? "DESC" : getFollowListDTO.getOrder().equals("DESC") ? "DESC" : "ASC";
Integer limit= getFollowListDTO.getSize() > 0 ? getFollowListDTO.getSize() : 20;
Integer offset = getFollowListDTO.getPage() > 0 ? (getFollowListDTO.getPage() - 1) * getFollowListDTO.getSize() : 0;
followerList = userFollowMapper.getFollowerListByFollowee(accountId, limit,offset, order);
}
if (!followerList.isEmpty()){
// 判断当前用户是否与粉丝互关
followerList.forEach(follower -> {
String avatar = StringUtil.isNullOrEmpty(follower.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : follower.getAvatar();
follower.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
follower.setIsFollow(Objects.isNull(followeeMap.get(follower.getSenderId())) ? 0 : 1);
// follower.setFollowTime(followeeMap.get(follower.getUserId()));
});
return followerList;
}
return new ArrayList<>();
}
public Integer getIfFollowed(Long followeeId, Long followerId) {
// 设置当前用户是否关注了所查看作品的作者
QueryWrapper<UserFollow> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("followee_id", followeeId).eq("follower_id", followerId);
UserFollow userFollow = userFollowMapper.selectOne(queryWrapper);
return Objects.isNull(userFollow) ? 0 : 1;
}
public Long getPortfolioCount(Long accountId) {
QueryWrapper<Portfolio> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("account_id", accountId);
return baseMapper.selectCount(queryWrapper);
}
/**
* 获取关注列表
* @param accountId
* @return
*/
public List<Long> getFolloweeList(Long accountId){
QueryWrapper<UserFollow> qw = new QueryWrapper<>();
qw.eq("follower_id", accountId).select("followee_id");
List<UserFollow> userFollows = userFollowMapper.selectList(qw);
return userFollows.stream().map(UserFollow::getFolloweeId).collect(Collectors.toList());
}
}