Browse Source

Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product

jason 1 year ago
parent
commit
48b1c19f28
29 changed files with 1381 additions and 8 deletions
  1. 73 1
      sql/mysql/mall.sql
  2. 7 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
  3. 85 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/DiyPageController.java
  4. 82 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/DiyTemplateController.java
  5. 32 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageBaseVO.java
  6. 14 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageCreateReqVO.java
  7. 27 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPagePageReqVO.java
  8. 22 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageRespVO.java
  9. 20 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageUpdateReqVO.java
  10. 29 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateBaseVO.java
  11. 14 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateCreateReqVO.java
  12. 34 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplatePageReqVO.java
  13. 28 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateRespVO.java
  14. 20 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateUpdateReqVO.java
  15. 33 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/diy/DiyPageConvert.java
  16. 33 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/diy/DiyTemplateConvert.java
  17. 55 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/diy/DiyPageDO.java
  18. 60 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/diy/DiyTemplateDO.java
  19. 26 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/diy/DiyPageMapper.java
  20. 32 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/diy/DiyTemplateMapper.java
  21. 66 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageService.java
  22. 84 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageServiceImpl.java
  23. 63 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateService.java
  24. 100 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateServiceImpl.java
  25. 0 1
      yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImplTest.java
  26. 128 0
      yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageServiceImplTest.java
  27. 175 0
      yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateServiceImplTest.java
  28. 4 4
      yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/clean.sql
  29. 35 2
      yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql

+ 73 - 1
sql/mysql/mall.sql

@@ -1,2 +1,74 @@
 INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name)
-VALUES ('核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder');
+VALUES ('核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder');
+
+CREATE TABLE promotion_diy_template
+(
+    id                 bigint AUTO_INCREMENT COMMENT '装修模板编号'
+        PRIMARY KEY,
+    name               varchar(100)                          NOT NULL COMMENT '模板名称',
+    used               bit         DEFAULT b'0'              NOT NULL COMMENT '是否使用',
+    used_time          datetime                              NULL COMMENT '使用时间',
+    remark             varchar(255)                          NULL COMMENT '备注',
+    preview_image_urls varchar(2000)                         NULL COMMENT '预览图,多个逗号分隔',
+    property           text                                  NULL COMMENT '页面属性,JSON 格式',
+    creator            varchar(64) DEFAULT ''                NULL COMMENT '创建者',
+    create_time        datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    updater            varchar(64) DEFAULT ''                NULL COMMENT '更新者',
+    update_time        datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    deleted            bit         DEFAULT b'0'              NOT NULL COMMENT '是否删除',
+    tenant_id          bigint      DEFAULT 0                 NOT NULL COMMENT '租户编号'
+)
+    COMMENT '装修模板';
+
+CREATE TABLE promotion_diy_page
+(
+    id                 bigint AUTO_INCREMENT COMMENT '装修页面编号'
+        PRIMARY KEY,
+    template_id        bigint                                NULL COMMENT '装修模板编号',
+    name               varchar(100)                          NOT NULL COMMENT '页面名称',
+    remark             varchar(255)                          NULL COMMENT '备注',
+    preview_image_urls varchar(2000)                         NULL COMMENT '预览图,多个逗号分隔',
+    property           text                                  NULL COMMENT '页面属性,JSON 格式',
+    creator            varchar(64) DEFAULT ''                NULL COMMENT '创建者',
+    create_time        datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    updater            varchar(64) DEFAULT ''                NULL COMMENT '更新者',
+    update_time        datetime    DEFAULT CURRENT_TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    deleted            bit         DEFAULT b'0'              NOT NULL COMMENT '是否删除',
+    tenant_id          bigint      DEFAULT 0                 NOT NULL COMMENT '租户编号'
+)
+    COMMENT '装修页面';
+
+CREATE INDEX idx_template_id ON promotion_diy_page (template_id);
+
+-- 装修,上级菜单:营销中心
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
+VALUES ('商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 0, 'DiyTemplate');
+SELECT @diyParentId := LAST_INSERT_ID();
+
+-- 装修模板
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
+VALUES ('装修模板', '', 2, 1, @diyParentId, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 0, 'DiyTemplate');
+SELECT @parentId := LAST_INSERT_ID();
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('装修模板查询', 'promotion:diy-template:query', 3, 1, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('装修模板创建', 'promotion:diy-template:create', 3, 2, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('装修模板更新', 'promotion:diy-template:update', 3, 3, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('装修模板删除', 'promotion:diy-template:delete', 3, 4, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('装修模板使用', 'promotion:diy-template:use', 3, 5, @parentId, '', '', '', 0);
+
+-- 装修页面
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
+VALUES ('装修页面', '', 2, 2,  @diyParentId, 'diy-page', 'foundation:page-edit', 'mall/promotion/diy/page/index', 0, 'DiyPage');
+SELECT @parentId := LAST_INSERT_ID();
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('装修页面查询', 'promotion:diy-page:query', 3, 1, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('装修页面创建', 'promotion:diy-page:create', 3, 2, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('装修页面更新', 'promotion:diy-page:update', 3, 3, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('装修页面删除', 'promotion:diy-page:delete', 3, 4, @parentId, '', '', '', 0);;

+ 7 - 0
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java

@@ -116,4 +116,11 @@ public interface ErrorCodeConstants {
     // ========== 文章管理 1-013-016-000 ==========
     ErrorCode ARTICLE_NOT_EXISTS = new ErrorCode(1_013_016_000, "文章不存在");
 
+    // ========== 装修模板 1-013-017-000 ==========
+    ErrorCode DIY_TEMPLATE_NOT_EXISTS = new ErrorCode(1_013_017_000, "装修模板不存在");
+    ErrorCode DIY_TEMPLATE_USED_CANNOT_DELETE = new ErrorCode(1_013_017_001, "不能删除正在使用的装修模板");
+
+    // ========== 装修页面 1-013-018-000 ==========
+    ErrorCode DIY_PAGE_NOT_EXISTS = new ErrorCode(1_013_018_000, "装修页面不存在");
+
 }

+ 85 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/DiyPageController.java

@@ -0,0 +1,85 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageRespVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.convert.diy.DiyPageConvert;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;
+import cn.iocoder.yudao.module.promotion.service.diy.DiyPageService;
+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.validation.Valid;
+import java.util.Collection;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 装修页面")
+@RestController
+@RequestMapping("/promotion/diy-page")
+@Validated
+public class DiyPageController {
+
+    @Resource
+    private DiyPageService diyPageService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建装修页面")
+    @PreAuthorize("@ss.hasPermission('promotion:diy-page:create')")
+    public CommonResult<Long> createDiyPage(@Valid @RequestBody DiyPageCreateReqVO createReqVO) {
+        return success(diyPageService.createDiyPage(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新装修页面")
+    @PreAuthorize("@ss.hasPermission('promotion:diy-page:update')")
+    public CommonResult<Boolean> updateDiyPage(@Valid @RequestBody DiyPageUpdateReqVO updateReqVO) {
+        diyPageService.updateDiyPage(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除装修页面")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('promotion:diy-page:delete')")
+    public CommonResult<Boolean> deleteDiyPage(@RequestParam("id") Long id) {
+        diyPageService.deleteDiyPage(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得装修页面")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('promotion:diy-page:query')")
+    public CommonResult<DiyPageRespVO> getDiyPage(@RequestParam("id") Long id) {
+        DiyPageDO diyPage = diyPageService.getDiyPage(id);
+        return success(DiyPageConvert.INSTANCE.convert(diyPage));
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "获得装修页面列表")
+    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
+    @PreAuthorize("@ss.hasPermission('promotion:diy-page:query')")
+    public CommonResult<List<DiyPageRespVO>> getDiyPageList(@RequestParam("ids") Collection<Long> ids) {
+        List<DiyPageDO> list = diyPageService.getDiyPageList(ids);
+        return success(DiyPageConvert.INSTANCE.convertList(list));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得装修页面分页")
+    @PreAuthorize("@ss.hasPermission('promotion:diy-page:query')")
+    public CommonResult<PageResult<DiyPageRespVO>> getDiyPagePage(@Valid DiyPagePageReqVO pageVO) {
+        PageResult<DiyPageDO> pageResult = diyPageService.getDiyPagePage(pageVO);
+        return success(DiyPageConvert.INSTANCE.convertPage(pageResult));
+    }
+
+}

+ 82 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/DiyTemplateController.java

@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateRespVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.convert.diy.DiyTemplateConvert;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;
+import cn.iocoder.yudao.module.promotion.service.diy.DiyTemplateService;
+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.validation.Valid;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 装修模板")
+@RestController
+@RequestMapping("/promotion/diy-template")
+@Validated
+public class DiyTemplateController {
+
+    @Resource
+    private DiyTemplateService diyTemplateService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建装修模板")
+    @PreAuthorize("@ss.hasPermission('promotion:diy-template:create')")
+    public CommonResult<Long> createDiyTemplate(@Valid @RequestBody DiyTemplateCreateReqVO createReqVO) {
+        return success(diyTemplateService.createDiyTemplate(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新装修模板")
+    @PreAuthorize("@ss.hasPermission('promotion:diy-template:update')")
+    public CommonResult<Boolean> updateDiyTemplate(@Valid @RequestBody DiyTemplateUpdateReqVO updateReqVO) {
+        diyTemplateService.updateDiyTemplate(updateReqVO);
+        return success(true);
+    }
+
+    @PutMapping("/use")
+    @Operation(summary = "使用装修模板")
+    @PreAuthorize("@ss.hasPermission('promotion:diy-template:use')")
+    public CommonResult<Boolean> useDiyTemplate(@RequestParam("id") Long id) {
+        diyTemplateService.useDiyTemplate(id);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除装修模板")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('promotion:diy-template:delete')")
+    public CommonResult<Boolean> deleteDiyTemplate(@RequestParam("id") Long id) {
+        diyTemplateService.deleteDiyTemplate(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得装修模板")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('promotion:diy-template:query')")
+    public CommonResult<DiyTemplateRespVO> getDiyTemplate(@RequestParam("id") Long id) {
+        DiyTemplateDO diyTemplate = diyTemplateService.getDiyTemplate(id);
+        return success(DiyTemplateConvert.INSTANCE.convert(diyTemplate));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得装修模板分页")
+    @PreAuthorize("@ss.hasPermission('promotion:diy-template:query')")
+    public CommonResult<PageResult<DiyTemplateRespVO>> getDiyTemplatePage(@Valid DiyTemplatePageReqVO pageVO) {
+        PageResult<DiyTemplateDO> pageResult = diyTemplateService.getDiyTemplatePage(pageVO);
+        return success(DiyTemplateConvert.INSTANCE.convertPage(pageResult));
+    }
+
+}

+ 32 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageBaseVO.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * 装修页面 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class DiyPageBaseVO {
+
+    @Schema(description = "装修模板编号", example = "26179")
+    private Long templateId;
+
+    @Schema(description = "页面名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
+    @NotNull(message = "页面名称不能为空")
+    private String name;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "预览图")
+    private List<String> previewImageUrls;
+
+    @Schema(description = "页面属性", example = "[]")
+    private String property;
+
+}

+ 14 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - 装修页面创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DiyPageCreateReqVO extends DiyPageBaseVO {
+
+}

+ 27 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPagePageReqVO.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;
+
+import cn.iocoder.yudao.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 cn.iocoder.yudao.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 DiyPagePageReqVO extends PageParam {
+
+    @Schema(description = "页面名称", example = "王五")
+    private String name;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 22 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageRespVO.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 装修页面 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DiyPageRespVO extends DiyPageBaseVO {
+
+    @Schema(description = "装修页面编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12082")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 20 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/page/DiyPageUpdateReqVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 装修页面更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DiyPageUpdateReqVO extends DiyPageBaseVO {
+
+    @Schema(description = "装修页面编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12082")
+    @NotNull(message = "装修页面编号不能为空")
+    private Long id;
+
+}

+ 29 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateBaseVO.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * 装修模板 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class DiyTemplateBaseVO {
+
+    @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "默认主题")
+    @NotNull(message = "模板名称不能为空")
+    private String name;
+
+    @Schema(description = "备注", example = "默认主题")
+    private String remark;
+
+    @Schema(description = "预览图", example = "[https://www.iocoder.cn/1.jpg]")
+    private List<String> previewImageUrls;
+
+    @Schema(description = "模板属性,JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
+    private String property;
+
+}

+ 14 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - 装修模板创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DiyTemplateCreateReqVO extends DiyTemplateBaseVO {
+
+}

+ 34 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplatePageReqVO.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;
+
+import cn.iocoder.yudao.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 cn.iocoder.yudao.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 DiyTemplatePageReqVO extends PageParam {
+
+    @Schema(description = "模板名称", example = "默认主题")
+    private String name;
+
+    @Schema(description = "是否使用", example = "true")
+    private Boolean used;
+
+    @Schema(description = "使用时间", example = "使用时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] usedTime;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 28 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateRespVO.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 装修模板 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DiyTemplateRespVO extends DiyTemplateBaseVO {
+
+    @Schema(description = "装修模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31209")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+    @Schema(description = "是否使用", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean used;
+
+    @Schema(description = "使用时间", example = "使用时间")
+    private LocalDateTime usedTime;
+
+}

+ 20 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateUpdateReqVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 装修模板更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DiyTemplateUpdateReqVO extends DiyTemplateBaseVO {
+
+    @Schema(description = "装修模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31209")
+    @NotNull(message = "装修模板编号不能为空")
+    private Long id;
+
+}

+ 33 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/diy/DiyPageConvert.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.promotion.convert.diy;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageRespVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 装修页面 Convert
+ *
+ * @author owen
+ */
+@Mapper
+public interface DiyPageConvert {
+
+    DiyPageConvert INSTANCE = Mappers.getMapper(DiyPageConvert.class);
+
+    DiyPageDO convert(DiyPageCreateReqVO bean);
+
+    DiyPageDO convert(DiyPageUpdateReqVO bean);
+
+    DiyPageRespVO convert(DiyPageDO bean);
+
+    List<DiyPageRespVO> convertList(List<DiyPageDO> list);
+
+    PageResult<DiyPageRespVO> convertPage(PageResult<DiyPageDO> page);
+
+}

+ 33 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/diy/DiyTemplateConvert.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.promotion.convert.diy;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateRespVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 装修模板 Convert
+ *
+ * @author owen
+ */
+@Mapper
+public interface DiyTemplateConvert {
+
+    DiyTemplateConvert INSTANCE = Mappers.getMapper(DiyTemplateConvert.class);
+
+    DiyTemplateDO convert(DiyTemplateCreateReqVO bean);
+
+    DiyTemplateDO convert(DiyTemplateUpdateReqVO bean);
+
+    DiyTemplateRespVO convert(DiyTemplateDO bean);
+
+    List<DiyTemplateRespVO> convertList(List<DiyTemplateDO> list);
+
+    PageResult<DiyTemplateRespVO> convertPage(PageResult<DiyTemplateDO> page);
+
+}

+ 55 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/diy/DiyPageDO.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.promotion.dal.dataobject.diy;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.util.List;
+
+/**
+ * 装修页面 DO
+ *
+ * @author owen
+ */
+@TableName(value = "promotion_diy_page", autoResultMap = true)
+@KeySequence("promotion_diy_page_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class DiyPageDO extends BaseDO {
+
+    /**
+     * 装修页面编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 装修模板编号
+     */
+    private Long templateId;
+    /**
+     * 页面名称
+     */
+    private String name;
+    /**
+     * 备注
+     */
+    private String remark;
+    /**
+     * 预览图,多个逗号分隔
+     */
+    @TableField(typeHandler = StringListTypeHandler.class)
+    private List<String> previewImageUrls;
+    /**
+     * 页面属性,JSON 格式
+     */
+    private String property;
+
+}

+ 60 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/diy/DiyTemplateDO.java

@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.promotion.dal.dataobject.diy;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 装修模板 DO
+ *
+ * @author owen
+ */
+@TableName(value = "promotion_diy_template", autoResultMap = true)
+@KeySequence("promotion_diy_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class DiyTemplateDO extends BaseDO {
+
+    /**
+     * 装修模板编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 模板名称
+     */
+    private String name;
+    /**
+     * 是否使用
+     */
+    private Boolean used;
+    /**
+     * 使用时间
+     */
+    private LocalDateTime usedTime;
+    /**
+     * 备注
+     */
+    private String remark;
+    /**
+     * 预览图
+     */
+    @TableField(typeHandler = StringListTypeHandler.class)
+    private List<String> previewImageUrls;
+    /**
+     * 底部导航属性,JSON 格式
+     */
+    private String property;
+
+}

+ 26 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/diy/DiyPageMapper.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.promotion.dal.mysql.diy;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePageReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 装修页面 Mapper
+ *
+ * @author owen
+ */
+@Mapper
+public interface DiyPageMapper extends BaseMapperX<DiyPageDO> {
+
+    default PageResult<DiyPageDO> selectPage(DiyPagePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<DiyPageDO>()
+                .likeIfPresent(DiyPageDO::getName, reqVO.getName())
+                .betweenIfPresent(DiyPageDO::getCreateTime, reqVO.getCreateTime())
+                .isNull(DiyPageDO::getTemplateId)
+                .orderByDesc(DiyPageDO::getId));
+    }
+
+}

+ 32 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/diy/DiyTemplateMapper.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.promotion.dal.mysql.diy;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 装修模板 Mapper
+ *
+ * @author owen
+ */
+@Mapper
+public interface DiyTemplateMapper extends BaseMapperX<DiyTemplateDO> {
+
+    default PageResult<DiyTemplateDO> selectPage(DiyTemplatePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<DiyTemplateDO>()
+                .likeIfPresent(DiyTemplateDO::getName, reqVO.getName())
+                .eqIfPresent(DiyTemplateDO::getUsed, reqVO.getUsed())
+                .betweenIfPresent(DiyTemplateDO::getUsedTime, reqVO.getUsedTime())
+                .betweenIfPresent(DiyTemplateDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(DiyTemplateDO::getUsed) // 排序规则1:已使用的排到最前面
+                .orderByDesc(DiyTemplateDO::getId)); // 排序规则2:新创建的排到前面
+    }
+
+    default DiyTemplateDO selectByUsed(boolean used) {
+        return selectOne(DiyTemplateDO::getUsed, used);
+    }
+
+}

+ 66 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageService.java

@@ -0,0 +1,66 @@
+package cn.iocoder.yudao.module.promotion.service.diy;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;
+
+import javax.validation.Valid;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 装修页面 Service 接口
+ *
+ * @author owen
+ */
+public interface DiyPageService {
+
+    /**
+     * 创建装修页面
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createDiyPage(@Valid DiyPageCreateReqVO createReqVO);
+
+    /**
+     * 更新装修页面
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateDiyPage(@Valid DiyPageUpdateReqVO updateReqVO);
+
+    /**
+     * 删除装修页面
+     *
+     * @param id 编号
+     */
+    void deleteDiyPage(Long id);
+
+    /**
+     * 获得装修页面
+     *
+     * @param id 编号
+     * @return 装修页面
+     */
+    DiyPageDO getDiyPage(Long id);
+
+    /**
+     * 获得装修页面列表
+     *
+     * @param ids 编号
+     * @return 装修页面列表
+     */
+    List<DiyPageDO> getDiyPageList(Collection<Long> ids);
+
+    /**
+     * 获得装修页面分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 装修页面分页
+     */
+    PageResult<DiyPageDO> getDiyPagePage(DiyPagePageReqVO pageReqVO);
+
+}

+ 84 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageServiceImpl.java

@@ -0,0 +1,84 @@
+package cn.iocoder.yudao.module.promotion.service.diy;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.convert.diy.DiyPageConvert;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;
+import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyPageMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_PAGE_NOT_EXISTS;
+
+/**
+ * 装修页面 Service 实现类
+ *
+ * @author owen
+ */
+@Service
+@Validated
+public class DiyPageServiceImpl implements DiyPageService {
+
+    @Resource
+    private DiyPageMapper diyPageMapper;
+
+    @Override
+    public Long createDiyPage(DiyPageCreateReqVO createReqVO) {
+        // 插入
+        DiyPageDO diyPage = DiyPageConvert.INSTANCE.convert(createReqVO);
+        diyPageMapper.insert(diyPage);
+        // 返回
+        return diyPage.getId();
+    }
+
+    @Override
+    public void updateDiyPage(DiyPageUpdateReqVO updateReqVO) {
+        // 校验存在
+        validateDiyPageExists(updateReqVO.getId());
+        // 更新
+        DiyPageDO updateObj = DiyPageConvert.INSTANCE.convert(updateReqVO);
+        diyPageMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteDiyPage(Long id) {
+        // 校验存在
+        validateDiyPageExists(id);
+        // 删除
+        diyPageMapper.deleteById(id);
+    }
+
+    private void validateDiyPageExists(Long id) {
+        if (diyPageMapper.selectById(id) == null) {
+            throw exception(DIY_PAGE_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public DiyPageDO getDiyPage(Long id) {
+        return diyPageMapper.selectById(id);
+    }
+
+    @Override
+    public List<DiyPageDO> getDiyPageList(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return ListUtil.empty();
+        }
+        return diyPageMapper.selectBatchIds(ids);
+    }
+
+    @Override
+    public PageResult<DiyPageDO> getDiyPagePage(DiyPagePageReqVO pageReqVO) {
+        return diyPageMapper.selectPage(pageReqVO);
+    }
+
+}

+ 63 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateService.java

@@ -0,0 +1,63 @@
+package cn.iocoder.yudao.module.promotion.service.diy;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;
+
+import javax.validation.Valid;
+
+/**
+ * 装修模板 Service 接口
+ *
+ * @author owen
+ */
+public interface DiyTemplateService {
+
+    /**
+     * 创建装修模板
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createDiyTemplate(@Valid DiyTemplateCreateReqVO createReqVO);
+
+    /**
+     * 更新装修模板
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateDiyTemplate(@Valid DiyTemplateUpdateReqVO updateReqVO);
+
+    /**
+     * 删除装修模板
+     *
+     * @param id 编号
+     */
+    void deleteDiyTemplate(Long id);
+
+    /**
+     * 获得装修模板
+     *
+     * @param id 编号
+     * @return 装修模板
+     */
+    DiyTemplateDO getDiyTemplate(Long id);
+
+    /**
+     * 获得装修模板分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 装修模板分页
+     */
+    PageResult<DiyTemplateDO> getDiyTemplatePage(DiyTemplatePageReqVO pageReqVO);
+
+    /**
+     * 使用装修模板
+     *
+     * @param id 编号
+     */
+    void useDiyTemplate(Long id);
+
+}

+ 100 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateServiceImpl.java

@@ -0,0 +1,100 @@
+package cn.iocoder.yudao.module.promotion.service.diy;
+
+import cn.hutool.core.util.BooleanUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.convert.diy.DiyTemplateConvert;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;
+import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyTemplateMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_TEMPLATE_NOT_EXISTS;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_TEMPLATE_USED_CANNOT_DELETE;
+
+/**
+ * 装修模板 Service 实现类
+ *
+ * @author owen
+ */
+@Service
+@Validated
+public class DiyTemplateServiceImpl implements DiyTemplateService {
+
+    @Resource
+    private DiyTemplateMapper diyTemplateMapper;
+
+    @Override
+    public Long createDiyTemplate(DiyTemplateCreateReqVO createReqVO) {
+        // 插入
+        DiyTemplateDO diyTemplate = DiyTemplateConvert.INSTANCE.convert(createReqVO);
+        diyTemplateMapper.insert(diyTemplate);
+        // 返回
+        return diyTemplate.getId();
+    }
+
+    @Override
+    public void updateDiyTemplate(DiyTemplateUpdateReqVO updateReqVO) {
+        // 校验存在
+        validateDiyTemplateExists(updateReqVO.getId());
+        // 更新
+        DiyTemplateDO updateObj = DiyTemplateConvert.INSTANCE.convert(updateReqVO);
+        diyTemplateMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteDiyTemplate(Long id) {
+        // 校验存在
+        DiyTemplateDO diyTemplateDO = validateDiyTemplateExists(id);
+        // 校验使用中
+        if (BooleanUtil.isTrue(diyTemplateDO.getUsed())) {
+            throw exception(DIY_TEMPLATE_USED_CANNOT_DELETE);
+        }
+        // 删除
+        diyTemplateMapper.deleteById(id);
+    }
+
+    private DiyTemplateDO validateDiyTemplateExists(Long id) {
+        DiyTemplateDO diyTemplateDO = diyTemplateMapper.selectById(id);
+        if (diyTemplateDO == null) {
+            throw exception(DIY_TEMPLATE_NOT_EXISTS);
+        }
+        return diyTemplateDO;
+    }
+
+    @Override
+    public DiyTemplateDO getDiyTemplate(Long id) {
+        return diyTemplateMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<DiyTemplateDO> getDiyTemplatePage(DiyTemplatePageReqVO pageReqVO) {
+        return diyTemplateMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public void useDiyTemplate(Long id) {
+        // 校验存在
+        validateDiyTemplateExists(id);
+        // 已使用的更新为未使用
+        DiyTemplateDO used = diyTemplateMapper.selectByUsed(true);
+        if (used != null) {
+            this.updateUsed(used.getId(), false, null);
+        }
+        // 更新为已使用
+        this.updateUsed(id, true, LocalDateTime.now());
+    }
+
+    private void updateUsed(Long id, Boolean used, LocalDateTime usedTime) {
+        DiyTemplateDO updateObj = new DiyTemplateDO().setId(id)
+                .setUsed(used).setUsedTime(usedTime);
+        diyTemplateMapper.updateById(updateObj);
+    }
+
+}

+ 0 - 1
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImplTest.java

@@ -18,7 +18,6 @@ import org.springframework.context.annotation.Import;
 import javax.annotation.Resource;
 import java.time.Duration;
 import java.time.LocalDateTime;
-import java.util.Date;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;

+ 128 - 0
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageServiceImplTest.java

@@ -0,0 +1,128 @@
+package cn.iocoder.yudao.module.promotion.service.diy;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO;
+import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyPageMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_PAGE_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link DiyPageServiceImpl} 的单元测试类
+ *
+ * @author owen
+ */
+@Import(DiyPageServiceImpl.class)
+public class DiyPageServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private DiyPageServiceImpl diyPageService;
+
+    @Resource
+    private DiyPageMapper diyPageMapper;
+
+    @Test
+    public void testCreateDiyPage_success() {
+        // 准备参数
+        DiyPageCreateReqVO reqVO = randomPojo(DiyPageCreateReqVO.class);
+
+        // 调用
+        Long diyPageId = diyPageService.createDiyPage(reqVO);
+        // 断言
+        assertNotNull(diyPageId);
+        // 校验记录的属性是否正确
+        DiyPageDO diyPage = diyPageMapper.selectById(diyPageId);
+        assertPojoEquals(reqVO, diyPage);
+    }
+
+    @Test
+    public void testUpdateDiyPage_success() {
+        // mock 数据
+        DiyPageDO dbDiyPage = randomPojo(DiyPageDO.class);
+        diyPageMapper.insert(dbDiyPage);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        DiyPageUpdateReqVO reqVO = randomPojo(DiyPageUpdateReqVO.class, o -> {
+            o.setId(dbDiyPage.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        diyPageService.updateDiyPage(reqVO);
+        // 校验是否更新正确
+        DiyPageDO diyPage = diyPageMapper.selectById(reqVO.getId()); // 获取最新的
+        assertPojoEquals(reqVO, diyPage);
+    }
+
+    @Test
+    public void testUpdateDiyPage_notExists() {
+        // 准备参数
+        DiyPageUpdateReqVO reqVO = randomPojo(DiyPageUpdateReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> diyPageService.updateDiyPage(reqVO), DIY_PAGE_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteDiyPage_success() {
+        // mock 数据
+        DiyPageDO dbDiyPage = randomPojo(DiyPageDO.class);
+        diyPageMapper.insert(dbDiyPage);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbDiyPage.getId();
+
+        // 调用
+        diyPageService.deleteDiyPage(id);
+        // 校验数据不存在了
+        assertNull(diyPageMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteDiyPage_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> diyPageService.deleteDiyPage(id), DIY_PAGE_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetDiyPagePage() {
+        // mock 数据
+        DiyPageDO dbDiyPage = randomPojo(DiyPageDO.class, o -> { // 等会查询到
+            o.setName(null);
+            o.setCreateTime(null);
+        });
+        diyPageMapper.insert(dbDiyPage);
+        // 测试 name 不匹配
+        diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setName(null)));
+        // 测试 createTime 不匹配
+        diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setCreateTime(null)));
+        // 准备参数
+        DiyPagePageReqVO reqVO = new DiyPagePageReqVO();
+        reqVO.setName(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<DiyPageDO> pageResult = diyPageService.getDiyPagePage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbDiyPage, pageResult.getList().get(0));
+    }
+
+}

+ 175 - 0
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateServiceImplTest.java

@@ -0,0 +1,175 @@
+package cn.iocoder.yudao.module.promotion.service.diy;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;
+import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyTemplateMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_TEMPLATE_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link DiyTemplateServiceImpl} 的单元测试类
+ *
+ * @author owen
+ */
+@Import(DiyTemplateServiceImpl.class)
+public class DiyTemplateServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private DiyTemplateServiceImpl diyTemplateService;
+
+    @Resource
+    private DiyTemplateMapper diyTemplateMapper;
+
+    @Test
+    public void testCreateDiyTemplate_success() {
+        // 准备参数
+        DiyTemplateCreateReqVO reqVO = randomPojo(DiyTemplateCreateReqVO.class);
+
+        // 调用
+        Long diyTemplateId = diyTemplateService.createDiyTemplate(reqVO);
+        // 断言
+        assertNotNull(diyTemplateId);
+        // 校验记录的属性是否正确
+        DiyTemplateDO diyTemplate = diyTemplateMapper.selectById(diyTemplateId);
+        assertPojoEquals(reqVO, diyTemplate);
+    }
+
+    @Test
+    public void testUpdateDiyTemplate_success() {
+        // mock 数据
+        DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class);
+        diyTemplateMapper.insert(dbDiyTemplate);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        DiyTemplateUpdateReqVO reqVO = randomPojo(DiyTemplateUpdateReqVO.class, o -> {
+            o.setId(dbDiyTemplate.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        diyTemplateService.updateDiyTemplate(reqVO);
+        // 校验是否更新正确
+        DiyTemplateDO diyTemplate = diyTemplateMapper.selectById(reqVO.getId()); // 获取最新的
+        assertPojoEquals(reqVO, diyTemplate);
+    }
+
+    @Test
+    public void testUpdateDiyTemplate_notExists() {
+        // 准备参数
+        DiyTemplateUpdateReqVO reqVO = randomPojo(DiyTemplateUpdateReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> diyTemplateService.updateDiyTemplate(reqVO), DIY_TEMPLATE_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteDiyTemplate_success() {
+        // mock 数据
+        DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class);
+        diyTemplateMapper.insert(dbDiyTemplate);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbDiyTemplate.getId();
+
+        // 调用
+        diyTemplateService.deleteDiyTemplate(id);
+        // 校验数据不存在了
+        assertNull(diyTemplateMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteDiyTemplate_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> diyTemplateService.deleteDiyTemplate(id), DIY_TEMPLATE_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetDiyTemplatePage() {
+        // mock 数据
+        DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> { // 等会查询到
+            o.setName(null);
+            o.setUsed(null);
+            o.setUsedTime(null);
+            o.setRemark(null);
+            o.setPreviewImageUrls(null);
+            o.setProperty(null);
+            o.setCreateTime(null);
+        });
+        diyTemplateMapper.insert(dbDiyTemplate);
+        // 测试 name 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setName(null)));
+        // 测试 used 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsed(null)));
+        // 测试 usedTime 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsedTime(null)));
+        // 测试 remark 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setRemark(null)));
+        // 测试 previewImageUrls 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setPreviewImageUrls(null)));
+        // 测试 property 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setProperty(null)));
+        // 测试 createTime 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setCreateTime(null)));
+        // 准备参数
+        DiyTemplatePageReqVO reqVO = new DiyTemplatePageReqVO();
+        reqVO.setName(null);
+        reqVO.setUsed(null);
+        reqVO.setUsedTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<DiyTemplateDO> pageResult = diyTemplateService.getDiyTemplatePage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbDiyTemplate, pageResult.getList().get(0));
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetDiyTemplateList() {
+        // mock 数据
+        DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> { // 等会查询到
+            o.setName(null);
+            o.setUsed(null);
+            o.setUsedTime(null);
+            o.setRemark(null);
+            o.setPreviewImageUrls(null);
+            o.setProperty(null);
+            o.setCreateTime(null);
+        });
+        diyTemplateMapper.insert(dbDiyTemplate);
+        // 测试 name 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setName(null)));
+        // 测试 used 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsed(null)));
+        // 测试 usedTime 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsedTime(null)));
+        // 测试 remark 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setRemark(null)));
+        // 测试 previewImageUrls 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setPreviewImageUrls(null)));
+        // 测试 property 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setProperty(null)));
+        // 测试 createTime 不匹配
+        diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setCreateTime(null)));
+    }
+
+}

+ 4 - 4
yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/clean.sql

@@ -6,7 +6,7 @@ DELETE FROM "promotion_discount_activity";
 DELETE FROM "promotion_discount_product";
 DELETE FROM "promotion_seckill_config";
 DELETE FROM "promotion_combination_activity";
-DELETE
-FROM "promotion_article_category";
-DELETE
-FROM "promotion_article";
+DELETE FROM "promotion_article_category";
+DELETE FROM "promotion_article";
+DELETE FROM "promotion_diy_template";
+DELETE FROM "promotion_diy_page";

+ 35 - 2
yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql

@@ -112,7 +112,6 @@ CREATE TABLE IF NOT EXISTS "promotion_discount_activity"
     PRIMARY KEY ("id")
 ) COMMENT '限时折扣活动';
 
--- 将该建表 SQL 语句,添加到 yudao-module-promotion-biz 模块的 test/resources/sql/create_tables.sql 文件里
 CREATE TABLE IF NOT EXISTS "promotion_seckill_activity"
 (
     "id"                 bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,
@@ -220,4 +219,38 @@ CREATE TABLE IF NOT EXISTS "promotion_article"
     "deleted"          bit      NOT NULL DEFAULT FALSE,
     "tenant_id"        bigint   NOT NULL,
     PRIMARY KEY ("id")
-) COMMENT '文章管理表';
+) COMMENT '文章管理表';
+
+CREATE TABLE IF NOT EXISTS "promotion_diy_template"
+(
+    "id"                 bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "name"               varchar  NOT NULL,
+    "used"               bit      NOT NULL,
+    "used_time"          varchar,
+    "remark"             varchar,
+    "preview_image_urls" varchar,
+    "property"           varchar  NOT NULL,
+    "creator"            varchar           DEFAULT '',
+    "create_time"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater"            varchar           DEFAULT '',
+    "update_time"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted"            bit      NOT NULL DEFAULT FALSE,
+    "tenant_id"          bigint   NOT NULL DEFAULT 0,
+    PRIMARY KEY ("id")
+) COMMENT '装修模板';
+CREATE TABLE IF NOT EXISTS "promotion_diy_page"
+(
+    "id"                 bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "template_id"        bigint   NOT NULL,
+    "name"               varchar  NOT NULL,
+    "remark"             varchar,
+    "preview_image_urls" varchar,
+    "property"           varchar,
+    "creator"            varchar           DEFAULT '',
+    "create_time"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater"            varchar           DEFAULT '',
+    "update_time"        datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted"            bit      NOT NULL DEFAULT FALSE,
+    "tenant_id"          bigint   NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '装修页面';