feat(cms): 新增多个内容管理模块及配置支持

- 新增轮播图模块,包含实体、参数、控制器和数据库映射,实现轮播图的增删改查与状态管理
- 新增留言咨询模块,支持留言分页查询、详情查询、删除、状态修改及回复功能
- 新增产品分类模块,支持分类的分页查询、列表查询、增删改查及状态管理
- 新增产品模块,支持产品的分页查询、列表查询、增删改查及状态修改,产品添加时自动关联当前登录用户
- cmsArticleController 和 cmsNavigationController 添加文章及栏目状态修改接口,支持权限校验及缓存清理
- 更新 SecurityConfig 添加新的接口免认证配置,调整本地开发端口配置为9500
- 新增生产环境及本地开发环境的详细配置文件,包含数据库连接、Redis、日志、MQTT、文件服务器及阿里云服务配置等内容
This commit is contained in:
2026-07-17 18:31:12 +08:00
parent 01101d422f
commit 211cd2a7e8
32 changed files with 1590 additions and 2 deletions

View File

@@ -143,6 +143,20 @@ public class CmsArticleController extends BaseController {
return fail("删除失败");
}
@PreAuthorize("hasAuthority('cms:cmsArticle:update')")
@Operation(summary = "修改文章状态(发布/下线/草稿)")
@PutMapping("/status")
public ApiResult<?> updateStatus(@RequestBody CmsArticle article) {
CmsArticle entity = new CmsArticle();
entity.setArticleId(article.getArticleId());
entity.setStatus(article.getStatus());
if (cmsArticleService.updateById(entity)) {
redisUtil.delete(CACHE_KEY_ARTICLE + article.getArticleId());
return success("修改成功");
}
return fail("修改失败");
}
@PreAuthorize("hasAuthority('cms:cmsArticle:save')")
@Operation(summary = "批量添加文章")
@PostMapping("/batch")

View File

@@ -0,0 +1,85 @@
package com.gxwebsoft.cms.controller;
import com.gxwebsoft.cms.entity.CmsBanner;
import com.gxwebsoft.cms.param.CmsBannerParam;
import com.gxwebsoft.cms.service.CmsBannerService;
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-17
*/
@Tag(name = "轮播图管理")
@RestController
@RequestMapping("/api/cms/cms-banner")
public class CmsBannerController extends BaseController {
@Resource
private CmsBannerService cmsBannerService;
@Operation(summary = "分页查询轮播图")
@GetMapping("/page")
public ApiResult<PageResult<CmsBanner>> page(CmsBannerParam param) {
return success(cmsBannerService.pageRel(param));
}
@Operation(summary = "查询全部轮播图")
@GetMapping()
public ApiResult<List<CmsBanner>> list(CmsBannerParam param) {
return success(cmsBannerService.listRel(param));
}
@Operation(summary = "根据id查询轮播图")
@GetMapping("/{id}")
public ApiResult<CmsBanner> get(@PathVariable("id") Integer id) {
return success(cmsBannerService.getByIdRel(id));
}
@Operation(summary = "添加轮播图")
@PostMapping()
public ApiResult<?> save(@RequestBody CmsBanner cmsBanner) {
if (cmsBannerService.save(cmsBanner)) {
return success("添加成功");
}
return fail("添加失败");
}
@Operation(summary = "修改轮播图")
@PutMapping()
public ApiResult<?> update(@RequestBody CmsBanner cmsBanner) {
if (cmsBannerService.updateById(cmsBanner)) {
return success("修改成功");
}
return fail("修改失败");
}
@Operation(summary = "删除轮播图")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (cmsBannerService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
@Operation(summary = "修改轮播图状态(启用/停用)")
@PutMapping("/status")
public ApiResult<?> updateStatus(@RequestBody CmsBanner cmsBanner) {
CmsBanner entity = new CmsBanner();
entity.setBannerId(cmsBanner.getBannerId());
entity.setStatus(cmsBanner.getStatus());
if (cmsBannerService.updateById(entity)) {
return success("修改成功");
}
return fail("修改失败");
}
}

View File

@@ -0,0 +1,82 @@
package com.gxwebsoft.cms.controller;
import com.gxwebsoft.cms.entity.CmsMessage;
import com.gxwebsoft.cms.param.CmsMessageParam;
import com.gxwebsoft.cms.service.CmsMessageService;
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.time.LocalDateTime;
import java.util.List;
/**
* 留言咨询控制器
*
* @author WorkBuddy
* @since 2026-07-17
*/
@Tag(name = "留言咨询管理")
@RestController
@RequestMapping("/api/cms/cms-message")
public class CmsMessageController extends BaseController {
@Resource
private CmsMessageService cmsMessageService;
@Operation(summary = "分页查询留言咨询")
@GetMapping("/page")
public ApiResult<PageResult<CmsMessage>> page(CmsMessageParam param) {
return success(cmsMessageService.pageRel(param));
}
@Operation(summary = "查询全部留言咨询")
@GetMapping()
public ApiResult<List<CmsMessage>> list(CmsMessageParam param) {
return success(cmsMessageService.listRel(param));
}
@Operation(summary = "根据id查询留言咨询")
@GetMapping("/{id}")
public ApiResult<CmsMessage> get(@PathVariable("id") Integer id) {
return success(cmsMessageService.getByIdRel(id));
}
@Operation(summary = "删除留言咨询")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (cmsMessageService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
@Operation(summary = "修改留言状态(完成/归档)")
@PutMapping("/status")
public ApiResult<?> updateStatus(@RequestBody CmsMessage message) {
CmsMessage entity = new CmsMessage();
entity.setMessageId(message.getMessageId());
entity.setStatus(message.getStatus());
if (cmsMessageService.updateById(entity)) {
return success("修改成功");
}
return fail("修改失败");
}
@Operation(summary = "回复留言")
@PutMapping("/reply")
public ApiResult<?> reply(@RequestBody CmsMessage message) {
CmsMessage entity = new CmsMessage();
entity.setMessageId(message.getMessageId());
entity.setReply(message.getReply());
entity.setReplyTime(LocalDateTime.now());
entity.setStatus(1);
if (cmsMessageService.updateById(entity)) {
return success("回复成功");
}
return fail("回复失败");
}
}

View File

@@ -28,6 +28,7 @@ import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.Map;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -135,6 +136,27 @@ public class CmsNavigationController extends BaseController {
return fail("删除失败");
}
@PreAuthorize("hasAuthority('cms:cmsNavigation:update')")
@Operation(summary = "修改栏目状态")
@PutMapping("/status")
public ApiResult<?> updateStatus(@RequestBody Map<String, Object> body) {
Object menuIdObj = body.get("menuId");
Object statusObj = body.get("status");
if (menuIdObj == null) {
return fail("缺少 menuId");
}
CmsNavigation entity = new CmsNavigation();
entity.setNavigationId(((Number) menuIdObj).intValue());
if (statusObj != null) {
entity.setStatus(((Number) statusObj).intValue());
}
if (cmsNavigationService.updateById(entity)) {
redisUtil.delete(SITE_INFO_KEY_PREFIX.concat(getTenantId().toString()));
return success("修改成功");
}
return fail("修改失败");
}
@PreAuthorize("hasAuthority('cms:cmsNavigation:save')")
@Operation(summary = "批量添加网站导航记录表")
@PostMapping("/batch")

View File

@@ -0,0 +1,85 @@
package com.gxwebsoft.cms.controller;
import com.gxwebsoft.cms.entity.CmsProductCategory;
import com.gxwebsoft.cms.param.CmsProductCategoryParam;
import com.gxwebsoft.cms.service.CmsProductCategoryService;
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-17
*/
@Tag(name = "产品分类管理")
@RestController
@RequestMapping("/api/cms/cms-product-category")
public class CmsProductCategoryController extends BaseController {
@Resource
private CmsProductCategoryService cmsProductCategoryService;
@Operation(summary = "分页查询产品分类")
@GetMapping("/page")
public ApiResult<PageResult<CmsProductCategory>> page(CmsProductCategoryParam param) {
return success(cmsProductCategoryService.pageRel(param));
}
@Operation(summary = "查询全部产品分类")
@GetMapping()
public ApiResult<List<CmsProductCategory>> list(CmsProductCategoryParam param) {
return success(cmsProductCategoryService.listRel(param));
}
@Operation(summary = "根据id查询产品分类")
@GetMapping("/{id}")
public ApiResult<CmsProductCategory> get(@PathVariable("id") Integer id) {
return success(cmsProductCategoryService.getByIdRel(id));
}
@Operation(summary = "添加产品分类")
@PostMapping()
public ApiResult<?> save(@RequestBody CmsProductCategory category) {
if (cmsProductCategoryService.save(category)) {
return success("添加成功");
}
return fail("添加失败");
}
@Operation(summary = "修改产品分类")
@PutMapping()
public ApiResult<?> update(@RequestBody CmsProductCategory category) {
if (cmsProductCategoryService.updateById(category)) {
return success("修改成功");
}
return fail("修改失败");
}
@Operation(summary = "删除产品分类")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (cmsProductCategoryService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
@Operation(summary = "修改分类状态(启用/禁用)")
@PutMapping("/status")
public ApiResult<?> updateStatus(@RequestBody CmsProductCategory category) {
CmsProductCategory entity = new CmsProductCategory();
entity.setCategoryId(category.getCategoryId());
entity.setStatus(category.getStatus());
if (cmsProductCategoryService.updateById(entity)) {
return success("修改成功");
}
return fail("修改失败");
}
}

View File

@@ -0,0 +1,90 @@
package com.gxwebsoft.cms.controller;
import com.gxwebsoft.cms.entity.CmsProduct;
import com.gxwebsoft.cms.param.CmsProductParam;
import com.gxwebsoft.cms.service.CmsProductService;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
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-17
*/
@Tag(name = "产品管理")
@RestController
@RequestMapping("/api/cms/cms-product")
public class CmsProductController extends BaseController {
@Resource
private CmsProductService cmsProductService;
@Operation(summary = "分页查询产品")
@GetMapping("/page")
public ApiResult<PageResult<CmsProduct>> page(CmsProductParam param) {
return success(cmsProductService.pageRel(param));
}
@Operation(summary = "查询全部产品")
@GetMapping()
public ApiResult<List<CmsProduct>> list(CmsProductParam param) {
return success(cmsProductService.listRel(param));
}
@Operation(summary = "根据id查询产品")
@GetMapping("/{id}")
public ApiResult<CmsProduct> get(@PathVariable("id") Integer id) {
return success(cmsProductService.getByIdRel(id));
}
@Operation(summary = "添加产品")
@PostMapping()
public ApiResult<?> save(@RequestBody CmsProduct cmsProduct) {
User loginUser = getLoginUser();
if (loginUser != null) {
cmsProduct.setUserId(loginUser.getUserId());
}
if (cmsProductService.save(cmsProduct)) {
return success("添加成功");
}
return fail("添加失败");
}
@Operation(summary = "修改产品")
@PutMapping()
public ApiResult<?> update(@RequestBody CmsProduct cmsProduct) {
if (cmsProductService.updateById(cmsProduct)) {
return success("修改成功");
}
return fail("修改失败");
}
@Operation(summary = "删除产品")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (cmsProductService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
@Operation(summary = "修改产品状态(在售/下架)")
@PutMapping("/status")
public ApiResult<?> updateStatus(@RequestBody CmsProduct cmsProduct) {
CmsProduct entity = new CmsProduct();
entity.setProductId(cmsProduct.getProductId());
entity.setStatus(cmsProduct.getStatus());
if (cmsProductService.updateById(entity)) {
return success("修改成功");
}
return fail("修改失败");
}
}

View File

@@ -0,0 +1,82 @@
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-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("cms_banner")
@Schema(name = "CmsBanner对象", description = "轮播图")
public class CmsBanner implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "轮播图ID")
@TableId(value = "banner_id", type = IdType.AUTO)
private Integer bannerId;
@Schema(description = "标题")
private String title;
@Schema(description = "图片地址")
private String image;
@Schema(description = "跳转类型: 0无 1外链 2文章 3产品")
private Integer linkType;
@Schema(description = "外链地址")
private String linkUrl;
@Schema(description = "跳转目标ID(文章/产品)")
private Integer linkTargetId;
@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;
}

View File

@@ -0,0 +1,74 @@
package com.gxwebsoft.cms.entity;
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 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-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("cms_message")
@Schema(name = "CmsMessage对象", description = "留言咨询")
public class CmsMessage implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "留言ID")
@TableId(value = "message_id", type = IdType.AUTO)
private Integer messageId;
@Schema(description = "联系人姓名")
private String name;
@Schema(description = "手机号")
private String phone;
@Schema(description = "邮箱")
private String email;
@Schema(description = "公司名称")
private String company;
@Schema(description = "类型: 1产品咨询 2意见反馈 3商务合作")
private Integer type;
@Schema(description = "留言内容")
private String content;
@Schema(description = "回复内容")
private String reply;
@Schema(description = "回复时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime replyTime;
@Schema(description = "状态: 0待处理 1已回复 2已完成 3已归档")
private Integer status;
@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;
}

View File

@@ -0,0 +1,93 @@
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.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 产品
*
* @author WorkBuddy
* @since 2026-07-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("cms_product")
@Schema(name = "CmsProduct对象", description = "产品")
public class CmsProduct implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "产品ID")
@TableId(value = "product_id", type = IdType.AUTO)
private Integer productId;
@Schema(description = "产品名称")
private String productName;
@Schema(description = "封面图")
private String cover;
@Schema(description = "简介")
private String summary;
@Schema(description = "详情描述")
private String description;
@Schema(description = "分类ID")
private Integer categoryId;
@Schema(description = "分类名称(冗余存储)")
private String categoryName;
@Schema(description = "价格")
private BigDecimal price;
@Schema(description = "原价")
private BigDecimal originalPrice;
@Schema(description = "计价单位")
private String unit;
@Schema(description = "计费方式: 1包月 2包年 3一次性 4按量")
private Integer chargeType;
@Schema(description = "状态: 1在售 0下架")
private Integer status;
@Schema(description = "排序(数字越小越靠前)")
@JsonProperty("sortNum")
@JSONField(name = "sortNum")
private Integer sortNumber;
@Schema(description = "标签, 逗号分隔")
private String tags;
@Schema(description = "创建人ID")
private Integer userId;
@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;
}

View File

@@ -0,0 +1,59 @@
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-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("cms_product_category")
@Schema(name = "CmsProductCategory对象", description = "产品分类")
public class CmsProductCategory implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "分类ID")
@TableId(value = "category_id", type = IdType.AUTO)
private Integer categoryId;
@Schema(description = "分类名称")
private String categoryName;
@Schema(description = "状态: 1启用 0禁用")
private Integer status;
@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;
}

View File

@@ -0,0 +1,16 @@
package com.gxwebsoft.cms.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gxwebsoft.cms.entity.CmsBanner;
import org.apache.ibatis.annotations.Mapper;
/**
* 轮播图Mapper
*
* @author WorkBuddy
* @since 2026-07-17
*/
@Mapper
public interface CmsBannerMapper extends BaseMapper<CmsBanner> {
}

View File

@@ -0,0 +1,16 @@
package com.gxwebsoft.cms.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gxwebsoft.cms.entity.CmsMessage;
import org.apache.ibatis.annotations.Mapper;
/**
* 留言咨询Mapper
*
* @author WorkBuddy
* @since 2026-07-17
*/
@Mapper
public interface CmsMessageMapper extends BaseMapper<CmsMessage> {
}

View File

@@ -0,0 +1,16 @@
package com.gxwebsoft.cms.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gxwebsoft.cms.entity.CmsProductCategory;
import org.apache.ibatis.annotations.Mapper;
/**
* 产品分类Mapper
*
* @author WorkBuddy
* @since 2026-07-17
*/
@Mapper
public interface CmsProductCategoryMapper extends BaseMapper<CmsProductCategory> {
}

View File

@@ -0,0 +1,16 @@
package com.gxwebsoft.cms.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gxwebsoft.cms.entity.CmsProduct;
import org.apache.ibatis.annotations.Mapper;
/**
* 产品Mapper
*
* @author WorkBuddy
* @since 2026-07-17
*/
@Mapper
public interface CmsProductMapper extends BaseMapper<CmsProduct> {
}

View File

@@ -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-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "CmsBannerParam对象", description = "轮播图查询参数")
public class CmsBannerParam extends BaseParam {
private static final long serialVersionUID = 1L;
@Schema(description = "轮播图ID")
@QueryField(type = QueryType.EQ)
private Integer bannerId;
@Schema(description = "标题")
@QueryField(type = QueryType.LIKE)
private String title;
@Schema(description = "跳转类型: 0无 1外链 2文章 3产品")
@QueryField(type = QueryType.EQ)
private Integer linkType;
@Schema(description = "状态: 1启用 0停用")
@QueryField(type = QueryType.EQ)
private Integer status;
}

View File

@@ -0,0 +1,41 @@
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-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "CmsMessageParam对象", description = "留言咨询查询参数")
public class CmsMessageParam extends BaseParam {
private static final long serialVersionUID = 1L;
@Schema(description = "留言ID")
@QueryField(type = QueryType.EQ)
private Integer messageId;
@Schema(description = "联系人姓名")
@QueryField(type = QueryType.LIKE)
private String name;
@Schema(description = "手机号")
@QueryField(type = QueryType.EQ)
private String phone;
@Schema(description = "类型: 1产品咨询 2意见反馈 3商务合作")
@QueryField(type = QueryType.EQ)
private Integer type;
@Schema(description = "状态: 0待处理 1已回复 2已完成 3已归档")
@QueryField(type = QueryType.EQ)
private Integer status;
}

View File

@@ -0,0 +1,33 @@
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-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "CmsProductCategoryParam对象", description = "产品分类查询参数")
public class CmsProductCategoryParam extends BaseParam {
private static final long serialVersionUID = 1L;
@Schema(description = "分类ID")
@QueryField(type = QueryType.EQ)
private Integer categoryId;
@Schema(description = "分类名称")
@QueryField(type = QueryType.LIKE)
private String categoryName;
@Schema(description = "状态: 1启用 0禁用")
@QueryField(type = QueryType.EQ)
private Integer status;
}

View File

@@ -0,0 +1,41 @@
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-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "CmsProductParam对象", description = "产品查询参数")
public class CmsProductParam extends BaseParam {
private static final long serialVersionUID = 1L;
@Schema(description = "产品ID")
@QueryField(type = QueryType.EQ)
private Integer productId;
@Schema(description = "产品名称")
@QueryField(type = QueryType.LIKE)
private String productName;
@Schema(description = "分类ID")
@QueryField(type = QueryType.EQ)
private Integer categoryId;
@Schema(description = "状态: 1在售 0下架")
@QueryField(type = QueryType.EQ)
private Integer status;
@Schema(description = "计费方式: 1包月 2包年 3一次性 4按量")
@QueryField(type = QueryType.EQ)
private Integer chargeType;
}

View File

@@ -0,0 +1,41 @@
package com.gxwebsoft.cms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.cms.entity.CmsBanner;
import com.gxwebsoft.cms.param.CmsBannerParam;
import com.gxwebsoft.common.core.web.PageResult;
import java.util.List;
/**
* 轮播图Service接口
*
* @author WorkBuddy
* @since 2026-07-17
*/
public interface CmsBannerService extends IService<CmsBanner> {
/**
* 分页查询
*
* @param param 查询参数
* @return 分页结果
*/
PageResult<CmsBanner> pageRel(CmsBannerParam param);
/**
* 查询全部
*
* @param param 查询参数
* @return 列表
*/
List<CmsBanner> listRel(CmsBannerParam param);
/**
* 根据id查询
*
* @param bannerId 轮播图ID
* @return 轮播图
*/
CmsBanner getByIdRel(Integer bannerId);
}

View File

@@ -0,0 +1,41 @@
package com.gxwebsoft.cms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.cms.entity.CmsMessage;
import com.gxwebsoft.cms.param.CmsMessageParam;
import com.gxwebsoft.common.core.web.PageResult;
import java.util.List;
/**
* 留言咨询Service接口
*
* @author WorkBuddy
* @since 2026-07-17
*/
public interface CmsMessageService extends IService<CmsMessage> {
/**
* 分页查询
*
* @param param 查询参数
* @return 分页结果
*/
PageResult<CmsMessage> pageRel(CmsMessageParam param);
/**
* 查询全部
*
* @param param 查询参数
* @return 列表
*/
List<CmsMessage> listRel(CmsMessageParam param);
/**
* 根据id查询
*
* @param messageId 留言ID
* @return 留言
*/
CmsMessage getByIdRel(Integer messageId);
}

View File

@@ -0,0 +1,41 @@
package com.gxwebsoft.cms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.cms.entity.CmsProductCategory;
import com.gxwebsoft.cms.param.CmsProductCategoryParam;
import com.gxwebsoft.common.core.web.PageResult;
import java.util.List;
/**
* 产品分类Service接口
*
* @author WorkBuddy
* @since 2026-07-17
*/
public interface CmsProductCategoryService extends IService<CmsProductCategory> {
/**
* 分页查询
*
* @param param 查询参数
* @return 分页结果
*/
PageResult<CmsProductCategory> pageRel(CmsProductCategoryParam param);
/**
* 查询全部
*
* @param param 查询参数
* @return 列表
*/
List<CmsProductCategory> listRel(CmsProductCategoryParam param);
/**
* 根据id查询
*
* @param categoryId 分类ID
* @return 分类
*/
CmsProductCategory getByIdRel(Integer categoryId);
}

View File

@@ -0,0 +1,41 @@
package com.gxwebsoft.cms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.cms.entity.CmsProduct;
import com.gxwebsoft.cms.param.CmsProductParam;
import com.gxwebsoft.common.core.web.PageResult;
import java.util.List;
/**
* 产品Service接口
*
* @author WorkBuddy
* @since 2026-07-17
*/
public interface CmsProductService extends IService<CmsProduct> {
/**
* 分页查询
*
* @param param 查询参数
* @return 分页结果
*/
PageResult<CmsProduct> pageRel(CmsProductParam param);
/**
* 查询全部
*
* @param param 查询参数
* @return 列表
*/
List<CmsProduct> listRel(CmsProductParam param);
/**
* 根据id查询
*
* @param productId 产品ID
* @return 产品
*/
CmsProduct getByIdRel(Integer productId);
}

View File

@@ -0,0 +1,44 @@
package com.gxwebsoft.cms.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.cms.entity.CmsBanner;
import com.gxwebsoft.cms.mapper.CmsBannerMapper;
import com.gxwebsoft.cms.param.CmsBannerParam;
import com.gxwebsoft.cms.service.CmsBannerService;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 轮播图Service实现
*
* @author WorkBuddy
* @since 2026-07-17
*/
@Service
public class CmsBannerServiceImpl extends ServiceImpl<CmsBannerMapper, CmsBanner> implements CmsBannerService {
@Override
public PageResult<CmsBanner> pageRel(CmsBannerParam param) {
PageParam<CmsBanner, CmsBannerParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc");
IPage<CmsBanner> result = page(page, page.getOrderWrapper());
return new PageResult<>(result.getRecords(), result.getTotal());
}
@Override
public List<CmsBanner> listRel(CmsBannerParam param) {
PageParam<CmsBanner, CmsBannerParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc");
return list(page.getOrderWrapper());
}
@Override
public CmsBanner getByIdRel(Integer bannerId) {
return getById(bannerId);
}
}

View File

@@ -0,0 +1,57 @@
package com.gxwebsoft.cms.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.cms.entity.CmsMessage;
import com.gxwebsoft.cms.mapper.CmsMessageMapper;
import com.gxwebsoft.cms.param.CmsMessageParam;
import com.gxwebsoft.cms.service.CmsMessageService;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 留言咨询Service实现
*
* @author WorkBuddy
* @since 2026-07-17
*/
@Service
public class CmsMessageServiceImpl extends ServiceImpl<CmsMessageMapper, CmsMessage> implements CmsMessageService {
@Override
public PageResult<CmsMessage> pageRel(CmsMessageParam param) {
PageParam<CmsMessage, CmsMessageParam> page = new PageParam<>(param);
page.setDefaultOrder("create_time desc");
QueryWrapper<CmsMessage> wrapper = page.getWrapper();
if (StrUtil.isNotBlank(param.getKeywords())) {
wrapper.and(w -> w.like("name", param.getKeywords())
.or().like("content", param.getKeywords())
.or().like("company", param.getKeywords()));
}
IPage<CmsMessage> result = page(page, page.getOrderWrapper(wrapper));
return new PageResult<>(result.getRecords(), result.getTotal());
}
@Override
public List<CmsMessage> listRel(CmsMessageParam param) {
PageParam<CmsMessage, CmsMessageParam> page = new PageParam<>(param);
page.setDefaultOrder("create_time desc");
QueryWrapper<CmsMessage> wrapper = page.getWrapper();
if (StrUtil.isNotBlank(param.getKeywords())) {
wrapper.and(w -> w.like("name", param.getKeywords())
.or().like("content", param.getKeywords())
.or().like("company", param.getKeywords()));
}
return list(page.getOrderWrapper(wrapper));
}
@Override
public CmsMessage getByIdRel(Integer messageId) {
return getById(messageId);
}
}

View File

@@ -0,0 +1,44 @@
package com.gxwebsoft.cms.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.cms.entity.CmsProductCategory;
import com.gxwebsoft.cms.mapper.CmsProductCategoryMapper;
import com.gxwebsoft.cms.param.CmsProductCategoryParam;
import com.gxwebsoft.cms.service.CmsProductCategoryService;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 产品分类Service实现
*
* @author WorkBuddy
* @since 2026-07-17
*/
@Service
public class CmsProductCategoryServiceImpl extends ServiceImpl<CmsProductCategoryMapper, CmsProductCategory> implements CmsProductCategoryService {
@Override
public PageResult<CmsProductCategory> pageRel(CmsProductCategoryParam param) {
PageParam<CmsProductCategory, CmsProductCategoryParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc");
IPage<CmsProductCategory> result = page(page, page.getOrderWrapper());
return new PageResult<>(result.getRecords(), result.getTotal());
}
@Override
public List<CmsProductCategory> listRel(CmsProductCategoryParam param) {
PageParam<CmsProductCategory, CmsProductCategoryParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc");
return list(page.getOrderWrapper());
}
@Override
public CmsProductCategory getByIdRel(Integer categoryId) {
return getById(categoryId);
}
}

View File

@@ -0,0 +1,55 @@
package com.gxwebsoft.cms.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.cms.entity.CmsProduct;
import com.gxwebsoft.cms.mapper.CmsProductMapper;
import com.gxwebsoft.cms.param.CmsProductParam;
import com.gxwebsoft.cms.service.CmsProductService;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 产品Service实现
*
* @author WorkBuddy
* @since 2026-07-17
*/
@Service
public class CmsProductServiceImpl extends ServiceImpl<CmsProductMapper, CmsProduct> implements CmsProductService {
@Override
public PageResult<CmsProduct> pageRel(CmsProductParam param) {
PageParam<CmsProduct, CmsProductParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc");
QueryWrapper<CmsProduct> wrapper = page.getWrapper();
if (StrUtil.isNotBlank(param.getKeywords())) {
wrapper.and(w -> w.like("product_name", param.getKeywords())
.or().like("summary", param.getKeywords()));
}
IPage<CmsProduct> result = page(page, page.getOrderWrapper(wrapper));
return new PageResult<>(result.getRecords(), result.getTotal());
}
@Override
public List<CmsProduct> listRel(CmsProductParam param) {
PageParam<CmsProduct, CmsProductParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc");
QueryWrapper<CmsProduct> wrapper = page.getWrapper();
if (StrUtil.isNotBlank(param.getKeywords())) {
wrapper.and(w -> w.like("product_name", param.getKeywords())
.or().like("summary", param.getKeywords()));
}
return list(page.getOrderWrapper(wrapper));
}
@Override
public CmsProduct getByIdRel(Integer productId) {
return getById(productId);
}
}

View File

@@ -0,0 +1,123 @@
-- ----------------------------
-- 产品分类表
-- 官网产品模块的分类
-- ----------------------------
CREATE TABLE IF NOT EXISTS `cms_product_category` (
`category_id` INT NOT NULL AUTO_INCREMENT COMMENT '分类ID',
`category_name` VARCHAR(100) NOT NULL COMMENT '分类名称',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 1启用 0禁用',
`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 (`category_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品分类';
-- ----------------------------
-- 产品表
-- 官网展示的云产品
-- ----------------------------
CREATE TABLE IF NOT EXISTS `cms_product` (
`product_id` INT NOT NULL AUTO_INCREMENT COMMENT '产品ID',
`product_name` VARCHAR(200) NOT NULL COMMENT '产品名称',
`cover` VARCHAR(500) DEFAULT NULL COMMENT '封面图',
`summary` VARCHAR(500) DEFAULT NULL COMMENT '简介',
`description` TEXT DEFAULT NULL COMMENT '详情描述',
`category_id` INT DEFAULT NULL COMMENT '分类ID',
`category_name` VARCHAR(100) DEFAULT NULL COMMENT '分类名称(冗余存储, 便于列表展示)',
`price` DECIMAL(10,2) DEFAULT NULL COMMENT '价格',
`original_price` DECIMAL(10,2) DEFAULT NULL COMMENT '原价',
`unit` VARCHAR(20) DEFAULT NULL COMMENT '计价单位',
`charge_type` TINYINT DEFAULT 1 COMMENT '计费方式: 1包月 2包年 3一次性 4按量',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 1在售 0下架',
`sort_number` INT NOT NULL DEFAULT 0 COMMENT '排序(数字越小越靠前)',
`tags` VARCHAR(255) DEFAULT NULL COMMENT '标签, 逗号分隔',
`user_id` INT DEFAULT NULL COMMENT '创建人ID',
`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 (`product_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_category_id` (`category_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品';
-- ----------------------------
-- 案例表
-- 官网客户案例展示
-- ----------------------------
CREATE TABLE IF NOT EXISTS `cms_case` (
`case_id` INT NOT NULL AUTO_INCREMENT COMMENT '案例ID',
`case_name` VARCHAR(200) NOT NULL COMMENT '案例名称',
`cover` VARCHAR(500) DEFAULT NULL COMMENT '封面图',
`summary` VARCHAR(500) DEFAULT NULL COMMENT '简介',
`content` TEXT DEFAULT NULL COMMENT '案例详情',
`industry` VARCHAR(100) DEFAULT NULL COMMENT '所属行业',
`customer` VARCHAR(200) DEFAULT NULL COMMENT '客户名称',
`product_used` VARCHAR(255) DEFAULT NULL COMMENT '使用产品',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 1已发布 0已下线',
`sort_number` INT NOT NULL DEFAULT 0 COMMENT '排序(数字越小越靠前)',
`views` 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 (`case_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_industry` (`industry`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='案例';
-- ----------------------------
-- 轮播图表
-- 官网首页轮播图
-- ----------------------------
CREATE TABLE IF NOT EXISTS `cms_banner` (
`banner_id` INT NOT NULL AUTO_INCREMENT COMMENT '轮播图ID',
`title` VARCHAR(200) DEFAULT NULL COMMENT '标题',
`image` VARCHAR(500) NOT NULL COMMENT '图片地址',
`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(文章/产品)',
`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(500) 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 (`banner_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='轮播图';
-- ----------------------------
-- 留言咨询表
-- 官网留言/咨询
-- ----------------------------
CREATE TABLE IF NOT EXISTS `cms_message` (
`message_id` INT NOT NULL AUTO_INCREMENT COMMENT '留言ID',
`name` VARCHAR(50) NOT NULL COMMENT '联系人姓名',
`phone` VARCHAR(20) DEFAULT NULL COMMENT '手机号',
`email` VARCHAR(100) DEFAULT NULL COMMENT '邮箱',
`company` VARCHAR(100) DEFAULT NULL COMMENT '公司名称',
`type` TINYINT NOT NULL DEFAULT 1 COMMENT '类型: 1产品咨询 2意见反馈 3商务合作',
`content` TEXT NOT NULL COMMENT '留言内容',
`reply` TEXT DEFAULT NULL COMMENT '回复内容',
`reply_time` DATETIME DEFAULT NULL COMMENT '回复时间',
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态: 0待处理 1已回复 2已完成 3已归档',
`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 (`message_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_phone` (`phone`),
KEY `idx_status` (`status`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='留言咨询';

View File

@@ -39,6 +39,7 @@ public class SecurityConfig {
.permitAll()
.antMatchers(
"/api/login",
"/api/loginBySms",
"/api/qr-login/**",
"/api/register",
"/api/cms/website/createWebsite",
@@ -81,7 +82,9 @@ public class SecurityConfig {
"/api/shop/shop-order/test",
"/api/qr-code/**",
"/api/shop/order-delivery/notify",
"/api/cms/cms-contact-lead/submit"
"/api/cms/cms-contact-lead/submit",
"/api/led/bme/stop-replace",
"/api/led/bme/number-sources"
)
.permitAll()
.anyRequest()

View File

@@ -0,0 +1,25 @@
# 2026-06-24 工作日志
## LED大屏项目前后端分析与静态化部署方案评估
### 项目概况
- 后端: Spring Boot 2.7.18 + MyBatis-Plus + Redis13个业务模块的大型单体应用
- LED模块只是其中一个只有2个API接口停替诊查询/号源查询本质是医院BME中台的代理
- 前端: Vue 3 + TypeScript + Vite + Ant Design Vue + ele-admin-pro
- LED页面(/views/led/index.vue): 医院大厅大屏展示两个表格每10秒自动翻页轮播
### 关键发现
- LED后端核心逻辑: Token获取/刷新(Redis缓存5天) + MD5签名 + HTTP转发BME + 响应扁平化
- BME中台地址: http://16.1.4.201:7979需要appid/secret-key认证
- 前端日期硬编码为2025-12-23应改为动态获取
- /led路由未加入WHITE_LIST白名单大屏需登录才能访问
- 前端API基地址: localStorage ApiUrl > 环境变量 VITE_API_URL
### 部署方案建议
客户内网无法部署面板环境推荐方案A: Nginx(静态前端) + Go轻量后端
- Go后端只需实现2个接口 + 内存缓存Token + MD5签名无需MySQL/Redis
- 替代方案: Python(Flask/FastAPI) / OpenResty(Nginx+Lua) / 保留Java JAR(需Java+Redis)
- 纯静态不可行: BME API需要secret-key认证不能暴露到前端
### 状态
- 仅分析,未改动任何代码

View File

@@ -2,7 +2,8 @@
# 服务器配置
server:
port: 9200
# 9500 与 website-admin 本地开发代理server/api/_* 的 dev 目标 127.0.0.1:9500一致
port: 9500
# 数据源配置
spring:

View File

@@ -0,0 +1,85 @@
# 生产环境配置
# 数据源配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/led?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: nnws112233
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
druid:
remove-abandoned: true
# redis
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
# 日志配置
logging:
file:
name: websoft-modules.log
level:
root: DEBUG
com.gxwebsoft: DEBUG
com.baomidou.mybatisplus: DEBUG
socketio:
host: 0.0.0.0 #IP地址
# MQTT配置
mqtt:
enabled: true # 启用MQTT服务
host: tcp://132.232.214.96:1883
username: swdev
password: Sw20250523
client-id-prefix: hjm_car_
topic: /SW_GPS/#
qos: 2
connection-timeout: 10
keep-alive-interval: 20
auto-reconnect: true
# 框架配置
config:
# 文件服务器
file-server: https://file-s209.shoplnk.cn
# 基础模块接口
server-url: https://server.websoft.top/api
# 业务模块接口
api-url: https://cms-api.websoft.top/api
upload-path: /www/wwwroot/file.ws
# 阿里云OSS云存储
endpoint: https://oss-cn-shenzhen.aliyuncs.com
accessKeyId: LTAI4GKGZ9Z2Z8JZ77c3GNZP
accessKeySecret: BiDkpS7UXj72HWwDWaFZxiXjNFBNCM
bucketName: oss-gxwebsoft
bucketDomain: https://oss.wsdns.cn
aliyunDomain: https://oss-gxwebsoft.oss-cn-shenzhen.aliyuncs.com
# 生产环境证书配置
certificate:
load-mode: VOLUME # 生产环境从Docker挂载卷加载
cert-root-path: /www/wwwroot/file.ws
# 支付配置缓存
payment:
cache:
# 支付配置缓存键前缀,生产环境使用 Payment:1* 格式
key-prefix: "Payment:1"
# 缓存过期时间(小时)
expire-hours: 24
# 阿里云翻译配置
aliyun:
translate:
access-key-id: LTAI5tEsyhW4GCKbds1qsopg
access-key-secret: zltFlQrYVAoq2KMFDWgLa3GhkMNeyO
endpoint: mt.cn-hangzhou.aliyuncs.com
wechatpay:
transfer:
scene-id: 1005
scene-report-infos-json: '[{"info_type":"岗位类型","info_content":"配送员"},{"info_type":"报酬说明","info_content":"12月份配送费"}]'

View File

@@ -0,0 +1,85 @@
# 生产环境配置
# 数据源配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/led?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: nnws112233
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
druid:
remove-abandoned: true
# redis
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
# 日志配置
logging:
file:
name: websoft-modules.log
level:
root: WARN
com.gxwebsoft: ERROR
com.baomidou.mybatisplus: ERROR
socketio:
host: 0.0.0.0 #IP地址
# MQTT配置
mqtt:
enabled: true # 启用MQTT服务
host: tcp://132.232.214.96:1883
username: swdev
password: Sw20250523
client-id-prefix: hjm_car_
topic: /SW_GPS/#
qos: 2
connection-timeout: 10
keep-alive-interval: 20
auto-reconnect: true
# 框架配置
config:
# 文件服务器
file-server: https://file-s209.shoplnk.cn
# 基础模块接口
server-url: https://server.websoft.top/api
# 业务模块接口
api-url: https://cms-api.websoft.top/api
upload-path: /www/wwwroot/file.ws
# 阿里云OSS云存储
endpoint: https://oss-cn-shenzhen.aliyuncs.com
accessKeyId: LTAI4GKGZ9Z2Z8JZ77c3GNZP
accessKeySecret: BiDkpS7UXj72HWwDWaFZxiXjNFBNCM
bucketName: oss-gxwebsoft
bucketDomain: https://oss.wsdns.cn
aliyunDomain: https://oss-gxwebsoft.oss-cn-shenzhen.aliyuncs.com
# 生产环境证书配置
certificate:
load-mode: VOLUME # 生产环境从Docker挂载卷加载
cert-root-path: /www/wwwroot/file.ws
# 支付配置缓存
payment:
cache:
# 支付配置缓存键前缀,生产环境使用 Payment:1* 格式
key-prefix: "Payment:1"
# 缓存过期时间(小时)
expire-hours: 24
# 阿里云翻译配置
aliyun:
translate:
access-key-id: LTAI5tEsyhW4GCKbds1qsopg
access-key-secret: zltFlQrYVAoq2KMFDWgLa3GhkMNeyO
endpoint: mt.cn-hangzhou.aliyuncs.com
wechatpay:
transfer:
scene-id: 1005
scene-report-infos-json: '[{"info_type":"岗位类型","info_content":"配送员"},{"info_type":"报酬说明","info_content":"12月份配送费"}]'