diff --git a/ruoyi-site/ruoyi-h5/doc/dml/h5_component.sql b/ruoyi-site/ruoyi-h5/doc/dml/h5_component.sql new file mode 100644 index 000000000..e41e11241 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/doc/dml/h5_component.sql @@ -0,0 +1,112 @@ +-- H5 组件库系统建表脚本 +-- 创建时间:2026-03-06 + +-- 1. 组件分类表 (两级分类) +CREATE TABLE `h5_component_category` ( + [id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37) BIGINT NOT NULL COMMENT '主键 ID', + `parent_id` BIGINT NOT NULL DEFAULT 0 COMMENT '父分类 ID(0 为一级分类)', + `name` VARCHAR(100) NOT NULL COMMENT '分类名称', + `type` VARCHAR(50) NOT NULL COMMENT '分类类型 (text/image/extension/background/music)', + `sort` INT NOT NULL DEFAULT 0 COMMENT '排序值', + `icon` VARCHAR(500) DEFAULT NULL COMMENT '分类图标 URL', + `description` VARCHAR(500) DEFAULT NULL COMMENT '分类描述', + `is_system` TINYINT NOT NULL DEFAULT 0 COMMENT '是否系统预设 (1:是 0:否)', + `create_time` BIGINT NOT NULL COMMENT '创建时间 (Unix 时间戳秒级)', + `update_time` BIGINT DEFAULT NULL COMMENT '更新时间 (Unix 时间戳秒级)', + PRIMARY KEY ([id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37)), + INDEX `idx_parent_id` (`parent_id`), + INDEX `idx_type` (`type`), + INDEX `idx_sort` (`sort`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='H5 组件分类表'; + +-- 2. 组件表 +CREATE TABLE `h5_component` ( + [id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37) BIGINT NOT NULL COMMENT '主键 ID', + `name` VARCHAR(200) NOT NULL COMMENT '组件名称', + `type` VARCHAR(50) NOT NULL COMMENT '组件类型 (text/image/form/map/background/music)', + `thumbnail_url` VARCHAR(500) DEFAULT NULL COMMENT '缩略图 URL', + `preview_url` VARCHAR(500) DEFAULT NULL COMMENT '预览图 URL', + `content` JSON NOT NULL COMMENT '组件完整配置数据 (JSON 格式)', + `usage_count` BIGINT NOT NULL DEFAULT 0 COMMENT '使用次数统计', + `like_count` BIGINT NOT NULL DEFAULT 0 COMMENT '点赞数量', + `is_premium` TINYINT NOT NULL DEFAULT 0 COMMENT '是否付费组件 (1:是 0:否)', + `is_system` TINYINT NOT NULL DEFAULT 0 COMMENT '是否系统预设 (1:是 0:否)', + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态 (1:启用 0:禁用)', + `remark` VARCHAR(1000) DEFAULT NULL COMMENT '备注说明', + `create_time` BIGINT NOT NULL COMMENT '创建时间 (Unix 时间戳秒级)', + `update_time` BIGINT DEFAULT NULL COMMENT '更新时间 (Unix 时间戳秒级)', + PRIMARY KEY ([id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37)), + INDEX `idx_type` (`type`), + INDEX `idx_status` (`status`), + INDEX `idx_usage_count` (`usage_count`), + INDEX `idx_create_time` (`create_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='H5 组件表'; + +-- 3. 标签表 +CREATE TABLE `h5_tag` ( + [id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37) BIGINT NOT NULL COMMENT '主键 ID', + `name` VARCHAR(100) NOT NULL COMMENT '标签名称', + `type` VARCHAR(50) DEFAULT NULL COMMENT '标签适用类型,NULL 表示通用', + `sort` INT NOT NULL DEFAULT 0 COMMENT '排序值', + `is_hot` TINYINT NOT NULL DEFAULT 0 COMMENT '是否热门 (1:是 0:否)', + `use_count` BIGINT NOT NULL DEFAULT 0 COMMENT '使用次数', + `create_time` BIGINT NOT NULL COMMENT '创建时间 (Unix 时间戳秒级)', + `update_time` BIGINT DEFAULT NULL COMMENT '更新时间 (Unix 时间戳秒级)', + PRIMARY KEY ([id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37)), + UNIQUE INDEX `uk_name` (`name`), + INDEX `idx_type` (`type`), + INDEX `idx_is_hot` (`is_hot`), + INDEX `idx_sort` (`sort`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='H5 组件标签表'; + +-- 4. 组件分类关联表 (多对多) +CREATE TABLE `h5_component_category_rel` ( + [id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37) BIGINT NOT NULL COMMENT '主键 ID', + `component_id` BIGINT NOT NULL COMMENT '组件 ID', + `category_id` BIGINT NOT NULL COMMENT '分类 ID', + `create_time` BIGINT NOT NULL COMMENT '创建时间 (Unix 时间戳秒级)', + PRIMARY KEY ([id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37)), + UNIQUE INDEX `uk_component_category` (`component_id`, `category_id`), + INDEX `idx_category_id` (`category_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='H5 组件与分类关联表'; + +-- 5. 组件标签关联表 (多对多) +CREATE TABLE `h5_component_tag_rel` ( + [id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37) BIGINT NOT NULL COMMENT '主键 ID', + `component_id` BIGINT NOT NULL COMMENT '组件 ID', + `tag_id` BIGINT NOT NULL COMMENT '标签 ID', + `create_time` BIGINT NOT NULL COMMENT '创建时间 (Unix 时间戳秒级)', + PRIMARY KEY ([id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37)), + UNIQUE INDEX `uk_component_tag` (`component_id`, `tag_id`), + INDEX `idx_tag_id` (`tag_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='H5 组件与标签关联表'; + +-- 初始化数据 - 一级分类 (固定 5 大类) +INSERT INTO `h5_component_category` ([id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37), `parent_id`, [name](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-common\ruoyi-common-translation\src\main\java\org\dromara\common\translation\core\TranslationInterface.java#L27-L27), `type`, `sort`, `icon`, `description`, `is_system`, `create_time`) VALUES +(1, 0, '文字', 'text', 1, NULL, '文字类组件,包括标题、正文、艺术字等', 1, UNIX_TIMESTAMP()), +(2, 0, '图片', 'image', 2, NULL, '图片类组件,包括产品图、背景图、装饰图等', 1, UNIX_TIMESTAMP()), +(3, 0, '扩展', 'extension', 3, NULL, '扩展功能组件,包括表单、定位、微信头像等', 1, UNIX_TIMESTAMP()), +(4, 0, '背景', 'background', 4, NULL, '背景类组件,包括渐变色、图案背景等', 1, UNIX_TIMESTAMP()), +(5, 0, '音乐', 'music', 5, NULL, '音乐音频类组件', 1, UNIX_TIMESTAMP()); + +-- 初始化数据 - 二级分类示例 +INSERT INTO `h5_component_category` ([id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37), `parent_id`, [name](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-common\ruoyi-common-translation\src\main\java\org\dromara\common\translation\core\TranslationInterface.java#L27-L27), `type`, `sort`, `is_system`, `create_time`) VALUES +(101, 1, '标题文字', 'text', 1, 1, UNIX_TIMESTAMP()), +(102, 1, '正文文本', 'text', 2, 1, UNIX_TIMESTAMP()), +(103, 1, '艺术字', 'text', 3, 1, UNIX_TIMESTAMP()), +(201, 2, '产品图片', 'image', 1, 1, UNIX_TIMESTAMP()), +(202, 2, '装饰元素', 'image', 2, 1, UNIX_TIMESTAMP()), +(301, 3, '表单组件', 'extension', 1, 1, UNIX_TIMESTAMP()), +(302, 3, '地图定位', 'extension', 2, 1, UNIX_TIMESTAMP()), +(303, 3, '社交组件', 'extension', 3, 1, UNIX_TIMESTAMP()); + +-- 初始化数据 - 标签示例 +INSERT INTO `h5_tag` ([id](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-admin\src\test\java\org\dromara\test\DemoUnitTest.java#L37-L37), [name](file://D:\project\gitee\RuoYi-Vue-Plus\ruoyi-common\ruoyi-common-translation\src\main\java\org\dromara\common\translation\core\TranslationInterface.java#L27-L27), `type`, `sort`, `is_hot`, `create_time`) VALUES +(1, '简约', NULL, 1, 1, UNIX_TIMESTAMP()), +(2, '商务', NULL, 2, 1, UNIX_TIMESTAMP()), +(3, '创意', NULL, 3, 1, UNIX_TIMESTAMP()), +(4, '节日', NULL, 4, 0, UNIX_TIMESTAMP()), +(5, '促销', NULL, 5, 1, UNIX_TIMESTAMP()), +(6, '中国风', NULL, 6, 0, UNIX_TIMESTAMP()), +(7, '科技感', NULL, 7, 1, UNIX_TIMESTAMP()), +(8, '清新', NULL, 8, 0, UNIX_TIMESTAMP()); diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/controller/H5ComponentCategoryController.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/controller/H5ComponentCategoryController.java new file mode 100644 index 000000000..82a63b644 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/controller/H5ComponentCategoryController.java @@ -0,0 +1,88 @@ +package com.luban.api.controller; + +import com.luban.api.entity.H5ComponentCategory; +import com.luban.api.service.H5ComponentCategoryService; +import com.luban.api.vo.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * H5 组件分类控制器 + * + * @author system + * @since 2026-03-06 + */ +@Tag(name = "H5 组件分类管理") +@RestController +@RequestMapping("/h5/category") +@RequiredArgsConstructor +public class H5ComponentCategoryController { + + private final H5ComponentCategoryService h5ComponentCategoryService; + + @Operation(summary = "查询所有一级分类") + @GetMapping("/top-level") + public Result> getTopLevelCategories() { + List result = h5ComponentCategoryService.getTopLevelCategories(); + return new Result<>(result); + } + + @Operation(summary = "根据父分类 ID 查询子分类") + @GetMapping("/children") + public Result> getChildCategories( + @Parameter(description = "父分类 ID", required = true) @RequestParam Long parentId) { + + List result = h5ComponentCategoryService.getChildCategories(parentId); + return new Result<>(result); + } + + @Operation(summary = "根据类型查询分类") + @GetMapping("/by-type") + public Result> getCategoriesByType( + @Parameter(description = "组件类型", required = true) @RequestParam String type) { + + List result = h5ComponentCategoryService.getCategoriesByType(type); + return new Result<>(result); + } + + @Operation(summary = "获取分类树形结构") + @GetMapping("/tree") + public Result> getCategoryTree() { + List result = h5ComponentCategoryService.getCategoryTree(); + return new Result<>(result); + } + + @Operation(summary = "创建分类") + @PostMapping + public Result create( + @Parameter(description = "分类信息", required = true) @RequestBody @Validated H5ComponentCategory category) { + + Long id = h5ComponentCategoryService.create(category); + return new Result<>(id); + } + + @Operation(summary = "更新分类") + @PutMapping("/{id}") + public Result update( + @Parameter(description = "分类 ID", required = true) @PathVariable Long id, + @Parameter(description = "分类信息", required = true) @RequestBody @Validated H5ComponentCategory category) { + + category.setId(id); + h5ComponentCategoryService.update(category); + return new Result<>(); + } + + @Operation(summary = "删除分类") + @DeleteMapping("/{id}") + public Result deleteById( + @Parameter(description = "分类 ID", required = true) @PathVariable Long id) { + h5ComponentCategoryService.deleteById(id); + return new Result<>(); + } +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/controller/H5ComponentController.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/controller/H5ComponentController.java new file mode 100644 index 000000000..530c4bdd6 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/controller/H5ComponentController.java @@ -0,0 +1,119 @@ +package com.luban.api.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.luban.api.entity.H5Component; +import com.luban.api.service.H5ComponentService; +import com.luban.api.vo.H5ComponentVO; +import com.luban.api.vo.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * H5 组件控制器 + * + * @author system + * @since 2026-03-06 + */ +@Tag(name = "H5 组件管理") +@RestController +@RequestMapping("/h5/component") +@RequiredArgsConstructor +public class H5ComponentController { + + private final H5ComponentService h5ComponentService; + + @Operation(summary = "根据 ID 查询组件") + @GetMapping("/{id}") + public Result getById( + @Parameter(description = "组件 ID") @PathVariable Long id) { + H5ComponentVO vo = h5ComponentService.getById(id); + return new Result<>(vo); + } + + @Operation(summary = "分页查询组件列表") + @GetMapping("/page") + public Result> getPage( + @Parameter(description = "页码", required = true) @RequestParam Integer page, + @Parameter(description = "每页大小", required = true) @RequestParam Integer pageSize, + @Parameter(description = "组件类型") @RequestParam(required = false) String type, + @Parameter(description = "搜索关键词") @RequestParam(required = false) String keyword, + @Parameter(description = "分类 ID") @RequestParam(required = false) Long categoryId, + @Parameter(description = "标签 ID") @RequestParam(required = false) Long tagId) { + + Page result = h5ComponentService.getPage(page, pageSize, type, keyword, categoryId, tagId); + return new Result<>(result); + } + + @Operation(summary = "创建组件") + @PostMapping + public Result create( + @Parameter(description = "组件信息", required = true) @RequestBody @Validated H5Component component, + @Parameter(description = "分类 ID 列表") @RequestBody(required = false) List categoryIds, + @Parameter(description = "标签 ID 列表") @RequestBody(required = false) List tagIds) { + + Long id = h5ComponentService.create(component, categoryIds, tagIds); + return new Result<>(id); + } + + @Operation(summary = "更新组件") + @PutMapping("/{id}") + public Result update( + @Parameter(description = "组件 ID", required = true) @PathVariable Long id, + @Parameter(description = "组件信息", required = true) @RequestBody @Validated H5Component component, + @Parameter(description = "分类 ID 列表") @RequestBody(required = false) List categoryIds, + @Parameter(description = "标签 ID 列表") @RequestBody(required = false) List tagIds) { + + component.setId(id); + h5ComponentService.update(component, categoryIds, tagIds); + return new Result<>(); + } + + @Operation(summary = "删除组件") + @DeleteMapping("/{id}") + public Result deleteById( + @Parameter(description = "组件 ID", required = true) @PathVariable Long id) { + h5ComponentService.deleteById(id); + return new Result<>(); + } + + @Operation(summary = "批量删除组件") + @DeleteMapping("/batch") + public Result deleteBatch( + @Parameter(description = "组件 ID 列表", required = true) @RequestBody List ids) { + h5ComponentService.deleteBatch(ids); + return new Result<>(); + } + + @Operation(summary = "增加使用次数") + @PostMapping("/{id}/usage") + public Result incrementUsageCount( + @Parameter(description = "组件 ID", required = true) @PathVariable Long id) { + h5ComponentService.incrementUsageCount(id); + return new Result<>(); + } + + @Operation(summary = "增加点赞数") + @PostMapping("/{id}/like") + public Result incrementLikeCount( + @Parameter(description = "组件 ID", required = true) @PathVariable Long id) { + h5ComponentService.incrementLikeCount(id); + return new Result<>(); + } + + @Operation(summary = "获取热门组件") + @GetMapping("/hot") + public Result> getHotComponents( + @Parameter(description = "组件类型") @RequestParam(required = false) String type, + @Parameter(description = "数量限制", required = true) @RequestParam Integer limit) { + + List result = h5ComponentService.getHotComponents(type, limit); + return new Result<>(result); + } +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/controller/H5TagController.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/controller/H5TagController.java new file mode 100644 index 000000000..113453041 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/controller/H5TagController.java @@ -0,0 +1,81 @@ +package com.luban.api.controller; + +import com.luban.api.entity.H5Tag; +import com.luban.api.service.H5TagService; +import com.luban.api.vo.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * H5 组件标签控制器 + * + * @author system + * @since 2026-03-06 + */ +@Tag(name = "H5 组件标签管理") +@RestController +@RequestMapping("/h5/tag") +@RequiredArgsConstructor +public class H5TagController { + + private final H5TagService h5TagService; + + @Operation(summary = "查询所有标签") + @GetMapping + public Result> getAllTags() { + List result = h5TagService.getAllTags(); + return new Result<>(result); + } + + @Operation(summary = "查询热门标签") + @GetMapping("/hot") + public Result> getHotTags( + @Parameter(description = "数量限制", required = true) @RequestParam Integer limit) { + + List result = h5TagService.getHotTags(limit); + return new Result<>(result); + } + + @Operation(summary = "根据类型查询标签") + @GetMapping("/by-type") + public Result> getTagsByType( + @Parameter(description = "组件类型") @RequestParam(required = false) String type) { + + List result = h5TagService.getTagsByType(type); + return new Result<>(result); + } + + @Operation(summary = "创建标签") + @PostMapping + public Result create( + @Parameter(description = "标签信息", required = true) @RequestBody @Validated H5Tag tag) { + + Long id = h5TagService.create(tag); + return new Result<>(id); + } + + @Operation(summary = "更新标签") + @PutMapping("/{id}") + public Result update( + @Parameter(description = "标签 ID", required = true) @PathVariable Long id, + @Parameter(description = "标签信息", required = true) @RequestBody @Validated H5Tag tag) { + + tag.setId(id); + h5TagService.update(tag); + return new Result<>(); + } + + @Operation(summary = "删除标签") + @DeleteMapping("/{id}") + public Result deleteById( + @Parameter(description = "标签 ID", required = true) @PathVariable Long id) { + h5TagService.deleteById(id); + return new Result<>(); + } +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/convert/H5ComponentConvert.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/convert/H5ComponentConvert.java new file mode 100644 index 000000000..57d5ff148 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/convert/H5ComponentConvert.java @@ -0,0 +1,26 @@ +package com.luban.api.convert; + +import com.luban.api.entity.H5Component; +import com.luban.api.vo.H5ComponentVO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * H5 组件转换类 + * + * @author system + * @since 2026-03-06 + */ +@Mapper +public interface H5ComponentConvert { + + H5ComponentConvert INSTANCE = Mappers.getMapper(H5ComponentConvert.class); + + /** + * Entity 转 VO + * + * @param entity 实体 + * @return VO + */ + H5ComponentVO toVO(H5Component entity); +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentCategoryDao.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentCategoryDao.java new file mode 100644 index 000000000..58651237f --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentCategoryDao.java @@ -0,0 +1,16 @@ +package com.luban.api.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.luban.api.entity.H5ComponentCategory; +import org.apache.ibatis.annotations.Mapper; + +/** + * H5 组件分类 Mapper 接口 + * + * @author system + * @since 2026-03-06 + */ +@Mapper +public interface H5ComponentCategoryDao extends BaseMapper { + +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentCategoryRelDao.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentCategoryRelDao.java new file mode 100644 index 000000000..338c38208 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentCategoryRelDao.java @@ -0,0 +1,16 @@ +package com.luban.api.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.luban.api.entity.H5ComponentCategoryRel; +import org.apache.ibatis.annotations.Mapper; + +/** + * H5 组件与分类关联 Mapper 接口 + * + * @author system + * @since 2026-03-06 + */ +@Mapper +public interface H5ComponentCategoryRelDao extends BaseMapper { + +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentDao.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentDao.java new file mode 100644 index 000000000..9647c0262 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentDao.java @@ -0,0 +1,16 @@ +package com.luban.api.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.luban.api.entity.H5Component; +import org.apache.ibatis.annotations.Mapper; + +/** + * H5 组件 Mapper 接口 + * + * @author system + * @since 2026-03-06 + */ +@Mapper +public interface H5ComponentDao extends BaseMapper { + +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentTagRelDao.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentTagRelDao.java new file mode 100644 index 000000000..fc46a66f0 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5ComponentTagRelDao.java @@ -0,0 +1,16 @@ +package com.luban.api.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.luban.api.entity.H5ComponentTagRel; +import org.apache.ibatis.annotations.Mapper; + +/** + * H5 组件与标签关联 Mapper 接口 + * + * @author system + * @since 2026-03-06 + */ +@Mapper +public interface H5ComponentTagRelDao extends BaseMapper { + +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5TagDao.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5TagDao.java new file mode 100644 index 000000000..79a2adceb --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/dao/H5TagDao.java @@ -0,0 +1,16 @@ +package com.luban.api.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.luban.api.entity.H5Tag; +import org.apache.ibatis.annotations.Mapper; + +/** + * H5 组件标签 Mapper 接口 + * + * @author system + * @since 2026-03-06 + */ +@Mapper +public interface H5TagDao extends BaseMapper { + +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5Component.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5Component.java new file mode 100644 index 000000000..7c51da21e --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5Component.java @@ -0,0 +1,94 @@ +package com.luban.api.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * H5 组件实体 + * + * @author system + * @since 2026-03-06 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("h5_component") +@NoArgsConstructor +@AllArgsConstructor +public class H5Component { + + /** + * 主键 ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 组件名称 + */ + private String name; + + /** + * 组件类型 (text/image/form/map/background/music) + */ + private String type; + + /** + * 缩略图 URL + */ + private String thumbnailUrl; + + /** + * 预览图 URL + */ + private String previewUrl; + + /** + * 组件完整配置数据 (JSON 格式) + */ + private String content; + + /** + * 使用次数统计 + */ + private Long usageCount; + + /** + * 点赞数量 + */ + private Long likeCount; + + /** + * 是否付费组件 (1:是 0:否) + */ + private Integer isPremium; + + /** + * 是否系统预设 (1:是 0:否) + */ + private Integer isSystem; + + /** + * 状态 (1:启用 0:禁用) + */ + private Integer status; + + /** + * 备注说明 + */ + private String remark; + + /** + * 创建时间 (Unix 时间戳秒级) + */ + private Long createTime; + + /** + * 更新时间 (Unix 时间戳秒级) + */ + private Long updateTime; +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5ComponentCategory.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5ComponentCategory.java new file mode 100644 index 000000000..2a75a7e66 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5ComponentCategory.java @@ -0,0 +1,74 @@ +package com.luban.api.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * H5 组件分类实体 + * + * @author system + * @since 2026-03-06 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("h5_component_category") +@NoArgsConstructor +@AllArgsConstructor +public class H5ComponentCategory { + + /** + * 主键 ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 父分类 ID(0 为一级分类) + */ + private Long parentId; + + /** + * 分类名称 + */ + private String name; + + /** + * 分类类型 (text/image/extension/background/music) + */ + private String type; + + /** + * 排序值 + */ + private Integer sort; + + /** + * 分类图标 URL + */ + private String icon; + + /** + * 分类描述 + */ + private String description; + + /** + * 是否系统预设 (1:是 0:否) + */ + private Integer isSystem; + + /** + * 创建时间 (Unix 时间戳秒级) + */ + private Long createTime; + + /** + * 更新时间 (Unix 时间戳秒级) + */ + private Long updateTime; +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5ComponentCategoryRel.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5ComponentCategoryRel.java new file mode 100644 index 000000000..842d8a5bd --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5ComponentCategoryRel.java @@ -0,0 +1,44 @@ +package com.luban.api.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * H5 组件与分类关联实体 + * + * @author system + * @since 2026-03-06 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("h5_component_category_rel") +@NoArgsConstructor +@AllArgsConstructor +public class H5ComponentCategoryRel { + + /** + * 主键 ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 组件 ID + */ + private Long componentId; + + /** + * 分类 ID + */ + private Long categoryId; + + /** + * 创建时间 (Unix 时间戳秒级) + */ + private Long createTime; +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5ComponentTagRel.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5ComponentTagRel.java new file mode 100644 index 000000000..9a1979919 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5ComponentTagRel.java @@ -0,0 +1,44 @@ +package com.luban.api.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * H5 组件与标签关联实体 + * + * @author system + * @since 2026-03-06 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("h5_component_tag_rel") +@NoArgsConstructor +@AllArgsConstructor +public class H5ComponentTagRel { + + /** + * 主键 ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 组件 ID + */ + private Long componentId; + + /** + * 标签 ID + */ + private Long tagId; + + /** + * 创建时间 (Unix 时间戳秒级) + */ + private Long createTime; +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5Tag.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5Tag.java new file mode 100644 index 000000000..e225f8069 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/entity/H5Tag.java @@ -0,0 +1,64 @@ +package com.luban.api.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * H5 组件标签实体 + * + * @author system + * @since 2026-03-06 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("h5_tag") +@NoArgsConstructor +@AllArgsConstructor +public class H5Tag { + + /** + * 主键 ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 标签名称 + */ + private String name; + + /** + * 标签适用类型,NULL 表示通用 + */ + private String type; + + /** + * 排序值 + */ + private Integer sort; + + /** + * 是否热门 (1:是 0:否) + */ + private Integer isHot; + + /** + * 使用次数 + */ + private Long useCount; + + /** + * 创建时间 (Unix 时间戳秒级) + */ + private Long createTime; + + /** + * 更新时间 (Unix 时间戳秒级) + */ + private Long updateTime; +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/H5ComponentCategoryService.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/H5ComponentCategoryService.java new file mode 100644 index 000000000..88283ba62 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/H5ComponentCategoryService.java @@ -0,0 +1,66 @@ +package com.luban.api.service; + +import com.luban.api.entity.H5ComponentCategory; + +import java.util.List; + +/** + * H5 组件分类服务接口 + * + * @author system + * @since 2026-03-06 + */ +public interface H5ComponentCategoryService { + + /** + * 查询所有一级分类 + * + * @return 一级分类列表 + */ + List getTopLevelCategories(); + + /** + * 根据父分类 ID 查询子分类 + * + * @param parentId 父分类 ID + * @return 子分类列表 + */ + List getChildCategories(Long parentId); + + /** + * 根据类型查询分类 + * + * @param type 组件类型 + * @return 分类列表 + */ + List getCategoriesByType(String type); + + /** + * 创建分类 + * + * @param category 分类信息 + * @return 分类 ID + */ + Long create(H5ComponentCategory category); + + /** + * 更新分类 + * + * @param category 分类信息 + */ + void update(H5ComponentCategory category); + + /** + * 删除分类 + * + * @param id 分类 ID + */ + void deleteById(Long id); + + /** + * 获取分类树形结构 + * + * @return 树形分类列表 + */ + List getCategoryTree(); +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/H5ComponentService.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/H5ComponentService.java new file mode 100644 index 000000000..83a7c675f --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/H5ComponentService.java @@ -0,0 +1,93 @@ +package com.luban.api.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.luban.api.entity.H5Component; +import com.luban.api.vo.H5ComponentVO; + +import java.util.List; + +/** + * H5 组件服务接口 + * + * @author system + * @since 2026-03-06 + */ +public interface H5ComponentService { + + /** + * 根据 ID 查询组件 + * + * @param id 组件 ID + * @return 组件信息 + */ + H5ComponentVO getById(Long id); + + /** + * 分页查询组件列表 + * + * @param page 页码 + * @param pageSize 每页大小 + * @param type 组件类型 (可选) + * @param keyword 搜索关键词 (可选) + * @param categoryId 分类 ID(可选) + * @param tagId 标签 ID(可选) + * @return 分页数据 + */ + Page getPage(Integer page, Integer pageSize, String type, String keyword, Long categoryId, Long tagId); + + /** + * 创建组件 + * + * @param component 组件信息 + * @param categoryIds 分类 ID 列表 + * @param tagIds 标签 ID 列表 + * @return 组件 ID + */ + Long create(H5Component component, List categoryIds, List tagIds); + + /** + * 更新组件 + * + * @param component 组件信息 + * @param categoryIds 分类 ID 列表 + * @param tagIds 标签 ID 列表 + */ + void update(H5Component component, List categoryIds, List tagIds); + + /** + * 删除组件 + * + * @param id 组件 ID + */ + void deleteById(Long id); + + /** + * 批量删除组件 + * + * @param ids 组件 ID 列表 + */ + void deleteBatch(List ids); + + /** + * 增加使用次数 + * + * @param id 组件 ID + */ + void incrementUsageCount(Long id); + + /** + * 增加点赞数 + * + * @param id 组件 ID + */ + void incrementLikeCount(Long id); + + /** + * 获取热门组件 + * + * @param type 组件类型 (可选) + * @param limit 数量限制 + * @return 组件列表 + */ + List getHotComponents(String type, Integer limit); +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/H5TagService.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/H5TagService.java new file mode 100644 index 000000000..23857cea2 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/H5TagService.java @@ -0,0 +1,66 @@ +package com.luban.api.service; + +import com.luban.api.entity.H5Tag; + +import java.util.List; + +/** + * H5 组件标签服务接口 + * + * @author system + * @since 2026-03-06 + */ +public interface H5TagService { + + /** + * 查询所有标签 + * + * @return 标签列表 + */ + List getAllTags(); + + /** + * 查询热门标签 + * + * @param limit 数量限制 + * @return 热门标签列表 + */ + List getHotTags(Integer limit); + + /** + * 根据类型查询标签 + * + * @param type 组件类型 + * @return 标签列表 + */ + List getTagsByType(String type); + + /** + * 创建标签 + * + * @param tag 标签信息 + * @return 标签 ID + */ + Long create(H5Tag tag); + + /** + * 更新标签 + * + * @param tag 标签信息 + */ + void update(H5Tag tag); + + /** + * 删除标签 + * + * @param id 标签 ID + */ + void deleteById(Long id); + + /** + * 增加标签使用次数 + * + * @param id 标签 ID + */ + void incrementUseCount(Long id); +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/impl/H5ComponentCategoryServiceImpl.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/impl/H5ComponentCategoryServiceImpl.java new file mode 100644 index 000000000..23ebe8fae --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/impl/H5ComponentCategoryServiceImpl.java @@ -0,0 +1,110 @@ +package com.luban.api.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.luban.api.dao.H5ComponentCategoryDao; +import com.luban.api.entity.H5ComponentCategory; +import com.luban.api.service.H5ComponentCategoryService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.List; + +/** + * H5 组件分类服务实现类 + * + * @author system + * @since 2026-03-06 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class H5ComponentCategoryServiceImpl extends ServiceImpl implements H5ComponentCategoryService { + + private final H5ComponentCategoryDao h5ComponentCategoryDao; + + @Override + public List getTopLevelCategories() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5ComponentCategory::getParentId, 0L) + .orderByAsc(H5ComponentCategory::getSort); + + return baseMapper.selectList(wrapper); + } + + @Override + public List getChildCategories(Long parentId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5ComponentCategory::getParentId, parentId) + .orderByAsc(H5ComponentCategory::getSort); + + return baseMapper.selectList(wrapper); + } + + @Override + public List getCategoriesByType(String type) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5ComponentCategory::getType, type) + .orderByAsc(H5ComponentCategory::getParentId) + .orderByAsc(H5ComponentCategory::getSort); + + return baseMapper.selectList(wrapper); + } + + @Override + public Long create(H5ComponentCategory category) { + long now = Instant.now().getEpochSecond(); + + if (category.getParentId() == null) { + category.setParentId(0L); + } + + category.setCreateTime(now); + category.setUpdateTime(now); + + baseMapper.insert(category); + + return category.getId(); + } + + @Override + public void update(H5ComponentCategory category) { + category.setUpdateTime(Instant.now().getEpochSecond()); + baseMapper.updateById(category); + } + + @Override + public void deleteById(Long id) { + // 检查是否有子分类 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5ComponentCategory::getParentId, id); + Long count = baseMapper.selectCount(wrapper); + + if (count > 0) { + throw new RuntimeException("该分类下存在子分类,无法删除"); + } + + baseMapper.deleteById(id); + } + + @Override + public List getCategoryTree() { + // 查询所有一级分类 + List topLevelCategories = getTopLevelCategories(); + + // 为每个一级分类查询子分类 + for (H5ComponentCategory topLevel : topLevelCategories) { + List childCategories = getChildCategories(topLevel.getId()); + // 这里简单处理,实际可以考虑使用专门的树形结构 VO + if (!childCategories.isEmpty()) { + // 可以在返回时添加额外信息 + log.debug("分类 {} 有 {} 个子分类", topLevel.getName(), childCategories.size()); + } + } + + return topLevelCategories; + } +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/impl/H5ComponentServiceImpl.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/impl/H5ComponentServiceImpl.java new file mode 100644 index 000000000..b5c2edac3 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/impl/H5ComponentServiceImpl.java @@ -0,0 +1,250 @@ +package com.luban.api.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.luban.api.dao.H5ComponentCategoryRelDao; +import com.luban.api.dao.H5ComponentDao; +import com.luban.api.dao.H5ComponentTagRelDao; +import com.luban.api.entity.H5Component; +import com.luban.api.entity.H5ComponentCategoryRel; +import com.luban.api.entity.H5ComponentTagRel; +import com.luban.api.service.H5ComponentService; +import com.luban.api.convert.H5ComponentConvert; +import com.luban.api.vo.H5ComponentVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.List; +import java.util.stream.Collectors; + +/** + * H5 组件服务实现类 + * + * @author system + * @since 2026-03-06 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class H5ComponentServiceImpl extends ServiceImpl implements H5ComponentService { + + private final H5ComponentCategoryRelDao h5ComponentCategoryRelDao; + private final H5ComponentTagRelDao h5ComponentTagRelDao; + private final H5ComponentConvert h5ComponentConvert; + + @Override + public H5ComponentVO getById(Long id) { + H5Component component = baseMapper.selectById(id); + return h5ComponentConvert.toVO(component); + } + + @Override + public Page getPage(Integer page, Integer pageSize, String type, String keyword, Long categoryId, Long tagId) { + Page mpPage = new Page<>(page, pageSize); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5Component::getStatus, 1); // 只查询启用的组件 + + wrapper.eq(StrUtil.isNotBlank(type), H5Component::getType, type); + + if (StrUtil.isNotBlank(keyword)) { + wrapper.and(w -> w.like(H5Component::getName, keyword) + .or().like(H5Component::getRemark, keyword)); + } + + // 按分类筛选 + if (categoryId != null) { + List componentIds = getComponentIdsByCategory(categoryId); + if (componentIds.isEmpty()) { + return new Page(page, pageSize).setRecords(List.of()); + } + wrapper.in(H5Component::getId, componentIds); + } + + // 按标签筛选 + if (tagId != null) { + List componentIds = getComponentIdsByTag(tagId); + if (componentIds.isEmpty()) { + return new Page(page, pageSize).setRecords(List.of()); + } + wrapper.in(H5Component::getId, componentIds); + } + + wrapper.orderByDesc(H5Component::getUsageCount) + .orderByDesc(H5Component::getCreateTime); + + Page resultPage = baseMapper.selectPage(mpPage, wrapper); + + Page voPage = new Page<>(resultPage.getCurrent(), resultPage.getSize(), resultPage.getTotal()); + List voList = resultPage.getRecords().stream() + .map(h5ComponentConvert::toVO) + .collect(Collectors.toList()); + voPage.setRecords(voList); + + return voPage; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long create(H5Component component, List categoryIds, List tagIds) { + long now = Instant.now().getEpochSecond(); + + // 初始化数据 + component.setCreateTime(now); + component.setUpdateTime(now); + component.setUsageCount(0L); + component.setLikeCount(0L); + component.setStatus(1); + + baseMapper.insert(component); + + // 保存分类关联 + if (categoryIds != null && !categoryIds.isEmpty()) { + saveCategoryRels(component.getId(), categoryIds, now); + } + + // 保存标签关联 + if (tagIds != null && !tagIds.isEmpty()) { + saveTagRels(component.getId(), tagIds, now); + } + + return component.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(H5Component component, List categoryIds, List tagIds) { + long now = Instant.now().getEpochSecond(); + + component.setUpdateTime(now); + baseMapper.updateById(component); + + // 更新分类关联 + if (categoryIds != null) { + deleteCategoryRels(component.getId()); + if (!categoryIds.isEmpty()) { + saveCategoryRels(component.getId(), categoryIds, now); + } + } + + // 更新标签关联 + if (tagIds != null) { + deleteTagRels(component.getId()); + if (!tagIds.isEmpty()) { + saveTagRels(component.getId(), tagIds, now); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteById(Long id) { + // 删除组件 + baseMapper.deleteById(id); + + // 删除分类关联 + deleteCategoryRels(id); + + // 删除标签关联 + deleteTagRels(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteBatch(List ids) { + ids.forEach(this::deleteById); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void incrementUsageCount(Long id) { + H5Component component = baseMapper.selectById(id); + if (component != null) { + component.setUsageCount(component.getUsageCount() + 1); + component.setUpdateTime(Instant.now().getEpochSecond()); + baseMapper.updateById(component); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void incrementLikeCount(Long id) { + H5Component component = baseMapper.selectById(id); + if (component != null) { + component.setLikeCount(component.getLikeCount() + 1); + component.setUpdateTime(Instant.now().getEpochSecond()); + baseMapper.updateById(component); + } + } + + @Override + public List getHotComponents(String type, Integer limit) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5Component::getStatus, 1); + + if (StrUtil.isNotBlank(type)) { + wrapper.eq(H5Component::getType, type); + } + + wrapper.orderByDesc(H5Component::getUsageCount) + .last("LIMIT " + limit); + + List components = baseMapper.selectList(wrapper); + return components.stream() + .map(h5ComponentConvert::toVO) + .collect(Collectors.toList()); + } + + private List getComponentIdsByCategory(Long categoryId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5ComponentCategoryRel::getCategoryId, categoryId); + wrapper.select(H5ComponentCategoryRel::getComponentId); + + return h5ComponentCategoryRelDao.selectList(wrapper).stream() + .map(H5ComponentCategoryRel::getComponentId) + .collect(Collectors.toList()); + } + + private List getComponentIdsByTag(Long tagId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5ComponentTagRel::getTagId, tagId); + wrapper.select(H5ComponentTagRel::getComponentId); + + return h5ComponentTagRelDao.selectList(wrapper).stream() + .map(H5ComponentTagRel::getComponentId) + .collect(Collectors.toList()); + } + + private void saveCategoryRels(Long componentId, List categoryIds, long createTime) { + List rels = categoryIds.stream() + .map(categoryId -> new H5ComponentCategoryRel(null, componentId, categoryId, createTime)) + .collect(Collectors.toList()); + + rels.forEach(h5ComponentCategoryRelDao::insert); + } + + private void saveTagRels(Long componentId, List tagIds, long createTime) { + List rels = tagIds.stream() + .map(tagId -> new H5ComponentTagRel(null, componentId, tagId, createTime)) + .collect(Collectors.toList()); + + rels.forEach(h5ComponentTagRelDao::insert); + } + + private void deleteCategoryRels(Long componentId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5ComponentCategoryRel::getComponentId, componentId); + h5ComponentCategoryRelDao.delete(wrapper); + } + + private void deleteTagRels(Long componentId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5ComponentTagRel::getComponentId, componentId); + h5ComponentTagRelDao.delete(wrapper); + } +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/impl/H5TagServiceImpl.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/impl/H5TagServiceImpl.java new file mode 100644 index 000000000..e038a9ea5 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/service/impl/H5TagServiceImpl.java @@ -0,0 +1,89 @@ +package com.luban.api.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.luban.api.dao.H5TagDao; +import com.luban.api.entity.H5Tag; +import com.luban.api.service.H5TagService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.List; + +/** + * H5 组件标签服务实现类 + * + * @author system + * @since 2026-03-06 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class H5TagServiceImpl extends ServiceImpl implements H5TagService { + + private final H5TagDao h5TagDao; + + @Override + public List getAllTags() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.orderByAsc(H5Tag::getSort, H5Tag::getId); + + return baseMapper.selectList(wrapper); + } + + @Override + public List getHotTags(Integer limit) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(H5Tag::getIsHot, 1) + .orderByDesc(H5Tag::getUseCount) + .last("LIMIT " + limit); + + return baseMapper.selectList(wrapper); + } + + @Override + public List getTagsByType(String type) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.and(StrUtil.isNotBlank(type), w -> w.eq(H5Tag::getType, type).isNull(H5Tag::getType)); + wrapper.orderByAsc(H5Tag::getSort, H5Tag::getId); + + return baseMapper.selectList(wrapper); + } + + @Override + public Long create(H5Tag tag) { + long now = Instant.now().getEpochSecond(); + + tag.setCreateTime(now); + tag.setUpdateTime(now); + tag.setUseCount(0L); + + baseMapper.insert(tag); + + return tag.getId(); + } + + @Override + public void update(H5Tag tag) { + tag.setUpdateTime(Instant.now().getEpochSecond()); + baseMapper.updateById(tag); + } + + @Override + public void deleteById(Long id) { + baseMapper.deleteById(id); + } + + @Override + public void incrementUseCount(Long id) { + H5Tag tag = baseMapper.selectById(id); + if (tag != null) { + tag.setUseCount(tag.getUseCount() + 1); + tag.setUpdateTime(Instant.now().getEpochSecond()); + baseMapper.updateById(tag); + } + } +} diff --git a/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/vo/H5ComponentVO.java b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/vo/H5ComponentVO.java new file mode 100644 index 000000000..4100a6d96 --- /dev/null +++ b/ruoyi-site/ruoyi-h5/src/main/java/com/luban/api/vo/H5ComponentVO.java @@ -0,0 +1,62 @@ +package com.luban.api.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * H5 组件 VO + * + * @author system + * @since 2026-03-06 + */ +@Data +@Schema(description = "H5 组件信息") +public class H5ComponentVO { + + @Schema(description = "组件 ID") + private Long id; + + @Schema(description = "组件名称") + private String name; + + @Schema(description = "组件类型 (text/image/form/map/background/music)") + private String type; + + @Schema(description = "缩略图 URL") + private String thumbnailUrl; + + @Schema(description = "预览图 URL") + private String previewUrl; + + @Schema(description = "组件完整配置数据 (JSON 格式)") + private String content; + + @Schema(description = "使用次数统计") + private Long usageCount; + + @Schema(description = "点赞数量") + private Long likeCount; + + @Schema(description = "是否付费组件 (1:是 0:否)") + private Integer isPremium; + + @Schema(description = "状态 (1:启用 0:禁用)") + private Integer status; + + @Schema(description = "备注说明") + private String remark; + + @Schema(description = "创建时间 (Unix 时间戳秒级)") + private Long createTime; + + @Schema(description = "更新时间 (Unix 时间戳秒级)") + private Long updateTime; + + @Schema(description = "关联的分类 ID 列表") + private List categoryIds; + + @Schema(description = "关联的标签 ID 列表") + private List tagIds; +}