From f625ee38e1d980affbfbb8cf152588806580d0cb Mon Sep 17 00:00:00 2001 From: xiongxiaoyang <773861846@qq.com> Date: Tue, 17 Nov 2020 10:25:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=9C=E5=AE=B6=E5=90=8E=E5=8F=B0=E5=AE=8C?= =?UTF-8?q?=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../novel/controller/AuthorController.java | 124 +++++-- .../novel/controller/BookController.java | 10 +- .../novel/controller/PageController.java | 10 +- .../java2nb/novel/service/BookService.java | 38 +- .../novel/service/impl/BookServiceImpl.java | 222 +++++++++++- .../main/resources/static/javascript/date.js | 16 + .../author/author_income_detail.html | 10 +- .../templates/author/content_add.html | 21 +- .../templates/author/content_update.html | 220 ++++++++++++ .../resources/templates/author/index.html | 71 ++-- .../templates/author/index_list.html | 330 ++++++++++++++++++ .../resources/templates/book/book_detail.html | 43 ++- 12 files changed, 1016 insertions(+), 99 deletions(-) create mode 100644 novel-front/src/main/resources/static/javascript/date.js create mode 100644 novel-front/src/main/resources/templates/author/content_update.html create mode 100644 novel-front/src/main/resources/templates/author/index_list.html diff --git a/novel-front/src/main/java/com/java2nb/novel/controller/AuthorController.java b/novel-front/src/main/java/com/java2nb/novel/controller/AuthorController.java index 6c4bcd8..56644a3 100644 --- a/novel-front/src/main/java/com/java2nb/novel/controller/AuthorController.java +++ b/novel-front/src/main/java/com/java2nb/novel/controller/AuthorController.java @@ -2,7 +2,9 @@ package com.java2nb.novel.controller; import com.github.pagehelper.PageInfo; import com.java2nb.novel.core.bean.ResultBean; +import com.java2nb.novel.core.bean.UserDetails; import com.java2nb.novel.core.enums.ResponseStatus; +import com.java2nb.novel.core.exception.BusinessException; import com.java2nb.novel.core.utils.BeanUtil; import com.java2nb.novel.entity.Author; import com.java2nb.novel.entity.Book; @@ -58,15 +60,7 @@ public class AuthorController extends BaseController{ @PostMapping("addBook") public ResultBean addBook(@RequestParam("bookDesc") String bookDesc,Book book,HttpServletRequest request){ - //查询作家信息 - Author author = authorService.queryAuthor(getUserDetails(request).getId()); - - //判断作者状态是否正常 - if(author.getStatus()==1){ - //封禁状态,不能发布小说 - return ResultBean.fail(ResponseStatus.AUTHOR_STATUS_FORBIDDEN); - - } + Author author = checkAuthor(request); //bookDesc不能使用book对象来接收,否则会自动去掉前面的空格 book.setBookDesc(bookDesc @@ -83,14 +77,7 @@ public class AuthorController extends BaseController{ * */ @PostMapping("updateBookStatus") public ResultBean updateBookStatus(Long bookId,Byte status,HttpServletRequest request){ - //查询作家信息 - Author author = authorService.queryAuthor(getUserDetails(request).getId()); - - //判断作者状态是否正常 - if(author.getStatus()==1){ - //封禁状态,不能发布小说 - return ResultBean.fail(ResponseStatus.AUTHOR_STATUS_FORBIDDEN); - } + Author author = checkAuthor(request); //更新小说状态,上架或下架 bookService.updateBookStatus(bookId,status,author.getId()); @@ -100,24 +87,79 @@ public class AuthorController extends BaseController{ + /** + * 删除章节 + */ + @PostMapping("deleteIndex") + public ResultBean deleteIndex(Long indexId, HttpServletRequest request) { + + Author author = checkAuthor(request); + + //删除章节 + bookService.deleteIndex(indexId, author.getId()); + + return ResultBean.ok(); + } + + /** + * 更新章节名 + */ + @PostMapping("updateIndexName") + public ResultBean updateIndexName(Long indexId, String indexName, HttpServletRequest request) { + + Author author = checkAuthor(request); + + //更新章节名 + bookService.updateIndexName(indexId, indexName, author.getId()); + + return ResultBean.ok(); + } + + + + /** * 发布章节内容 - * */ + */ @PostMapping("addBookContent") - public ResultBean addBookContent(Long bookId,String indexName,String content,Byte isVip,HttpServletRequest request){ - //查询作家信息 - Author author = authorService.queryAuthor(getUserDetails(request).getId()); + public ResultBean addBookContent(Long bookId, String indexName, String content,Byte isVip, HttpServletRequest request) { + Author author = checkAuthor(request); - //判断作者状态是否正常 - if(author.getStatus()==1){ - //封禁状态,不能发布小说 - return ResultBean.fail(ResponseStatus.AUTHOR_STATUS_FORBIDDEN); - } - - content = content.replaceAll("\\n","
") - .replaceAll("\\s"," "); + content = content.replaceAll("\\n", "
") + .replaceAll("\\s", " "); //发布章节内容 - bookService.addBookContent(bookId,indexName,content,isVip,author.getId()); + bookService.addBookContent(bookId, indexName, content,isVip, author.getId()); + + return ResultBean.ok(); + } + + /** + * 查询章节内容 + */ + @PostMapping("queryIndexContent") + public ResultBean queryIndexContent(Long indexId, HttpServletRequest request) { + + Author author = checkAuthor(request); + + String content = bookService.queryIndexContent(indexId, author.getId()); + + content = content.replaceAll("
", "\n") + .replaceAll(" ", " "); + + return ResultBean.ok(content); + } + + /** + * 更新章节内容 + */ + @PostMapping("updateBookContent") + public ResultBean updateBookContent(Long indexId, String indexName, String content, HttpServletRequest request) { + Author author = checkAuthor(request); + + content = content.replaceAll("\\n", "
") + .replaceAll("\\s", " "); + //更新章节内容 + bookService.updateBookContent(indexId, indexName, content, author.getId()); return ResultBean.ok(); } @@ -151,6 +193,28 @@ public class AuthorController extends BaseController{ )); } + private Author checkAuthor(HttpServletRequest request) { + + UserDetails userDetails = getUserDetails(request); + if (userDetails == null) { + throw new BusinessException(ResponseStatus.NO_LOGIN); + } + + //查询作家信息 + Author author = authorService.queryAuthor(userDetails.getId()); + + //判断作者状态是否正常 + if (author.getStatus() == 1) { + //封禁状态,不能发布小说 + throw new BusinessException(ResponseStatus.AUTHOR_STATUS_FORBIDDEN); + } + + + return author; + + + } + diff --git a/novel-front/src/main/java/com/java2nb/novel/controller/BookController.java b/novel-front/src/main/java/com/java2nb/novel/controller/BookController.java index d186c0d..a007c62 100644 --- a/novel-front/src/main/java/com/java2nb/novel/controller/BookController.java +++ b/novel-front/src/main/java/com/java2nb/novel/controller/BookController.java @@ -167,7 +167,15 @@ public class BookController extends BaseController{ * */ @PostMapping("queryNewIndexList") public ResultBean queryNewIndexList(Long bookId){ - return ResultBean.ok(bookService.queryIndexList(bookId,"index_num desc",10)); + return ResultBean.ok(bookService.queryIndexList(bookId,"index_num desc",1,10)); + } + + /** + * 目录页 + * */ + @PostMapping("/queryIndexList") + public ResultBean indexList(Long bookId,@RequestParam(value = "curr", defaultValue = "1") int page, @RequestParam(value = "limit", defaultValue = "5") int pageSize,@RequestParam(value = "orderBy") String orderBy) { + return ResultBean.ok(new PageInfo<>(bookService.queryIndexList(bookId,orderBy,page,pageSize))); } diff --git a/novel-front/src/main/java/com/java2nb/novel/controller/PageController.java b/novel-front/src/main/java/com/java2nb/novel/controller/PageController.java index 45cfc32..aa6ba24 100644 --- a/novel-front/src/main/java/com/java2nb/novel/controller/PageController.java +++ b/novel-front/src/main/java/com/java2nb/novel/controller/PageController.java @@ -101,9 +101,11 @@ public class PageController extends BaseController{ public String bookDetail(@PathVariable("bookId") Long bookId, Model model) { Book book = bookService.queryBookDetail(bookId); model.addAttribute("book",book); - //查询首章目录ID - Long firstBookIndexId = bookService.queryFirstBookIndexId(bookId); - model.addAttribute("firstBookIndexId",firstBookIndexId); + if(book.getLastIndexId() != null) { + //查询首章目录ID + Long firstBookIndexId = bookService.queryFirstBookIndexId(bookId); + model.addAttribute("firstBookIndexId", firstBookIndexId); + } return ThreadLocalUtil.getTemplateDir()+"book/book_detail"; } @@ -114,7 +116,7 @@ public class PageController extends BaseController{ public String indexList(@PathVariable("bookId") Long bookId, Model model) { Book book = bookService.queryBookDetail(bookId); model.addAttribute("book",book); - List bookIndexList = bookService.queryIndexList(bookId,null,null); + List bookIndexList = bookService.queryIndexList(bookId,null,1,null); model.addAttribute("bookIndexList",bookIndexList); model.addAttribute("bookIndexCount",bookIndexList.size()); return ThreadLocalUtil.getTemplateDir()+"book/book_index"; diff --git a/novel-front/src/main/java/com/java2nb/novel/service/BookService.java b/novel-front/src/main/java/com/java2nb/novel/service/BookService.java index 21afe4a..ced701e 100644 --- a/novel-front/src/main/java/com/java2nb/novel/service/BookService.java +++ b/novel-front/src/main/java/com/java2nb/novel/service/BookService.java @@ -66,10 +66,12 @@ public interface BookService { * 查询目录列表 * @param bookId 书籍ID * @param orderBy 排序 - *@param limit 查询条数 + * @param page 查询页码 + *@param pageSize 分页大小 *@return 目录集合 * */ - List queryIndexList(Long bookId, String orderBy, Integer limit); + List queryIndexList(Long bookId, String orderBy, Integer page, Integer pageSize); + /** * 查询目录 @@ -244,4 +246,36 @@ public interface BookService { * @return 作品列表 */ List queryBookList(Long authorId); + + /** + * 删除章节 + * @param indexId + * @param authorId 作家ID + */ + void deleteIndex(Long indexId, Long authorId); + + /** + * 更新章节名 + * @param indexId + * @param indexName + * @param authorId + */ + void updateIndexName(Long indexId, String indexName, Long authorId); + + /** + * 查询章节内容 + * @param indexId + * @param authorId + * @return + */ + String queryIndexContent(Long indexId, Long authorId); + + /** + * 更新章节内容 + * @param indexId + * @param indexName + * @param content + * @param authorId + */ + void updateBookContent( Long indexId, String indexName, String content, Long authorId); } diff --git a/novel-front/src/main/java/com/java2nb/novel/service/impl/BookServiceImpl.java b/novel-front/src/main/java/com/java2nb/novel/service/impl/BookServiceImpl.java index 1f8434c..fc6dd08 100644 --- a/novel-front/src/main/java/com/java2nb/novel/service/impl/BookServiceImpl.java +++ b/novel-front/src/main/java/com/java2nb/novel/service/impl/BookServiceImpl.java @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.mybatis.dynamic.sql.SortSpecification; import org.mybatis.dynamic.sql.render.RenderingStrategies; +import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; @@ -32,6 +33,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import tk.mybatis.orderbyhelper.OrderByHelper; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -41,6 +43,7 @@ import java.util.stream.Collectors; import static com.java2nb.novel.mapper.BookCategoryDynamicSqlSupport.bookCategory; import static com.java2nb.novel.mapper.BookCommentDynamicSqlSupport.bookComment; import static com.java2nb.novel.mapper.BookContentDynamicSqlSupport.bookContent; +import static com.java2nb.novel.mapper.BookContentDynamicSqlSupport.content; import static com.java2nb.novel.mapper.BookDynamicSqlSupport.*; import static com.java2nb.novel.mapper.BookIndexDynamicSqlSupport.bookIndex; import static org.mybatis.dynamic.sql.SqlBuilder.*; @@ -233,12 +236,12 @@ public class BookServiceImpl implements BookService { } @Override - public List queryIndexList(Long bookId, String orderBy, Integer limit) { + public List queryIndexList(Long bookId, String orderBy, Integer page, Integer pageSize) { if (StringUtils.isNotBlank(orderBy)) { OrderByHelper.orderBy(orderBy); } - if (limit != null) { - PageHelper.startPage(1, limit); + if (page != null && pageSize != null) { + PageHelper.startPage(page, pageSize); } SelectStatementProvider selectStatement = select(BookIndexDynamicSqlSupport.id, BookIndexDynamicSqlSupport.bookId, BookIndexDynamicSqlSupport.indexNum, BookIndexDynamicSqlSupport.indexName, BookIndexDynamicSqlSupport.updateTime, BookIndexDynamicSqlSupport.isVip) @@ -250,6 +253,7 @@ public class BookServiceImpl implements BookService { return bookIndexMapper.selectMany(selectStatement); } + @Override public BookIndex queryBookIndex(Long bookIndexId) { SelectStatementProvider selectStatement = select(BookIndexDynamicSqlSupport.id, BookIndexDynamicSqlSupport.bookId, BookIndexDynamicSqlSupport.indexNum, BookIndexDynamicSqlSupport.indexName, BookIndexDynamicSqlSupport.wordCount, BookIndexDynamicSqlSupport.updateTime, BookIndexDynamicSqlSupport.isVip) @@ -488,7 +492,7 @@ public class BookServiceImpl implements BookService { PageHelper.startPage(page, pageSize); - SelectStatementProvider selectStatement = select(id, bookName, visitCount, yesterdayBuy,lastIndexName, status) + SelectStatementProvider selectStatement = select(id, bookName, picUrl, catName, visitCount, yesterdayBuy, lastIndexUpdateTime, updateTime, wordCount, lastIndexName, status) .from(book) .where(authorId, isEqualTo(authorService.queryAuthor(userId).getId())) .orderBy(BookDynamicSqlSupport.createTime.descending()) @@ -538,10 +542,6 @@ public class BookServiceImpl implements BookService { //并不是更新自己的小说 return; } - if(book.getStatus() != 1){ - //小说未上架,不能设置VIP - isVip = 0; - } Long lastIndexId = new IdWorker().nextId(); Date currentDate = new Date(); int wordCount = content.length(); @@ -608,5 +608,211 @@ public class BookServiceImpl implements BookService { .render(RenderingStrategies.MYBATIS3)); } + @Transactional(rollbackFor = Exception.class) + @Override + public void deleteIndex(Long indexId, Long authorId) { + + //查询小说章节表信息 + List bookIndices = bookIndexMapper.selectMany( + select(BookIndexDynamicSqlSupport.bookId, BookIndexDynamicSqlSupport.wordCount) + .from(bookIndex) + .where(BookIndexDynamicSqlSupport.id, isEqualTo(indexId)).build().render(RenderingStrategy.MYBATIS3)); + if (bookIndices.size() > 0) { + BookIndex bookIndex = bookIndices.get(0); + //获取小说ID + Long bookId = bookIndex.getBookId(); + //查询小说表信息 + List books = bookMapper.selectMany( + select(wordCount, BookDynamicSqlSupport.authorId) + .from(book) + .where(id, isEqualTo(bookId)) + .build() + .render(RenderingStrategy.MYBATIS3)); + if (books.size() > 0) { + Book book = books.get(0); + int wordCount = book.getWordCount(); + //作者ID相同,表明该小说是登录用户发布,可以删除 + if (book.getAuthorId().equals(authorId)) { + //删除目录表和内容表记录 + bookIndexMapper.deleteByPrimaryKey(indexId); + bookContentMapper.delete(deleteFrom(bookContent).where(BookContentDynamicSqlSupport.indexId, isEqualTo(indexId)).build() + .render(RenderingStrategies.MYBATIS3)); + //更新总字数 + wordCount = wordCount - bookIndex.getWordCount(); + //更新最新章节 + Long lastIndexId = null; + String lastIndexName = null; + Date lastIndexUpdateTime = null; + List lastBookIndices = bookIndexMapper.selectMany( + select(BookIndexDynamicSqlSupport.id, BookIndexDynamicSqlSupport.indexName, BookIndexDynamicSqlSupport.createTime) + .from(BookIndexDynamicSqlSupport.bookIndex) + .where(BookIndexDynamicSqlSupport.bookId, isEqualTo(bookId)) + .orderBy(BookIndexDynamicSqlSupport.indexNum.descending()) + .limit(1) + .build() + .render(RenderingStrategy.MYBATIS3)); + if (lastBookIndices.size() > 0) { + BookIndex lastBookIndex = lastBookIndices.get(0); + lastIndexId = lastBookIndex.getId(); + lastIndexName = lastBookIndex.getIndexName(); + lastIndexUpdateTime = lastBookIndex.getCreateTime(); + + } + //更新小说主表信息 + bookMapper.update(update(BookDynamicSqlSupport.book) + .set(BookDynamicSqlSupport.wordCount) + .equalTo(wordCount) + .set(updateTime) + .equalTo(new Date()) + .set(BookDynamicSqlSupport.lastIndexId) + .equalTo(lastIndexId) + .set(BookDynamicSqlSupport.lastIndexName) + .equalTo(lastIndexName) + .set(BookDynamicSqlSupport.lastIndexUpdateTime) + .equalTo(lastIndexUpdateTime) + .where(id, isEqualTo(bookId)) + .build() + .render(RenderingStrategies.MYBATIS3)); + + + } + } + + + } + + + } + + @Override + public void updateIndexName(Long indexId, String indexName, Long authorId) { + //查询小说章节表信息 + List bookIndices = bookIndexMapper.selectMany( + select(BookIndexDynamicSqlSupport.bookId, BookIndexDynamicSqlSupport.wordCount) + .from(bookIndex) + .where(BookIndexDynamicSqlSupport.id, isEqualTo(indexId)).build().render(RenderingStrategy.MYBATIS3)); + if (bookIndices.size() > 0) { + BookIndex bookIndex = bookIndices.get(0); + //获取小说ID + Long bookId = bookIndex.getBookId(); + //查询小说表信息 + List books = bookMapper.selectMany( + select(wordCount, BookDynamicSqlSupport.authorId) + .from(book) + .where(id, isEqualTo(bookId)) + .build() + .render(RenderingStrategy.MYBATIS3)); + if (books.size() > 0) { + Book book = books.get(0); + //作者ID相同,表明该小说是登录用户发布,可以修改 + if (book.getAuthorId().equals(authorId)) { + + bookIndexMapper.update( + update(BookIndexDynamicSqlSupport.bookIndex) + .set(BookIndexDynamicSqlSupport.indexName) + .equalTo(indexName) + .set(BookIndexDynamicSqlSupport.updateTime) + .equalTo(new Date()) + .where(BookIndexDynamicSqlSupport.id, isEqualTo(indexId)) + .build() + .render(RenderingStrategy.MYBATIS3)); + + + } + } + + + } + } + + @Override + public String queryIndexContent(Long indexId, Long authorId) { + //查询小说章节表信息 + List bookIndices = bookIndexMapper.selectMany( + select(BookIndexDynamicSqlSupport.bookId, BookIndexDynamicSqlSupport.wordCount) + .from(bookIndex) + .where(BookIndexDynamicSqlSupport.id, isEqualTo(indexId)).build().render(RenderingStrategy.MYBATIS3)); + if (bookIndices.size() > 0) { + BookIndex bookIndex = bookIndices.get(0); + //获取小说ID + Long bookId = bookIndex.getBookId(); + //查询小说表信息 + List books = bookMapper.selectMany( + select(wordCount, BookDynamicSqlSupport.authorId) + .from(book) + .where(id, isEqualTo(bookId)) + .build() + .render(RenderingStrategy.MYBATIS3)); + if (books.size() > 0) { + Book book = books.get(0); + //作者ID相同,表明该小说是登录用户发布 + if (book.getAuthorId().equals(authorId)) { + return bookContentMapper.selectMany( + select(content) + .from(bookContent) + .where(BookContentDynamicSqlSupport.indexId, isEqualTo(indexId)) + .limit(1) + .build().render(RenderingStrategy.MYBATIS3)) + .get(0).getContent(); + } + + } + } + return ""; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void updateBookContent(Long indexId, String indexName, String content, Long authorId) { + + //查询小说章节表信息 + List bookIndices = bookIndexMapper.selectMany( + select(BookIndexDynamicSqlSupport.bookId, BookIndexDynamicSqlSupport.wordCount) + .from(bookIndex) + .where(BookIndexDynamicSqlSupport.id, isEqualTo(indexId)).build().render(RenderingStrategy.MYBATIS3)); + if (bookIndices.size() > 0) { + BookIndex bookIndex = bookIndices.get(0); + //获取小说ID + Long bookId = bookIndex.getBookId(); + //查询小说表信息 + List books = bookMapper.selectMany( + select(wordCount, BookDynamicSqlSupport.authorId) + .from(book) + .where(id, isEqualTo(bookId)) + .build() + .render(RenderingStrategy.MYBATIS3)); + if (books.size() > 0) { + Book book = books.get(0); + //作者ID相同,表明该小说是登录用户发布,可以修改 + if (book.getAuthorId().equals(authorId)) { + Date currentDate = new Date(); + int wordCount = content.length(); + + //更新小说目录表 + int update = bookIndexMapper.update( + update(BookIndexDynamicSqlSupport.bookIndex) + .set(BookIndexDynamicSqlSupport.indexName) + .equalTo(indexName) + .set(BookIndexDynamicSqlSupport.wordCount) + .equalTo(wordCount) + .set(BookIndexDynamicSqlSupport.updateTime) + .equalTo(currentDate) + .where(BookIndexDynamicSqlSupport.id, isEqualTo(indexId)) + .build().render(RenderingStrategy.MYBATIS3)); + + //更新小说内容表 + bookContentMapper.update( + update(BookContentDynamicSqlSupport.bookContent) + .set(BookContentDynamicSqlSupport.content) + .equalTo(content) + .where(BookContentDynamicSqlSupport.indexId, isEqualTo(indexId)) + .build().render(RenderingStrategy.MYBATIS3)); + + } + } + + } + } + } diff --git a/novel-front/src/main/resources/static/javascript/date.js b/novel-front/src/main/resources/static/javascript/date.js new file mode 100644 index 0000000..14392a1 --- /dev/null +++ b/novel-front/src/main/resources/static/javascript/date.js @@ -0,0 +1,16 @@ +//格式化时间函数 +Date.prototype.Format = function (fmt) { + var o = { + "M+": this.getMonth() + 1, //月份 + "d+": this.getDate(), //日 + "h+": this.getHours(), //小时 + "m+": this.getMinutes(), //分 + "s+": this.getSeconds(), //秒 + "q+": Math.floor((this.getMonth() + 3) / 3), //季度 + "S": this.getMilliseconds() //毫秒 + }; + if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); + for (var k in o) + if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); + return fmt; +}; diff --git a/novel-front/src/main/resources/templates/author/author_income_detail.html b/novel-front/src/main/resources/templates/author/author_income_detail.html index 7cdddf7..17bbd31 100644 --- a/novel-front/src/main/resources/templates/author/author_income_detail.html +++ b/novel-front/src/main/resources/templates/author/author_income_detail.html @@ -109,16 +109,24 @@ + + + + + + + + diff --git a/novel-front/src/main/resources/templates/author/index.html b/novel-front/src/main/resources/templates/author/index.html index 9226ec1..5fadc28 100644 --- a/novel-front/src/main/resources/templates/author/index.html +++ b/novel-front/src/main/resources/templates/author/index.html @@ -8,6 +8,19 @@ 作家管理系统-小说精品屋 + @@ -35,7 +48,11 @@
-
+
+ + +
+