- 新增表 t_public_todo 及 Entity/Mapper/Service/Controller 四个类 - 对外接口 7 个:list/add/update/updateStatus/remove/removeDone/stats - Controller 不继承 BaseController,不依赖登录态 - WebSecurityConfig 放行 /public-todo/**,与个人待办 /todo/** 隔离 - 落库脚本 sql/public_todo.sql(幂等建表 + 示例数据)
This commit is contained in:
@@ -84,6 +84,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
// 待办清单:多人数据隔离,必须登录后访问
|
||||
.antMatchers("/todo/**")
|
||||
.authenticated()
|
||||
// 公开待办看板:对外分享,无需登录
|
||||
.antMatchers("/public-todo/**")
|
||||
.permitAll()
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginProcessingUrl("/login")
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
package com.freedom.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.freedom.admin.common.Result;
|
||||
import com.freedom.admin.entity.PublicTodo;
|
||||
import com.freedom.admin.service.PublicTodoService;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 控制层-公开待办看板
|
||||
*
|
||||
* @author zhangjiaxun
|
||||
* @description 对外分享的待办清单,无需登录即可访问,全体访问者共享同一份清单。
|
||||
* 与个人待办 t_todo 完全独立,互不影响。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("public-todo")
|
||||
@Log4j2
|
||||
public class PublicTodoController {
|
||||
|
||||
/**
|
||||
* 操作人标识(公开页面无登录态,统一记录为 guest)
|
||||
*/
|
||||
private static final String OPERATOR = "guest";
|
||||
|
||||
/**
|
||||
* 单次返回上限,防止数据量过大拖慢页面
|
||||
*/
|
||||
private static final int MAX_ROWS = 500;
|
||||
|
||||
@Autowired
|
||||
private PublicTodoService publicTodoService;
|
||||
|
||||
/**
|
||||
* 查询公开看板全部待办
|
||||
*
|
||||
* @param status 状态筛选:0未完成 1已完成,null 为全部
|
||||
* @param priority 优先级筛选:1低 2中 3高
|
||||
* @param category 分类筛选
|
||||
* @param keyword 标题/备注关键字
|
||||
* @param sort 排序:created 最新创建 / priority 优先级 / due 截止日期 / title 标题
|
||||
*/
|
||||
@RequestMapping("list")
|
||||
public Result<List<PublicTodo>> list(Integer status, Integer priority, String category,
|
||||
String keyword, String sort) {
|
||||
try {
|
||||
QueryWrapper<PublicTodo> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("del_flag", 0);
|
||||
queryWrapper.eq(status != null, "status", status);
|
||||
queryWrapper.eq(priority != null, "priority", priority);
|
||||
queryWrapper.eq(StringUtils.isNotBlank(category), "category", category);
|
||||
if (StringUtils.isNotBlank(keyword)) {
|
||||
String kw = keyword.trim();
|
||||
queryWrapper.and(w -> w.like("title", kw).or().like("remark", kw));
|
||||
}
|
||||
// 排序:由于列表合并了未完成/已完成,统一在服务端排序,前端不再二次处理
|
||||
if ("priority".equals(sort)) {
|
||||
queryWrapper.orderByDesc("priority").orderByAsc("status").orderByDesc("create_date");
|
||||
} else if ("due".equals(sort)) {
|
||||
// 无截止日期的排在最后
|
||||
queryWrapper.orderByAsc("due_date IS NULL").orderByAsc("due_date").orderByAsc("status");
|
||||
} else if ("title".equals(sort)) {
|
||||
queryWrapper.orderByAsc("title");
|
||||
} else {
|
||||
queryWrapper.orderByAsc("status").orderByDesc("create_date");
|
||||
}
|
||||
queryWrapper.last("LIMIT " + MAX_ROWS);
|
||||
List<PublicTodo> list = publicTodoService.list(queryWrapper);
|
||||
return Result.ok().setData(list);
|
||||
} catch (Exception e) {
|
||||
log.error("查询公开待办失败", e);
|
||||
return Result.error().message("查询失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增公开待办
|
||||
*/
|
||||
@RequestMapping("add")
|
||||
public Result add(PublicTodo publicTodo) {
|
||||
if (StringUtils.isBlank(publicTodo.getTitle())) {
|
||||
return Result.error().message("标题不能为空");
|
||||
}
|
||||
try {
|
||||
publicTodo.setId(null);
|
||||
if (publicTodo.getStatus() == null) {
|
||||
publicTodo.setStatus(0);
|
||||
}
|
||||
if (publicTodo.getPriority() == null) {
|
||||
publicTodo.setPriority(2);
|
||||
}
|
||||
publicTodo.setCreateBy(OPERATOR);
|
||||
publicTodo.setCreateDate(new Date());
|
||||
publicTodo.setUpdateBy(OPERATOR);
|
||||
publicTodo.setUpdateDate(new Date());
|
||||
boolean save = publicTodoService.save(publicTodo);
|
||||
return Result.ok().setData(save);
|
||||
} catch (Exception e) {
|
||||
log.error("新增公开待办失败", e);
|
||||
return Result.error().message("新增失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新公开待办(标题 / 备注 / 优先级 / 分类 / 截止日期 / 状态)
|
||||
*/
|
||||
@RequestMapping("update")
|
||||
public Result update(PublicTodo publicTodo) {
|
||||
if (publicTodo.getId() == null) {
|
||||
return Result.error().message("id 不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(publicTodo.getTitle())) {
|
||||
return Result.error().message("标题不能为空");
|
||||
}
|
||||
try {
|
||||
publicTodo.setUpdateBy(OPERATOR);
|
||||
publicTodo.setUpdateDate(new Date());
|
||||
publicTodo.setCreateBy(null);
|
||||
publicTodo.setCreateDate(null);
|
||||
boolean update = publicTodoService.updateById(publicTodo);
|
||||
return Result.ok().setData(update);
|
||||
} catch (Exception e) {
|
||||
log.error("更新公开待办失败", e);
|
||||
return Result.error().message("更新失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换完成状态
|
||||
*/
|
||||
@RequestMapping("updateStatus")
|
||||
public Result updateStatus(Long id, Integer status) {
|
||||
if (id == null) {
|
||||
return Result.error().message("id 不能为空");
|
||||
}
|
||||
if (status == null) {
|
||||
return Result.error().message("status 不能为空");
|
||||
}
|
||||
try {
|
||||
UpdateWrapper<PublicTodo> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("id", id).eq("del_flag", 0);
|
||||
updateWrapper.set("status", status);
|
||||
updateWrapper.set("update_by", OPERATOR);
|
||||
updateWrapper.set("update_date", new Date());
|
||||
boolean update = publicTodoService.update(updateWrapper);
|
||||
return Result.ok().setData(update);
|
||||
} catch (Exception e) {
|
||||
log.error("更新公开待办状态失败", e);
|
||||
return Result.error().message("更新失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除公开待办(逻辑删除)
|
||||
*/
|
||||
@RequestMapping("remove")
|
||||
public Result remove(Long id) {
|
||||
if (id == null) {
|
||||
return Result.error().message("id 不能为空");
|
||||
}
|
||||
try {
|
||||
boolean remove = publicTodoService.removeById(id);
|
||||
return Result.ok().setData(remove);
|
||||
} catch (Exception e) {
|
||||
log.error("删除公开待办失败", e);
|
||||
return Result.error().message("删除失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除已完成的待办(逻辑删除)
|
||||
*/
|
||||
@RequestMapping("removeDone")
|
||||
public Result removeDone() {
|
||||
try {
|
||||
UpdateWrapper<PublicTodo> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("status", 1).eq("del_flag", 0);
|
||||
updateWrapper.set("del_flag", 1);
|
||||
updateWrapper.set("update_by", OPERATOR);
|
||||
updateWrapper.set("update_date", new Date());
|
||||
boolean update = publicTodoService.update(updateWrapper);
|
||||
return Result.ok().setData(update);
|
||||
} catch (Exception e) {
|
||||
log.error("批量删除已完成待办失败", e);
|
||||
return Result.error().message("清理失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 看板统计(总数 / 未完成 / 已完成 / 已逾期 / 分类分布)
|
||||
*/
|
||||
@RequestMapping("stats")
|
||||
public Result<Map<String, Object>> stats() {
|
||||
try {
|
||||
QueryWrapper<PublicTodo> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("del_flag", 0);
|
||||
List<PublicTodo> list = publicTodoService.list(queryWrapper);
|
||||
|
||||
int total = list.size();
|
||||
int done = 0;
|
||||
int overdue = 0;
|
||||
Map<String, Integer> byCategory = new HashMap<>();
|
||||
Date today = new Date();
|
||||
for (PublicTodo item : list) {
|
||||
if (item.getStatus() != null && item.getStatus() == 1) {
|
||||
done++;
|
||||
} else if (item.getDueDate() != null && item.getDueDate().before(today)) {
|
||||
overdue++;
|
||||
}
|
||||
String cat = StringUtils.isBlank(item.getCategory()) ? "未分类" : item.getCategory();
|
||||
byCategory.merge(cat, 1, Integer::sum);
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("total", total);
|
||||
data.put("done", done);
|
||||
data.put("active", total - done);
|
||||
data.put("overdue", overdue);
|
||||
data.put("byCategory", byCategory);
|
||||
return Result.ok().setData(data);
|
||||
} catch (Exception e) {
|
||||
log.error("公开待办统计失败", e);
|
||||
return Result.error().message("统计失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.freedom.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* t_public_todo
|
||||
* 公开待办看板(无需登录即可访问,全体访问者共享同一份清单)
|
||||
*
|
||||
* @author zhangjiaxun
|
||||
*/
|
||||
@Data
|
||||
@TableName("t_public_todo")
|
||||
public class PublicTodo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 优先级:1低 2中 3高
|
||||
*/
|
||||
private Integer priority;
|
||||
|
||||
/**
|
||||
* 分类
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 截止日期
|
||||
*/
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date dueDate;
|
||||
|
||||
/**
|
||||
* 状态:0未完成 1已完成
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createDate;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date updateDate;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
private Boolean delFlag;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.freedom.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.freedom.admin.entity.PublicTodo;
|
||||
|
||||
/**
|
||||
* 数据访问层-公开待办看板表
|
||||
*
|
||||
* @author zhangjiaxun
|
||||
*/
|
||||
public interface PublicTodoMapper extends BaseMapper<PublicTodo> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.freedom.admin.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.freedom.admin.entity.PublicTodo;
|
||||
|
||||
/**
|
||||
* 业务层-公开待办看板表
|
||||
*
|
||||
* @author zhangjiaxun
|
||||
*/
|
||||
public interface PublicTodoService extends IService<PublicTodo> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.freedom.admin.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.freedom.admin.entity.PublicTodo;
|
||||
import com.freedom.admin.mapper.PublicTodoMapper;
|
||||
import com.freedom.admin.service.PublicTodoService;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 业务层实现-公开待办看板表
|
||||
*
|
||||
* @author zhangjiaxun
|
||||
*/
|
||||
@Service
|
||||
@Log4j2
|
||||
public class PublicTodoServiceImpl extends ServiceImpl<PublicTodoMapper, PublicTodo> implements PublicTodoService {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user