feat: support article demo videos and category counts

This commit is contained in:
王鹏
2026-08-17 15:50:17 +08:00
parent 2bf0a6db59
commit a1a806f266
8 changed files with 101 additions and 8 deletions

View File

@@ -2,6 +2,7 @@ package com.ruoyi.web.controller.app;
import com.ruoyi.app.domain.AppBlogArticle; import com.ruoyi.app.domain.AppBlogArticle;
import com.ruoyi.app.mapper.AppBlogArticleMapper; import com.ruoyi.app.mapper.AppBlogArticleMapper;
import com.ruoyi.app.mapper.AppResourceMapper;
import com.ruoyi.app.service.ImageUrlService; import com.ruoyi.app.service.ImageUrlService;
import com.ruoyi.app.service.IAppBlogArticleService; import com.ruoyi.app.service.IAppBlogArticleService;
import com.ruoyi.common.annotation.Log; import com.ruoyi.common.annotation.Log;
@@ -19,7 +20,9 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 文章Controller * 文章Controller
@@ -39,6 +42,9 @@ public class AppBlogArticleController extends BaseController {
@Resource @Resource
private AppBlogArticleMapper appBlogArticleMapper; private AppBlogArticleMapper appBlogArticleMapper;
@Resource
private AppResourceMapper appResourceMapper;
@Resource @Resource
private ITtFileService fileService; private ITtFileService fileService;
@@ -87,6 +93,19 @@ public class AppBlogArticleController extends BaseController {
return success(appBlogArticle); return success(appBlogArticle);
} }
/**
* 获取首页四个分类的可见内容数量
*/
@GetMapping(value = "/app/homeCategoryCounts")
public AjaxResult homeCategoryCounts() {
Map<String, Long> counts = new LinkedHashMap<>();
counts.put("highQuality", appBlogArticleMapper.countByArticleType(7L, 1L));
counts.put("resource", appResourceMapper.countByIsShow(1L));
counts.put("integral", appBlogArticleMapper.countByArticleType(4L, 1L));
counts.put("free", appBlogArticleMapper.countByArticleType(8L, 1L));
return success(counts);
}
/** /**
* 新增文章 * 新增文章
*/ */

View File

@@ -43,6 +43,14 @@ public class AppBlogArticle extends BaseEntity
/** List-card thumbnail URL (not persisted). */ /** List-card thumbnail URL (not persisted). */
private String showImgThumb; private String showImgThumb;
/** 视频号演示视频feedId */
@Excel(name = "视频号演示视频ID")
private String videoFeedId;
/** 第二个视频号演示视频feedId */
@Excel(name = "视频号演示视频ID 2")
private String videoFeedId2;
/** 关联已有资源关联app_resource id */ /** 关联已有资源关联app_resource id */
@Excel(name = "关联已有资源", readConverterExp = "关=联app_resource,i=d") @Excel(name = "关联已有资源", readConverterExp = "关=联app_resource,i=d")
private Long appResourceId; private Long appResourceId;
@@ -171,6 +179,24 @@ public class AppBlogArticle extends BaseEntity
{ {
return showImgThumb; return showImgThumb;
} }
public void setVideoFeedId(String videoFeedId)
{
this.videoFeedId = videoFeedId;
}
public String getVideoFeedId()
{
return videoFeedId;
}
public void setVideoFeedId2(String videoFeedId2)
{
this.videoFeedId2 = videoFeedId2;
}
public String getVideoFeedId2()
{
return videoFeedId2;
}
public void setAppResourceId(Long appResourceId) public void setAppResourceId(Long appResourceId)
{ {
this.appResourceId = appResourceId; this.appResourceId = appResourceId;
@@ -260,6 +286,8 @@ public class AppBlogArticle extends BaseEntity
.append("contentInfo", getContentInfo()) .append("contentInfo", getContentInfo())
.append("articleType", getArticleType()) .append("articleType", getArticleType())
.append("showImg", getShowImg()) .append("showImg", getShowImg())
.append("videoFeedId", getVideoFeedId())
.append("videoFeedId2", getVideoFeedId2())
.append("appResourceId", getAppResourceId()) .append("appResourceId", getAppResourceId())
.append("lookNumber", getLookNumber()) .append("lookNumber", getLookNumber())
.append("loveNumber", getLoveNumber()) .append("loveNumber", getLoveNumber())

View File

@@ -3,6 +3,8 @@ package com.ruoyi.app.mapper;
import java.util.List; import java.util.List;
import com.ruoyi.app.domain.AppBlogArticle; import com.ruoyi.app.domain.AppBlogArticle;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update; import org.apache.ibatis.annotations.Update;
/** /**
@@ -37,6 +39,10 @@ public interface AppBlogArticleMapper
*/ */
public List<AppBlogArticle> selectAppBlogArticleList(AppBlogArticle appBlogArticle); public List<AppBlogArticle> selectAppBlogArticleList(AppBlogArticle appBlogArticle);
/** 查询指定类型的可见文章数量 */
@Select("select count(*) from app_blog_article where article_type = #{articleType} and is_show = #{isShow}")
public Long countByArticleType(@Param("articleType") Long articleType, @Param("isShow") Long isShow);
/** /**
* 新增文章 * 新增文章
* *

View File

@@ -4,6 +4,7 @@ import java.util.List;
import com.ruoyi.app.domain.AppResource; import com.ruoyi.app.domain.AppResource;
import com.ruoyi.app.domain.AppResourceList; import com.ruoyi.app.domain.AppResourceList;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update; import org.apache.ibatis.annotations.Update;
/** /**
@@ -44,6 +45,10 @@ public interface AppResourceMapper
*/ */
public List<AppResource> selectAppResourceList(AppResource appResource); public List<AppResource> selectAppResourceList(AppResource appResource);
/** 查询可见资源数量 */
@Select("select count(*) from app_resource where is_show = #{isShow}")
public Long countByIsShow(@Param("isShow") Long isShow);
/** /**
* 新增资源 * 新增资源
* *

View File

@@ -11,6 +11,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="articleType" column="article_type" /> <result property="articleType" column="article_type" />
<result property="keyword" column="keyword" /> <result property="keyword" column="keyword" />
<result property="showImg" column="show_img" /> <result property="showImg" column="show_img" />
<result property="videoFeedId" column="video_feed_id" />
<result property="videoFeedId2" column="video_feed_id_2" />
<result property="appResourceId" column="app_resource_id" /> <result property="appResourceId" column="app_resource_id" />
<result property="lookNumber" column="look_number" /> <result property="lookNumber" column="look_number" />
<result property="loveNumber" column="love_number" /> <result property="loveNumber" column="love_number" />
@@ -46,12 +48,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap> </resultMap>
<sql id="selectAppBlogArticleVo"> <sql id="selectAppBlogArticleVo">
select id, title, content_info, article_type, keyword, show_img, app_resource_id, look_number, weight, love_number, is_recommendation, is_show, is_ad, ad_number, del_flag, create_by, create_time, remark from app_blog_article select id, title, content_info, article_type, keyword, show_img, video_feed_id, video_feed_id_2, app_resource_id, look_number, weight, love_number, is_recommendation, is_show, is_ad, ad_number, del_flag, create_by, create_time, remark from app_blog_article
</sql> </sql>
<select id="selectAppBlogArticleList" parameterType="AppBlogArticle" resultMap="AppBlogArticleWithResourceResult"> <select id="selectAppBlogArticleList" parameterType="AppBlogArticle" resultMap="AppBlogArticleWithResourceResult">
select select
a.id, a.title, a.content_info, a.article_type, a.keyword, a.show_img, a.app_resource_id, a.id, a.title, a.content_info, a.article_type, a.keyword, a.show_img, a.video_feed_id, a.video_feed_id_2, a.app_resource_id,
a.look_number, a.weight, a.love_number, a.is_recommendation, a.is_show, a.is_ad, a.look_number, a.weight, a.love_number, a.is_recommendation, a.is_show, a.is_ad,
a.ad_number, a.del_flag, a.create_by, a.create_time, a.remark, a.ad_number, a.del_flag, a.create_by, a.create_time, a.remark,
r.id as resource_id, r.id as resource_id,
@@ -83,6 +85,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</where> </where>
<choose> <choose>
<when test="orderType == 'look_number'"> ORDER BY a.look_number desc, a.create_time desc</when > <when test="orderType == 'look_number'"> ORDER BY a.look_number desc, a.create_time desc</when >
<when test="orderType == 'create_time'"> ORDER BY a.create_time desc, a.id desc</when>
<otherwise> ORDER BY a.weight desc, a.create_time desc</otherwise > <otherwise> ORDER BY a.weight desc, a.create_time desc</otherwise >
</choose> </choose>
</select> </select>
@@ -106,6 +109,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="articleType != null">article_type,</if> <if test="articleType != null">article_type,</if>
<if test="keyword != null and keyword != ''">keyword,</if> <if test="keyword != null and keyword != ''">keyword,</if>
<if test="showImg != null">show_img,</if> <if test="showImg != null">show_img,</if>
<if test="videoFeedId != null">video_feed_id,</if>
<if test="videoFeedId2 != null">video_feed_id_2,</if>
<if test="appResourceId != null">app_resource_id,</if> <if test="appResourceId != null">app_resource_id,</if>
<if test="lookNumber != null">look_number,</if> <if test="lookNumber != null">look_number,</if>
<if test="loveNumber != null">love_number,</if> <if test="loveNumber != null">love_number,</if>
@@ -125,6 +130,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="articleType != null">#{articleType},</if> <if test="articleType != null">#{articleType},</if>
<if test="keyword != null and keyword != ''">#{keyword},</if> <if test="keyword != null and keyword != ''">#{keyword},</if>
<if test="showImg != null">#{showImg},</if> <if test="showImg != null">#{showImg},</if>
<if test="videoFeedId != null">#{videoFeedId},</if>
<if test="videoFeedId2 != null">#{videoFeedId2},</if>
<if test="appResourceId != null">#{appResourceId},</if> <if test="appResourceId != null">#{appResourceId},</if>
<if test="lookNumber != null">#{lookNumber},</if> <if test="lookNumber != null">#{lookNumber},</if>
<if test="loveNumber != null">#{loveNumber},</if> <if test="loveNumber != null">#{loveNumber},</if>
@@ -148,6 +155,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="articleType != null">article_type = #{articleType},</if> <if test="articleType != null">article_type = #{articleType},</if>
<if test="keyword != null and keyword != ''">keyword = #{keyword},</if> <if test="keyword != null and keyword != ''">keyword = #{keyword},</if>
<if test="showImg != null">show_img = #{showImg},</if> <if test="showImg != null">show_img = #{showImg},</if>
<if test="videoFeedId != null">video_feed_id = #{videoFeedId},</if>
<if test="videoFeedId2 != null">video_feed_id_2 = #{videoFeedId2},</if>
<if test="appResourceId != null">app_resource_id = #{appResourceId},</if> <if test="appResourceId != null">app_resource_id = #{appResourceId},</if>
<if test="lookNumber != null">look_number = #{lookNumber},</if> <if test="lookNumber != null">look_number = #{lookNumber},</if>
<if test="loveNumber != null">love_number = #{loveNumber},</if> <if test="loveNumber != null">love_number = #{loveNumber},</if>

View File

@@ -9,9 +9,6 @@
<el-option v-for="dict in dict.type.article_type" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in dict.type.article_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="关键字" prop="keyword">
<el-input v-model="queryParams.keyword" placeholder="请输入搜素关键字" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="推荐文章" prop="isRecommendation"> <el-form-item label="推荐文章" prop="isRecommendation">
<el-select v-model="queryParams.isRecommendation" placeholder="请选择是否为推荐文章" clearable> <el-select v-model="queryParams.isRecommendation" placeholder="请选择是否为推荐文章" clearable>
<el-option v-for="dict in dict.type.sys_yes_no2" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in dict.type.sys_yes_no2" :key="dict.value" :label="dict.label" :value="dict.value" />
@@ -58,8 +55,6 @@
<dict-tag :options="dict.type.article_type" :value="scope.row.articleType" /> <dict-tag :options="dict.type.article_type" :value="scope.row.articleType" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="搜素关键字" align="center" prop="keyword" />
<el-table-column label="游览次数" align="center" prop="lookNumber" /> <el-table-column label="游览次数" align="center" prop="lookNumber" />
<el-table-column label="点赞次数" align="center" prop="loveNumber" /> <el-table-column label="点赞次数" align="center" prop="loveNumber" />
<el-table-column label="权重" align="center" prop="weight" /> <el-table-column label="权重" align="center" prop="weight" />
@@ -80,8 +75,16 @@
</el-table-column> </el-table-column>
<!-- <el-table-column label="需要观看几次广告解锁" align="center" prop="adNumber" /> --> <!-- <el-table-column label="需要观看几次广告解锁" align="center" prop="adNumber" /> -->
<el-table-column label="关联已有资源" align="center" prop="appResourceId" /> <el-table-column label="关联已有资源" align="center" prop="appResourceId" />
<el-table-column label="演示视频" align="center" prop="videoFeedId" width="90">
<template slot-scope="scope">
<el-tag :type="scope.row.videoFeedId || scope.row.videoFeedId2 ? 'success' : 'info'" size="mini">
{{ scope.row.videoFeedId && scope.row.videoFeedId2
? '2个视频'
: (scope.row.videoFeedId || scope.row.videoFeedId2 ? '1个视频' : '未配置') }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160px" /> <el-table-column label="创建时间" align="center" prop="createTime" width="160px" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
@@ -119,6 +122,20 @@
<el-input v-model="form.showImg" placeholder="请输入封面图url" /> <el-input v-model="form.showImg" placeholder="请输入封面图url" />
<image-upload style="margin-top: 10px;" v-model="form.showImg"/> <image-upload style="margin-top: 10px;" v-model="form.showImg"/>
</el-form-item> </el-form-item>
<el-form-item label="演示视频ID" prop="videoFeedId">
<el-input
v-model.trim="form.videoFeedId"
placeholder="请输入 export/ 开头的视频 feedId留空表示暂无演示视频"
clearable
/>
</el-form-item>
<el-form-item label="演示视频ID 2" prop="videoFeedId2">
<el-input
v-model.trim="form.videoFeedId2"
placeholder="请输入第二个视频的 feedId留空表示只有一个演示视频"
clearable
/>
</el-form-item>
<el-form-item label="权重" prop="weight"> <el-form-item label="权重" prop="weight">
<el-input v-model="form.weight" placeholder="请输入权重 999 表示为置顶" /> <el-input v-model="form.weight" placeholder="请输入权重 999 表示为置顶" />
</el-form-item> </el-form-item>
@@ -283,6 +300,8 @@
contentInfo: null, contentInfo: null,
articleType: null, articleType: null,
showImg: null, showImg: null,
videoFeedId: null,
videoFeedId2: null,
appResourceId: null, appResourceId: null,
lookNumber: null, lookNumber: null,
loveNumber: null, loveNumber: null,

View File

@@ -0,0 +1,4 @@
-- 为每篇文章单独配置微信视频号演示视频。
-- 视频号 finderUserName 由小程序统一配置,此处只保存视频 feedId。
ALTER TABLE `app_blog_article`
ADD COLUMN `video_feed_id` VARCHAR(255) NULL COMMENT '视频号演示视频feedId' AFTER `show_img`;

View File

@@ -0,0 +1,3 @@
-- 每篇文章最多维护两个微信视频号演示视频。
ALTER TABLE `app_blog_article`
ADD COLUMN `video_feed_id_2` VARCHAR(255) NULL COMMENT '第二个视频号演示视频feedId' AFTER `video_feed_id`;