初始化git 基础代码
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
package com.heima.article;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@MapperScan("com.heima.article.mapper")
|
||||
@EnableAsync
|
||||
public class ArticleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ArticleApplication.class,args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.heima.article.controller.v1;
|
||||
|
||||
import com.heima.article.service.ApArticleService;
|
||||
import com.heima.common.constants.ArticleConstants;
|
||||
import com.heima.model.common.article.dtos.ArticleHomeDto;
|
||||
import com.heima.model.common.dtos.ResponseResult;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/article")
|
||||
public class ArticleHomeController {
|
||||
|
||||
@Autowired
|
||||
private ApArticleService apArticleService;
|
||||
|
||||
|
||||
/**
|
||||
* 加载首页
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/load")
|
||||
public ResponseResult load(@RequestBody ArticleHomeDto dto) {
|
||||
return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_MORE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/loadmore")
|
||||
public ResponseResult loadmore(@RequestBody ArticleHomeDto dto) {
|
||||
return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_MORE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载最新
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/loadnew")
|
||||
public ResponseResult loadnew(@RequestBody ArticleHomeDto dto) {
|
||||
return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_NEW);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.heima.article.feign;
|
||||
|
||||
import com.heima.apis.article.IArticleClient;
|
||||
import com.heima.article.service.ApArticleService;
|
||||
import com.heima.model.article.dtos.ArticleDto;
|
||||
import com.heima.model.common.dtos.ResponseResult;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class ArticleClient implements IArticleClient {
|
||||
|
||||
@Autowired
|
||||
private ApArticleService apArticleService;
|
||||
|
||||
@PostMapping("/api/v1/article/save")
|
||||
@Override
|
||||
public ResponseResult saveArticle(@RequestBody ArticleDto dto) {
|
||||
return apArticleService.saveArticle(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.heima.article.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.heima.model.common.article.pojos.ApArticleConfig ;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface ApArticleConfigMapper extends BaseMapper<ApArticleConfig> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.heima.article.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.heima.model.common.article.pojos.ApArticleContent;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface ApArticleContentMapper extends BaseMapper<ApArticleContent> {
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.heima.article.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.heima.model.common.article.dtos.ArticleHomeDto;
|
||||
import com.heima.model.common.article.pojos.ApArticle;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ApArticleMapper extends BaseMapper<ApArticle> {
|
||||
|
||||
/**
|
||||
* 加载文章列表
|
||||
* @param dto
|
||||
* @param type 1. 加载更多 2. 加载最新
|
||||
* @return
|
||||
*/
|
||||
public List<ApArticle> loadArticleList(ArticleHomeDto dto, Short type);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.heima.article.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.heima.model.article.dtos.ArticleDto;
|
||||
import com.heima.model.common.article.dtos.ArticleHomeDto;
|
||||
import com.heima.model.common.article.pojos.ApArticle;
|
||||
import com.heima.model.common.dtos.ResponseResult;
|
||||
|
||||
public interface ApArticleService extends IService<ApArticle> {
|
||||
|
||||
/**
|
||||
* 加载文件列表
|
||||
* @param dto
|
||||
* @param type 1. 加载更多 2. 加载最新
|
||||
* @return
|
||||
*/
|
||||
public ResponseResult load(ArticleHomeDto dto, Short type);
|
||||
|
||||
/**
|
||||
* 保存App端相关文章
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
public ResponseResult saveArticle(ArticleDto dto);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.heima.article.service;
|
||||
|
||||
import com.heima.model.common.article.pojos.ApArticle;
|
||||
|
||||
public interface ArticleFreemarkerservice {
|
||||
|
||||
/**
|
||||
* 生成静态文件到minio中
|
||||
* @param apArticle
|
||||
* @param content
|
||||
*/
|
||||
public void buildArticleToMinIO(ApArticle apArticle, String content);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.heima.article.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.heima.article.mapper.ApArticleContentMapper;
|
||||
import com.heima.article.service.ArticleFreemarkerservice;
|
||||
import com.heima.model.common.article.pojos.ApArticleContent;
|
||||
import com.heima.article.mapper.ApArticleConfigMapper;
|
||||
import com.heima.model.common.article.pojos.ApArticleConfig;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.heima.article.mapper.ApArticleMapper;
|
||||
import com.heima.article.service.ApArticleService;
|
||||
import com.heima.common.constants.ArticleConstants;
|
||||
import com.heima.model.article.dtos.ArticleDto;
|
||||
import com.heima.model.common.article.dtos.ArticleHomeDto;
|
||||
import com.heima.model.common.article.pojos.ApArticle;
|
||||
import com.heima.model.common.dtos.ResponseResult;
|
||||
import com.heima.model.common.enums.AppHttpCodeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@Slf4j
|
||||
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
|
||||
|
||||
@Autowired
|
||||
private ApArticleMapper apArticleMapper;
|
||||
|
||||
private final static short MAX_PAGE_SIZE = 50;
|
||||
|
||||
/**
|
||||
* 加载文件列表
|
||||
*
|
||||
* @param dto
|
||||
* @param type 1. 加载更多 2. 加载最新
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ResponseResult load(ArticleHomeDto dto, Short type) {
|
||||
// 1.校验参数
|
||||
// 分页条数校验
|
||||
Integer size = dto.getSize();
|
||||
if (size == null || size == 0) {
|
||||
size = 10;
|
||||
}
|
||||
// 分页的值不超过50
|
||||
size = Math.min(size, MAX_PAGE_SIZE);
|
||||
|
||||
// 校验参数
|
||||
if (!type.equals(ArticleConstants.LOADTYPE_LOAD_MORE) && !type.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){
|
||||
type = ArticleConstants.LOADTYPE_LOAD_MORE;
|
||||
}
|
||||
|
||||
// 频道参数校验
|
||||
if (StringUtils.isBlank(dto.getTag())) {
|
||||
dto.setTag(ArticleConstants.DEFAULT_TAG);
|
||||
}
|
||||
|
||||
// 时间校验
|
||||
if (dto.getMaxBehotTime() == null) {
|
||||
dto.setMaxBehotTime(new Date());
|
||||
}
|
||||
if (dto.getMinBehotTime() == null) {
|
||||
dto.setMinBehotTime(new Date());
|
||||
}
|
||||
|
||||
// 2.查询数据
|
||||
List<ApArticle> articleList = apArticleMapper.loadArticleList(dto, type);
|
||||
|
||||
// 3.结果返回
|
||||
return ResponseResult.okResult(articleList);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private ApArticleConfigMapper apArticleConfigMapper;
|
||||
|
||||
@Autowired
|
||||
private ApArticleContentMapper apArticleContentMapper;
|
||||
|
||||
@Autowired
|
||||
private ArticleFreemarkerservice articleFreemarkerservice;
|
||||
|
||||
/**
|
||||
* 保存App端相关文章
|
||||
*
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ResponseResult saveArticle(ArticleDto dto) {
|
||||
// 1. 检查参数
|
||||
if (dto == null) {
|
||||
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
|
||||
}
|
||||
|
||||
ApArticle apArticle = new ApArticle();
|
||||
BeanUtils.copyProperties(dto, apArticle);
|
||||
|
||||
// 2. 判断是否存在参数
|
||||
if (dto.getId() == null) {
|
||||
// 2.1 不存在id 保存
|
||||
// 保存文章
|
||||
save(apArticle);
|
||||
|
||||
// 保存配置
|
||||
ApArticleConfig apArticleConfig = new ApArticleConfig(apArticle.getId());
|
||||
apArticleConfigMapper.insert(apArticleConfig);
|
||||
|
||||
// 保存文章内容
|
||||
ApArticleContent apArticleContent = new ApArticleContent();
|
||||
apArticleContent.setArticleId(apArticle.getId());
|
||||
apArticleContent.setContent(dto.getContent());
|
||||
apArticleContentMapper.insert(apArticleContent);
|
||||
} else {
|
||||
// 2.2 存在id 修改
|
||||
updateById(apArticle);
|
||||
|
||||
// 修改文章内容
|
||||
ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, dto.getId()));
|
||||
apArticleContent.setContent(dto.getContent());
|
||||
apArticleContentMapper.updateById(apArticleContent);
|
||||
}
|
||||
|
||||
// 异步调用 生成静态文件上传到minio
|
||||
articleFreemarkerservice.buildArticleToMinIO(apArticle,dto.getContent());
|
||||
|
||||
// 3. 结果返回
|
||||
return ResponseResult.okResult(apArticle.getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.heima.article.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.heima.article.mapper.ApArticleContentMapper;
|
||||
import com.heima.article.service.ApArticleService;
|
||||
import com.heima.article.service.ArticleFreemarkerservice;
|
||||
import com.heima.file.service.FileStorageService;
|
||||
import com.heima.model.common.article.pojos.ApArticle;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@Transactional
|
||||
public class ArticleFreemarkerserviceImpl implements ArticleFreemarkerservice {
|
||||
|
||||
@Autowired
|
||||
private ApArticleContentMapper apArticleContentMapper;
|
||||
|
||||
@Autowired
|
||||
private Configuration configuration;
|
||||
|
||||
@Autowired
|
||||
private FileStorageService fileStorageService;
|
||||
|
||||
@Autowired
|
||||
private ApArticleService apArticleService;
|
||||
|
||||
/**
|
||||
* 生成静态文件到minio中
|
||||
*
|
||||
* @param apArticle
|
||||
* @param content
|
||||
*/
|
||||
@Async
|
||||
@Override
|
||||
public void buildArticleToMinIO(ApArticle apArticle, String content) {
|
||||
// 4.1 获取文章内容
|
||||
if (StringUtils.isNotBlank(content)) {
|
||||
// 4.2 文章内容通过freemarker生成html文件
|
||||
Template template = null;
|
||||
StringWriter out = new StringWriter();
|
||||
try {
|
||||
template = configuration.getTemplate("article.ftl");
|
||||
Map<String, Object> contentDataModel = new HashMap<>();
|
||||
contentDataModel.put("content", JSONArray.parseArray(content));
|
||||
template.process(contentDataModel, out);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
|
||||
// 4.3 把html文件上传到minio中
|
||||
InputStream in = new ByteArrayInputStream(out.toString().getBytes());
|
||||
String path = fileStorageService.uploadHtmlFile("", apArticle.getId() + ".html", in);
|
||||
System.out.println("上传文件路径:" + path);
|
||||
|
||||
// 4.4 修改ap_article表,保存static_url字段
|
||||
if (path != null) {
|
||||
boolean updated = apArticleService.update(Wrappers.<ApArticle>lambdaUpdate()
|
||||
.eq(ApArticle::getId, apArticle.getId())
|
||||
.set(ApArticle::getStaticUrl, path));
|
||||
System.out.println("更新结果:" + updated);
|
||||
if (!updated) {
|
||||
log.error("更新失败,可能没有匹配到对应的ApArticle记录");
|
||||
}
|
||||
} else {
|
||||
log.error("文件上传失败,path为空");
|
||||
}
|
||||
} else {
|
||||
log.error("未找到对应的文章内容或内容为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
server:
|
||||
port: 51802
|
||||
spring:
|
||||
application:
|
||||
name: leadnews-article
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 192.168.200.130:8848
|
||||
config:
|
||||
server-addr: 192.168.200.130:8848
|
||||
file-extension: yml
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<configuration>
|
||||
<!--定义日志文件的存储地址,使用绝对路径-->
|
||||
<property name="LOG_HOME" value="d:/logs"/>
|
||||
|
||||
<!-- Console 输出设置 -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<charset>utf8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 按照每天生成日志文件 -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!--日志文件输出的文件名-->
|
||||
<fileNamePattern>${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 异步输出 -->
|
||||
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
|
||||
<queueSize>512</queueSize>
|
||||
<!-- 添加附加的appender,最多只能添加一个 -->
|
||||
<appender-ref ref="FILE"/>
|
||||
</appender>
|
||||
|
||||
|
||||
<logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
<logger name="org.springframework.boot" level="debug"/>
|
||||
<root level="info">
|
||||
<!--<appender-ref ref="ASYNC"/>-->
|
||||
<appender-ref ref="FILE"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.heima.article.mapper.ApArticleMapper">
|
||||
|
||||
<resultMap id="resultMap" type="com.heima.model.common.article.pojos.ApArticle">
|
||||
<id column="id" property="id"/>
|
||||
<result column="title" property="title"/>
|
||||
<result column="author_id" property="authorId"/>
|
||||
<result column="author_name" property="authorName"/>
|
||||
<result column="channel_id" property="channelId"/>
|
||||
<result column="channel_name" property="channelName"/>
|
||||
<result column="layout" property="layout"/>
|
||||
<result column="flag" property="flag"/>
|
||||
<result column="images" property="images"/>
|
||||
<result column="labels" property="labels"/>
|
||||
<result column="likes" property="likes"/>
|
||||
<result column="collection" property="collection"/>
|
||||
<result column="comment" property="comment"/>
|
||||
<result column="views" property="views"/>
|
||||
<result column="province_id" property="provinceId"/>
|
||||
<result column="city_id" property="cityId"/>
|
||||
<result column="county_id" property="countyId"/>
|
||||
<result column="created_time" property="createdTime"/>
|
||||
<result column="publish_time" property="publishTime"/>
|
||||
<result column="sync_status" property="syncStatus"/>
|
||||
<result column="static_url" property="staticUrl"/>
|
||||
</resultMap>
|
||||
<select id="loadArticleList" resultMap="resultMap">
|
||||
SELECT
|
||||
aa.*
|
||||
FROM
|
||||
`ap_article` aa
|
||||
LEFT JOIN ap_article_config aac ON aa.id = aac.article_id
|
||||
<where>
|
||||
and aac.is_delete != 1
|
||||
and aac.is_down != 1
|
||||
<!-- loadmore -->
|
||||
<if test="type != null and type == 1">
|
||||
and aa.publish_time <![CDATA[<]]> #{dto.minBehotTime}
|
||||
</if>
|
||||
<if test="type != null and type == 2">
|
||||
and aa.publish_time <![CDATA[>]]> #{dto.maxBehotTime}
|
||||
</if>
|
||||
<if test="dto.tag != '__all__'">
|
||||
and aa.channel_id = #{dto.tag}
|
||||
</if>
|
||||
</where>
|
||||
order by aa.publish_time desc
|
||||
limit #{dto.size}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,182 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
|
||||
<title>黑马头条</title>
|
||||
<!-- 引入样式文件 -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.12.20/lib/index.css">
|
||||
<!-- 页面样式 -->
|
||||
<link rel="stylesheet" href="../../../plugins/css/index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<div class="article">
|
||||
<van-row>
|
||||
<van-col span="24" class="article-title" v-html="title"></van-col>
|
||||
</van-row>
|
||||
|
||||
<van-row type="flex" align="center" class="article-header">
|
||||
<van-col span="3">
|
||||
<van-image round class="article-avatar" src="https://p3.pstatp.com/thumb/1480/7186611868"></van-image>
|
||||
</van-col>
|
||||
<van-col span="16">
|
||||
<div v-html="authorName"></div>
|
||||
<div>{{ publishTime | timestampToDateTime }}</div>
|
||||
</van-col>
|
||||
<van-col span="5">
|
||||
<van-button round :icon="relation.isfollow ? '' : 'plus'" type="info" class="article-focus"
|
||||
:text="relation.isfollow ? '取消关注' : '关注'" :loading="followLoading" @click="handleClickArticleFollow">
|
||||
</van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
|
||||
<van-row class="article-content">
|
||||
<#if content??>
|
||||
<#list content as item>
|
||||
<#if item.type='text'>
|
||||
<van-col span="24" class="article-text">${item.value}</van-col>
|
||||
<#else>
|
||||
<van-col span="24" class="article-image">
|
||||
<van-image width="100%" src="${item.value}"></van-image>
|
||||
</van-col>
|
||||
</#if>
|
||||
</#list>
|
||||
</#if>
|
||||
</van-row>
|
||||
|
||||
<van-row type="flex" justify="center" class="article-action">
|
||||
<van-col>
|
||||
<van-button round :icon="relation.islike ? 'good-job' : 'good-job-o'" class="article-like"
|
||||
:loading="likeLoading" :text="relation.islike ? '取消赞' : '点赞'" @click="handleClickArticleLike"></van-button>
|
||||
<van-button round :icon="relation.isunlike ? 'delete' : 'delete-o'" class="article-unlike"
|
||||
:loading="unlikeLoading" @click="handleClickArticleUnlike">不喜欢</van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
|
||||
<!-- 文章评论列表 -->
|
||||
<van-list v-model="commentsLoading" :finished="commentsFinished" finished-text="没有更多了"
|
||||
@load="onLoadArticleComments">
|
||||
<van-row id="#comment-view" type="flex" class="article-comment" v-for="(item, index) in comments" :key="index">
|
||||
<van-col span="3">
|
||||
<van-image round src="https://p3.pstatp.com/thumb/1480/7186611868" class="article-avatar"></van-image>
|
||||
</van-col>
|
||||
<van-col span="21">
|
||||
<van-row type="flex" align="center" justify="space-between">
|
||||
<van-col class="comment-author" v-html="item.authorName"></van-col>
|
||||
<van-col>
|
||||
<van-button round :icon="item.operation === 0 ? 'good-job' : 'good-job-o'" size="normal"
|
||||
@click="handleClickCommentLike(item)">{{ item.likes || '' }}
|
||||
</van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
|
||||
<van-row>
|
||||
<van-col class="comment-content" v-html="item.content"></van-col>
|
||||
</van-row>
|
||||
<van-row type="flex" align="center">
|
||||
<van-col span="10" class="comment-time">
|
||||
{{ item.createdTime | timestampToDateTime }}
|
||||
</van-col>
|
||||
<van-col span="3">
|
||||
<van-button round size="normal" v-html="item.reply" @click="showCommentRepliesPopup(item.id)">回复 {{
|
||||
item.reply || '' }}
|
||||
</van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
</van-col>
|
||||
</van-row>
|
||||
</van-list>
|
||||
</div>
|
||||
<!-- 文章底部栏 -->
|
||||
<van-row type="flex" justify="space-around" align="center" class="article-bottom-bar">
|
||||
<van-col span="13">
|
||||
<van-field v-model="commentValue" placeholder="写评论">
|
||||
<template #button>
|
||||
<van-button icon="back-top" @click="handleSaveComment"></van-button>
|
||||
</template>
|
||||
</van-field>
|
||||
</van-col>
|
||||
<van-col span="3">
|
||||
<van-button icon="comment-o" @click="handleScrollIntoCommentView"></van-button>
|
||||
</van-col>
|
||||
<van-col span="3">
|
||||
<van-button :icon="relation.iscollection ? 'star' : 'star-o'" :loading="collectionLoading"
|
||||
@click="handleClickArticleCollection"></van-button>
|
||||
</van-col>
|
||||
<van-col span="3">
|
||||
<van-button icon="share-o"></van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
|
||||
<!-- 评论Popup 弹出层 -->
|
||||
<van-popup v-model="showPopup" closeable position="bottom"
|
||||
:style="{ width: '750px', height: '60%', left: '50%', 'margin-left': '-375px' }">
|
||||
<!-- 评论回复列表 -->
|
||||
<van-list v-model="commentRepliesLoading" :finished="commentRepliesFinished" finished-text="没有更多了"
|
||||
@load="onLoadCommentReplies">
|
||||
<van-row id="#comment-reply-view" type="flex" class="article-comment-reply"
|
||||
v-for="(item, index) in commentReplies" :key="index">
|
||||
<van-col span="3">
|
||||
<van-image round src="https://p3.pstatp.com/thumb/1480/7186611868" class="article-avatar"></van-image>
|
||||
</van-col>
|
||||
<van-col span="21">
|
||||
<van-row type="flex" align="center" justify="space-between">
|
||||
<van-col class="comment-author" v-html="item.authorName"></van-col>
|
||||
<van-col>
|
||||
<van-button round :icon="item.operation === 0 ? 'good-job' : 'good-job-o'" size="normal"
|
||||
@click="handleClickCommentReplyLike(item)">{{ item.likes || '' }}
|
||||
</van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
|
||||
<van-row>
|
||||
<van-col class="comment-content" v-html="item.content"></van-col>
|
||||
</van-row>
|
||||
<van-row type="flex" align="center">
|
||||
<!-- TODO: js计算时间差 -->
|
||||
<van-col span="10" class="comment-time">
|
||||
{{ item.createdTime | timestampToDateTime }}
|
||||
</van-col>
|
||||
</van-row>
|
||||
</van-col>
|
||||
</van-row>
|
||||
</van-list>
|
||||
<!-- 评论回复底部栏 -->
|
||||
<van-row type="flex" justify="space-around" align="center" class="comment-reply-bottom-bar">
|
||||
<van-col span="13">
|
||||
<van-field v-model="commentReplyValue" placeholder="写评论">
|
||||
<template #button>
|
||||
<van-button icon="back-top" @click="handleSaveCommentReply"></van-button>
|
||||
</template>
|
||||
</van-field>
|
||||
</van-col>
|
||||
<van-col span="3">
|
||||
<van-button icon="comment-o"></van-button>
|
||||
</van-col>
|
||||
<van-col span="3">
|
||||
<van-button icon="star-o"></van-button>
|
||||
</van-col>
|
||||
<van-col span="3">
|
||||
<van-button icon="share-o"></van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
</van-popup>
|
||||
</div>
|
||||
|
||||
<!-- 引入 Vue 和 Vant 的 JS 文件 -->
|
||||
<script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js">
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vant@2.12.20/lib/vant.min.js"></script>
|
||||
<!-- 引入 Axios 的 JS 文件 -->
|
||||
<#--<script src="https://unpkg.com/axios/dist/axios.min.js"></script>-->
|
||||
<script src="../../../plugins/js/axios.min.js"></script>
|
||||
<!-- 页面逻辑 -->
|
||||
<script src="../../../plugins/js/index.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,73 @@
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.heima.article.ArticleApplication;
|
||||
import com.heima.article.mapper.ApArticleContentMapper;
|
||||
import com.heima.article.service.ApArticleService;
|
||||
import com.heima.file.service.FileStorageService;
|
||||
import com.heima.model.common.article.pojos.ApArticle;
|
||||
import com.heima.model.common.article.pojos.ApArticleContent;
|
||||
import freemarker.template.Template;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import freemarker.template.Configuration;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SpringBootTest(classes = ArticleApplication.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
public class ArticleFreemarkerTest {
|
||||
|
||||
@Autowired
|
||||
private ApArticleContentMapper apArticleContentMapper;
|
||||
|
||||
@Autowired
|
||||
private Configuration configuration;
|
||||
|
||||
@Autowired
|
||||
private FileStorageService fileStorageService;
|
||||
|
||||
@Autowired
|
||||
private ApArticleService apArticleService;
|
||||
|
||||
@Test
|
||||
public void createStaticUrlText() throws Exception {
|
||||
// 4.1 获取文章内容
|
||||
ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, "1383827787629252610"));
|
||||
if (apArticleContent != null && StringUtils.isNotBlank(apArticleContent.getContent())) {
|
||||
// 4.2 文章内容通过freemarker生成html文件
|
||||
Template template = configuration.getTemplate("article.ftl");
|
||||
Map<String, Object> content = new HashMap<>();
|
||||
content.put("content", JSONArray.parseArray(apArticleContent.getContent()));
|
||||
StringWriter out = new StringWriter();
|
||||
template.process(content, out);
|
||||
|
||||
// 4.3 把html文件上传到minio中
|
||||
InputStream in = new ByteArrayInputStream(out.toString().getBytes());
|
||||
String path = fileStorageService.uploadHtmlFile("", apArticleContent.getArticleId() + ".html", in);
|
||||
System.out.println("上传文件路径:" + path);
|
||||
|
||||
// 4.4 修改ap_article表,保存static_url字段
|
||||
if (path != null) {
|
||||
boolean updated = apArticleService.update(Wrappers.<ApArticle>lambdaUpdate()
|
||||
.eq(ApArticle::getId, apArticleContent.getArticleId())
|
||||
.set(ApArticle::getStaticUrl, path));
|
||||
System.out.println("更新结果:" + updated);
|
||||
if (!updated) {
|
||||
System.out.println("更新失败,可能没有匹配到对应的ApArticle记录");
|
||||
}
|
||||
} else {
|
||||
System.out.println("文件上传失败,path为空");
|
||||
}
|
||||
} else {
|
||||
System.out.println("未找到对应的文章内容或内容为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user