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 @@ + +
+ + + + 新增栏目 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 显示 + 隐藏 + + + + + 开启 + 关闭 + +
子栏目开启轮播后,其上级栏目会自动开启。
+
+
+ + 取消 + 确定 + +
+
+ + + 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}}
+
+
+
+
榜样引领 · 风采绽放
+
+
+ +
+ + +
+ + +
+
+
+ + + 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 @@ + +
+ + + + + + + + + + + + + + + + + + +
+ 搜索 + 重置 +
+
+
+ + + + 发布内容 + + + + + + + + + + + + + + + + + + + + + +
+
+ + + 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
+
+
+ +
栏目浏览量占比
+
+
+
+ + +
栏目数据明细
+ + + + + + +
+
+
+ + +