SpringBoot 省级旅游口号管理系统设计与实现
前言
随着信息技术的飞速发展,旅游业作为全球经济增长的重要引擎之一,正经历着前所未有的数字化转型。旅游口号作为吸引游客、传播地方文化的重要手段,其管理和检索的效率直接关系到旅游营销的成功与否。在大数据时代背景下,如何高效地存储、管理和检索海量的省级旅游口号数据,成为旅游信息化建设的关键问题之一。
本实践旨在探索 SpringBoot 与 PostgreSQL 在省级旅游口号管理中的应用,通过构建一个基于 SpringBoot 的省级旅游口号存储及检索系统,实现对旅游口号的高效存储、精准检索和便捷管理。在系统设计过程中,充分考虑了系统的可扩展性、数据的安全性和查询的高效性。通过 SpringBoot 的依赖注入和自动配置功能,简化了开发流程,提高了开发效率。此外,系统还采用了 RESTful API 架构,方便与其他系统进行集成和数据交互。
一、数据库存储设计
在进行省级宣传口号的信息管理时,需要考虑以下两点:支持时间的连续性,例如某省份在不同年份使用不同的宣传口号。本节将从梳理实现过程中需要考虑的点以及给出实际的数据库表接口设计和 SQL 脚本两个方面进行讲解。
1、一些需要考虑的点
在实际情况下,时间连续性是一个比较常见的需求。比如一些口号经过多次的变更,在实现上我们会分割成多个部分,比如 2023 年以前是一个口号,2024 到 2025 年又是一个口号,而 2025 年又启用了一个新的口号。这都是需要考虑的点。在进行数据赋值时,可以假定 -1 表示没有明确的起始年份,而在未来的年份上,使用 9999 来进行设置。除此之外,还需要设置当前口号的使用状态,比如使用 0 表示无效,而 1 表示有效。同时为了方便查询,将省份 code 和省份名称都设计到数据库表中。
2、物理表设计及表结构
省级旅游口号的表结构如下:

对应的示例数据库脚本如下:
CREATE TABLE "public"."biz_tourism_slogans_info" (
"pk_id" int8 NOT NULL,
"province_code" varchar(10) COLLATE "pg_catalog"."default" NOT NULL,
"province_name" varchar(20) COLLATE "pg_catalog"."default" NOT NULL,
"start_year" int2,
"end_year" int2,
"slogan" varchar(50) COLLATE "pg_catalog"."default",
"activity_flag" int2,
"create_by" varchar(64) COLLATE "pg_catalog"."default",
"create_time" timestamp(6),
"update_by" varchar(64) COLLATE "pg_catalog"."default",
"update_time" timestamp(6),
CONSTRAINT "pk_biz_tourism_slogans_info" PRIMARY KEY ("pk_id")
);
COMMENT ON COLUMN "biz_tourism_slogans_info"."pk_id" IS '主键';
COMMENT ON COLUMN "biz_tourism_slogans_info"."province_code" IS '省份 code';
COMMENT ON COLUMN "biz_tourism_slogans_info"."province_name" IS '省份名称';
COMMENT ON COLUMN "biz_tourism_slogans_info"."start_year" IS '开始年份,-1 没有具体年份';
COMMENT ON COLUMN "biz_tourism_slogans_info"."end_year" IS '结束年份,9999 表示';
COMMENT ON COLUMN "biz_tourism_slogans_info"."slogan" IS '口号';
COMMENT ON COLUMN "biz_tourism_slogans_info"."activity_flag" IS '生效标记,0 无效,1 有效';
COMMENT ON COLUMN "biz_tourism_slogans_info"."create_by" IS '创建人';
COMMENT ON COLUMN "biz_tourism_slogans_info"."create_time" IS '创建时间';
COMMENT ON COLUMN "biz_tourism_slogans_info"."update_by" IS '更新人';
COMMENT ON COLUMN "biz_tourism_slogans_info"."update_time" IS '更新时间';
COMMENT ON TABLE "biz_tourism_slogans_info" IS '旅游口号信息表,用于存储旅游口号信息';
实际情况下起始可以加一些数据库的索引用来进行查询加速。
二、SpringBoot 后端设计
以上完成了数据库的设计和物理表结构的建模之后,接下来就可以围绕宣传口号表来进行实现。下面按照传统的 MVC 三层架构来进行说明。
1、模型层实现
首先来简单介绍一下模型层,这里包括数据库表对应的 JavaBean 和数据库操作对象,即 Mapper 接口类。这三个类的具体代码如下所示:
package com.yelang.project.extend.scenicspot.domain;
import java.io.Serializable;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* - 旅游口号信息表,用于存储旅游口号信息
*/
@TableName(value = "biz_tourism_slogans_info")
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class TourismSlogansInfo implements Serializable {
private static final long serialVersionUID = 5255612972674800046L;
@TableId(value = "pk_id")
private Long pkId; // 主键
@TableField(value = "province_code")
private String provinceCode; // 省份 code
@TableField(value = "province_name")
private String provinceName; // 省份名称
@TableField(value = "start_year")
private Integer startYear; // 开始年份,-1 表示没有具体年份
@TableField(value = "end_year")
private Integer endYear; // 结束年份,9999 表示
private String slogan; // 口号
@TableField(value = "activity_flag")
private Integer activityFlag; // 生效标记,0 无效,1 有效
@TableField(value = "create_by")
private String createBy; // 创建人
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(value = "create_time")
private Date createTime; // 创建时间
@TableField(value = "update_by")
private String updateBy; // 更新人
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(value = "update_time")
private Date updateTime; // 更新时间
public TourismSlogansInfo(String provinceCode, String provinceName, Integer startYear, Integer endYear, String slogan, Integer activityFlag, String createBy, Date createTime, String updateBy, Date updateTime) {
super();
this.provinceCode = provinceCode;
this.provinceName = provinceName;
this.startYear = startYear;
this.endYear = endYear;
this.slogan = slogan;
this.activityFlag = activityFlag;
this.createBy = createBy;
this.createTime = createTime;
this.updateBy = updateBy;
this.updateTime = updateTime;
}
}
在 Mapper 接口中,为了方便在业务层对口号进行唯一性校验,这里增加一个根据口号和省份 code 的重复性查询接口,方法如下:
package com.yelang.project.extend.scenicspot.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yelang.project.extend.scenicspot.domain.TourismSlogansInfo;
import com.yelang.project.extend.scenicspot.domain.TourismSlogansInfoVO;
public interface TourismSlogansInfoMapper extends BaseMapper<TourismSlogansInfo> {
static final String FIND_SLOGANS_BYPROVINCECODE_SQL = "<script>" +
"select * from biz_tourism_slogans_info where province_code = #{provinceCode} and slogan = #{slogan} limit 1 " +
"</script>";
@Select(FIND_SLOGANS_BYPROVINCECODE_SQL)
TourismSlogansInfo findSlogansByProvinceCode(@Param("provinceCode") String provinceCode, @Param("slogan") String slogans);
}
2、业务层实现
业务层的方法实现比较简单,常规的增加、修改、删除操作跟其他的类操作一致,这里给出业务层的接口定义:
package com.yelang.project.extend.scenicspot.service;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yelang.project.extend.scenicspot.domain.TourismSlogansInfo;
import com.yelang.project.extend.scenicspot.domain.TourismSlogansInfoVO;
public interface ITourismSlogansInfoService extends IService<TourismSlogansInfo> {
public List<TourismSlogansInfo> selectList(TourismSlogansInfo tourismSlogansInfo);
public int insertEntity(TourismSlogansInfo tourismSlogansInfo);
public int updateEntity(TourismSlogansInfo tourismSlogansInfo);
/**
* - 校验省份 code 和口号是否唯一
* @param tourismSlogansInfo 省份口号信息
* @return 结果
*/
public String checkSlogansProvinceName(TourismSlogansInfo tourismSlogansInfo);
public int deleteByIds(String ids);
List<TourismSlogansInfoVO> findProvinceTourismSlogans();
}
在业务实现类中,对于省级宣传口号的唯一性判断实现逻辑如下:
@Override
public String checkSlogansProvinceName(TourismSlogansInfo tourismSlogansInfo) {
Long pkId = null == tourismSlogansInfo.getPkId() ? -1L : tourismSlogansInfo.getPkId();
TourismSlogansInfo dbSlogans = this.baseMapper.findSlogansByProvinceCode(tourismSlogansInfo.getProvinceCode(), tourismSlogansInfo.getSlogan());
if (com.yelang.common.utils.StringUtils.isNotNull(dbSlogans) && dbSlogans.getPkId().longValue() != pkId.longValue()) {
return UserConstants.DEFAULT_COMMON_NOT_UNIQUE;
}
return UserConstants.DEFAULT_COMMON_UNIQUE;
}
3、控制层实现
控制层实现比较简单,这里不一一列举,需要说明的是,为了保证前后端都对省份口号进行重复校验,我们不仅在前端会调用 ajax 来进行宣传口号的唯一性确认,在新增和编辑的接口中也增加相应的验证逻辑,对数据进行控制。核心代码如下所示:
@RequiresPermissions("sspot:tourismslogans:add")
@Log(title = "旅游口号管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(TourismSlogansInfo tourismSlogansInfo) {
if (UserConstants.DEFAULT_COMMON_NOT_UNIQUE.equals(tourismslogansService.checkSlogansProvinceName(tourismSlogansInfo))) {
return error("新增旅游口号'" + tourismSlogansInfo.getSlogan() + "'失败,口号已存在");
}
return toAjax(tourismslogansService.insertEntity(tourismSlogansInfo));
}
/**
* - 校验旅游口号
*/
@PostMapping("/checkSlogansUnique")
@ResponseBody
public String checkSlogansUnique(TourismSlogansInfo tourismSlogansInfo) {
return tourismslogansService.checkSlogansProvinceName(tourismSlogansInfo);
}
@RequiresPermissions("sspot:tourismslogans:edit")
@Log(title = "旅游口号管理", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
@ResponseBody
public AjaxResult editSave(TourismSlogansInfo tourismSlogansInfo) {
if (UserConstants.DEFAULT_COMMON_NOT_UNIQUE.equals(tourismslogansService.checkSlogansProvinceName(tourismSlogansInfo))) {
return error("修改旅游口号'" + tourismSlogansInfo.getSlogan() + "'失败,口号已存在");
}
return toAjax(tourismslogansService.updateEntity(tourismSlogansInfo));
}
经过以上的代码就实现了 SpringBoot 的后台程序实现,接下来对前端界面的布局和要素设置进行简单介绍。
三、前端界面及成果展示
本节将重点讲解前端页面的实现以及对列表界面和新增、编辑页面的成果示意介绍。
1、前端界面实现
系统前端界面使用原生 html 页面来展示,整体框架采用 Thymeleaf 模板技术进行渲染。与其他的信息新增或者编辑窗口不一样的是,旅游口号信息页面中需要对省份的旅游口号进行重复性检测,并且在页面中会有省份的信息选择,因此有两个地方需要注意的,一个是下拉框的设置,第二个是需要调用后台的 ajax 进行校验。下拉框的设置代码如下:
<div>
<label>省份信息:</label>
<div>
<input type="hidden" name="provinceName" />
<select name="provinceCode">
<option th:each="province : ${provinceList}" th:text="${province['name']}" th:value="${province['code']}"></option>
</select>
</div>
</div>
调用 ajax 请求进行验证的方法如下:
$("#form-tourismslogans-add").validate({
onkeyup: false,
rules: {
slogan: {
remote: {
url: prefix + "/checkSlogansUnique",
type: "post",
dataType: "json",
data: {
"slogan": function() { return $.common.trim($("#slogan").val()); },
"provinceCode": function() { return $("#provinceCode :selected").val(); }
},
dataFilter: function(data, type) { return $.validate.unique(data); }
}
}
},
messages: {
"slogan": {
remote: "旅游口号已经存在"
}
},
focusCleanup: true
});
2、旅游口号列表界面
下面来看一下完整的旅游口号列表管理界面,系统支持按照省份名称和口号信息来进行模糊查询。同时可以隐藏或者展示查询条件。


3、旅游口号新增、编辑

口号重复提示
四、总结
以上就是本文的主要内容,本实践旨在探索 SpringBoot 在省级旅游口号管理中的应用,通过构建一个基于 SpringBoot 的省级旅游口号存储及检索系统,实现对旅游口号的高效存储、精准检索和便捷管理。通过本实践,期望为旅游行业的信息化建设提供一个可借鉴的案例。文章详细介绍了如何进行数据库存储的设计,SpringBoot 的后端实现以及面向 Thymeleaf 的前端页面实现,最后提供了列表及新增、编辑页面示例。


