feat&fix: 轮播图、日志补丁

This commit is contained in:
筱锋xiao_lfeng 2024-01-19 18:15:14 +08:00
parent e93cf90d5b
commit e0b4e02acc
Signed by: XiaoLFeng
GPG Key ID: F693AA12AABBFA87
15 changed files with 433 additions and 19 deletions

View File

@ -397,3 +397,23 @@ node6 --|> node4
| 3 | `data` | json数据 | json | | YES | | |
| 4 | `created_at` | 创建时间 | timestamp | | NO | DEFAULT_GENERATED | CURRENT_TIMESTAMP |
| 5 | `updated_at` | 修改时间 | timestamp | | YES | | |
### oa_message 数据表
#### 说明
> 【消息表】用户消息获取如站内消息等信息的消息需要配合Email操作
#### 数据表字段属性
| 序号 | 名称 | 描述 | 类型 | 键 | 为空 | 额外 | 默认值 |
| :--: | :----------: | :----------: | :-------------: | :--: | :--: | :---------------: | :---------------: |
| 1 | `id` | 消息主键 | bigint unsigned | PRI | NO | auto_increment | |
| 2 | `uid` | 用户主键 | bigint unsigned | MUL | NO | | |
| 3 | `title` | 消息抬头 | varchar(100) | | NO | | |
| 4 | `text` | 消息正文 | text | | NO | | |
| 5 | `read` | 消息是否已读 | tinyint(1) | | NO | | 0 |
| 6 | `created_at` | 创建时间 | timestamp | | NO | DEFAULT_GENERATED | CURRENT_TIMESTAMP |
| 7 | `deleted_at` | 删除时间 | timestamp | | YES | | |

View File

@ -4,6 +4,7 @@ import com.google.gson.Gson;
import com.jsl.oa.utils.ErrorCode;
import com.jsl.oa.utils.JwtUtil;
import com.jsl.oa.utils.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
@ -20,6 +21,7 @@ import javax.servlet.http.HttpServletRequest;
* @version v1.1.0
* @since v1.1.0
*/
@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter {
/**
@ -41,6 +43,7 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
} else {
// 解析Bearer后面的令牌
token = token.replace("Bearer ", "");
log.info("请求令牌:" + token);
return JwtUtil.verify(token);
}
}

View File

@ -28,6 +28,7 @@ public class ShiroConfiguration {
filterChainDefinitionMap.put("/auth/**/**", "anon"); // 登录接口允许匿名访问
filterChainDefinitionMap.put("/unauthorized", "anon"); // 未授权接口允许匿名访问
filterChainDefinitionMap.put("/", "anon"); // 首页允许匿名访问
filterChainDefinitionMap.put("/info/header-image/get", "anon"); // 信息接口允许匿名访问
filterChainDefinitionMap.put("/**/**", "jwt"); // 其他接口一律拦截(需要Token)
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

View File

@ -1,9 +1,76 @@
package com.jsl.oa.controllers;
import com.jsl.oa.model.voData.business.info.CarouselVO;
import com.jsl.oa.services.InfoService;
import com.jsl.oa.utils.BaseResponse;
import com.jsl.oa.utils.ErrorCode;
import com.jsl.oa.utils.Processing;
import com.jsl.oa.utils.ResultUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
/**
* <h1>信息控制器</h1>
* <hr/>
* 信息控制器包含信息获取接口
*
* @author 筱锋xiao_lfeng
* @version v1.1.0
* @since v1.1.0
*/
@Slf4j
@RestController
@RequiredArgsConstructor
public class InfoController {
private final InfoService infoService;
@GetMapping("/info/header-image/get")
public BaseResponse infoGetHeaderImage() {
log.info("请求接口[GET]: /info/header-image/get");
return infoService.getHeaderImage();
}
@PutMapping("/info/header-image/edit")
public BaseResponse infoEditHeaderImage(@RequestBody @Validated CarouselVO carouselVO, @RequestParam Integer id, HttpServletRequest request, @NotNull BindingResult bindingResult) {
log.info("请求接口[PUT]: /info/header-image/edit");
// 参数校验
if (bindingResult.hasErrors()) {
log.warn("参数校验失败: {}", Processing.getValidatedErrorList(bindingResult));
return ResultUtil.error(ErrorCode.PARAMETER_ERROR, Processing.getValidatedErrorList(bindingResult));
}
if (id == null) {
log.warn("参数校验失败: {}", "id不能为空");
return ResultUtil.error(ErrorCode.PARAMETER_ERROR, "id不能为空");
}
return infoService.editHeaderImage(request, carouselVO, id);
}
@DeleteMapping("/info/header-image/del")
public BaseResponse infoDelHeaderImage(@RequestParam Integer id, HttpServletRequest request) {
log.info("请求接口[DELETE]: /info/header-image/del");
return infoService.delHeaderImage(request, id);
}
@PostMapping("/info/header-image/add")
public BaseResponse infoAddHeaderImage(@RequestBody @Validated CarouselVO carouselVO, HttpServletRequest request, @NotNull BindingResult bindingResult) {
log.info("请求接口[POST]: /info/header-image/add");
// 参数校验
if (bindingResult.hasErrors()) {
log.warn("参数校验失败: {}", Processing.getValidatedErrorList(bindingResult));
return ResultUtil.error(ErrorCode.PARAMETER_ERROR, Processing.getValidatedErrorList(bindingResult));
}
return infoService.addHeaderImage(request, carouselVO);
}
@PutMapping("/info/header-image/edit-setting")
public BaseResponse infoEditSettingHeaderImage(@RequestBody @Validated CarouselVO carouselVO, @RequestParam Integer id, HttpServletRequest request, @NotNull BindingResult bindingResult) {
log.info("请求接口[PUT]: /info/header-image/edit-setting");
return null;
}
}

View File

@ -0,0 +1,80 @@
package com.jsl.oa.dao;
import com.google.gson.Gson;
import com.jsl.oa.mapper.InfoMapper;
import com.jsl.oa.model.doData.info.CarouselDO;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
/**
* <h1>轮播图数据表</h1>
* <hr/>
* 内容进入自定义实体类
*
* @author 筱锋xiao_lfeng
* @version v1.1.0
* @since v1.1.0
*/
@Component
@RequiredArgsConstructor
public class InfoDAO {
private final InfoMapper infoMapper;
private final Gson gson;
/**
* <h2>获取轮播图</h2>
* <hr/>
* 获取轮播图
*
* @return {@link CarouselDO}
*/
public CarouselDO getCarousel() {
String getCarouselSql = infoMapper.getCarousel();
CarouselDO getCarousel = null;
if (!getCarouselSql.equals("{}")) {
getCarousel = gson.fromJson(getCarouselSql, CarouselDO.class);
}
if (getCarousel == null) {
// 初始化
getCarousel = new CarouselDO();
getCarousel.setOrder("desc");
getCarousel.setData(new ArrayList<>());
try {
infoMapper.insertCarousel();
} catch (DuplicateKeyException ignored) {
}
}
// 获取排序
for (int i = 0; i < getCarousel.getData().size(); i++) {
for (int j = 0; j < getCarousel.getData().size(); j++) {
if (getCarousel.getOrder().equals("desc")) {
if (getCarousel.getData().get(i).getDisplayOrder() > getCarousel.getData().get(j).getDisplayOrder()) {
Collections.swap(getCarousel.getData(), i, j);
}
} else {
if (getCarousel.getData().get(i).getDisplayOrder() < getCarousel.getData().get(j).getDisplayOrder()) {
Collections.swap(getCarousel.getData(), i, j);
}
}
}
}
return getCarousel;
}
/**
* <h2>设置轮播图</h2>
* <hr/>
* 设置轮播图
*
* @param carouselDO 轮播图
* @return {@link Boolean}
*/
public boolean setCarousel(CarouselDO carouselDO) {
String setCarouselSql = gson.toJson(carouselDO);
return infoMapper.setCarousel(setCarouselSql);
}
}

View File

@ -1,6 +1,8 @@
package com.jsl.oa.exception;
import com.jsl.oa.utils.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
/**
* <h1>业务异常类</h1>
@ -12,9 +14,11 @@ import com.jsl.oa.utils.ErrorCode;
* @author 筱锋xiao_lfeng
* @see RuntimeException
*/
@Slf4j
public class BusinessException extends RuntimeException {
public BusinessException(ErrorCode errorCode) {
public BusinessException(@NotNull ErrorCode errorCode) {
super(errorCode.getOutput() + "|" + errorCode.getMessage());
log.warn("业务异常: {}", errorCode.getOutput() + "|" + errorCode.getMessage());
}
}

View File

@ -36,6 +36,6 @@ public class ProcessException {
@ExceptionHandler(value = Exception.class)
public ResponseEntity<BaseResponse> businessException(@NotNull Exception e) {
log.error(e.getMessage(), e);
return ResultUtil.error("Exception", 500, "服务器异常");
return ResultUtil.error("ServerInternalError", 500, "服务器内部错误");
}
}

View File

@ -1,6 +1,7 @@
package com.jsl.oa.mapper;
import com.jsl.oa.model.doData.ConfigDO;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
@ -45,4 +46,14 @@ public interface InfoMapper {
*/
@Update("INSERT INTO organize_oa.oa_config (value, data, created_at) VALUES (#{value}, #{data}, #{createdAt})")
void insertSecurityKey(ConfigDO configDO);
@Select("SELECT data FROM organize_oa.oa_config WHERE value= 'carousel'")
String getCarousel();
@Update("UPDATE organize_oa.oa_config SET data = #{setCarouselSql} WHERE value = 'carousel'")
boolean setCarousel(String setCarouselSql);
@Insert("INSERT INTO organize_oa.oa_config (value, data, created_at) VALUES ('carousel', null, NOW())")
void insertCarousel();
}

View File

@ -0,0 +1,34 @@
package com.jsl.oa.model.doData.info;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* <h1>轮播图数据表</h1>
* <hr/>
* 内容进入自定义实体类
*
* @version v1.1.0
* @since v1.1.0
* @author 筱锋xiao_lfeng
*/
@Data
public class CarouselDO {
private String order;
private List<DataDO> data;
@Data
@Accessors(chain = true)
public static class DataDO {
private Integer displayOrder;
private String image;
private String title;
private String description;
private Boolean isActive;
private String createdAt;
private String updatedAt;
private String author;
}
}

View File

@ -0,0 +1,21 @@
package com.jsl.oa.model.voData.business.info;
import lombok.Data;
/**
* <h1>轮播图VO</h1>
* <hr/>
* 轮播图VO用于接收轮播图请求
*
* @version v1.1.0
* @since v1.1.0
* @author 筱锋xiao_lfeng
*/
@Data
public class CarouselVO {
private Integer displayOrder;
private String image;
private String title;
private String description;
private Boolean isActive;
}

View File

@ -0,0 +1,60 @@
package com.jsl.oa.services;
import com.jsl.oa.model.voData.business.info.CarouselVO;
import com.jsl.oa.utils.BaseResponse;
import javax.servlet.http.HttpServletRequest;
/**
* <h1>信息服务接口</h1>
* <hr/>
* 信息服务接口包含信息获取接口
*
* @author 筱锋xiao_lfeng
* @version v1.1.0
* @since v1.1.0
*/
public interface InfoService {
/**
* <h2>添加轮播图</h2>
* <hr/>
* 添加轮播图
*
* @param request 请求
* @param carouselVO 轮播图VO
* @return {@link BaseResponse}
*/
BaseResponse addHeaderImage(HttpServletRequest request, CarouselVO carouselVO);
/**
* <h2>编辑轮播图</h2>
* <hr/>
* 编辑轮播图
*
* @param request 请求
* @param carouselVO 轮播图VO
* @param id 轮播图ID
* @return {@link BaseResponse}
*/
BaseResponse editHeaderImage(HttpServletRequest request, CarouselVO carouselVO, Integer id);
/**
* <h2>获取轮播图</h2>
* <hr/>
* 获取轮播图
*
* @return {@link BaseResponse}
*/
BaseResponse getHeaderImage();
/**
* <h2>删除轮播图</h2>
* <hr/>
* 删除轮播图
*
* @param request 请求
* @param id 轮播图ID
* @return {@link BaseResponse}
*/
BaseResponse delHeaderImage(HttpServletRequest request, Integer id);
}

View File

@ -0,0 +1,112 @@
package com.jsl.oa.services.impl;
import com.jsl.oa.dao.InfoDAO;
import com.jsl.oa.dao.UserDAO;
import com.jsl.oa.mapper.RoleMapper;
import com.jsl.oa.model.doData.UserDO;
import com.jsl.oa.model.doData.info.CarouselDO;
import com.jsl.oa.model.voData.business.info.CarouselVO;
import com.jsl.oa.services.InfoService;
import com.jsl.oa.utils.BaseResponse;
import com.jsl.oa.utils.ErrorCode;
import com.jsl.oa.utils.Processing;
import com.jsl.oa.utils.ResultUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.sql.Timestamp;
@Slf4j
@Service
@RequiredArgsConstructor
public class InfoServiceImpl implements InfoService {
private final RoleMapper roleMapper;
private final InfoDAO infoDAO;
private final UserDAO userDAO;
@Override
public BaseResponse addHeaderImage(HttpServletRequest request, CarouselVO carouselVO) {
// 用户权限校验
if (!Processing.checkUserIsAdmin(request, roleMapper)) {
return ResultUtil.error(ErrorCode.NOT_ADMIN);
}
// 获取用户
Long userId = Processing.getAuthHeaderToUserId(request);
UserDO userDO = userDAO.getUserById(userId);
// 获取轮播图信息
CarouselDO carouselDO = infoDAO.getCarousel();
// 添加轮播图
CarouselDO.DataDO carousel = new CarouselDO.DataDO();
carousel.setDisplayOrder(carouselVO.getDisplayOrder())
.setImage(carouselVO.getImage())
.setDescription(carouselVO.getDescription())
.setTitle(carouselVO.getTitle())
.setIsActive(carouselVO.getIsActive())
.setAuthor(userDO.getUsername())
.setCreatedAt(new Timestamp(System.currentTimeMillis()).toString());
carouselDO.getData().add(carousel);
// 保存轮播图
if (infoDAO.setCarousel(carouselDO)) {
return ResultUtil.success();
} else {
return ResultUtil.error(ErrorCode.DATABASE_UPDATE_ERROR);
}
}
@Override
public BaseResponse editHeaderImage(HttpServletRequest request, CarouselVO carouselVO, Integer id) {
// 用户权限校验
if (!Processing.checkUserIsAdmin(request, roleMapper)) {
return ResultUtil.error(ErrorCode.NOT_ADMIN);
}
// 获取用户
Long userId = Processing.getAuthHeaderToUserId(request);
UserDO userDO = userDAO.getUserById(userId);
// 获取轮播图信息
CarouselDO carouselDO = infoDAO.getCarousel();
// 获取指定轮播图
CarouselDO.DataDO carousel = carouselDO.getData().get(id);
carousel.setDisplayOrder(carouselVO.getDisplayOrder())
.setImage(carouselVO.getImage())
.setDescription(carouselVO.getDescription())
.setTitle(carouselVO.getTitle())
.setIsActive(carouselVO.getIsActive())
.setAuthor(userDO.getUsername())
.setUpdatedAt(new Timestamp(System.currentTimeMillis()).toString());
// 保存轮播图
if (infoDAO.setCarousel(carouselDO)) {
return ResultUtil.success();
} else {
return ResultUtil.error(ErrorCode.DATABASE_UPDATE_ERROR);
}
}
@Override
public BaseResponse getHeaderImage() {
CarouselDO carouselDO = infoDAO.getCarousel();
return ResultUtil.success(carouselDO);
}
@Override
public BaseResponse delHeaderImage(HttpServletRequest request, Integer id) {
// 用户权限校验
if (!Processing.checkUserIsAdmin(request, roleMapper)) {
return ResultUtil.error(ErrorCode.NOT_ADMIN);
}
// 获取轮播图信息
CarouselDO carouselDO = infoDAO.getCarousel();
// 删除指定轮播图
if (id > carouselDO.getData().size()) {
return ResultUtil.error(ErrorCode.ID_NOT_EXIST);
}
CarouselDO.DataDO data = carouselDO.getData().remove(id - 1);
// 保存轮播图
if (infoDAO.setCarousel(carouselDO)) {
return ResultUtil.success(data);
} else {
return ResultUtil.error(ErrorCode.DATABASE_UPDATE_ERROR);
}
}
}

View File

@ -1,7 +1,9 @@
package com.jsl.oa.utils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Getter
public enum ErrorCode {
WRONG_PASSWORD("WrongPassword", 40010, "密码错误"),
@ -17,10 +19,11 @@ public enum ErrorCode {
VERIFICATION_INVALID("VerificationInvalid", 40102, "验证码无效"),
TOKEN_NOT_EXIST("TokenNotExist", 40103, "Token不存在"),
USER_IS_LOCKED("UserIsLocked", 40300, "用户已被锁定"),
USER_IS_DEACTIVATED("UserIsDeactivated", 40300, "用户已被禁用"),
NOT_ADMIN("NotAdmin", 40300, "不是管理员"),
EMAIL_LOGIN_NOT_SUPPORT("EmailLoginNotSupport", 40300, "请使用邮箱登陆"),
PASSWORD_NOT_SAME("PasswordNotSame", 40301, "两次密码不一致"),
USER_IS_DEACTIVATED("UserIsDeactivated", 40301, "用户已被禁用"),
NOT_ADMIN("NotAdmin", 40302, "不是管理员"),
EMAIL_LOGIN_NOT_SUPPORT("EmailLoginNotSupport", 40303, "请使用邮箱登陆"),
PASSWORD_NOT_SAME("PasswordNotSame", 40304, "两次密码不一致"),
ID_NOT_EXIST("IdNotExist", 40305, "ID不存在"),
DATABASE_INSERT_ERROR("DatabaseInsertError", 50010, "数据库插入错误"),
DATABASE_UPDATE_ERROR("DatabaseUpdateError", 50011, "数据库更新错误"),
DATABASE_DELETE_ERROR("DatabaseDeleteError", 50012, "数据库删除错误"),

View File

@ -54,10 +54,10 @@ public class JwtUtil {
try {
Long getTokenInUserId = getUserId(token);
// 验证用户名是否匹配
log.debug("Token值" + getTokenInUserId.toString());
log.info("令牌用户主键:" + getTokenInUserId.toString());
return Pattern.matches("^[0-9]+$", getTokenInUserId.toString());
} catch (Exception e) {
log.debug("Token验证失败", e);
log.info("令牌错误或失效");
return false;
}
}
@ -80,9 +80,7 @@ public class JwtUtil {
long userId;
try {
userId = Long.parseLong(claimsJws.getBody().getSubject());
log.debug("用户ID" + userId);
} catch (NumberFormatException exception) {
log.debug("用户ID格式错误", exception);
throw new NumberFormatException("用户ID格式错误");
}
return userId;

View File

@ -10,48 +10,48 @@ public class ResultUtil {
@Contract(" -> new")
public static @NotNull BaseResponse success() {
log.debug("请求接口成功");
log.info("成功: Success[200] {}", "操作成功");
return new BaseResponse("Success", 200, "操作成功", null);
}
@Contract("_ -> new")
public static @NotNull BaseResponse success(String message) {
log.debug("请求接口成功");
log.info("成功: Success[200] {}", message);
return new BaseResponse("Success", 200, message, null);
}
@Contract(value = "_ -> new", pure = true)
public static @NotNull BaseResponse success(Object data) {
log.debug("请求接口成功");
log.info("成功: Success[200] {}", "操作成功");
return new BaseResponse("Success", 200, "操作成功", data);
}
@Contract(value = "_, _ -> new", pure = true)
public static @NotNull BaseResponse success(String message, Object data) {
log.debug("请求接口成功");
log.info("成功: Success[200] {}", message);
return new BaseResponse("Success", 200, message, data);
}
@Contract("_ -> new")
public static @NotNull BaseResponse error(@NotNull ErrorCode errorCode) {
log.debug("请求接口错误[" + errorCode.getCode() + "] " + errorCode.getMessage());
log.warn("失败: 错误码[" + errorCode.getCode() + "]: {} - {}", errorCode.getOutput(), errorCode.getMessage());
return new BaseResponse(errorCode.getOutput(), errorCode.getCode(), errorCode.getMessage());
}
@Contract("_, _ -> new")
public static @NotNull BaseResponse error(@NotNull ErrorCode errorCode, Object data) {
log.debug("请求接口错误[" + errorCode.getCode() + "] " + errorCode.getMessage());
log.warn("失败: 错误码[" + errorCode.getCode() + "]: {} - {}", errorCode.getOutput(), errorCode.getMessage());
return new BaseResponse(errorCode.getOutput(), errorCode.getCode(), errorCode.getMessage(), data);
}
@Contract(value = "_, _, _, _ -> new", pure = true)
public static @NotNull BaseResponse error(String output, Integer code, String message, Object data) {
log.debug("请求接口错误[" + code + "] " + message);
log.warn("失败: 错误码[" + code + "]: {} - {}", output, message);
return new BaseResponse(output, code, message, data);
}
public static @NotNull ResponseEntity<BaseResponse> error(String output, Integer code, String message) {
log.debug("请求接口错误[" + code + "] " + message);
log.warn("失败: 错误码[" + code + "]: {} - {}", output, message);
return ResponseEntity.status(code)
.body(new BaseResponse(output, code, message));
}