守望博客是支持创建专栏的功能,即可以将一系列相关的文章归档到专栏中,方便用户管理和查阅文章。这里主要讲解专栏的创建、修改和删除功能,至于专栏还涉及其它的功能,例如关注专栏等后续会穿插着介绍。

1、创建专栏

接收和处理专栏相关功能的操作的方法会放在GroupController类中,首先创建专栏的页面为:

这里有两个地方需要特别说明:

第一这个分类数据,此处分类数据正是本博客网站的分类数据,这个分类数据是系统初始化时加入的,这个初始化的功能后续会加入,目前此处就是提前将数据先写入到数据库中,例如本博客作为技术类博客,则分类有:前端、架构、区块链、云计算等。

然后这个分类信息数据一般初始化后,就不会有修改,则这个数据是放入缓存中,即,CategoryCache:

/**
* 缓存分类信息
* 分类信息放到系统永久缓存中,存放形式为:"_CATEGORY" + categoryId为key,value为分类信息对象
*
* @author lzj
* @since 1.0
* @date [2019-07-22]
*/
@Slf4j
@DependsOn("categoryService")
@Component("categoryCache")
public class CategoryCache implements ICache<List<Category>> { /**
* 注入基于Spring提供的Cache接口实例,默认由Ehcache实现
* TODO 以后也可以是Redis、Memcached提供实现
*/
@Autowired
private CacheManager cacheManager; @Autowired
private ICategoryService categoryService; /**
* 缓存实例
*/
private Cache cache; /**
* key的前缀
*/
private String keyPrefix = "_CATEGORY"; /**
* 分类信息根节点ID
*/
public static final String ROOT_CATEGORY_ID = "0"; @PostConstruct
public void init() {
// 获取系统永久缓存实例
cache = cacheManager.getCache(Const.CACHE_SYSTEM_ETERNAL);
log.info("获取系统永久缓存实例"); log.debug("开始加载父分类信息");
List<Category> categorys = categoryService.getByParentId(ROOT_CATEGORY_ID);
if (categorys != null && !categorys.isEmpty()) {
put(keyPrefix + ROOT_CATEGORY_ID, categorys);
}
log.debug("加载完毕父分类信息");
} @Override
public List<Category> get(Object key) {
Cache.ValueWrapper valueWrapper = cache.get(keyPrefix + key);
if (valueWrapper == null) {
// 从数据库重新加载一次
List<Category> categorys = categoryService.getByParentId((String) key);
if (categorys == null) {
return null;
} // 再次放到缓存中
put(key, categorys); return categorys;
}
return (List<Category>) valueWrapper.get();
} @Override
public void put(Object key, List<Category> value) {
cache.put(keyPrefix + key, value);
} @Override
public void remove(Object key) {
cache.evict(keyPrefix + key);
}
}

第二需要说明的是,此处的上传控制是用的webuploader,利用webuploader处理的上传文件的话,需要按如下方式初始化:

$(function() {
var _list = $('#fileList');
var ratio = window.devicePixelRatio || 1;
var thumbnailWidth = 100 * ratio;
var thumbnailHeight = 100 * ratio; // 初始化Web Uploader
var uploader = WebUploader.create({ // 选完文件后,是否自动上传。
auto: true, // swf文件路径
swf: '${rc.contextPath}/static/plugins/webuploader/Uploader.swf', // 文件接收服务端。
server: '${rc.contextPath}/upload', // 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: '#filePicker', fileVal: "_uploadFile", formData: {
_distId:'_distId',
_distType:'_groupLogo',
}, // 只允许选择图片文件。
accept: {
title: 'Images',
extensions: 'gif,jpg,jpeg,png',
mimeTypes: 'image/*'
}, fileNumLimit: 1,
fileSizeLimit: 2 * 1024 * 1024, // 2 M
fileSingleSizeLimit: 2 * 1024 * 1024 // 2 M
}); // 当有文件添加进来的时候
uploader.on( 'fileQueued', function( file ) {
var li = $(
'<div id="' + file.id + '" class="file-item thumbnail text-center">' +
'<img>' +
// '<div class="info">' + file.name + '</div>' +
'</div>'
),
img = li.find('img'); // _list为容器jQuery实例
_list.append( li ); // 创建缩略图
// 如果为非图片文件,可以不用调用此方法。
// thumbnailWidth x thumbnailHeight 为 100 x 100
uploader.makeThumb( file, function( error, src ) {
if ( error ) {
img.replaceWith('<span>不能预览</span>');
return;
} img.attr( 'src', src );
}, thumbnailWidth, thumbnailHeight );
}); // 文件上传过程中创建进度条实时显示。
uploader.on( 'uploadProgress', function( file, percentage ) {
}); // 文件上传成功,给item添加成功class, 用样式标记上传成功。
uploader.on( 'uploadSuccess', function(file, response) {
$( '#'+file.id ).addClass('upload-state-done');
$( '#'+file.id ).append('<a class="del" href="javascript:void(0);">删除</a>' )
$("#logo").val(response.url);
}); // 文件上传失败,显示上传出错。
uploader.on( 'uploadError', function( file ) {
var li = $( '#'+file.id ),
error = li.find('div.error'); // 避免重复创建
if ( !error.length ) {
error = $('<div class="error"></div>').appendTo( li );
} error.text('上传失败');
}); // 完成上传完了,成功或者失败,先删除进度条。
uploader.on( 'uploadComplete', function( file ) {
}); // 执行删除方法
_list.on('click', '.del', function () {
var Id = $(this).parent().attr('id');
//删除该图片
uploader.removeFile(uploader.getFile(Id, true));
$(this).parent().remove();
$("#logo").val("");
});
});

这里后台需要处理专栏的Logo图片的信息,根据上一章节的方式处理,即有UploadGroupLogoHandler类,如:

/**
* 上传专栏Logo处理类
*
* @author lzj
* @since 1.0
* @date [2019-07-23]
*/
@Slf4j
@Component("_groupLogo")
public class UploadGroupLogoHandler implements IUploadHandler { @Resource(name = "configCache")
private ICache<Config> configCache; @Override
public Object upload(MultipartFile file, String distType, String userId) throws Exception {
Map<String, Object> result = new HashMap<String, Object>();
try {
// 获取图片的原始名称
String originalName = file.getOriginalFilename(); // 判断图片的类型
if (!(originalName.endsWith(".jpg") || originalName.endsWith(".JPG") || originalName.endsWith(".png") || originalName.endsWith(".PNG") || originalName.endsWith(".gif") || originalName.endsWith(".GIF") || originalName.endsWith(".jpeg") || originalName.endsWith(".JPEG"))) {
throw new TipException("您上传的图片类型有误,请上传格式为jpg、png或gif");
} // 获取图片的大小
long fileSize = file.getSize(); // 图片大小不能超过2M, 2M = 2 * 1024 * 1024B = 2097152B
if (fileSize > 2097152L) {
throw new TipException("您上传的图片超过2M");
} Config config = configCache.get(Config.CONFIG_IMG_GROUP_LOGO_PATH);
// 保存头像的根目录
String basePath = config.getConfigValue();
if (!(basePath.endsWith("/") || basePath.endsWith("\\"))) {
basePath += "/";
} // 根据当前时间构建yyyyMM的文件夹,建立到月的文件夹
String dateDirName = DateUtil.date2Str(new Date(), DateUtil.YEAR_MONTH_FORMAT);
basePath += dateDirName; File imageDir = new File(basePath);
if (!imageDir.exists()) {
imageDir.mkdirs();
} String fileNewName = IdGenarator.guid() + originalName.substring(originalName.lastIndexOf("."));
FileUtil.copy(file.getInputStream(), new FileOutputStream(new File(imageDir, fileNewName))); result.put("url", dateDirName + "/" + fileNewName);
result.put("msg", "上传成功");
} catch (TipException e) {
result.put("url", "");
result.put("msg", e.getMessage());
} catch (Exception e) {
log.error("上传失败", e);
result.put("url", "");
result.put("msg", "上传失败");
}
return result;
} @Override
public void download(String fileId, HttpServletResponse response) throws Exception {
} @Override
public Object list(String distType, String userId) throws Exception {
return null;
}
}

最后创建专栏的核心代码如下:

/**
* 创建专栏
*
* @param request
* @param session
* @return
*/
@RequestMapping(value = "/user/group/add", method = RequestMethod.POST)
@ResponseBody
public Result add(HttpServletRequest request, HttpSession session) {
Result result = new Result();
try {
// 接收参数
String categoryId = request.getParameter("categoryId");
String name = request.getParameter("name");
String logo = request.getParameter("logo");
String introduce = request.getParameter("introduce"); // 校验参数
if (StringUtils.isEmpty(categoryId) || StringUtils.isEmpty(name) || StringUtils.isEmpty(logo) || StringUtils.isEmpty(introduce)) {
throw new TipException("缺少必要参数");
} // 获取登录信息
User tempUser = (User) session.getAttribute(Const.SESSION_USER);
String userId = tempUser.getUserId(); // 构建专栏对象
Group group = new Group();
group.setGroupId(IdGenarator.longIdStr());
group.setName(name);
group.setLogo(logo);
group.setIntroduce(introduce);
group.setCategoryId(categoryId);
group.setCreator(userId);
group.setCreateTime(new Date());
// 从系统配置项获取专栏是否审核
Config config = configCache.get(Config.CONFIG_GROUP_AUDIT);
if (config != null && "1".equals(config.getConfigValue())) {
// 需要审核
group.setStatus(Group.STATUS_NO);
} else {
// 不需要审核
group.setStatus(Group.STATUS_SUCCESS);
} // 保存专栏信息
boolean flag = groupService.save(group);
if (!flag) {
throw new TipException("创建专栏失败");
} result.setCode(Result.CODE_SUCCESS);
result.setMsg("成功创建专栏");
} catch (TipException e) {
result.setCode(Result.CODE_EXCEPTION);
result.setMsg(e.getMessage());
} catch (Exception e) {
log.error("创建专栏失败", e);
result.setCode(Result.CODE_EXCEPTION);
result.setMsg("创建专栏失败");
}
return result;
}

2、修改专栏

有了前面新增专栏的基础,其实修改专栏的功能就相对简单很多了,此处只列出处理修改的核心代码即可,如:

/**
* 修改专栏
*
* @param groupId
* @param request
* @param session
* @return
*/
@RequestMapping(value = "/user/group/edit/{groupId}", method = RequestMethod.POST)
@ResponseBody
public Result edit(@PathVariable("groupId") String groupId, HttpServletRequest request, HttpSession session) {
Result result = new Result();
try {
// 根据id获取专栏信息
Group group = groupService.getById(groupId);
if (group == null || StringUtils.isEmpty(group.getGroupId())) {
log.error("groupId: " + groupId + ": 该专栏不存在");
throw new TipException("该专栏不存在");
} // 获取用户信息
User tempUser = (User) session.getAttribute(Const.SESSION_USER);
String userId = tempUser.getUserId();
if (!userId.equals(group.getCreator())) {
log.error("userId: " + userId + "修改别人的groupId: " + groupId);
throw new TipException("不能修改别人的专栏");
} // 接收参数
String categoryId = request.getParameter("categoryId");
String name = request.getParameter("name");
String introduce = request.getParameter("introduce");
String logo = request.getParameter("logo"); // 校验参数
if (StringUtils.isEmpty(categoryId) || StringUtils.isEmpty(name) || StringUtils.isEmpty(introduce)) {
throw new TipException("缺少必要参数");
} group.setCategoryId(categoryId);
group.setName(name);
group.setIntroduce(introduce);
if (!StringUtils.isEmpty(logo)) {
group.setLogo(logo);
}
group.setUpdateTime(new Date()); // 修改
boolean flag = groupService.updateById(group);
if (!flag) {
throw new TipException("修改专栏失败");
} result.setCode(Result.CODE_SUCCESS);
result.setMsg("修改专栏成功");
} catch (TipException e) {
result.setCode(Result.CODE_EXCEPTION);
result.setMsg(e.getMessage());
} catch (Exception e) {
log.error("修改专栏失败", e);
result.setCode(Result.CODE_EXCEPTION);
result.setMsg("修改专栏失败"); }
return result;
}

3、删除专栏

删除专栏功能是很简单的,但是需要考虑到,删除专栏后,需要处理其它与之关联的数据信息,此处由于其它模块还没有完成,所以先将加一个TODO,后续会再处理。那删除专栏的核心代码如下:

/**
* 根据ID删除专栏
*
* @param groupId
* @param session
* @return
*/
@RequestMapping(value = "/user/group/delete/{groupId}", method = RequestMethod.GET)
@ResponseBody
public Result delete(@PathVariable("groupId") String groupId, HttpSession session) {
Result result = new Result();
try {
// 根据id获取专栏信息
Group group = groupService.getById(groupId);
if (group == null || StringUtils.isEmpty(group.getGroupId())) {
log.error("groupId: " + groupId + ": 该专栏不存在");
throw new TipException("该专栏不存在");
} // 获取用户信息
User tempUser = (User) session.getAttribute(Const.SESSION_USER);
String userId = tempUser.getUserId();
if (!userId.equals(group.getCreator())) {
log.error("userId: " + userId + "删除别人的groupId: " + groupId);
throw new TipException("不能删除别人的专栏");
} // 删除
boolean flag = groupService.removeById(groupId);
if (!flag) {
throw new TipException("删除专栏失败");
} // TODO 删除专栏后,需要处理其它关联的数据,由于其它模块还没有,此处后续处理 result.setCode(Result.CODE_SUCCESS);
result.setMsg("删除专栏成功");
} catch (TipException e) {
result.setCode(Result.CODE_EXCEPTION);
result.setMsg(e.getMessage());
} catch (Exception e) {
log.error("删除专栏失败", e);
result.setCode(Result.CODE_EXCEPTION);
result.setMsg("删除专栏失败");
}
return result;
}

关注我

以你最方便的方式关注我:

微信公众号:

基于SpringBoot从零构建博客网站 - 新增创建、修改、删除专栏功能的更多相关文章

  1. 基于SpringBoot从零构建博客网站 - 技术选型和整合开发环境

    技术选型和整合开发环境 1.技术选型 博客网站是基于SpringBoot整合其它模块而开发的,那么每个模块选择的技术如下: SpringBoot版本选择目前较新的2.1.1.RELEASE版本 持久化 ...

  2. 基于SpringBoot从零构建博客网站 - 确定需求和表结构

    要确定一个系统的需求,首先需要明确该系统的用户有哪些,然后针对每一类用户,确定其需求.对于博客网站来说,用户有3大类,分别是: 作者,也即是注册用户 游客,也即非注册用户 管理员,网站维护人员 那么从 ...

  3. 基于SpringBoot从零构建博客网站 - 设计可扩展上传模块和开发修改头像密码功能

    上传模块在web开发中是很常见的功能也是很重要的功能,在web应用中需要上传的可以是图片.pdf.压缩包等其它类型的文件,同时对于图片可能需要回显,对于其它文件要能够支持下载等.在守望博客系统中对于上 ...

  4. 基于SpringBoot从零构建博客网站 - 集成editor.md开发发布文章功能

    发布文章功能里面最重要的就是需要集成富文本编辑器,目前富文本编辑器有很多,例如ueditor,CKEditor.editor.md等.这里守望博客里面是集成的editor.md,因为editor.md ...

  5. 基于SpringBoot从零构建博客网站 - 开发设置主页标识和修改个人信息功能

    由于守望博客系统中支持由用户自己设置个人主页的URL的后半段,所以必须要用户设置该标识的功能,而且是用户注册登录之后自动弹出的页面,如果用户没有设置该标识,其它的操作是不能够操作的,同时要求主页标识只 ...

  6. 基于SpringBoot从零构建博客网站 - 开发文章详情页面

    文章详情页面是博客系统中最为重要的页面,登录用户与游客都可以浏览文章详情页面,只不过只有登录用户才能进行其它的一些操作,比如评论.点赞和收藏等等. 本次的开发任务只是将文章详情页面展示出来,至于一些收 ...

  7. 基于SpringBoot从零构建博客网站 - 整合ehcache和开发注册登录功能

    对于程序中一些字典信息.配置信息应该在程序启动时加载到缓存中,用时先到缓存中取,如果没有命中,再到数据库中获取同时放到缓存中,这样做可以减轻数据库层的压力.目前暂时先整合ehcache缓存,同时预留了 ...

  8. 基于SpringBoot从零构建博客网站 - 整合lombok和mybatis-plus提高开发效率

    在上一章节中<技术选型和整合开发环境>,确定了开发的技术,但是如果直接这样用的话,可能开发效率会不高,为了提高开发的效率,这里再整合lombok和mybatis-plus两个组件. 1.l ...

  9. 基于SpringBoot从零构建博客网站 - 分页显示文章列表功能

    显示文章列表一般都是采用分页显示,比如每页10篇文章显示.这样就不用每次就将所有的文章查询出来,而且当文章数量特别多的时候,如果一次性查询出来很容易出现OOM异常. 后台的分页插件采用的是mybati ...

随机推荐

  1. lock和synchronized如何选择?

    1.lock是一个接口,而synchronized是java关键字,synchronized是内置的语言实现. 2.synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁,而l ...

  2. Mybatis_two

    SqlMapConfig.xml配置文件 SqlMapConfig.xml中配置的内容和顺序如下: properties(属性) settings(全局配置参数) typeAliases(类型别名) ...

  3. prometheus-operator监控Kubernetes

    Operator Operator是由CoreOS公司开发的,用来扩展 Kubernetes API,特定的应用程序控制器,它用来创建.配置和管理复杂的有状态应用,如数据库.缓存和监控系统.Opera ...

  4. 【IDE】idea在debug模式启动非常慢,debug模式一直在启动中状态

    现象:一直处于启动中状态,日志刷的很慢,非debug模式正常启动: 最终解决方式:下图按钮,取消所有打过的断点,问题解决

  5. ajax入门级

    AJAX AJAX:即异步的JavaScript 和 XML,是一种用于创建快速动态网页的技术: 传统的网页(不使用AJAX)如果需要更新内容,必需重载整个网页面: 使用AJAX则不与要加载更新整个网 ...

  6. Docker镜像和容器管理(二)

    Docker安装 Docker镜像管理 https://hub.docker.com/ 是公共的一个Docker镜像仓库,类似GitHub一样,上面有非常多的开源项目镜像. 可以直接在命令行搜索镜像 ...

  7. intel FPGA CLKn pin 是否能直接进PLL?

    原创 by DeeZeng FPGA的时钟需要从专用的时钟管脚输入,那CLKn 作为Single-End时钟pin时是否能直接进 PLL呢? 通过查看对应FPGA型号的手册,得出以下结论 1. Cyc ...

  8. 使用OpenAPI构建更智能的API

    像OpenAPI这样的API描述规范是一个关键工具,您应该尽可能地将其好好掌握,记录和执行API的工作由计算机和开发人员完成:OpenAPI 3.0现在允许额外的表现力,可以让机器为我们做更多有用的工 ...

  9. Discuz ML! V3.X 代码注入漏洞

    Discuz ML! V3.X 代码注入漏洞 前言 Discuz!ML是一个由CodersClub.org创建的多语言,集成,功能齐全的开源网络平台,用于构建像"社交网络"这样的互 ...

  10. web文件下载(附方案及源码配置)

    1. 场景描述 因项目需查询数据量比较大(需要查询Hbase等nosql数据库),采用用户点击查询后,后台查询并生成查询文件:然后消息通知用户后,用户点击下载的方式来满足用户需求. 2. 解决方案 W ...