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, "邮箱已存在");
+ }
+ }
+
+}