5.CMS内容管理系统
[taotao]-CMS内容管理系统
1.CMS
CMS:Content Management System"的缩写,意为"内容管理系统",此处主要是针对首页内容的信息管理,包括但不限于内容的多级分组、广告项目、商品基本信息(标题、价格、链接、内容)等信息
此处主要实现首页的轮播图展示、内容分类及信息管理,构建参考内容如下所示
其核心构建表由tb_content_category:内容分类表、tb_content:内容信息表
2.内容分类及信息管理
【1】内容分类管理
🔖内容分类列表展示
需求分析
初始化easyUI的tree控件url:content/category/list
请求的参数:id,父节点id
返回结果:json数据(EasyUITreeNode列表),是一个列表,列表中每个元素包含三个属性:id、text、state
dao层
从tb_content_category表中取数据,根据parentid查询分类列表。可以使用逆向工程生成的代码
service层
接收一个parentId,根据parentID查询节点列表。创建一个EasyUITreeNode列表。返回。
参数:Long parentId
返回值:List<EasyUITreeNode>
public interface ContentCatgoryService {
public List<EasyUITreeNode> getContentCatList(Long parentId) ;
}
// ServiceImpl:
@Service
public class ContentCatgoryServiceImpl implements ContentCatgoryService {
@Autowired
private TbContentCategoryMapper contentCategoryMapper;
@Override
public List<EasyUITreeNode> getContentCatList(Long parentId) {
//根据parentId查询子节点列表
TbContentCategoryExample example = new TbContentCategoryExample();
Criteria criteria = example.createCriteria();
criteria.andParentIdEqualTo(parentId);
//执行查询
List<TbContentCategory> list = contentCategoryMapper.selectByExample(example);
//转换成EasyUITreeNode列表
List<EasyUITreeNode> resultList = new ArrayList<>();
for (TbContentCategory tbContentCategory : list) {
//创建一EasyUITreeNode节点
EasyUITreeNode node = new EasyUITreeNode();
node.setId(tbContentCategory.getId());
node.setText(tbContentCategory.getName());
node.setState(tbContentCategory.getIsParent()?"closed":"open");
//添加到列表
resultList.add(node);
}
return resultList;
}
}
controller层
接收parentId调用Service查询节点列表,返回节点列表。返回json数据,需要使用@ResponseBody
@Controller
@RequestMapping("/content/category")
public class ContentCategoryController {
@Autowired
private ContentCatgoryService contentCatgoryService;
@RequestMapping("/list")
@ResponseBody
public List<EasyUITreeNode> getContentCatList(@RequestParam(value="id", defaultValue="0")Long parentId) {
List<EasyUITreeNode> list = contentCatgoryService.getContentCatList(parentId);
return list;
}
}
测试:启动taotao-manager测试
🔖新增节点
需求分析
请求的url:/content/category/create
请求的参数:parentId、name
返回结果:返回TaotaoResult,包含新添加记录的id
dao层
插入内容分类数据可以使用逆向工程实现
service层
接收两个参数:parentId、name
创建一个对应tb_content_category表的pojo对象,并补全pojo对象的属性
插入数据(数据处理成功返回主键),并使用TaotaoResult包装id,返回响应结果
@Override
public TaotaoResult insertCatgory(Long parentId, String name) {
//创建一个pojo对象
TbContentCategory contentCategory = new TbContentCategory();
contentCategory.setName(name);
contentCategory.setParentId(parentId);
//1(正常),2(删除)
contentCategory.setStatus(1);
contentCategory.setIsParent(false);
//'排列序号,表示同级类目的展现次序,如数值相等则按名称次序排列。取值范围:大于零的整数'
contentCategory.setSortOrder(1);
contentCategory.setCreated(new Date());
contentCategory.setUpdated(new Date());
//插入数据
contentCategoryMapper.insert(contentCategory);
//取返回的主键
Long id = contentCategory.getId();
//判断父节点的isparent属性
//查询父节点
TbContentCategory parentNode = contentCategoryMapper.selectByPrimaryKey(parentId);
if (!parentNode.getIsParent()) {
parentNode.setIsParent(true);
//更新父节点
contentCategoryMapper.updateByPrimaryKey(parentNode);
}
//返回主键
return TaotaoResult.ok(id);
}
controller层
接收两个参数,parentId、name。调用Service插入数据,返回TaotaoResult。返回json格式的数据
@RequestMapping("/create")
@ResponseBody
public TaotaoResult createNode(Long parentId, String name) {
TaotaoResult result = contentCatgoryService.insertCatgory(parentId, name);
return result;
}
测试:可能存在的问题分析
问题1:如果连续插入两条数据,发现第二次插入数据的时候焦点却在第一次插入的数据处,从而导致数据无法正常插入,因此此处考虑可能是重复id导致,查看相应的jsp文件,修改如下:(此外要考虑新增分类的归属问题,看其是否存在父节点)
🔖重命名节点、删除节点
dao层
Dao层采用逆向工程生成的mapper文件
service层
// 重命名内容分类(修改)
public TaotaoResult updateCatgory(Long id, String name);
// 根据id删除内容分类(考虑删除的是父节点还是子节点,根据不同的情况处理)
public TaotaoResult deleteCatgory(Long id);
ServiceImpl:
@Service
public class ContentCatgoryServiceImpl implements ContentCatgoryService {
@Autowired
private TbContentCategoryMapper contentCategoryMapper;
@Override
public List<EasyUITreeNode> getContentCatList(Long parentId) {
// 根据parentId查询子节点列表
TbContentCategoryExample example = new TbContentCategoryExample();
Criteria criteria = example.createCriteria();
criteria.andParentIdEqualTo(parentId);
// 执行查询
List<TbContentCategory> list = contentCategoryMapper.selectByExample(example);
// 转换成EasyUITreeNode列表
List<EasyUITreeNode> resultList = new ArrayList<>();
for (TbContentCategory tbContentCategory : list) {
// 创建一EasyUITreeNode节点
EasyUITreeNode node = new EasyUITreeNode();
node.setId(tbContentCategory.getId());
node.setText(tbContentCategory.getName());
node.setState(tbContentCategory.getIsParent() ? "closed" : "open");
// 添加到列表
resultList.add(node);
}
return resultList;
}
@Override
public TaotaoResult insertCatgory(Long parentId, String name) {
// 创建一个pojo对象
TbContentCategory contentCategory = new TbContentCategory();
contentCategory.setName(name);
contentCategory.setParentId(parentId);
// 1(正常),2(删除)
contentCategory.setStatus(1);
contentCategory.setIsParent(false);
// '排列序号,表示同级类目的展现次序,如数值相等则按名称次序排列。取值范围:大于零的整数'
contentCategory.setSortOrder(1);
contentCategory.setCreated(new Date());
contentCategory.setUpdated(new Date());
// 插入数据
contentCategoryMapper.insert(contentCategory);
// 取返回的主键
Long id = contentCategory.getId();
// 判断父节点的isparent属性
// 查询父节点
TbContentCategory parentNode = contentCategoryMapper.selectByPrimaryKey(parentId);
if (!parentNode.getIsParent()) {
parentNode.setIsParent(true);
// 更新父节点
contentCategoryMapper.updateByPrimaryKey(parentNode);
}
// 返回主键
return TaotaoResult.ok(id);
}
@Override
public TaotaoResult updateCatgory(Long id, String name) {
// 通过id查询节点对象
TbContentCategory contentCategory = contentCategoryMapper.selectByPrimaryKey(id);
// 判断新的name值与原来的值是否相同,如果相同则不用更新
if (name != null && name.equals(contentCategory.getName())) {
return TaotaoResult.ok();
}
contentCategory.setName(name);
// 设置更新时间
contentCategory.setUpdated(new Date());
// 更新数据库
contentCategoryMapper.updateByPrimaryKey(contentCategory);
// 返回结果
return TaotaoResult.ok();
}
@Override
public TaotaoResult deleteCatgory(Long id) {
// 首先要判断当前删除的节点是叶子节点还是父节点,要依次递归删除
// 删除分类,就是改节点的状态为2(为了避免数据冗余,选择硬删除数据)
TbContentCategory contentCategory = contentCategoryMapper.selectByPrimaryKey(id);
// 硬删除数据
contentCategoryMapper.deleteByPrimaryKey(id);
/* 状态:可选值:1(正常),2(删除),软删除数据
contentCategory.setStatus(2);
contentCategoryMapper.updateByPrimaryKey(contentCategory);*/
// 需要判断一下要删除的这个节点是否是父节点,如果是父节点,那么就级联
// 删除这个父节点下的所有子节点(采用递归的方式删除)
if (contentCategory.getIsParent()) {
deleteNode(contentCategory.getId());
}
// 需要判断父节点当前还有没有子节点,如果有子节点就不用做修改
// 如果父节点没有子节点了,那么要修改父节点的isParent属性为false即变为叶子节点
TbContentCategory parent = contentCategoryMapper.selectByPrimaryKey(contentCategory.getParentId());
List<TbContentCategory> list = getContentCategoryListByParentId(parent.getId());
/**
* 判断父节点是否有子节点是判断这个父节点下的所有子节点的状态,
* 如果是软删除,则依次判断每个子节点的状态,如果状态都是2就说明
* 所有子节点都被“删除”,则需要修改当前节点的状态
* 如果是硬删除,则只需要判断当前子节点列表是否为空即可
*/
/* boolean flag = false;
for (TbContentCategory tbContentCategory : list) {
if (tbContentCategory.getStatus() == 0) {
flag = true;
break;
}
}
// 如果没有子节点了
if (!flag) {
parent.setIsParent(false);
contentCategoryMapper.updateByPrimaryKey(parent);
}*/
// 此处硬删除判断是否为父节点,直接判断这个父节点下是否有子节点
if(CollectionUtils.isEmpty(list)) {
// 子节点列表为空,说明没有子节点,则需要修改当前节点状态
parent.setIsParent(false);
contentCategoryMapper.updateByPrimaryKey(parent);
}
// 返回结果
return TaotaoResult.ok();
}
// 通过父节点id来查询所有子节点,这是抽离出来的公共方法
private List<TbContentCategory> getContentCategoryListByParentId(long parentId) {
TbContentCategoryExample example = new TbContentCategoryExample();
Criteria criteria = example.createCriteria();
criteria.andParentIdEqualTo(parentId);
List<TbContentCategory> list = contentCategoryMapper.selectByExample(example);
return list;
}
// 递归删除节点
private void deleteNode(long parentId) {
List<TbContentCategory> list = getContentCategoryListByParentId(parentId);
for (TbContentCategory contentCategory : list) {
//contentCategory.setStatus(2);
//contentCategoryMapper.updateByPrimaryKey(contentCategory);
// 为了避免冗余数据,此处硬删除数据
contentCategoryMapper.deleteByPrimaryKey(contentCategory.getId());
if (contentCategory.getIsParent()) {
deleteNode(contentCategory.getId());
}
}
}
}
controller层
内容分类删除需要注意的问题:
需要判断删除的节点是否存在子节点,不存在则直接删除,存在则依次递归删除(级联删除)
其次需要判断当前节点删除后会对其父节点有何影响,及时更新其对应父节点的状态
【2】内容信息管理
内容列表
jsp实现
dao层
可以实现逆向工程生成的mapper文件+PageHelper实现
service层
Service层接收分页参数,分别为page(第几页)、rows(多少行数据)。随后通过调用dao查询商品列表,结合pagehelper实现分页,返回对应的商品列表。
通过创建一个pojo对象,使其返回一个EasyUIDateGrid支持的数据格式。此pojo应该放到taotao-common工程(通用,使其可以被多个项目引用)中。创建完成要在taotao-common工程中执行mvn clean install,及时更新maven仓库。(此处省略)
Service层代码实现:
@Service
public class ContentServiceImpl implements ContentService {
@Autowired
private TbContentMapper contentMapper;
@Override
public EUDataGridResult getContentList(Integer page, Integer rows,Long categoryId) {
// 查询Content列表
TbContentExample example = new TbContentExample();
Criteria criteria = example.createCriteria();
criteria.andCategoryIdEqualTo(categoryId);
// 分页处理
PageHelper.startPage(page, rows);
// List<TbContent> list = contentMapper.selectByExample(example);
List<TbContent> list = contentMapper.selectByExampleWithBLOBs(example);
// 创建一个返回值对象
EUDataGridResult result = new EUDataGridResult();
result.setRows(list);
// 取记录总条数
PageInfo<TbContent> pageInfo = new PageInfo<>(list);
result.setTotal(pageInfo.getTotal());
return result;
}
}
controller层
接收页面传递过来的参数page、rows。返回json格式的数据(@ResponseBody EUDataGridResult)
访问路径:/content/list
@RequestMapping("/item/list")
@ResponseBody
public EUDataGridResult getItemList(Integer page, Integer rows) {
EUDataGridResult result = itemService.getItemList(page, rows);
return result;
}
可能存在的问题
测试过程中遇到的问题,Controller层与相应的jsp文件不匹配?实质上页面能够访问到controller层,但其跳转访问页面显示是找不到对应的映射页面,导致报404出错。
在测试的时候Controller没有加@ResponseBody注解,导致其默认解析为视图,因此跳转到指定页面无法访问,报404错误。通过F12查看相关的参数,返回的数据类型是“text/html”,在controller层添加@ResponseBody注解进行测试
新增内容
需求分析
请求url:/content-add
表单提交处理:content-add.jsp
请求的url:/content/save
提交的内容:表单中的内容(使用TbContent接收)
返回值:TaotaoResult
dao层
tb_content可以使用逆向工程生成代码
service层
接收一个pojo对象,补全属性。插入数据。返回TaotaoResult。
参数:TbContent
返回值:TaotaoResult
@Service
public class ContentServiceImpl implements ContentService {
@Autowired
private TbContentMapper contentMapper;
@Override
public TaotaoResult insertContent(TbContent content) {
content.setCreated(new Date());
content.setUpdated(new Date());
//插入数据
contentMapper.insert(content);
return TaotaoResult.ok();
}
}
controller层
接收表单中的内容,使用TbContent接收。调用Service插入数据。返回TaotaoResult(json数据)
@RequestMapping("/content/save")
@ResponseBody
public TaotaoResult insertContent(TbContent content) {
TaotaoResult result = contentService.insertContent(content);
return result;
}
编辑内容
需求分析
dao层
使用mybatis逆向工程生成
service层
@Override
public TaotaoResult updateContent(TbContent content) {
content.setUpdated(new Date());
// 修改数据
contentMapper.updateByPrimaryKeyWithBLOBs(content);
return TaotaoResult.ok();
}
controller层
@RequestMapping("/content/edit")
@ResponseBody
public TaotaoResult updateContent(TbContent content) {
TaotaoResult result = contentService.updateContent(content);
return result;
}
删除内容
需求分析
dao层
使用mybatis生成的mapper文件
service层
@Override
public TaotaoResult deleteContent(List<Long> contentIds) {
for(Long id : contentIds) {
contentMapper.deleteByPrimaryKey(id);
}
return TaotaoResult.ok();
}
controller层
@RequestMapping("/content/delete")
@ResponseBody
public TaotaoResult deleteContent(@RequestParam(value="ids")List<Long> contentIds) {
TaotaoResult result = contentService.deleteContent(contentIds);
return result;
}
3.轮播图展示
【1】基本概念
如果不使用ajax如何获得数据?
在portal的Service层调用taotao-rest的服务,获得数据,把数据传递给jsp即可
使用HttpClient实现java代码模拟浏览器调用服务请求数据
HttpClient
(1)使用步骤
第一步:把HttpClient使用的jar包添加到工程中
第二步:创建一个HttpClient的测试类
第三步:创建测试方法
第四步:创建一个HttpClient对象
第五步:创建一个HttpGet对象,需要制定一个请求的url
第六步:执行请求
第七步:接收返回结果。HttpEntity对象
第八步:取响应的内容
第九步:关闭HttpGet、HttpClient
(2)Get请求
参考代码
public class HttpClientTest {
@Test
public void testHttpGet() throws Exception {
// 第一步:把HttpClient使用的jar包添加到工程中。
// 第二步:创建一个HttpClient的测试类
// 第三步:创建测试方法。
// 第四步:创建一个HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
// 第五步:创建一个HttpGet对象,需要制定一个请求的url
HttpGet get = new HttpGet("http://www.itheima.com");
// 第六步:执行请求。
CloseableHttpResponse response = httpClient.execute(get);
// 第七步:接收返回结果。HttpEntity对象。
HttpEntity entity = response.getEntity();
// 第八步:取响应的内容。
String html = EntityUtils.toString(entity);
System.out.println(html);
// 第九步:关闭response、HttpClient。
response.close();
httpClient.close();
}
}
连接测试
(3)Post请求
实现步骤
第一步:创建一个httpClient对象
第二步:创建一个HttpPost对象(需要指定一个url)
第三步:创建一个list模拟表单,list中每个元素是一个NameValuePair对象
第四步:需要把表单包装到Entity对象中。StringEntity
第五步:执行请求
第六步:接收返回结果
第七步:关闭流
在taotao-portal中添加controller方法测试
public class HttpClientTest {
@Test
public void testHttpPost() throws Exception {
// 第一步:创建一个httpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
// 第二步:创建一个HttpPost对象。需要指定一个url
HttpPost post = new HttpPost("http://localhost:8082/posttest.html");
// 第三步:创建一个list模拟表单,list中每个元素是一个NameValuePair对象
List<NameValuePair> formList = new ArrayList<>();
formList.add(new BasicNameValuePair("name", "张三"));
formList.add(new BasicNameValuePair("pass", "1243"));
// 第四步:需要把表单包装到Entity对象中。StringEntity
StringEntity entity = new UrlEncodedFormEntity(formList, "utf-8");
post.setEntity(entity);
// 第五步:执行请求。
CloseableHttpResponse response = httpClient.execute(post);
// 第六步:接收返回结果
HttpEntity httpEntity = response.getEntity();
String result = EntityUtils.toString(httpEntity);
System.out.println(result);
// 第七步:关闭流。
response.close();
httpClient.close();
}
}
测试说明
重新启动taotao-portal,如果出现下述问题可能是由于当前编写的类没有编译导致,需要清理代码随后启动测试(taotao-portal执行mvn clean,随后重新启动taotao-portal,若还无法执行则重新保存一下测试文件,再次debug测试)
如果测试过程中出现乱码问题:
(4)restclient-ui:服务器连接测试
restclient-ui
restclient-ui-3.5-jar-with-dependencies.jar说明,也可通过命令行打开
进入到当前jar包指定的目录,随后通过java命令行进行操作
测试说明
Method选择post
Body选择String body
提交表单和提交json数据,content-Type不同
表单的content-type:application/x-www-form-urlencoded
json的content-type:application/json
如果是json格式的数据,需要通过一个对象进行接收,借助@RequestBody注解完成pojo对象转义或者是借助Map接收
实际使用时需要把HttpClient封装成一个工具类,可以把工具类放到taotao-common(公共内容定义)中
【2】轮播图
发布内容查询服务
功能分析
根据内容分类id查询tb_content表,得到此分类下的内容列表,返回。
请求的url:/rest/content/
返回的数据:json数据:包含查询内容列表、包含状态、包含错误消息(TaotaoResult)
dao层
使用mybatis逆向工程
service层
接收内容分类id作为参数,使用mapper根据分类id查询内容列表,返回内容列表。
参数:Long cid
返回结果:
List<TbContent>
@Service
public class ContentServiceImpl implements ContentService {
@Autowired
private TbContentMapper contentMapper;
@Override
public List<TbContent> getContentList(Long cid) {
// 根据cid查询内容列表
TbContentExample example = new TbContentExample();
Criteria criteria = example.createCriteria();
criteria.andCategoryIdEqualTo(cid);
//执行查询
List<TbContent> list = contentMapper.selectByExampleWithBLOBs(example);
return list;
}
}
controller层
接收参数cid,从url中取参数。调用Service查询内容列表,返回json数据,返回TaotaoResult包装内容列表
@Controller
public class ContentController {
@Autowired
private ContentService contentService;
@RequestMapping("/content/{cid}")
@ResponseBody
public TaotaoResult getContentList(@PathVariable Long cid) {
try {
List<TbContent> list = contentService.getContentList(cid);
return TaotaoResult.ok(list);
} catch (Exception e) {
e.printStackTrace();
return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
}
}
}
测试
启动taotao-rest,测试:http://localhost:8081/rest/content/89
展示轮播图
功能分析
在taotao-portal中实现,在service层借助HttpClient调用rest发布的服务,并将把结果传递给jsp
- json格式
- 数据节点
public class AdNode {
private int height;
private int width;
private String src;
private int heightB;
private int widthB;
private String srcB;
private String alt;
private String href;
// 对应getter、setter构造器
}
dao层
taotao-portal不直接与数据库进行交互,而是通过service层调用taotao-rest发布的服务,得到响应的json字符串,随后将json转化为java对象进行处理
service层
使用HttpClient调用taotao-rest发布的服务,得到一个json字符串。随后把json转换成java对象,取data属性,把list转换成AdNode的list。并AdNode的list转换成指定格式的json数据返回
参数:cid(从配置文件中获得)
返回结果:String
@Service
public class ContentServiceImpl implements ContentService {
@Value("${REST_BASE_URL}")
private String REST_BASE_URL;
@Value("${REST_CONTENT_URL}")
private String REST_CONTENT_URL;
@Value("${REST_CONTENT_AD1_CID}")
private String REST_CONTENT_AD1_CID;
/**
* 获得大广告位的内容
* <p>Title: getAd1List</p>
* <p>Description: </p>
* @return
* @see com.taotao.portal.service.ContentService#getAd1List()
*/
@Override
public String getAd1List() {
//调用服务获得数据
// String json ="http://localhost/8081/rest/content/89";
String json = HttpClientUtil.doGet(REST_BASE_URL + REST_CONTENT_URL + REST_CONTENT_AD1_CID);
//把json转换成java对象
TaotaoResult taotaoResult = TaotaoResult.formatToList(json, TbContent.class);
//取data属性,内容列表
List<TbContent> contentList = (List<TbContent>) taotaoResult.getData();
//把内容列表转换成AdNode列表
List<AdNode> resultList = new ArrayList<>();
for (TbContent tbContent : contentList) {
AdNode node = new AdNode();
node.setHeight(240);
node.setWidth(670);
node.setSrc(tbContent.getPic());
node.setHeightB(240);
node.setWidthB(550);
node.setSrcB(tbContent.getPic2());
node.setAlt(tbContent.getSubTitle());
node.setHref(tbContent.getUrl());
resultList.add(node);
}
//需要把resultList转换成json数据
String resultJson = JsonUtils.objectToJson(resultList);
return resultJson;
}
}
controller层
调用Service查询广告位内容,传递给jsp即可
测试
测试,将taotao-common重新打包,启动taotao-rest,随后启动taotao-portal进行测试
可能出现结果:没有找到指定的数据,因此此处需要借助taotao-manager后台管理系统对图片进行设置,可以通过右键查看源代码(检查),显示连接失败,说明图片链接地址有误,此处则通过后台管理系统设置相关图片,再次测试,结果正常显示