Browse Source

feat: 增加小程序栏目管理后台接口

luofeiyun 1 month ago
parent
commit
11b4780efc
33 changed files with 2486 additions and 0 deletions
  1. 138 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/CatalogueController.java
  2. 34 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/detail/CatalogueDetailPageReqVO.java
  3. 30 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/detail/CatalogueDetailRespVO.java
  4. 22 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/detail/CatalogueDetailSaveReqVO.java
  5. 67 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/vo/CataloguePageReqVO.java
  6. 114 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/vo/CatalogueRespVO.java
  7. 76 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/vo/CatalogueSaveReqVO.java
  8. 29 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/vo/CatalogueSimpleRespVO.java
  9. 156 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/content/ContentController.java
  10. 59 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/content/vo/ContentPageReqVO.java
  11. 74 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/content/vo/ContentRespVO.java
  12. 48 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/content/vo/ContentSaveReqVO.java
  13. 21 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/content/vo/ContentSimpleRespVO.java
  14. 298 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/app/content/AppContentController.java
  15. 22 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/app/content/vo/AppCatalogueRespVO.java
  16. 60 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/app/content/vo/AppCategoryRespVO.java
  17. 57 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/app/content/vo/AppContentReqVO.java
  18. 58 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/app/content/vo/AppContentRespVO.java
  19. 38 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/convert/catalogue/CatalogueConvert.java
  20. 109 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/dataobject/catalogue/CatalogueDO.java
  21. 42 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/dataobject/cataloguedetail/CatalogueDetailDO.java
  22. 79 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/dataobject/content/ContentDO.java
  23. 54 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/dto/CategoryExtraRespDTO.java
  24. 82 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/mysql/catalogue/CatalogueMapper.java
  25. 35 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/mysql/cataloguedetail/CatalogueDetailMapper.java
  26. 48 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/mysql/content/ContentMapper.java
  27. 16 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/enums/ErrorCodeConstants.java
  28. 91 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/catalogue/CatalogueService.java
  29. 230 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/catalogue/CatalogueServiceImpl.java
  30. 65 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/cataloguedetail/CatalogueDetailService.java
  31. 81 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/cataloguedetail/CatalogueDetailServiceImpl.java
  32. 66 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/content/ContentService.java
  33. 87 0
      ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/content/ContentServiceImpl.java

+ 138 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/CatalogueController.java

@@ -0,0 +1,138 @@
+package com.yc.ship.module.miniapplet.controller.admin.catalogue;
+
+import com.yc.ship.framework.common.pojo.CommonResult;
+import com.yc.ship.framework.common.pojo.PageParam;
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.framework.common.util.collection.CollectionUtils;
+import com.yc.ship.framework.common.util.object.BeanUtils;
+import com.yc.ship.framework.excel.core.util.ExcelUtils;
+import com.yc.ship.framework.operatelog.core.annotations.OperateLog;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailRespVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CataloguePageReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CatalogueRespVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CatalogueSaveReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CatalogueSimpleRespVO;
+import com.yc.ship.module.miniapplet.convert.catalogue.CatalogueConvert;
+import com.yc.ship.module.miniapplet.dal.dataobject.catalogue.CatalogueDO;
+import com.yc.ship.module.miniapplet.service.catalogue.CatalogueService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static com.yc.ship.framework.common.pojo.CommonResult.success;
+import static com.yc.ship.framework.common.util.collection.CollectionUtils.convertList;
+import static com.yc.ship.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
+
+@Tag(name = "管理后台 - 目录类型")
+@RestController
+@RequestMapping("/catalogue")
+@Validated
+public class CatalogueController {
+
+    @Resource
+    private CatalogueService catalogueService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建")
+    @PreAuthorize("@ss.hasPermission('applet:catalogue:create')")
+    public CommonResult<Long> createCatalogue(@Valid @RequestBody CatalogueSaveReqVO createReqVO) {
+        return success(catalogueService.createCatalogue(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新")
+    @PreAuthorize("@ss.hasPermission('applet:catalogue:update')")
+    public CommonResult<Boolean> updateCatalogue(@Valid @RequestBody CatalogueSaveReqVO updateReqVO) {
+        catalogueService.updateCatalogue(updateReqVO);
+        return success(true);
+    }
+
+    @PutMapping("/updateDetails")
+    @Operation(summary = "更新产品详情排序")
+    @PreAuthorize("@ss.hasPermission('applet:catalogue:update')")
+    public CommonResult<Boolean> updateCatalogueDetails(@Valid @RequestBody CatalogueSaveReqVO updateReqVO) {
+        catalogueService.updateCatalogueDetails(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('applet:catalogue:delete')")
+    public CommonResult<Boolean> deleteCatalogue(@RequestParam("id") Long id) {
+        catalogueService.deleteCatalogue(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('applet:catalogue:query')")
+    public CommonResult<CatalogueRespVO> getCatalogue(@RequestParam("id") Long id, @RequestParam("level") String level) {
+//        CatalogueDO catalogue = catalogueService.getCatalogue(id);
+//        CatalogueRespVO respVO = BeanUtils.toBean(catalogue, CatalogueRespVO.class);
+//        if (respVO.getBusinessType() == 1) {
+//            respVO.setDetailsList(Arrays.asList(catalogue.getDetails().split(",")));
+//        }
+        CatalogueRespVO respVO = catalogueService.getCatalogueResp(id);
+        if (StringUtils.equals("1", level)) {
+            // 产品排序,先初始化数据
+            List<CatalogueDetailRespVO> detailRespVOS = catalogueService.initCatalogueDetails(id);
+            respVO.setDetailRespVOS(detailRespVOS);
+        }
+        return success(respVO);
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得分页")
+    @PreAuthorize("@ss.hasPermission('applet:catalogue:query')")
+    public CommonResult<PageResult<CatalogueRespVO>> getCataloguePage(@Valid CataloguePageReqVO pageReqVO) {
+        PageResult<CatalogueDO> pageResult = catalogueService.getCataloguePage(pageReqVO);
+        List<CatalogueDO> catalogueList = catalogueService.getCatalogueList(convertList(pageResult.getList(), CatalogueDO::getParentId));
+        Map<Long, CatalogueDO> catalogueMap = CollectionUtils.convertMap(catalogueList, CatalogueDO::getId);
+        return success(new PageResult<>(CatalogueConvert.INSTANCE.convertCatalogueList(pageResult.getList(), catalogueMap), pageResult.getTotal()));
+//        return success(BeanUtils.toBean(pageResult, CatalogueRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出 Excel")
+    @PreAuthorize("@ss.hasPermission('applet:catalogue:export')")
+    @OperateLog(type = EXPORT,enable = false)
+    public void exportCatalogueExcel(@Valid CataloguePageReqVO pageReqVO, HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<CatalogueDO> list = catalogueService.getCataloguePage(pageReqVO).getList();
+        List<CatalogueDO> catalogueList = catalogueService.getCatalogueList(convertList(list, CatalogueDO::getParentId));
+        Map<Long, CatalogueDO> catalogueMap = CollectionUtils.convertMap(catalogueList, CatalogueDO::getId);
+        List<CatalogueRespVO> respList = CatalogueConvert.INSTANCE.convertCatalogueList(list, catalogueMap);
+        // 导出 Excel
+        ExcelUtils.write(response, "目录类型.xls", "数据", CatalogueRespVO.class, respList);
+    }
+
+    @GetMapping({"/getCatalogueTree"})
+    @Operation(summary = "获取目录类型树", description = "只包含被启用的目录类型,主要用于前端的下拉选项")
+    public CommonResult<List<Map<String, Object>>> getCatalogueTree(@RequestParam("businessType") Integer businessType, @RequestParam("type") String type) {
+        type = StringUtils.isBlank(type) ? "" : type;
+        businessType = businessType == null ? 0 : businessType;
+        List<Map<String, Object>> list = catalogueService.getCatalogueTree(businessType, type);
+        return success(list);
+    }
+
+    @GetMapping({"/list-all-simple", "/simple-list"})
+    @Operation(summary = "获取目录类型精简信息列表", description = "只包含被开启的目录类型,主要用于前端的下拉选项")
+    public CommonResult<List<CatalogueSimpleRespVO>> getSimpleCatalogueList(@RequestParam("businessType") Integer businessType, @RequestParam("status") Integer status) {
+        List<CatalogueDO> list = catalogueService.getCatalogueListByStatus(businessType == null ? 0 : businessType, status == null ? 1 : status);
+        return success(BeanUtils.toBean(list, CatalogueSimpleRespVO.class));
+    }
+
+}

+ 34 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/detail/CatalogueDetailPageReqVO.java

@@ -0,0 +1,34 @@
+package com.yc.ship.module.miniapplet.controller.admin.catalogue.detail;
+
+import com.yc.ship.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.yc.ship.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+
+@Schema(description = "管理后台 - 栏目产品排序分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CatalogueDetailPageReqVO extends PageParam {
+
+    @Schema(description = "栏目ID", example = "19032")
+    private Long catalogueId;
+
+    @Schema(description = "小程序产品ID", example = "19032")
+    private Long categoryId;
+
+    @Schema(description = "排序号")
+    private Integer sortNum;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 30 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/detail/CatalogueDetailRespVO.java

@@ -0,0 +1,30 @@
+package com.yc.ship.module.miniapplet.controller.admin.catalogue.detail;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 栏目产品排序 Response VO")
+@Data
+public class CatalogueDetailRespVO {
+
+    @Schema(description = "栏目产品排序ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2750")
+    private Long id;
+
+    @Schema(description = "栏目ID", example = "19032")
+    private Long catalogueId;
+
+    @Schema(description = "小程序产品ID", example = "19032")
+    private Long categoryId;
+
+    @Schema(description = "小程序产品名称", example = "19032")
+    private String categoryName;
+
+    @Schema(description = "排序号")
+    private Integer sortNum;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+
+}

+ 22 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/detail/CatalogueDetailSaveReqVO.java

@@ -0,0 +1,22 @@
+package com.yc.ship.module.miniapplet.controller.admin.catalogue.detail;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 栏目产品排序新增/修改 Request VO")
+@Data
+public class CatalogueDetailSaveReqVO {
+
+    @Schema(description = "栏目产品排序ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2750")
+    private Long id;
+
+    @Schema(description = "栏目ID", example = "19032")
+    private Long catalogueId;
+
+    @Schema(description = "小程序产品ID", example = "19032")
+    private Long categoryId;
+
+    @Schema(description = "排序号")
+    private Integer sortNum;
+
+}

+ 67 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/vo/CataloguePageReqVO.java

@@ -0,0 +1,67 @@
+package com.yc.ship.module.miniapplet.controller.admin.catalogue.vo;
+
+import com.yc.ship.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.yc.ship.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+
+@Schema(description = "管理后台 - 目录类型分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CataloguePageReqVO extends PageParam {
+
+    @Schema(description = "目录名称", example = "芋艿")
+    private String name;
+
+    @Schema(description = "目录编码")
+    private String code;
+
+    @Schema(description = "业务类型 0.目录 1.栏目")
+    private Integer businessType;
+
+    @Schema(description = "目录类型 1.列表 2.内容 3.外部链接", example = "2")
+    private Integer type;
+
+    @Schema(description = "父级目录", example = "5315")
+    private Long parentId;
+
+    @Schema(description = "目录副标题")
+    private String title;
+
+    @Schema(description = "目录Icon图标")
+    private String logo;
+
+    @Schema(description = "外部链接", example = "https://www.iocoder.cn")
+    private String url;
+
+    @Schema(description = "启用状态 0否 1是")
+    private Integer isUse;
+
+    @Schema(description = "是否允许评论 0否 1是")
+    private Integer isComment;
+
+    @Schema(description = "允许用户发布 0否 1是")
+    private Integer isRelease;
+
+    @Schema(description = "用户发布审核 0否 1是")
+    private Integer isAudit;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "排序号")
+    private Integer sortNum;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 114 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/vo/CatalogueRespVO.java

@@ -0,0 +1,114 @@
+package com.yc.ship.module.miniapplet.controller.admin.catalogue.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.yc.ship.framework.excel.core.annotations.DictFormat;
+import com.yc.ship.framework.excel.core.convert.DictConvert;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailRespVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - 目录类型 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class CatalogueRespVO {
+
+    @Schema(description = "目录类型主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "27482")
+    @ExcelProperty("目录类型主键ID")
+    private Long id;
+
+    @Schema(description = "目录名称", example = "芋艿")
+    @ExcelProperty("目录名称")
+    private String name;
+
+    @Schema(description = "目录编码")
+    @ExcelProperty("目录编码")
+    private String code;
+
+    @Schema(description = "业务类型")
+    @ExcelProperty("业务类型")
+    private Integer businessType;
+
+    @Schema(description = "目录类型 1.列表 2.内容 3.外部链接", example = "2")
+    @ExcelProperty(value = "目录类型", converter = DictConvert.class)
+    @DictFormat("catalogue_type") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+    private Integer type;
+
+    @Schema(description = "父级目录", example = "5315")
+//    @ExcelProperty("父级目录")
+    private Long parentId;
+
+    @Schema(description = "父级目录名称", example = "5315")
+    @ExcelProperty("父级目录名称")
+    private String parentName;
+
+    @Schema(description = "目录副标题")
+    @ExcelProperty("目录副标题")
+    private String title;
+
+    @Schema(description = "目录Icon图标")
+    @ExcelProperty("Icon图标")
+    private String logo;
+
+    @Schema(description = "图片地址")
+    @ExcelProperty("头部图片")
+    private String imgUrl;
+
+    @Schema(description = "外部链接", example = "https://www.iocoder.cn")
+    @ExcelProperty("外部链接")
+    private String url;
+
+    @Schema(description = "启用状态 0否 1是")
+    @ExcelProperty(value = "启用状态", converter = DictConvert.class)
+    @DictFormat("use_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+    private Integer isUse;
+
+    @Schema(description = "是否允许评论 0否 1是")
+    @ExcelProperty("是否允许评论")
+    @DictFormat("yes_no")
+    private Integer isComment;
+
+    @Schema(description = "允许用户发布 0否 1是")
+    @ExcelProperty("允许用户发布")
+    @DictFormat("yes_no")
+    private Integer isRelease;
+
+    @Schema(description = "用户发布审核 0否 1是")
+    @ExcelProperty("用户发布审核")
+    @DictFormat("yes_no")
+    private Integer isAudit;
+
+    @Schema(description = "是否栏目推荐 0否 1是")
+    @ExcelProperty("是否栏目推荐")
+    @DictFormat("yes_no")
+    private Integer isRecommend;
+
+    @Schema(description = "展示类型 0.产品 1.子产品")
+    private Integer showType;
+
+    @Schema(description = "列表页设置")
+    @ExcelProperty("列表页设置")
+    private List<String> detailsList;
+
+    @Schema(description = "备注", example = "随便")
+    @ExcelProperty("备注")
+    private String remark;
+
+    @Schema(description = "排序号")
+    @ExcelProperty("排序号")
+    private Integer sortNum;
+
+    @Schema(description = "创建时间")
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+    @Schema(description = "头部图片地址")
+    private String headerVideo;
+
+
+    @Schema(description = "产品排序", example = "")
+    private List<CatalogueDetailRespVO> detailRespVOS;
+}

+ 76 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/vo/CatalogueSaveReqVO.java

@@ -0,0 +1,76 @@
+package com.yc.ship.module.miniapplet.controller.admin.catalogue.vo;
+
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailSaveReqVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "管理后台 - 目录类型新增/修改 Request VO")
+@Data
+public class CatalogueSaveReqVO {
+
+    @Schema(description = "目录类型主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "27482")
+    private Long id;
+
+    @Schema(description = "目录名称", example = "芋艿")
+    private String name;
+
+    @Schema(description = "目录编码")
+    private String code;
+
+    @Schema(description = "业务类型 0.目录 1.栏目 2.分类")
+    private Integer businessType;
+
+    @Schema(description = "目录类型 1.列表 2.内容 3.外部链接", example = "2")
+    private Integer type;
+
+    @Schema(description = "父级目录", example = "5315")
+    private Long parentId;
+
+    @Schema(description = "目录副标题")
+    private String title;
+
+    @Schema(description = "目录Icon图标")
+    private String logo;
+
+    @Schema(description = "图片地址")
+    private String imgUrl;
+
+    @Schema(description = "外部链接", example = "https://www.iocoder.cn")
+    private String url;
+
+    @Schema(description = "启用状态 0否 1是")
+    private Integer isUse;
+
+    @Schema(description = "是否允许评论 0否 1是")
+    private Integer isComment;
+
+    @Schema(description = "允许用户发布 0否 1是")
+    private Integer isRelease;
+
+    @Schema(description = "用户发布审核 0否 1是")
+    private Integer isAudit;
+
+    @Schema(description = "是否栏目推荐 0否 1是")
+    private Integer isRecommend;
+
+    @Schema(description = "展示类型 0.产品 1.子产品")
+    private Integer showType;
+
+    @Schema(description = "列表页设置")
+    private List<String> detailsList;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "排序号")
+    private Integer sortNum;
+
+    @Schema(description = "头部图片地址")
+    private String headerVideo;
+
+
+    @Schema(description = "产品排序", example = "")
+    private List<CatalogueDetailSaveReqVO> detailRespVOS;
+}

+ 29 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/catalogue/vo/CatalogueSimpleRespVO.java

@@ -0,0 +1,29 @@
+package com.yc.ship.module.miniapplet.controller.admin.catalogue.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.yc.ship.framework.excel.core.annotations.DictFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 目录类型 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class CatalogueSimpleRespVO {
+
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "27482")
+    private Long id;
+
+    @Schema(description = "目录名称", example = "芋艿")
+    private String name;
+
+    @Schema(description = "目录编码")
+    private String code;
+
+    @Schema(description = "目录类型", example = "2")
+    @DictFormat("catalogue_type") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+    private Integer type;
+
+    @Schema(description = "父级目录", example = "5315")
+    private Long parentId;
+
+}

+ 156 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/content/ContentController.java

@@ -0,0 +1,156 @@
+package com.yc.ship.module.miniapplet.controller.admin.content;
+
+import com.yc.ship.framework.common.pojo.CommonResult;
+import com.yc.ship.framework.common.pojo.PageParam;
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.framework.common.util.collection.CollectionUtils;
+import com.yc.ship.framework.common.util.object.BeanUtils;
+import com.yc.ship.framework.excel.core.util.ExcelUtils;
+import com.yc.ship.framework.operatelog.core.annotations.OperateLog;
+import com.yc.ship.module.miniapplet.controller.admin.content.vo.ContentPageReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.content.vo.ContentRespVO;
+import com.yc.ship.module.miniapplet.controller.admin.content.vo.ContentSaveReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.content.vo.ContentSimpleRespVO;
+import com.yc.ship.module.miniapplet.convert.catalogue.CatalogueConvert;
+import com.yc.ship.module.miniapplet.dal.dataobject.catalogue.CatalogueDO;
+import com.yc.ship.module.miniapplet.dal.dataobject.content.ContentDO;
+import com.yc.ship.module.miniapplet.service.catalogue.CatalogueService;
+import com.yc.ship.module.miniapplet.service.content.ContentService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.yc.ship.framework.common.pojo.CommonResult.success;
+import static com.yc.ship.framework.common.util.collection.CollectionUtils.convertList;
+import static com.yc.ship.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
+import static com.yc.ship.module.miniapplet.enums.ErrorCodeConstants.*;
+
+
+@Tag(name = "管理后台 - 目录内容")
+@RestController
+@RequestMapping("/content")
+@Validated
+public class ContentController {
+
+    @Resource
+    private CatalogueService catalogueService;
+
+    @Resource
+    private ContentService contentService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建")
+    @PreAuthorize("@ss.hasPermission('applet:content:create')")
+    public CommonResult<Long> createContent(@Valid @RequestBody ContentSaveReqVO createReqVO) {
+        verifyCatalogue(null, createReqVO.getCatalogueId());
+        return success(contentService.createContent(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新")
+    @PreAuthorize("@ss.hasAnyPermissions('applet:content:update','applet:content:withdraw')")
+    public CommonResult<Boolean> updateContent(@Valid @RequestBody ContentSaveReqVO updateReqVO) {
+        verifyCatalogue(updateReqVO.getId(), updateReqVO.getCatalogueId());
+        contentService.updateContent(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('applet:content:delete')")
+    public CommonResult<Boolean> deleteContent(@RequestParam("id") Long id) {
+        contentService.deleteContent(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('applet:content:query')")
+    public CommonResult<ContentRespVO> getContent(@RequestParam("id") Long id) {
+        ContentDO content = contentService.getContent(id);
+        return success(BeanUtils.toBean(content, ContentRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得分页")
+    @PreAuthorize("@ss.hasPermission('applet:content:query')")
+    public CommonResult<PageResult<ContentRespVO>> getContentPage(@Valid ContentPageReqVO pageReqVO) {
+        PageResult<ContentDO> pageResult = contentService.getContentPage(pageReqVO);
+        List<CatalogueDO> catalogueList = catalogueService.getCatalogueList(convertList(pageResult.getList(), ContentDO::getCatalogueId));
+        Map<Long, CatalogueDO> catalogueMap = CollectionUtils.convertMap(catalogueList, CatalogueDO::getId);
+        return success(new PageResult<>(CatalogueConvert.INSTANCE.convertContentList(pageResult.getList(), catalogueMap),
+                pageResult.getTotal()));
+//        return success(BeanUtils.toBean(pageResult, ContentRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出 Excel")
+    @PreAuthorize("@ss.hasPermission('applet:content:export')")
+    @OperateLog(type = EXPORT,enable = false)
+    public void exportContentExcel(@Valid ContentPageReqVO pageReqVO, HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<ContentDO> list = contentService.getContentPage(pageReqVO).getList();
+        List<CatalogueDO> catalogueList = catalogueService.getCatalogueList(convertList(list, ContentDO::getCatalogueId));
+        Map<Long, CatalogueDO> catalogueMap = CollectionUtils.convertMap(catalogueList, CatalogueDO::getId);
+        List<ContentRespVO> respList = CatalogueConvert.INSTANCE.convertContentList(list, catalogueMap);
+        // 导出 Excel
+        ExcelUtils.write(response, "内容.xls", "数据", ContentRespVO.class,
+                respList);
+    }
+
+    /**
+     * 验证目录类型
+     *
+     * @param catalogueId 目录类型ID
+     */
+    private void verifyCatalogue(Long id, Long catalogueId) {
+        CatalogueDO catalogueDO = catalogueService.getCatalogue(catalogueId);
+        if (catalogueDO == null) {
+            throw exception(CATALOGUE_NOT_EXISTS);
+        }
+        Integer type = catalogueDO.getType();
+        List<ContentDO> contentDOS = contentService.getContentList(catalogueId, null);
+        if (id != null) {
+            // 过滤出 非当前操作的内容ID
+            contentDOS = contentDOS.stream()
+                    .filter(contentDO -> !id.equals(contentDO.getId()))
+                    .collect(Collectors.toList());
+        }
+        switch (type) {
+            case 1:
+                // 1.列表
+
+                break;
+            case 2:
+                // 2.内容
+                if (!contentDOS.isEmpty()) {
+                    throw exception(CATALOGUE_TYPE_EXCEED);
+                }
+                break;
+            default:
+                // 3.外部链接
+                throw exception(CATALOGUE_TYPE_ERROR);
+        }
+    }
+
+    @GetMapping({"/list-all-simple", "/simple-list"})
+    @Operation(summary = "获取内容精简信息列表", description = "只包含被开启内容精简信息,主要用于前端的下拉选项")
+    public CommonResult<List<ContentSimpleRespVO>> getSimpleContentList(@RequestParam("catalogueId") Long catalogueId, @RequestParam("status") Integer status) {
+        List<ContentDO> list = contentService.getContentList(catalogueId, status == null ? 1 : status);
+        return success(BeanUtils.toBean(list, ContentSimpleRespVO.class));
+    }
+}

+ 59 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/content/vo/ContentPageReqVO.java

@@ -0,0 +1,59 @@
+package com.yc.ship.module.miniapplet.controller.admin.content.vo;
+
+import com.yc.ship.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.yc.ship.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+
+@Schema(description = "管理后台 - 目录内容分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ContentPageReqVO extends PageParam {
+
+    @Schema(description = "内容名称", example = "李四")
+    private String name;
+
+    @Schema(description = "内容副标题")
+    private String title;
+
+    @Schema(description = "文章内容")
+    private String content;
+
+    @Schema(description = "目录ID", example = "20092")
+    private Long catalogueId;
+
+    @Schema(description = "文章介绍")
+    private String introduce;
+
+    @Schema(description = "关键词")
+    private String keyword;
+
+    @Schema(description = "封面图")
+    private String cover;
+
+    @Schema(description = "视频")
+    private String video;
+
+    @Schema(description = "外部链接", example = "https://www.iocoder.cn")
+    private String url;
+
+    @Schema(description = "发布状态 0未发布 1已发布", example = "2")
+    private Integer releaseStatus;
+
+    @Schema(description = "发布时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] releaseTime;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 74 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/content/vo/ContentRespVO.java

@@ -0,0 +1,74 @@
+package com.yc.ship.module.miniapplet.controller.admin.content.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.yc.ship.framework.excel.core.annotations.DictFormat;
+import com.yc.ship.framework.excel.core.convert.DictConvert;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 目录内容 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class ContentRespVO {
+
+    @Schema(description = "内容主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10566")
+    @ExcelProperty("内容主键ID")
+    private Long id;
+
+    @Schema(description = "内容名称", example = "李四")
+    @ExcelProperty("内容名称")
+    private String name;
+
+    @Schema(description = "内容副标题")
+    @ExcelProperty("内容副标题")
+    private String title;
+
+    @Schema(description = "文章内容")
+    @ExcelProperty("文章内容")
+    private String content;
+
+    @Schema(description = "目录ID", example = "20092")
+//    @ExcelProperty("目录ID")
+    private Long catalogueId;
+
+    @Schema(description = "目录名称", example = "20092")
+    @ExcelProperty("目录名称")
+    private String catalogueName;
+
+    @Schema(description = "文章介绍")
+    @ExcelProperty("文章介绍")
+    private String introduce;
+
+    @Schema(description = "关键词")
+    @ExcelProperty("关键词")
+    private String keyword;
+
+    @Schema(description = "封面图")
+    @ExcelProperty("封面图")
+    private String cover;
+
+    @Schema(description = "视频")
+    @ExcelProperty("视频")
+    private String video;
+
+    @Schema(description = "外部链接", example = "https://www.iocoder.cn")
+    @ExcelProperty("外部链接")
+    private String url;
+
+    @Schema(description = "发布状态 0未发布 1已发布", example = "2")
+    @ExcelProperty(value = "发布状态", converter = DictConvert.class)
+    @DictFormat("release_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+    private Integer releaseStatus;
+
+    @Schema(description = "发布时间")
+    @ExcelProperty("发布时间")
+    private LocalDateTime releaseTime;
+
+    @Schema(description = "创建时间")
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 48 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/content/vo/ContentSaveReqVO.java

@@ -0,0 +1,48 @@
+package com.yc.ship.module.miniapplet.controller.admin.content.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 目录内容新增/修改 Request VO")
+@Data
+public class ContentSaveReqVO {
+
+    @Schema(description = "内容主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10566")
+    private Long id;
+
+    @Schema(description = "内容名称", example = "李四")
+    private String name;
+
+    @Schema(description = "内容副标题")
+    private String title;
+
+    @Schema(description = "文章内容")
+    private String content;
+
+    @Schema(description = "目录ID", example = "20092")
+    private Long catalogueId;
+
+    @Schema(description = "文章介绍")
+    private String introduce;
+
+    @Schema(description = "关键词")
+    private String keyword;
+
+    @Schema(description = "封面图")
+    private String cover;
+
+    @Schema(description = "视频")
+    private String video;
+
+    @Schema(description = "外部链接", example = "https://www.iocoder.cn")
+    private String url;
+
+    @Schema(description = "发布状态 0未发布 1已发布", example = "2")
+    private Integer releaseStatus;
+
+    @Schema(description = "发布时间")
+    private LocalDateTime releaseTime;
+
+}

+ 21 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/admin/content/vo/ContentSimpleRespVO.java

@@ -0,0 +1,21 @@
+package com.yc.ship.module.miniapplet.controller.admin.content.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 目录内容 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class ContentSimpleRespVO {
+
+    @Schema(description = "内容主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10566")
+    @ExcelProperty("内容主键ID")
+    private Long id;
+
+    @Schema(description = "内容名称", example = "李四")
+    @ExcelProperty("内容名称")
+    private String name;
+
+}

+ 298 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/app/content/AppContentController.java

@@ -0,0 +1,298 @@
+package com.yc.ship.module.miniapplet.controller.app.content;
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.yc.ship.framework.common.pojo.CommonResult;
+import com.yc.ship.framework.common.util.object.BeanUtils;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailRespVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CatalogueRespVO;
+import com.yc.ship.module.miniapplet.controller.app.content.vo.AppCatalogueRespVO;
+import com.yc.ship.module.miniapplet.controller.app.content.vo.AppCategoryRespVO;
+import com.yc.ship.module.miniapplet.controller.app.content.vo.AppContentReqVO;
+import com.yc.ship.module.miniapplet.controller.app.content.vo.AppContentRespVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.catalogue.CatalogueDO;
+import com.yc.ship.module.miniapplet.dal.dataobject.content.ContentDO;
+import com.yc.ship.module.miniapplet.dal.dto.CategoryExtraRespDTO;
+import com.yc.ship.module.miniapplet.service.catalogue.CatalogueService;
+import com.yc.ship.module.miniapplet.service.content.ContentService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.yc.ship.framework.common.pojo.CommonResult.success;
+
+/**
+ * @author :yaochao
+ * @description :TODO
+ * @date :2024/8/19 17:13
+ */
+@Tag(name = "用户 APP - 目录/栏目/产品分组分类")
+@RestController
+@RequestMapping("/app/content")
+@Validated
+@Slf4j
+public class AppContentController {
+//
+//    @Resource
+//    private ProductApi productApi;
+
+    @Resource
+    private CatalogueService catalogueService;
+
+    @Resource
+    private ContentService contentService;
+//    @Autowired
+//    private ProductGroupService productGroupService;
+//
+//    @Resource
+//    private ProductService productService;
+
+    @PostMapping("/catalogueList")
+//    @Operation(summary = "获取目录/栏目/产品分组分类列表")
+    public CommonResult<List<AppCatalogueRespVO>> catalogueList(@RequestBody @Valid AppContentReqVO reqVO) {
+        List<CatalogueDO> catalogueDOS = catalogueService.getCatalogueList(reqVO);
+        List<AppCatalogueRespVO> respVOS = BeanUtils.toBean(catalogueDOS, AppCatalogueRespVO.class);
+        return success(respVOS);
+    }
+
+//    @PostMapping("/catalogue")
+////    @Operation(summary = "获取目录/栏目/产品分组分类详情")
+//    public CommonResult<AppCatalogueRespVO> catalogue(@RequestBody @Valid AppContentReqVO reqVO) {
+////        CatalogueDO catalogueDO = catalogueService.getCatalogue(reqVO.getCatalogueId());
+////        AppCatalogueRespVO respVO = BeanUtils.toBean(catalogueDO, AppCatalogueRespVO.class);
+//        CatalogueRespVO catalogueResp = catalogueService.getCatalogueResp(reqVO.getCatalogueId());
+//        AppCatalogueRespVO respVO = BeanUtils.toBean(catalogueResp, AppCatalogueRespVO.class);
+//        if (catalogueResp.getBusinessType() == 1) {
+//            List<AppCategoryRespVO> categoryList = getCategoryRespList(reqVO);
+//            if (StringUtils.isBlank(respVO.getImgUrl())) {
+//                respVO.setImgUrl(categoryList.stream()
+//                        .filter(category -> StringUtils.isNotBlank(category.getExtraInfo().getImg()))
+//                        .map(category -> category.getExtraInfo().getImg())
+//                        .findFirst()
+//                        .orElse(null));
+//            }
+//            respVO.setCategoryList(categoryList);
+//            reqVO.setCatalogueParentId(reqVO.getCatalogueId());
+//            reqVO.setCatalogueId(null);
+//            List<CatalogueDO> childCatalogueDOS = catalogueService.getCatalogueList(reqVO);
+//            List<AppCatalogueRespVO> respVOS = BeanUtils.toBean(childCatalogueDOS, AppCatalogueRespVO.class);
+//            for (AppCatalogueRespVO catalogueRespVO : respVOS) {
+//                reqVO.setCatalogueId(catalogueRespVO.getId());
+//                List<AppCategoryRespVO> childCategoryList = getCategoryRespList(reqVO);
+//                catalogueRespVO.setCategoryList(childCategoryList);
+//                if (StringUtils.isBlank(catalogueRespVO.getImgUrl())) {
+//                    respVO.setImgUrl(childCategoryList.stream()
+//                            .filter(category -> StringUtils.isNotBlank(category.getExtraInfo().getImg()))
+//                            .map(category -> category.getExtraInfo().getImg())
+//                            .findFirst()
+//                            .orElse(null));
+//                }
+//            }
+//            respVO.setCatalogueList(respVOS);
+//        }
+//        return success(respVO);
+//    }
+
+    @PostMapping("/contentList")
+//    @Operation(summary = "获取内容列表")
+    public CommonResult<List<AppContentRespVO>> contentList(@RequestBody @Valid AppContentReqVO reqVO) {
+        List<AppContentRespVO> respVOS = new ArrayList<>();
+        if (reqVO.getCatalogueId() == null) {
+            List<CatalogueDO> catalogueDOS = catalogueService.getCatalogueList(reqVO);
+            if (!catalogueDOS.isEmpty()) {
+                reqVO.setCatalogueId(catalogueDOS.get(0).getId());
+            } else {
+                return success(respVOS);
+            }
+        }
+        List<ContentDO> contentDOS = contentService.getContentList(reqVO.getCatalogueId(), 1);
+        respVOS = BeanUtils.toBean(contentDOS, AppContentRespVO.class);
+        return success(respVOS);
+    }
+
+    @PostMapping("/content")
+//    @Operation(summary = "获取内容详情")
+    public CommonResult<AppContentRespVO> content(@RequestBody @Valid AppContentReqVO reqVO) {
+        ContentDO contentDO = contentService.getContent(reqVO.getId());
+        AppContentRespVO respVO = BeanUtils.toBean(contentDO, AppContentRespVO.class);
+        return success(respVO);
+    }
+
+//    @PostMapping("/productList")
+////    @Operation(summary = "获取子产品列表")
+//    public CommonResult<List<AppProductSpuRespVO>> productList(@RequestBody @Valid AppContentReqVO reqVO) {
+//        return success(getProductList(reqVO.getCategoryId(), reqVO.getProductName()));
+//    }
+
+//    @PostMapping("/product")
+////    @Operation(summary = "获取子产品详情")
+//    public CommonResult<AppProductSpuRespVO> product(@RequestBody @Valid AppContentReqVO reqVO) {
+//        AppProductSpuRespVO respVO = getProduct(reqVO.getProductId());
+//        return success(respVO);
+//    }
+
+//    @PostMapping("/search")
+////    @Operation(summary = "查询产品和子产品")
+//    public CommonResult<?> search(@RequestBody @Valid AppContentReqVO reqVO) {
+//        String search = reqVO.getSearch() == null ? "" : reqVO.getSearch();
+//        List<CategoryRespDTO> respVOS = productApi.getCategoryListByName(search).getCheckedData();
+//        respVOS = respVOS.stream().filter(item -> item.getUseStatus()!=null && item.getUseStatus() == 1).collect(Collectors.toList());
+//        List<AppCategoryRespVO> categoryList = BeanUtils.toBean(respVOS, AppCategoryRespVO.class);
+//        Map<String, Object> respVO = new HashMap<>();
+//        respVO.put("categoryList", categoryList);
+//        List<ProductDO> productDOS = productService.getProductListByCategoryId(null, search, 1);
+//        List<AppProductSpuRespVO> productList = BeanUtils.toBean(productDOS, AppProductSpuRespVO.class);
+//        respVO.put("productList", productList);
+//        return success(respVO);
+//    }
+
+//    @PostMapping("/searchRelationProduct")
+////    @Operation(summary = "根据产品ID或子产品ID查询产品关联的子产品")
+//    public CommonResult<?> searchRelationProduct(@RequestBody @Valid AppContentReqVO reqVO) {
+//        Long productId = reqVO.getProductId();
+//        Long categoryId = reqVO.getCategoryId();
+//        if (productId == null && categoryId == null) {
+//            return error(130_003, "产品ID或子产品ID至少有一个必传");
+//        }
+//        if (categoryId == null) {
+//            ProductDO product = productService.getProduct(productId);
+//            if (product != null) {
+//                categoryId = product.getCategoryId();
+//            }
+//        }
+//        CategoryRespDTO category = productApi.getCategory(categoryId).getCheckedData();
+//        String relationProducts = category.getExtraInfo().getAppletProducts();
+//        List<AppProductSpuRespVO> respVOS = new ArrayList<>(3);
+//        if (StringUtils.isNotBlank(relationProducts)) {
+//            JSONArray jsonArray = JSONUtil.parseArray(relationProducts);
+//            for (int i = 0; i < jsonArray.size(); i++) {
+//                JSONObject jsonObject = jsonArray.getJSONObject(i);
+//                Long relationProductId = jsonObject.getLong("id");
+//                AppProductSpuRespVO respVO = getProduct(relationProductId);
+//                respVOS.add(respVO);
+//            }
+//        }
+//        return success(respVOS);
+//    }
+
+
+//    @PostMapping("/day/price/get")
+//    @Operation(summary = "获得日历价")
+//    public CommonResult<List<AppProductDayPriceRespVO>> getAppletDayPrice(@Valid @RequestBody AppletDayStockPriceReqVO reqVO) {
+//        return success( productService.getProductDayPrice(reqVO));
+//    }
+
+//    private AppProductSpuRespVO getProduct(Long productId) {
+//        ProductDO product = productService.getProduct(productId);
+//        AppProductSpuRespVO respVO = BeanUtils.toBean(product, AppProductSpuRespVO.class);
+//
+//        List<ProductGroupDO> groupDOList = productGroupService.getListByAppletProductId(productId);
+//        if (groupDOList != null && !groupDOList.isEmpty()) {
+//            respVO.setProductGroupList(BeanUtils.toBean(groupDOList, ProductGroupRespVO.class));
+//        }
+//        if (getLoginUserId() != null && product.getId() != null) {
+//            respVO.setFavoriteId(productApi.getAppIsFavorite(getLoginUserId(), product.getId()).getCheckedData());
+//        }
+//        CategoryRespDTO categoryDTOS = productApi.getCategory(product.getCategoryId()).getCheckedData();
+//        if (categoryDTOS != null) {
+//            respVO.setSpuType(categoryDTOS.getExtraInfo().getSpuType());
+//            respVO.setCatalogueType(categoryDTOS.getExtraInfo().getType());
+//        }
+//        if (respVO.getStatus() == 2) {
+//            throw exception0(130_003, "该商品已下架");
+//        }
+//        return respVO;
+//    }
+
+//    private List<AppProductSpuRespVO> getProductList(Long categoryId, String productName) {
+//        List<ProductDO> productList = productService.getProductAndGroup(categoryId, productName);
+//        List<AppProductSpuRespVO> respVOS = BeanUtils.toBean(productList, AppProductSpuRespVO.class);
+//        respVOS = respVOS.stream().filter(item -> {
+//            if (item.getStatus() == 1) {
+//                if (getLoginUserId() != null && item.getId() != null) {
+//                    item.setFavoriteId(productApi.getAppIsFavorite(getLoginUserId(), item.getId()).getCheckedData());
+//                }
+//                return true;
+//            }
+//            return false;
+//        }).collect(Collectors.toList());
+//        return respVOS;
+//    }
+
+    /**
+     * @param reqVO
+     * @return
+     */
+//    private List<AppCategoryRespVO> getCategoryRespList(AppContentReqVO reqVO) {
+//        List<CategoryRespDTO> categoryDTOS = productApi.getCategoryListByCatalogueId(reqVO.getCatalogueId(), 1).getCheckedData();
+//        List<AppCategoryRespVO> categoryList = BeanUtils.toBean(categoryDTOS, AppCategoryRespVO.class);
+//        if (StringUtils.isNotBlank(reqVO.getLongitude()) && StringUtils.isNotBlank(reqVO.getLatitude()) && reqVO.getRice() != null) {
+//            // 根据经纬度查询产品分组(小程序产品)
+//            double[] bounds = GeoUtils.calculateBoundingCoordinates(Double.parseDouble(reqVO.getLatitude()), Double.parseDouble(reqVO.getLongitude()), reqVO.getRice());
+//            // 开始纬度
+//            double latitude1 = bounds[0];
+//            // 结束纬度
+//            double latitude2 = bounds[2];
+//            // 开始经度
+//            double longitude1 = bounds[1];
+//            // 开始经度
+//            double longitude2 = bounds[3];
+//            categoryList = categoryList.stream().filter(e -> {
+//                CategoryExtraRespDTO extraInfo = e.getExtraInfo();
+//                if (extraInfo != null && StringUtils.isNotBlank(extraInfo.getCoordinate())) {
+//                    String[] split = extraInfo.getCoordinate().split(",");
+//                    double latitude = Double.parseDouble(split[1]);
+//                    double longitude = Double.parseDouble(split[0]);
+//                    return latitude >= latitude1 && latitude <= latitude2 && longitude >= longitude1 && longitude <= longitude2;
+//                }
+//                return false;
+//            }).collect(Collectors.toList());
+//        }
+//        for (AppCategoryRespVO appCategoryRespVO : categoryList) {
+//            List<AppProductSpuRespVO> spuList = getProductList(appCategoryRespVO.getId(), null);
+//            // 使用 Stream API 找到最低售价
+//            Optional<BigDecimal> minPrice = spuList.stream()
+//                    .map(AppProductSpuRespVO::getSalePrice)
+//                    .min(BigDecimal::compareTo);
+//            // 使用 Stream API 计算售卖总数
+//            int sellNum = 0;
+//            if (!spuList.isEmpty()) {
+//                sellNum = spuList.stream()
+//                        .mapToInt(AppProductSpuRespVO::getSellNum)
+//                        .sum();
+//            }
+//            spuList = spuList.stream().filter(e -> {
+//                CategoryExtraRespDTO extraInfo = appCategoryRespVO.getExtraInfo();
+//                e.setSpuType(extraInfo.getSpuType());
+//                return true;
+//            }).collect(Collectors.toList());
+//            appCategoryRespVO.setSalePrice(minPrice.orElse(BigDecimal.ZERO));
+//            appCategoryRespVO.setSellNum(sellNum);
+//            appCategoryRespVO.setSpuList(spuList);
+//            appCategoryRespVO.setResourceNum(spuList.size());
+//        }
+//        List<CatalogueDetailRespVO> respVOS = catalogueService.getCatalogueDetailList(reqVO.getCatalogueId());
+//        Map<Long, Integer> sortNumMap = respVOS.stream()
+//                .collect(Collectors.toMap(CatalogueDetailRespVO::getCategoryId, CatalogueDetailRespVO::getSortNum));
+//        for (AppCategoryRespVO category : categoryList) {
+//            category.setSortNum(sortNumMap.getOrDefault(category.getId(), 0));
+//        }
+//        categoryList.sort(Comparator.comparing(AppCategoryRespVO::getSortNum));
+//        return categoryList;
+//    }
+
+}

+ 22 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/app/content/vo/AppCatalogueRespVO.java

@@ -0,0 +1,22 @@
+package com.yc.ship.module.miniapplet.controller.app.content.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CatalogueRespVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "小程序接口 - 目录类型 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class AppCatalogueRespVO extends CatalogueRespVO {
+
+    @Schema(description = "产品分组列表")
+    private List<AppCategoryRespVO> categoryList;
+
+    @Schema(description = "子目录/子栏目/子分类")
+    private List<AppCatalogueRespVO> catalogueList;
+}

+ 60 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/app/content/vo/AppCategoryRespVO.java

@@ -0,0 +1,60 @@
+package com.yc.ship.module.miniapplet.controller.app.content.vo;
+
+import com.yc.ship.module.miniapplet.dal.dto.CategoryExtraRespDTO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "RPC 服务 - Category 产品分组/资源 Response VO")
+@Data
+public class AppCategoryRespVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "21187")
+    private Long id;
+
+    @Schema(description = "父ID", example = "27454")
+    private Long parentId;
+
+    @Schema(description = "状态", example = "2")
+    private Integer useStatus;
+
+    @Schema(description = "编码")
+    private String cateCode;
+
+    @Schema(description = "分类名称", example = "芋艿")
+    private String cateName;
+
+    @Schema(description = "排序号")
+    private Integer sortNum;
+
+    @Schema(description = "类型;0资源分类 1产品分类 2小程序产品分类", example = "1")
+    private Integer type;
+
+    @Schema(description = "父路径;逗号分隔")
+    private String parentPath;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+
+    @Schema(description = "销售价")
+    private BigDecimal salePrice;
+
+    @Schema(description = "已售数量", example = "1")
+    private Integer sellNum;
+
+    @Schema(description = "关联资源数量")
+    private Integer resourceNum;
+
+//    @Schema(description = "分组类型 1.票务类 2.文创类 3.酒店类 4.餐饮类", example = "2")
+//    private Integer groupType;
+
+    @Schema(description = "小程序信息")
+    private CategoryExtraRespDTO extraInfo;
+
+//    @Schema(description = "分类产品集合")
+//    private List<AppProductSpuRespVO> spuList;
+
+}

+ 57 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/app/content/vo/AppContentReqVO.java

@@ -0,0 +1,57 @@
+package com.yc.ship.module.miniapplet.controller.app.content.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Schema(description = "小程序 - 查询内容相关请求参数")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class AppContentReqVO {
+
+    @Schema(description = "产品名称或者子产品名称 search接口使用", example = "1024")
+    private String search;
+
+    @Schema(description = "内容id", example = "1024")
+    private Long id;
+
+    @Schema(description = "目录/栏目/产品分组分类id", example = "1024")
+    private Long catalogueId;
+
+    @Schema(description = "父级目录/栏目/产品分组分类id", example = "1024")
+    private Long catalogueParentId;
+
+    @Schema(description = "业务类型 0.目录 1.栏目 2.分类", example = "2")
+    private Integer businessType;
+
+    @Schema(description = "类型 :business_type =0 (1.列表 2.内容 3.外部链接) business_type =1 (无) business_type =2 (1.票务类 2.文创类 3.酒店类 4.餐饮类)", example = "1")
+    private Integer type;
+
+    @Schema(description = "目录编码")
+    private String code;
+
+    @Schema(description = "小程序产品ID 根据产品查子产品使用", example = "1024")
+    private Long categoryId;
+
+    @Schema(description = "小程序产品名称 根据产品查子产品详情", example = "1024")
+    private String categoryName;
+
+    @Schema(description = "小程序子产品ID 根据子产品查子产品详情", example = "1024")
+    private Long productId;
+
+    @Schema(description = "小程序子产品名称 根据子产品查子产品详情", example = "1024")
+    private String productName;
+
+    @Schema(description = "经度", example = "1")
+    private String longitude;
+
+    @Schema(description = "纬度", example = "1")
+    private String latitude;
+
+    @Schema(description = "米", example = "1")
+    private Integer rice;
+}

+ 58 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/controller/app/content/vo/AppContentRespVO.java

@@ -0,0 +1,58 @@
+package com.yc.ship.module.miniapplet.controller.app.content.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.yc.ship.framework.excel.core.annotations.DictFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "小程序接口 - 目录内容 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class AppContentRespVO {
+
+    @Schema(description = "内容主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10566")
+    private Long id;
+
+    @Schema(description = "内容名称", example = "李四")
+    private String name;
+
+    @Schema(description = "内容副标题")
+    private String title;
+
+    @Schema(description = "文章内容")
+    private String content;
+
+    @Schema(description = "目录ID", example = "20092")
+    private Long catalogueId;
+
+    @Schema(description = "目录名称", example = "20092")
+    private String catalogueName;
+
+    @Schema(description = "文章介绍")
+    private String introduce;
+
+    @Schema(description = "关键词")
+    private String keyword;
+
+    @Schema(description = "封面图")
+    private String cover;
+
+    @Schema(description = "视频")
+    private String video;
+
+    @Schema(description = "外部链接", example = "https://www.iocoder.cn")
+    private String url;
+
+    @Schema(description = "发布状态 0未发布 1已发布", example = "2")
+    @DictFormat("release_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+    private Integer releaseStatus;
+
+    @Schema(description = "发布时间")
+    private LocalDateTime releaseTime;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+
+}

+ 38 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/convert/catalogue/CatalogueConvert.java

@@ -0,0 +1,38 @@
+package com.yc.ship.module.miniapplet.convert.catalogue;
+
+import com.yc.ship.framework.common.util.collection.CollectionUtils;
+import com.yc.ship.framework.common.util.collection.MapUtils;
+import com.yc.ship.framework.common.util.object.BeanUtils;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CatalogueRespVO;
+import com.yc.ship.module.miniapplet.controller.admin.content.vo.ContentRespVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.catalogue.CatalogueDO;
+import com.yc.ship.module.miniapplet.dal.dataobject.content.ContentDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Map;
+
+@Mapper
+public interface CatalogueConvert {
+
+    CatalogueConvert INSTANCE = Mappers.getMapper(CatalogueConvert.class);
+
+    default List<CatalogueRespVO> convertCatalogueList(List<CatalogueDO> list, Map<Long, CatalogueDO> catalogueMap) {
+        return CollectionUtils.convertList(list, catalogue -> {
+            CatalogueRespVO catalogueVO = BeanUtils.toBean(catalogue, CatalogueRespVO.class);
+            MapUtils.findAndThen(catalogueMap, catalogue.getParentId(), c -> catalogueVO.setParentName(c.getName()));
+            return catalogueVO;
+        });
+    }
+
+    default List<ContentRespVO> convertContentList(List<ContentDO> list, Map<Long, CatalogueDO> catalogueMap) {
+        return CollectionUtils.convertList(list, content -> {
+            ContentRespVO contentVO = BeanUtils.toBean(content, ContentRespVO.class);
+            MapUtils.findAndThen(catalogueMap, contentVO.getCatalogueId(), c -> contentVO.setCatalogueName(c.getName()));
+            return contentVO;
+        });
+    }
+
+
+}

+ 109 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/dataobject/catalogue/CatalogueDO.java

@@ -0,0 +1,109 @@
+package com.yc.ship.module.miniapplet.dal.dataobject.catalogue;
+
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.sun.xml.bind.v2.TODO;
+import com.yc.ship.framework.tenant.core.db.TenantBaseDO;
+import lombok.*;
+
+/**
+ * 目录类型 DO
+ *
+ * @author 管理员
+ */
+@TableName("applet_catalogue")
+@KeySequence("applet_catalogue_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CatalogueDO extends TenantBaseDO {
+
+    /**
+     * 目录类型主键ID
+     */
+    @TableId
+    private Long id;
+    /**
+     * 目录名称
+     */
+    private String name;
+    /**
+     * 目录编码
+     */
+    private String code;
+
+    /**
+     * 业务类型 0.目录 1.栏目 2.分类
+     */
+    private Integer businessType;
+
+    /**
+     * 目录类型 1.列表 2.内容 3.外部链接
+     * <p>
+     * 枚举 {@link TODO catalogue_type 对应的类}
+     */
+    private Integer type;
+    /**
+     * 父级目录
+     */
+    private Long parentId;
+    /**
+     * 目录副标题
+     */
+    private String title;
+    /**
+     * 目录Icon图标
+     */
+    private String logo;
+    /**
+     * 图片地址
+     */
+    private String imgUrl;
+    /**
+     * 外部链接
+     */
+    private String url;
+    /**
+     * 启用状态 0否 1是
+     * <p>
+     * 枚举 {@link TODO use_status 对应的类}
+     */
+    private Integer isUse;
+    /**
+     * 是否允许评论 0否 1是
+     */
+    private Integer isComment;
+    /**
+     * 允许用户发布 0否 1是
+     */
+    private Integer isRelease;
+    /**
+     * 用户发布审核 0否 1是
+     */
+    private Integer isAudit;
+    /**
+     * 是否栏目推荐 0否 1是
+     */
+    private Integer isRecommend;
+    /**
+     * 展示类型 0.产品 1.子产品
+     */
+    private Integer showType;
+    /**
+     * 列表页设置
+     */
+    private String details;
+    /**
+     * 备注
+     */
+    private String remark;
+    /**
+     * 排序号
+     */
+    private Integer sortNum;
+
+}

+ 42 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/dataobject/cataloguedetail/CatalogueDetailDO.java

@@ -0,0 +1,42 @@
+package com.yc.ship.module.miniapplet.dal.dataobject.cataloguedetail;
+
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.yc.ship.framework.tenant.core.db.TenantBaseDO;
+import lombok.*;
+
+/**
+ * 栏目产品排序 DO
+ *
+ * @author 管理员
+ */
+@TableName("applet_catalogue_detail")
+@KeySequence("applet_catalogue_detail_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CatalogueDetailDO extends TenantBaseDO {
+
+    /**
+     * 栏目产品排序ID
+     */
+    @TableId
+    private Long id;
+    /**
+     * 栏目ID
+     */
+    private Long catalogueId;
+    /**
+     * 小程序产品ID
+     */
+    private Long categoryId;
+    /**
+     * 排序号
+     */
+    private Integer sortNum;
+
+}

+ 79 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/dataobject/content/ContentDO.java

@@ -0,0 +1,79 @@
+package com.yc.ship.module.miniapplet.dal.dataobject.content;
+
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.sun.xml.bind.v2.TODO;
+import com.yc.ship.framework.tenant.core.db.TenantBaseDO;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+/**
+ * 目录内容 DO
+ *
+ * @author 管理员
+ */
+@TableName("applet_content")
+@KeySequence("applet_content_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ContentDO extends TenantBaseDO {
+
+    /**
+     * 内容主键ID
+     */
+    @TableId
+    private Long id;
+    /**
+     * 内容名称
+     */
+    private String name;
+    /**
+     * 内容副标题
+     */
+    private String title;
+    /**
+     * 文章内容
+     */
+    private String content;
+    /**
+     * 目录ID
+     */
+    private Long catalogueId;
+    /**
+     * 文章介绍
+     */
+    private String introduce;
+    /**
+     * 关键词
+     */
+    private String keyword;
+    /**
+     * 封面图
+     */
+    private String cover;
+    /**
+     * 视频
+     */
+    private String video;
+    /**
+     * 外部链接
+     */
+    private String url;
+    /**
+     * 发布状态 0未发布 1已发布
+     * <p>
+     * 枚举 {@link TODO release_status 对应的类}
+     */
+    private Integer releaseStatus;
+    /**
+     * 发布时间
+     */
+    private LocalDateTime releaseTime;
+
+}

+ 54 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/dto/CategoryExtraRespDTO.java

@@ -0,0 +1,54 @@
+package com.yc.ship.module.miniapplet.dal.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 小程序产品分类 Response VO")
+@Data
+public class CategoryExtraRespDTO {
+
+    @Schema(description = "同product_category主键", example = "23952")
+    private Long id;
+
+    @Schema(description = "分类描述", example = "你说的对")
+    private String description;
+
+    @Schema(description = "产品类型", example = "2")
+    private Long type;
+
+    @Schema(description = "商品类型 1.票务类 2.文创类 3.酒店类 4.餐饮类")
+    private Integer spuType;
+
+    @Schema(description = "评价类型")
+    private String evaluateType;
+
+    @Schema(description = "经纬度")
+    private String coordinate;
+
+    @Schema(description = "标签")
+    private String tags;
+
+    @Schema(description = "栏目ID")
+    private String columns;
+
+    @Schema(description = "地址")
+    private String address;
+
+    @Schema(description = "封面图")
+    private String img;
+
+    @Schema(description = "产品简介")
+    private String profile;
+
+    @Schema(description = "产品详情")
+    private String details;
+
+    @Schema(description = "启用热门推荐")
+    private Integer enableHotAds;
+
+    /**
+     * 关联子产品
+     */
+    @Schema(description = "小程序产品关联子产品")
+    private String appletProducts;
+}

+ 82 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/mysql/catalogue/CatalogueMapper.java

@@ -0,0 +1,82 @@
+package com.yc.ship.module.miniapplet.dal.mysql.catalogue;
+
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.framework.mybatis.core.mapper.BaseMapperX;
+import com.yc.ship.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CataloguePageReqVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.catalogue.CatalogueDO;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 目录类型 Mapper
+ *
+ * @author 管理员
+ */
+@Mapper
+public interface CatalogueMapper extends BaseMapperX<CatalogueDO> {
+
+    default PageResult<CatalogueDO> selectPage(CataloguePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<CatalogueDO>()
+                .likeIfPresent(CatalogueDO::getName, reqVO.getName())
+                .eqIfPresent(CatalogueDO::getCode, reqVO.getCode())
+                .eqIfPresent(CatalogueDO::getBusinessType, reqVO.getBusinessType())
+                .eqIfPresent(CatalogueDO::getType, reqVO.getType())
+                .eqIfPresent(CatalogueDO::getParentId, reqVO.getParentId())
+                .eqIfPresent(CatalogueDO::getTitle, reqVO.getTitle())
+                .eqIfPresent(CatalogueDO::getLogo, reqVO.getLogo())
+                .eqIfPresent(CatalogueDO::getUrl, reqVO.getUrl())
+                .eqIfPresent(CatalogueDO::getIsUse, reqVO.getIsUse())
+                .eqIfPresent(CatalogueDO::getIsComment, reqVO.getIsComment())
+                .eqIfPresent(CatalogueDO::getIsRelease, reqVO.getIsRelease())
+                .eqIfPresent(CatalogueDO::getIsAudit, reqVO.getIsAudit())
+                .eqIfPresent(CatalogueDO::getRemark, reqVO.getRemark())
+                .eqIfPresent(CatalogueDO::getSortNum, reqVO.getSortNum())
+                .betweenIfPresent(CatalogueDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(CatalogueDO::getId));
+    }
+
+    default List<CatalogueDO> getCatalogueByParentId(Long parentId, Integer businessType, String type) {
+        LambdaQueryWrapperX<CatalogueDO> wrapper = new LambdaQueryWrapperX<>();
+        wrapper.eqIfPresent(CatalogueDO::getParentId, parentId);
+        if (StringUtils.equals("c", type)) {
+            wrapper.inIfPresent(CatalogueDO::getType, 1, 2);
+        }
+        wrapper.eqIfPresent(CatalogueDO::getBusinessType, businessType);
+        wrapper.eqIfPresent(CatalogueDO::getIsUse, 1);
+        wrapper.orderByDesc(CatalogueDO::getId);
+        return selectList(wrapper);
+    }
+
+    default List<CatalogueDO> getCatalogueList(Long id, Long prentId, Integer businessType, Integer type, String code,String name) {
+        LambdaQueryWrapperX<CatalogueDO> wrapper = new LambdaQueryWrapperX<>();
+        if (id != null) {
+            wrapper.eqIfPresent(CatalogueDO::getId, id);
+        }
+        if (type != null) {
+            wrapper.eqIfPresent(CatalogueDO::getType, type);
+        }
+        if (StringUtils.isNotBlank(code)) {
+            wrapper.eqIfPresent(CatalogueDO::getCode, code);
+        }
+        if (StringUtils.isNotBlank(name)) {
+            wrapper.likeIfPresent(CatalogueDO::getName, name);
+        }
+        if (prentId != null) {
+            wrapper.eqIfPresent(CatalogueDO::getParentId, prentId);
+        } else {
+            wrapper.eqIfPresent(CatalogueDO::getParentId, 0);
+        }
+        wrapper.eqIfPresent(CatalogueDO::getIsUse, 1);
+        if (businessType != null) {
+            wrapper.eqIfPresent(CatalogueDO::getBusinessType, businessType);
+//            if (businessType == 1) {
+//                wrapper.orderByDesc(CatalogueDO::getIsRecommend);
+//            }
+        }
+        wrapper.orderByAsc(CatalogueDO::getSortNum);
+        return selectList(wrapper);
+    }
+}

+ 35 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/mysql/cataloguedetail/CatalogueDetailMapper.java

@@ -0,0 +1,35 @@
+package com.yc.ship.module.miniapplet.dal.mysql.cataloguedetail;
+
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.framework.mybatis.core.mapper.BaseMapperX;
+import com.yc.ship.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailPageReqVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.cataloguedetail.CatalogueDetailDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 栏目产品排序 Mapper
+ *
+ * @author 管理员
+ */
+@Mapper
+public interface CatalogueDetailMapper extends BaseMapperX<CatalogueDetailDO> {
+
+    default PageResult<CatalogueDetailDO> selectPage(CatalogueDetailPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<CatalogueDetailDO>()
+                .eqIfPresent(CatalogueDetailDO::getCategoryId, reqVO.getCategoryId())
+                .eqIfPresent(CatalogueDetailDO::getSortNum, reqVO.getSortNum())
+                .betweenIfPresent(CatalogueDetailDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(CatalogueDetailDO::getId));
+    }
+
+    default List<CatalogueDetailDO> getCatalogueDetailList(Long catalogueId) {
+        LambdaQueryWrapperX<CatalogueDetailDO> queryWrapperX = new LambdaQueryWrapperX<>();
+        queryWrapperX.eqIfPresent(CatalogueDetailDO::getCatalogueId,catalogueId);
+        queryWrapperX.orderByAsc(CatalogueDetailDO::getSortNum);
+        return selectList(queryWrapperX);
+    }
+
+}

+ 48 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/dal/mysql/content/ContentMapper.java

@@ -0,0 +1,48 @@
+package com.yc.ship.module.miniapplet.dal.mysql.content;
+
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.framework.mybatis.core.mapper.BaseMapperX;
+import com.yc.ship.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.yc.ship.module.miniapplet.controller.admin.content.vo.ContentPageReqVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.content.ContentDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 目录内容 Mapper
+ *
+ * @author 管理员
+ */
+@Mapper
+public interface ContentMapper extends BaseMapperX<ContentDO> {
+
+    default PageResult<ContentDO> selectPage(ContentPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<ContentDO>()
+                .likeIfPresent(ContentDO::getName, reqVO.getName())
+                .eqIfPresent(ContentDO::getTitle, reqVO.getTitle())
+                .eqIfPresent(ContentDO::getContent, reqVO.getContent())
+                .eqIfPresent(ContentDO::getCatalogueId, reqVO.getCatalogueId())
+                .eqIfPresent(ContentDO::getIntroduce, reqVO.getIntroduce())
+                .eqIfPresent(ContentDO::getKeyword, reqVO.getKeyword())
+                .eqIfPresent(ContentDO::getCover, reqVO.getCover())
+                .eqIfPresent(ContentDO::getVideo, reqVO.getVideo())
+                .eqIfPresent(ContentDO::getUrl, reqVO.getUrl())
+                .eqIfPresent(ContentDO::getReleaseStatus, reqVO.getReleaseStatus())
+                .betweenIfPresent(ContentDO::getReleaseTime, reqVO.getReleaseTime())
+                .betweenIfPresent(ContentDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(ContentDO::getId));
+    }
+
+    default List<ContentDO> getContentList(Long catalogueId, Integer releaseStatus) {
+        LambdaQueryWrapperX<ContentDO> queryWrapperX = new LambdaQueryWrapperX<>();
+        if (catalogueId != null) {
+            queryWrapperX.eqIfPresent(ContentDO::getCatalogueId, catalogueId);
+        }
+        if (releaseStatus != null) {
+            queryWrapperX.eq(ContentDO::getReleaseStatus, releaseStatus);
+        }
+        queryWrapperX.orderByDesc(ContentDO::getReleaseTime);
+        return selectList(queryWrapperX);
+    }
+}

+ 16 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/enums/ErrorCodeConstants.java

@@ -26,4 +26,20 @@ public interface ErrorCodeConstants {
 
     ErrorCode PROBLEM_TYPE_NOT_EXISTS = new ErrorCode(90_002, "问题类型不存在");
 
+    ErrorCode CATALOGUE_DETAIL_NOT_EXISTS = new ErrorCode(180_001, "栏目产品排序不存在");
+
+    ErrorCode CATALOGUE_NOT_EXISTS = new ErrorCode(10_001, "目录类型不存在");
+
+    ErrorCode CATALOGUE_BINDING = new ErrorCode(10_002, "当前数据已被绑定");
+
+    ErrorCode CATALOGUE_BINDING_CONTENT = new ErrorCode(10_003, "目录类型已被其它内容绑定");
+
+    ErrorCode CATALOGUE_TYPE_ERROR = new ErrorCode(10_004, "目录类型为外部链接,不能绑定内容");
+
+    ErrorCode CATALOGUE_TYPE_EXCEED = new ErrorCode(10_005, "目录类型为内容,绑定内容超出限制");
+
+    ErrorCode CONTENT_NOT_EXISTS = new ErrorCode(20_002, "目录内容不存在");
+
+    ErrorCode RESOURCE_NOT_EXISTS = new ErrorCode(100_001, "资源不存在");
+
 }

+ 91 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/catalogue/CatalogueService.java

@@ -0,0 +1,91 @@
+package com.yc.ship.module.miniapplet.service.catalogue;
+
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailRespVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CataloguePageReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CatalogueRespVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CatalogueSaveReqVO;
+import com.yc.ship.module.miniapplet.controller.app.content.vo.AppContentReqVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.catalogue.CatalogueDO;
+
+import javax.validation.Valid;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 目录类型 Service 接口
+ *
+ * @author 管理员
+ */
+public interface CatalogueService {
+
+    /**
+     * 创建目录类型
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createCatalogue(@Valid CatalogueSaveReqVO createReqVO);
+
+    /**
+     * 更新目录类型
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateCatalogue(@Valid CatalogueSaveReqVO updateReqVO);
+
+    /**
+     * 更新目录类型
+     * @param updateReqVO
+     */
+    void updateCatalogueDetails(@Valid CatalogueSaveReqVO updateReqVO);
+
+    /**
+     * 删除目录类型
+     *
+     * @param id 编号
+     */
+    void deleteCatalogue(Long id);
+
+    /**
+     * 获得目录类型
+     *
+     * @param id 编号
+     * @return 目录类型
+     */
+    CatalogueDO getCatalogue(Long id);
+
+    /**
+     * 获得目录类型
+     *
+     * @param id 编号
+     * @return 目录类型
+     */
+    CatalogueRespVO getCatalogueResp(Long id);
+
+    /**
+     * 获得目录类型分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 目录类型分页
+     */
+    PageResult<CatalogueDO> getCataloguePage(CataloguePageReqVO pageReqVO);
+
+    List<CatalogueDO> getCatalogueList(Collection<Long> ids);
+
+    /**
+     * 查目录类型树
+     *
+     * @return
+     */
+    List<Map<String, Object>> getCatalogueTree(Integer businessType,String type);
+
+    List<CatalogueDO> getCatalogueListByStatus(Integer businessType,Integer status);
+
+    List<CatalogueDO> getCatalogueList(AppContentReqVO reqVO);
+
+    List<CatalogueDetailRespVO> initCatalogueDetails(Long id);
+
+    List<CatalogueDetailRespVO> getCatalogueDetailList(Long id);
+}

+ 230 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/catalogue/CatalogueServiceImpl.java

@@ -0,0 +1,230 @@
+package com.yc.ship.module.miniapplet.service.catalogue;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.framework.common.util.object.BeanUtils;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailRespVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailSaveReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CataloguePageReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CatalogueRespVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.vo.CatalogueSaveReqVO;
+import com.yc.ship.module.miniapplet.controller.app.content.vo.AppContentReqVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.catalogue.CatalogueDO;
+import com.yc.ship.module.miniapplet.dal.dataobject.cataloguedetail.CatalogueDetailDO;
+import com.yc.ship.module.miniapplet.dal.dataobject.content.ContentDO;
+import com.yc.ship.module.miniapplet.dal.mysql.catalogue.CatalogueMapper;
+import com.yc.ship.module.miniapplet.service.cataloguedetail.CatalogueDetailService;
+import com.yc.ship.module.miniapplet.service.content.ContentService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.yc.ship.module.miniapplet.enums.ErrorCodeConstants.*;
+
+
+/**
+ * 目录类型 Service 实现类
+ *
+ * @author 管理员
+ */
+@Service
+@Validated
+public class CatalogueServiceImpl implements CatalogueService {
+
+//    @Resource
+//    private ProductApi productApi;
+
+    @Resource
+    private ContentService contentService;
+
+
+    @Resource
+    private CatalogueDetailService catalogueDetailService;
+
+    @Resource
+    private CatalogueMapper catalogueMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createCatalogue(CatalogueSaveReqVO createReqVO) {
+        // 插入
+        CatalogueDO catalogue = BeanUtils.toBean(createReqVO, CatalogueDO.class);
+        catalogue.setDetails(StringUtils.join(createReqVO.getDetailsList(), ","));
+        Long id = IdWorker.getId(catalogue);
+        catalogue.setId(id);
+        catalogue.setImgUrl(createReqVO.getHeaderVideo());
+        catalogueMapper.insert(catalogue);
+        // 返回
+        return catalogue.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateCatalogue(CatalogueSaveReqVO updateReqVO) {
+        // 校验存在
+        validateCatalogueExists(updateReqVO.getId());
+        // 更新
+        CatalogueDO updateObj = BeanUtils.toBean(updateReqVO, CatalogueDO.class);
+        updateObj.setDetails(StringUtils.join(updateReqVO.getDetailsList(), ","));
+        updateObj.setImgUrl(updateReqVO.getHeaderVideo());
+        catalogueMapper.updateById(updateObj);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateCatalogueDetails(CatalogueSaveReqVO updateReqVO) {
+        for (CatalogueDetailSaveReqVO detail : updateReqVO.getDetailRespVOS()) {
+            catalogueDetailService.updateCatalogueDetail(detail);
+        }
+    }
+
+    @Override
+    public void deleteCatalogue(Long id) {
+
+        //TODO: 待完善
+        // 校验存在
+        CatalogueDO catalogueDO = validateCatalogueExists(id);
+//        if (catalogueDO.getBusinessType() == 1 || catalogueDO.getBusinessType() == 2) {
+//            List<CategoryRespDTO> categoryRespDTOS = productApi.getCategoryListByCatalogueId(id, catalogueDO.getBusinessType()).getCheckedData();
+//            if (!categoryRespDTOS.isEmpty()) {
+//                String categoryName = categoryRespDTOS.stream().map(CategoryRespDTO::getCateName).collect(Collectors.joining(","));
+//                String businessTypeMsg = catalogueDO.getBusinessType() == 1 ? "栏目" : "分类";
+//                throw exception0(10_002, "当前" + businessTypeMsg + "已被" + categoryName + "产品绑定,不能删除");
+//            }
+//        }
+        if (catalogueDO.getBusinessType() != 2) {
+            List<CatalogueDO> catalogueDOS = catalogueMapper.getCatalogueByParentId(id, catalogueDO.getBusinessType(), "");
+            if (!catalogueDOS.isEmpty()) {
+                throw exception(CATALOGUE_BINDING);
+            }
+        }
+        List<ContentDO> contentDOS = contentService.getContentList(id, null);
+        if (!contentDOS.isEmpty()) {
+            throw exception(CATALOGUE_BINDING_CONTENT);
+        }
+        // 删除
+        catalogueMapper.deleteById(id);
+    }
+
+    private CatalogueDO validateCatalogueExists(Long id) {
+        CatalogueDO catalogueDO = catalogueMapper.selectById(id);
+        if (catalogueDO == null) {
+            throw exception(CATALOGUE_NOT_EXISTS);
+        }
+        return catalogueDO;
+    }
+
+    @Override
+    public CatalogueDO getCatalogue(Long id) {
+        return catalogueMapper.selectById(id);
+    }
+
+    @Override
+    public CatalogueRespVO getCatalogueResp(Long id) {
+        CatalogueDO catalogue = getCatalogue(id);
+        CatalogueRespVO respVO = BeanUtils.toBean(catalogue, CatalogueRespVO.class);
+        return respVO;
+    }
+
+    @Override
+    public PageResult<CatalogueDO> getCataloguePage(CataloguePageReqVO pageReqVO) {
+        return catalogueMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<CatalogueDO> getCatalogueList(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+        return catalogueMapper.selectBatchIds(ids);
+    }
+
+    @Override
+    public List<Map<String, Object>> getCatalogueTree(Integer businessType, String type) {
+        return getCatalogueTree(0L, businessType, type);
+    }
+
+    @Override
+    public List<CatalogueDO> getCatalogueListByStatus(Integer businessType, Integer status) {
+        return catalogueMapper.selectList(CatalogueDO::getBusinessType, businessType, CatalogueDO::getIsUse, status);
+    }
+
+    @Override
+    public List<CatalogueDO> getCatalogueList(AppContentReqVO reqVO) {
+        return catalogueMapper.getCatalogueList(reqVO.getCatalogueId(), reqVO.getCatalogueParentId(), reqVO.getBusinessType(), reqVO.getType(), reqVO.getCode(), reqVO.getCategoryName());
+    }
+
+    @Override
+    public List<CatalogueDetailRespVO> initCatalogueDetails(Long id) {
+        //TODO: 待完善
+        // 获取 CategoryRespDTO 列表
+//        List<CategoryRespDTO> respDTOS = productApi.getCategoryListByCatalogueId(id, 1).getCheckedData();
+        // 获取 CatalogueDetailDO 列表
+        List<CatalogueDetailRespVO> respVOS = getCatalogueDetailList(id);
+        // 创建一个 Map 来存储 CategoryRespDTO 的 id 和 name 映射
+//        Map<Long, CategoryRespDTO> categoryMap = respDTOS.stream()
+//                .collect(Collectors.toMap(CategoryRespDTO::getId, category -> category));
+        // 创建一个 Set 来存储已存在的 CatalogueDetailDO 的 categoryId
+        Set<Long> existingCategoryIds = respVOS.stream()
+                .map(CatalogueDetailRespVO::getCategoryId)
+                .collect(Collectors.toSet());
+        // 找出需要新增的 CategoryRespDTO
+//        List<CategoryRespDTO> newCategories = respDTOS.stream()
+//                .filter(category -> !existingCategoryIds.contains(category.getId()))
+//                .collect(Collectors.toList());
+        // 新增 CatalogueDetailDO
+        int index = respVOS.size();
+//        for (CategoryRespDTO newCategory : newCategories) {
+//            CatalogueDetailSaveReqVO detailSaveReqVO = new CatalogueDetailSaveReqVO();
+//            detailSaveReqVO.setCatalogueId(id);
+//            detailSaveReqVO.setCategoryId(newCategory.getId());
+//            detailSaveReqVO.setSortNum(index++);
+//            catalogueDetailService.createCatalogueDetail(detailSaveReqVO);
+//        }
+        // 找出需要删除的 CatalogueDetailDO
+//        List<CatalogueDetailRespVO> deletedDetails = respVOS.stream()
+//                .filter(detail -> !categoryMap.containsKey(detail.getCategoryId()))
+//                .collect(Collectors.toList());
+//        // 删除 CatalogueDetailDO
+//        for (CatalogueDetailRespVO deletedDetail : deletedDetails) {
+//            catalogueDetailService.deleteCatalogueDetail(deletedDetail.getId());
+//        }
+//        // 重新获取 CatalogueDetailDO 列表
+//        respVOS = getCatalogueDetailList(id);
+//        // 设置 name 属性到 respVOS
+//        for (CatalogueDetailRespVO respVO : respVOS) {
+//            CategoryRespDTO category = categoryMap.get(respVO.getCategoryId());
+//            if (category != null) {
+//                respVO.setCategoryName(category.getCateName());
+//            }
+//        }
+        return respVOS;
+    }
+
+    @Override
+    public List<CatalogueDetailRespVO> getCatalogueDetailList(Long id) {
+        // 获取 CatalogueDetailDO 列表
+        List<CatalogueDetailDO> detailDOS = catalogueDetailService.getCatalogueDetailList(id);
+        return BeanUtils.toBean(detailDOS, CatalogueDetailRespVO.class);
+    }
+
+    private List<Map<String, Object>> getCatalogueTree(Long prentId, Integer businessType, String type) {
+        List<CatalogueDO> catalogueDOS = catalogueMapper.getCatalogueByParentId(prentId, businessType, type);
+        return catalogueDOS.stream().map(catalogueDO -> {
+            Map<String, Object> data = new HashMap<>(3);
+            data.put("value", catalogueDO.getId());
+            data.put("label", catalogueDO.getName());
+            data.put("type", catalogueDO.getType());
+            data.put("children", getCatalogueTree(catalogueDO.getId(), businessType, type));
+            return data;
+        }).collect(Collectors.toList());
+    }
+
+}

+ 65 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/cataloguedetail/CatalogueDetailService.java

@@ -0,0 +1,65 @@
+package com.yc.ship.module.miniapplet.service.cataloguedetail;
+
+
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailPageReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailSaveReqVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.cataloguedetail.CatalogueDetailDO;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 栏目产品排序 Service 接口
+ *
+ * @author 管理员
+ */
+public interface CatalogueDetailService {
+
+    /**
+     * 创建栏目产品排序
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createCatalogueDetail(@Valid CatalogueDetailSaveReqVO createReqVO);
+
+    /**
+     * 更新栏目产品排序
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateCatalogueDetail(@Valid CatalogueDetailSaveReqVO updateReqVO);
+
+    /**
+     * 删除栏目产品排序
+     *
+     * @param id 编号
+     */
+    void deleteCatalogueDetail(Long id);
+
+    /**
+     * 获得栏目产品排序
+     *
+     * @param id 编号
+     * @return 栏目产品排序
+     */
+    CatalogueDetailDO getCatalogueDetail(Long id);
+
+    /**
+     * 根据栏目ID查小程序产品排序
+     *
+     * @param catalogueId
+     * @return
+     */
+    List<CatalogueDetailDO> getCatalogueDetailList(Long catalogueId);
+
+    /**
+     * 获得栏目产品排序分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 栏目产品排序分页
+     */
+    PageResult<CatalogueDetailDO> getCatalogueDetailPage(CatalogueDetailPageReqVO pageReqVO);
+
+}

+ 81 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/cataloguedetail/CatalogueDetailServiceImpl.java

@@ -0,0 +1,81 @@
+package com.yc.ship.module.miniapplet.service.cataloguedetail;
+
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.framework.common.util.object.BeanUtils;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailPageReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.catalogue.detail.CatalogueDetailSaveReqVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.cataloguedetail.CatalogueDetailDO;
+import com.yc.ship.module.miniapplet.dal.mysql.cataloguedetail.CatalogueDetailMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.yc.ship.module.miniapplet.enums.ErrorCodeConstants.CATALOGUE_DETAIL_NOT_EXISTS;
+
+
+/**
+ * 栏目产品排序 Service 实现类
+ *
+ * @author 管理员
+ */
+@Service
+@Validated
+public class CatalogueDetailServiceImpl implements CatalogueDetailService {
+
+    @Resource
+    private CatalogueDetailMapper catalogueDetailMapper;
+
+    @Override
+    public Long createCatalogueDetail(CatalogueDetailSaveReqVO createReqVO) {
+        // 插入
+        CatalogueDetailDO catalogueDetail = BeanUtils.toBean(createReqVO, CatalogueDetailDO.class);
+        catalogueDetail.setId(IdWorker.getId(catalogueDetail));
+        catalogueDetail.setDeleted(false);
+        catalogueDetailMapper.insert(catalogueDetail);
+        // 返回
+        return catalogueDetail.getId();
+    }
+
+    @Override
+    public void updateCatalogueDetail(CatalogueDetailSaveReqVO updateReqVO) {
+        // 校验存在
+        validateCatalogueDetailExists(updateReqVO.getId());
+        // 更新
+        CatalogueDetailDO updateObj = BeanUtils.toBean(updateReqVO, CatalogueDetailDO.class);
+        catalogueDetailMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteCatalogueDetail(Long id) {
+        // 校验存在
+        validateCatalogueDetailExists(id);
+        // 删除
+        catalogueDetailMapper.deleteById(id);
+    }
+
+    private void validateCatalogueDetailExists(Long id) {
+        if (catalogueDetailMapper.selectById(id) == null) {
+            throw exception(CATALOGUE_DETAIL_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public CatalogueDetailDO getCatalogueDetail(Long id) {
+        return catalogueDetailMapper.selectById(id);
+    }
+
+    @Override
+    public List<CatalogueDetailDO> getCatalogueDetailList(Long catalogueId) {
+        return catalogueDetailMapper.getCatalogueDetailList(catalogueId);
+    }
+
+    @Override
+    public PageResult<CatalogueDetailDO> getCatalogueDetailPage(CatalogueDetailPageReqVO pageReqVO) {
+        return catalogueDetailMapper.selectPage(pageReqVO);
+    }
+
+}

+ 66 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/content/ContentService.java

@@ -0,0 +1,66 @@
+package com.yc.ship.module.miniapplet.service.content;
+
+
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.module.miniapplet.controller.admin.content.vo.ContentPageReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.content.vo.ContentSaveReqVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.content.ContentDO;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 目录内容 Service 接口
+ *
+ * @author 管理员
+ */
+public interface ContentService {
+
+    /**
+     * 创建目录内容
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createContent(@Valid ContentSaveReqVO createReqVO);
+
+    /**
+     * 更新目录内容
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateContent(@Valid ContentSaveReqVO updateReqVO);
+
+    /**
+     * 删除目录内容
+     *
+     * @param id 编号
+     */
+    void deleteContent(Long id);
+
+    /**
+     * 获得目录内容
+     *
+     * @param id 编号
+     * @return 目录内容
+     */
+    ContentDO getContent(Long id);
+
+    /**
+     * 获得目录内容分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 目录内容分页
+     */
+    PageResult<ContentDO> getContentPage(ContentPageReqVO pageReqVO);
+
+
+    /**
+     * 根据目录类型获得目录内容
+     *
+     * @param catalogueId 编号
+     * @return 目录内容
+     */
+    List<ContentDO> getContentList(Long catalogueId,Integer releaseStatus);
+
+}

+ 87 - 0
ship-module-miniapplet/src/main/java/com/yc/ship/module/miniapplet/service/content/ContentServiceImpl.java

@@ -0,0 +1,87 @@
+package com.yc.ship.module.miniapplet.service.content;
+
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.framework.common.util.object.BeanUtils;
+import com.yc.ship.module.miniapplet.controller.admin.content.vo.ContentPageReqVO;
+import com.yc.ship.module.miniapplet.controller.admin.content.vo.ContentSaveReqVO;
+import com.yc.ship.module.miniapplet.dal.dataobject.content.ContentDO;
+import com.yc.ship.module.miniapplet.dal.mysql.content.ContentMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.yc.ship.module.miniapplet.enums.ErrorCodeConstants.CONTENT_NOT_EXISTS;
+
+
+/**
+ * 目录内容 Service 实现类
+ *
+ * @author 管理员
+ */
+@Service
+@Validated
+public class ContentServiceImpl implements ContentService {
+
+    @Resource
+    private ContentMapper contentMapper;
+
+    @Override
+    public Long createContent(ContentSaveReqVO createReqVO) {
+        // 插入
+        ContentDO content = BeanUtils.toBean(createReqVO, ContentDO.class);
+        content.setId(IdWorker.getId(content));
+        if (content.getReleaseStatus() == 1) {
+            content.setReleaseTime(LocalDateTime.now());
+        }
+        contentMapper.insert(content);
+        // 返回
+        return content.getId();
+    }
+
+    @Override
+    public void updateContent(ContentSaveReqVO updateReqVO) {
+        // 校验存在
+        validateContentExists(updateReqVO.getId());
+        // 更新
+        ContentDO updateObj = BeanUtils.toBean(updateReqVO, ContentDO.class);
+        if (updateObj.getReleaseStatus() == 1) {
+            updateObj.setReleaseTime(LocalDateTime.now());
+        }
+        contentMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteContent(Long id) {
+        // 校验存在
+        validateContentExists(id);
+        // 删除
+        contentMapper.deleteById(id);
+    }
+
+    private void validateContentExists(Long id) {
+        if (contentMapper.selectById(id) == null) {
+            throw exception(CONTENT_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public ContentDO getContent(Long id) {
+        return contentMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<ContentDO> getContentPage(ContentPageReqVO pageReqVO) {
+        return contentMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<ContentDO> getContentList(Long catalogueId, Integer releaseStatus) {
+        return contentMapper.getContentList(catalogueId, releaseStatus);
+    }
+
+}