diff --git a/jog-admin/pom.xml b/jog-admin/pom.xml index 98d2955..283c1c2 100644 --- a/jog-admin/pom.xml +++ b/jog-admin/pom.xml @@ -29,6 +29,11 @@ com.baomidou mybatis-plus-spring-boot3-starter + + com.baomidou + mybatis-plus-jsqlparser + 3.5.9 + com.mysql mysql-connector-j diff --git a/jog-admin/src/main/java/fun/nojava/admin/JogApplication.java b/jog-admin/src/main/java/fun/nojava/admin/JogApplication.java new file mode 100644 index 0000000..032081d --- /dev/null +++ b/jog-admin/src/main/java/fun/nojava/admin/JogApplication.java @@ -0,0 +1,12 @@ +package fun.nojava.admin; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JogApplication { + + public static void main(String[] args) { + SpringApplication.run(JogApplication.class, args); + } +} diff --git a/jog-admin/src/main/java/fun/nojava/admin/config/MyBatisPlusConfig.java b/jog-admin/src/main/java/fun/nojava/admin/config/MyBatisPlusConfig.java new file mode 100644 index 0000000..613051a --- /dev/null +++ b/jog-admin/src/main/java/fun/nojava/admin/config/MyBatisPlusConfig.java @@ -0,0 +1,38 @@ +package fun.nojava.admin.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.LocalDateTime; + +@Configuration +public class MyBatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } + + @Bean + public MetaObjectHandler metaObjectHandler() { + return new MetaObjectHandler() { + @Override + public void insertFill(MetaObject metaObject) { + this.strictInsertFill(metaObject, "createdAt", LocalDateTime::now, LocalDateTime.class); + this.strictInsertFill(metaObject, "updatedAt", LocalDateTime::now, LocalDateTime.class); + } + + @Override + public void updateFill(MetaObject metaObject) { + this.strictUpdateFill(metaObject, "updatedAt", LocalDateTime::now, LocalDateTime.class); + } + }; + } +} diff --git a/jog-admin/target/classes/application.yml b/jog-admin/target/classes/application.yml new file mode 100644 index 0000000..2484461 --- /dev/null +++ b/jog-admin/target/classes/application.yml @@ -0,0 +1,70 @@ +server: + port: 8080 + servlet: + context-path: / + +spring: + application: + name: jog-system + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/jog_db?useUnicode=true&characterEncoding=utf8mb4&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: root + data: + redis: + host: localhost + port: 6379 + database: 0 + timeout: 5000ms + lettuce: + pool: + max-active: 20 + max-idle: 10 + min-idle: 5 + mail: + host: smtp.example.com + port: 587 + username: noreply@example.com + password: your-mail-password + properties: + mail: + smtp: + auth: true + starttls: + enable: true + flyway: + enabled: true + locations: classpath:db/migration + baseline-on-migrate: true + encoding: UTF-8 + +mybatis-plus: + mapper-locations: classpath*:/mapper/**/*.xml + configuration: + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + global-config: + db-config: + logic-delete-field: deleted + logic-delete-value: 1 + logic-not-delete-value: 0 + +jwt: + secret: YourSuperSecretKeyForJwtTokenGenerationMustBeAtLeast256BitsLongForHS256Algorithm + access-token-expire: 7200 + refresh-token-expire: 86400 + refresh-token-expire-remember: 1209600 + +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui.html + +logging: + level: + fun.nojava: DEBUG + org.springframework.security: DEBUG diff --git a/jog-admin/target/classes/db/migration/V1__init_schema.sql b/jog-admin/target/classes/db/migration/V1__init_schema.sql new file mode 100644 index 0000000..2f9b638 --- /dev/null +++ b/jog-admin/target/classes/db/migration/V1__init_schema.sql @@ -0,0 +1,207 @@ +-- V1__init_schema.sql +-- 博客系统数据库初始化脚本 + +CREATE DATABASE IF NOT EXISTS blog_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +USE blog_db; + +-- 用户表 +CREATE TABLE IF NOT EXISTS sys_user ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) DEFAULT NULL COMMENT '用户名', + email VARCHAR(100) NOT NULL COMMENT '邮箱', + phone VARCHAR(20) DEFAULT NULL COMMENT '手机号', + password VARCHAR(255) NOT NULL COMMENT '密码', + nickname VARCHAR(50) DEFAULT NULL COMMENT '昵称', + avatar VARCHAR(500) DEFAULT NULL COMMENT '头像URL', + user_type TINYINT NOT NULL DEFAULT 1 COMMENT '用户类型:0管理员 1普通用户', + status TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0正常 1锁定', + locked_until DATETIME DEFAULT NULL COMMENT '锁定截止时间', + login_fail_count INT NOT NULL DEFAULT 0 COMMENT '登录失败次数', + email_verified TINYINT(1) NOT NULL DEFAULT 0 COMMENT '邮箱是否验证', + last_login_ip VARCHAR(50) DEFAULT NULL COMMENT '最后登录IP', + last_login_time DATETIME DEFAULT NULL COMMENT '最后登录时间', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除', + UNIQUE KEY uk_email (email), + UNIQUE KEY uk_username (username), + KEY idx_phone (phone) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表'; + +-- 用户会话表 +CREATE TABLE IF NOT EXISTS sys_user_session ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id BIGINT NOT NULL COMMENT '用户ID', + token VARCHAR(500) NOT NULL COMMENT '访问令牌', + device_fingerprint VARCHAR(100) DEFAULT NULL COMMENT '设备指纹', + ip_address VARCHAR(50) DEFAULT NULL COMMENT 'IP地址', + user_agent VARCHAR(500) DEFAULT NULL COMMENT 'User-Agent', + expire_time DATETIME NOT NULL COMMENT '过期时间', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY idx_user_id (user_id), + KEY idx_token (token(255)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户会话表'; + +-- 登录日志表 +CREATE TABLE IF NOT EXISTS sys_login_log ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id BIGINT DEFAULT NULL COMMENT '用户ID', + username VARCHAR(100) DEFAULT NULL COMMENT '登录账号', + login_type TINYINT NOT NULL COMMENT '登录类型:0管理员 1用户', + result TINYINT NOT NULL COMMENT '结果:0失败 1成功', + fail_reason VARCHAR(200) DEFAULT NULL COMMENT '失败原因', + ip_address VARCHAR(50) DEFAULT NULL COMMENT 'IP地址', + device_fingerprint VARCHAR(100) DEFAULT NULL COMMENT '设备指纹', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY idx_user_id (user_id), + KEY idx_created_at (created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='登录日志表'; + +-- 用户关注表 +CREATE TABLE IF NOT EXISTS sys_user_follow ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + follower_id BIGINT NOT NULL COMMENT '关注者ID', + followee_id BIGINT NOT NULL COMMENT '被关注者ID', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE KEY uk_follow (follower_id, followee_id), + KEY idx_followee (followee_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户关注表'; + +-- 通知表 +CREATE TABLE IF NOT EXISTS sys_notification ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + receiver_id BIGINT NOT NULL COMMENT '接收者ID', + sender_id BIGINT DEFAULT NULL COMMENT '发送者ID', + type TINYINT NOT NULL COMMENT '类型:0回复 1点赞 2系统', + content VARCHAR(500) NOT NULL COMMENT '内容', + related_id BIGINT DEFAULT NULL COMMENT '关联ID', + is_read TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已读', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY idx_receiver (receiver_id), + KEY idx_created_at (created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='通知表'; + +-- 系统配置表 +CREATE TABLE IF NOT EXISTS sys_config ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + config_key VARCHAR(100) NOT NULL COMMENT '配置键', + config_value VARCHAR(500) NOT NULL COMMENT '配置值', + description VARCHAR(200) DEFAULT NULL COMMENT '描述', + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE KEY uk_config_key (config_key) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统配置表'; + +-- 敏感词表 +CREATE TABLE IF NOT EXISTS sys_sensitive_word ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + word VARCHAR(50) NOT NULL COMMENT '敏感词', + replacement VARCHAR(50) DEFAULT '*' COMMENT '替换词', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE KEY uk_word (word) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='敏感词表'; + +-- 分类表 +CREATE TABLE IF NOT EXISTS blog_category ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(50) NOT NULL COMMENT '分类名称', + description VARCHAR(200) DEFAULT NULL COMMENT '描述', + enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用', + sort INT NOT NULL DEFAULT 0 COMMENT '排序', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE KEY uk_name (name) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分类表'; + +-- 标签表 +CREATE TABLE IF NOT EXISTS blog_tag ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(50) NOT NULL COMMENT '标签名称', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE KEY uk_name (name) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='标签表'; + +-- 文章表 +CREATE TABLE IF NOT EXISTS blog_article ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id BIGINT NOT NULL COMMENT '作者ID', + category_id BIGINT DEFAULT NULL COMMENT '分类ID', + title VARCHAR(200) NOT NULL COMMENT '标题', + summary VARCHAR(500) DEFAULT NULL COMMENT '摘要', + content LONGTEXT NOT NULL COMMENT '内容', + cover_image VARCHAR(500) DEFAULT NULL COMMENT '封面图URL', + status TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0草稿 1已发布 2回收站', + visibility TINYINT NOT NULL DEFAULT 0 COMMENT '可见性:0公开 1私密 2仅粉丝', + view_count INT NOT NULL DEFAULT 0 COMMENT '浏览量', + like_count INT NOT NULL DEFAULT 0 COMMENT '点赞数', + comment_count INT NOT NULL DEFAULT 0 COMMENT '评论数', + allow_comment TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许评论', + published_at DATETIME DEFAULT NULL COMMENT '发布时间', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除', + KEY idx_user_id (user_id), + KEY idx_category_id (category_id), + KEY idx_status (status), + KEY idx_published_at (published_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文章表'; + +-- 文章标签关联表 +CREATE TABLE IF NOT EXISTS blog_article_tag ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + article_id BIGINT NOT NULL COMMENT '文章ID', + tag_id BIGINT NOT NULL COMMENT '标签ID', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE KEY uk_article_tag (article_id, tag_id), + KEY idx_tag_id (tag_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文章标签关联表'; + +-- 文章点赞表 +CREATE TABLE IF NOT EXISTS blog_article_like ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + article_id BIGINT NOT NULL COMMENT '文章ID', + user_id BIGINT NOT NULL COMMENT '用户ID', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE KEY uk_article_user (article_id, user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文章点赞表'; + +-- 评论表 +CREATE TABLE IF NOT EXISTS blog_comment ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + article_id BIGINT NOT NULL COMMENT '文章ID', + user_id BIGINT NOT NULL COMMENT '评论者ID', + parent_id BIGINT DEFAULT NULL COMMENT '父评论ID', + reply_to_user_id BIGINT DEFAULT NULL COMMENT '回复目标用户ID', + content VARCHAR(1000) NOT NULL COMMENT '评论内容', + level TINYINT NOT NULL DEFAULT 1 COMMENT '评论层级', + status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0待审核 1已通过 2已屏蔽 3已删除', + like_count INT NOT NULL DEFAULT 0 COMMENT '点赞数', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除', + KEY idx_article_id (article_id), + KEY idx_user_id (user_id), + KEY idx_parent_id (parent_id), + KEY idx_status (status) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='评论表'; + +-- 评论点赞表 +CREATE TABLE IF NOT EXISTS blog_comment_like ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + comment_id BIGINT NOT NULL COMMENT '评论ID', + user_id BIGINT NOT NULL COMMENT '用户ID', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE KEY uk_comment_user (comment_id, user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='评论点赞表'; + +-- 图片表 +CREATE TABLE IF NOT EXISTS blog_image ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id BIGINT NOT NULL COMMENT '上传者ID', + original_name VARCHAR(200) NOT NULL COMMENT '原始文件名', + stored_name VARCHAR(200) NOT NULL COMMENT '存储文件名', + file_path VARCHAR(500) NOT NULL COMMENT '文件路径', + url VARCHAR(500) NOT NULL COMMENT '访问URL', + file_size BIGINT NOT NULL COMMENT '文件大小(字节)', + mime_type VARCHAR(50) NOT NULL COMMENT 'MIME类型', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY idx_user_id (user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='图片表'; diff --git a/jog-admin/target/classes/db/migration/V2__init_data.sql b/jog-admin/target/classes/db/migration/V2__init_data.sql new file mode 100644 index 0000000..57a8629 --- /dev/null +++ b/jog-admin/target/classes/db/migration/V2__init_data.sql @@ -0,0 +1,29 @@ +-- V2__init_data.sql +-- 初始化数据 + +USE blog_db; + +-- 插入管理员账号(密码:Admin@123) +INSERT INTO sys_user (username, email, password, nickname, user_type, status, email_verified) +VALUES ('admin', 'admin@blog.com', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '系统管理员', 0, 0, 1); + +-- 插入默认分类 +INSERT INTO blog_category (name, description, enabled, sort) VALUES +('技术', '技术相关文章', 1, 1), +('生活', '生活随笔', 1, 2), +('读书', '读书笔记', 1, 3); + +-- 插入默认标签 +INSERT INTO blog_tag (name) VALUES +('Java'), +('Spring Boot'), +('Vue'), +('前端'), +('数据库'); + +-- 插入系统配置 +INSERT INTO sys_config (config_key, config_value, description) VALUES +('site_name', '我的博客', '站点名称'), +('site_description', '一个简洁的博客系统', '站点描述'), +('comment_audit', 'false', '评论是否需要审核'), +('max_comment_level', '3', '最大评论层级'); diff --git a/jog-admin/target/classes/fun/nojava/admin/JogApplication.class b/jog-admin/target/classes/fun/nojava/admin/JogApplication.class new file mode 100644 index 0000000..ec610d4 Binary files /dev/null and b/jog-admin/target/classes/fun/nojava/admin/JogApplication.class differ diff --git a/jog-admin/target/classes/fun/nojava/admin/config/MyBatisPlusConfig$1.class b/jog-admin/target/classes/fun/nojava/admin/config/MyBatisPlusConfig$1.class new file mode 100644 index 0000000..e3baee8 Binary files /dev/null and b/jog-admin/target/classes/fun/nojava/admin/config/MyBatisPlusConfig$1.class differ diff --git a/jog-admin/target/classes/fun/nojava/admin/config/MyBatisPlusConfig.class b/jog-admin/target/classes/fun/nojava/admin/config/MyBatisPlusConfig.class new file mode 100644 index 0000000..a559962 Binary files /dev/null and b/jog-admin/target/classes/fun/nojava/admin/config/MyBatisPlusConfig.class differ diff --git a/jog-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/jog-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..167b637 --- /dev/null +++ b/jog-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,3 @@ +fun\nojava\admin\JogApplication.class +fun\nojava\admin\config\MyBatisPlusConfig$1.class +fun\nojava\admin\config\MyBatisPlusConfig.class diff --git a/jog-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/jog-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..6a5549e --- /dev/null +++ b/jog-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,2 @@ +E:\person\Jog\jog-admin\src\main\java\fun\nojava\admin\config\MyBatisPlusConfig.java +E:\person\Jog\jog-admin\src\main\java\fun\nojava\admin\JogApplication.java diff --git a/jog-common/src/main/java/fun/nojava/common/exception/ErrorCode.java b/jog-common/src/main/java/fun/nojava/common/exception/ErrorCode.java index 3ea2b8c..0f663b4 100644 --- a/jog-common/src/main/java/fun/nojava/common/exception/ErrorCode.java +++ b/jog-common/src/main/java/fun/nojava/common/exception/ErrorCode.java @@ -28,6 +28,10 @@ public enum ErrorCode { TOKEN_INVALID(1010, "Token无效"), TOKEN_EXPIRED(1011, "Token已过期"), SESSION_KICKED(1012, "会话已被踢出"), + USER_NOT_FOUND(1013, "用户不存在"), + PASSWORD_ERROR(1014, "密码错误"), + PERMISSION_DENIED(1015, "权限不足"), + EMAIL_EXISTS(1016, "邮箱已存在"), ARTICLE_NOT_FOUND(2001, "文章不存在"), ARTICLE_NO_PERMISSION(2002, "无权操作该文章"), @@ -41,7 +45,8 @@ public enum ErrorCode { COMMENT_NOT_FOUND(3001, "评论不存在"), COMMENT_AUDIT_PENDING(3002, "评论待审核"), COMMENT_NO_PERMISSION(3003, "无权操作该评论"), - COMMENT_LEVEL_EXCEEDED(3004, "评论层级超限"); + COMMENT_LEVEL_EXCEEDED(3004, "评论层级超限"), + COMMENT_LEVEL_LIMIT(3005, "评论层级超限"); private final int code; private final String message; diff --git a/jog-common/target/classes/fun/nojava/common/annotation/RateLimit.class b/jog-common/target/classes/fun/nojava/common/annotation/RateLimit.class new file mode 100644 index 0000000..bb1848c Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/annotation/RateLimit.class differ diff --git a/jog-common/target/classes/fun/nojava/common/constant/BlogConstants.class b/jog-common/target/classes/fun/nojava/common/constant/BlogConstants.class new file mode 100644 index 0000000..378de5b Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/constant/BlogConstants.class differ diff --git a/jog-common/target/classes/fun/nojava/common/constant/SecurityConstants.class b/jog-common/target/classes/fun/nojava/common/constant/SecurityConstants.class new file mode 100644 index 0000000..f7b722f Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/constant/SecurityConstants.class differ diff --git a/jog-common/target/classes/fun/nojava/common/enums/ArticleStatus.class b/jog-common/target/classes/fun/nojava/common/enums/ArticleStatus.class new file mode 100644 index 0000000..109537b Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/enums/ArticleStatus.class differ diff --git a/jog-common/target/classes/fun/nojava/common/enums/ArticleVisibility.class b/jog-common/target/classes/fun/nojava/common/enums/ArticleVisibility.class new file mode 100644 index 0000000..602d730 Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/enums/ArticleVisibility.class differ diff --git a/jog-common/target/classes/fun/nojava/common/enums/CommentStatus.class b/jog-common/target/classes/fun/nojava/common/enums/CommentStatus.class new file mode 100644 index 0000000..b8cbc1c Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/enums/CommentStatus.class differ diff --git a/jog-common/target/classes/fun/nojava/common/enums/LoginType.class b/jog-common/target/classes/fun/nojava/common/enums/LoginType.class new file mode 100644 index 0000000..3cf5452 Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/enums/LoginType.class differ diff --git a/jog-common/target/classes/fun/nojava/common/enums/NotificationType.class b/jog-common/target/classes/fun/nojava/common/enums/NotificationType.class new file mode 100644 index 0000000..3589bb9 Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/enums/NotificationType.class differ diff --git a/jog-common/target/classes/fun/nojava/common/enums/UserStatus.class b/jog-common/target/classes/fun/nojava/common/enums/UserStatus.class new file mode 100644 index 0000000..a21c18a Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/enums/UserStatus.class differ diff --git a/jog-common/target/classes/fun/nojava/common/enums/UserType.class b/jog-common/target/classes/fun/nojava/common/enums/UserType.class new file mode 100644 index 0000000..51e3ebd Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/enums/UserType.class differ diff --git a/jog-common/target/classes/fun/nojava/common/exception/BusinessException.class b/jog-common/target/classes/fun/nojava/common/exception/BusinessException.class new file mode 100644 index 0000000..53cbbd3 Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/exception/BusinessException.class differ diff --git a/jog-common/target/classes/fun/nojava/common/exception/ErrorCode.class b/jog-common/target/classes/fun/nojava/common/exception/ErrorCode.class new file mode 100644 index 0000000..f78db2c Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/exception/ErrorCode.class differ diff --git a/jog-common/target/classes/fun/nojava/common/model/ApiResult.class b/jog-common/target/classes/fun/nojava/common/model/ApiResult.class new file mode 100644 index 0000000..28ed725 Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/model/ApiResult.class differ diff --git a/jog-common/target/classes/fun/nojava/common/model/IdVO.class b/jog-common/target/classes/fun/nojava/common/model/IdVO.class new file mode 100644 index 0000000..b5bb3cd Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/model/IdVO.class differ diff --git a/jog-common/target/classes/fun/nojava/common/model/PageParam.class b/jog-common/target/classes/fun/nojava/common/model/PageParam.class new file mode 100644 index 0000000..dfbc432 Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/model/PageParam.class differ diff --git a/jog-common/target/classes/fun/nojava/common/model/PageResult.class b/jog-common/target/classes/fun/nojava/common/model/PageResult.class new file mode 100644 index 0000000..9d47036 Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/model/PageResult.class differ diff --git a/jog-common/target/classes/fun/nojava/common/util/ApiResultUtils.class b/jog-common/target/classes/fun/nojava/common/util/ApiResultUtils.class new file mode 100644 index 0000000..5378204 Binary files /dev/null and b/jog-common/target/classes/fun/nojava/common/util/ApiResultUtils.class differ diff --git a/jog-common/target/jog-common-1.0.0.jar b/jog-common/target/jog-common-1.0.0.jar new file mode 100644 index 0000000..4233290 Binary files /dev/null and b/jog-common/target/jog-common-1.0.0.jar differ diff --git a/jog-common/target/maven-archiver/pom.properties b/jog-common/target/maven-archiver/pom.properties new file mode 100644 index 0000000..e897384 --- /dev/null +++ b/jog-common/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=jog-common +groupId=fun.nojava +version=1.0.0 diff --git a/jog-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/jog-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..c2b9834 --- /dev/null +++ b/jog-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,17 @@ +fun\nojava\common\enums\NotificationType.class +fun\nojava\common\constant\BlogConstants.class +fun\nojava\common\constant\SecurityConstants.class +fun\nojava\common\exception\ErrorCode.class +fun\nojava\common\exception\BusinessException.class +fun\nojava\common\model\IdVO.class +fun\nojava\common\enums\ArticleStatus.class +fun\nojava\common\enums\LoginType.class +fun\nojava\common\enums\UserStatus.class +fun\nojava\common\model\PageResult.class +fun\nojava\common\util\ApiResultUtils.class +fun\nojava\common\model\PageParam.class +fun\nojava\common\enums\CommentStatus.class +fun\nojava\common\enums\ArticleVisibility.class +fun\nojava\common\enums\UserType.class +fun\nojava\common\model\ApiResult.class +fun\nojava\common\annotation\RateLimit.class diff --git a/jog-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/jog-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..a862ef7 --- /dev/null +++ b/jog-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,17 @@ +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\annotation\RateLimit.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\constant\BlogConstants.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\constant\SecurityConstants.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\enums\ArticleStatus.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\enums\ArticleVisibility.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\enums\CommentStatus.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\enums\LoginType.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\enums\NotificationType.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\enums\UserStatus.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\enums\UserType.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\exception\BusinessException.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\exception\ErrorCode.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\model\ApiResult.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\model\IdVO.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\model\PageParam.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\model\PageResult.java +E:\person\Jog\jog-common\src\main\java\fun\nojava\common\util\ApiResultUtils.java diff --git a/jog-framework/src/main/java/fun/nojava/framework/security/JwtTokenProvider.java b/jog-framework/src/main/java/fun/nojava/framework/security/JwtTokenProvider.java index be45b6a..2460a3f 100644 --- a/jog-framework/src/main/java/fun/nojava/framework/security/JwtTokenProvider.java +++ b/jog-framework/src/main/java/fun/nojava/framework/security/JwtTokenProvider.java @@ -19,13 +19,13 @@ public class JwtTokenProvider { private final JwtProperties jwtProperties; - public String generateAccessToken(Long userId, String role) { + public String generateAccessToken(Long userId, Integer role) { Date now = new Date(); Date expiryDate = new Date(now.getTime() + jwtProperties.getAccessTokenExpire() * 1000); return Jwts.builder() .subject(String.valueOf(userId)) - .claim("role", role) + .claim("role", String.valueOf(role)) .issuedAt(now) .expiration(expiryDate) .signWith(jwtProperties.getSigningKey()) diff --git a/jog-framework/src/main/java/fun/nojava/framework/security/TokenBlacklistService.java b/jog-framework/src/main/java/fun/nojava/framework/security/TokenBlacklistService.java index 17691c6..a87ccc6 100644 --- a/jog-framework/src/main/java/fun/nojava/framework/security/TokenBlacklistService.java +++ b/jog-framework/src/main/java/fun/nojava/framework/security/TokenBlacklistService.java @@ -40,4 +40,8 @@ public class TokenBlacklistService { String key = SecurityConstants.REFRESH_TOKEN_KEY + userId; redisTemplate.delete(key); } + + public void addToBlacklist(String token) { + blacklistAccessToken(token, 7200); + } } diff --git a/jog-framework/target/classes/fun/nojava/framework/aspect/LogAspect.class b/jog-framework/target/classes/fun/nojava/framework/aspect/LogAspect.class new file mode 100644 index 0000000..0b4233c Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/aspect/LogAspect.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/aspect/RateLimitAspect.class b/jog-framework/target/classes/fun/nojava/framework/aspect/RateLimitAspect.class new file mode 100644 index 0000000..50ffb3f Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/aspect/RateLimitAspect.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/config/JwtProperties.class b/jog-framework/target/classes/fun/nojava/framework/config/JwtProperties.class new file mode 100644 index 0000000..9a778a2 Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/config/JwtProperties.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/config/SecurityConfig.class b/jog-framework/target/classes/fun/nojava/framework/config/SecurityConfig.class new file mode 100644 index 0000000..4e7995c Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/config/SecurityConfig.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/config/WebConfig.class b/jog-framework/target/classes/fun/nojava/framework/config/WebConfig.class new file mode 100644 index 0000000..833e3a0 Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/config/WebConfig.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/filter/XssFilter.class b/jog-framework/target/classes/fun/nojava/framework/filter/XssFilter.class new file mode 100644 index 0000000..5186920 Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/filter/XssFilter.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/handler/GlobalExceptionHandler.class b/jog-framework/target/classes/fun/nojava/framework/handler/GlobalExceptionHandler.class new file mode 100644 index 0000000..d8bc910 Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/handler/GlobalExceptionHandler.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/security/JwtAuthenticationEntryPoint.class b/jog-framework/target/classes/fun/nojava/framework/security/JwtAuthenticationEntryPoint.class new file mode 100644 index 0000000..edb3bea Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/security/JwtAuthenticationEntryPoint.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/security/JwtAuthenticationFilter.class b/jog-framework/target/classes/fun/nojava/framework/security/JwtAuthenticationFilter.class new file mode 100644 index 0000000..2ae8566 Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/security/JwtAuthenticationFilter.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/security/JwtTokenProvider.class b/jog-framework/target/classes/fun/nojava/framework/security/JwtTokenProvider.class new file mode 100644 index 0000000..4c9dcaf Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/security/JwtTokenProvider.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/security/TokenBlacklistService.class b/jog-framework/target/classes/fun/nojava/framework/security/TokenBlacklistService.class new file mode 100644 index 0000000..dced970 Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/security/TokenBlacklistService.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/util/WebUtils.class b/jog-framework/target/classes/fun/nojava/framework/util/WebUtils.class new file mode 100644 index 0000000..275f608 Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/util/WebUtils.class differ diff --git a/jog-framework/target/classes/fun/nojava/framework/wrapper/XssHttpServletRequestWrapper.class b/jog-framework/target/classes/fun/nojava/framework/wrapper/XssHttpServletRequestWrapper.class new file mode 100644 index 0000000..0e22b03 Binary files /dev/null and b/jog-framework/target/classes/fun/nojava/framework/wrapper/XssHttpServletRequestWrapper.class differ diff --git a/jog-framework/target/jog-framework-1.0.0.jar b/jog-framework/target/jog-framework-1.0.0.jar new file mode 100644 index 0000000..28c76e3 Binary files /dev/null and b/jog-framework/target/jog-framework-1.0.0.jar differ diff --git a/jog-framework/target/maven-archiver/pom.properties b/jog-framework/target/maven-archiver/pom.properties new file mode 100644 index 0000000..c629892 --- /dev/null +++ b/jog-framework/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=jog-framework +groupId=fun.nojava +version=1.0.0 diff --git a/jog-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/jog-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..1c5004e --- /dev/null +++ b/jog-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,13 @@ +fun\nojava\framework\security\JwtAuthenticationFilter.class +fun\nojava\framework\util\WebUtils.class +fun\nojava\framework\config\WebConfig.class +fun\nojava\framework\aspect\RateLimitAspect.class +fun\nojava\framework\security\JwtAuthenticationEntryPoint.class +fun\nojava\framework\handler\GlobalExceptionHandler.class +fun\nojava\framework\config\SecurityConfig.class +fun\nojava\framework\wrapper\XssHttpServletRequestWrapper.class +fun\nojava\framework\config\JwtProperties.class +fun\nojava\framework\security\TokenBlacklistService.class +fun\nojava\framework\filter\XssFilter.class +fun\nojava\framework\security\JwtTokenProvider.class +fun\nojava\framework\aspect\LogAspect.class diff --git a/jog-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/jog-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..4a9078b --- /dev/null +++ b/jog-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,13 @@ +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\aspect\LogAspect.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\aspect\RateLimitAspect.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\config\JwtProperties.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\config\SecurityConfig.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\config\WebConfig.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\filter\XssFilter.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\handler\GlobalExceptionHandler.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\security\JwtAuthenticationEntryPoint.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\security\JwtAuthenticationFilter.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\security\JwtTokenProvider.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\security\TokenBlacklistService.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\util\WebUtils.java +E:\person\Jog\jog-framework\src\main\java\fun\nojava\framework\wrapper\XssHttpServletRequestWrapper.java diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/AdminArticleController.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/AdminArticleController.java new file mode 100644 index 0000000..166b6af --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/AdminArticleController.java @@ -0,0 +1,37 @@ +package fun.nojava.module.blog.controller; + +import fun.nojava.common.model.ApiResult; +import fun.nojava.common.model.PageResult; +import fun.nojava.module.blog.dto.ArticleListVO; +import fun.nojava.module.blog.service.ArticleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "管理员-文章管理") +@RestController +@RequestMapping("/api/admin/article") +@RequiredArgsConstructor +public class AdminArticleController { + + private final ArticleService articleService; + + @Operation(summary = "管理文章列表") + @GetMapping("/list") + public ApiResult> listArticles( + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "15") int pageSize, + @RequestParam(required = false) Integer status, + @RequestParam(required = false) Long categoryId, + @RequestParam(required = false) String keyword) { + return ApiResult.success(articleService.listAdminArticles(page, pageSize, status, categoryId, keyword)); + } + + @Operation(summary = "删除文章(管理员)") + @DeleteMapping("/{id}") + public ApiResult deleteArticle(@PathVariable Long id) { + articleService.adminDeleteArticle(id); + return ApiResult.success(); + } +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/ArticleController.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/ArticleController.java new file mode 100644 index 0000000..3225d42 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/ArticleController.java @@ -0,0 +1,76 @@ +package fun.nojava.module.blog.controller; + +import fun.nojava.common.model.ApiResult; +import fun.nojava.common.model.PageResult; +import fun.nojava.module.blog.dto.*; +import fun.nojava.module.blog.service.ArticleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "用户文章接口") +@RestController +@RequestMapping("/api/article") +@RequiredArgsConstructor +public class ArticleController { + + private final ArticleService articleService; + + @Operation(summary = "创建文章") + @PostMapping + public ApiResult createArticle( + @AuthenticationPrincipal Long userId, + @Valid @RequestBody CreateArticleDTO dto) { + return ApiResult.success(articleService.createArticle(userId, dto)); + } + + @Operation(summary = "更新文章") + @PutMapping("/{id}") + public ApiResult updateArticle( + @AuthenticationPrincipal Long userId, + @PathVariable Long id, + @Valid @RequestBody UpdateArticleDTO dto) { + articleService.updateArticle(userId, id, dto); + return ApiResult.success(); + } + + @Operation(summary = "我的文章列表") + @GetMapping("/my") + public ApiResult> listMyArticles( + @AuthenticationPrincipal Long userId, + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) Integer status) { + return ApiResult.success(articleService.listUserArticles(userId, page, pageSize, status)); + } + + @Operation(summary = "移至回收站") + @PostMapping("/{id}/recycle") + public ApiResult moveToRecycleBin( + @AuthenticationPrincipal Long userId, + @PathVariable Long id) { + articleService.moveToRecycleBin(userId, id); + return ApiResult.success(); + } + + @Operation(summary = "从回收站恢复") + @PostMapping("/{id}/restore") + public ApiResult restoreArticle( + @AuthenticationPrincipal Long userId, + @PathVariable Long id) { + articleService.restoreArticle(userId, id); + return ApiResult.success(); + } + + @Operation(summary = "删除文章") + @DeleteMapping("/{id}") + public ApiResult deleteArticle( + @AuthenticationPrincipal Long userId, + @PathVariable Long id) { + articleService.deleteArticle(userId, id); + return ApiResult.success(); + } +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/CategoryController.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/CategoryController.java new file mode 100644 index 0000000..b4002d8 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/CategoryController.java @@ -0,0 +1,61 @@ +package fun.nojava.module.blog.controller; + +import fun.nojava.common.model.ApiResult; +import fun.nojava.module.blog.entity.Category; +import fun.nojava.module.blog.service.CategoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "分类接口") +@RestController +@RequestMapping("/api/public/category") +@RequiredArgsConstructor +class PublicCategoryController { + + private final CategoryService categoryService; + + @Operation(summary = "分类列表") + @GetMapping("/list") + public ApiResult> listCategories() { + return ApiResult.success(categoryService.listAllCategories()); + } +} + +@Tag(name = "管理员-分类管理") +@RestController +@RequestMapping("/api/admin/category") +@RequiredArgsConstructor +class AdminCategoryController { + + private final CategoryService categoryService; + + @Operation(summary = "创建分类") + @PostMapping + public ApiResult createCategory( + @RequestParam String name, + @RequestParam(required = false) String description) { + return ApiResult.success(categoryService.createCategory(name, description)); + } + + @Operation(summary = "更新分类") + @PutMapping("/{id}") + public ApiResult updateCategory( + @PathVariable Long id, + @RequestParam(required = false) String name, + @RequestParam(required = false) String description, + @RequestParam(required = false) Boolean enabled) { + categoryService.updateCategory(id, name, description, enabled); + return ApiResult.success(); + } + + @Operation(summary = "删除分类") + @DeleteMapping("/{id}") + public ApiResult deleteCategory(@PathVariable Long id) { + categoryService.deleteCategory(id); + return ApiResult.success(); + } +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/PublicArticleController.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/PublicArticleController.java new file mode 100644 index 0000000..0968e1e --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/controller/PublicArticleController.java @@ -0,0 +1,49 @@ +package fun.nojava.module.blog.controller; + +import fun.nojava.common.model.ApiResult; +import fun.nojava.common.model.PageResult; +import fun.nojava.module.blog.dto.ArticleListVO; +import fun.nojava.module.blog.dto.ArticleVO; +import fun.nojava.module.blog.service.ArticleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "公开文章接口") +@RestController +@RequestMapping("/api/public/article") +@RequiredArgsConstructor +public class PublicArticleController { + + private final ArticleService articleService; + + @Operation(summary = "文章列表") + @GetMapping("/list") + public ApiResult> listArticles( + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) Long categoryId, + @RequestParam(required = false) Long tagId, + @RequestParam(required = false) String keyword) { + return ApiResult.success(articleService.listPublicArticles(page, pageSize, categoryId, tagId, keyword)); + } + + @Operation(summary = "文章详情") + @GetMapping("/{id}") + public ApiResult getArticle( + @PathVariable Long id, + @AuthenticationPrincipal Long userId) { + articleService.incrementViewCount(id); + return ApiResult.success(articleService.getArticleDetail(id, userId)); + } + + @Operation(summary = "点赞文章") + @PostMapping("/{id}/like") + public ApiResult toggleLike( + @PathVariable Long id, + @AuthenticationPrincipal Long userId) { + return ApiResult.success(articleService.toggleLike(userId, id)); + } +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/ArticleListVO.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/ArticleListVO.java new file mode 100644 index 0000000..8811208 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/ArticleListVO.java @@ -0,0 +1,53 @@ +package fun.nojava.module.blog.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "文章列表项") +public class ArticleListVO { + + @Schema(description = "文章ID") + private Long id; + + @Schema(description = "标题") + private String title; + + @Schema(description = "摘要") + private String summary; + + @Schema(description = "封面图") + private String coverImage; + + @Schema(description = "浏览量") + private Integer viewCount; + + @Schema(description = "点赞数") + private Integer likeCount; + + @Schema(description = "评论数") + private Integer commentCount; + + @Schema(description = "作者ID") + private Long userId; + + @Schema(description = "作者昵称") + private String authorNickname; + + @Schema(description = "作者头像") + private String authorAvatar; + + @Schema(description = "分类名称") + private String categoryName; + + @Schema(description = "发布时间") + private LocalDateTime publishedAt; +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/ArticleVO.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/ArticleVO.java new file mode 100644 index 0000000..d6ae7af --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/ArticleVO.java @@ -0,0 +1,50 @@ +package fun.nojava.module.blog.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ArticleVO { + + private Long id; + + private String title; + + private String summary; + + private String content; + + private String coverImage; + + private Integer viewCount; + + private Integer likeCount; + + private Integer commentCount; + + private Long categoryId; + + private String categoryName; + + private List tags; + + private Long userId; + + private String authorNickname; + + private String authorAvatar; + + private LocalDateTime publishedAt; + + private LocalDateTime createdAt; + + private Boolean liked; +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/CreateArticleDTO.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/CreateArticleDTO.java new file mode 100644 index 0000000..f7af50c --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/CreateArticleDTO.java @@ -0,0 +1,30 @@ +package fun.nojava.module.blog.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.util.List; + +@Data +public class CreateArticleDTO { + + @NotBlank(message = "标题不能为空") + @Size(max = 200, message = "标题最长200字符") + private String title; + + private String summary; + + @NotBlank(message = "内容不能为空") + private String content; + + private String coverImage; + + private Long categoryId; + + private List tagIds; + + private Integer status = 0; + + private Integer visibility = 0; +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/UpdateArticleDTO.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/UpdateArticleDTO.java new file mode 100644 index 0000000..87ca976 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/dto/UpdateArticleDTO.java @@ -0,0 +1,30 @@ +package fun.nojava.module.blog.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.util.List; + +@Data +public class UpdateArticleDTO { + + @NotBlank(message = "标题不能为空") + @Size(max = 200, message = "标题最长200字符") + private String title; + + private String summary; + + @NotBlank(message = "内容不能为空") + private String content; + + private String coverImage; + + private Long categoryId; + + private List tagIds; + + private Integer status; + + private Integer visibility; +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/Article.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/Article.java new file mode 100644 index 0000000..9bf0df7 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/Article.java @@ -0,0 +1,47 @@ +package fun.nojava.module.blog.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("article") +public class Article { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long userId; + + private Long categoryId; + + private String title; + + private String summary; + + private String content; + + private String coverImage; + + private Integer status; + + private Integer visibility; + + private Integer viewCount; + + private Integer likeCount; + + private Integer commentCount; + + private LocalDateTime publishedAt; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updatedAt; + + @TableLogic + private Integer deleted; +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/ArticleLike.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/ArticleLike.java new file mode 100644 index 0000000..a9e0f9a --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/ArticleLike.java @@ -0,0 +1,21 @@ +package fun.nojava.module.blog.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("article_like") +public class ArticleLike { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long articleId; + + private Long userId; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/ArticleTag.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/ArticleTag.java new file mode 100644 index 0000000..a46eb5e --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/ArticleTag.java @@ -0,0 +1,16 @@ +package fun.nojava.module.blog.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +@Data +@TableName("article_tag") +public class ArticleTag { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long articleId; + + private Long tagId; +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/Category.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/Category.java new file mode 100644 index 0000000..b8ce05f --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/Category.java @@ -0,0 +1,25 @@ +package fun.nojava.module.blog.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("category") +public class Category { + + @TableId(type = IdType.AUTO) + private Long id; + + private String name; + + private String description; + + private Integer sort; + + private Boolean enabled; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/Tag.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/Tag.java new file mode 100644 index 0000000..0ffcb13 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/entity/Tag.java @@ -0,0 +1,19 @@ +package fun.nojava.module.blog.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("tag") +public class Tag { + + @TableId(type = IdType.AUTO) + private Long id; + + private String name; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/ArticleLikeMapper.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/ArticleLikeMapper.java new file mode 100644 index 0000000..56078eb --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/ArticleLikeMapper.java @@ -0,0 +1,9 @@ +package fun.nojava.module.blog.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import fun.nojava.module.blog.entity.ArticleLike; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ArticleLikeMapper extends BaseMapper { +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/ArticleMapper.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/ArticleMapper.java new file mode 100644 index 0000000..f8b3657 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/ArticleMapper.java @@ -0,0 +1,9 @@ +package fun.nojava.module.blog.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import fun.nojava.module.blog.entity.Article; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ArticleMapper extends BaseMapper
{ +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/ArticleTagMapper.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/ArticleTagMapper.java new file mode 100644 index 0000000..ab5d998 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/ArticleTagMapper.java @@ -0,0 +1,9 @@ +package fun.nojava.module.blog.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import fun.nojava.module.blog.entity.ArticleTag; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ArticleTagMapper extends BaseMapper { +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/CategoryMapper.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/CategoryMapper.java new file mode 100644 index 0000000..3077b99 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/CategoryMapper.java @@ -0,0 +1,9 @@ +package fun.nojava.module.blog.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import fun.nojava.module.blog.entity.Category; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CategoryMapper extends BaseMapper { +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/TagMapper.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/TagMapper.java new file mode 100644 index 0000000..ed6a0b6 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/mapper/TagMapper.java @@ -0,0 +1,9 @@ +package fun.nojava.module.blog.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import fun.nojava.module.blog.entity.Tag; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TagMapper extends BaseMapper { +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/service/ArticleService.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/service/ArticleService.java new file mode 100644 index 0000000..b9f8819 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/service/ArticleService.java @@ -0,0 +1,33 @@ +package fun.nojava.module.blog.service; + +import fun.nojava.common.model.PageResult; +import fun.nojava.module.blog.dto.*; + +public interface ArticleService { + + Long createArticle(Long userId, CreateArticleDTO dto); + + void updateArticle(Long userId, Long articleId, UpdateArticleDTO dto); + + ArticleVO getArticleDetail(Long articleId, Long currentUserId); + + PageResult listPublicArticles(int page, int pageSize, Long categoryId, Long tagId, String keyword); + + PageResult listUserArticles(Long userId, int page, int pageSize, Integer status); + + PageResult listAdminArticles(int page, int pageSize, Integer status, Long categoryId, String keyword); + + void deleteArticle(Long userId, Long articleId); + + void adminDeleteArticle(Long articleId); + + void moveToRecycleBin(Long userId, Long articleId); + + void restoreArticle(Long userId, Long articleId); + + void incrementViewCount(Long articleId); + + boolean toggleLike(Long userId, Long articleId); + + boolean isLiked(Long userId, Long articleId); +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/service/CategoryService.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/service/CategoryService.java new file mode 100644 index 0000000..6f9ba06 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/service/CategoryService.java @@ -0,0 +1,16 @@ +package fun.nojava.module.blog.service; + +import fun.nojava.module.blog.entity.Category; + +import java.util.List; + +public interface CategoryService { + + List listAllCategories(); + + Category createCategory(String name, String description); + + void updateCategory(Long id, String name, String description, Boolean enabled); + + void deleteCategory(Long id); +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/service/impl/ArticleServiceImpl.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/service/impl/ArticleServiceImpl.java new file mode 100644 index 0000000..691e007 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/service/impl/ArticleServiceImpl.java @@ -0,0 +1,330 @@ +package fun.nojava.module.blog.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import fun.nojava.common.enums.ArticleStatus; +import fun.nojava.common.enums.ArticleVisibility; +import fun.nojava.common.exception.BusinessException; +import fun.nojava.common.exception.ErrorCode; +import fun.nojava.common.model.PageResult; +import fun.nojava.module.blog.dto.*; +import fun.nojava.module.blog.entity.*; +import fun.nojava.module.blog.mapper.*; +import fun.nojava.module.blog.service.ArticleService; +import fun.nojava.module.system.entity.User; +import fun.nojava.module.system.mapper.UserMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ArticleServiceImpl implements ArticleService { + + private final ArticleMapper articleMapper; + private final CategoryMapper categoryMapper; + private final TagMapper tagMapper; + private final ArticleTagMapper articleTagMapper; + private final ArticleLikeMapper articleLikeMapper; + private final UserMapper userMapper; + + @Override + @Transactional + public Long createArticle(Long userId, CreateArticleDTO dto) { + Article article = new Article(); + article.setUserId(userId); + article.setCategoryId(dto.getCategoryId()); + article.setTitle(dto.getTitle()); + article.setSummary(dto.getSummary()); + article.setContent(dto.getContent()); + article.setCoverImage(dto.getCoverImage()); + article.setStatus(dto.getStatus()); + article.setVisibility(dto.getVisibility()); + article.setViewCount(0); + article.setLikeCount(0); + article.setCommentCount(0); + article.setDeleted(0); + + if (dto.getStatus() == ArticleStatus.PUBLISHED.getCode()) { + article.setPublishedAt(LocalDateTime.now()); + } + + articleMapper.insert(article); + + if (dto.getTagIds() != null && !dto.getTagIds().isEmpty()) { + for (Long tagId : dto.getTagIds()) { + ArticleTag articleTag = new ArticleTag(); + articleTag.setArticleId(article.getId()); + articleTag.setTagId(tagId); + articleTagMapper.insert(articleTag); + } + } + + return article.getId(); + } + + @Override + @Transactional + public void updateArticle(Long userId, Long articleId, UpdateArticleDTO dto) { + Article article = getArticleAndCheckOwner(userId, articleId); + + article.setTitle(dto.getTitle()); + article.setSummary(dto.getSummary()); + article.setContent(dto.getContent()); + article.setCoverImage(dto.getCoverImage()); + article.setCategoryId(dto.getCategoryId()); + if (dto.getStatus() != null) { + article.setStatus(dto.getStatus()); + if (dto.getStatus() == ArticleStatus.PUBLISHED.getCode() && article.getPublishedAt() == null) { + article.setPublishedAt(LocalDateTime.now()); + } + } + if (dto.getVisibility() != null) { + article.setVisibility(dto.getVisibility()); + } + + articleMapper.updateById(article); + + if (dto.getTagIds() != null) { + articleTagMapper.delete(new LambdaQueryWrapper().eq(ArticleTag::getArticleId, articleId)); + for (Long tagId : dto.getTagIds()) { + ArticleTag articleTag = new ArticleTag(); + articleTag.setArticleId(articleId); + articleTag.setTagId(tagId); + articleTagMapper.insert(articleTag); + } + } + } + + @Override + public ArticleVO getArticleDetail(Long articleId, Long currentUserId) { + Article article = articleMapper.selectById(articleId); + if (article == null) { + throw new BusinessException(ErrorCode.ARTICLE_NOT_FOUND); + } + + return buildArticleVO(article, currentUserId); + } + + @Override + public PageResult listPublicArticles(int page, int pageSize, Long categoryId, Long tagId, String keyword) { + LambdaQueryWrapper
wrapper = new LambdaQueryWrapper
() + .eq(Article::getStatus, ArticleStatus.PUBLISHED.getCode()) + .eq(Article::getVisibility, ArticleVisibility.PUBLIC.getCode()); + + if (categoryId != null) { + wrapper.eq(Article::getCategoryId, categoryId); + } + if (keyword != null && !keyword.isEmpty()) { + wrapper.and(w -> w.like(Article::getTitle, keyword).or().like(Article::getSummary, keyword)); + } + if (tagId != null) { + List articleIds = articleTagMapper.selectList( + new LambdaQueryWrapper().eq(ArticleTag::getTagId, tagId) + ).stream().map(ArticleTag::getArticleId).toList(); + if (articleIds.isEmpty()) { + return new PageResult<>(Collections.emptyList(), 0, page, pageSize); + } + wrapper.in(Article::getId, articleIds); + } + + wrapper.orderByDesc(Article::getPublishedAt); + Page
articlePage = articleMapper.selectPage(new Page<>(page, pageSize), wrapper); + + List records = articlePage.getRecords().stream() + .map(this::buildArticleListVO) + .toList(); + + return new PageResult<>(records, articlePage.getTotal(), page, pageSize); + } + + @Override + public PageResult listUserArticles(Long userId, int page, int pageSize, Integer status) { + LambdaQueryWrapper
wrapper = new LambdaQueryWrapper
() + .eq(Article::getUserId, userId); + if (status != null) { + wrapper.eq(Article::getStatus, status); + } + wrapper.orderByDesc(Article::getCreatedAt); + + Page
articlePage = articleMapper.selectPage(new Page<>(page, pageSize), wrapper); + List records = articlePage.getRecords().stream() + .map(this::buildArticleListVO) + .toList(); + + return new PageResult<>(records, articlePage.getTotal(), page, pageSize); + } + + @Override + public PageResult listAdminArticles(int page, int pageSize, Integer status, Long categoryId, String keyword) { + LambdaQueryWrapper
wrapper = new LambdaQueryWrapper<>(); + if (status != null) { + wrapper.eq(Article::getStatus, status); + } + if (categoryId != null) { + wrapper.eq(Article::getCategoryId, categoryId); + } + if (keyword != null && !keyword.isEmpty()) { + wrapper.and(w -> w.like(Article::getTitle, keyword).or().like(Article::getSummary, keyword)); + } + wrapper.orderByDesc(Article::getCreatedAt); + + Page
articlePage = articleMapper.selectPage(new Page<>(page, pageSize), wrapper); + List records = articlePage.getRecords().stream() + .map(this::buildArticleListVO) + .toList(); + + return new PageResult<>(records, articlePage.getTotal(), page, pageSize); + } + + @Override + @Transactional + public void deleteArticle(Long userId, Long articleId) { + Article article = getArticleAndCheckOwner(userId, articleId); + articleMapper.deleteById(articleId); + } + + @Override + @Transactional + public void adminDeleteArticle(Long articleId) { + Article article = articleMapper.selectById(articleId); + if (article == null) { + throw new BusinessException(ErrorCode.ARTICLE_NOT_FOUND); + } + articleMapper.deleteById(articleId); + } + + @Override + @Transactional + public void moveToRecycleBin(Long userId, Long articleId) { + Article article = getArticleAndCheckOwner(userId, articleId); + article.setStatus(ArticleStatus.RECYCLE.getCode()); + article.setUpdatedAt(LocalDateTime.now()); + articleMapper.updateById(article); + } + + @Override + @Transactional + public void restoreArticle(Long userId, Long articleId) { + Article article = getArticleAndCheckOwner(userId, articleId); + article.setStatus(ArticleStatus.DRAFT.getCode()); + article.setUpdatedAt(LocalDateTime.now()); + articleMapper.updateById(article); + } + + @Override + public void incrementViewCount(Long articleId) { + Article article = articleMapper.selectById(articleId); + if (article != null) { + article.setViewCount(article.getViewCount() + 1); + articleMapper.updateById(article); + } + } + + @Override + @Transactional + public boolean toggleLike(Long userId, Long articleId) { + ArticleLike existing = articleLikeMapper.selectOne( + new LambdaQueryWrapper() + .eq(ArticleLike::getArticleId, articleId) + .eq(ArticleLike::getUserId, userId) + ); + + Article article = articleMapper.selectById(articleId); + if (article == null) { + throw new BusinessException(ErrorCode.ARTICLE_NOT_FOUND); + } + + if (existing != null) { + articleLikeMapper.deleteById(existing.getId()); + article.setLikeCount(article.getLikeCount() - 1); + articleMapper.updateById(article); + return false; + } else { + ArticleLike like = new ArticleLike(); + like.setArticleId(articleId); + like.setUserId(userId); + articleLikeMapper.insert(like); + article.setLikeCount(article.getLikeCount() + 1); + articleMapper.updateById(article); + return true; + } + } + + @Override + public boolean isLiked(Long userId, Long articleId) { + if (userId == null) return false; + return articleLikeMapper.selectCount( + new LambdaQueryWrapper() + .eq(ArticleLike::getArticleId, articleId) + .eq(ArticleLike::getUserId, userId) + ) > 0; + } + + private Article getArticleAndCheckOwner(Long userId, Long articleId) { + Article article = articleMapper.selectById(articleId); + if (article == null) { + throw new BusinessException(ErrorCode.ARTICLE_NOT_FOUND); + } + if (!article.getUserId().equals(userId)) { + throw new BusinessException(ErrorCode.PERMISSION_DENIED); + } + return article; + } + + private ArticleVO buildArticleVO(Article article, Long currentUserId) { + User author = userMapper.selectById(article.getUserId()); + Category category = article.getCategoryId() != null ? categoryMapper.selectById(article.getCategoryId()) : null; + + List tags = articleTagMapper.selectList( + new LambdaQueryWrapper().eq(ArticleTag::getArticleId, article.getId()) + ).stream().map(at -> { + Tag tag = tagMapper.selectById(at.getTagId()); + return tag != null ? tag.getName() : null; + }).filter(t -> t != null).toList(); + + return ArticleVO.builder() + .id(article.getId()) + .title(article.getTitle()) + .summary(article.getSummary()) + .content(article.getContent()) + .coverImage(article.getCoverImage()) + .viewCount(article.getViewCount()) + .likeCount(article.getLikeCount()) + .commentCount(article.getCommentCount()) + .categoryId(article.getCategoryId()) + .categoryName(category != null ? category.getName() : null) + .tags(tags) + .userId(article.getUserId()) + .authorNickname(author != null ? author.getNickname() : null) + .authorAvatar(author != null ? author.getAvatar() : null) + .publishedAt(article.getPublishedAt()) + .createdAt(article.getCreatedAt()) + .liked(isLiked(currentUserId, article.getId())) + .build(); + } + + private ArticleListVO buildArticleListVO(Article article) { + User author = userMapper.selectById(article.getUserId()); + Category category = article.getCategoryId() != null ? categoryMapper.selectById(article.getCategoryId()) : null; + + return ArticleListVO.builder() + .id(article.getId()) + .title(article.getTitle()) + .summary(article.getSummary()) + .coverImage(article.getCoverImage()) + .viewCount(article.getViewCount()) + .likeCount(article.getLikeCount()) + .commentCount(article.getCommentCount()) + .userId(article.getUserId()) + .authorNickname(author != null ? author.getNickname() : null) + .authorAvatar(author != null ? author.getAvatar() : null) + .categoryName(category != null ? category.getName() : null) + .publishedAt(article.getPublishedAt()) + .build(); + } +} diff --git a/jog-module-blog/src/main/java/fun/nojava/module/blog/service/impl/CategoryServiceImpl.java b/jog-module-blog/src/main/java/fun/nojava/module/blog/service/impl/CategoryServiceImpl.java new file mode 100644 index 0000000..1c7cbc0 --- /dev/null +++ b/jog-module-blog/src/main/java/fun/nojava/module/blog/service/impl/CategoryServiceImpl.java @@ -0,0 +1,53 @@ +package fun.nojava.module.blog.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import fun.nojava.module.blog.entity.Category; +import fun.nojava.module.blog.mapper.CategoryMapper; +import fun.nojava.module.blog.service.CategoryService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class CategoryServiceImpl implements CategoryService { + + private final CategoryMapper categoryMapper; + + @Override + public List listAllCategories() { + return categoryMapper.selectList( + new LambdaQueryWrapper() + .eq(Category::getEnabled, true) + .orderByAsc(Category::getSort) + ); + } + + @Override + public Category createCategory(String name, String description) { + Category category = new Category(); + category.setName(name); + category.setDescription(description); + category.setSort(0); + category.setEnabled(true); + categoryMapper.insert(category); + return category; + } + + @Override + public void updateCategory(Long id, String name, String description, Boolean enabled) { + Category category = categoryMapper.selectById(id); + if (category != null) { + if (name != null) category.setName(name); + if (description != null) category.setDescription(description); + if (enabled != null) category.setEnabled(enabled); + categoryMapper.updateById(category); + } + } + + @Override + public void deleteCategory(Long id) { + categoryMapper.deleteById(id); + } +} diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/controller/AdminArticleController.class b/jog-module-blog/target/classes/fun/nojava/module/blog/controller/AdminArticleController.class new file mode 100644 index 0000000..188ea5f Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/controller/AdminArticleController.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/controller/AdminCategoryController.class b/jog-module-blog/target/classes/fun/nojava/module/blog/controller/AdminCategoryController.class new file mode 100644 index 0000000..4924b4d Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/controller/AdminCategoryController.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/controller/ArticleController.class b/jog-module-blog/target/classes/fun/nojava/module/blog/controller/ArticleController.class new file mode 100644 index 0000000..2d14db5 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/controller/ArticleController.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/controller/PublicArticleController.class b/jog-module-blog/target/classes/fun/nojava/module/blog/controller/PublicArticleController.class new file mode 100644 index 0000000..87dabcc Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/controller/PublicArticleController.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/controller/PublicCategoryController.class b/jog-module-blog/target/classes/fun/nojava/module/blog/controller/PublicCategoryController.class new file mode 100644 index 0000000..eb095cb Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/controller/PublicCategoryController.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleListVO$ArticleListVOBuilder.class b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleListVO$ArticleListVOBuilder.class new file mode 100644 index 0000000..2297ea6 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleListVO$ArticleListVOBuilder.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleListVO.class b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleListVO.class new file mode 100644 index 0000000..6f4506f Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleListVO.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleVO$ArticleVOBuilder.class b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleVO$ArticleVOBuilder.class new file mode 100644 index 0000000..9f200e5 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleVO$ArticleVOBuilder.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleVO.class b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleVO.class new file mode 100644 index 0000000..e09614c Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/ArticleVO.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/dto/CreateArticleDTO.class b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/CreateArticleDTO.class new file mode 100644 index 0000000..e524c11 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/CreateArticleDTO.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/dto/UpdateArticleDTO.class b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/UpdateArticleDTO.class new file mode 100644 index 0000000..dd33766 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/dto/UpdateArticleDTO.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/entity/Article.class b/jog-module-blog/target/classes/fun/nojava/module/blog/entity/Article.class new file mode 100644 index 0000000..91d5efe Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/entity/Article.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/entity/ArticleLike.class b/jog-module-blog/target/classes/fun/nojava/module/blog/entity/ArticleLike.class new file mode 100644 index 0000000..5e9a48a Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/entity/ArticleLike.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/entity/ArticleTag.class b/jog-module-blog/target/classes/fun/nojava/module/blog/entity/ArticleTag.class new file mode 100644 index 0000000..96c2fcb Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/entity/ArticleTag.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/entity/Category.class b/jog-module-blog/target/classes/fun/nojava/module/blog/entity/Category.class new file mode 100644 index 0000000..bc9c85f Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/entity/Category.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/entity/Tag.class b/jog-module-blog/target/classes/fun/nojava/module/blog/entity/Tag.class new file mode 100644 index 0000000..5bf71e2 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/entity/Tag.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/ArticleLikeMapper.class b/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/ArticleLikeMapper.class new file mode 100644 index 0000000..069fd23 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/ArticleLikeMapper.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/ArticleMapper.class b/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/ArticleMapper.class new file mode 100644 index 0000000..659c45d Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/ArticleMapper.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/ArticleTagMapper.class b/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/ArticleTagMapper.class new file mode 100644 index 0000000..6f11729 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/ArticleTagMapper.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/CategoryMapper.class b/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/CategoryMapper.class new file mode 100644 index 0000000..722e1c8 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/CategoryMapper.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/TagMapper.class b/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/TagMapper.class new file mode 100644 index 0000000..115ec73 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/mapper/TagMapper.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/service/ArticleService.class b/jog-module-blog/target/classes/fun/nojava/module/blog/service/ArticleService.class new file mode 100644 index 0000000..2d68099 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/service/ArticleService.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/service/CategoryService.class b/jog-module-blog/target/classes/fun/nojava/module/blog/service/CategoryService.class new file mode 100644 index 0000000..eb91a12 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/service/CategoryService.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/service/impl/ArticleServiceImpl.class b/jog-module-blog/target/classes/fun/nojava/module/blog/service/impl/ArticleServiceImpl.class new file mode 100644 index 0000000..e5a3c1b Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/service/impl/ArticleServiceImpl.class differ diff --git a/jog-module-blog/target/classes/fun/nojava/module/blog/service/impl/CategoryServiceImpl.class b/jog-module-blog/target/classes/fun/nojava/module/blog/service/impl/CategoryServiceImpl.class new file mode 100644 index 0000000..9e341b2 Binary files /dev/null and b/jog-module-blog/target/classes/fun/nojava/module/blog/service/impl/CategoryServiceImpl.class differ diff --git a/jog-module-blog/target/jog-module-blog-1.0.0.jar b/jog-module-blog/target/jog-module-blog-1.0.0.jar new file mode 100644 index 0000000..c3f2dd9 Binary files /dev/null and b/jog-module-blog/target/jog-module-blog-1.0.0.jar differ diff --git a/jog-module-blog/target/maven-archiver/pom.properties b/jog-module-blog/target/maven-archiver/pom.properties new file mode 100644 index 0000000..37e075a --- /dev/null +++ b/jog-module-blog/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=jog-module-blog +groupId=fun.nojava +version=1.0.0 diff --git a/jog-module-blog/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/jog-module-blog/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..c2dd24f --- /dev/null +++ b/jog-module-blog/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,25 @@ +fun\nojava\module\blog\entity\ArticleTag.class +fun\nojava\module\blog\service\ArticleService.class +fun\nojava\module\blog\controller\PublicArticleController.class +fun\nojava\module\blog\controller\PublicCategoryController.class +fun\nojava\module\blog\dto\UpdateArticleDTO.class +fun\nojava\module\blog\service\CategoryService.class +fun\nojava\module\blog\dto\ArticleListVO.class +fun\nojava\module\blog\entity\Category.class +fun\nojava\module\blog\dto\CreateArticleDTO.class +fun\nojava\module\blog\service\impl\ArticleServiceImpl.class +fun\nojava\module\blog\dto\ArticleVO$ArticleVOBuilder.class +fun\nojava\module\blog\controller\AdminCategoryController.class +fun\nojava\module\blog\service\impl\CategoryServiceImpl.class +fun\nojava\module\blog\dto\ArticleVO.class +fun\nojava\module\blog\entity\ArticleLike.class +fun\nojava\module\blog\controller\AdminArticleController.class +fun\nojava\module\blog\controller\ArticleController.class +fun\nojava\module\blog\mapper\TagMapper.class +fun\nojava\module\blog\mapper\ArticleLikeMapper.class +fun\nojava\module\blog\dto\ArticleListVO$ArticleListVOBuilder.class +fun\nojava\module\blog\entity\Tag.class +fun\nojava\module\blog\mapper\ArticleMapper.class +fun\nojava\module\blog\entity\Article.class +fun\nojava\module\blog\mapper\CategoryMapper.class +fun\nojava\module\blog\mapper\ArticleTagMapper.class diff --git a/jog-module-blog/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/jog-module-blog/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..d7177b8 --- /dev/null +++ b/jog-module-blog/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,22 @@ +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\controller\AdminArticleController.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\controller\ArticleController.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\controller\CategoryController.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\controller\PublicArticleController.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\dto\ArticleListVO.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\dto\ArticleVO.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\dto\CreateArticleDTO.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\dto\UpdateArticleDTO.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\entity\Article.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\entity\ArticleLike.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\entity\ArticleTag.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\entity\Category.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\entity\Tag.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\mapper\ArticleLikeMapper.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\mapper\ArticleMapper.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\mapper\ArticleTagMapper.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\mapper\CategoryMapper.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\mapper\TagMapper.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\service\ArticleService.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\service\CategoryService.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\service\impl\ArticleServiceImpl.java +E:\person\Jog\jog-module-blog\src\main\java\fun\nojava\module\blog\service\impl\CategoryServiceImpl.java diff --git a/jog-module-comment/src/main/java/fun/nojava/module/comment/controller/CommentController.java b/jog-module-comment/src/main/java/fun/nojava/module/comment/controller/CommentController.java new file mode 100644 index 0000000..1d090f1 --- /dev/null +++ b/jog-module-comment/src/main/java/fun/nojava/module/comment/controller/CommentController.java @@ -0,0 +1,91 @@ +package fun.nojava.module.comment.controller; + +import fun.nojava.common.model.ApiResult; +import fun.nojava.common.model.PageResult; +import fun.nojava.module.comment.dto.CommentVO; +import fun.nojava.module.comment.dto.CreateCommentDTO; +import fun.nojava.module.comment.service.CommentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "评论接口") +@RestController +@RequestMapping("/api/comment") +@RequiredArgsConstructor +public class CommentController { + + private final CommentService commentService; + + @Operation(summary = "发表评论") + @PostMapping + public ApiResult createComment( + @AuthenticationPrincipal Long userId, + @Valid @RequestBody CreateCommentDTO dto) { + return ApiResult.success(commentService.createComment(userId, dto)); + } + + @Operation(summary = "删除评论") + @DeleteMapping("/{id}") + public ApiResult deleteComment( + @AuthenticationPrincipal Long userId, + @PathVariable Long id) { + commentService.deleteComment(userId, id); + return ApiResult.success(); + } + + @Operation(summary = "文章评论列表") + @GetMapping("/article/{articleId}") + public ApiResult> listArticleComments( + @PathVariable Long articleId, + @AuthenticationPrincipal Long userId) { + return ApiResult.success(commentService.listArticleComments(articleId, userId)); + } + + @Operation(summary = "点赞评论") + @PostMapping("/{id}/like") + public ApiResult toggleLike( + @PathVariable Long id, + @AuthenticationPrincipal Long userId) { + return ApiResult.success(commentService.toggleLike(userId, id)); + } +} + +@Tag(name = "管理员-评论管理") +@RestController +@RequestMapping("/api/admin/comment") +@RequiredArgsConstructor +class AdminCommentController { + + private final CommentService commentService; + + @Operation(summary = "评论列表") + @GetMapping("/list") + public ApiResult> listComments( + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "15") int pageSize, + @RequestParam(required = false) Integer status) { + return ApiResult.success(commentService.listAdminComments(page, pageSize, status)); + } + + @Operation(summary = "审核评论") + @PostMapping("/{id}/audit") + public ApiResult auditComment( + @PathVariable Long id, + @RequestParam Integer status) { + commentService.auditComment(id, status); + return ApiResult.success(); + } + + @Operation(summary = "删除评论") + @DeleteMapping("/{id}") + public ApiResult deleteComment(@PathVariable Long id) { + commentService.auditComment(id, 3); + return ApiResult.success(); + } +} diff --git a/jog-module-comment/src/main/java/fun/nojava/module/comment/dto/CommentVO.java b/jog-module-comment/src/main/java/fun/nojava/module/comment/dto/CommentVO.java new file mode 100644 index 0000000..0209f59 --- /dev/null +++ b/jog-module-comment/src/main/java/fun/nojava/module/comment/dto/CommentVO.java @@ -0,0 +1,44 @@ +package fun.nojava.module.comment.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CommentVO { + + private Long id; + + private Long articleId; + + private Long userId; + + private String nickname; + + private String avatar; + + private Long parentId; + + private Long replyToId; + + private String replyToNickname; + + private String content; + + private Integer status; + + private Integer likeCount; + + private LocalDateTime createdAt; + + private Boolean liked; + + private List replies; +} diff --git a/jog-module-comment/src/main/java/fun/nojava/module/comment/dto/CreateCommentDTO.java b/jog-module-comment/src/main/java/fun/nojava/module/comment/dto/CreateCommentDTO.java new file mode 100644 index 0000000..739ad61 --- /dev/null +++ b/jog-module-comment/src/main/java/fun/nojava/module/comment/dto/CreateCommentDTO.java @@ -0,0 +1,19 @@ +package fun.nojava.module.comment.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class CreateCommentDTO { + + @NotNull(message = "文章ID不能为空") + private Long articleId; + + private Long parentId; + + private Long replyToId; + + @NotBlank(message = "评论内容不能为空") + private String content; +} diff --git a/jog-module-comment/src/main/java/fun/nojava/module/comment/entity/Comment.java b/jog-module-comment/src/main/java/fun/nojava/module/comment/entity/Comment.java new file mode 100644 index 0000000..40e73dd --- /dev/null +++ b/jog-module-comment/src/main/java/fun/nojava/module/comment/entity/Comment.java @@ -0,0 +1,34 @@ +package fun.nojava.module.comment.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("comment") +public class Comment { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long articleId; + + private Long userId; + + private Long parentId; + + private Long replyToId; + + private String content; + + private Integer status; + + private Integer likeCount; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; + + @TableLogic + private Integer deleted; +} diff --git a/jog-module-comment/src/main/java/fun/nojava/module/comment/entity/CommentLike.java b/jog-module-comment/src/main/java/fun/nojava/module/comment/entity/CommentLike.java new file mode 100644 index 0000000..d03da2a --- /dev/null +++ b/jog-module-comment/src/main/java/fun/nojava/module/comment/entity/CommentLike.java @@ -0,0 +1,21 @@ +package fun.nojava.module.comment.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("comment_like") +public class CommentLike { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long commentId; + + private Long userId; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; +} diff --git a/jog-module-comment/src/main/java/fun/nojava/module/comment/mapper/CommentLikeMapper.java b/jog-module-comment/src/main/java/fun/nojava/module/comment/mapper/CommentLikeMapper.java new file mode 100644 index 0000000..f4540e7 --- /dev/null +++ b/jog-module-comment/src/main/java/fun/nojava/module/comment/mapper/CommentLikeMapper.java @@ -0,0 +1,9 @@ +package fun.nojava.module.comment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import fun.nojava.module.comment.entity.CommentLike; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CommentLikeMapper extends BaseMapper { +} diff --git a/jog-module-comment/src/main/java/fun/nojava/module/comment/mapper/CommentMapper.java b/jog-module-comment/src/main/java/fun/nojava/module/comment/mapper/CommentMapper.java new file mode 100644 index 0000000..3bec70b --- /dev/null +++ b/jog-module-comment/src/main/java/fun/nojava/module/comment/mapper/CommentMapper.java @@ -0,0 +1,9 @@ +package fun.nojava.module.comment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import fun.nojava.module.comment.entity.Comment; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CommentMapper extends BaseMapper { +} diff --git a/jog-module-comment/src/main/java/fun/nojava/module/comment/service/CommentService.java b/jog-module-comment/src/main/java/fun/nojava/module/comment/service/CommentService.java new file mode 100644 index 0000000..892401e --- /dev/null +++ b/jog-module-comment/src/main/java/fun/nojava/module/comment/service/CommentService.java @@ -0,0 +1,22 @@ +package fun.nojava.module.comment.service; + +import fun.nojava.common.model.PageResult; +import fun.nojava.module.comment.dto.CommentVO; +import fun.nojava.module.comment.dto.CreateCommentDTO; + +import java.util.List; + +public interface CommentService { + + Long createComment(Long userId, CreateCommentDTO dto); + + void deleteComment(Long userId, Long commentId); + + List listArticleComments(Long articleId, Long currentUserId); + + PageResult listAdminComments(int page, int pageSize, Integer status); + + void auditComment(Long commentId, Integer status); + + boolean toggleLike(Long userId, Long commentId); +} diff --git a/jog-module-comment/src/main/java/fun/nojava/module/comment/service/impl/CommentServiceImpl.java b/jog-module-comment/src/main/java/fun/nojava/module/comment/service/impl/CommentServiceImpl.java new file mode 100644 index 0000000..879f7e3 --- /dev/null +++ b/jog-module-comment/src/main/java/fun/nojava/module/comment/service/impl/CommentServiceImpl.java @@ -0,0 +1,185 @@ +package fun.nojava.module.comment.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import fun.nojava.common.enums.CommentStatus; +import fun.nojava.common.exception.BusinessException; +import fun.nojava.common.exception.ErrorCode; +import fun.nojava.common.model.PageResult; +import fun.nojava.module.comment.dto.CommentVO; +import fun.nojava.module.comment.dto.CreateCommentDTO; +import fun.nojava.module.comment.entity.Comment; +import fun.nojava.module.comment.entity.CommentLike; +import fun.nojava.module.comment.mapper.CommentLikeMapper; +import fun.nojava.module.comment.mapper.CommentMapper; +import fun.nojava.module.comment.service.CommentService; +import fun.nojava.module.system.entity.User; +import fun.nojava.module.system.mapper.UserMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class CommentServiceImpl implements CommentService { + + private final CommentMapper commentMapper; + private final CommentLikeMapper commentLikeMapper; + private final UserMapper userMapper; + + @Override + @Transactional + public Long createComment(Long userId, CreateCommentDTO dto) { + if (dto.getParentId() != null) { + Comment parent = commentMapper.selectById(dto.getParentId()); + if (parent == null) { + throw new BusinessException(ErrorCode.COMMENT_NOT_FOUND); + } + if (parent.getParentId() != null) { + throw new BusinessException(ErrorCode.COMMENT_LEVEL_LIMIT); + } + } + + Comment comment = new Comment(); + comment.setArticleId(dto.getArticleId()); + comment.setUserId(userId); + comment.setParentId(dto.getParentId()); + comment.setReplyToId(dto.getReplyToId()); + comment.setContent(dto.getContent()); + comment.setStatus(CommentStatus.APPROVED.getCode()); + comment.setLikeCount(0); + comment.setDeleted(0); + commentMapper.insert(comment); + + return comment.getId(); + } + + @Override + @Transactional + public void deleteComment(Long userId, Long commentId) { + Comment comment = commentMapper.selectById(commentId); + if (comment == null) { + throw new BusinessException(ErrorCode.COMMENT_NOT_FOUND); + } + if (!comment.getUserId().equals(userId)) { + throw new BusinessException(ErrorCode.PERMISSION_DENIED); + } + commentMapper.deleteById(commentId); + } + + @Override + public List listArticleComments(Long articleId, Long currentUserId) { + List rootComments = commentMapper.selectList( + new LambdaQueryWrapper() + .eq(Comment::getArticleId, articleId) + .eq(Comment::getParentId, null) + .eq(Comment::getStatus, CommentStatus.APPROVED.getCode()) + .orderByDesc(Comment::getCreatedAt) + ); + + List result = new ArrayList<>(); + for (Comment root : rootComments) { + CommentVO vo = buildCommentVO(root, currentUserId); + List replies = commentMapper.selectList( + new LambdaQueryWrapper() + .eq(Comment::getParentId, root.getId()) + .eq(Comment::getStatus, CommentStatus.APPROVED.getCode()) + .orderByAsc(Comment::getCreatedAt) + ); + vo.setReplies(replies.stream().map(r -> buildCommentVO(r, currentUserId)).toList()); + result.add(vo); + } + + return result; + } + + @Override + public PageResult listAdminComments(int page, int pageSize, Integer status) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (status != null) { + wrapper.eq(Comment::getStatus, status); + } + wrapper.orderByDesc(Comment::getCreatedAt); + + Page commentPage = commentMapper.selectPage(new Page<>(page, pageSize), wrapper); + List records = commentPage.getRecords().stream() + .map(c -> buildCommentVO(c, null)) + .toList(); + + return new PageResult<>(records, commentPage.getTotal(), page, pageSize); + } + + @Override + @Transactional + public void auditComment(Long commentId, Integer status) { + Comment comment = commentMapper.selectById(commentId); + if (comment == null) { + throw new BusinessException(ErrorCode.COMMENT_NOT_FOUND); + } + comment.setStatus(status); + commentMapper.updateById(comment); + } + + @Override + @Transactional + public boolean toggleLike(Long userId, Long commentId) { + CommentLike existing = commentLikeMapper.selectOne( + new LambdaQueryWrapper() + .eq(CommentLike::getCommentId, commentId) + .eq(CommentLike::getUserId, userId) + ); + + Comment comment = commentMapper.selectById(commentId); + if (comment == null) { + throw new BusinessException(ErrorCode.COMMENT_NOT_FOUND); + } + + if (existing != null) { + commentLikeMapper.deleteById(existing.getId()); + comment.setLikeCount(comment.getLikeCount() - 1); + commentMapper.updateById(comment); + return false; + } else { + CommentLike like = new CommentLike(); + like.setCommentId(commentId); + like.setUserId(userId); + commentLikeMapper.insert(like); + comment.setLikeCount(comment.getLikeCount() + 1); + commentMapper.updateById(comment); + return true; + } + } + + private CommentVO buildCommentVO(Comment comment, Long currentUserId) { + User user = userMapper.selectById(comment.getUserId()); + User replyToUser = comment.getReplyToId() != null ? userMapper.selectById(comment.getReplyToId()) : null; + + boolean liked = false; + if (currentUserId != null) { + liked = commentLikeMapper.selectCount( + new LambdaQueryWrapper() + .eq(CommentLike::getCommentId, comment.getId()) + .eq(CommentLike::getUserId, currentUserId) + ) > 0; + } + + return CommentVO.builder() + .id(comment.getId()) + .articleId(comment.getArticleId()) + .userId(comment.getUserId()) + .nickname(user != null ? user.getNickname() : null) + .avatar(user != null ? user.getAvatar() : null) + .parentId(comment.getParentId()) + .replyToId(comment.getReplyToId()) + .replyToNickname(replyToUser != null ? replyToUser.getNickname() : null) + .content(comment.getContent()) + .status(comment.getStatus()) + .likeCount(comment.getLikeCount()) + .createdAt(comment.getCreatedAt()) + .liked(liked) + .build(); + } +} diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/controller/AdminCommentController.class b/jog-module-comment/target/classes/fun/nojava/module/comment/controller/AdminCommentController.class new file mode 100644 index 0000000..ca98dbc Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/controller/AdminCommentController.class differ diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/controller/CommentController.class b/jog-module-comment/target/classes/fun/nojava/module/comment/controller/CommentController.class new file mode 100644 index 0000000..c7caeba Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/controller/CommentController.class differ diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/dto/CommentVO$CommentVOBuilder.class b/jog-module-comment/target/classes/fun/nojava/module/comment/dto/CommentVO$CommentVOBuilder.class new file mode 100644 index 0000000..12f71a6 Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/dto/CommentVO$CommentVOBuilder.class differ diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/dto/CommentVO.class b/jog-module-comment/target/classes/fun/nojava/module/comment/dto/CommentVO.class new file mode 100644 index 0000000..7a5a23f Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/dto/CommentVO.class differ diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/dto/CreateCommentDTO.class b/jog-module-comment/target/classes/fun/nojava/module/comment/dto/CreateCommentDTO.class new file mode 100644 index 0000000..59e2689 Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/dto/CreateCommentDTO.class differ diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/entity/Comment.class b/jog-module-comment/target/classes/fun/nojava/module/comment/entity/Comment.class new file mode 100644 index 0000000..20ca366 Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/entity/Comment.class differ diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/entity/CommentLike.class b/jog-module-comment/target/classes/fun/nojava/module/comment/entity/CommentLike.class new file mode 100644 index 0000000..a8901cb Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/entity/CommentLike.class differ diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/mapper/CommentLikeMapper.class b/jog-module-comment/target/classes/fun/nojava/module/comment/mapper/CommentLikeMapper.class new file mode 100644 index 0000000..2770ddb Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/mapper/CommentLikeMapper.class differ diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/mapper/CommentMapper.class b/jog-module-comment/target/classes/fun/nojava/module/comment/mapper/CommentMapper.class new file mode 100644 index 0000000..ffcd297 Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/mapper/CommentMapper.class differ diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/service/CommentService.class b/jog-module-comment/target/classes/fun/nojava/module/comment/service/CommentService.class new file mode 100644 index 0000000..6d62074 Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/service/CommentService.class differ diff --git a/jog-module-comment/target/classes/fun/nojava/module/comment/service/impl/CommentServiceImpl.class b/jog-module-comment/target/classes/fun/nojava/module/comment/service/impl/CommentServiceImpl.class new file mode 100644 index 0000000..dcf4da9 Binary files /dev/null and b/jog-module-comment/target/classes/fun/nojava/module/comment/service/impl/CommentServiceImpl.class differ diff --git a/jog-module-comment/target/jog-module-comment-1.0.0.jar b/jog-module-comment/target/jog-module-comment-1.0.0.jar new file mode 100644 index 0000000..23f17ba Binary files /dev/null and b/jog-module-comment/target/jog-module-comment-1.0.0.jar differ diff --git a/jog-module-comment/target/maven-archiver/pom.properties b/jog-module-comment/target/maven-archiver/pom.properties new file mode 100644 index 0000000..2dc3503 --- /dev/null +++ b/jog-module-comment/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=jog-module-comment +groupId=fun.nojava +version=1.0.0 diff --git a/jog-module-comment/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/jog-module-comment/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..c96eb1c --- /dev/null +++ b/jog-module-comment/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,11 @@ +fun\nojava\module\comment\dto\CreateCommentDTO.class +fun\nojava\module\comment\dto\CommentVO.class +fun\nojava\module\comment\entity\Comment.class +fun\nojava\module\comment\entity\CommentLike.class +fun\nojava\module\comment\controller\CommentController.class +fun\nojava\module\comment\service\CommentService.class +fun\nojava\module\comment\service\impl\CommentServiceImpl.class +fun\nojava\module\comment\dto\CommentVO$CommentVOBuilder.class +fun\nojava\module\comment\mapper\CommentLikeMapper.class +fun\nojava\module\comment\mapper\CommentMapper.class +fun\nojava\module\comment\controller\AdminCommentController.class diff --git a/jog-module-comment/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/jog-module-comment/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..d35b798 --- /dev/null +++ b/jog-module-comment/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,9 @@ +E:\person\Jog\jog-module-comment\src\main\java\fun\nojava\module\comment\controller\CommentController.java +E:\person\Jog\jog-module-comment\src\main\java\fun\nojava\module\comment\dto\CommentVO.java +E:\person\Jog\jog-module-comment\src\main\java\fun\nojava\module\comment\dto\CreateCommentDTO.java +E:\person\Jog\jog-module-comment\src\main\java\fun\nojava\module\comment\entity\Comment.java +E:\person\Jog\jog-module-comment\src\main\java\fun\nojava\module\comment\entity\CommentLike.java +E:\person\Jog\jog-module-comment\src\main\java\fun\nojava\module\comment\mapper\CommentLikeMapper.java +E:\person\Jog\jog-module-comment\src\main\java\fun\nojava\module\comment\mapper\CommentMapper.java +E:\person\Jog\jog-module-comment\src\main\java\fun\nojava\module\comment\service\CommentService.java +E:\person\Jog\jog-module-comment\src\main\java\fun\nojava\module\comment\service\impl\CommentServiceImpl.java diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/controller/AuthController.java b/jog-module-system/src/main/java/fun/nojava/module/system/controller/AuthController.java new file mode 100644 index 0000000..4406ba9 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/controller/AuthController.java @@ -0,0 +1,62 @@ +package fun.nojava.module.system.controller; + +import fun.nojava.common.annotation.RateLimit; +import fun.nojava.common.model.ApiResult; +import fun.nojava.framework.util.WebUtils; +import fun.nojava.module.system.dto.LoginDTO; +import fun.nojava.module.system.dto.LoginVO; +import fun.nojava.module.system.dto.RegisterDTO; +import fun.nojava.module.system.service.AuthService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "认证接口") +@RestController +@RequestMapping("/api/auth") +@RequiredArgsConstructor +public class AuthController { + + private final AuthService authService; + + @Operation(summary = "用户登录") + @PostMapping("/login") + @RateLimit(key = "login", maxCount = 5, duration = 60) + public ApiResult login(@Valid @RequestBody LoginDTO dto) { + LoginVO vo = authService.login(dto, WebUtils.getClientIp(), WebUtils.getUserAgent()); + return ApiResult.success(vo); + } + + @Operation(summary = "管理员登录") + @PostMapping("/admin/login") + @RateLimit(key = "admin_login", maxCount = 5, duration = 60) + public ApiResult adminLogin(@Valid @RequestBody LoginDTO dto) { + LoginVO vo = authService.adminLogin(dto, WebUtils.getClientIp(), WebUtils.getUserAgent()); + return ApiResult.success(vo); + } + + @Operation(summary = "用户注册") + @PostMapping("/register") + @RateLimit(key = "register", maxCount = 3, duration = 3600) + public ApiResult register(@Valid @RequestBody RegisterDTO dto) { + authService.register(dto); + return ApiResult.success(); + } + + @Operation(summary = "刷新Token") + @PostMapping("/refresh") + public ApiResult refreshToken(@RequestHeader("Refresh-Token") String refreshToken) { + LoginVO vo = authService.refreshToken(refreshToken); + return ApiResult.success(vo); + } + + @Operation(summary = "退出登录") + @PostMapping("/logout") + public ApiResult logout(@RequestHeader("Authorization") String authHeader) { + String token = authHeader.replace("Bearer ", ""); + authService.logout(token); + return ApiResult.success(); + } +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/controller/UserController.java b/jog-module-system/src/main/java/fun/nojava/module/system/controller/UserController.java new file mode 100644 index 0000000..8feafa5 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/controller/UserController.java @@ -0,0 +1,64 @@ +package fun.nojava.module.system.controller; + +import fun.nojava.common.model.ApiResult; +import fun.nojava.common.model.PageResult; +import fun.nojava.module.system.dto.UserInfoVO; +import fun.nojava.module.system.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "用户接口") +@RestController +@RequestMapping("/api/user") +@RequiredArgsConstructor +public class UserController { + + private final UserService userService; + + @Operation(summary = "获取当前用户信息") + @GetMapping("/info") + public ApiResult getCurrentUser(@AuthenticationPrincipal Long userId) { + return ApiResult.success(userService.getUserInfo(userId)); + } + + @Operation(summary = "获取用户信息") + @GetMapping("/{id}") + public ApiResult getUserInfo(@PathVariable Long id) { + return ApiResult.success(userService.getUserInfo(id)); + } +} + +@Tag(name = "管理员-用户管理") +@RestController +@RequestMapping("/api/admin/user") +@RequiredArgsConstructor +class AdminUserController { + + private final UserService userService; + + @Operation(summary = "用户列表") + @GetMapping("/list") + public ApiResult> listUsers( + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "15") int pageSize, + @RequestParam(required = false) String keyword) { + return ApiResult.success(userService.listUsers(page, pageSize, keyword)); + } + + @Operation(summary = "重置密码") + @PostMapping("/{id}/reset-password") + public ApiResult resetPassword(@PathVariable Long id, @RequestParam String newPassword) { + userService.resetPassword(id, newPassword); + return ApiResult.success(); + } + + @Operation(summary = "更新状态") + @PostMapping("/{id}/status") + public ApiResult updateStatus(@PathVariable Long id, @RequestParam int status) { + userService.updateUserStatus(id, status); + return ApiResult.success(); + } +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/dto/LoginDTO.java b/jog-module-system/src/main/java/fun/nojava/module/system/dto/LoginDTO.java new file mode 100644 index 0000000..0fa9245 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/dto/LoginDTO.java @@ -0,0 +1,20 @@ +package fun.nojava.module.system.dto; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Data +public class LoginDTO { + + @NotBlank(message = "邮箱不能为空") + @Email(message = "邮箱格式不正确") + private String email; + + @NotBlank(message = "密码不能为空") + @Size(min = 6, max = 20, message = "密码长度6-20位") + private String password; + + private Boolean rememberMe = false; +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/dto/LoginVO.java b/jog-module-system/src/main/java/fun/nojava/module/system/dto/LoginVO.java new file mode 100644 index 0000000..d91961a --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/dto/LoginVO.java @@ -0,0 +1,27 @@ +package fun.nojava.module.system.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LoginVO { + + private Long userId; + + private String email; + + private String nickname; + + private String avatar; + + private Integer type; + + private String accessToken; + + private String refreshToken; +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/dto/RegisterDTO.java b/jog-module-system/src/main/java/fun/nojava/module/system/dto/RegisterDTO.java new file mode 100644 index 0000000..64ba365 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/dto/RegisterDTO.java @@ -0,0 +1,22 @@ +package fun.nojava.module.system.dto; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Data +public class RegisterDTO { + + @NotBlank(message = "邮箱不能为空") + @Email(message = "邮箱格式不正确") + private String email; + + @NotBlank(message = "密码不能为空") + @Size(min = 6, max = 20, message = "密码长度6-20位") + private String password; + + @NotBlank(message = "昵称不能为空") + @Size(max = 50, message = "昵称最长50字符") + private String nickname; +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/dto/UserInfoVO.java b/jog-module-system/src/main/java/fun/nojava/module/system/dto/UserInfoVO.java new file mode 100644 index 0000000..513e197 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/dto/UserInfoVO.java @@ -0,0 +1,33 @@ +package fun.nojava.module.system.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserInfoVO { + + private Long id; + + private String email; + + private String nickname; + + private String avatar; + + private String bio; + + private Integer type; + + private Integer status; + + private Integer articleCount; + + private Integer followerCount; + + private Integer followingCount; +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/entity/LoginLog.java b/jog-module-system/src/main/java/fun/nojava/module/system/entity/LoginLog.java new file mode 100644 index 0000000..e2bc1e1 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/entity/LoginLog.java @@ -0,0 +1,31 @@ +package fun.nojava.module.system.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("login_log") +public class LoginLog { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long userId; + + private String email; + + private Integer loginType; + + private String ipAddress; + + private String userAgent; + + private Integer status; + + private String failReason; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/entity/User.java b/jog-module-system/src/main/java/fun/nojava/module/system/entity/User.java new file mode 100644 index 0000000..782adc6 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/entity/User.java @@ -0,0 +1,37 @@ +package fun.nojava.module.system.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("user") +public class User { + + @TableId(type = IdType.AUTO) + private Long id; + + private String email; + + private String password; + + private String nickname; + + private String avatar; + + private String bio; + + private Integer type; + + private Integer status; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updatedAt; + + @TableLogic + private Integer deleted; +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/entity/UserSession.java b/jog-module-system/src/main/java/fun/nojava/module/system/entity/UserSession.java new file mode 100644 index 0000000..141e7f6 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/entity/UserSession.java @@ -0,0 +1,27 @@ +package fun.nojava.module.system.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("user_session") +public class UserSession { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long userId; + + private String refreshToken; + + private String deviceInfo; + + private String ipAddress; + + private LocalDateTime expiresAt; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/mapper/LoginLogMapper.java b/jog-module-system/src/main/java/fun/nojava/module/system/mapper/LoginLogMapper.java new file mode 100644 index 0000000..1a758fb --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/mapper/LoginLogMapper.java @@ -0,0 +1,9 @@ +package fun.nojava.module.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import fun.nojava.module.system.entity.LoginLog; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface LoginLogMapper extends BaseMapper { +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/mapper/UserMapper.java b/jog-module-system/src/main/java/fun/nojava/module/system/mapper/UserMapper.java new file mode 100644 index 0000000..d0da547 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/mapper/UserMapper.java @@ -0,0 +1,9 @@ +package fun.nojava.module.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import fun.nojava.module.system.entity.User; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UserMapper extends BaseMapper { +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/mapper/UserSessionMapper.java b/jog-module-system/src/main/java/fun/nojava/module/system/mapper/UserSessionMapper.java new file mode 100644 index 0000000..0862959 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/mapper/UserSessionMapper.java @@ -0,0 +1,9 @@ +package fun.nojava.module.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import fun.nojava.module.system.entity.UserSession; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UserSessionMapper extends BaseMapper { +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/service/AuthService.java b/jog-module-system/src/main/java/fun/nojava/module/system/service/AuthService.java new file mode 100644 index 0000000..768be18 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/service/AuthService.java @@ -0,0 +1,20 @@ +package fun.nojava.module.system.service; + +import fun.nojava.module.system.dto.LoginDTO; +import fun.nojava.module.system.dto.LoginVO; +import fun.nojava.module.system.dto.RegisterDTO; + +public interface AuthService { + + LoginVO login(LoginDTO dto, String ipAddress, String userAgent); + + LoginVO adminLogin(LoginDTO dto, String ipAddress, String userAgent); + + void register(RegisterDTO dto); + + LoginVO refreshToken(String refreshToken); + + void logout(String accessToken); + + void logoutAll(Long userId); +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/service/UserService.java b/jog-module-system/src/main/java/fun/nojava/module/system/service/UserService.java new file mode 100644 index 0000000..973fa56 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/service/UserService.java @@ -0,0 +1,24 @@ +package fun.nojava.module.system.service; + +import fun.nojava.common.model.PageResult; +import fun.nojava.module.system.dto.UserInfoVO; +import fun.nojava.module.system.entity.User; + +public interface UserService { + + UserInfoVO getUserInfo(Long userId); + + User getById(Long id); + + PageResult listUsers(int page, int pageSize, String keyword); + + void resetPassword(Long userId, String newPassword); + + void updateUserStatus(Long userId, int status); + + boolean isFollowing(Long followerId, Long followeeId); + + void follow(Long followerId, Long followeeId); + + void unfollow(Long followerId, Long followeeId); +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/service/impl/AuthServiceImpl.java b/jog-module-system/src/main/java/fun/nojava/module/system/service/impl/AuthServiceImpl.java new file mode 100644 index 0000000..68dd37c --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/service/impl/AuthServiceImpl.java @@ -0,0 +1,171 @@ +package fun.nojava.module.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import fun.nojava.common.enums.UserStatus; +import fun.nojava.common.enums.UserType; +import fun.nojava.common.exception.BusinessException; +import fun.nojava.common.exception.ErrorCode; +import fun.nojava.framework.security.JwtTokenProvider; +import fun.nojava.framework.security.TokenBlacklistService; +import fun.nojava.module.system.dto.LoginDTO; +import fun.nojava.module.system.dto.LoginVO; +import fun.nojava.module.system.dto.RegisterDTO; +import fun.nojava.module.system.entity.LoginLog; +import fun.nojava.module.system.entity.User; +import fun.nojava.module.system.entity.UserSession; +import fun.nojava.module.system.mapper.LoginLogMapper; +import fun.nojava.module.system.mapper.UserMapper; +import fun.nojava.module.system.mapper.UserSessionMapper; +import fun.nojava.module.system.service.AuthService; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Service +@RequiredArgsConstructor +public class AuthServiceImpl implements AuthService { + + private final UserMapper userMapper; + private final LoginLogMapper loginLogMapper; + private final UserSessionMapper userSessionMapper; + private final PasswordEncoder passwordEncoder; + private final JwtTokenProvider jwtTokenProvider; + private final TokenBlacklistService tokenBlacklistService; + + @Override + @Transactional + public LoginVO login(LoginDTO dto, String ipAddress, String userAgent) { + User user = userMapper.selectOne( + new LambdaQueryWrapper().eq(User::getEmail, dto.getEmail()) + ); + + if (user == null) { + recordLoginLog(null, dto.getEmail(), ipAddress, userAgent, 0, "用户不存在"); + throw new BusinessException(ErrorCode.USER_NOT_FOUND); + } + + if (user.getStatus() == UserStatus.LOCKED.getCode()) { + recordLoginLog(user.getId(), dto.getEmail(), ipAddress, userAgent, 0, "账号已锁定"); + throw new BusinessException(ErrorCode.ACCOUNT_LOCKED); + } + + if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) { + recordLoginLog(user.getId(), dto.getEmail(), ipAddress, userAgent, 0, "密码错误"); + throw new BusinessException(ErrorCode.PASSWORD_ERROR); + } + + recordLoginLog(user.getId(), dto.getEmail(), ipAddress, userAgent, 1, null); + + return generateLoginVO(user, dto.getRememberMe()); + } + + @Override + @Transactional + public LoginVO adminLogin(LoginDTO dto, String ipAddress, String userAgent) { + LoginVO loginVO = login(dto, ipAddress, userAgent); + + User user = userMapper.selectById(loginVO.getUserId()); + if (user.getType() != UserType.ADMIN.getCode()) { + throw new BusinessException(ErrorCode.PERMISSION_DENIED); + } + + return loginVO; + } + + @Override + @Transactional + public void register(RegisterDTO dto) { + Long count = userMapper.selectCount( + new LambdaQueryWrapper().eq(User::getEmail, dto.getEmail()) + ); + if (count > 0) { + throw new BusinessException(ErrorCode.EMAIL_EXISTS); + } + + User user = new User(); + user.setEmail(dto.getEmail()); + user.setPassword(passwordEncoder.encode(dto.getPassword())); + user.setNickname(dto.getNickname()); + user.setType(UserType.USER.getCode()); + user.setStatus(UserStatus.NORMAL.getCode()); + user.setDeleted(0); + userMapper.insert(user); + } + + @Override + public LoginVO refreshToken(String refreshToken) { + if (!jwtTokenProvider.validateToken(refreshToken)) { + throw new BusinessException(ErrorCode.TOKEN_INVALID); + } + + Long userId = jwtTokenProvider.getUserIdFromToken(refreshToken); + User user = userMapper.selectById(userId); + + if (user == null) { + throw new BusinessException(ErrorCode.USER_NOT_FOUND); + } + + String accessToken = jwtTokenProvider.generateAccessToken(userId, user.getType()); + String newRefreshToken = jwtTokenProvider.generateRefreshToken(userId); + + return LoginVO.builder() + .userId(userId) + .email(user.getEmail()) + .nickname(user.getNickname()) + .avatar(user.getAvatar()) + .type(user.getType()) + .accessToken(accessToken) + .refreshToken(newRefreshToken) + .build(); + } + + @Override + public void logout(String accessToken) { + tokenBlacklistService.addToBlacklist(accessToken); + } + + @Override + public void logoutAll(Long userId) { + userSessionMapper.delete( + new LambdaQueryWrapper().eq(UserSession::getUserId, userId) + ); + } + + private LoginVO generateLoginVO(User user, Boolean rememberMe) { + String accessToken = jwtTokenProvider.generateAccessToken(user.getId(), user.getType()); + String refreshToken = jwtTokenProvider.generateRefreshToken(user.getId()); + + UserSession session = new UserSession(); + session.setUserId(user.getId()); + session.setRefreshToken(refreshToken); + session.setExpiresAt(LocalDateTime.now().plusSeconds( + rememberMe ? 1209600L : 86400L + )); + userSessionMapper.insert(session); + + return LoginVO.builder() + .userId(user.getId()) + .email(user.getEmail()) + .nickname(user.getNickname()) + .avatar(user.getAvatar()) + .type(user.getType()) + .accessToken(accessToken) + .refreshToken(refreshToken) + .build(); + } + + private void recordLoginLog(Long userId, String email, String ip, String ua, int status, String reason) { + LoginLog log = new LoginLog(); + log.setUserId(userId); + log.setEmail(email); + log.setLoginType(1); + log.setIpAddress(ip); + log.setUserAgent(ua); + log.setStatus(status); + log.setFailReason(reason); + loginLogMapper.insert(log); + } +} diff --git a/jog-module-system/src/main/java/fun/nojava/module/system/service/impl/UserServiceImpl.java b/jog-module-system/src/main/java/fun/nojava/module/system/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..16a1aa2 --- /dev/null +++ b/jog-module-system/src/main/java/fun/nojava/module/system/service/impl/UserServiceImpl.java @@ -0,0 +1,106 @@ +package fun.nojava.module.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import fun.nojava.common.exception.BusinessException; +import fun.nojava.common.exception.ErrorCode; +import fun.nojava.common.model.PageResult; +import fun.nojava.module.system.dto.UserInfoVO; +import fun.nojava.module.system.entity.User; +import fun.nojava.module.system.mapper.UserMapper; +import fun.nojava.module.system.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class UserServiceImpl implements UserService { + + private final UserMapper userMapper; + private final PasswordEncoder passwordEncoder; + + @Override + public UserInfoVO getUserInfo(Long userId) { + User user = userMapper.selectById(userId); + if (user == null) { + throw new BusinessException(ErrorCode.USER_NOT_FOUND); + } + + return UserInfoVO.builder() + .id(user.getId()) + .email(user.getEmail()) + .nickname(user.getNickname()) + .avatar(user.getAvatar()) + .bio(user.getBio()) + .type(user.getType()) + .status(user.getStatus()) + .articleCount(0) + .followerCount(0) + .followingCount(0) + .build(); + } + + @Override + public User getById(Long id) { + return userMapper.selectById(id); + } + + @Override + public PageResult listUsers(int page, int pageSize, String keyword) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (keyword != null && !keyword.isEmpty()) { + wrapper.and(w -> w.like(User::getNickname, keyword).or().like(User::getEmail, keyword)); + } + wrapper.orderByDesc(User::getCreatedAt); + + Page userPage = userMapper.selectPage(new Page<>(page, pageSize), wrapper); + List records = userPage.getRecords().stream() + .map(u -> UserInfoVO.builder() + .id(u.getId()) + .email(u.getEmail()) + .nickname(u.getNickname()) + .avatar(u.getAvatar()) + .type(u.getType()) + .status(u.getStatus()) + .build()) + .toList(); + + return new PageResult<>(records, userPage.getTotal(), page, pageSize); + } + + @Override + public void resetPassword(Long userId, String newPassword) { + User user = userMapper.selectById(userId); + if (user == null) { + throw new BusinessException(ErrorCode.USER_NOT_FOUND); + } + user.setPassword(passwordEncoder.encode(newPassword)); + userMapper.updateById(user); + } + + @Override + public void updateUserStatus(Long userId, int status) { + User user = userMapper.selectById(userId); + if (user == null) { + throw new BusinessException(ErrorCode.USER_NOT_FOUND); + } + user.setStatus(status); + userMapper.updateById(user); + } + + @Override + public boolean isFollowing(Long followerId, Long followeeId) { + return false; + } + + @Override + public void follow(Long followerId, Long followeeId) { + } + + @Override + public void unfollow(Long followerId, Long followeeId) { + } +} diff --git a/jog-module-system/target/classes/fun/nojava/module/system/controller/AdminUserController.class b/jog-module-system/target/classes/fun/nojava/module/system/controller/AdminUserController.class new file mode 100644 index 0000000..cf9934c Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/controller/AdminUserController.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/controller/AuthController.class b/jog-module-system/target/classes/fun/nojava/module/system/controller/AuthController.class new file mode 100644 index 0000000..860f397 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/controller/AuthController.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/controller/UserController.class b/jog-module-system/target/classes/fun/nojava/module/system/controller/UserController.class new file mode 100644 index 0000000..5b9895b Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/controller/UserController.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/dto/LoginDTO.class b/jog-module-system/target/classes/fun/nojava/module/system/dto/LoginDTO.class new file mode 100644 index 0000000..90bfcd8 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/dto/LoginDTO.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/dto/LoginVO$LoginVOBuilder.class b/jog-module-system/target/classes/fun/nojava/module/system/dto/LoginVO$LoginVOBuilder.class new file mode 100644 index 0000000..299e960 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/dto/LoginVO$LoginVOBuilder.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/dto/LoginVO.class b/jog-module-system/target/classes/fun/nojava/module/system/dto/LoginVO.class new file mode 100644 index 0000000..5f08874 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/dto/LoginVO.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/dto/RegisterDTO.class b/jog-module-system/target/classes/fun/nojava/module/system/dto/RegisterDTO.class new file mode 100644 index 0000000..5725890 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/dto/RegisterDTO.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/dto/UserInfoVO$UserInfoVOBuilder.class b/jog-module-system/target/classes/fun/nojava/module/system/dto/UserInfoVO$UserInfoVOBuilder.class new file mode 100644 index 0000000..37b18dc Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/dto/UserInfoVO$UserInfoVOBuilder.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/dto/UserInfoVO.class b/jog-module-system/target/classes/fun/nojava/module/system/dto/UserInfoVO.class new file mode 100644 index 0000000..adc1bc9 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/dto/UserInfoVO.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/entity/LoginLog.class b/jog-module-system/target/classes/fun/nojava/module/system/entity/LoginLog.class new file mode 100644 index 0000000..6c0f45a Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/entity/LoginLog.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/entity/User.class b/jog-module-system/target/classes/fun/nojava/module/system/entity/User.class new file mode 100644 index 0000000..07ed2fc Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/entity/User.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/entity/UserSession.class b/jog-module-system/target/classes/fun/nojava/module/system/entity/UserSession.class new file mode 100644 index 0000000..86bdf9e Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/entity/UserSession.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/mapper/LoginLogMapper.class b/jog-module-system/target/classes/fun/nojava/module/system/mapper/LoginLogMapper.class new file mode 100644 index 0000000..be0ce52 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/mapper/LoginLogMapper.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/mapper/UserMapper.class b/jog-module-system/target/classes/fun/nojava/module/system/mapper/UserMapper.class new file mode 100644 index 0000000..b3f2c85 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/mapper/UserMapper.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/mapper/UserSessionMapper.class b/jog-module-system/target/classes/fun/nojava/module/system/mapper/UserSessionMapper.class new file mode 100644 index 0000000..6d7dd80 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/mapper/UserSessionMapper.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/service/AuthService.class b/jog-module-system/target/classes/fun/nojava/module/system/service/AuthService.class new file mode 100644 index 0000000..f5e2124 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/service/AuthService.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/service/UserService.class b/jog-module-system/target/classes/fun/nojava/module/system/service/UserService.class new file mode 100644 index 0000000..171a898 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/service/UserService.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/service/impl/AuthServiceImpl.class b/jog-module-system/target/classes/fun/nojava/module/system/service/impl/AuthServiceImpl.class new file mode 100644 index 0000000..2c60a3a Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/service/impl/AuthServiceImpl.class differ diff --git a/jog-module-system/target/classes/fun/nojava/module/system/service/impl/UserServiceImpl.class b/jog-module-system/target/classes/fun/nojava/module/system/service/impl/UserServiceImpl.class new file mode 100644 index 0000000..7f09050 Binary files /dev/null and b/jog-module-system/target/classes/fun/nojava/module/system/service/impl/UserServiceImpl.class differ diff --git a/jog-module-system/target/jog-module-system-1.0.0.jar b/jog-module-system/target/jog-module-system-1.0.0.jar new file mode 100644 index 0000000..88f3880 Binary files /dev/null and b/jog-module-system/target/jog-module-system-1.0.0.jar differ diff --git a/jog-module-system/target/maven-archiver/pom.properties b/jog-module-system/target/maven-archiver/pom.properties new file mode 100644 index 0000000..46ed235 --- /dev/null +++ b/jog-module-system/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=jog-module-system +groupId=fun.nojava +version=1.0.0 diff --git a/jog-module-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/jog-module-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..224a856 --- /dev/null +++ b/jog-module-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,19 @@ +fun\nojava\module\system\dto\LoginVO$LoginVOBuilder.class +fun\nojava\module\system\entity\LoginLog.class +fun\nojava\module\system\controller\AdminUserController.class +fun\nojava\module\system\service\impl\AuthServiceImpl.class +fun\nojava\module\system\service\UserService.class +fun\nojava\module\system\mapper\UserSessionMapper.class +fun\nojava\module\system\controller\AuthController.class +fun\nojava\module\system\dto\LoginDTO.class +fun\nojava\module\system\dto\UserInfoVO$UserInfoVOBuilder.class +fun\nojava\module\system\dto\UserInfoVO.class +fun\nojava\module\system\mapper\UserMapper.class +fun\nojava\module\system\dto\RegisterDTO.class +fun\nojava\module\system\entity\User.class +fun\nojava\module\system\entity\UserSession.class +fun\nojava\module\system\service\AuthService.class +fun\nojava\module\system\dto\LoginVO.class +fun\nojava\module\system\service\impl\UserServiceImpl.class +fun\nojava\module\system\controller\UserController.class +fun\nojava\module\system\mapper\LoginLogMapper.class diff --git a/jog-module-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/jog-module-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..95f41f2 --- /dev/null +++ b/jog-module-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,16 @@ +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\controller\AuthController.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\controller\UserController.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\dto\LoginDTO.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\dto\LoginVO.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\dto\RegisterDTO.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\dto\UserInfoVO.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\entity\LoginLog.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\entity\User.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\entity\UserSession.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\mapper\LoginLogMapper.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\mapper\UserMapper.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\mapper\UserSessionMapper.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\service\AuthService.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\service\impl\AuthServiceImpl.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\service\impl\UserServiceImpl.java +E:\person\Jog\jog-module-system\src\main\java\fun\nojava\module\system\service\UserService.java