Bug Fixes
Article Archive Query
SELECT
FROM_UNIXTIME(create_date/1000,'%Y') AS year,
FROM_UNIXTIME(create_date/1000,'%m') AS month,
COUNT(*) AS count
FROM ms_article
GROUP BY year, month
File Upload Functionality
API Specification
Endpoint: /upload
Method: POST
Parameters:
| Parameter | Type | Description |
|---|---|---|
| image | file | Uploaded file |
Response Format:
{
"success": true,
"code": 200,
"msg": "success",
"data": "https://static.cherr.com/aa.png"
}
Maven Dependency
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.13.0, 7.13.99]</version>
</dependency>
Upload Controller Implementation
The controller generates unique filenames using UUID to prevent conflicts:
UUID.randomUUID().toString()creates a universal unique identifierStringUtils.substringAfterLast(originalFilename, ".")extracts file extension- Combines both to form "unique-id.extension" filename
@RestController
@RequestMapping("upload")
public class FileUploadController {
@Autowired
private CloudStorageUtil cloudStorage;
@PostMapping
public Result handleUpload(@RequestParam("image") MultipartFile uploadedFile) {
String originalName = uploadedFile.getOriginalFilename();
String uniqueFileName = generateUniqueName(originalName);
boolean status = cloudStorage.storeFile(uploadedFile, uniqueFileName);
if (status) {
return Result.success(CloudStorageUtil.baseUrl + uniqueFileName);
}
return Result.fail(20001, "Upload failed");
}
private String generateUniqueName(String fileName) {
return UUID.randomUUID().toString() + "." +
StringUtils.substringAfterLast(fileName, ".");
}
}
Cloud Storage Configuration
Application Properties
spring.servlet.multipart.max-request-size=20MB
spring.servlet.multipart.max-file-size=2MB
Storage Utility Class
@Component
public class CloudStorageUtil {
public static final String baseUrl = "http://sbf25hzn6.hb-bkt.clouddn.com/";
@Value("")
private String accessKey;
@Value("")
private String secretKey;
private static final String BUCKET_NAME = "cherriesovo-blog";
public boolean storeFile(MultipartFile file, String fileName) {
Configuration config = new Configuration(Region.huabei());
UploadManager manager = new UploadManager(config);
try {
byte[] fileData = file.getBytes();
Auth authentication = Auth.create(accessKey, secretKey);
String token = authentication.uploadToken(BUCKET_NAME);
Response response = manager.put(fileData, fileName, token);
JSON.parseObject(response.bodyString(), DefaultPutRet.class);
return true;
} catch (Exception exception) {
exception.printStackTrace();
}
return false;
}
}
Navigation and Content Classification
Category Listing
API Details
Endpoint: /categorys/detail
Method: GET
Response Example:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 1,
"avatar": "/static/category/front.png",
"categoryName": "Frontend",
"description": "Frontend development topics"
}
]
}
Data Transfer Object
@Data
public class CategoryDto {
private Long id;
private String avatar;
private String categoryName;
private String description;
}
Controller Layer
@RestController
@RequestMapping("categorys")
public class CategoryController {
@GetMapping("detail")
public Result getAllCategories() {
return categoryService.retrieveAllWithDetails();
}
}
Service Implementation
@Override
public Result retrieveAllWithDetails() {
List<Category> categories = categoryMapper.selectList(new LambdaQueryWrapper<>());
return Result.success(transformToDtoList(categories));
}
Tag Management
API Details
Endpoint: /tags/detail
Method: GET
Response Example:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 5,
"tagName": "springboot",
"avatar": "/static/tag/java.png"
}
]
}
Tag DTO
@Data
public class TagDto {
private Long id;
private String tagName;
private String avatar;
}
Tag Controller
@RestController
@RequestMapping("tags")
public class TagController {
@GetMapping("detail")
public Result getAllTags() {
return tagService.retrieveAllWithTagDetails();
}
}
Tag Service
@Override
public Result retrieveAllWithTagDetails() {
LambdaQueryWrapper<Tag> wrapper = new LambdaQueryWrapper<>();
List<Tag> tags = tagMapper.selectList(wrapper);
return Result.success(convertToTagDtoList(tags));
}
Category-Based Article Filtering
API Specification
Endpoint: /category/detail/{id}
Method: GET
Parameters:
| Parameter | Type | Description |
|---|---|---|
| id | path | Category identifier |
Controller Implementation
@GetMapping("detail/{id}")
public Result getCategoryById(@PathVariable("id") Long categoryId) {
return categoryService.findByIdWithDetails(categoryId);
}
Service Logic
@Override
public Result findByIdWithDetails(Long id) {
Category category = categoryMapper.selectById(id);
CategoryDto dto = convertToDto(category);
return Result.success(dto);
}
Enhanced Article Pagination
Modified pagination service to support category filtering:
@Override
public List<ArticleDto> paginateArticles(ArticleQueryParams params) {
Page<Article> page = new Page<>(params.getPageNum(), params.getPageSize());
LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
if (params.getCategoryId() != null) {
wrapper.eq(Article::getCategoryId, params.getCategoryId());
}
wrapper.orderByDesc(Article::getPriority, Article::getCreatedDate);
Page<Article> resultPage = articleMapper.selectPage(page, wrapper);
List<Article> articles = resultPage.getRecords();
return transformToArticleDtoList(articles, true, false, true);
}
Query Parameters Structure
@Data
public class ArticleQueryParams {
private int pageNum = 1;
private int pageSize = 10;
private Long categoryId;
private Long tagId;
}
Tag-Based Article Filtering
API Details
Endpoint: /tags/detail/{id}
Method: GET
Parameters:
| Parameter | Type | Description |
|---|---|---|
| id | path | Tag identifier |
Controller Code
@GetMapping("detail/{id}")
public Result getTagById(@PathVariable("id") Long tagId) {
return tagService.findTagWithDetails(tagId);
}
Service Implementation
@Override
public Result findTagWithDetails(Long id) {
Tag tag = tagMapper.selectById(id);
TagDto dto = convertToTagDto(tag);
return Result.success(dto);
}
Extended Article Query Logic
Enhanced article service to support tag-based filtering through junction table:
@Override
public List<ArticleDto> paginateArticles(ArticleQueryParams params) {
Page<Article> page = new Page<>(params.getPageNum(), params.getPageSize());
LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
if (params.getCategoryId() != null) {
wrapper.eq(Article::getCategoryId, params.getCategoryId());
}
List<Long> articleIds = new ArrayList<>();
if (params.getTagId() != null) {
LambdaQueryWrapper<ArticleTag> tagWrapper = new LambdaQueryWrapper<>();
tagWrapper.eq(ArticleTag::getTagId, params.getTagId());
List<ArticleTag> articleTags = articleTagMapper.selectList(tagWrapper);
for (ArticleTag relation : articleTags) {
articleIds.add(relation.getArticleId());
}
if (!articleIds.isEmpty()) {
wrapper.in(Article::getId, articleIds);
}
}
wrapper.orderByDesc(Article::getPriority, Article::getCreatedDate);
Page<Article> resultPage = articleMapper.selectPage(page, wrapper);
List<Article> articles = resultPage.getRecords();
return transformToArticleDtoList(articles, true, false, true);
}