Commit 36797681 authored by Liang Ding's avatar Liang Ding

🚧 #12256 分类文章查询接口

parent 827852e1
......@@ -28,17 +28,15 @@ import org.b3log.latke.servlet.annotation.RequestProcessing;
import org.b3log.latke.servlet.annotation.RequestProcessor;
import org.b3log.latke.servlet.renderer.freemarker.AbstractFreeMarkerRenderer;
import org.b3log.latke.servlet.renderer.freemarker.FreeMarkerRenderer;
import org.b3log.latke.util.Paginator;
import org.b3log.latke.util.Requests;
import org.b3log.latke.util.Strings;
import org.b3log.solo.model.Article;
import org.b3log.solo.model.Category;
import org.b3log.solo.model.Common;
import org.b3log.solo.model.Option;
import org.b3log.solo.model.Tag;
import org.b3log.solo.processor.util.Filler;
import org.b3log.solo.service.*;
import org.b3log.solo.util.Skins;
import org.b3log.solo.util.comparator.Comparators;
import org.json.JSONException;
import org.json.JSONObject;
......@@ -48,7 +46,6 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.List;
import java.util.Map;
......@@ -56,7 +53,7 @@ import java.util.Map;
* Category processor.
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.0, Apr 7, 2017
* @version 1.0.0.1, Apr 8, 2017
* @since 2.0.0
*/
@RequestProcessor
......@@ -98,10 +95,10 @@ public class CategoryProcessor {
private UserQueryService userQueryService;
/**
* Tag query service.
* Category query service.
*/
@Inject
private TagQueryService tagQueryService;
private CategoryQueryService categoryQueryService;
/**
* Statistic management service.
......@@ -110,18 +107,18 @@ public class CategoryProcessor {
private StatisticMgmtService statisticMgmtService;
/**
* Gets the request page number from the specified request URI and tag title.
* Gets the request page number from the specified request URI and category URI.
*
* @param requestURI the specified request URI
* @param tagTitle the specified tag title
* @param categoryURI the specified category URI
* @return page number, returns {@code -1} if the specified request URI can not convert to an number
*/
private static int getCurrentPageNum(final String requestURI, final String tagTitle) {
if (Strings.isEmptyOrNull(tagTitle)) {
private static int getCurrentPageNum(final String requestURI, final String categoryURI) {
if (Strings.isEmptyOrNull(categoryURI)) {
return -1;
}
final String pageNumString = requestURI.substring((Latkes.getContextPath() + "/tags/" + tagTitle + "/").length());
final String pageNumString = requestURI.substring((Latkes.getContextPath() + "/category/" + categoryURI + "/").length());
return Requests.getCurrentPageNum(pageNumString);
}
......@@ -171,42 +168,33 @@ public class CategoryProcessor {
final int currentPageNum = getCurrentPageNum(requestURI, categoryURI);
if (-1 == currentPageNum) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
LOGGER.log(Level.DEBUG, "Category [URI={0}, currentPageNum={1}]", categoryURI, currentPageNum);
categoryURI = URLDecoder.decode(categoryURI, "UTF-8");
final JSONObject result = tagQueryService.getTagByTitle(categoryURI);
final JSONObject category = categoryQueryService.getByURI(categoryURI);
if (null == result) {
if (null == category) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final JSONObject tag = result.getJSONObject(Tag.TAG);
final String tagId = tag.getString(Keys.OBJECT_ID);
dataModel.put(Category.CATEGORY, category);
final JSONObject preference = preferenceQueryService.getPreference();
Skins.fillLangs(preference.optString(Option.ID_C_LOCALE_STRING), (String) request.getAttribute(Keys.TEMAPLTE_DIR_NAME), dataModel);
final int pageSize = preference.getInt(Option.ID_C_ARTICLE_LIST_DISPLAY_COUNT);
final int windowSize = preference.getInt(Option.ID_C_ARTICLE_LIST_PAGINATION_WINDOW_SIZE);
final String categoryId = category.optString(Keys.OBJECT_ID);
final List<JSONObject> articles = articleQueryService.getArticlesByTag(tagId, currentPageNum, pageSize);
final JSONObject result = articleQueryService.getCategoryArticles(categoryId, currentPageNum, pageSize);
final List<JSONObject> articles = (List<JSONObject>) result.opt(Article.ARTICLES);
if (articles.isEmpty()) {
try {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} catch (final IOException ex) {
LOGGER.error(ex.getMessage());
}
}
Skins.fillLangs(preference.optString(Option.ID_C_LOCALE_STRING), (String) request.getAttribute(Keys.TEMAPLTE_DIR_NAME), dataModel);
final boolean hasMultipleUsers = userQueryService.hasMultipleUsers();
if (hasMultipleUsers) {
filler.setArticlesExProperties(request, articles, preference);
} else {
......@@ -216,36 +204,18 @@ public class CategoryProcessor {
filler.setArticlesExProperties(request, articles, author, preference);
}
final int tagArticleCount = tag.getInt(Tag.TAG_PUBLISHED_REFERENCE_COUNT);
final int pageCount = (int) Math.ceil((double) tagArticleCount / (double) pageSize);
LOGGER.log(Level.TRACE, "Paginate tag-articles[currentPageNum={0}, pageSize={1}, pageCount={2}, windowSize={3}]",
new Object[]{currentPageNum, pageSize, pageCount, windowSize});
final List<Integer> pageNums = Paginator.paginate(currentPageNum, pageSize, pageCount, windowSize);
LOGGER.log(Level.TRACE, "tag-articles[pageNums={0}]", pageNums);
Collections.sort(articles, Comparators.ARTICLE_CREATE_DATE_COMPARATOR);
final int pageCount = result.optJSONObject(Pagination.PAGINATION).optInt(Pagination.PAGINATION_PAGE_COUNT);
final List<Integer> pageNums = (List) result.optJSONObject(Pagination.PAGINATION).opt(Pagination.PAGINATION_PAGE_NUMS);
fillPagination(dataModel, pageCount, currentPageNum, articles, pageNums);
dataModel.put(Common.PATH, "/tags/" + URLEncoder.encode(categoryURI, "UTF-8"));
dataModel.put(Keys.OBJECT_ID, tagId);
dataModel.put(Tag.TAG, tag);
dataModel.put(Common.PATH, "/category/" + URLEncoder.encode(categoryURI, "UTF-8"));
filler.fillSide(request, dataModel, preference);
filler.fillBlogHeader(request, response, dataModel, preference);
filler.fillBlogFooter(request, dataModel, preference);
statisticMgmtService.incBlogViewCount(request, response);
} catch (final ServiceException e) {
LOGGER.log(Level.ERROR, e.getMessage(), e);
try {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} catch (final IOException ex) {
LOGGER.error(ex.getMessage());
}
} catch (final JSONException e) {
} catch (final ServiceException | JSONException e) {
LOGGER.log(Level.ERROR, e.getMessage(), e);
try {
......
......@@ -15,17 +15,6 @@
*/
package org.b3log.solo.service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.b3log.latke.Keys;
import org.b3log.latke.logging.Level;
import org.b3log.latke.logging.Logger;
......@@ -40,17 +29,8 @@ import org.b3log.latke.util.CollectionUtils;
import org.b3log.latke.util.Paginator;
import org.b3log.latke.util.Stopwatchs;
import org.b3log.latke.util.Strings;
import org.b3log.solo.model.Article;
import static org.b3log.solo.model.Article.*;
import org.b3log.solo.model.Common;
import org.b3log.solo.model.Option;
import org.b3log.solo.model.Sign;
import org.b3log.solo.model.Tag;
import org.b3log.solo.repository.ArchiveDateArticleRepository;
import org.b3log.solo.repository.ArticleRepository;
import org.b3log.solo.repository.TagArticleRepository;
import org.b3log.solo.repository.TagRepository;
import org.b3log.solo.repository.UserRepository;
import org.b3log.solo.model.*;
import org.b3log.solo.repository.*;
import org.b3log.solo.util.Emotions;
import org.b3log.solo.util.Markdowns;
import org.b3log.solo.util.comparator.Comparators;
......@@ -58,13 +38,20 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;
import static org.b3log.solo.model.Article.*;
/**
* Article query service.
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @author <a href="http://blog.sweelia.com">ArmstrongCN</a>
* @author <a href="http://zephyr.b3log.org">Zephyr</a>
* @version 1.1.3.4, Nov 17, 2016
* @version 1.2.3.4, Apr 8, 2017
* @since 0.3.5
*/
@Service
......@@ -73,13 +60,7 @@ public class ArticleQueryService {
/**
* Logger.
*/
private static final Logger LOGGER = Logger.getLogger(ArticleQueryService.class.getName());
/**
* User service.
*/
@Inject
private UserQueryService userQueryService;
private static final Logger LOGGER = Logger.getLogger(ArticleQueryService.class);
/**
* User repository.
......@@ -93,6 +74,18 @@ public class ArticleQueryService {
@Inject
private ArticleRepository articleRepository;
/**
* Category-Tag repository.
*/
@Inject
private CategoryTagRepository categoryTagRepository;
/**
* User service.
*/
@Inject
private UserQueryService userQueryService;
/**
* Preference query service.
*/
......@@ -129,6 +122,90 @@ public class ArticleQueryService {
@Inject
private LangPropsService langPropsService;
/**
* Gets category articles.
*
* @param categoryId the specified category id
* @param currentPageNum the specified current page number
* @param pageSize the specified page size
* @return result
* @throws ServiceException service exception
*/
public JSONObject getCategoryArticles(final String categoryId,
final int currentPageNum, final int pageSize) throws ServiceException {
final JSONObject ret = new JSONObject();
ret.put(Article.ARTICLES, (Object) Collections.emptyList());
final JSONObject pagination = new JSONObject();
ret.put(Pagination.PAGINATION, pagination);
pagination.put(Pagination.PAGINATION_PAGE_COUNT, 0);
pagination.put(Pagination.PAGINATION_PAGE_NUMS, (Object) Collections.emptyList());
try {
final JSONArray categoryTags =
categoryTagRepository.getByCategoryId(categoryId, 1, Integer.MAX_VALUE)
.optJSONArray(Keys.RESULTS);
if (categoryTags.length() <= 0) {
return ret;
}
final List<String> tagIds = new ArrayList<>();
for (int i = 0; i < categoryTags.length(); i++) {
tagIds.add(categoryTags.optJSONObject(i).optString(Tag.TAG + "_" + Keys.OBJECT_ID));
}
Query query = new Query().setFilter(
new PropertyFilter(Tag.TAG + "_" + Keys.OBJECT_ID, FilterOperator.IN, tagIds)).
setCurrentPageNum(currentPageNum).setPageSize(pageSize).
addSort(Keys.OBJECT_ID, SortDirection.DESCENDING);
JSONObject result = tagArticleRepository.get(query);
final JSONArray tagArticles = result.optJSONArray(Keys.RESULTS);
if (tagArticles.length() <= 0) {
return ret;
}
final int pageCount = result.optJSONObject(Pagination.PAGINATION).optInt(Pagination.PAGINATION_PAGE_COUNT);
final JSONObject preference = preferenceQueryService.getPreference();
final int windowSize = preference.optInt(Option.ID_C_ARTICLE_LIST_PAGINATION_WINDOW_SIZE);
final List<Integer> pageNums = Paginator.paginate(currentPageNum, pageSize, pageCount, windowSize);
pagination.put(Pagination.PAGINATION_PAGE_COUNT, pageCount);
pagination.put(Pagination.PAGINATION_PAGE_NUMS, (Object) pageNums);
final Set<String> articleIds = new HashSet<>();
for (int i = 0; i < tagArticles.length(); i++) {
articleIds.add(tagArticles.optJSONObject(i).optString(Article.ARTICLE + "_" + Keys.OBJECT_ID));
}
query = new Query().setFilter(CompositeFilterOperator.and(
new PropertyFilter(Keys.OBJECT_ID, FilterOperator.IN, articleIds))).
setPageCount(1).addSort(Keys.OBJECT_ID, SortDirection.DESCENDING);
final List<JSONObject> articles = new ArrayList<>();
final JSONArray articleArray = articleRepository.get(query).optJSONArray(Keys.RESULTS);
for (int i = 0; i < articleArray.length(); i++) {
final JSONObject article = articleArray.optJSONObject(i);
if (!article.optBoolean(Article.ARTICLE_IS_PUBLISHED)) {
// Skips the unpublished article
continue;
}
article.put(ARTICLE_CREATE_TIME, ((Date) article.opt(ARTICLE_CREATE_DATE)).getTime());
articles.add(article);
}
ret.put(Article.ARTICLES, (Object) articles);
return ret;
} catch (final RepositoryException | ServiceException e) {
LOGGER.log(Level.ERROR, "Gets category articles error", e);
throw new ServiceException(e);
}
}
/**
* Can the current user access an article specified by the given article id?
*
......@@ -149,20 +226,14 @@ public class ArticleQueryService {
final JSONObject article = articleRepository.get(articleId);
final String currentUserEmail = userQueryService.getCurrentUser(request).getString(User.USER_EMAIL);
if (!article.getString(Article.ARTICLE_AUTHOR_EMAIL).equals(currentUserEmail)) {
return false;
}
return true;
return article.getString(Article.ARTICLE_AUTHOR_EMAIL).equals(currentUserEmail);
}
/**
* Checks whether need password to view the specified article with the specified request.
*
* <p>
* Checks session, if not represents, checks article property {@link Article#ARTICLE_VIEW_PWD view password}.
* </p>
*
* <p>
* The blogger itself dose not need view password never.
* </p>
......@@ -231,12 +302,10 @@ public class ArticleQueryService {
/**
* Gets the specified article's author.
*
* <p>
* The specified article has a property {@value Article#ARTICLE_AUTHOR_EMAIL}, this method will use this property to
* get a user from users.
* </p>
*
* <p>
* If can't find the specified article's author (i.e. the author has been removed by administrator), returns
* administrator.
......@@ -367,7 +436,6 @@ public class ArticleQueryService {
/**
* Gets an article by the specified article id.
*
* <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p>
......@@ -397,7 +465,6 @@ public class ArticleQueryService {
* }
* }
* </pre>, returns {@code null} if not found
*
* @throws ServiceException service exception
*/
public JSONObject getArticle(final String articleId) throws ServiceException {
......@@ -450,26 +517,21 @@ public class ArticleQueryService {
/**
* Gets articles(by crate date descending) by the specified request json object.
*
* <p>
* If the property "articleIsPublished" of the specified request json object is {@code true}, the returned articles
* all are published, {@code false} otherwise.
* </p>
*
* <p>
* Specified the "excludes" for results properties exclusion.
* </p>
*
* @param requestJSONObject the specified request json object, for example, <pre>
* {
* @param requestJSONObject the specified request json object, for example,
* "paginationCurrentPageNum": 1,
* "paginationPageSize": 20,
* "paginationWindowSize": 10,
* "articleIsPublished": boolean,
* "excludes": ["", ....] // Optional
* }, see {@link Pagination} for more details
* </pre>
*
* see {@link Pagination} for more details
* @return for example, <pre>
* {
* "pagination": {
......@@ -491,7 +553,6 @@ public class ArticleQueryService {
* }, ....]
* }
* </pre>, order by article update date and sticky(put top).
*
* @throws ServiceException service exception
* @see Pagination
*/
......@@ -675,7 +736,6 @@ public class ArticleQueryService {
/**
* Gets a list of articles randomly with the specified fetch size.
*
* <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p>
......@@ -699,7 +759,6 @@ public class ArticleQueryService {
/**
* Gets the relevant published articles of the specified article.
*
* <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p>
......@@ -796,7 +855,6 @@ public class ArticleQueryService {
/**
* Gets the next article(by create date) by the specified article id.
*
* <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p>
......@@ -809,7 +867,6 @@ public class ArticleQueryService {
* "articleAbstract": ""
* }
* </pre> returns {@code null} if not found
*
* @throws ServiceException service exception
*/
public JSONObject getNextArticle(final String articleId) throws ServiceException {
......@@ -823,7 +880,6 @@ public class ArticleQueryService {
/**
* Gets the previous article(by create date) by the specified article id.
*
* <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p>
......@@ -836,7 +892,6 @@ public class ArticleQueryService {
* "articleAbstract": ""
* }
* </pre> returns {@code null} if not found
*
* @throws ServiceException service exception
*/
public JSONObject getPreviousArticle(final String articleId) throws ServiceException {
......@@ -850,7 +905,6 @@ public class ArticleQueryService {
/**
* Gets an article by the specified article id.
*
* <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p>
......@@ -870,7 +924,6 @@ public class ArticleQueryService {
/**
* Gets an article by the specified article permalink.
*
* <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p>
......@@ -925,7 +978,6 @@ public class ArticleQueryService {
/**
* Gets article contents with the specified article id.
*
* <p>
* Invoking this method dose not effect on article view count.
* </p>
......@@ -1014,7 +1066,6 @@ public class ArticleQueryService {
/**
* Removes unused properties of each article in the specified articles.
*
* <p>
* Remains the following properties:
* <ul>
......@@ -1022,7 +1073,6 @@ public class ArticleQueryService {
* <li>{@link Article#ARTICLE_PERMALINK article permalink}</li>
* </ul>
* </p>
*
* <p>
* The batch version of method {@link #removeUnusedProperties(org.json.JSONObject)}.
* </p>
......@@ -1038,7 +1088,6 @@ public class ArticleQueryService {
/**
* Removes unused properties of the specified article.
*
* <p>
* Remains the following properties:
* <ul>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment