parent
e0edf15b42
commit
33f17306cc
@ -0,0 +1,33 @@
|
|||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.vksfeng.quan.constant;
|
||||||
|
|
||||||
|
public class AchievementConstant {
|
||||||
|
public static final String OBJECTIVE_ADDED = "OBJECTIVE_ADDED";
|
||||||
|
public static final String OBJECTIVE_COMPLETED = "OBJECTIVE_COMPLETED";
|
||||||
|
public static final String TASK_ADDED = "TASK_ADDED";
|
||||||
|
public static final String TASK_COMPLETED = "TASK_COMPLETED";
|
||||||
|
public static final String RESOURCE_SHARED = "RESOURCE_SHARED"; // 这里的一个小问题是,评论和资源分享都应该能触发
|
||||||
|
public static final String FRIEND_ADDED = "FRIEND_ADDED";
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.vksfeng.quan.constant;
|
||||||
|
|
||||||
|
public class RedisConstant {
|
||||||
|
public static final String USER_LOCATION_KEY = "quan:geo:users";
|
||||||
|
|
||||||
|
public static final String ACHIEVEMENT_STREAM_KEY = "quan:achievement_stream";
|
||||||
|
|
||||||
|
public static final String PROMPT_TEMPLATE_KEY = "quan:prompt:template";
|
||||||
|
|
||||||
|
public static final String PROMPT_DOMAIN_LIST_KEY = "quan:prompt:domains";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.vksfeng.quan.context;
|
||||||
|
|
||||||
|
public class BaseContext {
|
||||||
|
public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
|
||||||
|
|
||||||
|
public static void setCurrentId(Long id) {
|
||||||
|
threadLocal.set(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Long getCurrentId() { return threadLocal.get(); }
|
||||||
|
|
||||||
|
public static void removeCurrentId() {
|
||||||
|
threadLocal.remove();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class BaseException extends RuntimeException{
|
||||||
|
|
||||||
|
BaseException() { }
|
||||||
|
|
||||||
|
BaseException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class DuplicateLikeException extends BaseException {
|
||||||
|
public DuplicateLikeException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DuplicateLikeException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class EmailExistsException extends BaseException{
|
||||||
|
public EmailExistsException() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmailExistsException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class FavoriteAlreadyExistException extends BaseException {
|
||||||
|
|
||||||
|
public FavoriteAlreadyExistException() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public FavoriteAlreadyExistException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class FeedNotExistException extends BaseException{
|
||||||
|
public FeedNotExistException() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public FeedNotExistException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class FriendshipAlreadyExistException extends BaseException{
|
||||||
|
public FriendshipAlreadyExistException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FriendshipAlreadyExistException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class FriendshipNotExistException extends BaseException{
|
||||||
|
|
||||||
|
public FriendshipNotExistException() {
|
||||||
|
super("好友关系不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
public FriendshipNotExistException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class LikeAlreadyExistException extends BaseException{
|
||||||
|
public LikeAlreadyExistException() {
|
||||||
|
super("点赞已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
public LikeAlreadyExistException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class NotLoginException extends BaseException{
|
||||||
|
public NotLoginException() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotLoginException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class ObjectiveCreationFailureException extends BaseException{
|
||||||
|
public ObjectiveCreationFailureException() {
|
||||||
|
}
|
||||||
|
public ObjectiveCreationFailureException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class PasswordErrorException extends BaseException{
|
||||||
|
public PasswordErrorException() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public PasswordErrorException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class TaskNotExistException extends BaseException{
|
||||||
|
public TaskNotExistException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskNotExistException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class UnknownException extends BaseException{
|
||||||
|
public UnknownException() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnknownException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class UnknownTaskTypeException extends BaseException{
|
||||||
|
public UnknownTaskTypeException() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnknownTaskTypeException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class UserNotExistsException extends BaseException{
|
||||||
|
public UserNotExistsException(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserNotExistsException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.vksfeng.quan.exception;
|
||||||
|
|
||||||
|
public class UsernameExistsException extends BaseException{
|
||||||
|
|
||||||
|
public UsernameExistsException() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public UsernameExistsException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.vksfeng.quan.properties;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "quan.alioss")
|
||||||
|
@Data
|
||||||
|
public class AliOSSProperties {
|
||||||
|
private String endpoint;
|
||||||
|
private String accessKeyId;
|
||||||
|
private String accessKeySecret;
|
||||||
|
private String bucketName;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.vksfeng.quan.result;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class PageResult {
|
||||||
|
private Integer totalPage;
|
||||||
|
private Integer total;
|
||||||
|
private List list;
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package com.vksfeng.quan.util;
|
||||||
|
|
||||||
|
import com.aliyun.oss.*;
|
||||||
|
import com.vksfeng.quan.properties.AliOSSProperties;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class AliOSSUtils {
|
||||||
|
|
||||||
|
private String endpoint;
|
||||||
|
private String accessKeyId;
|
||||||
|
private String accessKeySecret;
|
||||||
|
private String bucketName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* @param objectName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String upload(byte[] bytes, String objectName) {
|
||||||
|
|
||||||
|
OSS ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建PutObject请求。
|
||||||
|
ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
|
||||||
|
} catch (OSSException oe) {
|
||||||
|
System.out.println("Caught an OSSException, which means your request made it to OSS, "
|
||||||
|
+ "but was rejected with an error response for some reason.");
|
||||||
|
System.out.println("Error Message:" + oe.getErrorMessage());
|
||||||
|
System.out.println("Error Code:" + oe.getErrorCode());
|
||||||
|
System.out.println("Request ID:" + oe.getRequestId());
|
||||||
|
System.out.println("Host ID:" + oe.getHostId());
|
||||||
|
} catch (ClientException ce) {
|
||||||
|
System.out.println("Caught an ClientException, which means the client encountered "
|
||||||
|
+ "a serious internal problem while trying to communicate with OSS, "
|
||||||
|
+ "such as not being able to access the network.");
|
||||||
|
System.out.println("Error Message:" + ce.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (ossClient != null) {
|
||||||
|
ossClient.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//文件访问路径规则 https://BucketName.Endpoint/ObjectName
|
||||||
|
StringBuilder stringBuilder = new StringBuilder("https://");
|
||||||
|
stringBuilder
|
||||||
|
.append(bucketName)
|
||||||
|
.append(".")
|
||||||
|
.append(endpoint)
|
||||||
|
.append("/")
|
||||||
|
.append(objectName);
|
||||||
|
|
||||||
|
log.info("文件上传到:{}", stringBuilder.toString());
|
||||||
|
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
|||||||
|
//package com.vksfeng.quan.util;
|
||||||
|
//
|
||||||
|
//import cn.hutool.core.date.DateUtil;
|
||||||
|
//import cn.hutool.core.io.FileUtil;
|
||||||
|
//import cn.hutool.core.util.IdUtil;
|
||||||
|
//import io.minio.*;
|
||||||
|
//import io.minio.errors.*;
|
||||||
|
//import io.minio.http.Method;
|
||||||
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
|
//import org.apache.tomcat.util.http.fileupload.FileUploadException;
|
||||||
|
//import org.springframework.stereotype.Component;
|
||||||
|
//import org.springframework.web.multipart.MultipartFile;
|
||||||
|
//
|
||||||
|
//import java.io.IOException;
|
||||||
|
//import java.security.InvalidKeyException;
|
||||||
|
//import java.security.NoSuchAlgorithmException;
|
||||||
|
//import java.time.LocalDateTime;
|
||||||
|
//import java.util.concurrent.TimeUnit;
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//@Slf4j
|
||||||
|
//@Component
|
||||||
|
//public class MinioUtils {
|
||||||
|
//
|
||||||
|
// private final MinioClient minioClient;
|
||||||
|
// private final String bucketName;
|
||||||
|
//
|
||||||
|
// public MinioUtils(MinioClient minioClient, String bucketName) {
|
||||||
|
// this.minioClient = minioClient;
|
||||||
|
// this.bucketName = bucketName;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private final int presignedUrlExpireDay = 7;
|
||||||
|
//
|
||||||
|
// public String uploadFile(MultipartFile file) throws FileUploadException {
|
||||||
|
// try {
|
||||||
|
// String objectId = generateFileName(file);
|
||||||
|
// uploadToMinio(file, objectId);
|
||||||
|
// String url = generatePresignedUrl(objectId);
|
||||||
|
// if (url == null || url.isEmpty()) {
|
||||||
|
// throw new FileUploadException("文件上传成功但无法生成 URL");
|
||||||
|
// }
|
||||||
|
// return url;
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("MinIO 文件上传失败", e);
|
||||||
|
// throw new FileUploadException("文件上传失败", e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private String generateFileName(MultipartFile file) {
|
||||||
|
// String originalFilename = file.getOriginalFilename();
|
||||||
|
// String suffix = (originalFilename != null) ? FileUtil.getSuffix(originalFilename) : "";
|
||||||
|
// suffix = suffix.isEmpty() ? "bin" : suffix;
|
||||||
|
// String uuid = IdUtil.simpleUUID();
|
||||||
|
// return DateUtil.format(LocalDateTime.now(), "yyyy/MM/dd") + "/" + uuid + "." + suffix;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void uploadToMinio(MultipartFile file, String objectId) throws IOException, MinioException, NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
// try {
|
||||||
|
// PutObjectArgs putArgs = PutObjectArgs.builder()
|
||||||
|
// .bucket(bucketName)
|
||||||
|
// .object(objectId)
|
||||||
|
// .contentType(file.getContentType())
|
||||||
|
// .stream(file.getInputStream(), file.getSize(), -1)
|
||||||
|
// .build();
|
||||||
|
// minioClient.putObject(putArgs);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
//// throw new MinioException();
|
||||||
|
// throw e;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private String generatePresignedUrl(String objectId) throws MinioException, IOException, NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
// try {
|
||||||
|
// GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
|
||||||
|
// .bucket(bucketName)
|
||||||
|
// .object(objectId)
|
||||||
|
// .method(Method.GET)
|
||||||
|
// .expiry(presignedUrlExpireDay, TimeUnit.DAYS)
|
||||||
|
// .build();
|
||||||
|
// return minioClient.getPresignedObjectUrl(args);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// throw e;
|
||||||
|
//// throw new MinioException();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.vksfeng.quan.util;
|
||||||
|
|
||||||
|
public abstract class RegexPatterns {
|
||||||
|
/**
|
||||||
|
* 手机号正则
|
||||||
|
*/
|
||||||
|
public static final String PHONE_REGEX = "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";
|
||||||
|
/**
|
||||||
|
* 邮箱正则
|
||||||
|
*/
|
||||||
|
public static final String EMAIL_REGEX = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";
|
||||||
|
/**
|
||||||
|
* 密码正则。4~32位的字母、数字、下划线
|
||||||
|
*/
|
||||||
|
public static final String PASSWORD_REGEX = "^\\w{4,32}$";
|
||||||
|
/**
|
||||||
|
* 验证码正则, 6位数字或字母
|
||||||
|
*/
|
||||||
|
public static final String VERIFY_CODE_REGEX = "^[a-zA-Z\\d]{6}$";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.vksfeng</groupId>
|
||||||
|
<artifactId>quan</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>quan-gateway</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.vksfeng;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("Hello world!");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.vksfeng</groupId>
|
||||||
|
<artifactId>quan</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>quan-server</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.vksfeng</groupId>
|
||||||
|
<artifactId>quan-common</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis.spring.boot</groupId>
|
||||||
|
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 用于json的序列化 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
|
||||||
|
<!-- jwt 用于鉴权系统 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-mail</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 集成redis依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-pool2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.lettuce</groupId>
|
||||||
|
<artifactId>lettuce-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
|
<version>2.7.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.aliyun.oss</groupId>
|
||||||
|
<artifactId>aliyun-sdk-oss</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.vksfeng.quan;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableConfigurationProperties
|
||||||
|
@EnableScheduling
|
||||||
|
public class QuanApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(QuanApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.vksfeng.quan.achievement.controller;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.achievement.pojo.dto.AchievementDTO;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.vo.UserAchievementVO;
|
||||||
|
import com.vksfeng.quan.achievement.service.AchievementService;
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/achievement")
|
||||||
|
public class AchievementController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AchievementService achievementService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取用户成就")
|
||||||
|
@GetMapping
|
||||||
|
public Result<List<UserAchievementVO>> getUserAchievement() {
|
||||||
|
return achievementService.getUserAchievement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "插入成就(管理员接口)")
|
||||||
|
@PostMapping("/admin/insert")
|
||||||
|
public Result insertAchievement(@RequestBody AchievementDTO achievementDTO) {
|
||||||
|
return achievementService.insertAchievement(achievementDTO);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.vksfeng.quan.achievement.mapper;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.achievement.pojo.entity.Achievement;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface AchievementMapper {
|
||||||
|
List<Achievement> findByType(String type);
|
||||||
|
|
||||||
|
void insert(Achievement achievement);
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.vksfeng.quan.achievement.mapper;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.achievement.pojo.entity.UserAchievement;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.vo.UserAchievementVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface UserAchievementMapper {
|
||||||
|
// 判断用户是否已经获得该成就
|
||||||
|
boolean exists(Long userId, Long achievementId);
|
||||||
|
|
||||||
|
// 插入用户获得成就记录
|
||||||
|
void insert(UserAchievement userAchievement);
|
||||||
|
|
||||||
|
List<UserAchievementVO> getUserAchievement(Long userId);
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.vksfeng.quan.achievement.pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AchievementDTO {
|
||||||
|
private String code;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String iconUrl;
|
||||||
|
private String type;
|
||||||
|
private Map<String, Object> ruleJson;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.vksfeng.quan.achievement.pojo.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Achievement {
|
||||||
|
private Long id;
|
||||||
|
private String code;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String iconUrl;
|
||||||
|
private String type;
|
||||||
|
private String ruleJson;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.vksfeng.quan.achievement.pojo.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AchievementTriggerEvent {
|
||||||
|
private Long userId;
|
||||||
|
private String type;
|
||||||
|
private Map<String, Object> payload;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.vksfeng.quan.achievement.pojo.entity;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class RuleCondition {
|
||||||
|
private String field;
|
||||||
|
private String op;
|
||||||
|
private Object value;
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.vksfeng.quan.achievement.pojo.entity;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class RuleJson {
|
||||||
|
private String type;
|
||||||
|
private List<RuleCondition> conditions;
|
||||||
|
private String logic = "AND"; // 默认为AND
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.vksfeng.quan.achievement.pojo.entity;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class UserAchievement {
|
||||||
|
private Long id;
|
||||||
|
private Long userId;
|
||||||
|
private Long achievementId;
|
||||||
|
private LocalDateTime achievedAt;
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.vksfeng.quan.achievement.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class UserAchievementVO {
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String iconUrl;
|
||||||
|
private LocalDateTime achievedAt;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.vksfeng.quan.achievement.service;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.achievement.pojo.dto.AchievementDTO;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.vo.UserAchievementVO;
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface AchievementService {
|
||||||
|
Result<List<UserAchievementVO>> getUserAchievement();
|
||||||
|
|
||||||
|
Result insertAchievement(AchievementDTO achievementDTO);
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.vksfeng.quan.achievement.service.Impl;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.vksfeng.quan.achievement.mapper.AchievementMapper;
|
||||||
|
import com.vksfeng.quan.achievement.mapper.UserAchievementMapper;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.dto.AchievementDTO;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.entity.Achievement;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.vo.UserAchievementVO;
|
||||||
|
import com.vksfeng.quan.achievement.service.AchievementService;
|
||||||
|
import com.vksfeng.quan.context.BaseContext;
|
||||||
|
import com.vksfeng.quan.exception.NotLoginException;
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AchievementServiceImpl implements AchievementService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserAchievementMapper userAchievementMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AchievementMapper achievementMapper;
|
||||||
|
|
||||||
|
public Long getUserId() {
|
||||||
|
Long userId = BaseContext.getCurrentId();
|
||||||
|
if (userId == null) {
|
||||||
|
throw new NotLoginException("用户未登录");
|
||||||
|
}
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<List<UserAchievementVO>> getUserAchievement() {
|
||||||
|
List<UserAchievementVO> userAchievementVOList = userAchievementMapper.getUserAchievement(getUserId());
|
||||||
|
return Result.success(userAchievementVOList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result insertAchievement(AchievementDTO achievementDTO) {
|
||||||
|
if (getUserId() != 1) {
|
||||||
|
return Result.error("权限不足");
|
||||||
|
}
|
||||||
|
Achievement achievement = new Achievement();
|
||||||
|
BeanUtils.copyProperties(achievementDTO, achievement);
|
||||||
|
try {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
achievement.setRuleJson(mapper.writeValueAsString(achievementDTO.getRuleJson()));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new RuntimeException("规则 JSON 序列化失败", e);
|
||||||
|
}
|
||||||
|
achievement.setCreatedAt(LocalDateTime.now());
|
||||||
|
achievementMapper.insert(achievement);
|
||||||
|
return Result.success("插入成功");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.vksfeng.quan.achievement.util;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.achievement.pojo.entity.AchievementTriggerEvent;
|
||||||
|
import com.vksfeng.quan.mapper.FriendshipMapper;
|
||||||
|
import com.vksfeng.quan.mapper.ObjectiveMapper;
|
||||||
|
import com.vksfeng.quan.mapper.TaskMapper;
|
||||||
|
import com.vksfeng.quan.service.ResourceHubService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.vksfeng.quan.constant.AchievementConstant.*;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class AchievementContextBuilder {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TaskMapper taskMapper;
|
||||||
|
@Autowired
|
||||||
|
private FriendshipMapper friendshipMapper;
|
||||||
|
@Autowired
|
||||||
|
private ObjectiveMapper objectiveMapper;
|
||||||
|
@Autowired
|
||||||
|
private ResourceHubService resourceHubService;
|
||||||
|
|
||||||
|
public Map<String, Object> buildContext(AchievementTriggerEvent event) {
|
||||||
|
Long userId = event.getUserId();
|
||||||
|
String type = event.getType();
|
||||||
|
|
||||||
|
Map<String, Object> ctx = new HashMap<>();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case OBJECTIVE_ADDED -> {
|
||||||
|
ctx.put("createdObjectives", objectiveMapper.getObjectiveCount(userId));
|
||||||
|
}
|
||||||
|
case TASK_COMPLETED -> {
|
||||||
|
ctx.put("completedTasks", taskMapper.getCompletedTaskCount(userId));
|
||||||
|
}
|
||||||
|
case FRIEND_ADDED -> {
|
||||||
|
ctx.put("friendsCount", friendshipMapper.countFriends(userId));
|
||||||
|
}
|
||||||
|
case RESOURCE_SHARED -> {
|
||||||
|
ctx.put("sharedResources", resourceHubService.getUserResourceCount(userId));
|
||||||
|
ctx.put("resourceLikesReceived", resourceHubService.getUserReceivedLikes(userId));
|
||||||
|
ctx.put("resourceCommentsReceived", resourceHubService.getUserReceivedComments(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扩展类型
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.vksfeng.quan.achievement.util;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.vksfeng.quan.constant.RedisConstant.ACHIEVEMENT_STREAM_KEY;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class AchievementEventPublisher {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StringRedisTemplate redisTemplate;
|
||||||
|
|
||||||
|
public void publish(Long userId, String type, Map<String, Object> payload) {
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
try {
|
||||||
|
Map<String, String> message = new HashMap<>();
|
||||||
|
message.put("userId", userId.toString());
|
||||||
|
message.put("type", type);
|
||||||
|
message.put("payload", objectMapper.writeValueAsString(payload == null ? Map.of() : payload));
|
||||||
|
|
||||||
|
redisTemplate.opsForStream().add(ACHIEVEMENT_STREAM_KEY, message);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 生产环境中可以 log.error + 监控上报
|
||||||
|
throw new RuntimeException("发布成就事件失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
|||||||
|
package com.vksfeng.quan.achievement.util;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.vksfeng.quan.achievement.mapper.AchievementMapper;
|
||||||
|
import com.vksfeng.quan.achievement.mapper.UserAchievementMapper;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.entity.Achievement;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.entity.AchievementTriggerEvent;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.entity.RuleJson;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.entity.UserAchievement;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.connection.stream.MapRecord;
|
||||||
|
import org.springframework.data.redis.connection.stream.StreamOffset;
|
||||||
|
import org.springframework.data.redis.connection.stream.StreamReadOptions;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import static com.vksfeng.quan.constant.RedisConstant.ACHIEVEMENT_STREAM_KEY;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class AchievementListener {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StringRedisTemplate redisTemplate;
|
||||||
|
@Autowired
|
||||||
|
private AchievementMapper achievementMapper;
|
||||||
|
@Autowired
|
||||||
|
private UserAchievementMapper userAchievementMapper;
|
||||||
|
@Autowired
|
||||||
|
private RuleEvaluator ruleEvaluator;
|
||||||
|
@Autowired
|
||||||
|
private AchievementContextBuilder contextBuilder;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void startListen() {
|
||||||
|
Executors.newSingleThreadExecutor().submit(() -> {
|
||||||
|
log.info("成就监听器启动...");
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
List<MapRecord<String, Object, Object>> records =
|
||||||
|
redisTemplate.opsForStream().read(StreamReadOptions.empty().block(Duration.ofSeconds(2)).count(10),
|
||||||
|
StreamOffset.latest(ACHIEVEMENT_STREAM_KEY));
|
||||||
|
|
||||||
|
if (records != null) {
|
||||||
|
for (MapRecord<String, Object, Object> record : records) {
|
||||||
|
handleRecord(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Redis Stream 成就事件监听失败:", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRecord(MapRecord<String, Object, Object> record) throws JsonProcessingException {
|
||||||
|
Map<Object, Object> value = record.getValue();
|
||||||
|
AchievementTriggerEvent event = parseEvent(value);
|
||||||
|
|
||||||
|
log.info("收到行为事件:{}", event);
|
||||||
|
|
||||||
|
List<Achievement> achievements = achievementMapper.findByType(event.getType());
|
||||||
|
|
||||||
|
if (achievements.isEmpty()) {
|
||||||
|
log.info("没有找到类型为 {} 的成就", event.getType());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Achievement achievement : achievements) {
|
||||||
|
// 是否已经达成
|
||||||
|
if (userAchievementMapper.exists(event.getUserId(), achievement.getId())) continue;
|
||||||
|
|
||||||
|
// 解析规则 JSON
|
||||||
|
RuleJson rule = null;
|
||||||
|
try {
|
||||||
|
rule = new ObjectMapper().readValue(achievement.getRuleJson(), RuleJson.class);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造上下文
|
||||||
|
Map<String, Object> context = contextBuilder.buildContext(event);
|
||||||
|
|
||||||
|
// 规则判断
|
||||||
|
boolean passed = ruleEvaluator.evaluate(rule, context);
|
||||||
|
|
||||||
|
if (passed) {
|
||||||
|
log.info("用户 {} 达成成就:{}", event.getUserId(), achievement.getCode());
|
||||||
|
|
||||||
|
userAchievementMapper.insert(new UserAchievement(null, event.getUserId(), achievement.getId(), LocalDateTime.now()));
|
||||||
|
// TODO: 推送通知、插入日志、广播给好友
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AchievementTriggerEvent parseEvent(Map<Object, Object> value) throws JsonProcessingException {
|
||||||
|
Long userId = Long.parseLong(value.get("userId").toString());
|
||||||
|
String type = value.get("type").toString();
|
||||||
|
String payloadStr = value.getOrDefault("payload", "{}").toString();
|
||||||
|
Map<String, Object> payload = new ObjectMapper().readValue(payloadStr, new TypeReference<>() {});
|
||||||
|
AchievementTriggerEvent event = new AchievementTriggerEvent();
|
||||||
|
event.setUserId(userId);
|
||||||
|
event.setType(type);
|
||||||
|
event.setPayload(payload);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.vksfeng.quan.achievement.util;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.achievement.pojo.entity.RuleCondition;
|
||||||
|
import com.vksfeng.quan.achievement.pojo.entity.RuleJson;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class RuleEvaluator {
|
||||||
|
|
||||||
|
public boolean evaluate(RuleJson rule, Map<String, Object> context) {
|
||||||
|
|
||||||
|
List<Boolean> results = new ArrayList<>();
|
||||||
|
|
||||||
|
for (RuleCondition cond : rule.getConditions()) {
|
||||||
|
Object actual = context.get(cond.getField());
|
||||||
|
Object expected = cond.getValue();
|
||||||
|
boolean matched = compare(actual, cond.getOp(), expected);
|
||||||
|
results.add(matched);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "AND".equals(rule.getLogic())
|
||||||
|
? results.stream().allMatch(Boolean::booleanValue)
|
||||||
|
: results.stream().anyMatch(Boolean::booleanValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean compare(Object actual, String op, Object expected) {
|
||||||
|
if (actual instanceof Number && expected instanceof Number) {
|
||||||
|
double a = ((Number) actual).doubleValue();
|
||||||
|
double b = ((Number) expected).doubleValue();
|
||||||
|
return switch (op) {
|
||||||
|
case "==" -> a == b;
|
||||||
|
case "!=" -> a != b;
|
||||||
|
case ">" -> a > b;
|
||||||
|
case ">=" -> a >= b;
|
||||||
|
case "<" -> a < b;
|
||||||
|
case "<=" -> a <= b;
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return switch (op) {
|
||||||
|
case "==" -> Objects.equals(actual, expected);
|
||||||
|
case "!=" -> !Objects.equals(actual, expected);
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.analysis_pojo.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TaskCompletionDay {
|
||||||
|
private LocalDate date; // 日期
|
||||||
|
private Integer count; // 当天完成任务次数
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.vksfeng.quan.analysis_pojo.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class HeatMapVO {
|
||||||
|
private LocalDate date;
|
||||||
|
private Integer count;
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.analysis_pojo.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LeaderboardCountVO {
|
||||||
|
private Long userId;
|
||||||
|
private String username;
|
||||||
|
private String avatarUrl;
|
||||||
|
private Integer completeCount;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.vksfeng.quan.analysis_pojo.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TaskCompletionRateVO {
|
||||||
|
private Integer completeCount;
|
||||||
|
private Integer totalCount;
|
||||||
|
private Double completionRate;
|
||||||
|
private LocalDate date;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.vksfeng.quan.analysis_pojo.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class UserProfileForPeerVO {
|
||||||
|
private Integer objectiveCount;
|
||||||
|
private Integer objectiveCompletedCount;
|
||||||
|
private Integer taskCount;
|
||||||
|
private Integer taskDoneCompletedCount;
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
//package com.vksfeng.quan.config;
|
||||||
|
//
|
||||||
|
//import com.vksfeng.quan.util.MinioUtils;
|
||||||
|
//import io.minio.MinioClient;
|
||||||
|
//import org.springframework.beans.factory.annotation.Value;
|
||||||
|
//import org.springframework.context.annotation.Bean;
|
||||||
|
//import org.springframework.context.annotation.Configuration;
|
||||||
|
//
|
||||||
|
//@Configuration
|
||||||
|
//public class MinioConfig {
|
||||||
|
//
|
||||||
|
// @Value("${minio.endpoint}")
|
||||||
|
// private String minioEndpoint;
|
||||||
|
//
|
||||||
|
// @Value("${minio.access-key}")
|
||||||
|
// private String minioAccessKey;
|
||||||
|
//
|
||||||
|
// @Value("${minio.secret-key}")
|
||||||
|
// private String minioSecretKey;
|
||||||
|
//
|
||||||
|
// @Value("${minio.bucket-name}")
|
||||||
|
// private String bucketName;
|
||||||
|
//
|
||||||
|
// @Bean
|
||||||
|
// public MinioClient getMinioClient() {
|
||||||
|
// return MinioClient.builder()
|
||||||
|
// .endpoint(minioEndpoint)
|
||||||
|
// .credentials(minioAccessKey, minioSecretKey)
|
||||||
|
// .build();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Bean
|
||||||
|
// public MinioUtils minioUtils(MinioClient minioClient) {
|
||||||
|
// return new MinioUtils(minioClient, bucketName);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.vksfeng.quan.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class RestConfig {
|
||||||
|
@Bean
|
||||||
|
public RestTemplate restTemplate() {
|
||||||
|
return new RestTemplate();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.vksfeng.quan.config;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.interceptor.JwtTokenInterceptor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
public class WebMvcConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtTokenInterceptor jwtTokenInterceptor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
log.info("开始注册自定义拦截器...");
|
||||||
|
registry.addInterceptor(jwtTokenInterceptor)
|
||||||
|
.addPathPatterns("/**")
|
||||||
|
.excludePathPatterns("/login", "/register", "/send-code")
|
||||||
|
.excludePathPatterns("/swagger-ui.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CorsFilter corsFilter() {
|
||||||
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
|
// 允许所有来源的跨域请求
|
||||||
|
config.addAllowedOriginPattern("*");
|
||||||
|
config.addAllowedHeader("*");
|
||||||
|
config.addAllowedMethod("*");
|
||||||
|
config.setAllowCredentials(true);
|
||||||
|
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
source.registerCorsConfiguration("/**", config);
|
||||||
|
|
||||||
|
return new CorsFilter(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// swagger
|
||||||
|
@Override
|
||||||
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
|
registry.addResourceHandler("/swagger-ui/**")
|
||||||
|
.addResourceLocations("classpath:/META-INF/resources/webjars/springdoc-openapi-ui/")
|
||||||
|
.resourceChain(false);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.vksfeng.quan.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class WebSocketConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ServerEndpointExporter serverEndpointExporter() {
|
||||||
|
return new ServerEndpointExporter();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.vksfeng.quan.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import com.vksfeng.quan.analysis_pojo.vo.HeatMapVO;
|
||||||
|
import com.vksfeng.quan.analysis_pojo.vo.LeaderboardCountVO;
|
||||||
|
import com.vksfeng.quan.analysis_pojo.vo.TaskCompletionRateVO;
|
||||||
|
import com.vksfeng.quan.analysis_pojo.vo.UserProfileForPeerVO;
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.vo.TaskVO;
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
import com.vksfeng.quan.service.AnalysisService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "分析统计模块")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/analysis")
|
||||||
|
@Slf4j
|
||||||
|
public class AnalysisController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AnalysisService analysisService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取月度任务日历")
|
||||||
|
@GetMapping("/calendar/tasks/month")
|
||||||
|
public Result<List<TaskVO>> getMonthlyTasksCalendar(@RequestParam Long userId, @RequestParam int year, @RequestParam int month) {
|
||||||
|
if (month < 1 || month > 12) {
|
||||||
|
return Result.error("月份不合法");
|
||||||
|
}
|
||||||
|
List<TaskVO> tasks = analysisService.getMonthlyTasksCalendar(userId, year, month);
|
||||||
|
return Result.success(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取年度任务热力图")
|
||||||
|
@GetMapping("/heatmap/tasks")
|
||||||
|
public Result<List<HeatMapVO>> getYearlyTasksHeatMap(@RequestParam Long userId, @RequestParam int year) {
|
||||||
|
return analysisService.getYearlyTasksHeatMap(userId, year);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取任务排行榜")
|
||||||
|
@GetMapping("/leaderboard/task")
|
||||||
|
public Result<List<LeaderboardCountVO>> getTaskLeaderboard(@RequestParam String period) {
|
||||||
|
if (period.equals("weekly")) {
|
||||||
|
return Result.success(analysisService.getWeeklyTaskLeaderboard());
|
||||||
|
} else if (period.equals("monthly")) {
|
||||||
|
return Result.success(analysisService.getMonthlyTaskLeaderboard());
|
||||||
|
} else {
|
||||||
|
return Result.error("参数不合法");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取好友任务目标状态")
|
||||||
|
@GetMapping("/friend/{friendId}/objhubstatus")
|
||||||
|
public Result<UserProfileForPeerVO> getFriendObjectiveHubStatus(@PathVariable Long friendId) {
|
||||||
|
UserProfileForPeerVO userProfileForPeerVO = analysisService.getFriendObjectiveHubStatus(friendId);
|
||||||
|
return Result.success(userProfileForPeerVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取近一周每日任务完成率")
|
||||||
|
@GetMapping("/completion/task/weekly")
|
||||||
|
public Result<List<TaskCompletionRateVO>> getWeeklyTaskCompletionRate() {
|
||||||
|
return analysisService.getWeeklyTaskCompletionRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "用户活跃度信息")
|
||||||
|
@GetMapping("/activity")
|
||||||
|
public Result getUserActivity() {
|
||||||
|
return analysisService.getUserActivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取用户社交成就")
|
||||||
|
@GetMapping("/social/achievement")
|
||||||
|
public Result getSocialAchievement() {
|
||||||
|
return analysisService.getUserSocialAchievement();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.vksfeng.quan.controller;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.dto.FeedCommentDTO;
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.vo.FeedCommentVO;
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.vo.FeedVO;
|
||||||
|
import com.vksfeng.quan.result.PageResult;
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
import com.vksfeng.quan.service.FeedService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
// TODO 权限管理
|
||||||
|
@Tag(name = "动态模块")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/feed")
|
||||||
|
public class FeedController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FeedService feedService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取动态列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<PageResult> getFeedList(@RequestParam Integer page, @RequestParam Integer pageSize) {
|
||||||
|
PageResult pageResult = feedService.getFeedList(page, pageSize);
|
||||||
|
return Result.success(pageResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "点赞动态")
|
||||||
|
@PostMapping("/like")
|
||||||
|
public Result like(@RequestBody Map<String, Long> request) {
|
||||||
|
feedService.like(request.get("feed_id"));
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "取消点赞")
|
||||||
|
@DeleteMapping("/{feedId}/like")
|
||||||
|
public Result unlike(@PathVariable Long feedId) {
|
||||||
|
feedService.unlike(feedId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "评论动态")
|
||||||
|
@PostMapping("/comment")
|
||||||
|
public Result comment(@RequestBody FeedCommentDTO feedCommentDTO) {
|
||||||
|
feedService.comment(feedCommentDTO);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取动态评论列表")
|
||||||
|
@GetMapping("/comments")
|
||||||
|
public Result<List<FeedCommentVO>> getCommentList(@RequestParam Long feedId) {
|
||||||
|
List<FeedCommentVO> commentList = feedService.getCommentList(feedId);
|
||||||
|
return Result.success(commentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "动态信息推送")
|
||||||
|
@GetMapping("/recent")
|
||||||
|
public Result<List<FeedVO>> getRecentFeed() {
|
||||||
|
return feedService.getRecentFeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.vksfeng.quan.controller;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
import com.vksfeng.quan.service.FileService;
|
||||||
|
import org.apache.tomcat.util.http.fileupload.FileUploadException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/files")
|
||||||
|
public class FileController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FileService fileService;
|
||||||
|
|
||||||
|
@PostMapping("/upload")
|
||||||
|
public Result uploadAvatar(MultipartFile file) throws FileUploadException {
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
return Result.error("文件为空");
|
||||||
|
}
|
||||||
|
return fileService.uploadFile(file);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package com.vksfeng.quan.controller;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.context.BaseContext;
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.dto.FriendshipDTO;
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.vo.UserSearchVO;
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
import com.vksfeng.quan.service.FriendshipService;
|
||||||
|
import com.vksfeng.quan.user_pojo.vo.UserVO;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "好友管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/friends")
|
||||||
|
@Slf4j
|
||||||
|
public class FriendshipController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FriendshipService friendshipService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取好友列表")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public Result<List<UserVO>> getFriends() {
|
||||||
|
Long userId = BaseContext.getCurrentId();
|
||||||
|
if (userId == null) {
|
||||||
|
return Result.error("用户未登录");
|
||||||
|
}
|
||||||
|
log.info("获取好友列表,userId:{}", userId);
|
||||||
|
List<UserVO> friends = friendshipService.getFriends(userId);
|
||||||
|
return Result.success(friends);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取好友申请信息")
|
||||||
|
@GetMapping("/requestlist")
|
||||||
|
public Result<List<UserVO>> getFriendRequests() {
|
||||||
|
Long userId = BaseContext.getCurrentId();
|
||||||
|
if (userId == null) {
|
||||||
|
return Result.error("用户未登录");
|
||||||
|
}
|
||||||
|
List<UserVO> request = friendshipService.getRequests(userId);
|
||||||
|
return Result.success(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "搜索用户以添加好友")
|
||||||
|
@GetMapping("/search")
|
||||||
|
public Result<UserSearchVO> searchUser(@RequestParam Long id) {
|
||||||
|
return friendshipService.searchUser(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "添加好友")
|
||||||
|
@PostMapping("/request")
|
||||||
|
public Result<String> addFriendRequest(@RequestBody FriendshipDTO friendshipDTO) {
|
||||||
|
Long userId = BaseContext.getCurrentId();
|
||||||
|
if (userId == null) {
|
||||||
|
return Result.error("用户未登录");
|
||||||
|
}
|
||||||
|
if (userId.equals(friendshipDTO.getUserId())) {
|
||||||
|
return Result.error("你已经是自己的好朋友啦,就不要加自己了");
|
||||||
|
}
|
||||||
|
friendshipDTO.setUserId(userId);
|
||||||
|
friendshipDTO.setStatus("pending");
|
||||||
|
friendshipService.addFriendRequest(friendshipDTO);
|
||||||
|
return Result.success("添加申请已发送");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "同意好友请求")
|
||||||
|
@PostMapping("/accept")
|
||||||
|
public Result<String> acceptFriend(@RequestBody FriendshipDTO friendshipDTO) {
|
||||||
|
Long userId = BaseContext.getCurrentId();
|
||||||
|
if (userId == null) {
|
||||||
|
return Result.error("用户未登录");
|
||||||
|
}
|
||||||
|
friendshipDTO.setFriendId(userId);
|
||||||
|
friendshipDTO.setStatus("accepted");
|
||||||
|
friendshipService.acceptFriendRequest(friendshipDTO);
|
||||||
|
return Result.success("好友申请已同意");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "拒绝好友请求/删除好友")
|
||||||
|
@PostMapping("/remove")
|
||||||
|
public Result<String> removeFriend(@RequestBody FriendshipDTO friendshipDTO) {
|
||||||
|
Long userId = BaseContext.getCurrentId();
|
||||||
|
if (userId == null) {
|
||||||
|
return Result.error("用户未登录");
|
||||||
|
}
|
||||||
|
friendshipDTO.setUserId(userId);
|
||||||
|
friendshipService.removeFriend(friendshipDTO);
|
||||||
|
return Result.success("好友申请已拒绝");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "附近的人推荐")
|
||||||
|
@GetMapping("/nearby")
|
||||||
|
public Result<List<UserSearchVO>> nearbyUsers() {
|
||||||
|
return friendshipService.getNearbyUsers();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.vksfeng.quan.controller;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.resourcehub_pojo.vo.ImageResult;
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
import com.vksfeng.quan.service.ImageProxyService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class ImageProxyController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ImageProxyService imageProxyService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片代理接口
|
||||||
|
* @param url 外部图片 URL
|
||||||
|
* @return 返回 Result 封装的图片数据或错误信息
|
||||||
|
*/
|
||||||
|
@GetMapping("/img-proxy")
|
||||||
|
public Result<ImageResult> getImage(@RequestParam String url) {
|
||||||
|
if (url == null || url.isEmpty()) {
|
||||||
|
return Result.error("缺少 URL 参数");
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageProxyService.getImage(url);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.vksfeng.quan.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONArray;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.obengine_pojo.dto.ObjectiveWithFeedbackDTO;
|
||||||
|
import com.vksfeng.quan.obengine_pojo.vo.FollowUpQuestionVO;
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.dto.ObjectiveDTO;
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
import com.vksfeng.quan.service.SparkAIService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.*;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "OB引擎模块")
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/obengine")
|
||||||
|
public class ObEngineController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SparkAIService sparkAIService;
|
||||||
|
|
||||||
|
@Operation(summary = "生成跟进问题")
|
||||||
|
@PostMapping("/generateFollowup")
|
||||||
|
public Result<List<FollowUpQuestionVO>> generateFollowup(@RequestBody ObjectiveDTO objectiveDTO) {
|
||||||
|
return sparkAIService.generateFollowupQuestions(objectiveDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "生成详细计划")
|
||||||
|
@PostMapping("/generateDetailedPlan")
|
||||||
|
public Result generateDetailedPlan(@RequestBody ObjectiveWithFeedbackDTO objectiveWithFeedbackDTO) {
|
||||||
|
sparkAIService.generateDetailedPlan(objectiveWithFeedbackDTO);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,127 @@
|
|||||||
|
package com.vksfeng.quan.controller;
|
||||||
|
|
||||||
|
import cn.hutool.db.Page;
|
||||||
|
import com.vksfeng.quan.resourcehub_pojo.dto.ResourceDTO;
|
||||||
|
import com.vksfeng.quan.resourcehub_pojo.vo.ResourceCommentVO;
|
||||||
|
import com.vksfeng.quan.resourcehub_pojo.vo.ResourceVO;
|
||||||
|
import com.vksfeng.quan.result.PageResult;
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
import com.vksfeng.quan.service.ResourceHubService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "资源中心模块")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/resources")
|
||||||
|
@Slf4j
|
||||||
|
public class ResourceHubController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResourceHubService resourceHubService;
|
||||||
|
|
||||||
|
@Operation(summary = "创建资源")
|
||||||
|
@PostMapping
|
||||||
|
public Result<ResourceVO> createResource(@RequestBody ResourceDTO resourceDTO) {
|
||||||
|
ResourceVO resourceVO = resourceHubService.createResource(resourceDTO);
|
||||||
|
return Result.success(resourceVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取资源")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<ResourceVO> getResource(@PathVariable Long id) {
|
||||||
|
ResourceVO resourceVO = resourceHubService.getResource(id);
|
||||||
|
if (resourceVO == null) {
|
||||||
|
return Result.error("资源不存在");
|
||||||
|
}
|
||||||
|
return Result.success(resourceVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取资源列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<PageResult> getResourceList(@RequestParam Integer page, @RequestParam Integer pageSize, @RequestParam(required = false) String category) {
|
||||||
|
PageResult pageResult = resourceHubService.getResourceList(page, pageSize, category);
|
||||||
|
return Result.success(pageResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取推荐资源列表")
|
||||||
|
@GetMapping("/recommend")
|
||||||
|
public Result<PageResult> getRecommendResourceList() {
|
||||||
|
return resourceHubService.getRecommendResourceList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取指定id用户的资源")
|
||||||
|
@GetMapping("/user/{userId}")
|
||||||
|
public Result<PageResult> getUserResourceList(@PathVariable Long userId, @RequestParam Integer page, @RequestParam Integer pageSize, @RequestParam(required = false) String category) {
|
||||||
|
return resourceHubService.getUserResourceList(userId, page, pageSize, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Operation(summary = "删除资源")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result deleteResource(@PathVariable Long id) {
|
||||||
|
// TODO 删除关联点赞与评论等
|
||||||
|
resourceHubService.deleteResource(id);
|
||||||
|
return Result.success("删除成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "点赞资源")
|
||||||
|
@PostMapping("/{id}/like")
|
||||||
|
public Result likeResource(@PathVariable Long id) {
|
||||||
|
resourceHubService.addLike(id);
|
||||||
|
return Result.success("点赞成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "取消点赞资源")
|
||||||
|
@DeleteMapping("/{id}/like")
|
||||||
|
public Result unlikeResource(@PathVariable Long id) {
|
||||||
|
resourceHubService.removeLike(id);
|
||||||
|
return Result.success("取消点赞成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "发布评论")
|
||||||
|
@PostMapping("/{id}/comments")
|
||||||
|
public Result addComment(@PathVariable Long id, @RequestBody Map<String, String> requestBody) {
|
||||||
|
String content = requestBody.get("content");
|
||||||
|
ResourceCommentVO resourceCommentVO = resourceHubService.addComment(id, content);
|
||||||
|
if (resourceCommentVO == null) {
|
||||||
|
return Result.error("评论失败");
|
||||||
|
}
|
||||||
|
return Result.success(resourceCommentVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取指定帖子的评论")
|
||||||
|
@GetMapping("/{id}/comments")
|
||||||
|
public Result<List<ResourceCommentVO>> getComments(@PathVariable Long id) {
|
||||||
|
List<ResourceCommentVO> resourceCommentVOList = resourceHubService.getCommentsByResourceId(id);
|
||||||
|
return Result.success(resourceCommentVOList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "收藏资源")
|
||||||
|
@PostMapping("/{id}/favorite")
|
||||||
|
public Result favoriteResource(@PathVariable Long id) {
|
||||||
|
resourceHubService.addFavorite(id);
|
||||||
|
return Result.success("收藏成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "取消收藏资源")
|
||||||
|
@DeleteMapping("/{id}/favorite")
|
||||||
|
public Result unfavoriteResource(@PathVariable Long id) {
|
||||||
|
resourceHubService.removeFavorite(id);
|
||||||
|
return Result.success("取消收藏成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取收藏列表")
|
||||||
|
@GetMapping("/favorite")
|
||||||
|
public Result<PageResult> getFavoriteList(@RequestParam Integer page, @RequestParam Integer pageSize, @RequestParam(required = false) String category) {
|
||||||
|
PageResult resourceVOList = resourceHubService.getFavoriteList(page, pageSize, category);
|
||||||
|
return Result.success(resourceVOList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package com.vksfeng.quan.controller;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.dto.RecurringTaskDTO;
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.dto.SingleTaskDTO;
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.vo.TaskVO;
|
||||||
|
import com.vksfeng.quan.result.Result;
|
||||||
|
import com.vksfeng.quan.service.ObjectiveHubService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "ObjHub模块")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/tasks")
|
||||||
|
public class TaskController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectiveHubService objectiveHubService;
|
||||||
|
|
||||||
|
// ----------------------------------------创建---------------------------------------------
|
||||||
|
|
||||||
|
@Operation(summary = "创建单次任务")
|
||||||
|
@PostMapping("/single")
|
||||||
|
public Result<TaskVO> createSingleTask(@RequestBody SingleTaskDTO taskDTO) {
|
||||||
|
if (taskDTO.getObjectiveId() == null) {
|
||||||
|
return Result.error("任务不能独立存在");
|
||||||
|
}
|
||||||
|
TaskVO taskVO = objectiveHubService.createSingleTask(taskDTO);
|
||||||
|
return Result.success(taskVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建重复任务")
|
||||||
|
@PostMapping("/recurring")
|
||||||
|
public Result<TaskVO> createRecurringTask(@RequestBody RecurringTaskDTO taskDTO) {
|
||||||
|
if (taskDTO.getObjectiveId() == null) {
|
||||||
|
return Result.error("任务不能独立存在");
|
||||||
|
}
|
||||||
|
TaskVO taskVO = objectiveHubService.createRecurringTask(taskDTO);
|
||||||
|
return Result.success(taskVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------查询---------------------------------------------
|
||||||
|
|
||||||
|
@Operation(summary = "获取用户今日任务")
|
||||||
|
@GetMapping("/user/{id}/today")
|
||||||
|
public Result<List<TaskVO>> getUserTodayTasks(@PathVariable("id") Long userId) {
|
||||||
|
return Result.success(objectiveHubService.getUserTodayTasks(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据id获取任务信息")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<TaskVO> getTaskById(@PathVariable Long id) {
|
||||||
|
return Result.success(objectiveHubService.getTaskById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------更新---------------------------------------------
|
||||||
|
|
||||||
|
@Operation(summary = "更新单次任务信息")
|
||||||
|
@PutMapping("/single/{id}")
|
||||||
|
public Result updateSingleTask(@RequestBody SingleTaskDTO taskDTO, @PathVariable Long id) {
|
||||||
|
objectiveHubService.updateSingleTask(taskDTO, id);
|
||||||
|
return Result.success("任务修改成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新重复任务信息")
|
||||||
|
@PutMapping("/recurring/{id}")
|
||||||
|
public Result<TaskVO> updateRecurringTask(@RequestBody RecurringTaskDTO taskDTO, @PathVariable Long id) {
|
||||||
|
objectiveHubService.updateRecurringTask(taskDTO, id);
|
||||||
|
return Result.success("任务修改成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Operation(summary = "完成任务")
|
||||||
|
@PostMapping("/completion")
|
||||||
|
public Result completeTask(@RequestBody Map<String, Long> request) {
|
||||||
|
Long id = request.get("id");
|
||||||
|
return objectiveHubService.completeTask(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "撤销任务完成")
|
||||||
|
@DeleteMapping("/completion/{id}")
|
||||||
|
public Result deleteTaskCompletion(@PathVariable Long id) {
|
||||||
|
objectiveHubService.deleteTaskCompletion(id);
|
||||||
|
return Result.success("任务完成记录删除成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------删除---------------------------------------------
|
||||||
|
@Operation(summary = "根据id删除任务")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result deleteTask(@PathVariable Long id) {
|
||||||
|
objectiveHubService.deleteTask(id);
|
||||||
|
return Result.success("任务删除成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.vksfeng.quan.mapper;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.dto.FeedCommentDTO;
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.entity.Feed;
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.entity.FeedComment;
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.entity.FeedLike;
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.vo.FeedCommentVO;
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.vo.FeedVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface FeedMapper {
|
||||||
|
Integer count();
|
||||||
|
|
||||||
|
List<FeedVO> page(Integer offset, Integer pageSize, List<Long> userIds, Long userId);
|
||||||
|
|
||||||
|
void addFeedLike(FeedLike feedLike);
|
||||||
|
|
||||||
|
FeedVO getFeedById(Long feedId, Long userId);
|
||||||
|
|
||||||
|
void addFeedComment(FeedComment feedComment);
|
||||||
|
|
||||||
|
void removeFeedLike(Long feedId, Long userId);
|
||||||
|
|
||||||
|
List<FeedCommentVO> getCommentList(Long feedId);
|
||||||
|
|
||||||
|
void insertFeed(Feed feed);
|
||||||
|
|
||||||
|
FeedLike getFeedLike(Long feedId, Long userId);
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.vksfeng.quan.mapper;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.dto.FriendshipDTO;
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.entity.Friendship;
|
||||||
|
import com.vksfeng.quan.user_pojo.vo.UserVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface FriendshipMapper {
|
||||||
|
|
||||||
|
Integer countFriends(Long userId);
|
||||||
|
|
||||||
|
List<UserVO> list(Long userId);
|
||||||
|
|
||||||
|
void insert(Friendship friendship);
|
||||||
|
|
||||||
|
List<UserVO> getRequests(Long userId);
|
||||||
|
|
||||||
|
void update(Friendship friendship);
|
||||||
|
|
||||||
|
void remove(Long userId, Long friendId);
|
||||||
|
|
||||||
|
List<Friendship> selectByTwoId(Long aId, Long bId);
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.vksfeng.quan.mapper;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.entity.Objective;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ObjectiveMapper {
|
||||||
|
void insert(Objective objective);
|
||||||
|
|
||||||
|
Objective selectById(Long objectiveId);
|
||||||
|
|
||||||
|
void updateById(Objective objective, Long objectiveId);
|
||||||
|
|
||||||
|
void deleteById(Long objectiveId);
|
||||||
|
|
||||||
|
List<Long> getAllSubObjectivesId(Long objectiveId);
|
||||||
|
|
||||||
|
void deleteRelationByChildId(Long objectiveId);
|
||||||
|
|
||||||
|
void deleteRelationByFatherId(Long objectiveId);
|
||||||
|
|
||||||
|
List<Long> getAllObjectivesId(Long userId);
|
||||||
|
|
||||||
|
List<Objective> getAllMainObjectives(List<Long> mainObjectivesIds);
|
||||||
|
|
||||||
|
void createObjectiveRelation(Long parentObjectiveId, Long childObjectiveId);
|
||||||
|
|
||||||
|
void deleteObjectiveRelation(Long parentObjectiveId, Long childObjectiveId);
|
||||||
|
|
||||||
|
boolean isSubObjective(Long objectiveId);
|
||||||
|
|
||||||
|
Integer getObjectiveCount(Long userId);
|
||||||
|
|
||||||
|
Integer getCompletedObjectiveCount(Long userId);
|
||||||
|
|
||||||
|
void updateProgress(Double progress, Long objectiveId);
|
||||||
|
|
||||||
|
void addAiGuideForObjective(Long objectiveId, String aiGuide);
|
||||||
|
|
||||||
|
String getAiGuideForObjective(Long objectiveId);
|
||||||
|
|
||||||
|
void deleteAiGuideForObjective(Long objectiveId);
|
||||||
|
|
||||||
|
void completeObjectiveById(LocalDateTime now, Long objectiveId);
|
||||||
|
|
||||||
|
Long getFatherObjectiveIdBySubObjectiveId(Long objectiveId);
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.vksfeng.quan.mapper;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.resourcehub_pojo.entity.Resource;
|
||||||
|
import com.vksfeng.quan.resourcehub_pojo.entity.ResourceComment;
|
||||||
|
import com.vksfeng.quan.resourcehub_pojo.entity.ResourceFavorite;
|
||||||
|
import com.vksfeng.quan.resourcehub_pojo.entity.ResourceLike;
|
||||||
|
import com.vksfeng.quan.resourcehub_pojo.vo.ResourceCommentVO;
|
||||||
|
import com.vksfeng.quan.resourcehub_pojo.vo.ResourceVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ResourceMapper {
|
||||||
|
void createResource(Resource resource);
|
||||||
|
|
||||||
|
ResourceVO getResourceById(Long id, Long currentUserId);
|
||||||
|
|
||||||
|
// List<ResourceVO> getResourceList(List<Long> userIds, Long currentUserId);
|
||||||
|
|
||||||
|
void deleteResource(Long id, Long userId);
|
||||||
|
|
||||||
|
void addLike(ResourceLike resourceLike);
|
||||||
|
|
||||||
|
boolean isLiked(Long userId, Long resourceId);
|
||||||
|
|
||||||
|
void removeLike(Long resourceId, Long userId);
|
||||||
|
|
||||||
|
void addComment(ResourceComment resourceComment);
|
||||||
|
|
||||||
|
ResourceCommentVO getResourceCommentById(Long commentId);
|
||||||
|
|
||||||
|
List<ResourceCommentVO> getCommentsByResourceId(Long id);
|
||||||
|
|
||||||
|
void addFavorite(ResourceFavorite resourceFavorite);
|
||||||
|
|
||||||
|
boolean isFavorite(Long resourceId, Long userId);
|
||||||
|
|
||||||
|
void removeFavorite(Long resourceId, Long userId);
|
||||||
|
|
||||||
|
Integer getResourceCount(String category, List<Long> userIds);
|
||||||
|
|
||||||
|
List<ResourceVO> getResourceList(List<Long> userIds, Long currentUserId, Integer offset, Integer pageSize, String category);
|
||||||
|
|
||||||
|
List<ResourceVO> getFavoriteList(Long userId, Integer offset, Integer pageSize, String category);
|
||||||
|
|
||||||
|
Integer getFavoriteResourceCount(Long userId, String category);
|
||||||
|
|
||||||
|
Integer getUserReceivedLikes(Long userId);
|
||||||
|
|
||||||
|
Integer getUserReceivedComments(Long userId);
|
||||||
|
|
||||||
|
Integer getUserReceivedFavorites(Long userId);
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package com.vksfeng.quan.mapper;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.analysis_pojo.entity.TaskCompletionDay;
|
||||||
|
import com.vksfeng.quan.analysis_pojo.vo.HeatMapVO;
|
||||||
|
import com.vksfeng.quan.analysis_pojo.vo.LeaderboardCountVO;
|
||||||
|
import com.vksfeng.quan.analysis_pojo.vo.TaskCompletionRateVO;
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.entity.Task;
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.entity.TaskCompletion;
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.vo.TaskVO;
|
||||||
|
import org.apache.ibatis.annotations.Insert;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface TaskMapper {
|
||||||
|
|
||||||
|
void insert(Task task);
|
||||||
|
|
||||||
|
void insertObjectiveTaskRelation(Long objectiveId, Long taskId);
|
||||||
|
|
||||||
|
Task selectById(Long id);
|
||||||
|
|
||||||
|
void updateById(Task task, Long id);
|
||||||
|
|
||||||
|
void deleteById(Long id);
|
||||||
|
|
||||||
|
void deleteObjectiveTaskRelationByTaskId(Long id);
|
||||||
|
|
||||||
|
Long getObjectiveIdByTaskId(Long id);
|
||||||
|
|
||||||
|
List<TaskVO> getTodayTasks(Long userId, LocalDate today);
|
||||||
|
|
||||||
|
List<Long> getAllTasksIdByObjectiveId(Long objectiveId);
|
||||||
|
|
||||||
|
void deleteObjectiveTaskRelationByObjectiveId(Long objectiveId);
|
||||||
|
|
||||||
|
void createObjectiveTaskRelation(Long objectiveId, Long taskId);
|
||||||
|
|
||||||
|
void deleteObjectiveTaskRelation(Long objectiveId, Long taskId);
|
||||||
|
|
||||||
|
List<TaskVO> getMonthlyTasksCalendar(Long userId, int year, int month);
|
||||||
|
|
||||||
|
List<TaskCompletionDay> selectDailyCompletion(Long userId, int year, int month);
|
||||||
|
|
||||||
|
@Insert("INSERT INTO task_completion (task_id, user_id, completed_at) VALUES (#{taskId}, #{userId}, #{completedAt})")
|
||||||
|
void addTaskCompletion(TaskCompletion taskCompletion);
|
||||||
|
|
||||||
|
void deleteTaskCompletionByTaskId(Long taskId);
|
||||||
|
|
||||||
|
void deleteTaskCompletionByRecordId(Long id);
|
||||||
|
|
||||||
|
Long getLatestCompletionRecordId(Long taskId);
|
||||||
|
|
||||||
|
List<LeaderboardCountVO> getWeeklyTaskLeaderboard(List<Long> userIds);
|
||||||
|
|
||||||
|
List<LeaderboardCountVO> getMonthlyTaskLeaderboard(List<Long> userIds);
|
||||||
|
|
||||||
|
Integer getTaskCount(Long userId);
|
||||||
|
|
||||||
|
Integer getCompletedTaskCount(Long userId);
|
||||||
|
|
||||||
|
List<TaskCompletionRateVO> getTotalCount(Long userId, LocalDate startDate, LocalDate endDate);
|
||||||
|
|
||||||
|
List<TaskCompletionRateVO> getCompleteCount(Long userId, LocalDate startDate, LocalDate endDate);
|
||||||
|
|
||||||
|
List<Task> findRecurringCompletedTasks();
|
||||||
|
|
||||||
|
List<HeatMapVO> getYearlyTasksHeatMap(Long userId, int year);
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.vksfeng.quan.mapper;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.peerhub_pojo.vo.UserSearchVO;
|
||||||
|
import com.vksfeng.quan.user_pojo.entity.User;
|
||||||
|
import com.vksfeng.quan.user_pojo.entity.UserLocation;
|
||||||
|
import com.vksfeng.quan.user_pojo.vo.UserActivityVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface UserMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增用户数据
|
||||||
|
* @param user 用户数据
|
||||||
|
*/
|
||||||
|
void save(User user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态条件查找用户
|
||||||
|
* @param user 仅包含特定条件属性值的对象
|
||||||
|
*/
|
||||||
|
List<User> findUser(User user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查找条件
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
User getUserById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户信息
|
||||||
|
*/
|
||||||
|
void update(User user);
|
||||||
|
|
||||||
|
void saveUserLocation(UserLocation userLocation);
|
||||||
|
|
||||||
|
List<UserSearchVO> searchUsers(List<Long> userIds, Long currentUserId);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.vksfeng.quan.obengine_pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Feedback {
|
||||||
|
private String question;
|
||||||
|
private String answer;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.vksfeng.quan.obengine_pojo.dto;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.entity.Objective;
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.vo.ObjectiveVO;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ObjectivePlanDTO {
|
||||||
|
private Objective masterObjective;
|
||||||
|
private List<SubObjectiveDTO> subObjectives;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.vksfeng.quan.obengine_pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ObjectiveWithFeedbackDTO {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private LocalDate startDate;
|
||||||
|
private LocalDate endDate;
|
||||||
|
private List<Feedback> feedback;
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.vksfeng.quan.obengine_pojo.dto;
|
||||||
|
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.entity.Task;
|
||||||
|
import com.vksfeng.quan.objectivehub_pojo.vo.ObjectiveVO;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SubObjectiveDTO {
|
||||||
|
private Long id;
|
||||||
|
private Long userId;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private LocalDate startDate;
|
||||||
|
private LocalDate endDate;
|
||||||
|
private Boolean visibility;
|
||||||
|
private Double progress;
|
||||||
|
private Double weight;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private List<Task> tasks;
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.vksfeng.quan.obengine_pojo.spark;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SparkRequest {
|
||||||
|
private String model;
|
||||||
|
private List<Message> messages;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Message {
|
||||||
|
private String role;
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.vksfeng.quan.obengine_pojo.spark;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SparkResponse {
|
||||||
|
private List<Choice> choices;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Choice {
|
||||||
|
private Message message;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Message {
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.obengine_pojo.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class FollowUpQuestionVO {
|
||||||
|
private String question;
|
||||||
|
private List<String> options;
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.vksfeng.quan.objectivehub_pojo.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class ObjectiveDTO {
|
||||||
|
private Long id;
|
||||||
|
private Long objectiveId;
|
||||||
|
private Long userId;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private LocalDate startDate;
|
||||||
|
private LocalDate endDate;
|
||||||
|
private Boolean visibility;
|
||||||
|
private Double weight;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.vksfeng.quan.objectivehub_pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RelationDTO {
|
||||||
|
private Long childObjectiveId;
|
||||||
|
private Long taskId;
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.vksfeng.quan.objectivehub_pojo.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class SingleTaskDTO {
|
||||||
|
// task共有
|
||||||
|
private Long id;
|
||||||
|
private Long userId;
|
||||||
|
private Long objectiveId;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String status; // 'pending' or 'completed'
|
||||||
|
private Boolean visibility;
|
||||||
|
private String type; // 'single' or 'recurring'
|
||||||
|
|
||||||
|
// single_task类特有
|
||||||
|
private LocalDate ddl; // 单次任务的截止日期
|
||||||
|
private LocalDateTime completedAt; // 单次任务的完成时间
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.vksfeng.quan.objectivehub_pojo.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class Objective {
|
||||||
|
|
||||||
|
private Long id; // 主键
|
||||||
|
private Long userId; // 用户ID
|
||||||
|
private String name; // 目标名称
|
||||||
|
private String description; // 目标描述
|
||||||
|
private LocalDate startDate; // 开始日期
|
||||||
|
private LocalDate endDate; // 结束日期
|
||||||
|
private Boolean visibility; // 可见性
|
||||||
|
private Double progress; // 进度
|
||||||
|
private Double weight; // 权重
|
||||||
|
private LocalDateTime completedAt; // 完成时间
|
||||||
|
private LocalDateTime createdAt; // 创建时间
|
||||||
|
private LocalDateTime updatedAt; // 更新时间
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.vksfeng.quan.objectivehub_pojo.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class ObjectiveVO {
|
||||||
|
private Long id;
|
||||||
|
private Long userId;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private LocalDate startDate;
|
||||||
|
private LocalDate endDate;
|
||||||
|
private Boolean visibility;
|
||||||
|
private Double progress;
|
||||||
|
private Double weight;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private List<ObjectiveVO> children;
|
||||||
|
private List<TaskVO> tasks;
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.vksfeng.quan.peerhub_pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class FeedCommentDTO {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long feedId;
|
||||||
|
private String content;
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.vksfeng.quan.peerhub_pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class FriendshipDTO {
|
||||||
|
private Long userId;
|
||||||
|
private Long friendId;
|
||||||
|
private String status; // 'pending', 'accepted', 'blocked'
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.vksfeng.quan.peerhub_pojo.entity;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class Feed {
|
||||||
|
private Long id;
|
||||||
|
private Long userId;
|
||||||
|
private String type; // task_completed, objective_completed, 'post'
|
||||||
|
private Long relatedId;
|
||||||
|
private String content;
|
||||||
|
private Long likeCount;
|
||||||
|
private Long commentCount;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.vksfeng.quan.peerhub_pojo.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class FeedComment {
|
||||||
|
private Long id;
|
||||||
|
private Long feedId;
|
||||||
|
private Long userId;
|
||||||
|
private String content;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.vksfeng.quan.peerhub_pojo.entity;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class FeedLike {
|
||||||
|
private Long id;
|
||||||
|
private Long feedId;
|
||||||
|
private Long userId;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue