diff --git a/src/main/java/com/gxwebsoft/cms/controller/CmsBannerGroupController.java b/src/main/java/com/gxwebsoft/cms/controller/CmsBannerGroupController.java new file mode 100644 index 0000000..6be1cc8 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/controller/CmsBannerGroupController.java @@ -0,0 +1,153 @@ +package com.gxwebsoft.cms.controller; + +import com.gxwebsoft.cms.entity.CmsBannerGroup; +import com.gxwebsoft.cms.entity.CmsBannerItem; +import com.gxwebsoft.cms.mapper.CmsBannerItemMapper; +import com.gxwebsoft.cms.param.CmsBannerGroupParam; +import com.gxwebsoft.cms.service.CmsBannerGroupService; +import com.gxwebsoft.common.core.web.ApiResult; +import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.common.core.web.PageResult; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 轮播组管理控制器 + * + * @author WorkBuddy + * @since 2026-07-19 + */ +@Tag(name = "轮播组管理") +@RestController +@RequestMapping("/api/cms/cms-banner-group") +public class CmsBannerGroupController extends BaseController { + + @Resource + private CmsBannerGroupService cmsBannerGroupService; + + @Resource + private CmsBannerItemMapper cmsBannerItemMapper; + + @Operation(summary = "分页查询轮播组(带组内items)") + @GetMapping("/page") + public ApiResult> page(CmsBannerGroupParam param) { + return success(cmsBannerGroupService.pageRel(param)); + } + + @Operation(summary = "查询全部轮播组(带组内items)") + @GetMapping() + public ApiResult> list(CmsBannerGroupParam param) { + return success(cmsBannerGroupService.listRel(param)); + } + + @Operation(summary = "根据id查询轮播组(含组内items)") + @GetMapping("/{groupId}") + public ApiResult get(@PathVariable("groupId") Integer groupId) { + CmsBannerGroup group = cmsBannerGroupService.getByIdRel(groupId); + if (group == null) { + return fail("轮播组不存在", group); + } + return success(group); + } + + @Operation(summary = "添加轮播组(可携带items一次性创建)") + @PostMapping() + public ApiResult save(@RequestBody CmsBannerGroup group) { + if (cmsBannerGroupService.saveGroup(group)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @Operation(summary = "修改轮播组(可携带items一次性全量替换)") + @PutMapping() + public ApiResult update(@RequestBody CmsBannerGroup group) { + if (group.getGroupId() == null) { + return fail("轮播组ID不能为空"); + } + if (cmsBannerGroupService.updateGroup(group)) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @Operation(summary = "删除轮播组(连带删除组内items)") + @DeleteMapping("/{groupId}") + public ApiResult remove(@PathVariable("groupId") Integer groupId) { + if (cmsBannerGroupService.removeGroup(groupId)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + @Operation(summary = "修改轮播组状态(启用/停用)") + @PutMapping("/status") + public ApiResult updateStatus(@RequestBody CmsBannerGroup group) { + CmsBannerGroup entity = new CmsBannerGroup(); + entity.setGroupId(group.getGroupId()); + entity.setStatus(group.getStatus()); + if (cmsBannerGroupService.updateById(entity)) { + return success("修改成功"); + } + return fail("修改失败"); + } + + // ---------------- 单项 CRUD(可选,保存组时已全量提交items,以下接口用于单独维护) ---------------- + + @Operation(summary = "单独新增轮播项") + @PostMapping("/{groupId}/item") + public ApiResult addItem(@PathVariable("groupId") Integer groupId, + @RequestBody CmsBannerItem item) { + item.setItemId(null); + item.setGroupId(groupId); + if (item.getLinkType() == null) { + item.setLinkType(0); + } + if (cmsBannerGroupService.getById(groupId) == null) { + return fail("轮播组不存在"); + } + if (bannerItemInsert(item)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @Operation(summary = "单独修改轮播项") + @PutMapping("/item") + public ApiResult updateItem(@RequestBody CmsBannerItem item) { + if (item.getItemId() == null) { + return fail("轮播项ID不能为空"); + } + if (bannerItemUpdate(item)) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @Operation(summary = "单独删除轮播项") + @DeleteMapping("/item/{itemId}") + public ApiResult removeItem(@PathVariable("itemId") Integer itemId) { + if (bannerItemDelete(itemId)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + // ---------------- 单项CRUD依赖的mapper ---------------- + + private boolean bannerItemInsert(CmsBannerItem item) { + return cmsBannerItemMapper.insert(item) > 0; + } + + private boolean bannerItemUpdate(CmsBannerItem item) { + return cmsBannerItemMapper.updateById(item) > 0; + } + + private boolean bannerItemDelete(Integer itemId) { + return cmsBannerItemMapper.deleteById(itemId) > 0; + } +} diff --git a/src/main/java/com/gxwebsoft/cms/entity/CmsBannerGroup.java b/src/main/java/com/gxwebsoft/cms/entity/CmsBannerGroup.java new file mode 100644 index 0000000..69ca357 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/entity/CmsBannerGroup.java @@ -0,0 +1,83 @@ +package com.gxwebsoft.cms.entity; + +import com.alibaba.fastjson.annotation.JSONField; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.List; + +/** + * 轮播组(多图轮播) + * + * @author WorkBuddy + * @since 2026-07-19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("cms_banner_group") +@Schema(name = "CmsBannerGroup对象", description = "轮播组(多图轮播)") +public class CmsBannerGroup implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "轮播组ID") + @TableId(value = "group_id", type = IdType.AUTO) + private Integer groupId; + + @Schema(description = "组标题(如:首页幻灯片)") + private String title; + + @Schema(description = "展示位置标识(home_slider/home_promo/inner_page等)") + private String position; + + @Schema(description = "状态: 1启用 0停用") + private Integer status; + + @Schema(description = "排序权重(越小越靠前)") + @JsonProperty("sortNum") + @JSONField(name = "sortNum") + private Integer sortNumber; + + @Schema(description = "生效开始时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime startTime; + + @Schema(description = "生效结束时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime endTime; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "是否删除: 0否 1是") + @TableLogic + private Integer deleted; + + @Schema(description = "租户id") + private Integer tenantId; + + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime updateTime; + + /** + * 组内轮播项(非数据库字段) + * 列表/详情查询时返回 + */ + @Schema(description = "组内轮播项") + @TableField(exist = false) + private List items; +} diff --git a/src/main/java/com/gxwebsoft/cms/entity/CmsBannerItem.java b/src/main/java/com/gxwebsoft/cms/entity/CmsBannerItem.java new file mode 100644 index 0000000..25bc739 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/entity/CmsBannerItem.java @@ -0,0 +1,71 @@ +package com.gxwebsoft.cms.entity; + +import com.alibaba.fastjson.annotation.JSONField; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 轮播项(组内单张图) + * + * @author WorkBuddy + * @since 2026-07-19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("cms_banner_item") +@Schema(name = "CmsBannerItem对象", description = "轮播项(组内单张图)") +public class CmsBannerItem implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "轮播项ID") + @TableId(value = "item_id", type = IdType.AUTO) + private Integer itemId; + + @Schema(description = "所属轮播组ID") + private Integer groupId; + + @Schema(description = "图片地址") + private String image; + + @Schema(description = "单图标题(alt/无障碍)") + private String title; + + @Schema(description = "跳转类型: 0无 1外链 2文章 3产品") + private Integer linkType; + + @Schema(description = "外链地址") + private String linkUrl; + + @Schema(description = "跳转目标ID(文章/产品)") + private Integer linkTargetId; + + @Schema(description = "组内排序(越小越靠前)") + @JsonProperty("sortNum") + @JSONField(name = "sortNum") + private Integer sortNumber; + + @Schema(description = "是否删除: 0否 1是") + @TableLogic + private Integer deleted; + + @Schema(description = "租户id") + private Integer tenantId; + + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime updateTime; +} diff --git a/src/main/java/com/gxwebsoft/cms/mapper/CmsBannerGroupMapper.java b/src/main/java/com/gxwebsoft/cms/mapper/CmsBannerGroupMapper.java new file mode 100644 index 0000000..84c3194 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/mapper/CmsBannerGroupMapper.java @@ -0,0 +1,16 @@ +package com.gxwebsoft.cms.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.gxwebsoft.cms.entity.CmsBannerGroup; +import org.apache.ibatis.annotations.Mapper; + +/** + * 轮播组Mapper + * + * @author WorkBuddy + * @since 2026-07-19 + */ +@Mapper +public interface CmsBannerGroupMapper extends BaseMapper { + +} diff --git a/src/main/java/com/gxwebsoft/cms/mapper/CmsBannerItemMapper.java b/src/main/java/com/gxwebsoft/cms/mapper/CmsBannerItemMapper.java new file mode 100644 index 0000000..2aae047 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/mapper/CmsBannerItemMapper.java @@ -0,0 +1,16 @@ +package com.gxwebsoft.cms.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.gxwebsoft.cms.entity.CmsBannerItem; +import org.apache.ibatis.annotations.Mapper; + +/** + * 轮播项Mapper + * + * @author WorkBuddy + * @since 2026-07-19 + */ +@Mapper +public interface CmsBannerItemMapper extends BaseMapper { + +} diff --git a/src/main/java/com/gxwebsoft/cms/param/CmsBannerGroupParam.java b/src/main/java/com/gxwebsoft/cms/param/CmsBannerGroupParam.java new file mode 100644 index 0000000..3de05b5 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/param/CmsBannerGroupParam.java @@ -0,0 +1,37 @@ +package com.gxwebsoft.cms.param; + +import com.gxwebsoft.common.core.annotation.QueryField; +import com.gxwebsoft.common.core.annotation.QueryType; +import com.gxwebsoft.common.core.web.BaseParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 轮播组查询参数 + * + * @author WorkBuddy + * @since 2026-07-19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Schema(name = "CmsBannerGroupParam对象", description = "轮播组查询参数") +public class CmsBannerGroupParam extends BaseParam { + private static final long serialVersionUID = 1L; + + @Schema(description = "轮播组ID") + @QueryField(type = QueryType.EQ) + private Integer groupId; + + @Schema(description = "组标题") + @QueryField(type = QueryType.LIKE) + private String title; + + @Schema(description = "展示位置标识") + @QueryField(type = QueryType.EQ) + private String position; + + @Schema(description = "状态: 1启用 0停用") + @QueryField(type = QueryType.EQ) + private Integer status; +} diff --git a/src/main/java/com/gxwebsoft/cms/service/CmsBannerGroupService.java b/src/main/java/com/gxwebsoft/cms/service/CmsBannerGroupService.java new file mode 100644 index 0000000..1b9a65e --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/service/CmsBannerGroupService.java @@ -0,0 +1,65 @@ +package com.gxwebsoft.cms.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.gxwebsoft.cms.entity.CmsBannerGroup; +import com.gxwebsoft.cms.param.CmsBannerGroupParam; +import com.gxwebsoft.common.core.web.PageResult; + +import java.util.List; + +/** + * 轮播组Service接口 + * + * @author WorkBuddy + * @since 2026-07-19 + */ +public interface CmsBannerGroupService extends IService { + + /** + * 分页查询(带组内items) + * + * @param param 查询参数 + * @return 分页结果 + */ + PageResult pageRel(CmsBannerGroupParam param); + + /** + * 查询全部(带组内items) + * + * @param param 查询参数 + * @return 列表 + */ + List listRel(CmsBannerGroupParam param); + + /** + * 根据id查询(带组内items) + * + * @param groupId 轮播组ID + * @return 轮播组 + */ + CmsBannerGroup getByIdRel(Integer groupId); + + /** + * 保存轮播组(可携带items一次性创建) + * + * @param group 轮播组(含items) + * @return 是否成功 + */ + boolean saveGroup(CmsBannerGroup group); + + /** + * 更新轮播组(可携带items一次性全量替换) + * + * @param group 轮播组(含items) + * @return 是否成功 + */ + boolean updateGroup(CmsBannerGroup group); + + /** + * 删除轮播组(连带删除组内items) + * + * @param groupId 轮播组ID + * @return 是否成功 + */ + boolean removeGroup(Integer groupId); +} diff --git a/src/main/java/com/gxwebsoft/cms/service/impl/CmsBannerGroupServiceImpl.java b/src/main/java/com/gxwebsoft/cms/service/impl/CmsBannerGroupServiceImpl.java new file mode 100644 index 0000000..a5b717a --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/service/impl/CmsBannerGroupServiceImpl.java @@ -0,0 +1,175 @@ +package com.gxwebsoft.cms.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gxwebsoft.cms.entity.CmsBannerGroup; +import com.gxwebsoft.cms.entity.CmsBannerItem; +import com.gxwebsoft.cms.mapper.CmsBannerGroupMapper; +import com.gxwebsoft.cms.mapper.CmsBannerItemMapper; +import com.gxwebsoft.cms.param.CmsBannerGroupParam; +import com.gxwebsoft.cms.service.CmsBannerGroupService; +import com.gxwebsoft.common.core.web.PageParam; +import com.gxwebsoft.common.core.web.PageResult; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 轮播组Service实现 + * + * @author WorkBuddy + * @since 2026-07-19 + */ +@Service +public class CmsBannerGroupServiceImpl extends ServiceImpl implements CmsBannerGroupService { + + @Resource + private CmsBannerItemMapper bannerItemMapper; + + @Override + public PageResult pageRel(CmsBannerGroupParam param) { + PageParam page = new PageParam<>(param); + page.setDefaultOrder("sort_number asc, create_time desc"); + IPage result = page(page, page.getOrderWrapper()); + List records = result.getRecords(); + // 批量填充 items + fillItems(records); + return new PageResult<>(records, result.getTotal()); + } + + @Override + public List listRel(CmsBannerGroupParam param) { + PageParam page = new PageParam<>(param); + page.setDefaultOrder("sort_number asc, create_time desc"); + List records = list(page.getOrderWrapper()); + fillItems(records); + return records; + } + + @Override + public CmsBannerGroup getByIdRel(Integer groupId) { + CmsBannerGroup group = getById(groupId); + if (group == null) { + return null; + } + List items = listItemsByGroupId(groupId); + group.setItems(items); + return group; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean saveGroup(CmsBannerGroup group) { + // 先保存组 + boolean ok = save(group); + if (!ok) { + return false; + } + // 再保存组内items + saveItems(group.getGroupId(), group.getItems()); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateGroup(CmsBannerGroup group) { + // 更新组主信息 + boolean ok = updateById(group); + if (!ok) { + return false; + } + Integer groupId = group.getGroupId(); + // 全量替换items: 先删除旧items, 再插入新items + if (group.getItems() != null) { + bannerItemMapper.delete(new LambdaQueryWrapper() + .eq(CmsBannerItem::getGroupId, groupId)); + saveItems(groupId, group.getItems()); + } + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean removeGroup(Integer groupId) { + // 逻辑删除组 + boolean ok = removeById(groupId); + if (!ok) { + return false; + } + // 逻辑删除组内items + bannerItemMapper.delete(new LambdaQueryWrapper() + .eq(CmsBannerItem::getGroupId, groupId)); + return true; + } + + // ----------------- private ----------------- + + /** + * 查询某个组下的所有轮播项(按sort_number升序) + */ + private List listItemsByGroupId(Integer groupId) { + if (groupId == null) { + return Collections.emptyList(); + } + return bannerItemMapper.selectList(new LambdaQueryWrapper() + .eq(CmsBannerItem::getGroupId, groupId) + .orderByAsc(CmsBannerItem::getSortNumber)); + } + + /** + * 批量查询多个组的items并按group分组 + */ + private Map> listItemsByGroupIds(List groupIds) { + if (groupIds == null || groupIds.isEmpty()) { + return Collections.emptyMap(); + } + List allItems = bannerItemMapper.selectList(new LambdaQueryWrapper() + .in(CmsBannerItem::getGroupId, groupIds) + .orderByAsc(CmsBannerItem::getSortNumber)); + return allItems.stream().collect(Collectors.groupingBy(CmsBannerItem::getGroupId)); + } + + /** + * 批量给group列表填充items + */ + private void fillItems(List records) { + if (records == null || records.isEmpty()) { + return; + } + List groupIds = records.stream() + .map(CmsBannerGroup::getGroupId) + .collect(Collectors.toList()); + Map> itemsMap = listItemsByGroupIds(groupIds); + for (CmsBannerGroup g : records) { + g.setItems(itemsMap.getOrDefault(g.getGroupId(), new ArrayList<>())); + } + } + + /** + * 批量保存组内items + */ + private void saveItems(Integer groupId, List items) { + if (groupId == null || items == null || items.isEmpty()) { + return; + } + for (int i = 0; i < items.size(); i++) { + CmsBannerItem item = items.get(i); + item.setItemId(null); // 新增时清除前端可能传入的ID + item.setGroupId(groupId); + if (item.getSortNumber() == null) { + item.setSortNumber(i); + } + if (item.getLinkType() == null) { + item.setLinkType(0); + } + bannerItemMapper.insert(item); + } + } +} diff --git a/src/main/java/com/gxwebsoft/cms/sql/cms_banner_group.sql b/src/main/java/com/gxwebsoft/cms/sql/cms_banner_group.sql new file mode 100644 index 0000000..f6d6b12 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/sql/cms_banner_group.sql @@ -0,0 +1,45 @@ +-- ---------------------------- +-- 轮播组表 +-- 一个轮播组对应一组多图轮播(如首页幻灯片) +-- 与 cms_banner(旧版单图) 并存,互不影响 +-- ---------------------------- +CREATE TABLE IF NOT EXISTS `cms_banner_group` ( + `group_id` INT NOT NULL AUTO_INCREMENT COMMENT '轮播组ID', + `title` VARCHAR(120) NOT NULL COMMENT '组标题(如:首页幻灯片)', + `position` VARCHAR(60) NOT NULL DEFAULT 'home_slider' COMMENT '展示位置标识(home_slider/home_promo/inner_page等)', + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 1启用 0停用', + `sort_number` INT NOT NULL DEFAULT 0 COMMENT '排序权重(越小越靠前)', + `start_time` DATETIME DEFAULT NULL COMMENT '生效开始时间', + `end_time` DATETIME DEFAULT NULL COMMENT '生效结束时间', + `remark` VARCHAR(255) DEFAULT NULL COMMENT '备注', + `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除: 0否 1是', + `tenant_id` INT DEFAULT NULL COMMENT '租户ID', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`group_id`), + KEY `idx_position` (`position`), + KEY `idx_status` (`status`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='轮播组(多图轮播)'; + +-- ---------------------------- +-- 轮播项表 +-- 组内单张图片及其跳转配置 +-- ---------------------------- +CREATE TABLE IF NOT EXISTS `cms_banner_item` ( + `item_id` INT NOT NULL AUTO_INCREMENT COMMENT '轮播项ID', + `group_id` INT NOT NULL COMMENT '所属轮播组ID', + `image` VARCHAR(500) NOT NULL COMMENT '图片地址', + `title` VARCHAR(120) DEFAULT NULL COMMENT '单图标题(alt/无障碍)', + `link_type` TINYINT NOT NULL DEFAULT 0 COMMENT '跳转类型: 0无 1外链 2文章 3产品', + `link_url` VARCHAR(500) DEFAULT NULL COMMENT '外链地址', + `link_target_id` INT DEFAULT NULL COMMENT '跳转目标ID(文章/产品)', + `sort_number` INT NOT NULL DEFAULT 0 COMMENT '组内排序(越小越靠前)', + `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除: 0否 1是', + `tenant_id` INT DEFAULT NULL COMMENT '租户ID', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`item_id`), + KEY `idx_group_id` (`group_id`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='轮播项(组内单张图)';