@@ -80,17 +78,33 @@ const act = {
this.swiper.destroy(true, true)
this.swiper = null
}
- this.swiper = new Swiper('.activity-swiper', {
+ const swiperElement = this.$refs.activitySwiper
+ if (!swiperElement) {
+ return
+ }
+ // 仅操作当前组件的轮播节点,避免页面其他轮播或尺寸观察器影响卡片宽度。
+ this.swiper = new Swiper(swiperElement, {
slidesPerView: 1,
spaceBetween: 18,
centeredSlides: false,
loop: false,
+ watchOverflow: true,
+ observer: false,
+ observeParents: false,
+ resizeObserver: false,
+ // 鼠标停留在活动区域时可横向浏览,到首尾后释放页面滚动。
+ mousewheel: {
+ enabled: true,
+ forceToAxis: false,
+ releaseOnEdges: true,
+ sensitivity: 0.8,
+ },
navigation: {
- nextEl: '.swiper-button-next',
- prevEl: '.swiper-button-prev',
+ nextEl: this.$refs.nextButton,
+ prevEl: this.$refs.prevButton,
},
pagination: {
- el: '.swiper-pagination',
+ el: this.$refs.pagination,
type: 'progressbar',
},
breakpoints: {
@@ -134,10 +148,25 @@ const act = {
mounted() {
this.listAct()
},
+ beforeDestroy() {
+ if (this.swiper) {
+ this.swiper.destroy(true, true)
+ this.swiper = null
+ }
+ },
style: /*language=CSS*/ `
.activity-section-wrapper {
width: 100%;
min-width: 0;
+ max-width: 100%;
+ box-sizing: border-box;
+ overflow: hidden;
+ }
+
+ .section-act {
+ width: 100%;
+ min-width: 0;
+ max-width: 100%;
}
.section-act .section-act-title span {
@@ -182,8 +211,9 @@ const act = {
position: relative;
width: 100%;
min-width: 0;
+ max-width: 100%;
box-sizing: border-box;
- padding: 9px 14px;
+ padding: 9px 54px 12px;
/*margin-top: 30px;*/
overflow: hidden;
}
@@ -207,6 +237,8 @@ const act = {
background: white;
width: 40px;
height: 40px;
+ top: 72px;
+ margin-top: 0;
border-radius: 50%;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
@@ -225,11 +257,9 @@ const act = {
font-weight: bold;
}
- /* 进度条样式 */
+ /* 进度条不展示,活动通过左右箭头或区域内滚轮浏览。 */
.activity-swiper-container .swiper-pagination {
- position: relative;
- margin-top: 12px;
- height: 4px;
+ display: none;
}
.activity-swiper-container .swiper-pagination-progressbar {
diff --git a/src/main/resources/views/layouts/v4/home/index.html b/src/main/resources/views/layouts/v4/home/index.html
index 2bf702d2..347e226d 100644
--- a/src/main/resources/views/layouts/v4/home/index.html
+++ b/src/main/resources/views/layouts/v4/home/index.html
@@ -53,16 +53,23 @@ layout("/layouts/v4/baseLayout.html"){
.act-card {
display: flex;
align-items: center;
- min-width: 320px;
+ width: 100%;
+ min-width: 0;
+ max-width: 100%;
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
- padding: 11px;
+ padding: 22px 11px;
+ box-sizing: border-box;
+ overflow: hidden;
transition: transform 0.2s, box-shadow 0.2s;
}
.home-section {
width: 100%;
+ min-width: 0;
+ max-width: 100%;
+ box-sizing: border-box;
}
.home-section-header {
From ff3e8075b5dcd2119026c573ad7331e2bfbb21e4 Mon Sep 17 00:00:00 2001
From: = <=>
Date: Wed, 29 Jul 2026 16:50:05 +0800
Subject: [PATCH 2/7] commit
---
.../ProposalDashboardController.java | 31 ++++++++++++++++---
1 file changed, 26 insertions(+), 5 deletions(-)
diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/dashboard/ProposalDashboardController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/dashboard/ProposalDashboardController.java
index f4b5b30b..8dfce38c 100644
--- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/dashboard/ProposalDashboardController.java
+++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/dashboard/ProposalDashboardController.java
@@ -79,14 +79,19 @@ public class ProposalDashboardController {
// 节点
ProcessDefine define = processDefineService.getLastByName("JDHTA_NC");
ProcessModel processModel = processDefineService.processDefineToModel(define);
- List
taskModels = processModel.getTasks();
+ // 按流程图的连线关系遍历任务节点,保证看板展示顺序与流程图一致。
+ List taskModels = processModel.getModels(TaskModel.class);
List excludeNodes = new ArrayList<>(3);
excludeNodes.add("撰写提案");
excludeNodes.add("邀请附议人");
excludeNodes.add("提案附议");
- List taskNodes = taskModels.stream().filter(taskModel -> !excludeNodes.contains(taskModel.getDisplayName()))
+ // opinion_unit_reply、two_unit_reply 为临时屏蔽的承办单位答复节点,不在看板展示。
+ List hiddenNodeIds = List.of("opinion_unit_reply", "two_unit_reply");
+ List taskNodes = new ArrayList<>(taskModels.stream()
+ .filter(taskModel -> !excludeNodes.contains(taskModel.getDisplayName()))
+ .filter(taskModel -> !hiddenNodeIds.contains(taskModel.getName()))
.map(taskModel -> NutMap.NEW()
.addv("name", taskModel.getDisplayName())
.addv("id", taskModel.getName())
@@ -94,19 +99,35 @@ public class ProposalDashboardController {
.addv("count", 0)
.addv("todoCount", 0)
.addv("doneCount", 0)
- ).toList();
- nodes.addAll(taskNodes);
+ ).toList());
// 立案结果
List caseFilingResult = sysDictService.getSubListByCode("PROPOSAL_CASE_FILING_RESULT");
+ List caseFilingResultNodes = new ArrayList<>();
for (Sys_dict dict : caseFilingResult) {
- nodes.add(NutMap.NEW()
+ caseFilingResultNodes.add(NutMap.NEW()
.addv("name", dict.getName())
.addv("id", dict.getCode())
.addv("type", "caseFilingResult")
.addv("count", 0));
}
+ // 立案结果需展示在委员会确认承办单位之前,反馈评价统一固定到看板末尾。
+ List feedbackNodes = taskNodes.stream()
+ .filter(taskNode -> "feedback".equals(taskNode.getString("id")))
+ .toList();
+ taskNodes.removeIf(taskNode -> "feedback".equals(taskNode.getString("id")));
+ int committeeFilingUnitIndex = taskNodes.size();
+ for (int i = 0; i < taskNodes.size(); i++) {
+ if ("committeeFilingUnit".equals(taskNodes.get(i).getString("id"))) {
+ committeeFilingUnitIndex = i;
+ break;
+ }
+ }
+ taskNodes.addAll(committeeFilingUnitIndex, caseFilingResultNodes);
+ nodes.addAll(taskNodes);
+ nodes.addAll(feedbackNodes);
+
// 查询待办任务
Sql todoSql = Sqls.create("""
SELECT
From 5043cee74765f9489f030081d2a33931216f2d9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=9C=E5=AD=A6=E6=88=90?= <1274337408@qq.com>
Date: Thu, 30 Jul 2026 15:42:06 +0800
Subject: [PATCH 3/7] =?UTF-8?q?web=E7=AB=AF-=E6=96=B0=E5=A2=9E=E9=A3=8E?=
=?UTF-8?q?=E9=87=87=E5=A2=99=E6=A8=A1=E5=9D=97(=E5=B7=A5=E4=BC=9A?=
=?UTF-8?q?=E4=BF=A1=E6=81=AF=E4=BC=A0=E6=92=AD=E4=B8=8E=E6=96=87=E5=8C=96?=
=?UTF-8?q?=E5=B1=95=E7=A4=BA);?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/SpreadCategoryController.java | 101 ++++++++
.../controller/SpreadCreationController.java | 81 ++++++
.../controller/SpreadDisplayController.java | 76 ++++++
.../controller/SpreadManageController.java | 78 ++++++
.../SpreadStatisticsController.java | 60 +++++
.../zhgh/spread/models/SpreadCategory.java | 55 ++++
.../app/zhgh/spread/models/SpreadContent.java | 99 ++++++++
.../zhgh/spread/param/SpreadContentQuery.java | 12 +
.../spread/param/SpreadStatisticsQuery.java | 11 +
.../spread/service/SpreadCategoryService.java | 22 ++
.../spread/service/SpreadContentService.java | 31 +++
.../service/SpreadStatisticsService.java | 15 ++
.../impl/SpreadCategoryServiceImpl.java | 180 ++++++++++++++
.../impl/SpreadContentServiceImpl.java | 234 ++++++++++++++++++
.../impl/SpreadStatisticsServiceImpl.java | 113 +++++++++
.../resources/db/menu/init_spread_menu.sql | 79 ++++++
src/main/resources/db/spread_create.sql | 76 ++++++
.../platform/zhgh/spread/category/index.html | 176 +++++++++++++
.../platform/zhgh/spread/creation/index.html | 198 +++++++++++++++
.../platform/zhgh/spread/display/index.html | 215 ++++++++++++++++
.../platform/zhgh/spread/manage/index.html | 180 ++++++++++++++
.../zhgh/spread/statistics/index.html | 178 +++++++++++++
22 files changed, 2270 insertions(+)
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCategoryController.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCreationController.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/controller/SpreadDisplayController.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/controller/SpreadManageController.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/controller/SpreadStatisticsController.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/models/SpreadCategory.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/models/SpreadContent.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/param/SpreadContentQuery.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/param/SpreadStatisticsQuery.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/service/SpreadCategoryService.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/service/SpreadContentService.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/service/SpreadStatisticsService.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadCategoryServiceImpl.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadContentServiceImpl.java
create mode 100644 src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadStatisticsServiceImpl.java
create mode 100644 src/main/resources/db/menu/init_spread_menu.sql
create mode 100644 src/main/resources/db/spread_create.sql
create mode 100644 src/main/resources/views/platform/zhgh/spread/category/index.html
create mode 100644 src/main/resources/views/platform/zhgh/spread/creation/index.html
create mode 100644 src/main/resources/views/platform/zhgh/spread/display/index.html
create mode 100644 src/main/resources/views/platform/zhgh/spread/manage/index.html
create mode 100644 src/main/resources/views/platform/zhgh/spread/statistics/index.html
diff --git a/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCategoryController.java b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCategoryController.java
new file mode 100644
index 00000000..073b686a
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCategoryController.java
@@ -0,0 +1,101 @@
+package com.budwk.app.zhgh.spread.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.budwk.app.base.annotation.SLog;
+import com.budwk.app.base.result.Result;
+import com.budwk.app.zhgh.spread.models.SpreadCategory;
+import com.budwk.app.zhgh.spread.service.SpreadCategoryService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.nutz.aop.interceptor.ioc.TransAop;
+import org.nutz.ioc.aop.Aop;
+import org.nutz.ioc.loader.annotation.Inject;
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.mvc.annotation.At;
+import org.nutz.mvc.annotation.Ok;
+
+@IocBean
+@Ok("json:full")
+@Api("风采墙-栏目管理")
+@At("/platform/spread/category")
+public class SpreadCategoryController {
+
+ @Inject
+ private SpreadCategoryService categoryService;
+
+ @At("")
+ @Ok("beetl:/platform/zhgh/spread/category/index.html")
+ @SaCheckPermission("spread.category")
+ public void index() {
+ }
+
+ @At
+ @ApiOperation("栏目列表")
+ @SaCheckPermission("spread.category")
+ public Result listData() {
+ return Result.success(categoryService.listCategories(false));
+ }
+
+ @At
+ @ApiOperation("栏目树")
+ @SaCheckPermission("spread.category")
+ public Result treeData() {
+ return Result.success(categoryService.categoryTree(false));
+ }
+
+ @At
+ @ApiOperation("新增栏目")
+ @SaCheckPermission("spread.category")
+ @SLog(tag = "风采墙-栏目管理", msg = "新增栏目:${args[0].name}")
+ @Aop(TransAop.READ_COMMITTED)
+ public Result doAdd(SpreadCategory category) {
+ try {
+ categoryService.saveCategory(category);
+ return Result.success();
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @At
+ @ApiOperation("编辑栏目")
+ @SaCheckPermission("spread.category")
+ @SLog(tag = "风采墙-栏目管理", msg = "编辑栏目:${args[0].id}")
+ @Aop(TransAop.READ_COMMITTED)
+ public Result doEdit(SpreadCategory category) {
+ try {
+ categoryService.updateCategory(category);
+ return Result.success();
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @At
+ @ApiOperation("删除栏目")
+ @SaCheckPermission("spread.category")
+ @SLog(tag = "风采墙-栏目管理", msg = "删除栏目:${args[0]}")
+ @Aop(TransAop.READ_COMMITTED)
+ public Result doDelete(String id) {
+ try {
+ categoryService.deleteCategory(id);
+ return Result.success();
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @At
+ @ApiOperation("设置栏目显示状态")
+ @SaCheckPermission("spread.category")
+ @SLog(tag = "风采墙-栏目管理", msg = "栏目:${args[0]},显示状态:${args[1]}")
+ @Aop(TransAop.READ_COMMITTED)
+ public Result doVisible(String id, Integer visible) {
+ try {
+ categoryService.setVisible(id, visible);
+ return Result.success();
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCreationController.java b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCreationController.java
new file mode 100644
index 00000000..181ca5e6
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCreationController.java
@@ -0,0 +1,81 @@
+package com.budwk.app.zhgh.spread.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.budwk.app.base.annotation.SLog;
+import com.budwk.app.base.result.Result;
+import com.budwk.app.zhgh.spread.models.SpreadContent;
+import com.budwk.app.zhgh.spread.service.SpreadCategoryService;
+import com.budwk.app.zhgh.spread.service.SpreadContentService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.nutz.aop.interceptor.ioc.TransAop;
+import org.nutz.ioc.aop.Aop;
+import org.nutz.ioc.loader.annotation.Inject;
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.mvc.annotation.At;
+import org.nutz.mvc.annotation.Ok;
+
+@IocBean
+@Ok("json:full")
+@Api("风采墙-内容发布")
+@At("/platform/spread/creation")
+public class SpreadCreationController {
+
+ @Inject
+ private SpreadCategoryService categoryService;
+
+ @Inject
+ private SpreadContentService contentService;
+
+ @At("")
+ @Ok("beetl:/platform/zhgh/spread/creation/index.html")
+ @SaCheckPermission("spread.creation")
+ public void index() {
+ }
+
+ @At
+ @ApiOperation("可选栏目树")
+ @SaCheckPermission("spread.creation")
+ public Result categoryTree() {
+ return Result.success(categoryService.categoryTree(true));
+ }
+
+ @At
+ @ApiOperation("内容详情")
+ @SaCheckPermission("spread.creation")
+ public Result detailData(String id) {
+ try {
+ return Result.success(contentService.getContent(id));
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @At
+ @ApiOperation("新增内容")
+ @SaCheckPermission("spread.creation")
+ @SLog(tag = "风采墙-内容发布", msg = "新增内容:${args[0].title}")
+ @Aop(TransAop.READ_COMMITTED)
+ public Result doAdd(SpreadContent content) {
+ try {
+ contentService.saveContent(content);
+ return Result.success();
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @At
+ @ApiOperation("编辑内容")
+ @SaCheckPermission("spread.creation")
+ @SLog(tag = "风采墙-内容发布", msg = "编辑内容:${args[0].id}")
+ @Aop(TransAop.READ_COMMITTED)
+ public Result doEdit(SpreadContent content) {
+ try {
+ contentService.updateContent(content);
+ return Result.success();
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadDisplayController.java b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadDisplayController.java
new file mode 100644
index 00000000..3ec323f3
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadDisplayController.java
@@ -0,0 +1,76 @@
+package com.budwk.app.zhgh.spread.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.budwk.app.base.param.PageForm;
+import com.budwk.app.base.result.Result;
+import com.budwk.app.zhgh.spread.service.SpreadCategoryService;
+import com.budwk.app.zhgh.spread.service.SpreadContentService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.nutz.ioc.loader.annotation.Inject;
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.mvc.annotation.At;
+import org.nutz.mvc.annotation.Ok;
+
+@IocBean
+@Ok("json:full")
+@Api("风采墙-展示")
+@At("/platform/spread/display")
+public class SpreadDisplayController {
+
+ @Inject
+ private SpreadCategoryService categoryService;
+
+ @Inject
+ private SpreadContentService contentService;
+
+ @At("")
+ @Ok("beetl:/platform/zhgh/spread/display/index.html")
+ @SaCheckPermission("spread.display")
+ public void index() {
+ }
+
+ @At
+ @ApiOperation("可见栏目树")
+ @SaCheckPermission("spread.display")
+ public Result categoryTree() {
+ return Result.success(categoryService.categoryTree(true));
+ }
+
+ @At
+ @ApiOperation("已发布内容分页")
+ @SaCheckPermission("spread.display")
+ public Result pageData(PageForm pageForm, String categoryId) {
+ return Result.success(contentService.pagePublished(pageForm, categoryId));
+ }
+
+ @At
+ @ApiOperation("内容详情")
+ @SaCheckPermission("spread.display")
+ public Result detailData(String id) {
+ try {
+ return Result.success(contentService.getContent(id));
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @At
+ @ApiOperation("轮播内容")
+ @SaCheckPermission("spread.display")
+ public Result sliderData() {
+ return Result.success(contentService.sliderContent());
+ }
+
+ @At
+ @ApiOperation("增加浏览量")
+ @SaCheckPermission("spread.display")
+ public Result addView(String id) {
+ try {
+ contentService.addViewCount(id);
+ return Result.success();
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadManageController.java b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadManageController.java
new file mode 100644
index 00000000..ac1ccd13
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadManageController.java
@@ -0,0 +1,78 @@
+package com.budwk.app.zhgh.spread.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.budwk.app.base.annotation.SLog;
+import com.budwk.app.base.param.PageForm;
+import com.budwk.app.base.result.Result;
+import com.budwk.app.zhgh.spread.param.SpreadContentQuery;
+import com.budwk.app.zhgh.spread.service.SpreadCategoryService;
+import com.budwk.app.zhgh.spread.service.SpreadContentService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.nutz.aop.interceptor.ioc.TransAop;
+import org.nutz.ioc.aop.Aop;
+import org.nutz.ioc.loader.annotation.Inject;
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.mvc.annotation.At;
+import org.nutz.mvc.annotation.Ok;
+
+@IocBean
+@Ok("json:full")
+@Api("风采墙-内容管理")
+@At("/platform/spread/manage")
+public class SpreadManageController {
+
+ @Inject
+ private SpreadCategoryService categoryService;
+
+ @Inject
+ private SpreadContentService contentService;
+
+ @At("")
+ @Ok("beetl:/platform/zhgh/spread/manage/index.html")
+ @SaCheckPermission("spread.manage")
+ public void index() {
+ }
+
+ @At
+ @ApiOperation("内容分页")
+ @SaCheckPermission("spread.manage")
+ public Result pageData(PageForm pageForm, SpreadContentQuery query) {
+ return Result.success(contentService.pageContent(pageForm, query));
+ }
+
+ @At
+ @ApiOperation("栏目树")
+ @SaCheckPermission("spread.manage")
+ public Result categoryTree() {
+ return Result.success(categoryService.categoryTree(false));
+ }
+
+ @At
+ @ApiOperation("删除内容")
+ @SaCheckPermission("spread.manage")
+ @SLog(tag = "风采墙-内容管理", msg = "删除内容:${args[0]}")
+ @Aop(TransAop.READ_COMMITTED)
+ public Result doDelete(String id) {
+ try {
+ contentService.deleteContent(id);
+ return Result.success();
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @At
+ @ApiOperation("发布或撤回内容")
+ @SaCheckPermission("spread.manage")
+ @SLog(tag = "风采墙-内容管理", msg = "内容:${args[0]},发布状态:${args[1]}")
+ @Aop(TransAop.READ_COMMITTED)
+ public Result doPublish(String id, Integer status) {
+ try {
+ contentService.publishContent(id, status);
+ return Result.success();
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadStatisticsController.java b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadStatisticsController.java
new file mode 100644
index 00000000..e81056d3
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadStatisticsController.java
@@ -0,0 +1,60 @@
+package com.budwk.app.zhgh.spread.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.budwk.app.base.result.Result;
+import com.budwk.app.zhgh.spread.param.SpreadStatisticsQuery;
+import com.budwk.app.zhgh.spread.service.SpreadCategoryService;
+import com.budwk.app.zhgh.spread.service.SpreadStatisticsService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.nutz.ioc.loader.annotation.Inject;
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.mvc.annotation.At;
+import org.nutz.mvc.annotation.Ok;
+
+@IocBean
+@Ok("json:full")
+@Api("风采墙-统计分析")
+@At("/platform/spread/statistics")
+public class SpreadStatisticsController {
+
+ @Inject
+ private SpreadCategoryService categoryService;
+
+ @Inject
+ private SpreadStatisticsService statisticsService;
+
+ @At("")
+ @Ok("beetl:/platform/zhgh/spread/statistics/index.html")
+ @SaCheckPermission("spread.statistics")
+ public void index() {
+ }
+
+ @At
+ @ApiOperation("栏目树")
+ @SaCheckPermission("spread.statistics")
+ public Result categoryTree() {
+ return Result.success(categoryService.categoryTree(false));
+ }
+
+ @At
+ @ApiOperation("统计卡片")
+ @SaCheckPermission("spread.statistics")
+ public Result summary(SpreadStatisticsQuery query) {
+ return Result.success(statisticsService.summary(query));
+ }
+
+ @At
+ @ApiOperation("浏览量前十")
+ @SaCheckPermission("spread.statistics")
+ public Result top10(SpreadStatisticsQuery query) {
+ return Result.success(statisticsService.top10(query));
+ }
+
+ @At
+ @ApiOperation("栏目统计")
+ @SaCheckPermission("spread.statistics")
+ public Result categoryData(SpreadStatisticsQuery query) {
+ return Result.success(statisticsService.categoryStatistics(query));
+ }
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/models/SpreadCategory.java b/src/main/java/com/budwk/app/zhgh/spread/models/SpreadCategory.java
new file mode 100644
index 00000000..371346c3
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/models/SpreadCategory.java
@@ -0,0 +1,55 @@
+package com.budwk.app.zhgh.spread.models;
+
+import com.budwk.app.base.model.BaseModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.nutz.dao.entity.annotation.ColDefine;
+import org.nutz.dao.entity.annotation.ColType;
+import org.nutz.dao.entity.annotation.Column;
+import org.nutz.dao.entity.annotation.Comment;
+import org.nutz.dao.entity.annotation.Name;
+import org.nutz.dao.entity.annotation.Table;
+import org.nutz.dao.interceptor.annotation.PrevInsert;
+
+@Data
+@Table("spread_category")
+@EqualsAndHashCode(callSuper = true)
+public class SpreadCategory extends BaseModel {
+
+ @Column
+ @Name
+ @Comment("ID")
+ @ColDefine(type = ColType.VARCHAR, width = 32)
+ @PrevInsert(uu32 = true)
+ private String id;
+
+ @Column
+ @Comment("父栏目ID,0表示顶级栏目")
+ @ColDefine(type = ColType.VARCHAR, width = 32)
+ private String parentId;
+
+ @Column
+ @Comment("栏目名称")
+ @ColDefine(type = ColType.VARCHAR, width = 100)
+ private String name;
+
+ @Column
+ @Comment("栏目编码")
+ @ColDefine(type = ColType.VARCHAR, width = 50)
+ private String code;
+
+ @Column
+ @Comment("排序")
+ @ColDefine(type = ColType.INT)
+ private Integer sortOrder;
+
+ @Column
+ @Comment("是否可见")
+ @ColDefine(type = ColType.INT)
+ private Integer visible;
+
+ @Column
+ @Comment("是否参与轮播")
+ @ColDefine(type = ColType.INT)
+ private Integer slider;
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/models/SpreadContent.java b/src/main/java/com/budwk/app/zhgh/spread/models/SpreadContent.java
new file mode 100644
index 00000000..2d7eca2a
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/models/SpreadContent.java
@@ -0,0 +1,99 @@
+package com.budwk.app.zhgh.spread.models;
+
+import com.budwk.app.base.model.BaseModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.nutz.dao.entity.annotation.ColDefine;
+import org.nutz.dao.entity.annotation.ColType;
+import org.nutz.dao.entity.annotation.Column;
+import org.nutz.dao.entity.annotation.Comment;
+import org.nutz.dao.entity.annotation.Name;
+import org.nutz.dao.entity.annotation.Table;
+import org.nutz.dao.interceptor.annotation.PrevInsert;
+
+@Data
+@Table("spread_content")
+@EqualsAndHashCode(callSuper = true)
+public class SpreadContent extends BaseModel {
+
+ @Column
+ @Name
+ @Comment("ID")
+ @ColDefine(type = ColType.VARCHAR, width = 32)
+ @PrevInsert(uu32 = true)
+ private String id;
+
+ @Column
+ @Comment("栏目ID")
+ @ColDefine(type = ColType.VARCHAR, width = 32)
+ private String categoryId;
+
+ @Column
+ @Comment("栏目名称")
+ @ColDefine(type = ColType.VARCHAR, width = 100)
+ private String categoryName;
+
+ @Column
+ @Comment("标题")
+ @ColDefine(type = ColType.VARCHAR, width = 300)
+ private String title;
+
+ @Column
+ @Comment("副标题")
+ @ColDefine(type = ColType.VARCHAR, width = 255)
+ private String subTitle;
+
+ @Column
+ @Comment("摘要")
+ @ColDefine(type = ColType.VARCHAR, width = 1000)
+ private String summary;
+
+ @Column
+ @Comment("正文")
+ @ColDefine(customType = "longtext")
+ private String contentBody;
+
+ @Column
+ @Comment("封面图")
+ @ColDefine(type = ColType.VARCHAR, width = 1000)
+ private String thumbUrl;
+
+ @Column
+ @Comment("轮播图片")
+ @ColDefine(customType = "longtext")
+ private String sliderImages;
+
+ @Column
+ @Comment("是否置顶")
+ @ColDefine(type = ColType.INT)
+ private Integer isTop;
+
+ @Column
+ @Comment("是否推荐")
+ @ColDefine(type = ColType.INT)
+ private Integer isRecommend;
+
+ @Column
+ @Comment("是否头条")
+ @ColDefine(type = ColType.INT)
+ private Integer isHeadline;
+
+ @Column
+ @Comment("浏览量")
+ @ColDefine(type = ColType.INT)
+ private Integer viewCount;
+
+ @Column
+ @Comment("排序")
+ @ColDefine(type = ColType.INT)
+ private Integer sortOrder;
+
+ @Column
+ @Comment("是否发布")
+ @ColDefine(type = ColType.INT)
+ private Integer isPublished;
+
+ @Column
+ @Comment("发布时间")
+ private Long publishedAt;
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/param/SpreadContentQuery.java b/src/main/java/com/budwk/app/zhgh/spread/param/SpreadContentQuery.java
new file mode 100644
index 00000000..0b744e57
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/param/SpreadContentQuery.java
@@ -0,0 +1,12 @@
+package com.budwk.app.zhgh.spread.param;
+
+import lombok.Data;
+
+@Data
+public class SpreadContentQuery {
+ private String categoryId;
+ private String title;
+ private Integer isPublished;
+ private String beginTime;
+ private String endTime;
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/param/SpreadStatisticsQuery.java b/src/main/java/com/budwk/app/zhgh/spread/param/SpreadStatisticsQuery.java
new file mode 100644
index 00000000..b404da49
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/param/SpreadStatisticsQuery.java
@@ -0,0 +1,11 @@
+package com.budwk.app.zhgh.spread.param;
+
+import lombok.Data;
+
+@Data
+public class SpreadStatisticsQuery {
+ private String categoryId;
+ private Integer isPublished;
+ private String beginTime;
+ private String endTime;
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/service/SpreadCategoryService.java b/src/main/java/com/budwk/app/zhgh/spread/service/SpreadCategoryService.java
new file mode 100644
index 00000000..6b2a714b
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/service/SpreadCategoryService.java
@@ -0,0 +1,22 @@
+package com.budwk.app.zhgh.spread.service;
+
+import com.budwk.app.base.service.BaseService;
+import com.budwk.app.zhgh.spread.models.SpreadCategory;
+import org.nutz.lang.util.NutMap;
+
+import java.util.List;
+
+public interface SpreadCategoryService extends BaseService {
+
+ List listCategories(boolean visibleOnly);
+
+ List categoryTree(boolean visibleOnly);
+
+ void saveCategory(SpreadCategory category);
+
+ void updateCategory(SpreadCategory category);
+
+ void deleteCategory(String id);
+
+ void setVisible(String id, Integer visible);
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/service/SpreadContentService.java b/src/main/java/com/budwk/app/zhgh/spread/service/SpreadContentService.java
new file mode 100644
index 00000000..07967870
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/service/SpreadContentService.java
@@ -0,0 +1,31 @@
+package com.budwk.app.zhgh.spread.service;
+
+import com.budwk.app.base.page.Pagination;
+import com.budwk.app.base.param.PageForm;
+import com.budwk.app.base.service.BaseService;
+import com.budwk.app.zhgh.spread.models.SpreadContent;
+import com.budwk.app.zhgh.spread.param.SpreadContentQuery;
+import org.nutz.lang.util.NutMap;
+
+import java.util.List;
+
+public interface SpreadContentService extends BaseService {
+
+ Pagination pageContent(PageForm pageForm, SpreadContentQuery query);
+
+ Pagination pagePublished(PageForm pageForm, String categoryId);
+
+ SpreadContent getContent(String id);
+
+ void saveContent(SpreadContent content);
+
+ void updateContent(SpreadContent content);
+
+ void deleteContent(String id);
+
+ void publishContent(String id, Integer status);
+
+ void addViewCount(String id);
+
+ List sliderContent();
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/service/SpreadStatisticsService.java b/src/main/java/com/budwk/app/zhgh/spread/service/SpreadStatisticsService.java
new file mode 100644
index 00000000..08b9002f
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/service/SpreadStatisticsService.java
@@ -0,0 +1,15 @@
+package com.budwk.app.zhgh.spread.service;
+
+import com.budwk.app.zhgh.spread.param.SpreadStatisticsQuery;
+import org.nutz.lang.util.NutMap;
+
+import java.util.List;
+
+public interface SpreadStatisticsService {
+
+ NutMap summary(SpreadStatisticsQuery query);
+
+ List top10(SpreadStatisticsQuery query);
+
+ List categoryStatistics(SpreadStatisticsQuery query);
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadCategoryServiceImpl.java b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadCategoryServiceImpl.java
new file mode 100644
index 00000000..3cf1d81c
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadCategoryServiceImpl.java
@@ -0,0 +1,180 @@
+package com.budwk.app.zhgh.spread.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.budwk.app.base.service.impl.BaseServiceImpl;
+import com.budwk.app.zhgh.spread.models.SpreadCategory;
+import com.budwk.app.zhgh.spread.models.SpreadContent;
+import com.budwk.app.zhgh.spread.service.SpreadCategoryService;
+import org.nutz.dao.Cnd;
+import org.nutz.dao.Dao;
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.lang.util.NutMap;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+@IocBean(args = {"refer:dao"})
+public class SpreadCategoryServiceImpl extends BaseServiceImpl implements SpreadCategoryService {
+
+ public SpreadCategoryServiceImpl(Dao dao) {
+ super(dao);
+ }
+
+ @Override
+ public List listCategories(boolean visibleOnly) {
+ Cnd cnd = Cnd.where("delFlag", "=", false);
+ if (visibleOnly) {
+ cnd.and("visible", "=", 1);
+ }
+ cnd.asc("sortOrder").asc("createdAt");
+ return query(cnd);
+ }
+
+ @Override
+ public List categoryTree(boolean visibleOnly) {
+ List categories = listCategories(visibleOnly);
+ Map nodeMap = new LinkedHashMap<>();
+ for (SpreadCategory category : categories) {
+ nodeMap.put(category.getId(), NutMap.NEW()
+ .addv("id", category.getId())
+ .addv("parentId", category.getParentId())
+ .addv("name", category.getName())
+ .addv("code", category.getCode())
+ .addv("sortOrder", category.getSortOrder())
+ .addv("visible", category.getVisible())
+ .addv("slider", category.getSlider())
+ .addv("children", new ArrayList<>()));
+ }
+ List roots = new ArrayList<>();
+ for (NutMap node : nodeMap.values()) {
+ String parentId = node.getString("parentId");
+ if ("0".equals(parentId) || !nodeMap.containsKey(parentId)) {
+ roots.add(node);
+ } else {
+ nodeMap.get(parentId).getList("children", NutMap.class).add(node);
+ }
+ }
+ return roots;
+ }
+
+ @Override
+ public void saveCategory(SpreadCategory category) {
+ validateCategory(category, null);
+ applyDefaults(category);
+ insert(category);
+ syncParentSlider(category);
+ }
+
+ @Override
+ public void updateCategory(SpreadCategory category) {
+ if (category == null || StrUtil.isBlank(category.getId())) {
+ throw new RuntimeException("请选择要编辑的栏目");
+ }
+ SpreadCategory dbCategory = fetch(category.getId());
+ if (dbCategory == null || Boolean.TRUE.equals(dbCategory.getDelFlag())) {
+ throw new RuntimeException("栏目不存在");
+ }
+ validateCategory(category, category.getId());
+ if (!dbCategory.getParentId().equals(category.getParentId())
+ && count(Cnd.where("parentId", "=", category.getId()).and("delFlag", "=", false)) > 0) {
+ throw new RuntimeException("该栏目下存在子栏目,不能移动");
+ }
+ applyDefaults(category);
+ updateIgnoreNull(category);
+ syncParentSlider(category);
+ if ("0".equals(category.getParentId()) && Integer.valueOf(0).equals(category.getSlider())) {
+ SpreadCategory child = new SpreadCategory();
+ child.setSlider(0);
+ dao().update(SpreadCategory.class, org.nutz.dao.Chain.make("slider", 0),
+ Cnd.where("parentId", "=", category.getId()).and("delFlag", "=", false));
+ }
+ }
+
+ @Override
+ public void deleteCategory(String id) {
+ if (StrUtil.isBlank(id)) {
+ throw new RuntimeException("请选择要删除的栏目");
+ }
+ List children = query(Cnd.where("parentId", "=", id).and("delFlag", "=", false));
+ List ids = new ArrayList<>();
+ ids.add(id);
+ children.forEach(item -> ids.add(item.getId()));
+ if (dao().count(SpreadContent.class,
+ Cnd.where("categoryId", "in", ids).and("delFlag", "=", false)) > 0) {
+ throw new RuntimeException("栏目下存在内容,请先删除相关内容");
+ }
+ dao().clear(SpreadCategory.class, Cnd.where("id", "in", ids));
+ }
+
+ @Override
+ public void setVisible(String id, Integer visible) {
+ if (StrUtil.isBlank(id) || (visible == null || (visible != 0 && visible != 1))) {
+ throw new RuntimeException("栏目或显示状态不正确");
+ }
+ dao().update(SpreadCategory.class, org.nutz.dao.Chain.make("visible", visible),
+ Cnd.where("id", "=", id).and("delFlag", "=", false));
+ if (visible == 0) {
+ dao().update(SpreadCategory.class, org.nutz.dao.Chain.make("visible", 0),
+ Cnd.where("parentId", "=", id).and("delFlag", "=", false));
+ }
+ }
+
+ private void validateCategory(SpreadCategory category, String excludeId) {
+ if (category == null || StrUtil.isBlank(category.getName())) {
+ throw new RuntimeException("栏目名称不能为空");
+ }
+ if (StrUtil.isBlank(category.getCode())) {
+ throw new RuntimeException("栏目编码不能为空");
+ }
+ category.setName(category.getName().trim());
+ category.setCode(category.getCode().trim());
+ category.setParentId(StrUtil.blankToDefault(category.getParentId(), "0"));
+ if (category.getId() != null && category.getId().equals(category.getParentId())) {
+ throw new RuntimeException("上级栏目不能选择自身");
+ }
+ if (!"0".equals(category.getParentId())) {
+ SpreadCategory parent = fetch(category.getParentId());
+ if (parent == null || Boolean.TRUE.equals(parent.getDelFlag())) {
+ throw new RuntimeException("上级栏目不存在");
+ }
+ if (!"0".equals(parent.getParentId())) {
+ throw new RuntimeException("风采墙栏目最多支持两级");
+ }
+ }
+ Cnd nameCnd = Cnd.where("name", "=", category.getName())
+ .and("parentId", "=", category.getParentId())
+ .and("delFlag", "=", false);
+ Cnd codeCnd = Cnd.where("code", "=", category.getCode()).and("delFlag", "=", false);
+ if (StrUtil.isNotBlank(excludeId)) {
+ nameCnd.and("id", "!=", excludeId);
+ codeCnd.and("id", "!=", excludeId);
+ }
+ if (count(nameCnd) > 0) {
+ throw new RuntimeException("同级栏目名称已存在");
+ }
+ if (count(codeCnd) > 0) {
+ throw new RuntimeException("栏目编码已存在");
+ }
+ }
+
+ private void applyDefaults(SpreadCategory category) {
+ if (category.getSortOrder() == null) {
+ category.setSortOrder(255);
+ }
+ if (category.getVisible() == null) {
+ category.setVisible(1);
+ }
+ if (category.getSlider() == null) {
+ category.setSlider(0);
+ }
+ }
+
+ private void syncParentSlider(SpreadCategory category) {
+ if (!"0".equals(category.getParentId()) && Integer.valueOf(1).equals(category.getSlider())) {
+ dao().update(SpreadCategory.class, org.nutz.dao.Chain.make("slider", 1),
+ Cnd.where("id", "=", category.getParentId()).and("delFlag", "=", false));
+ }
+ }
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadContentServiceImpl.java b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadContentServiceImpl.java
new file mode 100644
index 00000000..d58ce44a
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadContentServiceImpl.java
@@ -0,0 +1,234 @@
+package com.budwk.app.zhgh.spread.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import com.budwk.app.base.page.Pagination;
+import com.budwk.app.base.param.PageForm;
+import com.budwk.app.base.service.impl.BaseServiceImpl;
+import com.budwk.app.zhgh.spread.models.SpreadCategory;
+import com.budwk.app.zhgh.spread.models.SpreadContent;
+import com.budwk.app.zhgh.spread.param.SpreadContentQuery;
+import com.budwk.app.zhgh.spread.service.SpreadContentService;
+import org.nutz.dao.Cnd;
+import org.nutz.dao.Dao;
+import org.nutz.dao.Sqls;
+import org.nutz.dao.sql.Sql;
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.lang.util.NutMap;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@IocBean(args = {"refer:dao"})
+public class SpreadContentServiceImpl extends BaseServiceImpl implements SpreadContentService {
+
+ public SpreadContentServiceImpl(Dao dao) {
+ super(dao);
+ }
+
+ @Override
+ public Pagination pageContent(PageForm pageForm, SpreadContentQuery query) {
+ Cnd cnd = buildCondition(query);
+ Sql countSql = Sqls.create("""
+ SELECT COUNT(1)
+ FROM spread_content c
+ $condition
+ """);
+ countSql.setCondition(cnd);
+ countSql.setCallback(Sqls.callback.integer());
+ dao().execute(countSql);
+
+ Sql listSql = Sqls.create("""
+ SELECT c.*
+ FROM spread_content c
+ $condition
+ ORDER BY c.isTop DESC, c.sortOrder ASC, c.createdAt DESC
+ """);
+ listSql.setCondition(cnd);
+ listSql.setPager(dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
+ listSql.setCallback(Sqls.callback.maps());
+ dao().execute(listSql);
+ return new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(),
+ countSql.getInt(), listSql.getList(NutMap.class));
+ }
+
+ @Override
+ public Pagination pagePublished(PageForm pageForm, String categoryId) {
+ SpreadContentQuery query = new SpreadContentQuery();
+ query.setCategoryId(categoryId);
+ query.setIsPublished(1);
+ Cnd cnd = buildCondition(query);
+ Sql countSql = Sqls.create("SELECT COUNT(1) FROM spread_content c $condition");
+ countSql.setCondition(cnd);
+ countSql.setCallback(Sqls.callback.integer());
+ dao().execute(countSql);
+
+ Sql listSql = Sqls.create("""
+ SELECT c.*
+ FROM spread_content c
+ $condition
+ ORDER BY c.isTop DESC, c.publishedAt DESC, c.sortOrder ASC, c.createdAt DESC
+ """);
+ listSql.setCondition(cnd);
+ listSql.setPager(dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
+ listSql.setCallback(Sqls.callback.maps());
+ dao().execute(listSql);
+ return new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(),
+ countSql.getInt(), listSql.getList(NutMap.class));
+ }
+
+ @Override
+ public SpreadContent getContent(String id) {
+ if (StrUtil.isBlank(id)) {
+ throw new RuntimeException("请选择内容");
+ }
+ SpreadContent content = fetch(Cnd.where("id", "=", id).and("delFlag", "=", false));
+ if (content == null) {
+ throw new RuntimeException("内容不存在");
+ }
+ return content;
+ }
+
+ @Override
+ public void saveContent(SpreadContent content) {
+ validateContent(content);
+ applyDefaults(content);
+ if (Integer.valueOf(1).equals(content.getIsPublished())) {
+ content.setPublishedAt(System.currentTimeMillis());
+ }
+ insert(content);
+ }
+
+ @Override
+ public void updateContent(SpreadContent content) {
+ if (content == null || StrUtil.isBlank(content.getId())) {
+ throw new RuntimeException("请选择要编辑的内容");
+ }
+ SpreadContent dbContent = getContent(content.getId());
+ validateContent(content);
+ applyDefaults(content);
+ if (Integer.valueOf(1).equals(content.getIsPublished()) && !Integer.valueOf(1).equals(dbContent.getIsPublished())) {
+ content.setPublishedAt(System.currentTimeMillis());
+ }
+ updateIgnoreNull(content);
+ }
+
+ @Override
+ public void deleteContent(String id) {
+ getContent(id);
+ dao().clear(SpreadContent.class, Cnd.where("id", "=", id));
+ }
+
+ @Override
+ public void publishContent(String id, Integer status) {
+ getContent(id);
+ if (status == null || (status != 0 && status != 1)) {
+ throw new RuntimeException("发布状态不正确");
+ }
+ org.nutz.dao.Chain chain = org.nutz.dao.Chain.make("isPublished", status);
+ if (status == 1) {
+ chain.add("publishedAt", System.currentTimeMillis());
+ }
+ dao().update(SpreadContent.class, chain, Cnd.where("id", "=", id).and("delFlag", "=", false));
+ }
+
+ @Override
+ public void addViewCount(String id) {
+ getContent(id);
+ Sql sql = Sqls.create("""
+ UPDATE spread_content
+ SET viewCount = IFNULL(viewCount, 0) + 1,
+ updatedAt = @updatedAt
+ WHERE id = @id AND delFlag = 0
+ """);
+ sql.params().set("id", id);
+ sql.params().set("updatedAt", System.currentTimeMillis());
+ dao().execute(sql);
+ }
+
+ @Override
+ public List sliderContent() {
+ Sql sql = Sqls.create("""
+ SELECT c.id, c.title, c.summary, c.thumbUrl, c.sliderImages, c.categoryId, c.categoryName
+ FROM spread_content c
+ INNER JOIN spread_category child ON child.id = c.categoryId
+ LEFT JOIN spread_category parent ON parent.id = child.parentId
+ WHERE c.delFlag = 0
+ AND c.isPublished = 1
+ AND child.delFlag = 0
+ AND child.visible = 1
+ AND child.slider = 1
+ AND (child.parentId = '0' OR (parent.delFlag = 0 AND parent.visible = 1 AND parent.slider = 1))
+ ORDER BY c.isTop DESC, c.publishedAt DESC, c.createdAt DESC
+ LIMIT 10
+ """);
+ sql.setCallback(Sqls.callback.maps());
+ dao().execute(sql);
+ return sql.getList(NutMap.class);
+ }
+
+ private Cnd buildCondition(SpreadContentQuery query) {
+ Cnd cnd = Cnd.where("c.delFlag", "=", false);
+ if (query == null) {
+ return cnd;
+ }
+ if (StrUtil.isNotBlank(query.getCategoryId())) {
+ List categoryIds = new ArrayList<>();
+ categoryIds.add(query.getCategoryId());
+ List children = dao().query(SpreadCategory.class,
+ Cnd.where("parentId", "=", query.getCategoryId()).and("delFlag", "=", false));
+ children.forEach(item -> categoryIds.add(item.getId()));
+ cnd.and("c.categoryId", "in", categoryIds);
+ }
+ cnd.and(Cnd.likeEX("c.title", query.getTitle()));
+ cnd.andEX("c.isPublished", "=", query.getIsPublished());
+ if (StrUtil.isNotBlank(query.getBeginTime())) {
+ cnd.and("c.createdAt", ">=", DateUtil.parse(query.getBeginTime()).getTime());
+ }
+ if (StrUtil.isNotBlank(query.getEndTime())) {
+ cnd.and("c.createdAt", "<=", DateUtil.parse(query.getEndTime()).getTime());
+ }
+ return cnd;
+ }
+
+ private void validateContent(SpreadContent content) {
+ if (content == null || StrUtil.isBlank(content.getCategoryId())) {
+ throw new RuntimeException("请选择栏目");
+ }
+ SpreadCategory category = dao().fetch(SpreadCategory.class,
+ Cnd.where("id", "=", content.getCategoryId()).and("delFlag", "=", false));
+ if (category == null) {
+ throw new RuntimeException("栏目不存在");
+ }
+ if (dao().count(SpreadCategory.class,
+ Cnd.where("parentId", "=", category.getId()).and("delFlag", "=", false)) > 0) {
+ throw new RuntimeException("内容只能发布到末级栏目");
+ }
+ if (StrUtil.isBlank(content.getTitle())) {
+ throw new RuntimeException("标题不能为空");
+ }
+ content.setTitle(content.getTitle().trim());
+ content.setCategoryName(category.getName());
+ }
+
+ private void applyDefaults(SpreadContent content) {
+ if (content.getIsTop() == null) {
+ content.setIsTop(0);
+ }
+ if (content.getIsRecommend() == null) {
+ content.setIsRecommend(0);
+ }
+ if (content.getIsHeadline() == null) {
+ content.setIsHeadline(0);
+ }
+ if (content.getViewCount() == null) {
+ content.setViewCount(0);
+ }
+ if (content.getSortOrder() == null) {
+ content.setSortOrder(255);
+ }
+ if (content.getIsPublished() == null) {
+ content.setIsPublished(0);
+ }
+ }
+}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadStatisticsServiceImpl.java b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadStatisticsServiceImpl.java
new file mode 100644
index 00000000..9e8d2c84
--- /dev/null
+++ b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadStatisticsServiceImpl.java
@@ -0,0 +1,113 @@
+package com.budwk.app.zhgh.spread.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import com.budwk.app.zhgh.spread.param.SpreadStatisticsQuery;
+import com.budwk.app.zhgh.spread.service.SpreadStatisticsService;
+import org.nutz.dao.Dao;
+import org.nutz.dao.Sqls;
+import org.nutz.dao.sql.Sql;
+import org.nutz.ioc.loader.annotation.Inject;
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.lang.util.NutMap;
+
+import java.util.List;
+
+@IocBean
+public class SpreadStatisticsServiceImpl implements SpreadStatisticsService {
+
+ @Inject
+ private Dao dao;
+
+ @Override
+ public NutMap summary(SpreadStatisticsQuery query) {
+ String condition = buildContentCondition(query, "c");
+ Sql sql = Sqls.create("""
+ SELECT
+ (SELECT COUNT(1) FROM spread_category cg WHERE cg.delFlag = 0) AS categoryCount,
+ COUNT(1) AS contentCount,
+ IFNULL(SUM(CASE WHEN c.isPublished = 1 THEN 1 ELSE 0 END), 0) AS publishedCount,
+ IFNULL(SUM(c.viewCount), 0) AS viewCount,
+ IFNULL(ROUND(AVG(c.viewCount)), 0) AS avgViewCount
+ FROM spread_content c
+ WHERE c.delFlag = 0
+ $condition
+ """);
+ sql.setVar("condition", condition);
+ setParams(sql, query);
+ sql.setCallback(Sqls.callback.map());
+ dao.execute(sql);
+ NutMap result = sql.getObject(NutMap.class);
+ return result == null ? NutMap.NEW() : result;
+ }
+
+ @Override
+ public List top10(SpreadStatisticsQuery query) {
+ Sql sql = Sqls.create("""
+ SELECT c.id, c.title, c.categoryName, c.viewCount, c.isPublished, c.publishedAt
+ FROM spread_content c
+ WHERE c.delFlag = 0
+ $condition
+ ORDER BY c.viewCount DESC, c.updatedAt DESC
+ LIMIT 10
+ """);
+ sql.setVar("condition", buildContentCondition(query, "c"));
+ setParams(sql, query);
+ sql.setCallback(Sqls.callback.maps());
+ dao.execute(sql);
+ return sql.getList(NutMap.class);
+ }
+
+ @Override
+ public List categoryStatistics(SpreadStatisticsQuery query) {
+ Sql sql = Sqls.create("""
+ SELECT c.categoryId, c.categoryName, COUNT(1) AS contentCount,
+ IFNULL(SUM(c.viewCount), 0) AS viewCount
+ FROM spread_content c
+ WHERE c.delFlag = 0
+ $condition
+ GROUP BY c.categoryId, c.categoryName
+ ORDER BY viewCount DESC, contentCount DESC
+ """);
+ sql.setVar("condition", buildContentCondition(query, "c"));
+ setParams(sql, query);
+ sql.setCallback(Sqls.callback.maps());
+ dao.execute(sql);
+ return sql.getList(NutMap.class);
+ }
+
+ private String buildContentCondition(SpreadStatisticsQuery query, String alias) {
+ if (query == null) {
+ return "";
+ }
+ StringBuilder condition = new StringBuilder();
+ if (StrUtil.isNotBlank(query.getCategoryId())) {
+ condition.append(" AND ").append(alias)
+ .append(".categoryId IN (SELECT id FROM spread_category WHERE id = @categoryId OR parentId = @categoryId)");
+ }
+ if (query.getIsPublished() != null) {
+ condition.append(" AND ").append(alias).append(".isPublished = @isPublished");
+ }
+ if (StrUtil.isNotBlank(query.getBeginTime())) {
+ condition.append(" AND ").append(alias).append(".createdAt >= @beginAt");
+ }
+ if (StrUtil.isNotBlank(query.getEndTime())) {
+ condition.append(" AND ").append(alias).append(".createdAt <= @endAt");
+ }
+ return condition.toString();
+ }
+
+ private void setParams(Sql sql, SpreadStatisticsQuery query) {
+ if (query == null) {
+ return;
+ }
+ sql.params().set("categoryId", query.getCategoryId());
+ sql.params().set("isPublished", query.getIsPublished());
+ if (StrUtil.isNotBlank(query.getBeginTime())) {
+ sql.params().set("beginAt", DateUtil.parse(query.getBeginTime()).getTime());
+ }
+ if (StrUtil.isNotBlank(query.getEndTime())) {
+ sql.params().set("endAt", DateUtil.parse(query.getEndTime()).getTime());
+ }
+ }
+}
diff --git a/src/main/resources/db/menu/init_spread_menu.sql b/src/main/resources/db/menu/init_spread_menu.sql
new file mode 100644
index 00000000..26b52596
--- /dev/null
+++ b/src/main/resources/db/menu/init_spread_menu.sql
@@ -0,0 +1,79 @@
+-- 风采墙电脑端菜单。使用 permission 做幂等判断,脚本可重复执行。
+INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
+SELECT
+ '9f5c0200000000000000000000000001',
+ '',
+ LPAD(IFNULL(MAX(CAST(path AS UNSIGNED)), 0) + 1, 4, '0'),
+ '风采墙',
+ 'Style Wall',
+ 'menu',
+ '',
+ '',
+ 'ti-medall',
+ 1,
+ 0,
+ 'spread',
+ NULL,
+ 992,
+ 1,
+ '',
+ UNIX_TIMESTAMP(NOW()) * 1000,
+ '',
+ UNIX_TIMESTAMP(NOW()) * 1000,
+ 0,
+ 'PC',
+ NULL,
+ NULL,
+ 'f',
+ 0,
+ 0
+FROM sys_menu
+WHERE (parentId = '' OR parentId IS NULL)
+ AND CHAR_LENGTH(path) = 4
+HAVING NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'spread') t);
+
+INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
+SELECT '9f5c0200000000000000000000000002', p.id, CONCAT(p.path, '0001'), '风采展示', 'Style Display', 'menu', '/platform/spread/display', 'data-pjax', '', 1, 0, 'spread.display', NULL, 1, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'f', 0, 0
+FROM sys_menu p
+WHERE p.permission = 'spread'
+ AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'spread.display') t);
+
+INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
+SELECT '9f5c0200000000000000000000000003', p.id, CONCAT(p.path, '0002'), '内容发布', 'Content Creation', 'menu', '/platform/spread/creation', 'data-pjax', '', 1, 0, 'spread.creation', NULL, 2, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'n', 0, 0
+FROM sys_menu p
+WHERE p.permission = 'spread'
+ AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'spread.creation') t);
+
+INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
+SELECT '9f5c0200000000000000000000000004', p.id, CONCAT(p.path, '0003'), '内容管理', 'Content Manage', 'menu', '/platform/spread/manage', 'data-pjax', '', 1, 0, 'spread.manage', NULL, 3, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'n', 0, 0
+FROM sys_menu p
+WHERE p.permission = 'spread'
+ AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'spread.manage') t);
+
+INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
+SELECT '9f5c0200000000000000000000000005', p.id, CONCAT(p.path, '0004'), '栏目管理', 'Category Manage', 'menu', '/platform/spread/category', 'data-pjax', '', 1, 0, 'spread.category', NULL, 4, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'l', 0, 0
+FROM sys_menu p
+WHERE p.permission = 'spread'
+ AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'spread.category') t);
+
+INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
+SELECT '9f5c0200000000000000000000000006', p.id, CONCAT(p.path, '0005'), '统计分析', 'Statistics', 'menu', '/platform/spread/statistics', 'data-pjax', '', 1, 0, 'spread.statistics', NULL, 5, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 't', 0, 0
+FROM sys_menu p
+WHERE p.permission = 'spread'
+ AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'spread.statistics') t);
+
+-- 系统管理员默认拥有风采墙全部权限,其他角色可在角色管理中按需分配。
+INSERT INTO sys_role_menu (roleId, menuId)
+SELECT r.id, m.id
+FROM sys_role r
+JOIN sys_menu m ON m.permission IN (
+ 'spread',
+ 'spread.display',
+ 'spread.creation',
+ 'spread.manage',
+ 'spread.category',
+ 'spread.statistics'
+)
+LEFT JOIN sys_role_menu rm ON rm.roleId = r.id AND rm.menuId = m.id
+WHERE r.code = 'SYSADMIN'
+ AND rm.roleId IS NULL;
diff --git a/src/main/resources/db/spread_create.sql b/src/main/resources/db/spread_create.sql
new file mode 100644
index 00000000..44912c29
--- /dev/null
+++ b/src/main/resources/db/spread_create.sql
@@ -0,0 +1,76 @@
+-- 风采墙栏目表,适用于 MySQL 8。
+CREATE TABLE IF NOT EXISTS `spread_category` (
+ `id` varchar(32) NOT NULL COMMENT 'ID',
+ `parentId` varchar(32) NOT NULL DEFAULT '0' COMMENT '父栏目ID,0表示顶级栏目',
+ `name` varchar(100) NOT NULL COMMENT '栏目名称',
+ `code` varchar(50) NOT NULL COMMENT '栏目编码',
+ `sortOrder` int NOT NULL DEFAULT 255 COMMENT '排序,数值越小越靠前',
+ `visible` tinyint NOT NULL DEFAULT 1 COMMENT '是否可见:0否,1是',
+ `slider` tinyint NOT NULL DEFAULT 0 COMMENT '是否参与轮播:0否,1是',
+ `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
+ `createdAt` bigint DEFAULT NULL COMMENT '创建时间',
+ `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
+ `updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
+ `delFlag` tinyint NOT NULL DEFAULT 0 COMMENT '删除标记',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_spread_category_code` (`code`),
+ KEY `idx_spread_category_parent` (`parentId`),
+ KEY `idx_spread_category_visible_sort` (`visible`, `sortOrder`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='风采墙栏目';
+
+-- 风采墙内容表。
+CREATE TABLE IF NOT EXISTS `spread_content` (
+ `id` varchar(32) NOT NULL COMMENT 'ID',
+ `categoryId` varchar(32) NOT NULL COMMENT '栏目ID',
+ `categoryName` varchar(100) NOT NULL COMMENT '栏目名称',
+ `title` varchar(300) NOT NULL COMMENT '标题',
+ `subTitle` varchar(255) DEFAULT NULL COMMENT '副标题',
+ `summary` varchar(1000) DEFAULT NULL COMMENT '摘要',
+ `contentBody` longtext COMMENT 'HTML正文',
+ `thumbUrl` varchar(1000) DEFAULT NULL COMMENT '封面图',
+ `sliderImages` longtext COMMENT '轮播图片JSON',
+ `isTop` tinyint NOT NULL DEFAULT 0 COMMENT '是否置顶',
+ `isRecommend` tinyint NOT NULL DEFAULT 0 COMMENT '是否推荐',
+ `isHeadline` tinyint NOT NULL DEFAULT 0 COMMENT '是否头条',
+ `viewCount` int NOT NULL DEFAULT 0 COMMENT '浏览量',
+ `sortOrder` int NOT NULL DEFAULT 255 COMMENT '排序',
+ `isPublished` tinyint NOT NULL DEFAULT 0 COMMENT '是否发布',
+ `publishedAt` bigint DEFAULT NULL COMMENT '发布时间',
+ `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
+ `createdAt` bigint DEFAULT NULL COMMENT '创建时间',
+ `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
+ `updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
+ `delFlag` tinyint NOT NULL DEFAULT 0 COMMENT '删除标记',
+ PRIMARY KEY (`id`),
+ KEY `idx_spread_content_category` (`categoryId`),
+ KEY `idx_spread_content_publish` (`isPublished`, `publishedAt`),
+ KEY `idx_spread_content_top_sort` (`isTop`, `sortOrder`),
+ KEY `idx_spread_content_created` (`createdAt`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='风采墙内容';
+
+-- 初始化栏目,仅在对应编码不存在时插入,脚本可重复执行。
+INSERT INTO `spread_category`
+(`id`, `parentId`, `name`, `code`, `sortOrder`, `visible`, `slider`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
+SELECT '9f5c0100000000000000000000000001', '0', '风采展示', 'style_wall', 1, 1, 1, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
+WHERE NOT EXISTS (SELECT 1 FROM `spread_category` WHERE `code` = 'style_wall');
+
+INSERT INTO `spread_category`
+(`id`, `parentId`, `name`, `code`, `sortOrder`, `visible`, `slider`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
+SELECT '9f5c0100000000000000000000000002', p.id, '劳模风采', 'model_worker_style', 1, 1, 1, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
+FROM `spread_category` p
+WHERE p.code = 'style_wall'
+ AND NOT EXISTS (SELECT 1 FROM `spread_category` WHERE `code` = 'model_worker_style');
+
+INSERT INTO `spread_category`
+(`id`, `parentId`, `name`, `code`, `sortOrder`, `visible`, `slider`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
+SELECT '9f5c0100000000000000000000000003', p.id, '巾帼风采', 'women_style', 2, 1, 1, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
+FROM `spread_category` p
+WHERE p.code = 'style_wall'
+ AND NOT EXISTS (SELECT 1 FROM `spread_category` WHERE `code` = 'women_style');
+
+INSERT INTO `spread_category`
+(`id`, `parentId`, `name`, `code`, `sortOrder`, `visible`, `slider`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
+SELECT '9f5c0100000000000000000000000004', p.id, '教工风采', 'faculty_style', 3, 1, 1, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
+FROM `spread_category` p
+WHERE p.code = 'style_wall'
+ AND NOT EXISTS (SELECT 1 FROM `spread_category` WHERE `code` = 'faculty_style');
diff --git a/src/main/resources/views/platform/zhgh/spread/category/index.html b/src/main/resources/views/platform/zhgh/spread/category/index.html
new file mode 100644
index 00000000..a1922d9a
--- /dev/null
+++ b/src/main/resources/views/platform/zhgh/spread/category/index.html
@@ -0,0 +1,176 @@
+
+
+
+
+
+ 新增栏目
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{row.slider === 1 ? '开启' : '关闭'}}
+
+
+
+
+
+ 编辑
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 显示
+ 隐藏
+
+
+
+
+ 开启
+ 关闭
+
+ 子栏目开启轮播后,其上级栏目会自动开启。
+
+
+
+ 取消
+ 确定
+
+
+
+
+
+
diff --git a/src/main/resources/views/platform/zhgh/spread/creation/index.html b/src/main/resources/views/platform/zhgh/spread/creation/index.html
new file mode 100644
index 00000000..5e711604
--- /dev/null
+++ b/src/main/resources/views/platform/zhgh/spread/creation/index.html
@@ -0,0 +1,198 @@
+
+
+
+
+
+ {{formData.id ? '编辑风采内容' : '发布风采内容'}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 建议使用横版图片,单张不超过 10MB。
+
+
+
+ 仅栏目开启“参与轮播”后生效,最多上传 10 张。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存草稿
+ 立即发布
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/views/platform/zhgh/spread/display/index.html b/src/main/resources/views/platform/zhgh/spread/display/index.html
new file mode 100644
index 00000000..43dc8813
--- /dev/null
+++ b/src/main/resources/views/platform/zhgh/spread/display/index.html
@@ -0,0 +1,215 @@
+
+
+
+
+
+ 风采墙
+ 记录奋斗身影 · 展示教工风采 · 凝聚榜样力量
+
+
+
+
+
![]()
+
{{item.title}}
+
+
+
+
榜样引领 · 风采绽放
+
+
+
+
+
+
+
+
+
+
{{currentCategory.name || '风采展示'}}
+ 展示教职工的先进事迹与精彩瞬间
+
+
+
+ {{datePart(item.publishedAt, 'DD')}}
+ {{datePart(item.publishedAt, 'YYYY.MM')}}
+
+
+
+ 置顶
+ {{item.title}}
+
+
{{item.summary || item.subTitle || '点击查看详细内容'}}
+
+ {{item.viewCount || 0}}
+
+
+
+
+
+ 返回列表
+ {{detail.title}}
+
+ {{formatTime(detail.publishedAt || detail.createdAt)}}
+ {{detail.categoryName}} {{detail.viewCount || 0}}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/views/platform/zhgh/spread/manage/index.html b/src/main/resources/views/platform/zhgh/spread/manage/index.html
new file mode 100644
index 00000000..ee07dfc1
--- /dev/null
+++ b/src/main/resources/views/platform/zhgh/spread/manage/index.html
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 发布内容
+
+
+
+
+
+ 置顶
+ {{row.title}}
+
+
+
+
+
+
+
+
+ {{row.isPublished === 1 ? '已发布' : '草稿'}}
+
+
+
+
+ {{formatTime(row.publishedAt)}}
+
+
+
+ 编辑
+
+ {{row.isPublished === 1 ? '撤回' : '发布'}}
+
+ 删除
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/views/platform/zhgh/spread/statistics/index.html b/src/main/resources/views/platform/zhgh/spread/statistics/index.html
new file mode 100644
index 00000000..ebee0c79
--- /dev/null
+++ b/src/main/resources/views/platform/zhgh/spread/statistics/index.html
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
栏目数量
{{summary.categoryCount || 0}}
+
内容总数
{{summary.contentCount || 0}}
+
已发布
{{summary.publishedCount || 0}}
+
累计浏览
{{summary.viewCount || 0}}
+
平均浏览
{{summary.avgViewCount || 0}}
+
+
+
+
+ 浏览量 TOP 10
+
+
+
+ 栏目浏览量占比
+
+
+
+
+
+ 栏目数据明细
+
+
+
+
+
+
+
+
+
+
+
+
From b3406f8b005ee551c0aca67c4f2713e01de2e0dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=9C=E5=AD=A6=E6=88=90?= <1274337408@qq.com>
Date: Thu, 30 Jul 2026 16:59:50 +0800
Subject: [PATCH 4/7] =?UTF-8?q?web=E7=AB=AF-=E6=96=B0=E5=A2=9E=E9=A3=8E?=
=?UTF-8?q?=E9=87=87=E5=A2=99=E6=A8=A1=E5=9D=97(=E5=B7=A5=E4=BC=9A?=
=?UTF-8?q?=E4=BF=A1=E6=81=AF=E4=BC=A0=E6=92=AD=E4=B8=8E=E6=96=87=E5=8C=96?=
=?UTF-8?q?=E5=B1=95=E7=A4=BA):=20=E4=BF=AE=E5=A4=8D=E6=A0=8F=E7=9B=AE?=
=?UTF-8?q?=E7=AE=A1=E7=90=86=E4=B8=8B=E7=BA=A7=E6=A0=8F=E7=9B=AE=E6=97=A0?=
=?UTF-8?q?=E6=B3=95=E6=89=93=E5=BC=80=E7=AD=89=E9=97=AE=E9=A2=98;?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../impl/SpreadCategoryServiceImpl.java | 25 ++++++------
src/main/resources/db/spread_create.sql | 2 +-
.../platform/zhgh/spread/category/index.html | 38 ++++++++++++++-----
3 files changed, 43 insertions(+), 22 deletions(-)
diff --git a/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadCategoryServiceImpl.java b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadCategoryServiceImpl.java
index 3cf1d81c..b4670347 100644
--- a/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadCategoryServiceImpl.java
+++ b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadCategoryServiceImpl.java
@@ -36,7 +36,9 @@ public class SpreadCategoryServiceImpl extends BaseServiceImpl i
public List categoryTree(boolean visibleOnly) {
List categories = listCategories(visibleOnly);
Map nodeMap = new LinkedHashMap<>();
+ Map> childrenMap = new LinkedHashMap<>();
for (SpreadCategory category : categories) {
+ List children = new ArrayList<>();
nodeMap.put(category.getId(), NutMap.NEW()
.addv("id", category.getId())
.addv("parentId", category.getParentId())
@@ -45,7 +47,8 @@ public class SpreadCategoryServiceImpl extends BaseServiceImpl i
.addv("sortOrder", category.getSortOrder())
.addv("visible", category.getVisible())
.addv("slider", category.getSlider())
- .addv("children", new ArrayList<>()));
+ .addv("children", children));
+ childrenMap.put(category.getId(), children);
}
List roots = new ArrayList<>();
for (NutMap node : nodeMap.values()) {
@@ -53,9 +56,16 @@ public class SpreadCategoryServiceImpl extends BaseServiceImpl i
if ("0".equals(parentId) || !nodeMap.containsKey(parentId)) {
roots.add(node);
} else {
- nodeMap.get(parentId).getList("children", NutMap.class).add(node);
+ // 直接向节点持有的原始集合追加,避免 NutMap 类型转换后子节点没有写回。
+ childrenMap.get(parentId).add(node);
}
}
+ // 叶子栏目不返回空 children,避免级联选择器误判为仍有下一级。
+ nodeMap.forEach((id, node) -> {
+ if (childrenMap.get(id).isEmpty()) {
+ node.remove("children");
+ }
+ });
return roots;
}
@@ -125,11 +135,9 @@ public class SpreadCategoryServiceImpl extends BaseServiceImpl i
if (category == null || StrUtil.isBlank(category.getName())) {
throw new RuntimeException("栏目名称不能为空");
}
- if (StrUtil.isBlank(category.getCode())) {
- throw new RuntimeException("栏目编码不能为空");
- }
category.setName(category.getName().trim());
- category.setCode(category.getCode().trim());
+ // 参考模块不要求用户维护栏目编码,空值统一保存为 NULL。
+ category.setCode(StrUtil.isBlank(category.getCode()) ? null : category.getCode().trim());
category.setParentId(StrUtil.blankToDefault(category.getParentId(), "0"));
if (category.getId() != null && category.getId().equals(category.getParentId())) {
throw new RuntimeException("上级栏目不能选择自身");
@@ -146,17 +154,12 @@ public class SpreadCategoryServiceImpl extends BaseServiceImpl i
Cnd nameCnd = Cnd.where("name", "=", category.getName())
.and("parentId", "=", category.getParentId())
.and("delFlag", "=", false);
- Cnd codeCnd = Cnd.where("code", "=", category.getCode()).and("delFlag", "=", false);
if (StrUtil.isNotBlank(excludeId)) {
nameCnd.and("id", "!=", excludeId);
- codeCnd.and("id", "!=", excludeId);
}
if (count(nameCnd) > 0) {
throw new RuntimeException("同级栏目名称已存在");
}
- if (count(codeCnd) > 0) {
- throw new RuntimeException("栏目编码已存在");
- }
}
private void applyDefaults(SpreadCategory category) {
diff --git a/src/main/resources/db/spread_create.sql b/src/main/resources/db/spread_create.sql
index 44912c29..4bfda893 100644
--- a/src/main/resources/db/spread_create.sql
+++ b/src/main/resources/db/spread_create.sql
@@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS `spread_category` (
`id` varchar(32) NOT NULL COMMENT 'ID',
`parentId` varchar(32) NOT NULL DEFAULT '0' COMMENT '父栏目ID,0表示顶级栏目',
`name` varchar(100) NOT NULL COMMENT '栏目名称',
- `code` varchar(50) NOT NULL COMMENT '栏目编码',
+ `code` varchar(50) DEFAULT NULL COMMENT '栏目编码(兼容预置数据,页面无需维护)',
`sortOrder` int NOT NULL DEFAULT 255 COMMENT '排序,数值越小越靠前',
`visible` tinyint NOT NULL DEFAULT 1 COMMENT '是否可见:0否,1是',
`slider` tinyint NOT NULL DEFAULT 0 COMMENT '是否参与轮播:0否,1是',
diff --git a/src/main/resources/views/platform/zhgh/spread/category/index.html b/src/main/resources/views/platform/zhgh/spread/category/index.html
index a1922d9a..8582a479 100644
--- a/src/main/resources/views/platform/zhgh/spread/category/index.html
+++ b/src/main/resources/views/platform/zhgh/spread/category/index.html
@@ -6,11 +6,12 @@ layout("/layouts/platform.html"){
新增栏目
+ 展开全部
+ 折叠全部
-
-
@@ -46,9 +47,6 @@ layout("/layouts/platform.html"){
-
-
-
@@ -88,10 +86,6 @@ layout("/layouts/platform.html"){
rules: {
parentId: [{required: true, message: "请选择上级栏目", trigger: "change"}],
name: [{required: true, message: "请输入栏目名称", trigger: "blur"}],
- code: [
- {required: true, message: "请输入栏目编码", trigger: "blur"},
- {pattern: /^[A-Za-z][A-Za-z0-9_-]*$/, message: "编码须以字母开头,仅支持字母、数字、下划线和短横线", trigger: "blur"}
- ],
sortOrder: [{required: true, message: "请输入排序", trigger: "change"}]
}
}
@@ -104,6 +98,8 @@ layout("/layouts/platform.html"){
if (resp.code === 0) {
this.tableData = resp.data || []
this.rootOptions = (resp.data || []).slice()
+ // 栏目数据为异步加载,赋值后显式展开,避免组件只展示顶级栏目。
+ this.$nextTick(this.expandAll)
} else {
this.$message.warning(resp.msg)
}
@@ -112,7 +108,7 @@ layout("/layouts/platform.html"){
// 构造新增栏目默认值。
openAdd() {
this.dialogTitle = "新增栏目"
- this.formData = {parentId: "0", name: "", code: "", sortOrder: 255, visible: 1, slider: 0}
+ this.formData = {parentId: "0", name: "", sortOrder: 255, visible: 1, slider: 0}
this.dialogVisible = true
this.$nextTick(() => this.$refs.categoryForm && this.$refs.categoryForm.clearValidate())
},
@@ -124,6 +120,28 @@ layout("/layouts/platform.html"){
this.dialogVisible = true
this.$nextTick(() => this.$refs.categoryForm && this.$refs.categoryForm.clearValidate())
},
+ // 展开当前栏目树中的全部父级栏目。
+ expandAll() {
+ this.setAllExpanded(true)
+ },
+ // 折叠当前栏目树中的全部父级栏目。
+ collapseAll() {
+ this.setAllExpanded(false)
+ },
+ // Element UI 异步树表格需要逐行设置展开状态。
+ setAllExpanded(expanded) {
+ const table = this.$refs.categoryTable
+ if (!table) return
+ const walk = rows => {
+ (rows || []).forEach(row => {
+ if (row.children && row.children.length) {
+ table.toggleRowExpansion(row, expanded)
+ walk(row.children)
+ }
+ })
+ }
+ walk(this.tableData)
+ },
// 校验后调用新增或编辑接口。
submit() {
this.$refs.categoryForm.validate(valid => {
From e00fdc0c78aba5ceeb24635c34148285efe7f167 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=9C=E5=AD=A6=E6=88=90?= <1274337408@qq.com>
Date: Fri, 31 Jul 2026 16:50:17 +0800
Subject: [PATCH 5/7] =?UTF-8?q?web=E7=AB=AF-=E6=96=B0=E5=A2=9E=E9=A3=8E?=
=?UTF-8?q?=E9=87=87=E5=A2=99=E6=A8=A1=E5=9D=97(=E5=B7=A5=E4=BC=9A?=
=?UTF-8?q?=E4=BF=A1=E6=81=AF=E4=BC=A0=E6=92=AD=E4=B8=8E=E6=96=87=E5=8C=96?=
=?UTF-8?q?=E5=B1=95=E7=A4=BA):=20=E8=A1=A5=E5=85=85=E6=89=80=E5=B1=9E?=
=?UTF-8?q?=E5=B7=A5=E4=BC=9A;=E7=BB=9F=E8=AE=A1,=E5=86=85=E5=AE=B9?=
=?UTF-8?q?=E5=B1=95=E7=A4=BA=E7=AD=89=E6=A0=B7=E5=BC=8F=E8=B0=83=E6=95=B4?=
=?UTF-8?q?;?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/SpreadCreationController.java | 7 +
.../app/zhgh/spread/models/SpreadContent.java | 10 +
.../spread/service/SpreadContentService.java | 6 +
.../impl/SpreadContentServiceImpl.java | 86 +++++-
.../impl/SpreadStatisticsServiceImpl.java | 4 +-
src/main/resources/db/spread_create.sql | 3 +
.../static/assets/platform/img/spread-bg.png | Bin 0 -> 470854 bytes
.../platform/zhgh/spread/category/index.html | 2 +-
.../platform/zhgh/spread/creation/index.html | 267 ++++++++++++++---
.../platform/zhgh/spread/display/index.html | 269 ++++++++++++++----
.../platform/zhgh/spread/manage/index.html | 31 +-
.../zhgh/spread/statistics/index.html | 159 ++++++++---
12 files changed, 693 insertions(+), 151 deletions(-)
create mode 100644 src/main/resources/static/assets/platform/img/spread-bg.png
diff --git a/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCreationController.java b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCreationController.java
index 181ca5e6..c8ed90a2 100644
--- a/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCreationController.java
+++ b/src/main/java/com/budwk/app/zhgh/spread/controller/SpreadCreationController.java
@@ -40,6 +40,13 @@ public class SpreadCreationController {
return Result.success(categoryService.categoryTree(true));
}
+ @At
+ @ApiOperation("当前登录人员可选择的工会")
+ @SaCheckPermission("spread.creation")
+ public Result unionOptions() {
+ return Result.success(contentService.selectableUnions());
+ }
+
@At
@ApiOperation("内容详情")
@SaCheckPermission("spread.creation")
diff --git a/src/main/java/com/budwk/app/zhgh/spread/models/SpreadContent.java b/src/main/java/com/budwk/app/zhgh/spread/models/SpreadContent.java
index 2d7eca2a..64385893 100644
--- a/src/main/java/com/budwk/app/zhgh/spread/models/SpreadContent.java
+++ b/src/main/java/com/budwk/app/zhgh/spread/models/SpreadContent.java
@@ -33,6 +33,16 @@ public class SpreadContent extends BaseModel {
@ColDefine(type = ColType.VARCHAR, width = 100)
private String categoryName;
+ @Column
+ @Comment("所属工会ID")
+ @ColDefine(type = ColType.VARCHAR, width = 32)
+ private String unionId;
+
+ @Column
+ @Comment("所属工会名称")
+ @ColDefine(type = ColType.VARCHAR, width = 100)
+ private String unionName;
+
@Column
@Comment("标题")
@ColDefine(type = ColType.VARCHAR, width = 300)
diff --git a/src/main/java/com/budwk/app/zhgh/spread/service/SpreadContentService.java b/src/main/java/com/budwk/app/zhgh/spread/service/SpreadContentService.java
index 07967870..120eb588 100644
--- a/src/main/java/com/budwk/app/zhgh/spread/service/SpreadContentService.java
+++ b/src/main/java/com/budwk/app/zhgh/spread/service/SpreadContentService.java
@@ -5,6 +5,7 @@ import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.spread.models.SpreadContent;
import com.budwk.app.zhgh.spread.param.SpreadContentQuery;
+import com.budwk.app.sys.models.Sys_union;
import org.nutz.lang.util.NutMap;
import java.util.List;
@@ -28,4 +29,9 @@ public interface SpreadContentService extends BaseService {
void addViewCount(String id);
List sliderContent();
+
+ /**
+ * 查询当前登录人员可选择的工会。
+ */
+ List selectableUnions();
}
diff --git a/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadContentServiceImpl.java b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadContentServiceImpl.java
index d58ce44a..9871199d 100644
--- a/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadContentServiceImpl.java
+++ b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadContentServiceImpl.java
@@ -2,9 +2,16 @@ package com.budwk.app.zhgh.spread.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.impl.BaseServiceImpl;
+import com.budwk.app.sys.models.Sys_union;
+import com.budwk.app.web.commons.auth.utils.AuthUtil;
+import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.spread.models.SpreadCategory;
import com.budwk.app.zhgh.spread.models.SpreadContent;
import com.budwk.app.zhgh.spread.param.SpreadContentQuery;
@@ -17,6 +24,7 @@ import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
@IocBean(args = {"refer:dao"})
@@ -149,7 +157,7 @@ public class SpreadContentServiceImpl extends BaseServiceImpl imp
@Override
public List sliderContent() {
Sql sql = Sqls.create("""
- SELECT c.id, c.title, c.summary, c.thumbUrl, c.sliderImages, c.categoryId, c.categoryName
+ SELECT c.id, c.title, c.summary, c.thumbUrl, c.sliderImages, c.categoryId, c.categoryName, c.unionId, c.unionName
FROM spread_content c
INNER JOIN spread_category child ON child.id = c.categoryId
LEFT JOIN spread_category parent ON parent.id = child.parentId
@@ -167,6 +175,77 @@ public class SpreadContentServiceImpl extends BaseServiceImpl imp
return sql.getList(NutMap.class);
}
+ @Override
+ public List selectableUnions() {
+ Cnd cnd = Cnd.NEW();
+ if (!canSelectAllUnions()) {
+ String currentUnionId = SecurityUtil.getUnionId();
+ if (StrUtil.isBlank(currentUnionId)) {
+ return Collections.emptyList();
+ }
+ cnd.and("id", "=", currentUnionId);
+ }
+ cnd.asc("unionCode");
+ return dao().query(Sys_union.class, cnd);
+ }
+
+ /**
+ * 校工会管理员和系统管理员可选择全部工会,其他人员只能选择登录人员自己的分工会。
+ */
+ private boolean canSelectAllUnions() {
+ return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name());
+ }
+
+ /**
+ * 校验工会是否存在且在当前登录人员的可选择范围内。
+ */
+ private Sys_union requireSelectableUnion(String unionId) {
+ if (StrUtil.isBlank(unionId)) {
+ throw new RuntimeException("请选择所属工会");
+ }
+ Sys_union union = dao().fetch(Sys_union.class, unionId);
+ if (union == null) {
+ throw new RuntimeException("所属工会不存在");
+ }
+ if (!canSelectAllUnions() && !unionId.equals(SecurityUtil.getUnionId())) {
+ throw new RuntimeException("无权选择该所属工会");
+ }
+ return union;
+ }
+
+ /**
+ * 校验并规范每张轮播图的标题,轮播图统一继承内容所属工会。
+ */
+ private void validateSliderImages(SpreadContent content) {
+ if (StrUtil.isBlank(content.getSliderImages())) {
+ return;
+ }
+ JSONArray sliderArray;
+ try {
+ sliderArray = JSONUtil.parseArray(content.getSliderImages());
+ } catch (RuntimeException e) {
+ throw new RuntimeException("轮播图片数据格式不正确");
+ }
+ JSONArray normalizedArray = new JSONArray();
+ for (int i = 0; i < sliderArray.size(); i++) {
+ Object rawItem = sliderArray.get(i);
+ if (!(rawItem instanceof JSONObject)) {
+ throw new RuntimeException("请完善第" + (i + 1) + "张轮播图标题");
+ }
+ JSONObject item = (JSONObject) rawItem;
+ if (StrUtil.isBlank(item.getStr("src"))) {
+ throw new RuntimeException("第" + (i + 1) + "张轮播图地址不能为空");
+ }
+ if (StrUtil.isBlank(item.getStr("title"))) {
+ throw new RuntimeException("请输入第" + (i + 1) + "张轮播图标题");
+ }
+ JSONObject normalizedItem = new JSONObject();
+ normalizedItem.set("src", item.getStr("src"));
+ normalizedItem.set("title", item.getStr("title").trim());
+ normalizedArray.add(normalizedItem);
+ }
+ content.setSliderImages(JSONUtil.toJsonStr(normalizedArray));
+ }
private Cnd buildCondition(SpreadContentQuery query) {
Cnd cnd = Cnd.where("c.delFlag", "=", false);
if (query == null) {
@@ -204,11 +283,16 @@ public class SpreadContentServiceImpl extends BaseServiceImpl imp
Cnd.where("parentId", "=", category.getId()).and("delFlag", "=", false)) > 0) {
throw new RuntimeException("内容只能发布到末级栏目");
}
+
+ Sys_union union = requireSelectableUnion(content.getUnionId());
if (StrUtil.isBlank(content.getTitle())) {
throw new RuntimeException("标题不能为空");
}
content.setTitle(content.getTitle().trim());
content.setCategoryName(category.getName());
+ content.setUnionName(union.getName());
+ validateSliderImages(content);
+
}
private void applyDefaults(SpreadContent content) {
diff --git a/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadStatisticsServiceImpl.java b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadStatisticsServiceImpl.java
index 9e8d2c84..6ed7e0fb 100644
--- a/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadStatisticsServiceImpl.java
+++ b/src/main/java/com/budwk/app/zhgh/spread/service/impl/SpreadStatisticsServiceImpl.java
@@ -44,9 +44,10 @@ public class SpreadStatisticsServiceImpl implements SpreadStatisticsService {
@Override
public List top10(SpreadStatisticsQuery query) {
Sql sql = Sqls.create("""
- SELECT c.id, c.title, c.categoryName, c.viewCount, c.isPublished, c.publishedAt
+ SELECT c.id, c.title, c.summary, c.categoryName, c.unionId, c.unionName, c.viewCount, c.isPublished, c.publishedAt
FROM spread_content c
WHERE c.delFlag = 0
+ AND c.isPublished = 1
$condition
ORDER BY c.viewCount DESC, c.updatedAt DESC
LIMIT 10
@@ -65,6 +66,7 @@ public class SpreadStatisticsServiceImpl implements SpreadStatisticsService {
IFNULL(SUM(c.viewCount), 0) AS viewCount
FROM spread_content c
WHERE c.delFlag = 0
+ AND c.isPublished = 1
$condition
GROUP BY c.categoryId, c.categoryName
ORDER BY viewCount DESC, contentCount DESC
diff --git a/src/main/resources/db/spread_create.sql b/src/main/resources/db/spread_create.sql
index 4bfda893..91a6b6f5 100644
--- a/src/main/resources/db/spread_create.sql
+++ b/src/main/resources/db/spread_create.sql
@@ -23,6 +23,8 @@ CREATE TABLE IF NOT EXISTS `spread_content` (
`id` varchar(32) NOT NULL COMMENT 'ID',
`categoryId` varchar(32) NOT NULL COMMENT '栏目ID',
`categoryName` varchar(100) NOT NULL COMMENT '栏目名称',
+ `unionId` varchar(32) DEFAULT NULL COMMENT '所属工会ID',
+ `unionName` varchar(100) DEFAULT NULL COMMENT '所属工会名称',
`title` varchar(300) NOT NULL COMMENT '标题',
`subTitle` varchar(255) DEFAULT NULL COMMENT '副标题',
`summary` varchar(1000) DEFAULT NULL COMMENT '摘要',
@@ -43,6 +45,7 @@ CREATE TABLE IF NOT EXISTS `spread_content` (
`delFlag` tinyint NOT NULL DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_spread_content_category` (`categoryId`),
+ KEY `idx_spread_content_union` (`unionId`),
KEY `idx_spread_content_publish` (`isPublished`, `publishedAt`),
KEY `idx_spread_content_top_sort` (`isTop`, `sortOrder`),
KEY `idx_spread_content_created` (`createdAt`)
diff --git a/src/main/resources/static/assets/platform/img/spread-bg.png b/src/main/resources/static/assets/platform/img/spread-bg.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac3ce630e33916147a5f97f1ce23b38db3f2ccca
GIT binary patch
literal 470854
zcmV()K;OTKP))A!-qGq^X8R1TX_!vI?xc~j%{>AOQ{QaNBeT!fG!#yJY
zjQHBy-G92T^Z&Ww{pY!O$KPJ|^Zlux&$7K=VZVQU#clas<=L9ojrad^^SueTT`I!a
zyDK&R{IIVZH2ZfS@}4-qBmamPU&s8U8S`Cp$(qq?Xz!8ly&9-t;)3((o2xs5pZI+j
z51Kw-j-F#sr{6#5I?}2JBSsVF*P90R8MyuZ=Iam_N7f&_Xo77}lI=uw6Yi-J!O`LJ
z@amnz|KMX10lN127r%}YVhE*Br&AYN{NCr0y%cG;_sGvvZz*_h$RYDW2W
zyF!ogy-MU2b6~BoUhPkgkCA`!BC4x9f|6EJ5=nf~UDwuVR3hl6?tmzR*15pF9Q~U=
zZ|nWX*IvsF`kFmb4KDNJ<@S0l_CjR>X-yy>XH`az+^(CutG
z?(6;MkJk!}2+C4+_wHy%bJW=YHdgv=0Cd7M|ub(f2W4)q;
zuI>{R&{3zeO7)2jq{?a#W8D7^&MXi5fU7dsd)G&C%{(x`B)1gCMI2;b(A}w59$1af
zi1|knX8_4|G;|G{u+VQZek={i2BAF1@4$zVI^sZz;w^g8NJEK8I)Mqn)*aw=jR<13hT6I#g6bUp_y7C9Iz}ou8x7AG
z(p!Pvp8L;z+Wzh3uT^1K8ty+`wPTm>kH5_R_@~F<6-wc>v`G(AUr9h*a8KgRWA*m>#AX4M*}k3-F8JxP6`@}ck1MFWAI`7{1uQwBZ-AJ9JPqixBtqik+67dIRIDP_=8|D|m
za7k|u@$TzH>g$X>Ly!?AHE%V$)?Ppu+!H~*e4jB$-BDW6{G}7^^yUuaYwxSh4qsa~
zoy27XBZXgA}Ed8l9;F~rtC4+)X2)%d3WBsZa(2a?~XV)aCMSSmbEL%kFMWWsR3_>
z35i|%u0JkY&{AvI!KUd7M0$&og=F5?@>&^;rt>Wwjq-FJi45L(t0G?2M`0Z(d~~GR
z##sUb{#2|}AnaA7SW*FKb_VLID#GFT1^22hHu)w%agT%rDW5z)rN^^
zBoE)WksDP}UkJHjno`e$El0zPwI02Z;c)u)vrFvB)bei*Oy}@EW5tP?R;kh8XdrKQ
z7)WdO8|D7~Z~wKU8AHcxPOZ9Z`0c}vPX4z1=c`JlKmGk@a;qWz-SZ#s$o^{^0wb01
zg2MtJgVH%ZMH0)XUCWWI<9^7c#!lveU1Uv~!~^UiMt2{#2hFA;5F|6vWDOD>ak&ee
z8-XDmbyl5OT|}BcMRwmqptE2J}Pn(vd^2PT+cCOmcu^V
zDJH}hf|oTuf`9-b2spwO*^l%Y+YP4x68&Kd&7Ur8K`{TpGot6wa>`a@vnd
zU}E_vCrY1RFBi$C`W3)O*}`U6h9s_q^ZVD=54YFK3l!MH)wLA#=T?T^8AqEK$|YDxQaQ+=-Q+xK$I~H^23Z4f+ncPYKiv5
zLUf4Ol5M1F?Q+CuNrO~>QYKExVjPn3WK@W$@3wA)uyRvQo;o$JvA7P}YOwN3jkh!2)
zZ&*E%x{