From 0f9a58b085f1e4e7ac7ff31e67a65c43dbec2fdd Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 15 Oct 2025 22:30:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E4=B8=9A=E5=8A=A1=E6=A8=A1=E5=9D=97heritage-?= =?UTF-8?q?admin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增用户管理:用户列表、详情、禁用启用等功能 - 新增新闻管理:新闻的增删改查、发布撤回等功能 - 新增活动管理:活动的增删改查、发布管理、报名管理等功能 - 新增非遗项目管理:项目的增删改查、审核上架等功能 - 新增传承人管理:传承人的增删改查、认证管理等功能 - 新增评论管理:评论审核、删除等功能 - 新增数据统计:平台数据统计、排行榜、趋势分析等功能 - 所有接口均配置Sa-Token权限验证和Swagger接口文档 --- heritage-modules/heritage-admin/pom.xml | 56 +++ .../comment/HrtCommentAdminController.java | 111 +++++ .../event/HrtEventAdminController.java | 124 ++++++ .../heritage/HrtHeritageAdminController.java | 124 ++++++ .../HrtInheritorAdminController.java | 124 ++++++ .../news/HrtNewsAdminController.java | 124 ++++++ .../statistics/HrtStatisticsController.java | 90 ++++ .../user/HrtUserAdminController.java | 124 ++++++ .../comment/HrtCommentAdminService.java | 59 +++ .../comment/HrtCommentAdminServiceImpl.java | 293 +++++++++++++ .../service/event/HrtEventAdminService.java | 65 +++ .../event/HrtEventAdminServiceImpl.java | 307 ++++++++++++++ .../heritage/HrtHeritageAdminService.java | 65 +++ .../heritage/HrtHeritageAdminServiceImpl.java | 327 +++++++++++++++ .../inheritor/HrtInheritorAdminService.java | 65 +++ .../HrtInheritorAdminServiceImpl.java | 323 ++++++++++++++ .../service/news/HrtNewsAdminService.java | 65 +++ .../service/news/HrtNewsAdminServiceImpl.java | 334 +++++++++++++++ .../statistics/HrtStatisticsService.java | 50 +++ .../statistics/HrtStatisticsServiceImpl.java | 393 ++++++++++++++++++ .../service/user/HrtUserAdminService.java | 65 +++ .../service/user/HrtUserAdminServiceImpl.java | 351 ++++++++++++++++ 22 files changed, 3639 insertions(+) create mode 100644 heritage-modules/heritage-admin/pom.xml create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/comment/HrtCommentAdminController.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/event/HrtEventAdminController.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/heritage/HrtHeritageAdminController.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/inheritor/HrtInheritorAdminController.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/news/HrtNewsAdminController.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/statistics/HrtStatisticsController.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/user/HrtUserAdminController.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/comment/HrtCommentAdminService.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/comment/HrtCommentAdminServiceImpl.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/event/HrtEventAdminService.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/event/HrtEventAdminServiceImpl.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/heritage/HrtHeritageAdminService.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/heritage/HrtHeritageAdminServiceImpl.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/inheritor/HrtInheritorAdminService.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/inheritor/HrtInheritorAdminServiceImpl.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/news/HrtNewsAdminService.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/news/HrtNewsAdminServiceImpl.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/statistics/HrtStatisticsService.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/statistics/HrtStatisticsServiceImpl.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/user/HrtUserAdminService.java create mode 100644 heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/user/HrtUserAdminServiceImpl.java diff --git a/heritage-modules/heritage-admin/pom.xml b/heritage-modules/heritage-admin/pom.xml new file mode 100644 index 0000000..decae66 --- /dev/null +++ b/heritage-modules/heritage-admin/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + org.leocoder.heritage + heritage-backend + ${revision} + ../../pom.xml + + + + heritage-admin + heritage-admin + 后台管理模块 + + + + + org.leocoder.heritage + heritage-common + ${revision} + + + + org.leocoder.heritage + heritage-resultex + ${revision} + + + + org.leocoder.heritage + heritage-sa-token + ${revision} + + + + org.leocoder.heritage + heritage-oper-logs + ${revision} + + + + org.leocoder.heritage + heritage-oss + ${revision} + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + + diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/comment/HrtCommentAdminController.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/comment/HrtCommentAdminController.java new file mode 100644 index 0000000..4f2bde6 --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/comment/HrtCommentAdminController.java @@ -0,0 +1,111 @@ +package org.leocoder.heritage.admin.controller.comment; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.admin.service.comment.HrtCommentAdminService; +import org.leocoder.heritage.domain.model.bo.admin.HrtCommentAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtCommentAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtCommentAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtCommentAdminVo; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author Leocoder + * @description [后台评论管理控制器] + */ +@Tag(name = "后台评论管理", description = "评论审核、删除、查询等管理功能") +@Validated +@RequestMapping("/coder/admin/comment") +@RequiredArgsConstructor +@RestController +public class HrtCommentAdminController { + + private final HrtCommentAdminService commentAdminService; + + /** + * @description [分页查询评论列表] + * @author Leocoder + */ + @Operation(summary = "分页查询评论列表", description = "根据查询条件分页获取评论列表,支持多条件筛选") + @SaCheckPermission("heritage:comment:list") + @GetMapping("/listPage") + public IPage listPage(@Validated HrtCommentAdminQueryBo bo) { + return commentAdminService.listPage(bo); + } + + /** + * @description [查看评论详情] + * @author Leocoder + */ + @Operation(summary = "查看评论详情", description = "根据评论ID获取评论的完整详细信息") + @SaCheckPermission("heritage:comment:detail") + @GetMapping("/detail/{id}") + public HrtCommentAdminDetailVo detail(@Parameter(description = "评论ID") @PathVariable Long id) { + return commentAdminService.getDetail(id); + } + + /** + * @description [审核评论] + * @author Leocoder + */ + @Operation(summary = "审核评论", description = "审核评论(批准或拒绝)") + @SaCheckPermission("heritage:comment:audit") + @PutMapping("/audit") + public Boolean audit(@Validated @RequestBody HrtCommentAdminBo bo) { + return commentAdminService.auditComment(bo); + } + + /** + * @description [批量审核评论] + * @author Leocoder + */ + @Operation(summary = "批量审核评论", description = "批量审核多个评论") + @SaCheckPermission("heritage:comment:audit") + @PutMapping("/batchAudit") + public Boolean batchAudit( + @Parameter(description = "评论ID列表") @RequestBody List ids, + @Parameter(description = "审核状态:0-待审核,1-已通过,2-已拒绝") @RequestParam Integer status) { + return commentAdminService.batchAudit(ids, status); + } + + /** + * @description [删除评论] + * @author Leocoder + */ + @Operation(summary = "删除评论", description = "删除指定评论,同时会删除该评论下的所有回复") + @SaCheckPermission("heritage:comment:delete") + @DeleteMapping("/delete/{id}") + public Boolean delete(@Parameter(description = "评论ID") @PathVariable Long id) { + return commentAdminService.deleteComment(id); + } + + /** + * @description [批量删除评论] + * @author Leocoder + */ + @Operation(summary = "批量删除评论", description = "批量删除多个评论") + @SaCheckPermission("heritage:comment:delete") + @DeleteMapping("/batchDelete") + public Boolean batchDelete(@Parameter(description = "评论ID列表") @RequestBody List ids) { + return commentAdminService.batchDelete(ids); + } + + /** + * @description [获取待审核评论数量] + * @author Leocoder + */ + @Operation(summary = "获取待审核评论数量", description = "获取待审核评论的数量") + @SaCheckPermission("heritage:comment:list") + @GetMapping("/pendingCount") + public Long getPendingCount() { + return commentAdminService.getPendingCount(); + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/event/HrtEventAdminController.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/event/HrtEventAdminController.java new file mode 100644 index 0000000..be5821c --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/event/HrtEventAdminController.java @@ -0,0 +1,124 @@ +package org.leocoder.heritage.admin.controller.event; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.admin.service.event.HrtEventAdminService; +import org.leocoder.heritage.domain.model.bo.admin.HrtEventAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtEventAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtEventAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtEventAdminVo; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author Leocoder + * @description [活动管理控制器] + */ +@Tag(name = "活动管理", description = "非遗活动的增删改查、状态管理和发布控制") +@Validated +@RequestMapping("/coder/admin/event") +@RequiredArgsConstructor +@RestController +public class HrtEventAdminController { + + private final HrtEventAdminService eventAdminService; + + /** + * @description [分页查询活动列表] + * @author Leocoder + */ + @Operation(summary = "分页查询活动列表", description = "根据查询条件分页获取活动列表,支持标题、地点、状态等多条件筛选") + @SaCheckPermission("heritage:event:list") + @GetMapping("/listPage") + public IPage listPage(@Validated HrtEventAdminQueryBo bo) { + return eventAdminService.listPage(bo); + } + + /** + * @description [查看活动详情] + * @author Leocoder + */ + @Operation(summary = "查看活动详情", description = "根据活动ID获取活动的完整详细信息") + @SaCheckPermission("heritage:event:detail") + @GetMapping("/detail/{id}") + public HrtEventAdminDetailVo detail(@Parameter(description = "活动ID") @PathVariable Long id) { + return eventAdminService.getDetail(id); + } + + /** + * @description [新增活动] + * @author Leocoder + */ + @Operation(summary = "新增活动", description = "创建新的非遗活动,包括活动信息、时间安排和报名设置") + @SaCheckPermission("heritage:event:add") + @PostMapping("/add") + public Long add(@Validated @RequestBody HrtEventAdminBo bo) { + return eventAdminService.addEvent(bo); + } + + /** + * @description [编辑活动] + * @author Leocoder + */ + @Operation(summary = "编辑活动", description = "修改已存在活动的信息") + @SaCheckPermission("heritage:event:edit") + @PutMapping("/edit") + public Boolean edit(@Validated @RequestBody HrtEventAdminBo bo) { + return eventAdminService.editEvent(bo); + } + + /** + * @description [删除活动] + * @author Leocoder + */ + @Operation(summary = "删除活动", description = "根据ID删除指定活动(已有报名的活动不可删除)") + @SaCheckPermission("heritage:event:delete") + @DeleteMapping("/delete/{id}") + public Boolean delete(@Parameter(description = "活动ID") @PathVariable Long id) { + return eventAdminService.deleteEvent(id); + } + + /** + * @description [批量删除活动] + * @author Leocoder + */ + @Operation(summary = "批量删除活动", description = "批量删除多个活动(已有报名的活动不可删除)") + @SaCheckPermission("heritage:event:delete") + @DeleteMapping("/batchDelete") + public Boolean batchDelete(@Parameter(description = "活动ID列表") @RequestBody List ids) { + return eventAdminService.batchDelete(ids); + } + + /** + * @description [修改发布状态] + * @author Leocoder + */ + @Operation(summary = "修改发布状态", description = "修改活动的发布状态(0-草稿,1-已发布)") + @SaCheckPermission("heritage:event:status") + @PutMapping("/changePublishStatus") + public Boolean changePublishStatus( + @Parameter(description = "活动ID") @RequestParam Long id, + @Parameter(description = "发布状态:0-草稿,1-已发布") @RequestParam Integer publishStatus) { + return eventAdminService.changePublishStatus(id, publishStatus); + } + + /** + * @description [修改活动状态] + * @author Leocoder + */ + @Operation(summary = "修改活动状态", description = "修改活动的状态(upcoming-即将开始、ongoing-进行中、finished-已结束、cancelled-已取消)") + @SaCheckPermission("heritage:event:status") + @PutMapping("/changeEventStatus") + public Boolean changeEventStatus( + @Parameter(description = "活动ID") @RequestParam Long id, + @Parameter(description = "活动状态:upcoming、ongoing、finished、cancelled") @RequestParam String status) { + return eventAdminService.changeEventStatus(id, status); + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/heritage/HrtHeritageAdminController.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/heritage/HrtHeritageAdminController.java new file mode 100644 index 0000000..e37569f --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/heritage/HrtHeritageAdminController.java @@ -0,0 +1,124 @@ +package org.leocoder.heritage.admin.controller.heritage; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.admin.service.heritage.HrtHeritageAdminService; +import org.leocoder.heritage.domain.model.bo.admin.HrtHeritageAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtHeritageAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtHeritageAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtHeritageAdminVo; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author Leocoder + * @description [非遗项目管理控制器] + */ +@Tag(name = "非遗项目管理", description = "后台管理非遗项目的增删改查、审核、发布等功能") +@Validated +@RequestMapping("/coder/admin/heritage") +@RequiredArgsConstructor +@RestController +public class HrtHeritageAdminController { + + private final HrtHeritageAdminService hrtHeritageAdminService; + + /** + * @description [分页查询非遗项目列表] + * @author Leocoder + */ + @Operation(summary = "分页查询非遗项目列表", description = "支持多条件筛选和关键词搜索,包含草稿和已发布的项目") + @SaCheckPermission("heritage:items:list") + @GetMapping("/listPage") + public IPage listPage(@Validated HrtHeritageAdminQueryBo bo) { + return hrtHeritageAdminService.listPage(bo); + } + + /** + * @description [查看非遗项目详情] + * @author Leocoder + */ + @Operation(summary = "查看非遗项目详情", description = "根据ID查看非遗项目的完整详细信息") + @SaCheckPermission("heritage:items:detail") + @GetMapping("/detail/{id}") + public HrtHeritageAdminDetailVo detail(@Parameter(description = "非遗项目ID") @PathVariable Long id) { + return hrtHeritageAdminService.getDetail(id); + } + + /** + * @description [新增非遗项目] + * @author Leocoder + */ + @Operation(summary = "新增非遗项目", description = "新增一个非遗项目,返回新增项目的ID") + @SaCheckPermission("heritage:items:add") + @PostMapping("/add") + public Long add(@Validated @RequestBody HrtHeritageAdminBo bo) { + return hrtHeritageAdminService.addHeritage(bo); + } + + /** + * @description [编辑非遗项目] + * @author Leocoder + */ + @Operation(summary = "编辑非遗项目", description = "根据ID编辑非遗项目信息") + @SaCheckPermission("heritage:items:edit") + @PutMapping("/edit") + public Boolean edit(@Validated @RequestBody HrtHeritageAdminBo bo) { + return hrtHeritageAdminService.editHeritage(bo); + } + + /** + * @description [删除非遗项目] + * @author Leocoder + */ + @Operation(summary = "删除非遗项目", description = "根据ID逻辑删除非遗项目") + @SaCheckPermission("heritage:items:delete") + @DeleteMapping("/delete/{id}") + public Boolean delete(@Parameter(description = "非遗项目ID") @PathVariable Long id) { + return hrtHeritageAdminService.deleteHeritage(id); + } + + /** + * @description [批量删除非遗项目] + * @author Leocoder + */ + @Operation(summary = "批量删除非遗项目", description = "根据ID列表批量逻辑删除非遗项目") + @SaCheckPermission("heritage:items:delete") + @DeleteMapping("/batchDelete") + public Boolean batchDelete(@Parameter(description = "非遗项目ID列表") @RequestBody List ids) { + return hrtHeritageAdminService.batchDelete(ids); + } + + /** + * @description [修改发布状态] + * @author Leocoder + */ + @Operation(summary = "修改发布状态", description = "修改非遗项目的发布状态(0-草稿,1-已发布)") + @SaCheckPermission("heritage:items:status") + @PutMapping("/changePublishStatus") + public Boolean changePublishStatus( + @Parameter(description = "非遗项目ID") @RequestParam Long id, + @Parameter(description = "发布状态:0-草稿,1-已发布") @RequestParam Integer publishStatus) { + return hrtHeritageAdminService.changePublishStatus(id, publishStatus); + } + + /** + * @description [设置精选状态] + * @author Leocoder + */ + @Operation(summary = "设置精选状态", description = "设置非遗项目是否为精选项目(0-否,1-是)") + @SaCheckPermission("heritage:items:status") + @PutMapping("/setFeatured") + public Boolean setFeatured( + @Parameter(description = "非遗项目ID") @RequestParam Long id, + @Parameter(description = "精选状态:0-否,1-是") @RequestParam Integer isFeatured) { + return hrtHeritageAdminService.setFeatured(id, isFeatured); + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/inheritor/HrtInheritorAdminController.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/inheritor/HrtInheritorAdminController.java new file mode 100644 index 0000000..4a1f1fc --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/inheritor/HrtInheritorAdminController.java @@ -0,0 +1,124 @@ +package org.leocoder.heritage.admin.controller.inheritor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.admin.service.inheritor.HrtInheritorAdminService; +import org.leocoder.heritage.domain.model.bo.admin.HrtInheritorAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtInheritorAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtInheritorAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtInheritorAdminVo; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author Leocoder + * @description [传承人管理控制器] + */ +@Tag(name = "传承人管理", description = "后台管理传承人的增删改查、审核、发布等功能") +@Validated +@RequestMapping("/coder/admin/inheritor") +@RequiredArgsConstructor +@RestController +public class HrtInheritorAdminController { + + private final HrtInheritorAdminService hrtInheritorAdminService; + + /** + * @description [分页查询传承人列表] + * @author Leocoder + */ + @Operation(summary = "分页查询传承人列表", description = "支持多条件筛选和关键词搜索,包含草稿和已发布的传承人") + @SaCheckPermission("heritage:inheritors:list") + @GetMapping("/listPage") + public IPage listPage(@Validated HrtInheritorAdminQueryBo bo) { + return hrtInheritorAdminService.listPage(bo); + } + + /** + * @description [查看传承人详情] + * @author Leocoder + */ + @Operation(summary = "查看传承人详情", description = "根据ID查看传承人的完整详细信息") + @SaCheckPermission("heritage:inheritors:detail") + @GetMapping("/detail/{id}") + public HrtInheritorAdminDetailVo detail(@Parameter(description = "传承人ID") @PathVariable Long id) { + return hrtInheritorAdminService.getDetail(id); + } + + /** + * @description [新增传承人] + * @author Leocoder + */ + @Operation(summary = "新增传承人", description = "新增一个传承人,返回新增传承人的ID") + @SaCheckPermission("heritage:inheritors:add") + @PostMapping("/add") + public Long add(@Validated @RequestBody HrtInheritorAdminBo bo) { + return hrtInheritorAdminService.addInheritor(bo); + } + + /** + * @description [编辑传承人] + * @author Leocoder + */ + @Operation(summary = "编辑传承人", description = "根据ID编辑传承人信息") + @SaCheckPermission("heritage:inheritors:edit") + @PutMapping("/edit") + public Boolean edit(@Validated @RequestBody HrtInheritorAdminBo bo) { + return hrtInheritorAdminService.editInheritor(bo); + } + + /** + * @description [删除传承人] + * @author Leocoder + */ + @Operation(summary = "删除传承人", description = "根据ID逻辑删除传承人") + @SaCheckPermission("heritage:inheritors:delete") + @DeleteMapping("/delete/{id}") + public Boolean delete(@Parameter(description = "传承人ID") @PathVariable Long id) { + return hrtInheritorAdminService.deleteInheritor(id); + } + + /** + * @description [批量删除传承人] + * @author Leocoder + */ + @Operation(summary = "批量删除传承人", description = "根据ID列表批量逻辑删除传承人") + @SaCheckPermission("heritage:inheritors:delete") + @DeleteMapping("/batchDelete") + public Boolean batchDelete(@Parameter(description = "传承人ID列表") @RequestBody List ids) { + return hrtInheritorAdminService.batchDelete(ids); + } + + /** + * @description [修改发布状态] + * @author Leocoder + */ + @Operation(summary = "修改发布状态", description = "修改传承人的发布状态(0-草稿,1-已发布)") + @SaCheckPermission("heritage:inheritors:status") + @PutMapping("/changePublishStatus") + public Boolean changePublishStatus( + @Parameter(description = "传承人ID") @RequestParam Long id, + @Parameter(description = "发布状态:0-草稿,1-已发布") @RequestParam Integer publishStatus) { + return hrtInheritorAdminService.changePublishStatus(id, publishStatus); + } + + /** + * @description [设置精选状态] + * @author Leocoder + */ + @Operation(summary = "设置精选状态", description = "设置传承人是否为精选传承人(0-否,1-是)") + @SaCheckPermission("heritage:inheritors:status") + @PutMapping("/setFeatured") + public Boolean setFeatured( + @Parameter(description = "传承人ID") @RequestParam Long id, + @Parameter(description = "精选状态:0-否,1-是") @RequestParam Integer isFeatured) { + return hrtInheritorAdminService.setFeatured(id, isFeatured); + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/news/HrtNewsAdminController.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/news/HrtNewsAdminController.java new file mode 100644 index 0000000..7eb845a --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/news/HrtNewsAdminController.java @@ -0,0 +1,124 @@ +package org.leocoder.heritage.admin.controller.news; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.admin.service.news.HrtNewsAdminService; +import org.leocoder.heritage.domain.model.bo.admin.HrtNewsAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtNewsAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtNewsAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtNewsAdminVo; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author Leocoder + * @description [新闻资讯管理控制器] + */ +@Tag(name = "新闻资讯管理", description = "后台管理新闻资讯的增删改查、审核、发布等功能") +@Validated +@RequestMapping("/coder/admin/news") +@RequiredArgsConstructor +@RestController +public class HrtNewsAdminController { + + private final HrtNewsAdminService hrtNewsAdminService; + + /** + * @description [分页查询新闻资讯列表] + * @author Leocoder + */ + @Operation(summary = "分页查询新闻资讯列表", description = "支持多条件筛选和关键词搜索,包含草稿和已发布的新闻") + @SaCheckPermission("heritage:news:list") + @GetMapping("/listPage") + public IPage listPage(@Validated HrtNewsAdminQueryBo bo) { + return hrtNewsAdminService.listPage(bo); + } + + /** + * @description [查看新闻资讯详情] + * @author Leocoder + */ + @Operation(summary = "查看新闻资讯详情", description = "根据ID查看新闻资讯的完整详细信息") + @SaCheckPermission("heritage:news:detail") + @GetMapping("/detail/{id}") + public HrtNewsAdminDetailVo detail(@Parameter(description = "新闻资讯ID") @PathVariable Long id) { + return hrtNewsAdminService.getDetail(id); + } + + /** + * @description [新增新闻资讯] + * @author Leocoder + */ + @Operation(summary = "新增新闻资讯", description = "新增一条新闻资讯,返回新增新闻的ID") + @SaCheckPermission("heritage:news:add") + @PostMapping("/add") + public Long add(@Validated @RequestBody HrtNewsAdminBo bo) { + return hrtNewsAdminService.addNews(bo); + } + + /** + * @description [编辑新闻资讯] + * @author Leocoder + */ + @Operation(summary = "编辑新闻资讯", description = "根据ID编辑新闻资讯信息") + @SaCheckPermission("heritage:news:edit") + @PutMapping("/edit") + public Boolean edit(@Validated @RequestBody HrtNewsAdminBo bo) { + return hrtNewsAdminService.editNews(bo); + } + + /** + * @description [删除新闻资讯] + * @author Leocoder + */ + @Operation(summary = "删除新闻资讯", description = "根据ID逻辑删除新闻资讯") + @SaCheckPermission("heritage:news:delete") + @DeleteMapping("/delete/{id}") + public Boolean delete(@Parameter(description = "新闻资讯ID") @PathVariable Long id) { + return hrtNewsAdminService.deleteNews(id); + } + + /** + * @description [批量删除新闻资讯] + * @author Leocoder + */ + @Operation(summary = "批量删除新闻资讯", description = "根据ID列表批量逻辑删除新闻资讯") + @SaCheckPermission("heritage:news:delete") + @DeleteMapping("/batchDelete") + public Boolean batchDelete(@Parameter(description = "新闻资讯ID列表") @RequestBody List ids) { + return hrtNewsAdminService.batchDelete(ids); + } + + /** + * @description [修改发布状态] + * @author Leocoder + */ + @Operation(summary = "修改发布状态", description = "修改新闻资讯的发布状态(0-草稿,1-已发布)") + @SaCheckPermission("heritage:news:status") + @PutMapping("/changePublishStatus") + public Boolean changePublishStatus( + @Parameter(description = "新闻资讯ID") @RequestParam Long id, + @Parameter(description = "发布状态:0-草稿,1-已发布") @RequestParam Integer publishStatus) { + return hrtNewsAdminService.changePublishStatus(id, publishStatus); + } + + /** + * @description [设置置顶状态] + * @author Leocoder + */ + @Operation(summary = "设置置顶状态", description = "设置新闻资讯是否置顶显示(0-否,1-是)") + @SaCheckPermission("heritage:news:status") + @PutMapping("/setTop") + public Boolean setTop( + @Parameter(description = "新闻资讯ID") @RequestParam Long id, + @Parameter(description = "置顶状态:0-否,1-是") @RequestParam Integer isTop) { + return hrtNewsAdminService.setTop(id, isTop); + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/statistics/HrtStatisticsController.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/statistics/HrtStatisticsController.java new file mode 100644 index 0000000..0b1043f --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/statistics/HrtStatisticsController.java @@ -0,0 +1,90 @@ +package org.leocoder.heritage.admin.controller.statistics; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.admin.service.statistics.HrtStatisticsService; +import org.leocoder.heritage.domain.model.vo.admin.HrtRankingVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtStatisticsVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtTrendVo; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author Leocoder + * @description [统计分析控制器] + */ +@Tag(name = "统计分析管理", description = "平台数据统计、趋势分析和排行榜功能") +@Validated +@RequestMapping("/coder/admin/statistics") +@RequiredArgsConstructor +@RestController +public class HrtStatisticsController { + + private final HrtStatisticsService statisticsService; + + /** + * @description [获取核心统计数据] + * @author Leocoder + */ + @Operation(summary = "获取核心统计数据", description = "获取平台核心数据统计,包括非遗项目、用户、评论、点赞、收藏等数据") + @SaCheckPermission("heritage:statistics:view") + @GetMapping("/core") + public HrtStatisticsVo getStatistics() { + return statisticsService.getStatistics(); + } + + /** + * @description [获取用户增长趋势] + * @author Leocoder + */ + @Operation(summary = "获取用户增长趋势", description = "获取最近7天或30天的用户注册增长趋势数据") + @SaCheckPermission("heritage:statistics:view") + @GetMapping("/userTrend") + public List getUserTrend( + @Parameter(description = "天数:7或30") @RequestParam(defaultValue = "7") Integer days) { + return statisticsService.getUserTrend(days); + } + + /** + * @description [获取内容发布趋势] + * @author Leocoder + */ + @Operation(summary = "获取内容发布趋势", description = "获取最近7天或30天的内容发布趋势数据(非遗项目+新闻+活动)") + @SaCheckPermission("heritage:statistics:view") + @GetMapping("/contentTrend") + public List getContentTrend( + @Parameter(description = "天数:7或30") @RequestParam(defaultValue = "7") Integer days) { + return statisticsService.getContentTrend(days); + } + + /** + * @description [获取热门非遗项目排行榜] + * @author Leocoder + */ + @Operation(summary = "获取热门非遗项目排行榜", description = "获取热门非遗项目TOP榜单,支持按浏览量或收藏数排序") + @SaCheckPermission("heritage:statistics:view") + @GetMapping("/heritageRanking") + public List getHeritageRanking( + @Parameter(description = "排序类型:view-浏览量,favorite-收藏数") @RequestParam(defaultValue = "view") String type, + @Parameter(description = "返回数量") @RequestParam(defaultValue = "10") Integer limit) { + return statisticsService.getHeritageRanking(type, limit); + } + + /** + * @description [获取活跃用户排行榜] + * @author Leocoder + */ + @Operation(summary = "获取活跃用户排行榜", description = "获取活跃用户TOP榜单,根据评论、点赞、收藏等行为综合计算") + @SaCheckPermission("heritage:statistics:view") + @GetMapping("/activeUserRanking") + public List getActiveUserRanking( + @Parameter(description = "返回数量") @RequestParam(defaultValue = "10") Integer limit) { + return statisticsService.getActiveUserRanking(limit); + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/user/HrtUserAdminController.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/user/HrtUserAdminController.java new file mode 100644 index 0000000..ddc4ade --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/controller/user/HrtUserAdminController.java @@ -0,0 +1,124 @@ +package org.leocoder.heritage.admin.controller.user; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.admin.service.user.HrtUserAdminService; +import org.leocoder.heritage.domain.model.bo.admin.HrtUserAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtUserAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtUserAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtUserAdminVo; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author Leocoder + * @description [前台用户管理控制器] + */ +@Tag(name = "前台用户管理", description = "前台用户的增删改查、状态管理和密码重置") +@Validated +@RequestMapping("/coder/admin/user") +@RequiredArgsConstructor +@RestController +public class HrtUserAdminController { + + private final HrtUserAdminService userAdminService; + + /** + * @description [分页查询用户列表] + * @author Leocoder + */ + @Operation(summary = "分页查询用户列表", description = "根据查询条件分页获取前台用户列表,支持用户名、昵称、手机号等多条件筛选") + @SaCheckPermission("heritage:user:list") + @GetMapping("/listPage") + public IPage listPage(@Validated HrtUserAdminQueryBo bo) { + return userAdminService.listPage(bo); + } + + /** + * @description [查看用户详情] + * @author Leocoder + */ + @Operation(summary = "查看用户详情", description = "根据用户ID获取用户的完整详细信息") + @SaCheckPermission("heritage:user:detail") + @GetMapping("/detail/{id}") + public HrtUserAdminDetailVo detail(@Parameter(description = "用户ID") @PathVariable Long id) { + return userAdminService.getDetail(id); + } + + /** + * @description [新增用户] + * @author Leocoder + */ + @Operation(summary = "新增用户", description = "创建新的前台用户,包括用户基本信息和初始密码设置") + @SaCheckPermission("heritage:user:add") + @PostMapping("/add") + public Long add(@Validated @RequestBody HrtUserAdminBo bo) { + return userAdminService.addUser(bo); + } + + /** + * @description [编辑用户] + * @author Leocoder + */ + @Operation(summary = "编辑用户", description = "修改已存在用户的信息,可选择性更新密码") + @SaCheckPermission("heritage:user:edit") + @PutMapping("/edit") + public Boolean edit(@Validated @RequestBody HrtUserAdminBo bo) { + return userAdminService.editUser(bo); + } + + /** + * @description [删除用户] + * @author Leocoder + */ + @Operation(summary = "删除用户", description = "根据ID删除指定用户") + @SaCheckPermission("heritage:user:delete") + @DeleteMapping("/delete/{id}") + public Boolean delete(@Parameter(description = "用户ID") @PathVariable Long id) { + return userAdminService.deleteUser(id); + } + + /** + * @description [批量删除用户] + * @author Leocoder + */ + @Operation(summary = "批量删除用户", description = "批量删除多个用户") + @SaCheckPermission("heritage:user:delete") + @DeleteMapping("/batchDelete") + public Boolean batchDelete(@Parameter(description = "用户ID列表") @RequestBody List ids) { + return userAdminService.batchDelete(ids); + } + + /** + * @description [修改用户状态] + * @author Leocoder + */ + @Operation(summary = "修改用户状态", description = "修改用户的启用/禁用状态(0-禁用,1-正常)") + @SaCheckPermission("heritage:user:status") + @PutMapping("/changeStatus") + public Boolean changeStatus( + @Parameter(description = "用户ID") @RequestParam Long id, + @Parameter(description = "用户状态:0-禁用,1-正常") @RequestParam Integer status) { + return userAdminService.changeStatus(id, status); + } + + /** + * @description [重置用户密码] + * @author Leocoder + */ + @Operation(summary = "重置用户密码", description = "管理员重置指定用户的登录密码") + @SaCheckPermission("heritage:user:edit") + @PutMapping("/resetPassword") + public Boolean resetPassword( + @Parameter(description = "用户ID") @RequestParam Long id, + @Parameter(description = "新密码") @RequestParam String newPassword) { + return userAdminService.resetPassword(id, newPassword); + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/comment/HrtCommentAdminService.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/comment/HrtCommentAdminService.java new file mode 100644 index 0000000..8e0e775 --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/comment/HrtCommentAdminService.java @@ -0,0 +1,59 @@ +package org.leocoder.heritage.admin.service.comment; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.leocoder.heritage.domain.model.bo.admin.HrtCommentAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtCommentAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtCommentAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtCommentAdminVo; + +import java.util.List; + +/** + * @author Leocoder + * @description [后台评论管理服务接口] + */ +public interface HrtCommentAdminService { + + /** + * @description [分页查询评论列表] + * @author Leocoder + */ + IPage listPage(HrtCommentAdminQueryBo bo); + + /** + * @description [查看评论详情] + * @author Leocoder + */ + HrtCommentAdminDetailVo getDetail(Long id); + + /** + * @description [审核评论] + * @author Leocoder + */ + Boolean auditComment(HrtCommentAdminBo bo); + + /** + * @description [批量审核评论] + * @author Leocoder + */ + Boolean batchAudit(List ids, Integer status); + + /** + * @description [删除评论] + * @author Leocoder + */ + Boolean deleteComment(Long id); + + /** + * @description [批量删除评论] + * @author Leocoder + */ + Boolean batchDelete(List ids); + + /** + * @description [获取待审核评论数量] + * @author Leocoder + */ + Long getPendingCount(); + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/comment/HrtCommentAdminServiceImpl.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/comment/HrtCommentAdminServiceImpl.java new file mode 100644 index 0000000..943565f --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/comment/HrtCommentAdminServiceImpl.java @@ -0,0 +1,293 @@ +package org.leocoder.heritage.admin.service.comment; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.common.exception.BusinessException; +import org.leocoder.heritage.domain.model.bo.admin.HrtCommentAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtCommentAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtCommentAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtCommentAdminVo; +import org.leocoder.heritage.domain.pojo.portal.*; +import org.leocoder.heritage.mybatisplus.mapper.portal.*; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Objects; + +/** + * @author Leocoder + * @description [后台评论管理服务实现类] + */ +@Service +@RequiredArgsConstructor +public class HrtCommentAdminServiceImpl implements HrtCommentAdminService { + + private final HrtCommentMapper commentMapper; + private final HrtUserMapper userMapper; + private final HrtHeritageMapper heritageMapper; + private final HrtInheritorMapper inheritorMapper; + private final HrtNewsMapper newsMapper; + private final HrtEventMapper eventMapper; + + /** + * @description [分页查询评论列表] + * @author Leocoder + */ + @Override + public IPage listPage(HrtCommentAdminQueryBo bo) { + Page page = new Page<>(bo.getPageNum(), bo.getPageSize()); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 用户ID查询 + wrapper.eq(Objects.nonNull(bo.getUserId()), HrtComment::getUserId, bo.getUserId()); + + // 目标类型查询 + wrapper.eq(StrUtil.isNotEmpty(bo.getTargetType()), HrtComment::getTargetType, bo.getTargetType()); + + // 目标ID查询 + wrapper.eq(Objects.nonNull(bo.getTargetId()), HrtComment::getTargetId, bo.getTargetId()); + + // 评论内容模糊查询 + wrapper.like(StrUtil.isNotEmpty(bo.getContent()), HrtComment::getContent, bo.getContent()); + + // 状态查询 + wrapper.eq(Objects.nonNull(bo.getStatus()), HrtComment::getStatus, bo.getStatus()); + + // 时间范围查询 + if (StrUtil.isNotEmpty(bo.getStartTime())) { + LocalDateTime startDateTime = LocalDate.parse(bo.getStartTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd")) + .atStartOfDay(); + wrapper.ge(HrtComment::getCreateTime, startDateTime); + } + if (StrUtil.isNotEmpty(bo.getEndTime())) { + LocalDateTime endDateTime = LocalDate.parse(bo.getEndTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd")) + .atTime(LocalTime.MAX); + wrapper.le(HrtComment::getCreateTime, endDateTime); + } + + // 按创建时间倒序排序 + wrapper.orderByDesc(HrtComment::getCreateTime); + + IPage commentPage = commentMapper.selectPage(page, wrapper); + + // 转换为VO并补充用户信息和目标信息 + return commentPage.convert(comment -> { + HrtCommentAdminVo vo = BeanUtil.copyProperties(comment, HrtCommentAdminVo.class); + + // 补充用户信息 + HrtUser user = userMapper.selectById(comment.getUserId()); + if (user != null) { + vo.setUsername(user.getUsername()); + vo.setNickname(user.getNickname()); + vo.setAvatar(user.getAvatar()); + } + + // 补充目标信息 + fillTargetInfo(vo, comment.getTargetType(), comment.getTargetId()); + + return vo; + }); + } + + /** + * @description [查看评论详情] + * @author Leocoder + */ + @Override + public HrtCommentAdminDetailVo getDetail(Long id) { + HrtComment comment = commentMapper.selectById(id); + if (comment == null) { + throw new BusinessException(404, "评论不存在"); + } + + HrtCommentAdminDetailVo vo = BeanUtil.copyProperties(comment, HrtCommentAdminDetailVo.class); + + // 补充用户详细信息 + HrtUser user = userMapper.selectById(comment.getUserId()); + if (user != null) { + vo.setUsername(user.getUsername()); + vo.setNickname(user.getNickname()); + vo.setAvatar(user.getAvatar()); + vo.setPhone(user.getPhone()); + vo.setEmail(user.getEmail()); + } + + // 补充目标信息 + fillTargetInfo(vo, comment.getTargetType(), comment.getTargetId()); + + // 补充父评论信息 + if (comment.getParentId() != null && comment.getParentId() > 0) { + HrtComment parentComment = commentMapper.selectById(comment.getParentId()); + if (parentComment != null) { + vo.setParentContent(parentComment.getContent()); + + // 获取父评论用户昵称 + HrtUser parentUser = userMapper.selectById(parentComment.getUserId()); + if (parentUser != null) { + vo.setParentUserNickname(parentUser.getNickname()); + } + } + } + + // 查询回复数量 + LambdaQueryWrapper replyWrapper = new LambdaQueryWrapper<>(); + replyWrapper.eq(HrtComment::getParentId, id); + Long replyCount = commentMapper.selectCount(replyWrapper); + vo.setReplyCount(replyCount.intValue()); + + return vo; + } + + /** + * @description [审核评论] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean auditComment(HrtCommentAdminBo bo) { + HrtComment comment = commentMapper.selectById(bo.getId()); + if (comment == null) { + throw new BusinessException(404, "评论不存在"); + } + + if (bo.getStatus() == null || (bo.getStatus() != 0 && bo.getStatus() != 1 && bo.getStatus() != 2)) { + throw new BusinessException(400, "审核状态参数错误"); + } + + HrtComment updateComment = new HrtComment(); + updateComment.setId(bo.getId()); + updateComment.setStatus(bo.getStatus()); + + int result = commentMapper.updateById(updateComment); + return result > 0; + } + + /** + * @description [批量审核评论] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchAudit(List ids, Integer status) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException(400, "请选择要审核的评论"); + } + + if (status == null || (status != 0 && status != 1 && status != 2)) { + throw new BusinessException(400, "审核状态参数错误"); + } + + for (Long id : ids) { + HrtComment updateComment = new HrtComment(); + updateComment.setId(id); + updateComment.setStatus(status); + commentMapper.updateById(updateComment); + } + + return true; + } + + /** + * @description [删除评论] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteComment(Long id) { + HrtComment comment = commentMapper.selectById(id); + if (comment == null) { + throw new BusinessException(404, "评论不存在"); + } + + // 删除该评论的所有回复 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(HrtComment::getParentId, id); + commentMapper.delete(wrapper); + + // 删除评论本身 + int result = commentMapper.deleteById(id); + return result > 0; + } + + /** + * @description [批量删除评论] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchDelete(List ids) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException(400, "请选择要删除的评论"); + } + + for (Long id : ids) { + deleteComment(id); + } + + return true; + } + + /** + * @description [获取待审核评论数量] + * @author Leocoder + */ + @Override + public Long getPendingCount() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(HrtComment::getStatus, 0); + return commentMapper.selectCount(wrapper); + } + + /** + * @description [补充目标信息] + * @author Leocoder + */ + private void fillTargetInfo(Object vo, String targetType, Long targetId) { + String targetTitle = null; + + switch (targetType) { + case "heritage": + HrtHeritage heritage = heritageMapper.selectById(targetId); + if (heritage != null) { + targetTitle = heritage.getName(); + } + break; + case "inheritor": + HrtInheritor inheritor = inheritorMapper.selectById(targetId); + if (inheritor != null) { + targetTitle = inheritor.getName(); + } + break; + case "news": + HrtNews news = newsMapper.selectById(targetId); + if (news != null) { + targetTitle = news.getTitle(); + } + break; + case "event": + HrtEvent event = eventMapper.selectById(targetId); + if (event != null) { + targetTitle = event.getTitle(); + } + break; + } + + // 使用反射设置targetTitle + if (vo instanceof HrtCommentAdminVo) { + ((HrtCommentAdminVo) vo).setTargetTitle(targetTitle); + } else if (vo instanceof HrtCommentAdminDetailVo) { + ((HrtCommentAdminDetailVo) vo).setTargetTitle(targetTitle); + } + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/event/HrtEventAdminService.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/event/HrtEventAdminService.java new file mode 100644 index 0000000..0731130 --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/event/HrtEventAdminService.java @@ -0,0 +1,65 @@ +package org.leocoder.heritage.admin.service.event; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.leocoder.heritage.domain.model.bo.admin.HrtEventAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtEventAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtEventAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtEventAdminVo; + +import java.util.List; + +/** + * @author Leocoder + * @description [活动管理服务接口] + */ +public interface HrtEventAdminService { + + /** + * @description [分页查询活动列表(后台管理)] + * @author Leocoder + */ + IPage listPage(HrtEventAdminQueryBo bo); + + /** + * @description [查看活动详情(后台管理)] + * @author Leocoder + */ + HrtEventAdminDetailVo getDetail(Long id); + + /** + * @description [新增活动] + * @author Leocoder + */ + Long addEvent(HrtEventAdminBo bo); + + /** + * @description [编辑活动] + * @author Leocoder + */ + Boolean editEvent(HrtEventAdminBo bo); + + /** + * @description [删除活动] + * @author Leocoder + */ + Boolean deleteEvent(Long id); + + /** + * @description [批量删除活动] + * @author Leocoder + */ + Boolean batchDelete(List ids); + + /** + * @description [修改发布状态] + * @author Leocoder + */ + Boolean changePublishStatus(Long id, Integer publishStatus); + + /** + * @description [修改活动状态] + * @author Leocoder + */ + Boolean changeEventStatus(Long id, String status); + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/event/HrtEventAdminServiceImpl.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/event/HrtEventAdminServiceImpl.java new file mode 100644 index 0000000..9677e9e --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/event/HrtEventAdminServiceImpl.java @@ -0,0 +1,307 @@ +package org.leocoder.heritage.admin.service.event; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.common.exception.BusinessException; +import org.leocoder.heritage.domain.model.bo.admin.HrtEventAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtEventAdminQueryBo; +import org.leocoder.heritage.domain.pojo.portal.HrtEvent; +import org.leocoder.heritage.domain.model.vo.admin.HrtEventAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtEventAdminVo; +import org.leocoder.heritage.mybatisplus.mapper.portal.HrtEventMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +/** + * @author Leocoder + * @description [活动管理服务实现类] + */ +@Service +@RequiredArgsConstructor +public class HrtEventAdminServiceImpl implements HrtEventAdminService { + + private final HrtEventMapper eventMapper; + + /** + * @description [分页查询活动列表(后台管理)] + * @author Leocoder + */ + @Override + public IPage listPage(HrtEventAdminQueryBo bo) { + Page page = new Page<>(bo.getPageNum(), bo.getPageSize()); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 标题模糊查询 + wrapper.like(StrUtil.isNotEmpty(bo.getTitle()), HrtEvent::getTitle, bo.getTitle()); + + // 地点模糊查询 + wrapper.like(StrUtil.isNotEmpty(bo.getLocation()), HrtEvent::getLocation, bo.getLocation()); + + // 状态精确查询 + wrapper.eq(StrUtil.isNotEmpty(bo.getStatus()), HrtEvent::getStatus, bo.getStatus()); + + // 发布状态查询 + wrapper.eq(Objects.nonNull(bo.getPublishStatus()), HrtEvent::getPublishStatus, bo.getPublishStatus()); + + // 关键词搜索(标题、摘要、内容) + if (StrUtil.isNotEmpty(bo.getKeyword())) { + wrapper.and(w -> w + .like(HrtEvent::getTitle, bo.getKeyword()) + .or() + .like(HrtEvent::getSummary, bo.getKeyword()) + .or() + .like(HrtEvent::getContent, bo.getKeyword()) + ); + } + + // 活动开始时间范围查询 + wrapper.ge(Objects.nonNull(bo.getStartTimeBegin()), HrtEvent::getStartTime, bo.getStartTimeBegin()); + wrapper.le(Objects.nonNull(bo.getStartTimeEnd()), HrtEvent::getStartTime, bo.getStartTimeEnd()); + + // 排序处理 + if ("asc".equalsIgnoreCase(bo.getSortOrder())) { + switch (bo.getSortField()) { + case "view_count" -> wrapper.orderByAsc(HrtEvent::getViewCount); + case "start_time" -> wrapper.orderByAsc(HrtEvent::getStartTime); + case "current_participants" -> wrapper.orderByAsc(HrtEvent::getCurrentParticipants); + default -> wrapper.orderByAsc(HrtEvent::getCreateTime); + } + } else { + switch (bo.getSortField()) { + case "view_count" -> wrapper.orderByDesc(HrtEvent::getViewCount); + case "start_time" -> wrapper.orderByDesc(HrtEvent::getStartTime); + case "current_participants" -> wrapper.orderByDesc(HrtEvent::getCurrentParticipants); + default -> wrapper.orderByDesc(HrtEvent::getCreateTime); + } + } + + IPage eventPage = eventMapper.selectPage(page, wrapper); + return eventPage.convert(event -> BeanUtil.copyProperties(event, HrtEventAdminVo.class)); + } + + /** + * @description [查看活动详情(后台管理)] + * @author Leocoder + */ + @Override + public HrtEventAdminDetailVo getDetail(Long id) { + HrtEvent event = eventMapper.selectById(id); + if (event == null) { + throw new BusinessException(404, "活动不存在"); + } + return BeanUtil.copyProperties(event, HrtEventAdminDetailVo.class); + } + + /** + * @description [新增活动] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long addEvent(HrtEventAdminBo bo) { + // 验证时间合理性 + validateEventTimes(bo); + + HrtEvent event = BeanUtil.copyProperties(bo, HrtEvent.class); + + // 初始化当前参与人数为0 + event.setCurrentParticipants(0); + + // 初始化浏览量为0 + event.setViewCount(0); + + // 设置创建人 + event.setCreateBy(StpUtil.getLoginIdAsString()); + + int result = eventMapper.insert(event); + if (result <= 0) { + throw new BusinessException(500, "新增活动失败"); + } + + return event.getId(); + } + + /** + * @description [编辑活动] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean editEvent(HrtEventAdminBo bo) { + if (bo.getId() == null) { + throw new BusinessException(400, "活动ID不能为空"); + } + + // 检查活动是否存在 + HrtEvent existEvent = eventMapper.selectById(bo.getId()); + if (existEvent == null) { + throw new BusinessException(404, "活动不存在"); + } + + // 验证时间合理性 + validateEventTimes(bo); + + HrtEvent event = BeanUtil.copyProperties(bo, HrtEvent.class); + + // 设置更新人 + event.setUpdateBy(StpUtil.getLoginIdAsString()); + + int result = eventMapper.updateById(event); + return result > 0; + } + + /** + * @description [删除活动] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteEvent(Long id) { + // 检查活动是否存在 + HrtEvent event = eventMapper.selectById(id); + if (event == null) { + throw new BusinessException(404, "活动不存在"); + } + + // 检查是否有用户报名(如果已有报名,不允许删除) + if (event.getCurrentParticipants() != null && event.getCurrentParticipants() > 0) { + throw new BusinessException(400, "该活动已有用户报名,无法删除"); + } + + int result = eventMapper.deleteById(id); + return result > 0; + } + + /** + * @description [批量删除活动] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchDelete(List ids) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException(400, "请选择要删除的活动"); + } + + // 检查是否有活动已有报名 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.in(HrtEvent::getId, ids); + wrapper.gt(HrtEvent::getCurrentParticipants, 0); + + Long count = eventMapper.selectCount(wrapper); + if (count > 0) { + throw new BusinessException(400, "选中的活动中有已报名的活动,无法删除"); + } + + int result = eventMapper.deleteBatchIds(ids); + return result > 0; + } + + /** + * @description [修改发布状态] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean changePublishStatus(Long id, Integer publishStatus) { + // 检查活动是否存在 + HrtEvent event = eventMapper.selectById(id); + if (event == null) { + throw new BusinessException(404, "活动不存在"); + } + + if (publishStatus == null || (publishStatus != 0 && publishStatus != 1)) { + throw new BusinessException(400, "发布状态参数错误"); + } + + HrtEvent updateEvent = new HrtEvent(); + updateEvent.setId(id); + updateEvent.setPublishStatus(publishStatus); + updateEvent.setUpdateBy(StpUtil.getLoginIdAsString()); + + int result = eventMapper.updateById(updateEvent); + return result > 0; + } + + /** + * @description [修改活动状态] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean changeEventStatus(Long id, String status) { + // 检查活动是否存在 + HrtEvent event = eventMapper.selectById(id); + if (event == null) { + throw new BusinessException(404, "活动不存在"); + } + + // 验证状态值 + if (StrUtil.isEmpty(status)) { + throw new BusinessException(400, "活动状态不能为空"); + } + + if (!isValidEventStatus(status)) { + throw new BusinessException(400, "活动状态参数错误,只能是:upcoming、ongoing、finished、cancelled"); + } + + HrtEvent updateEvent = new HrtEvent(); + updateEvent.setId(id); + updateEvent.setStatus(status); + updateEvent.setUpdateBy(StpUtil.getLoginIdAsString()); + + int result = eventMapper.updateById(updateEvent); + return result > 0; + } + + /** + * @description [验证活动时间的合理性] + * @author Leocoder + */ + private void validateEventTimes(HrtEventAdminBo bo) { + LocalDateTime now = LocalDateTime.now(); + + // 验证活动时间 + if (bo.getStartTime() != null && bo.getEndTime() != null) { + if (bo.getEndTime().isBefore(bo.getStartTime())) { + throw new BusinessException(400, "活动结束时间不能早于开始时间"); + } + } + + // 验证报名时间 + if (bo.getRegistrationStart() != null && bo.getRegistrationEnd() != null) { + if (bo.getRegistrationEnd().isBefore(bo.getRegistrationStart())) { + throw new BusinessException(400, "报名结束时间不能早于报名开始时间"); + } + } + + // 验证报名时间与活动时间的关系 + if (bo.getRegistrationEnd() != null && bo.getStartTime() != null) { + if (bo.getRegistrationEnd().isAfter(bo.getStartTime())) { + throw new BusinessException(400, "报名结束时间不能晚于活动开始时间"); + } + } + } + + /** + * @description [验证活动状态是否合法] + * @author Leocoder + */ + private boolean isValidEventStatus(String status) { + return "upcoming".equals(status) + || "ongoing".equals(status) + || "finished".equals(status) + || "cancelled".equals(status); + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/heritage/HrtHeritageAdminService.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/heritage/HrtHeritageAdminService.java new file mode 100644 index 0000000..c066d36 --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/heritage/HrtHeritageAdminService.java @@ -0,0 +1,65 @@ +package org.leocoder.heritage.admin.service.heritage; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.leocoder.heritage.domain.model.bo.admin.HrtHeritageAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtHeritageAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtHeritageAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtHeritageAdminVo; + +import java.util.List; + +/** + * @author Leocoder + * @description [非遗项目管理服务接口] + */ +public interface HrtHeritageAdminService { + + /** + * @description [分页查询非遗项目列表(后台管理)] + * @author Leocoder + */ + IPage listPage(HrtHeritageAdminQueryBo bo); + + /** + * @description [查看非遗项目详情(后台管理)] + * @author Leocoder + */ + HrtHeritageAdminDetailVo getDetail(Long id); + + /** + * @description [新增非遗项目] + * @author Leocoder + */ + Long addHeritage(HrtHeritageAdminBo bo); + + /** + * @description [编辑非遗项目] + * @author Leocoder + */ + Boolean editHeritage(HrtHeritageAdminBo bo); + + /** + * @description [删除非遗项目] + * @author Leocoder + */ + Boolean deleteHeritage(Long id); + + /** + * @description [批量删除非遗项目] + * @author Leocoder + */ + Boolean batchDelete(List ids); + + /** + * @description [修改发布状态] + * @author Leocoder + */ + Boolean changePublishStatus(Long id, Integer publishStatus); + + /** + * @description [设置精选状态] + * @author Leocoder + */ + Boolean setFeatured(Long id, Integer isFeatured); + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/heritage/HrtHeritageAdminServiceImpl.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/heritage/HrtHeritageAdminServiceImpl.java new file mode 100644 index 0000000..97cdee7 --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/heritage/HrtHeritageAdminServiceImpl.java @@ -0,0 +1,327 @@ +package org.leocoder.heritage.admin.service.heritage; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.common.exception.BusinessException; +import org.leocoder.heritage.domain.model.bo.admin.HrtHeritageAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtHeritageAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtHeritageAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtHeritageAdminVo; +import org.leocoder.heritage.domain.pojo.portal.HrtHeritage; +import org.leocoder.heritage.mybatisplus.mapper.portal.HrtHeritageMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * @author Leocoder + * @description [非遗项目管理服务实现类] + */ +@Service +@RequiredArgsConstructor +public class HrtHeritageAdminServiceImpl implements HrtHeritageAdminService { + + private final HrtHeritageMapper hrtHeritageMapper; + + /** + * @description [分页查询非遗项目列表(后台管理)] + * @author Leocoder + */ + @Override + public IPage listPage(HrtHeritageAdminQueryBo bo) { + Page page = new Page<>(bo.getPageNum(), bo.getPageSize()); + + LambdaQueryWrapper wrapper = buildQueryWrapper(bo); + + // 排序 + applySorting(wrapper, bo.getSortField(), bo.getSortOrder()); + + IPage heritagePageResult = hrtHeritageMapper.selectPage(page, wrapper); + + // 转换为VO + return heritagePageResult.convert(heritage -> BeanUtil.copyProperties(heritage, HrtHeritageAdminVo.class)); + } + + /** + * @description [查看非遗项目详情(后台管理)] + * @author Leocoder + */ + @Override + public HrtHeritageAdminDetailVo getDetail(Long id) { + HrtHeritage heritage = hrtHeritageMapper.selectById(id); + + if (heritage == null) { + throw new BusinessException(404, "非遗项目不存在"); + } + + // 转换为详情VO + return BeanUtil.copyProperties(heritage, HrtHeritageAdminDetailVo.class); + } + + /** + * @description [新增非遗项目] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long addHeritage(HrtHeritageAdminBo bo) { + // 转换为实体 + HrtHeritage heritage = BeanUtil.copyProperties(bo, HrtHeritage.class); + + // 设置创建人 + heritage.setCreateBy(StpUtil.getLoginIdAsString()); + + // 初始化统计数据 + heritage.setViewCount(0); + heritage.setLikeCount(0); + heritage.setFavoriteCount(0); + heritage.setCommentCount(0); + + // 设置默认值 + if (heritage.getIsFeatured() == null) { + heritage.setIsFeatured(0); + } + if (heritage.getPublishStatus() == null) { + heritage.setPublishStatus(0); + } + if (heritage.getSortOrder() == null) { + heritage.setSortOrder(0); + } + + // 插入数据库 + int result = hrtHeritageMapper.insert(heritage); + + if (result <= 0) { + throw new BusinessException(500, "新增非遗项目失败"); + } + + return heritage.getId(); + } + + /** + * @description [编辑非遗项目] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean editHeritage(HrtHeritageAdminBo bo) { + // 验证ID + if (bo.getId() == null) { + throw new BusinessException(400, "非遗项目ID不能为空"); + } + + // 验证是否存在 + HrtHeritage existHeritage = hrtHeritageMapper.selectById(bo.getId()); + if (existHeritage == null) { + throw new BusinessException(404, "非遗项目不存在"); + } + + // 转换为实体 + HrtHeritage heritage = BeanUtil.copyProperties(bo, HrtHeritage.class); + + // 设置更新人 + heritage.setUpdateBy(StpUtil.getLoginIdAsString()); + + // 更新数据库 + int result = hrtHeritageMapper.updateById(heritage); + + if (result <= 0) { + throw new BusinessException(500, "编辑非遗项目失败"); + } + + return true; + } + + /** + * @description [删除非遗项目] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteHeritage(Long id) { + // 验证是否存在 + HrtHeritage heritage = hrtHeritageMapper.selectById(id); + if (heritage == null) { + throw new BusinessException(404, "非遗项目不存在"); + } + + // 逻辑删除 + int result = hrtHeritageMapper.deleteById(id); + + if (result <= 0) { + throw new BusinessException(500, "删除非遗项目失败"); + } + + return true; + } + + /** + * @description [批量删除非遗项目] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchDelete(List ids) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException(400, "删除的ID列表不能为空"); + } + + // 批量逻辑删除 + int result = hrtHeritageMapper.deleteBatchIds(ids); + + if (result <= 0) { + throw new BusinessException(500, "批量删除非遗项目失败"); + } + + return true; + } + + /** + * @description [修改发布状态] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean changePublishStatus(Long id, Integer publishStatus) { + // 验证参数 + if (publishStatus == null || (publishStatus != 0 && publishStatus != 1)) { + throw new BusinessException(400, "发布状态参数错误,必须为0或1"); + } + + // 验证是否存在 + HrtHeritage heritage = hrtHeritageMapper.selectById(id); + if (heritage == null) { + throw new BusinessException(404, "非遗项目不存在"); + } + + // 更新发布状态 + HrtHeritage updateHeritage = new HrtHeritage(); + updateHeritage.setId(id); + updateHeritage.setPublishStatus(publishStatus); + updateHeritage.setUpdateBy(StpUtil.getLoginIdAsString()); + + int result = hrtHeritageMapper.updateById(updateHeritage); + + if (result <= 0) { + throw new BusinessException(500, "修改发布状态失败"); + } + + return true; + } + + /** + * @description [设置精选状态] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean setFeatured(Long id, Integer isFeatured) { + // 验证参数 + if (isFeatured == null || (isFeatured != 0 && isFeatured != 1)) { + throw new BusinessException(400, "精选状态参数错误,必须为0或1"); + } + + // 验证是否存在 + HrtHeritage heritage = hrtHeritageMapper.selectById(id); + if (heritage == null) { + throw new BusinessException(404, "非遗项目不存在"); + } + + // 更新精选状态 + HrtHeritage updateHeritage = new HrtHeritage(); + updateHeritage.setId(id); + updateHeritage.setIsFeatured(isFeatured); + updateHeritage.setUpdateBy(StpUtil.getLoginIdAsString()); + + int result = hrtHeritageMapper.updateById(updateHeritage); + + if (result <= 0) { + throw new BusinessException(500, "设置精选状态失败"); + } + + return true; + } + + /** + * @description [构建查询条件] + * @author Leocoder + */ + private LambdaQueryWrapper buildQueryWrapper(HrtHeritageAdminQueryBo bo) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 关键词搜索(搜索名称、英文名称、描述) + if (StrUtil.isNotBlank(bo.getKeyword())) { + wrapper.and(w -> w + .like(HrtHeritage::getName, bo.getKeyword()) + .or().like(HrtHeritage::getNameEn, bo.getKeyword()) + .or().like(HrtHeritage::getDescription, bo.getKeyword()) + ); + } + + // 名称模糊查询 + wrapper.like(StrUtil.isNotBlank(bo.getName()), HrtHeritage::getName, bo.getName()); + + // 英文名称模糊查询 + wrapper.like(StrUtil.isNotBlank(bo.getNameEn()), HrtHeritage::getNameEn, bo.getNameEn()); + + // 分类 + wrapper.eq(StrUtil.isNotBlank(bo.getCategory()), HrtHeritage::getCategory, bo.getCategory()); + + // 级别 + wrapper.eq(StrUtil.isNotBlank(bo.getLevel()), HrtHeritage::getLevel, bo.getLevel()); + + // 省份 + wrapper.eq(StrUtil.isNotBlank(bo.getProvince()), HrtHeritage::getProvince, bo.getProvince()); + + // 城市 + wrapper.eq(StrUtil.isNotBlank(bo.getCity()), HrtHeritage::getCity, bo.getCity()); + + // 状态 + wrapper.eq(StrUtil.isNotBlank(bo.getStatus()), HrtHeritage::getStatus, bo.getStatus()); + + // 标签模糊匹配 + wrapper.like(StrUtil.isNotBlank(bo.getTag()), HrtHeritage::getTags, bo.getTag()); + + // 发布状态 + wrapper.eq(bo.getPublishStatus() != null, HrtHeritage::getPublishStatus, bo.getPublishStatus()); + + // 是否精选 + wrapper.eq(bo.getIsFeatured() != null, HrtHeritage::getIsFeatured, bo.getIsFeatured()); + + return wrapper; + } + + /** + * @description [应用排序规则] + * @author Leocoder + */ + private void applySorting(LambdaQueryWrapper wrapper, String sortField, String sortOrder) { + boolean isAsc = "asc".equalsIgnoreCase(sortOrder); + + switch (sortField) { + case "view_count": + wrapper.orderBy(true, isAsc, HrtHeritage::getViewCount); + break; + case "like_count": + wrapper.orderBy(true, isAsc, HrtHeritage::getLikeCount); + break; + case "favorite_count": + wrapper.orderBy(true, isAsc, HrtHeritage::getFavoriteCount); + break; + case "sort_order": + wrapper.orderBy(true, isAsc, HrtHeritage::getSortOrder); + break; + case "create_time": + default: + wrapper.orderBy(true, isAsc, HrtHeritage::getCreateTime); + break; + } + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/inheritor/HrtInheritorAdminService.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/inheritor/HrtInheritorAdminService.java new file mode 100644 index 0000000..b4c7e42 --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/inheritor/HrtInheritorAdminService.java @@ -0,0 +1,65 @@ +package org.leocoder.heritage.admin.service.inheritor; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.leocoder.heritage.domain.model.bo.admin.HrtInheritorAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtInheritorAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtInheritorAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtInheritorAdminVo; + +import java.util.List; + +/** + * @author Leocoder + * @description [传承人管理服务接口] + */ +public interface HrtInheritorAdminService { + + /** + * @description [分页查询传承人列表(后台管理)] + * @author Leocoder + */ + IPage listPage(HrtInheritorAdminQueryBo bo); + + /** + * @description [查看传承人详情(后台管理)] + * @author Leocoder + */ + HrtInheritorAdminDetailVo getDetail(Long id); + + /** + * @description [新增传承人] + * @author Leocoder + */ + Long addInheritor(HrtInheritorAdminBo bo); + + /** + * @description [编辑传承人] + * @author Leocoder + */ + Boolean editInheritor(HrtInheritorAdminBo bo); + + /** + * @description [删除传承人] + * @author Leocoder + */ + Boolean deleteInheritor(Long id); + + /** + * @description [批量删除传承人] + * @author Leocoder + */ + Boolean batchDelete(List ids); + + /** + * @description [修改发布状态] + * @author Leocoder + */ + Boolean changePublishStatus(Long id, Integer publishStatus); + + /** + * @description [设置精选状态] + * @author Leocoder + */ + Boolean setFeatured(Long id, Integer isFeatured); + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/inheritor/HrtInheritorAdminServiceImpl.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/inheritor/HrtInheritorAdminServiceImpl.java new file mode 100644 index 0000000..81122d6 --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/inheritor/HrtInheritorAdminServiceImpl.java @@ -0,0 +1,323 @@ +package org.leocoder.heritage.admin.service.inheritor; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.common.exception.BusinessException; +import org.leocoder.heritage.domain.model.bo.admin.HrtInheritorAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtInheritorAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtInheritorAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtInheritorAdminVo; +import org.leocoder.heritage.domain.pojo.portal.HrtInheritor; +import org.leocoder.heritage.mybatisplus.mapper.portal.HrtInheritorMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * @author Leocoder + * @description [传承人管理服务实现类] + */ +@Service +@RequiredArgsConstructor +public class HrtInheritorAdminServiceImpl implements HrtInheritorAdminService { + + private final HrtInheritorMapper hrtInheritorMapper; + + /** + * @description [分页查询传承人列表(后台管理)] + * @author Leocoder + */ + @Override + public IPage listPage(HrtInheritorAdminQueryBo bo) { + Page page = new Page<>(bo.getPageNum(), bo.getPageSize()); + + LambdaQueryWrapper wrapper = buildQueryWrapper(bo); + + // 排序 + applySorting(wrapper, bo.getSortField(), bo.getSortOrder()); + + IPage inheritorPageResult = hrtInheritorMapper.selectPage(page, wrapper); + + // 转换为VO + return inheritorPageResult.convert(inheritor -> BeanUtil.copyProperties(inheritor, HrtInheritorAdminVo.class)); + } + + /** + * @description [查看传承人详情(后台管理)] + * @author Leocoder + */ + @Override + public HrtInheritorAdminDetailVo getDetail(Long id) { + HrtInheritor inheritor = hrtInheritorMapper.selectById(id); + + if (inheritor == null) { + throw new BusinessException(404, "传承人不存在"); + } + + // 转换为详情VO + return BeanUtil.copyProperties(inheritor, HrtInheritorAdminDetailVo.class); + } + + /** + * @description [新增传承人] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long addInheritor(HrtInheritorAdminBo bo) { + // 转换为实体 + HrtInheritor inheritor = BeanUtil.copyProperties(bo, HrtInheritor.class); + + // 设置创建人 + inheritor.setCreateBy(StpUtil.getLoginIdAsString()); + + // 初始化统计数据 + inheritor.setViewCount(0); + inheritor.setLikeCount(0); + + // 设置默认值 + if (inheritor.getIsFeatured() == null) { + inheritor.setIsFeatured(0); + } + if (inheritor.getPublishStatus() == null) { + inheritor.setPublishStatus(0); + } + if (inheritor.getSortOrder() == null) { + inheritor.setSortOrder(0); + } + + // 插入数据库 + int result = hrtInheritorMapper.insert(inheritor); + + if (result <= 0) { + throw new BusinessException(500, "新增传承人失败"); + } + + return inheritor.getId(); + } + + /** + * @description [编辑传承人] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean editInheritor(HrtInheritorAdminBo bo) { + // 验证ID + if (bo.getId() == null) { + throw new BusinessException(400, "传承人ID不能为空"); + } + + // 验证是否存在 + HrtInheritor existInheritor = hrtInheritorMapper.selectById(bo.getId()); + if (existInheritor == null) { + throw new BusinessException(404, "传承人不存在"); + } + + // 转换为实体 + HrtInheritor inheritor = BeanUtil.copyProperties(bo, HrtInheritor.class); + + // 设置更新人 + inheritor.setUpdateBy(StpUtil.getLoginIdAsString()); + + // 更新数据库 + int result = hrtInheritorMapper.updateById(inheritor); + + if (result <= 0) { + throw new BusinessException(500, "编辑传承人失败"); + } + + return true; + } + + /** + * @description [删除传承人] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteInheritor(Long id) { + // 验证是否存在 + HrtInheritor inheritor = hrtInheritorMapper.selectById(id); + if (inheritor == null) { + throw new BusinessException(404, "传承人不存在"); + } + + // 逻辑删除 + int result = hrtInheritorMapper.deleteById(id); + + if (result <= 0) { + throw new BusinessException(500, "删除传承人失败"); + } + + return true; + } + + /** + * @description [批量删除传承人] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchDelete(List ids) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException(400, "删除的ID列表不能为空"); + } + + // 批量逻辑删除 + int result = hrtInheritorMapper.deleteBatchIds(ids); + + if (result <= 0) { + throw new BusinessException(500, "批量删除传承人失败"); + } + + return true; + } + + /** + * @description [修改发布状态] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean changePublishStatus(Long id, Integer publishStatus) { + // 验证参数 + if (publishStatus == null || (publishStatus != 0 && publishStatus != 1)) { + throw new BusinessException(400, "发布状态参数错误,必须为0或1"); + } + + // 验证是否存在 + HrtInheritor inheritor = hrtInheritorMapper.selectById(id); + if (inheritor == null) { + throw new BusinessException(404, "传承人不存在"); + } + + // 更新发布状态 + HrtInheritor updateInheritor = new HrtInheritor(); + updateInheritor.setId(id); + updateInheritor.setPublishStatus(publishStatus); + updateInheritor.setUpdateBy(StpUtil.getLoginIdAsString()); + + int result = hrtInheritorMapper.updateById(updateInheritor); + + if (result <= 0) { + throw new BusinessException(500, "修改发布状态失败"); + } + + return true; + } + + /** + * @description [设置精选状态] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean setFeatured(Long id, Integer isFeatured) { + // 验证参数 + if (isFeatured == null || (isFeatured != 0 && isFeatured != 1)) { + throw new BusinessException(400, "精选状态参数错误,必须为0或1"); + } + + // 验证是否存在 + HrtInheritor inheritor = hrtInheritorMapper.selectById(id); + if (inheritor == null) { + throw new BusinessException(404, "传承人不存在"); + } + + // 更新精选状态 + HrtInheritor updateInheritor = new HrtInheritor(); + updateInheritor.setId(id); + updateInheritor.setIsFeatured(isFeatured); + updateInheritor.setUpdateBy(StpUtil.getLoginIdAsString()); + + int result = hrtInheritorMapper.updateById(updateInheritor); + + if (result <= 0) { + throw new BusinessException(500, "设置精选状态失败"); + } + + return true; + } + + /** + * @description [构建查询条件] + * @author Leocoder + */ + private LambdaQueryWrapper buildQueryWrapper(HrtInheritorAdminQueryBo bo) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 关键词搜索(搜索姓名、简介、故事等字段) + if (StrUtil.isNotBlank(bo.getKeyword())) { + wrapper.and(w -> w + .like(HrtInheritor::getName, bo.getKeyword()) + .or().like(HrtInheritor::getNameEn, bo.getKeyword()) + .or().like(HrtInheritor::getIntroduction, bo.getKeyword()) + .or().like(HrtInheritor::getStory, bo.getKeyword()) + ); + } + + // 姓名模糊查询 + wrapper.like(StrUtil.isNotBlank(bo.getName()), HrtInheritor::getName, bo.getName()); + + // 英文名模糊查询 + wrapper.like(StrUtil.isNotBlank(bo.getNameEn()), HrtInheritor::getNameEn, bo.getNameEn()); + + // 性别 + wrapper.eq(bo.getGender() != null, HrtInheritor::getGender, bo.getGender()); + + // 关联非遗项目ID + wrapper.eq(bo.getHeritageId() != null, HrtInheritor::getHeritageId, bo.getHeritageId()); + + // 传承项目名称模糊查询 + wrapper.like(StrUtil.isNotBlank(bo.getHeritageName()), HrtInheritor::getHeritageName, bo.getHeritageName()); + + // 传承人级别 + wrapper.eq(StrUtil.isNotBlank(bo.getLevel()), HrtInheritor::getLevel, bo.getLevel()); + + // 省份 + wrapper.eq(StrUtil.isNotBlank(bo.getProvince()), HrtInheritor::getProvince, bo.getProvince()); + + // 城市 + wrapper.eq(StrUtil.isNotBlank(bo.getCity()), HrtInheritor::getCity, bo.getCity()); + + // 发布状态 + wrapper.eq(bo.getPublishStatus() != null, HrtInheritor::getPublishStatus, bo.getPublishStatus()); + + // 是否精选 + wrapper.eq(bo.getIsFeatured() != null, HrtInheritor::getIsFeatured, bo.getIsFeatured()); + + return wrapper; + } + + /** + * @description [应用排序规则] + * @author Leocoder + */ + private void applySorting(LambdaQueryWrapper wrapper, String sortField, String sortOrder) { + boolean isAsc = "asc".equalsIgnoreCase(sortOrder); + + switch (sortField) { + case "view_count": + wrapper.orderBy(true, isAsc, HrtInheritor::getViewCount); + break; + case "like_count": + wrapper.orderBy(true, isAsc, HrtInheritor::getLikeCount); + break; + case "sort_order": + wrapper.orderBy(true, isAsc, HrtInheritor::getSortOrder); + break; + case "create_time": + default: + wrapper.orderBy(true, isAsc, HrtInheritor::getCreateTime); + break; + } + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/news/HrtNewsAdminService.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/news/HrtNewsAdminService.java new file mode 100644 index 0000000..6497102 --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/news/HrtNewsAdminService.java @@ -0,0 +1,65 @@ +package org.leocoder.heritage.admin.service.news; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.leocoder.heritage.domain.model.bo.admin.HrtNewsAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtNewsAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtNewsAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtNewsAdminVo; + +import java.util.List; + +/** + * @author Leocoder + * @description [新闻资讯管理服务接口] + */ +public interface HrtNewsAdminService { + + /** + * @description [分页查询新闻资讯列表(后台管理)] + * @author Leocoder + */ + IPage listPage(HrtNewsAdminQueryBo bo); + + /** + * @description [查看新闻资讯详情(后台管理)] + * @author Leocoder + */ + HrtNewsAdminDetailVo getDetail(Long id); + + /** + * @description [新增新闻资讯] + * @author Leocoder + */ + Long addNews(HrtNewsAdminBo bo); + + /** + * @description [编辑新闻资讯] + * @author Leocoder + */ + Boolean editNews(HrtNewsAdminBo bo); + + /** + * @description [删除新闻资讯] + * @author Leocoder + */ + Boolean deleteNews(Long id); + + /** + * @description [批量删除新闻资讯] + * @author Leocoder + */ + Boolean batchDelete(List ids); + + /** + * @description [修改发布状态] + * @author Leocoder + */ + Boolean changePublishStatus(Long id, Integer publishStatus); + + /** + * @description [设置置顶状态] + * @author Leocoder + */ + Boolean setTop(Long id, Integer isTop); + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/news/HrtNewsAdminServiceImpl.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/news/HrtNewsAdminServiceImpl.java new file mode 100644 index 0000000..66a876b --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/news/HrtNewsAdminServiceImpl.java @@ -0,0 +1,334 @@ +package org.leocoder.heritage.admin.service.news; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.common.exception.BusinessException; +import org.leocoder.heritage.domain.model.bo.admin.HrtNewsAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtNewsAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtNewsAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtNewsAdminVo; +import org.leocoder.heritage.domain.pojo.portal.HrtNews; +import org.leocoder.heritage.mybatisplus.mapper.portal.HrtNewsMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * @author Leocoder + * @description [新闻资讯管理服务实现类] + */ +@Service +@RequiredArgsConstructor +public class HrtNewsAdminServiceImpl implements HrtNewsAdminService { + + private final HrtNewsMapper hrtNewsMapper; + + /** + * @description [分页查询新闻资讯列表(后台管理)] + * @author Leocoder + */ + @Override + public IPage listPage(HrtNewsAdminQueryBo bo) { + Page page = new Page<>(bo.getPageNum(), bo.getPageSize()); + + LambdaQueryWrapper wrapper = buildQueryWrapper(bo); + + // 排序 + applySorting(wrapper, bo.getSortField(), bo.getSortOrder()); + + IPage newsPageResult = hrtNewsMapper.selectPage(page, wrapper); + + // 转换为VO + return newsPageResult.convert(news -> BeanUtil.copyProperties(news, HrtNewsAdminVo.class)); + } + + /** + * @description [查看新闻资讯详情(后台管理)] + * @author Leocoder + */ + @Override + public HrtNewsAdminDetailVo getDetail(Long id) { + HrtNews news = hrtNewsMapper.selectById(id); + + if (news == null) { + throw new BusinessException(404, "新闻资讯不存在"); + } + + // 转换为详情VO + return BeanUtil.copyProperties(news, HrtNewsAdminDetailVo.class); + } + + /** + * @description [新增新闻资讯] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long addNews(HrtNewsAdminBo bo) { + // 转换为实体 + HrtNews news = BeanUtil.copyProperties(bo, HrtNews.class); + + // 设置创建人 + news.setCreateBy(StpUtil.getLoginIdAsString()); + + // 初始化统计数据 + news.setViewCount(0); + news.setLikeCount(0); + + // 设置默认值 + if (news.getIsTop() == null) { + news.setIsTop(0); + } + if (news.getPublishStatus() == null) { + news.setPublishStatus(0); + } + + // 如果发布状态为1且未设置发布时间,则设置为当前时间 + if (news.getPublishStatus() == 1 && news.getPublishTime() == null) { + news.setPublishTime(LocalDateTime.now()); + } + + // 插入数据库 + int result = hrtNewsMapper.insert(news); + + if (result <= 0) { + throw new BusinessException(500, "新增新闻资讯失败"); + } + + return news.getId(); + } + + /** + * @description [编辑新闻资讯] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean editNews(HrtNewsAdminBo bo) { + // 验证ID + if (bo.getId() == null) { + throw new BusinessException(400, "新闻资讯ID不能为空"); + } + + // 验证是否存在 + HrtNews existNews = hrtNewsMapper.selectById(bo.getId()); + if (existNews == null) { + throw new BusinessException(404, "新闻资讯不存在"); + } + + // 转换为实体 + HrtNews news = BeanUtil.copyProperties(bo, HrtNews.class); + + // 设置更新人 + news.setUpdateBy(StpUtil.getLoginIdAsString()); + + // 如果发布状态从草稿变为已发布,且未设置发布时间,则设置为当前时间 + if (news.getPublishStatus() != null && news.getPublishStatus() == 1 + && existNews.getPublishStatus() == 0 && news.getPublishTime() == null) { + news.setPublishTime(LocalDateTime.now()); + } + + // 更新数据库 + int result = hrtNewsMapper.updateById(news); + + if (result <= 0) { + throw new BusinessException(500, "编辑新闻资讯失败"); + } + + return true; + } + + /** + * @description [删除新闻资讯] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteNews(Long id) { + // 验证是否存在 + HrtNews news = hrtNewsMapper.selectById(id); + if (news == null) { + throw new BusinessException(404, "新闻资讯不存在"); + } + + // 逻辑删除 + int result = hrtNewsMapper.deleteById(id); + + if (result <= 0) { + throw new BusinessException(500, "删除新闻资讯失败"); + } + + return true; + } + + /** + * @description [批量删除新闻资讯] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchDelete(List ids) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException(400, "删除的ID列表不能为空"); + } + + // 批量逻辑删除 + int result = hrtNewsMapper.deleteBatchIds(ids); + + if (result <= 0) { + throw new BusinessException(500, "批量删除新闻资讯失败"); + } + + return true; + } + + /** + * @description [修改发布状态] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean changePublishStatus(Long id, Integer publishStatus) { + // 验证参数 + if (publishStatus == null || (publishStatus != 0 && publishStatus != 1)) { + throw new BusinessException(400, "发布状态参数错误,必须为0或1"); + } + + // 验证是否存在 + HrtNews news = hrtNewsMapper.selectById(id); + if (news == null) { + throw new BusinessException(404, "新闻资讯不存在"); + } + + // 更新发布状态 + HrtNews updateNews = new HrtNews(); + updateNews.setId(id); + updateNews.setPublishStatus(publishStatus); + updateNews.setUpdateBy(StpUtil.getLoginIdAsString()); + + // 如果从草稿变为已发布,且未设置发布时间,则设置为当前时间 + if (publishStatus == 1 && news.getPublishStatus() == 0 && news.getPublishTime() == null) { + updateNews.setPublishTime(LocalDateTime.now()); + } + + int result = hrtNewsMapper.updateById(updateNews); + + if (result <= 0) { + throw new BusinessException(500, "修改发布状态失败"); + } + + return true; + } + + /** + * @description [设置置顶状态] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean setTop(Long id, Integer isTop) { + // 验证参数 + if (isTop == null || (isTop != 0 && isTop != 1)) { + throw new BusinessException(400, "置顶状态参数错误,必须为0或1"); + } + + // 验证是否存在 + HrtNews news = hrtNewsMapper.selectById(id); + if (news == null) { + throw new BusinessException(404, "新闻资讯不存在"); + } + + // 更新置顶状态 + HrtNews updateNews = new HrtNews(); + updateNews.setId(id); + updateNews.setIsTop(isTop); + updateNews.setUpdateBy(StpUtil.getLoginIdAsString()); + + int result = hrtNewsMapper.updateById(updateNews); + + if (result <= 0) { + throw new BusinessException(500, "设置置顶状态失败"); + } + + return true; + } + + /** + * @description [构建查询条件] + * @author Leocoder + */ + private LambdaQueryWrapper buildQueryWrapper(HrtNewsAdminQueryBo bo) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 关键词搜索(搜索标题、摘要、内容等字段) + if (StrUtil.isNotBlank(bo.getKeyword())) { + wrapper.and(w -> w + .like(HrtNews::getTitle, bo.getKeyword()) + .or().like(HrtNews::getSummary, bo.getKeyword()) + .or().like(HrtNews::getContent, bo.getKeyword()) + ); + } + + // 标题模糊查询 + wrapper.like(StrUtil.isNotBlank(bo.getTitle()), HrtNews::getTitle, bo.getTitle()); + + // 作者模糊查询 + wrapper.like(StrUtil.isNotBlank(bo.getAuthor()), HrtNews::getAuthor, bo.getAuthor()); + + // 来源模糊查询 + wrapper.like(StrUtil.isNotBlank(bo.getSource()), HrtNews::getSource, bo.getSource()); + + // 分类 + wrapper.eq(StrUtil.isNotBlank(bo.getCategory()), HrtNews::getCategory, bo.getCategory()); + + // 标签模糊匹配 + wrapper.like(StrUtil.isNotBlank(bo.getTag()), HrtNews::getTags, bo.getTag()); + + // 是否置顶 + wrapper.eq(bo.getIsTop() != null, HrtNews::getIsTop, bo.getIsTop()); + + // 发布状态 + wrapper.eq(bo.getPublishStatus() != null, HrtNews::getPublishStatus, bo.getPublishStatus()); + + // 发布时间范围查询 + wrapper.ge(bo.getPublishTimeStart() != null, HrtNews::getPublishTime, bo.getPublishTimeStart()); + wrapper.le(bo.getPublishTimeEnd() != null, HrtNews::getPublishTime, bo.getPublishTimeEnd()); + + return wrapper; + } + + /** + * @description [应用排序规则] + * @author Leocoder + */ + private void applySorting(LambdaQueryWrapper wrapper, String sortField, String sortOrder) { + boolean isAsc = "asc".equalsIgnoreCase(sortOrder); + + switch (sortField) { + case "view_count": + wrapper.orderBy(true, isAsc, HrtNews::getViewCount); + break; + case "like_count": + wrapper.orderBy(true, isAsc, HrtNews::getLikeCount); + break; + case "publish_time": + wrapper.orderBy(true, isAsc, HrtNews::getPublishTime); + break; + case "create_time": + default: + wrapper.orderBy(true, isAsc, HrtNews::getCreateTime); + break; + } + + // 置顶的新闻始终排在前面 + wrapper.orderByDesc(HrtNews::getIsTop); + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/statistics/HrtStatisticsService.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/statistics/HrtStatisticsService.java new file mode 100644 index 0000000..a24bc8f --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/statistics/HrtStatisticsService.java @@ -0,0 +1,50 @@ +package org.leocoder.heritage.admin.service.statistics; + +import org.leocoder.heritage.domain.model.vo.admin.HrtRankingVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtStatisticsVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtTrendVo; + +import java.util.List; + +/** + * @author Leocoder + * @description [统计分析服务接口] + */ +public interface HrtStatisticsService { + + /** + * @description [获取核心统计数据] + * @author Leocoder + */ + HrtStatisticsVo getStatistics(); + + /** + * @description [获取用户增长趋势] + * @author Leocoder + * @param days 天数(7或30) + */ + List getUserTrend(Integer days); + + /** + * @description [获取内容发布趋势] + * @author Leocoder + * @param days 天数(7或30) + */ + List getContentTrend(Integer days); + + /** + * @description [获取热门非遗项目排行榜] + * @author Leocoder + * @param type 排序类型:view-浏览量,favorite-收藏数 + * @param limit 返回数量,默认10 + */ + List getHeritageRanking(String type, Integer limit); + + /** + * @description [获取活跃用户排行榜] + * @author Leocoder + * @param limit 返回数量,默认10 + */ + List getActiveUserRanking(Integer limit); + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/statistics/HrtStatisticsServiceImpl.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/statistics/HrtStatisticsServiceImpl.java new file mode 100644 index 0000000..1278c3f --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/statistics/HrtStatisticsServiceImpl.java @@ -0,0 +1,393 @@ +package org.leocoder.heritage.admin.service.statistics; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.domain.model.vo.admin.HrtRankingVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtStatisticsVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtTrendVo; +import org.leocoder.heritage.domain.pojo.portal.*; +import org.leocoder.heritage.mybatisplus.mapper.portal.*; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Leocoder + * @description [统计分析服务实现类] + */ +@Service +@RequiredArgsConstructor +public class HrtStatisticsServiceImpl implements HrtStatisticsService { + + private final HrtHeritageMapper heritageMapper; + private final HrtInheritorMapper inheritorMapper; + private final HrtUserMapper userMapper; + private final HrtEventMapper eventMapper; + private final HrtNewsMapper newsMapper; + private final HrtCommentMapper commentMapper; + private final HrtLikeMapper likeMapper; + private final HrtFavoriteMapper favoriteMapper; + private final HrtViewHistoryMapper viewHistoryMapper; + private final HrtEventRegistrationMapper registrationMapper; + + /** + * @description [获取核心统计数据] + * @author Leocoder + */ + @Override + public HrtStatisticsVo getStatistics() { + HrtStatisticsVo vo = new HrtStatisticsVo(); + + // 获取今日开始时间 + LocalDateTime todayStart = LocalDate.now().atStartOfDay(); + + // ===== 核心数据统计 ===== + + // 非遗项目统计 + vo.setHeritageTotal(heritageMapper.selectCount(null)); + vo.setHeritageTodayCount(countTodayRecords(heritageMapper, todayStart)); + + // 传承人统计 + vo.setInheritorTotal(inheritorMapper.selectCount(null)); + vo.setInheritorTodayCount(countTodayRecords(inheritorMapper, todayStart)); + + // 用户统计 + vo.setUserTotal(userMapper.selectCount(null)); + vo.setUserTodayCount(countTodayRecords(userMapper, todayStart)); + + // 活动统计 + vo.setEventTotal(eventMapper.selectCount(null)); + vo.setEventTodayCount(countTodayRecords(eventMapper, todayStart)); + + // 新闻统计 + vo.setNewsTotal(newsMapper.selectCount(null)); + vo.setNewsTodayCount(countTodayRecords(newsMapper, todayStart)); + + // ===== 用户行为统计 ===== + + // 评论统计 + vo.setCommentTotal(commentMapper.selectCount(null)); + vo.setCommentTodayCount(countTodayRecords(commentMapper, todayStart)); + + // 待审核评论数 + LambdaQueryWrapper pendingWrapper = new LambdaQueryWrapper<>(); + pendingWrapper.eq(HrtComment::getStatus, 0); + vo.setCommentPendingCount(commentMapper.selectCount(pendingWrapper)); + + // 点赞统计 + vo.setLikeTotal(likeMapper.selectCount(null)); + vo.setLikeTodayCount(countTodayRecords(likeMapper, todayStart)); + + // 收藏统计 + vo.setFavoriteTotal(favoriteMapper.selectCount(null)); + vo.setFavoriteTodayCount(countTodayRecords(favoriteMapper, todayStart)); + + // 浏览统计 + vo.setViewTotal(viewHistoryMapper.selectCount(null)); + vo.setViewTodayCount(countTodayRecords(viewHistoryMapper, todayStart)); + + // 活动报名统计 + vo.setRegistrationTotal(registrationMapper.selectCount(null)); + vo.setRegistrationTodayCount(countTodayRecords(registrationMapper, todayStart)); + + return vo; + } + + /** + * @description [获取用户增长趋势] + * @author Leocoder + */ + @Override + public List getUserTrend(Integer days) { + if (days == null || (days != 7 && days != 30)) { + days = 7; + } + + List trendList = new ArrayList<>(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + for (int i = days - 1; i >= 0; i--) { + LocalDate date = LocalDate.now().minusDays(i); + LocalDateTime startTime = date.atStartOfDay(); + LocalDateTime endTime = date.atTime(LocalTime.MAX); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtUser::getCreateTime, startTime); + wrapper.le(HrtUser::getCreateTime, endTime); + + Long count = userMapper.selectCount(wrapper); + + HrtTrendVo trendVo = new HrtTrendVo(); + trendVo.setDate(date.format(formatter)); + trendVo.setValue(count); + trendList.add(trendVo); + } + + return trendList; + } + + /** + * @description [获取内容发布趋势] + * @author Leocoder + */ + @Override + public List getContentTrend(Integer days) { + if (days == null || (days != 7 && days != 30)) { + days = 7; + } + + List trendList = new ArrayList<>(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + for (int i = days - 1; i >= 0; i--) { + LocalDate date = LocalDate.now().minusDays(i); + LocalDateTime startTime = date.atStartOfDay(); + LocalDateTime endTime = date.atTime(LocalTime.MAX); + + // 统计当天发布的内容总数(非遗项目+新闻+活动) + Long heritageCount = countByDateRange(heritageMapper, startTime, endTime); + Long newsCount = countByDateRange(newsMapper, startTime, endTime); + Long eventCount = countByDateRange(eventMapper, startTime, endTime); + + Long totalCount = heritageCount + newsCount + eventCount; + + HrtTrendVo trendVo = new HrtTrendVo(); + trendVo.setDate(date.format(formatter)); + trendVo.setValue(totalCount); + trendList.add(trendVo); + } + + return trendList; + } + + /** + * @description [获取热门非遗项目排行榜] + * @author Leocoder + */ + @Override + public List getHeritageRanking(String type, Integer limit) { + if (limit == null || limit <= 0) { + limit = 10; + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(HrtHeritage::getPublishStatus, 1); + + // 根据类型排序 + if ("favorite".equals(type)) { + wrapper.orderByDesc(HrtHeritage::getFavoriteCount); + } else { + // 默认按浏览量排序 + wrapper.orderByDesc(HrtHeritage::getViewCount); + } + + wrapper.last("LIMIT " + limit); + + List heritageList = heritageMapper.selectList(wrapper); + + return heritageList.stream().map(heritage -> { + HrtRankingVo vo = new HrtRankingVo(); + vo.setId(heritage.getId()); + vo.setTitle(heritage.getName()); + vo.setType("heritage"); + vo.setCoverImage(heritage.getCoverImage()); + vo.setValue("favorite".equals(type) ? heritage.getFavoriteCount().longValue() : heritage.getViewCount().longValue()); + vo.setValueType("favorite".equals(type) ? "收藏数" : "浏览量"); + return vo; + }).collect(Collectors.toList()); + } + + /** + * @description [获取活跃用户排行榜] + * @author Leocoder + */ + @Override + public List getActiveUserRanking(Integer limit) { + if (limit == null || limit <= 0) { + limit = 10; + } + + // 根据用户的评论数、点赞数、收藏数综合计算活跃度 + List allUsers = userMapper.selectList(null); + + List rankingList = allUsers.stream().map(user -> { + // 统计用户的评论数 + LambdaQueryWrapper commentWrapper = new LambdaQueryWrapper<>(); + commentWrapper.eq(HrtComment::getUserId, user.getId()); + Long commentCount = commentMapper.selectCount(commentWrapper); + + // 统计用户的点赞数 + LambdaQueryWrapper likeWrapper = new LambdaQueryWrapper<>(); + likeWrapper.eq(HrtLike::getUserId, user.getId()); + Long likeCount = likeMapper.selectCount(likeWrapper); + + // 统计用户的收藏数 + LambdaQueryWrapper favoriteWrapper = new LambdaQueryWrapper<>(); + favoriteWrapper.eq(HrtFavorite::getUserId, user.getId()); + Long favoriteCount = favoriteMapper.selectCount(favoriteWrapper); + + // 计算活跃度分数(评论权重3,点赞权重1,收藏权重2) + Long activityScore = commentCount * 3 + likeCount + favoriteCount * 2; + + HrtRankingVo vo = new HrtRankingVo(); + vo.setId(user.getId()); + vo.setTitle(user.getNickname() != null ? user.getNickname() : user.getUsername()); + vo.setType("user"); + vo.setCoverImage(user.getAvatar()); + vo.setValue(activityScore); + vo.setValueType("活跃度"); + return vo; + }) + .sorted(Comparator.comparing(HrtRankingVo::getValue).reversed()) + .limit(limit) + .collect(Collectors.toList()); + + // 设置排名 + for (int i = 0; i < rankingList.size(); i++) { + rankingList.get(i).setRank(i + 1); + } + + return rankingList; + } + + /** + * @description [统计今日新增非遗项目] + * @author Leocoder + */ + private Long countTodayRecords(HrtHeritageMapper mapper, LocalDateTime todayStart) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtHeritage::getCreateTime, todayStart); + return mapper.selectCount(wrapper); + } + + /** + * @description [统计今日新增传承人] + * @author Leocoder + */ + private Long countTodayRecords(HrtInheritorMapper mapper, LocalDateTime todayStart) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtInheritor::getCreateTime, todayStart); + return mapper.selectCount(wrapper); + } + + /** + * @description [统计今日新增用户] + * @author Leocoder + */ + private Long countTodayRecords(HrtUserMapper mapper, LocalDateTime todayStart) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtUser::getCreateTime, todayStart); + return mapper.selectCount(wrapper); + } + + /** + * @description [统计今日新增活动] + * @author Leocoder + */ + private Long countTodayRecords(HrtEventMapper mapper, LocalDateTime todayStart) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtEvent::getCreateTime, todayStart); + return mapper.selectCount(wrapper); + } + + /** + * @description [统计今日新增新闻] + * @author Leocoder + */ + private Long countTodayRecords(HrtNewsMapper mapper, LocalDateTime todayStart) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtNews::getCreateTime, todayStart); + return mapper.selectCount(wrapper); + } + + /** + * @description [统计今日新增评论] + * @author Leocoder + */ + private Long countTodayRecords(HrtCommentMapper mapper, LocalDateTime todayStart) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtComment::getCreateTime, todayStart); + return mapper.selectCount(wrapper); + } + + /** + * @description [统计今日新增点赞] + * @author Leocoder + */ + private Long countTodayRecords(HrtLikeMapper mapper, LocalDateTime todayStart) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtLike::getCreateTime, todayStart); + return mapper.selectCount(wrapper); + } + + /** + * @description [统计今日新增收藏] + * @author Leocoder + */ + private Long countTodayRecords(HrtFavoriteMapper mapper, LocalDateTime todayStart) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtFavorite::getCreateTime, todayStart); + return mapper.selectCount(wrapper); + } + + /** + * @description [统计今日新增浏览] + * @author Leocoder + */ + private Long countTodayRecords(HrtViewHistoryMapper mapper, LocalDateTime todayStart) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtViewHistory::getCreateTime, todayStart); + return mapper.selectCount(wrapper); + } + + /** + * @description [统计今日新增报名] + * @author Leocoder + */ + private Long countTodayRecords(HrtEventRegistrationMapper mapper, LocalDateTime todayStart) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtEventRegistration::getCreateTime, todayStart); + return mapper.selectCount(wrapper); + } + + /** + * @description [按日期范围统计非遗项目] + * @author Leocoder + */ + private Long countByDateRange(HrtHeritageMapper mapper, LocalDateTime startTime, LocalDateTime endTime) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtHeritage::getCreateTime, startTime); + wrapper.le(HrtHeritage::getCreateTime, endTime); + return mapper.selectCount(wrapper); + } + + /** + * @description [按日期范围统计新闻] + * @author Leocoder + */ + private Long countByDateRange(HrtNewsMapper mapper, LocalDateTime startTime, LocalDateTime endTime) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtNews::getCreateTime, startTime); + wrapper.le(HrtNews::getCreateTime, endTime); + return mapper.selectCount(wrapper); + } + + /** + * @description [按日期范围统计活动] + * @author Leocoder + */ + private Long countByDateRange(HrtEventMapper mapper, LocalDateTime startTime, LocalDateTime endTime) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(HrtEvent::getCreateTime, startTime); + wrapper.le(HrtEvent::getCreateTime, endTime); + return mapper.selectCount(wrapper); + } + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/user/HrtUserAdminService.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/user/HrtUserAdminService.java new file mode 100644 index 0000000..7c00a4d --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/user/HrtUserAdminService.java @@ -0,0 +1,65 @@ +package org.leocoder.heritage.admin.service.user; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.leocoder.heritage.domain.model.bo.admin.HrtUserAdminBo; +import org.leocoder.heritage.domain.model.bo.admin.HrtUserAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtUserAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtUserAdminVo; + +import java.util.List; + +/** + * @author Leocoder + * @description [前台用户管理服务接口] + */ +public interface HrtUserAdminService { + + /** + * @description [分页查询用户列表(后台管理)] + * @author Leocoder + */ + IPage listPage(HrtUserAdminQueryBo bo); + + /** + * @description [查看用户详情(后台管理)] + * @author Leocoder + */ + HrtUserAdminDetailVo getDetail(Long id); + + /** + * @description [新增用户] + * @author Leocoder + */ + Long addUser(HrtUserAdminBo bo); + + /** + * @description [编辑用户] + * @author Leocoder + */ + Boolean editUser(HrtUserAdminBo bo); + + /** + * @description [删除用户] + * @author Leocoder + */ + Boolean deleteUser(Long id); + + /** + * @description [批量删除用户] + * @author Leocoder + */ + Boolean batchDelete(List ids); + + /** + * @description [修改用户状态] + * @author Leocoder + */ + Boolean changeStatus(Long id, Integer status); + + /** + * @description [重置用户密码] + * @author Leocoder + */ + Boolean resetPassword(Long id, String newPassword); + +} diff --git a/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/user/HrtUserAdminServiceImpl.java b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/user/HrtUserAdminServiceImpl.java new file mode 100644 index 0000000..272f73b --- /dev/null +++ b/heritage-modules/heritage-admin/src/main/java/org/leocoder/heritage/admin/service/user/HrtUserAdminServiceImpl.java @@ -0,0 +1,351 @@ +package org.leocoder.heritage.admin.service.user; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.leocoder.heritage.common.exception.BusinessException; +import org.leocoder.heritage.domain.model.bo.admin.HrtUserAdminBo; +import org.leocoder.heritage.satoken.config.CoderSaTokenPasswordUtil; +import org.leocoder.heritage.domain.model.bo.admin.HrtUserAdminQueryBo; +import org.leocoder.heritage.domain.model.vo.admin.HrtUserAdminDetailVo; +import org.leocoder.heritage.domain.model.vo.admin.HrtUserAdminVo; +import org.leocoder.heritage.domain.pojo.portal.HrtUser; +import org.leocoder.heritage.mybatisplus.mapper.portal.HrtUserMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Objects; + +/** + * @author Leocoder + * @description [前台用户管理服务实现类] + */ +@Service +@RequiredArgsConstructor +public class HrtUserAdminServiceImpl implements HrtUserAdminService { + + private final HrtUserMapper userMapper; + + /** + * @description [分页查询用户列表(后台管理)] + * @author Leocoder + */ + @Override + public IPage listPage(HrtUserAdminQueryBo bo) { + Page page = new Page<>(bo.getPageNum(), bo.getPageSize()); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 用户名模糊查询 + wrapper.like(StrUtil.isNotEmpty(bo.getUsername()), HrtUser::getUsername, bo.getUsername()); + + // 昵称模糊查询 + wrapper.like(StrUtil.isNotEmpty(bo.getNickname()), HrtUser::getNickname, bo.getNickname()); + + // 手机号精确查询 + wrapper.eq(StrUtil.isNotEmpty(bo.getPhone()), HrtUser::getPhone, bo.getPhone()); + + // 邮箱精确查询 + wrapper.eq(StrUtil.isNotEmpty(bo.getEmail()), HrtUser::getEmail, bo.getEmail()); + + // 状态查询 + wrapper.eq(Objects.nonNull(bo.getStatus()), HrtUser::getStatus, bo.getStatus()); + + // 性别查询 + wrapper.eq(Objects.nonNull(bo.getGender()), HrtUser::getGender, bo.getGender()); + + // 省份查询 + wrapper.eq(StrUtil.isNotEmpty(bo.getProvince()), HrtUser::getProvince, bo.getProvince()); + + // 城市查询 + wrapper.eq(StrUtil.isNotEmpty(bo.getCity()), HrtUser::getCity, bo.getCity()); + + // 关键词搜索(用户名、昵称、手机号、邮箱) + if (StrUtil.isNotEmpty(bo.getKeyword())) { + wrapper.and(w -> w + .like(HrtUser::getUsername, bo.getKeyword()) + .or() + .like(HrtUser::getNickname, bo.getKeyword()) + .or() + .like(HrtUser::getPhone, bo.getKeyword()) + .or() + .like(HrtUser::getEmail, bo.getKeyword()) + ); + } + + // 注册时间范围查询 + wrapper.ge(Objects.nonNull(bo.getCreateTimeBegin()), HrtUser::getCreateTime, bo.getCreateTimeBegin()); + wrapper.le(Objects.nonNull(bo.getCreateTimeEnd()), HrtUser::getCreateTime, bo.getCreateTimeEnd()); + + // 排序处理 + if ("asc".equalsIgnoreCase(bo.getSortOrder())) { + if ("login_time".equals(bo.getSortField())) { + wrapper.orderByAsc(HrtUser::getLoginTime); + } else { + wrapper.orderByAsc(HrtUser::getCreateTime); + } + } else { + if ("login_time".equals(bo.getSortField())) { + wrapper.orderByDesc(HrtUser::getLoginTime); + } else { + wrapper.orderByDesc(HrtUser::getCreateTime); + } + } + + IPage userPage = userMapper.selectPage(page, wrapper); + return userPage.convert(user -> BeanUtil.copyProperties(user, HrtUserAdminVo.class)); + } + + /** + * @description [查看用户详情(后台管理)] + * @author Leocoder + */ + @Override + public HrtUserAdminDetailVo getDetail(Long id) { + HrtUser user = userMapper.selectById(id); + if (user == null) { + throw new BusinessException(404, "用户不存在"); + } + return BeanUtil.copyProperties(user, HrtUserAdminDetailVo.class); + } + + /** + * @description [新增用户] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long addUser(HrtUserAdminBo bo) { + // 验证用户名是否已存在 + checkUsernameExists(bo.getUsername(), null); + + // 验证手机号是否已存在 + if (StrUtil.isNotEmpty(bo.getPhone())) { + checkPhoneExists(bo.getPhone(), null); + } + + // 验证邮箱是否已存在 + if (StrUtil.isNotEmpty(bo.getEmail())) { + checkEmailExists(bo.getEmail(), null); + } + + // 密码必填校验 + if (StrUtil.isEmpty(bo.getPassword())) { + throw new BusinessException(400, "密码不能为空"); + } + + HrtUser user = BeanUtil.copyProperties(bo, HrtUser.class); + + // 处理空字符串转null,避免唯一索引冲突 + if (StrUtil.isEmpty(bo.getEmail())) { + user.setEmail(null); + } + if (StrUtil.isEmpty(bo.getPhone())) { + user.setPhone(null); + } + + // 加密密码 - 使用统一的密码加密工具 + String encryptedPassword = CoderSaTokenPasswordUtil.encryptPassword(bo.getPassword()); + user.setPassword(encryptedPassword); + + // 初始化状态为正常 + if (user.getStatus() == null) { + user.setStatus(1); + } + + int result = userMapper.insert(user); + if (result <= 0) { + throw new BusinessException(500, "新增用户失败"); + } + + return user.getId(); + } + + /** + * @description [编辑用户] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean editUser(HrtUserAdminBo bo) { + if (bo.getId() == null) { + throw new BusinessException(400, "用户ID不能为空"); + } + + // 检查用户是否存在 + HrtUser existUser = userMapper.selectById(bo.getId()); + if (existUser == null) { + throw new BusinessException(404, "用户不存在"); + } + + // 验证用户名是否已被其他用户使用 + checkUsernameExists(bo.getUsername(), bo.getId()); + + // 验证手机号是否已被其他用户使用 + if (StrUtil.isNotEmpty(bo.getPhone())) { + checkPhoneExists(bo.getPhone(), bo.getId()); + } + + // 验证邮箱是否已被其他用户使用 + if (StrUtil.isNotEmpty(bo.getEmail())) { + checkEmailExists(bo.getEmail(), bo.getId()); + } + + HrtUser user = BeanUtil.copyProperties(bo, HrtUser.class); + + // 处理空字符串转null,避免唯一索引冲突 + if (StrUtil.isEmpty(bo.getEmail())) { + user.setEmail(null); + } + if (StrUtil.isEmpty(bo.getPhone())) { + user.setPhone(null); + } + + // 编辑用户时不修改密码,密码修改请使用专门的重置密码接口 + user.setPassword(null); + + int result = userMapper.updateById(user); + return result > 0; + } + + /** + * @description [删除用户] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteUser(Long id) { + // 检查用户是否存在 + HrtUser user = userMapper.selectById(id); + if (user == null) { + throw new BusinessException(404, "用户不存在"); + } + + int result = userMapper.deleteById(id); + return result > 0; + } + + /** + * @description [批量删除用户] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchDelete(List ids) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException(400, "请选择要删除的用户"); + } + + int result = userMapper.deleteBatchIds(ids); + return result > 0; + } + + /** + * @description [修改用户状态] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean changeStatus(Long id, Integer status) { + // 检查用户是否存在 + HrtUser user = userMapper.selectById(id); + if (user == null) { + throw new BusinessException(404, "用户不存在"); + } + + if (status == null || (status != 0 && status != 1)) { + throw new BusinessException(400, "用户状态参数错误"); + } + + HrtUser updateUser = new HrtUser(); + updateUser.setId(id); + updateUser.setStatus(status); + + int result = userMapper.updateById(updateUser); + return result > 0; + } + + /** + * @description [重置用户密码] + * @author Leocoder + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean resetPassword(Long id, String newPassword) { + // 检查用户是否存在 + HrtUser user = userMapper.selectById(id); + if (user == null) { + throw new BusinessException(404, "用户不存在"); + } + + if (StrUtil.isEmpty(newPassword)) { + throw new BusinessException(400, "新密码不能为空"); + } + + // 加密密码 - 使用统一的密码加密工具 + String encryptedPassword = CoderSaTokenPasswordUtil.encryptPassword(newPassword); + + HrtUser updateUser = new HrtUser(); + updateUser.setId(id); + updateUser.setPassword(encryptedPassword); + + int result = userMapper.updateById(updateUser); + return result > 0; + } + + /** + * @description [检查用户名是否已存在] + * @author Leocoder + */ + private void checkUsernameExists(String username, Long excludeId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(HrtUser::getUsername, username); + if (excludeId != null) { + wrapper.ne(HrtUser::getId, excludeId); + } + + Long count = userMapper.selectCount(wrapper); + if (count > 0) { + throw new BusinessException(400, "用户名已存在"); + } + } + + /** + * @description [检查手机号是否已存在] + * @author Leocoder + */ + private void checkPhoneExists(String phone, Long excludeId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(HrtUser::getPhone, phone); + if (excludeId != null) { + wrapper.ne(HrtUser::getId, excludeId); + } + + Long count = userMapper.selectCount(wrapper); + if (count > 0) { + throw new BusinessException(400, "手机号已存在"); + } + } + + /** + * @description [检查邮箱是否已存在] + * @author Leocoder + */ + private void checkEmailExists(String email, Long excludeId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(HrtUser::getEmail, email); + if (excludeId != null) { + wrapper.ne(HrtUser::getId, excludeId); + } + + Long count = userMapper.selectCount(wrapper); + if (count > 0) { + throw new BusinessException(400, "邮箱已存在"); + } + } + +}