浏览代码

0807 增加作者功能

Qing 9 月之前
父节点
当前提交
0faba932d7

+ 116 - 5
novel-demo/src/main/java/com/sf/controller/AuthorInfoController.java

@@ -1,7 +1,21 @@
 package com.sf.controller;
 
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.stereotype.Controller;
+import com.sf.dto.RestResp;
+import com.sf.dto.req.*;
+import com.sf.dto.resp.BookChapterRespDto;
+import com.sf.dto.resp.BookInfoRespDto;
+import com.sf.dto.resp.ChapterContentRespDto;
+import com.sf.dto.resp.PageRespDto;
+import com.sf.service.IAuthorBookService;
+import com.sf.service.IAuthorInfoService;
+import com.sf.util.UserHolder;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.web.bind.annotation.*;
 
 /**
  * <p>
@@ -11,8 +25,105 @@ import org.springframework.stereotype.Controller;
  * @author baomidou
  * @since 2024-07-22
  */
-@Controller
-@RequestMapping("/authorInfo")
+@Tag(name = "AuthorController", description = "作家后台-作者模块")
+@RestController
+@RequestMapping("/api/author")
+@RequiredArgsConstructor
 public class AuthorInfoController {
 
-}
+    private final IAuthorInfoService authorService;
+
+    private final IAuthorBookService bookService;
+
+    /**
+     * 作家注册接口
+     */
+    @Operation(summary = "作家注册接口")
+    @PostMapping("register")
+    public RestResp<Void> register(@Valid @RequestBody AuthorRegisterReqDto dto) {
+        dto.setUserId(UserHolder.getUserId());
+        return authorService.register(dto);
+    }
+
+    /**
+     * 查询作家状态接口
+     */
+    @Operation(summary = "作家状态查询接口")
+    @GetMapping("status")
+    public RestResp<Integer> getStatus() {
+        return authorService.getStatus(UserHolder.getUserId());
+    }
+
+    /**
+     * 小说发布接口
+     */
+    @Operation(summary = "小说发布接口")
+    @PostMapping("book")
+    public RestResp<Void> publishBook(@Valid @RequestBody BookAddReqDto dto) {
+        return bookService.saveBook(dto);
+    }
+
+    /**
+     * 小说发布列表查询接口
+     */
+    @Operation(summary = "小说发布列表查询接口")
+    @GetMapping("books")
+    public RestResp<PageRespDto<BookInfoRespDto>> listBooks(@ParameterObject PageReqDto dto) {
+        return bookService.listAuthorBooks(dto);
+    }
+
+    /**
+     * 小说章节发布接口
+     */
+    @Operation(summary = "小说章节发布接口")
+    @PostMapping("book/chapter/{bookId}")
+    public RestResp<Void> publishBookChapter(
+            @Parameter(description = "小说ID") @PathVariable("bookId") Long bookId,
+            @Valid @RequestBody ChapterAddReqDto dto) {
+        dto.setBookId(bookId);
+        return bookService.saveBookChapter(dto);
+    }
+
+    /**
+     * 小说章节删除接口
+     */
+    @Operation(summary = "小说章节删除接口")
+    @DeleteMapping("book/chapter/{chapterId}")
+    public RestResp<Void> deleteBookChapter(
+            @Parameter(description = "章节ID") @PathVariable("chapterId") Long chapterId) {
+        return bookService.deleteBookChapter(chapterId);
+    }
+
+    /**
+     * 小说章节查询接口
+     */
+    @Operation(summary = "小说章节查询接口")
+    @GetMapping("book/chapter/{chapterId}")
+    public RestResp<ChapterContentRespDto> getBookChapter(
+            @Parameter(description = "章节ID") @PathVariable("chapterId") Long chapterId) {
+        return bookService.getBookChapter(chapterId);
+    }
+
+    /**
+     * 小说章节更新接口
+     */
+    @Operation(summary = "小说章节更新接口")
+    @PutMapping("book/chapter/{chapterId}")
+    public RestResp<Void> updateBookChapter(
+            @Parameter(description = "章节ID") @PathVariable("chapterId") Long chapterId,
+            @Valid @RequestBody ChapterUpdateReqDto dto) {
+        return bookService.updateBookChapter(chapterId, dto);
+    }
+
+    /**
+     * 小说章节发布列表查询接口
+     */
+    @Operation(summary = "小说章节发布列表查询接口")
+    @GetMapping("book/chapters/{bookId}")
+    public RestResp<PageRespDto<BookChapterRespDto>> listBookChapters(
+            @Parameter(description = "小说ID") @PathVariable("bookId") Long bookId,
+            @ParameterObject PageReqDto dto) {
+        return bookService.listBookChapters(bookId, dto);
+    }
+
+}

+ 58 - 0
novel-demo/src/main/java/com/sf/dto/req/AuthorRegisterReqDto.java

@@ -0,0 +1,58 @@
+package com.sf.dto.req;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.*;
+import lombok.Data;
+
+/**
+ * 作家注册 请求DTO
+ *
+ * @author xiongxiaoyang
+ * @date 2022/5/23
+ */
+@Data
+public class AuthorRegisterReqDto {
+
+    @Schema(hidden = true)
+    private Long userId;
+
+    /**
+     * 笔名
+     */
+    @Schema(description = "笔名", required = true)
+    @NotBlank(message = "笔名不能为空!")
+    private String penName;
+
+    /**
+     * 手机号码
+     */
+    @Schema(description = "手机号码", required = true)
+    @NotBlank(message = "手机号不能为空!")
+    @Pattern(regexp = "^1[3|4|5|6|7|8|9][0-9]{9}$", message = "手机号格式不正确!")
+    private String telPhone;
+
+    /**
+     * QQ或微信账号
+     */
+    @Schema(description = "QQ或微信账号", required = true)
+    @NotBlank(message = "QQ或微信账号不能为空!")
+    private String chatAccount;
+
+    /**
+     * 电子邮箱
+     */
+    @Schema(description = "电子邮箱", required = true)
+    @NotBlank(message = "电子邮箱不能为空!")
+    @Email(message = "邮箱格式不正确!")
+    private String email;
+
+    /**
+     * 作品方向;0-男频 1-女频
+     */
+    @Schema(description = "作品方向;0-男频 1-女频", required = true)
+    @NotNull(message = "作品方向不能为空!")
+    @Min(0)
+    @Max(1)
+    private Integer workDirection;
+
+}

+ 65 - 0
novel-demo/src/main/java/com/sf/dto/req/BookAddReqDto.java

@@ -0,0 +1,65 @@
+package com.sf.dto.req;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * 小说发布 请求DTO
+ *
+ * @author xiongxiaoyang
+ * @date 2022/5/23
+ */
+@Data
+public class BookAddReqDto {
+
+    /**
+     * 作品方向;0-男频 1-女频
+     */
+    @Schema(description = "作品方向;0-男频 1-女频", required = true)
+    @NotNull
+    private Integer workDirection;
+
+    /**
+     * 类别ID
+     */
+    @Schema(description = "类别ID", required = true)
+    @NotNull
+    private Long categoryId;
+
+    /**
+     * 类别名
+     */
+    @Schema(description = "类别名", required = true)
+    @NotBlank
+    private String categoryName;
+
+    /**
+     * 小说封面地址
+     */
+    @Schema(description = "小说封面地址", required = true)
+    @NotBlank
+    private String picUrl;
+
+    /**
+     * 小说名
+     */
+    @Schema(description = "小说名", required = true)
+    @NotBlank
+    private String bookName;
+
+    /**
+     * 书籍描述
+     */
+    @Schema(description = "书籍描述", required = true)
+    @NotBlank
+    private String bookDesc;
+
+    /**
+     * 是否收费;1-收费 0-免费
+     */
+    @Schema(description = "是否收费;1-收费 0-免费", required = true)
+    @NotNull
+    private Integer isVip;
+}

+ 50 - 0
novel-demo/src/main/java/com/sf/dto/req/ChapterAddReqDto.java

@@ -0,0 +1,50 @@
+package com.sf.dto.req;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 章节发布 请求DTO
+ *
+ * @author xiongxiaoyang
+ * @date 2022/5/23
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class ChapterAddReqDto {
+
+    /**
+     * 小说ID
+     */
+    @Schema(description = "小说ID", required = true)
+    private Long bookId;
+
+    /**
+     * 章节名
+     */
+    @NotBlank
+    @Schema(description = "章节名", required = true)
+    private String chapterName;
+
+    /**
+     * 章节内容
+     */
+    @Schema(description = "章节内容", required = true)
+    @NotBlank
+    private String chapterContent;
+
+    /**
+     * 是否收费;1-收费 0-免费
+     */
+    @Schema(description = "是否收费;1-收费 0-免费", required = true)
+    @NotNull
+    private Integer isVip;
+
+}

+ 38 - 0
novel-demo/src/main/java/com/sf/dto/req/ChapterUpdateReqDto.java

@@ -0,0 +1,38 @@
+package com.sf.dto.req;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * 章节发布 请求DTO
+ *
+ * @author xiongxiaoyang
+ * @date 2022/5/23
+ */
+@Data
+public class ChapterUpdateReqDto {
+
+    /**
+     * 章节名
+     */
+    @NotBlank
+    @Schema(description = "章节名", required = true)
+    private String chapterName;
+
+    /**
+     * 章节内容
+     */
+    @Schema(description = "章节内容", required = true)
+    @NotBlank
+    private String chapterContent;
+
+    /**
+     * 是否收费;1-收费 0-免费
+     */
+    @Schema(description = "是否收费;1-收费 0-免费", required = true)
+    @NotNull
+    private Integer isVip;
+
+}

+ 35 - 0
novel-demo/src/main/java/com/sf/dto/resp/ChapterContentRespDto.java

@@ -0,0 +1,35 @@
+package com.sf.dto.resp;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * 小说内容 响应DTO
+ *
+ * @author xiongxiaoyang
+ * @date 2022/5/15
+ */
+@Data
+@Builder
+public class ChapterContentRespDto {
+
+    /**
+     * 章节标题
+     */
+    @Schema(description = "章节名")
+    private String chapterName;
+
+    /**
+     * 章节内容
+     */
+    @Schema(description = "章节内容")
+    private String chapterContent;
+
+    /**
+     * 是否收费;1-收费 0-免费
+     */
+    @Schema(description = "是否收费;1-收费 0-免费")
+    private Integer isVip;
+
+}

+ 4 - 78
novel-demo/src/main/java/com/sf/entity/BookChapter.java

@@ -3,6 +3,8 @@ package com.sf.entity;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
 import java.io.Serializable;
 import java.time.LocalDateTime;
 
@@ -14,6 +16,7 @@ import java.time.LocalDateTime;
  * @author baomidou
  * @since 2024-05-25
  */
+@Data
 @TableName("book_chapter")
 public class BookChapter implements Serializable {
 
@@ -45,87 +48,10 @@ public class BookChapter implements Serializable {
     /**
      * 是否收费;1-收费 0-免费
      */
-    private Byte isVip;
+    private Integer isVip;
 
     private LocalDateTime createTime;
 
     private LocalDateTime updateTime;
 
-    public Long getId() {
-        return id;
-    }
-
-    public void setId(Long id) {
-        this.id = id;
-    }
-
-    public Long getBookId() {
-        return bookId;
-    }
-
-    public void setBookId(Long bookId) {
-        this.bookId = bookId;
-    }
-
-    public Integer getChapterNum() {
-        return chapterNum;
-    }
-
-    public void setChapterNum(Integer chapterNum) {
-        this.chapterNum = chapterNum;
-    }
-
-    public String getChapterName() {
-        return chapterName;
-    }
-
-    public void setChapterName(String chapterName) {
-        this.chapterName = chapterName;
-    }
-
-    public Integer getWordCount() {
-        return wordCount;
-    }
-
-    public void setWordCount(Integer wordCount) {
-        this.wordCount = wordCount;
-    }
-
-    public Byte getIsVip() {
-        return isVip;
-    }
-
-    public void setIsVip(Byte isVip) {
-        this.isVip = isVip;
-    }
-
-    public LocalDateTime getCreateTime() {
-        return createTime;
-    }
-
-    public void setCreateTime(LocalDateTime createTime) {
-        this.createTime = createTime;
-    }
-
-    public LocalDateTime getUpdateTime() {
-        return updateTime;
-    }
-
-    public void setUpdateTime(LocalDateTime updateTime) {
-        this.updateTime = updateTime;
-    }
-
-    @Override
-    public String toString() {
-        return "BookChapter{" +
-            "id = " + id +
-            ", bookId = " + bookId +
-            ", chapterNum = " + chapterNum +
-            ", chapterName = " + chapterName +
-            ", wordCount = " + wordCount +
-            ", isVip = " + isVip +
-            ", createTime = " + createTime +
-            ", updateTime = " + updateTime +
-        "}";
-    }
 }

+ 3 - 3
novel-demo/src/main/java/com/sf/entity/BookInfo.java

@@ -31,7 +31,7 @@ public class BookInfo implements Serializable {
     /**
      * 作品方向;0-男频 1-女频
      */
-    private Byte workDirection;
+    private Integer workDirection;
 
     /**
      * 类别ID
@@ -71,7 +71,7 @@ public class BookInfo implements Serializable {
     /**
      * 评分;总分:10 ,真实评分 = score/10
      */
-    private Byte score;
+    private Integer score;
 
     /**
      * 书籍状态;0-连载中 1-已完结
@@ -111,7 +111,7 @@ public class BookInfo implements Serializable {
     /**
      * 是否收费;1-收费 0-免费
      */
-    private Byte isVip;
+    private Integer isVip;
 
     /**
      * 创建时间

+ 72 - 0
novel-demo/src/main/java/com/sf/service/IAuthorBookService.java

@@ -0,0 +1,72 @@
+package com.sf.service;
+
+import com.sf.dto.RestResp;
+import com.sf.dto.req.BookAddReqDto;
+import com.sf.dto.req.ChapterAddReqDto;
+import com.sf.dto.req.ChapterUpdateReqDto;
+import com.sf.dto.req.PageReqDto;
+import com.sf.dto.resp.BookChapterRespDto;
+import com.sf.dto.resp.BookInfoRespDto;
+import com.sf.dto.resp.ChapterContentRespDto;
+import com.sf.dto.resp.PageRespDto;
+
+public interface IAuthorBookService {
+
+    /**
+     * 小说信息保存
+     *
+     * @param dto 小说信息
+     * @return void
+     */
+    RestResp<Void> saveBook(BookAddReqDto dto);
+
+    /**
+     * 小说章节信息保存
+     *
+     * @param dto 章节信息
+     * @return void
+     */
+    RestResp<Void> saveBookChapter(ChapterAddReqDto dto);
+
+    /**
+     * 查询作家发布小说列表
+     *
+     * @param dto 分页请求参数
+     * @return 小说分页列表数据
+     */
+    RestResp<PageRespDto<BookInfoRespDto>> listAuthorBooks(PageReqDto dto);
+
+    /**
+     * 查询小说发布章节列表
+     *
+     * @param bookId 小说ID
+     * @param dto    分页请求参数
+     * @return 章节分页列表数据
+     */
+    RestResp<PageRespDto<BookChapterRespDto>> listBookChapters(Long bookId, PageReqDto dto);
+
+    /**
+     * 小说章节删除
+     *
+     * @param chapterId 章节ID
+     * @return void
+     */
+    RestResp<Void> deleteBookChapter(Long chapterId);
+
+    /**
+     * 小说章节查询
+     *
+     * @param chapterId 章节ID
+     * @return 章节内容
+     */
+    RestResp<ChapterContentRespDto> getBookChapter(Long chapterId);
+
+    /**
+     * 小说章节更新
+     *
+     * @param chapterId 章节ID
+     * @param dto       更新内容
+     * @return void
+     */
+    RestResp<Void> updateBookChapter(Long chapterId, ChapterUpdateReqDto dto);
+}

+ 17 - 0
novel-demo/src/main/java/com/sf/service/IAuthorInfoService.java

@@ -1,5 +1,7 @@
 package com.sf.service;
 
+import com.sf.dto.RestResp;
+import com.sf.dto.req.AuthorRegisterReqDto;
 import com.sf.entity.AuthorInfo;
 import com.baomidou.mybatisplus.extension.service.IService;
 
@@ -13,4 +15,19 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface IAuthorInfoService extends IService<AuthorInfo> {
 
+    /**
+     * 作家注册
+     *
+     * @param dto 注册参数
+     * @return void
+     */
+    RestResp<Void> register(AuthorRegisterReqDto dto);
+
+    /**
+     * 查询作家状态
+     *
+     * @param userId 用户ID
+     * @return 作家状态
+     */
+    RestResp<Integer> getStatus(Long userId);
 }

+ 258 - 0
novel-demo/src/main/java/com/sf/service/impl/AuthorBookServiceImpl.java

@@ -0,0 +1,258 @@
+package com.sf.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.sf.dto.RestResp;
+import com.sf.dto.req.BookAddReqDto;
+import com.sf.dto.req.ChapterAddReqDto;
+import com.sf.dto.req.ChapterUpdateReqDto;
+import com.sf.dto.req.PageReqDto;
+import com.sf.dto.resp.*;
+import com.sf.entity.AuthorInfo;
+import com.sf.entity.BookChapter;
+import com.sf.entity.BookContent;
+import com.sf.entity.BookInfo;
+import com.sf.mapper.AuthorInfoMapper;
+import com.sf.mapper.BookChapterMapper;
+import com.sf.mapper.BookContentMapper;
+import com.sf.mapper.BookInfoMapper;
+import com.sf.service.IAuthorBookService;
+import com.sf.util.UserHolder;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+@Service
+@RequiredArgsConstructor
+public class AuthorBookServiceImpl implements IAuthorBookService {
+
+    private final BookInfoMapper bookInfoMapper;
+    private final BookChapterMapper bookChapterMapper;
+    private final BookContentMapper bookContentMapper;
+    private final AuthorInfoMapper authorInfoMapper;
+
+    @Override
+    public RestResp<Void> saveBook(BookAddReqDto dto) {
+        // 校验小说名是否已存在
+        LambdaQueryWrapper<BookInfo> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(BookInfo::getBookName, dto.getBookName());
+        if (bookInfoMapper.selectCount(queryWrapper) > 0) {
+            return RestResp.fail("00010", "小说名已经存在");
+        }
+        BookInfo bookInfo = new BookInfo();
+        // 设置作家信息
+        LambdaQueryWrapper<AuthorInfo> authorInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        authorInfoLambdaQueryWrapper.eq(AuthorInfo::getUserId, UserHolder.getUserId());
+        AuthorInfo authorInfo = authorInfoMapper.selectOne(authorInfoLambdaQueryWrapper);
+
+        bookInfo.setAuthorId(authorInfo.getId());
+        bookInfo.setAuthorName(authorInfo.getPenName());
+        // 设置其他信息
+        bookInfo.setWorkDirection(dto.getWorkDirection());
+        bookInfo.setCategoryId(dto.getCategoryId());
+        bookInfo.setCategoryName(dto.getCategoryName());
+        bookInfo.setBookName(dto.getBookName());
+        bookInfo.setPicUrl(dto.getPicUrl());
+        bookInfo.setBookDesc(dto.getBookDesc());
+        bookInfo.setIsVip(dto.getIsVip());
+        bookInfo.setScore(0);
+        bookInfo.setCreateTime(LocalDateTime.now());
+        bookInfo.setUpdateTime(LocalDateTime.now());
+        // 保存小说信息
+        bookInfoMapper.insert(bookInfo);
+        return RestResp.ok(null);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public RestResp<Void> saveBookChapter(ChapterAddReqDto dto) {
+        // 校验该作品是否属于当前作家
+        BookInfo bookInfo = bookInfoMapper.selectById(dto.getBookId());
+//        if (!Objects.equals(bookInfo.getAuthorId(), UserHolder.getAuthorId())) {
+//            return RestResp.fail(ErrorCodeEnum.USER_UN_AUTH);
+//        }
+        // 1) 保存章节相关信息到小说章节表
+        //  a) 查询最新章节号
+        int chapterNum = 0;
+        LambdaQueryWrapper<BookChapter> chapterQueryWrapper = new LambdaQueryWrapper<>();
+        chapterQueryWrapper.eq(BookChapter::getBookId, dto.getBookId())
+                .orderByDesc(BookChapter::getChapterNum).last("limit 1");
+        BookChapter bookChapter = bookChapterMapper.selectOne(chapterQueryWrapper);
+        if (Objects.nonNull(bookChapter)) {
+            chapterNum = bookChapter.getChapterNum() + 1;
+        }
+        //  b) 设置章节相关信息并保存
+        BookChapter newBookChapter = new BookChapter();
+        newBookChapter.setBookId(dto.getBookId());
+        newBookChapter.setChapterName(dto.getChapterName());
+        newBookChapter.setChapterNum(chapterNum);
+        newBookChapter.setWordCount(dto.getChapterContent().length());
+        newBookChapter.setIsVip(dto.getIsVip());
+        newBookChapter.setCreateTime(LocalDateTime.now());
+        newBookChapter.setUpdateTime(LocalDateTime.now());
+        bookChapterMapper.insert(newBookChapter);
+
+        // 2) 保存章节内容到小说内容表
+        BookContent bookContent = new BookContent();
+        bookContent.setContent(dto.getChapterContent());
+        bookContent.setChapterId(newBookChapter.getId());
+        bookContent.setCreateTime(LocalDateTime.now());
+        bookContent.setUpdateTime(LocalDateTime.now());
+        bookContentMapper.insert(bookContent);
+
+        // 3) 更新小说表最新章节信息和小说总字数信息
+        //  a) 更新小说表关于最新章节的信息
+        BookInfo newBookInfo = new BookInfo();
+        newBookInfo.setId(dto.getBookId());
+        newBookInfo.setLastChapterId(newBookChapter.getId());
+        newBookInfo.setLastChapterName(newBookChapter.getChapterName());
+        newBookInfo.setLastChapterUpdateTime(LocalDateTime.now());
+        newBookInfo.setWordCount(bookInfo.getWordCount() + newBookChapter.getWordCount());
+        newBookChapter.setUpdateTime(LocalDateTime.now());
+        bookInfoMapper.updateById(newBookInfo);
+
+        return RestResp.ok(null);
+    }
+
+    @Override
+    public RestResp<PageRespDto<BookInfoRespDto>> listAuthorBooks(PageReqDto dto) {
+        IPage<BookInfo> page = new Page<>();
+        page.setCurrent(dto.getPageNum());
+        page.setSize(dto.getPageSize());
+
+        LambdaQueryWrapper<AuthorInfo> authorInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        authorInfoLambdaQueryWrapper.eq(AuthorInfo::getUserId, UserHolder.getUserId());
+        AuthorInfo authorInfo = authorInfoMapper.selectOne(authorInfoLambdaQueryWrapper);
+
+        LambdaQueryWrapper<BookInfo> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(BookInfo::getAuthorId, authorInfo.getId())
+                .orderByDesc(BookInfo::getCreateTime);
+        IPage<BookInfo> bookInfoPage = bookInfoMapper.selectPage(page, queryWrapper);
+        return RestResp.ok(PageRespDto.of(dto.getPageNum(), dto.getPageSize(), page.getTotal(),
+                bookInfoPage.getRecords().stream().map(v -> BookInfoRespDto.builder()
+                        .id(v.getId())
+                        .bookName(v.getBookName())
+                        .picUrl(v.getPicUrl())
+                        .categoryName(v.getCategoryName())
+                        .wordCount(v.getWordCount())
+                        .visitCount(v.getVisitCount())
+                        .updateTime(v.getUpdateTime())
+                        .build()).toList()));
+    }
+
+    @Override
+    public RestResp<PageRespDto<BookChapterRespDto>> listBookChapters(Long bookId, PageReqDto dto) {
+        IPage<BookChapter> page = new Page<>();
+        page.setCurrent(dto.getPageNum());
+        page.setSize(dto.getPageSize());
+        LambdaQueryWrapper<BookChapter> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(BookChapter::getBookId, bookId)
+                .orderByDesc(BookChapter::getChapterNum);
+        IPage<BookChapter> bookChapterPage = bookChapterMapper.selectPage(page, queryWrapper);
+        return RestResp.ok(PageRespDto.of(dto.getPageNum(), dto.getPageSize(), page.getTotal(),
+                bookChapterPage.getRecords().stream().map(bookChapter -> BookChapterRespDto.builder()
+                        .id(bookChapter.getId())
+                        .chapterName(bookChapter.getChapterName())
+                        .chapterUpdateTime(bookChapter.getUpdateTime())
+                        .isVip(bookChapter.getIsVip())
+                        .build()).toList()));
+    }
+
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public RestResp<Void> deleteBookChapter(Long chapterId) {
+        // 1.查询章节信息
+        BookChapter chapter = bookChapterMapper.selectById(chapterId);
+        // 2.查询小说信息
+        BookInfo bookInfo = bookInfoMapper.selectById(chapter.getBookId());
+        // 3.删除章节信息
+        bookChapterMapper.deleteById(chapterId);
+        // 4.删除章节内容
+        LambdaQueryWrapper<BookContent> bookContentQueryWrapper = new LambdaQueryWrapper<>();
+        bookContentQueryWrapper.eq(BookContent::getChapterId, chapterId);
+        bookContentMapper.delete(bookContentQueryWrapper);
+        // 5.更新小说信息
+        BookInfo newBookInfo = new BookInfo();
+        newBookInfo.setId(chapter.getBookId());
+        newBookInfo.setUpdateTime(LocalDateTime.now());
+        newBookInfo.setWordCount(bookInfo.getWordCount() - chapter.getWordCount());
+        if (Objects.equals(bookInfo.getLastChapterId(), chapterId)) {
+            // 设置最新章节信息
+            LambdaQueryWrapper<BookChapter> bookChapterQueryWrapper = new LambdaQueryWrapper<>();
+            bookChapterQueryWrapper.eq(BookChapter::getBookId, chapter.getBookId())
+                    .orderByDesc(BookChapter::getChapterNum).last("limit 1");
+            BookChapter bookChapter = bookChapterMapper.selectOne(bookChapterQueryWrapper);
+            Long lastChapterId = 0L;
+            String lastChapterName = "";
+            LocalDateTime lastChapterUpdateTime = null;
+            if (Objects.nonNull(bookChapter)) {
+                lastChapterId = bookChapter.getId();
+                lastChapterName = bookChapter.getChapterName();
+                lastChapterUpdateTime = bookChapter.getUpdateTime();
+            }
+            newBookInfo.setLastChapterId(lastChapterId);
+            newBookInfo.setLastChapterName(lastChapterName);
+            newBookInfo.setLastChapterUpdateTime(lastChapterUpdateTime);
+        }
+        bookInfoMapper.updateById(newBookInfo);
+        return RestResp.ok(null);
+    }
+
+    @Override
+    public RestResp<ChapterContentRespDto> getBookChapter(Long chapterId) {
+        BookChapter chapter = bookChapterMapper.selectById(chapterId);
+        LambdaQueryWrapper<BookContent> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(BookContent::getChapterId, chapterId);
+        BookContent bookContent = bookContentMapper.selectOne(queryWrapper);
+        String content = bookContent.getContent();
+        return RestResp.ok(
+                ChapterContentRespDto.builder()
+                        .chapterName(chapter.getChapterName())
+                        .chapterContent(content)
+                        .isVip(chapter.getIsVip())
+                        .build());
+    }
+
+    @Transactional
+    @Override
+    public RestResp<Void> updateBookChapter(Long chapterId, ChapterUpdateReqDto dto) {
+        // 1.查询章节信息
+        BookChapter chapter = bookChapterMapper.selectById(chapterId);
+        // 2.查询小说信息
+        BookInfo bookInfo = bookInfoMapper.selectById(chapter.getBookId());
+        // 3.更新章节信息
+        BookChapter newChapter = new BookChapter();
+        newChapter.setId(chapterId);
+        newChapter.setChapterName(dto.getChapterName());
+        newChapter.setWordCount(dto.getChapterContent().length());
+        newChapter.setIsVip(dto.getIsVip());
+        newChapter.setUpdateTime(LocalDateTime.now());
+        bookChapterMapper.updateById(newChapter);
+        // 4.更新章节内容
+        BookContent newContent = new BookContent();
+        newContent.setContent(dto.getChapterContent());
+        newContent.setUpdateTime(LocalDateTime.now());
+        LambdaQueryWrapper<BookContent> bookContentQueryWrapper = new LambdaQueryWrapper<>();
+        bookContentQueryWrapper.eq(BookContent::getChapterId, chapterId);
+        bookContentMapper.update(newContent, bookContentQueryWrapper);
+        // 5.更新小说信息
+        BookInfo newBookInfo = new BookInfo();
+        newBookInfo.setId(chapter.getBookId());
+        newBookInfo.setUpdateTime(LocalDateTime.now());
+        newBookInfo.setWordCount(
+                bookInfo.getWordCount() - chapter.getWordCount() + dto.getChapterContent().length());
+        if (Objects.equals(bookInfo.getLastChapterId(), chapterId)) {
+            // 更新最新章节信息
+            newBookInfo.setLastChapterName(dto.getChapterName());
+            newBookInfo.setLastChapterUpdateTime(LocalDateTime.now());
+        }
+        bookInfoMapper.updateById(newBookInfo);
+        return RestResp.ok(null);
+    }
+
+}

+ 48 - 1
novel-demo/src/main/java/com/sf/service/impl/AuthorInfoServiceImpl.java

@@ -1,11 +1,20 @@
 package com.sf.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.sf.dto.RestResp;
+import com.sf.dto.req.AuthorRegisterReqDto;
 import com.sf.entity.AuthorInfo;
+import com.sf.entity.BookCategory;
 import com.sf.mapper.AuthorInfoMapper;
+import com.sf.mapper.BookCategoryMapper;
 import com.sf.service.IAuthorInfoService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDateTime;
+import java.util.Objects;
+
 /**
  * <p>
  * 作者信息 服务实现类
@@ -15,6 +24,44 @@ import org.springframework.stereotype.Service;
  * @since 2024-07-22
  */
 @Service
-public class AuthorInfoServiceImpl extends ServiceImpl<AuthorInfoMapper, AuthorInfo> implements IAuthorInfoService {
+@RequiredArgsConstructor
+public class AuthorInfoServiceImpl extends ServiceImpl<AuthorInfoMapper, AuthorInfo>
+        implements IAuthorInfoService {
+
+    private final AuthorInfoMapper authorInfoMapper;
+
+    @Override
+    public RestResp<Void> register(AuthorRegisterReqDto dto) {
+        // 校验该用户是否已注册为作家
+        LambdaQueryWrapper<AuthorInfo> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(AuthorInfo::getUserId, dto.getUserId());
+        AuthorInfo exist = authorInfoMapper.selectOne(queryWrapper);
+
+        if (Objects.nonNull(exist)) {
+            // 该用户已经是作家,直接返回
+            return RestResp.ok(null);
+        }
+        // 保存作家注册信息
+        AuthorInfo authorInfo = new AuthorInfo();
+        authorInfo.setUserId(dto.getUserId());
+        authorInfo.setChatAccount(dto.getChatAccount());
+        authorInfo.setEmail(dto.getEmail());
+        authorInfo.setInviteCode("0");
+        authorInfo.setTelPhone(dto.getTelPhone());
+        authorInfo.setPenName(dto.getPenName());
+        authorInfo.setWorkDirection(dto.getWorkDirection());
+        authorInfo.setCreateTime(LocalDateTime.now());
+        authorInfo.setUpdateTime(LocalDateTime.now());
+        authorInfoMapper.insert(authorInfo);
+        return RestResp.ok(null);
+    }
+
+    @Override
+    public RestResp<Integer> getStatus(Long userId) {
+        LambdaQueryWrapper<AuthorInfo> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(AuthorInfo::getUserId, userId);
+        AuthorInfo authorInfo = authorInfoMapper.selectOne(queryWrapper);
+        return Objects.isNull(authorInfo) ? RestResp.ok(null) : RestResp.ok(authorInfo.getStatus());
+    }
 
 }