今天我们来一个年终福利文章。
接口注释:
@ApiOperation(value = "首页", notes = "访问测试", httpMethod = "GET")
参数注释:
@ApiParam(name = "名字", value = "名字", required = true) String name
private ApiInfo apiInfo() {
return new ApiInfo(
"猿码优创管理API文档",
"猿码优创管理API文档",
"2.0",
null,
new Contact("kingyifan", "https://cnbuilder.cn/", "itw@tom.com"),
"Apache 2.0", "https://www.apache.org/licenses/LICENSE-2.0.html", Collections.emptyList());
}
form-data:
account=admin
password=admin
{
"msg": "登录成功(Login Success.)",
"code": 200,
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjdXJyZW50VGltZU1pbGxpcyI6IjE2MDY0NDg3MDM1NzciLCJleHAiOjE2MDY0OTE5MDMsImFjY291bnQiOiJhZG1pbiJ9.nI_J4K7UtuHISwM3weGYKd65p3VhD7LSqcuCnqKa8A0"
}
}
/**
* 登录授权
*
* @param account
* @param password
* @author 猿码优创 website:https://cnbuilder.cn/ Email:itw@tom.com
*/
@PostMapping("/login")
@ApiOperation(value = "用户", notes = "登录", httpMethod = "GET")
public R login(
@ApiParam(name = "account", value = "账号", required = true) @RequestParam(value = "account", required = true) String account,
@ApiParam(name = "password", value = "密码", required = true) @RequestParam(value = "password", required = true) String password,
HttpServletResponse httpServletResponse) {
Map reMap = new HashMap<>();
// 查询数据库中的帐号信息account
User userTemp = userService.getOne(new QueryWrapper<User>().eq("account", account));
if (userTemp == null) {
return R.error(500, "该帐号不存在(The account does not exist.)");
}
// 密码进行AES解密
String key = AesCipherUtil.deCrypto(userTemp.getPassword());
// 因为密码加密是以帐号+密码的形式进行加密的,所以解密后的对比是帐号+密码
if (key.equals(account + password)) {
// 清除可能存在的Shiro权限信息缓存
if (JedisUtil.exists(Constant.PREFIX_SHIRO_CACHE + account)) {
JedisUtil.delKey(Constant.PREFIX_SHIRO_CACHE + account);
}
//判斷redis是否存在 存在直接用相同的。这个可以避免同样的账号两个人登录 把另一个人给踢出 TODO:如果一个账号只能一个登录可以去掉这段代码~
String rtoken = (String) JedisUtil.getObject(Constant.EXISTENCE_TOKEN + account);
if (rtoken != null) {
httpServletResponse.setHeader("Authorization", rtoken);
httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
reMap.put("token", rtoken);
return R.ok(HttpStatus.OK.value(), "登录成功(Login Success.)", reMap);
}
// 设置RefreshToken,时间戳为当前时间戳,直接设置即可(不用先删后设,会覆盖已有的RefreshToken)
String currentTimeMillis = String.valueOf(System.currentTimeMillis());
JedisUtil.setObject(Constant.PREFIX_SHIRO_REFRESH_TOKEN + account, currentTimeMillis, Integer.parseInt(refreshTokenExpireTime));
// 从Header中Authorization返回AccessToken,时间戳为当前时间戳
String token = JwtUtil.sign(account, currentTimeMillis);
//存放redis
JedisUtil.setObject(Constant.EXISTENCE_TOKEN + account, token, Integer.parseInt(accessTokenExpireTime));
reMap.put("token", token);
httpServletResponse.setHeader("Authorization", token);
httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
return R.ok("登录成功(Login Success.)").data(reMap);
}
return R.error(500, "帐号或密码错误(Account or Password Error.)");
}
加一行注解即可:自己匹配权限
@RequiresPermissions(logical = Logical.AND, value = {"user:menus"})
代码样例:
@GetMapping(value = "/menus")
@ApiOperation(value = "获取菜单", notes = "获取菜单", httpMethod = "GET")
@SystemLogAnnotation(businessType = "用户", methodDetail = "获取菜单")
@RequiresPermissions(logical = Logical.AND, value = {"user:menus"})
public R menus() {
QueryWrapper<SysMenu> qr = new QueryWrapper<>();
//根据用户查询菜单权限
List<SysMenu> list = iSysMenuService.list(qr);
//配置
TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
// 自定义属性名 都要默认值的
treeNodeConfig.setWeightKey("sort");
treeNodeConfig.setIdKey("id");
// 最大递归深度
treeNodeConfig.setDeep(3);
//转换器
List<Tree<String>> treeNodes = TreeUtil.build(list, "-1", treeNodeConfig,
(treeNode, tree) -> {
tree.setId(treeNode.getId().toString());
tree.setParentId(treeNode.getParentid().toString());
tree.setWeight(treeNode.getSort());
tree.setName(treeNode.getName());
// 扩展属性 ...
tree.putExtra("isJump", treeNode.getIsJump() == null || treeNode.getIsJump() == 0 ? false : true);
tree.putExtra("icon", treeNode.getIcon() == null ? "" : treeNode.getIcon());
tree.putExtra("url", treeNode.getUrl());
});
return R.ok().data(treeNodes);
}
代码:
final注入:
private final UserUtil userUtil;这种final方式注入需要在controller或者service加@RequiredArgsConstructor
普通注入:
@Autowired
private UserUtil userUtil;
直接使用:
User user = userUtil.getUser();
//新增时序数据库数据
Map<String, String> tagsMap = new HashMap<>();//默认自己加索引
Map<String, Object> fieldsMap = new HashMap<>();//默认不加索引
tagsMap.put("name", "猿码优创");
tagsMap.put("website", "www.cnbuilder.cn");
tagsMap.put("Email", "itw@tom.com");
fieldsMap.put("author", "kingyifan");
influxDBConnect.insert("user", tagsMap, fieldsMap);
保存:
/**
* 插入
*
* @param measurement 表
* @param tags 标签
* @param fields 字段
*/
public void insert(String measurement, Map<String, String> tags, Map<String, Object> fields) {
Point.Builder builder = Point.measurement(measurement);
// 纳秒时会出现异常信息:partial write: points beyond retention policy dropped=1
// builder.time(System.nanoTime(), TimeUnit.NANOSECONDS);
builder.time(System.currentTimeMillis() / 1000, TimeUnit.SECONDS);
builder.tag(tags);
builder.fields(fields);
log.info("influxDB insert data:[{}]", builder.build().toString());
influxDB.write(database, "", builder.build());
}
PS:influxDB和mysql数据库不一样。
mysql返回的数据:
key:value
influxDb返回数据:
columns=[time, Email, author, name, website],
values=[[2020-12-17T10:16:39Z, itw@tom.com, kingyifan, 猿码优创, www.cnbuilder.cn]]]
代码:
/**
* 解析influx返回数据
*
* @param queryResult
* @return
*/
public static List<Map<String, Object>> convertQueryResult(QueryResult queryResult) {
List<Map<String, Object>> maps = new LinkedList<>();
if (queryResult.getResults().get(0).getSeries() == null || queryResult.getResults().get(0).getSeries().size() == 0) {
return null;
}
for (QueryResult.Series series : queryResult.getResults().get(0).getSeries()) {
List<String> columns = series.getColumns();
List<List<Object>> values = series.getValues();
for (List<Object> objects : values) {
Map map = new HashMap();
for (int i = 0; i < columns.size(); i++) {
String c = columns.get(i);
if (c.equals("time")) {
map.put(c, objects.get(i).toString().replace("+08:00", ""));
continue;
}
map.put(c, objects.get(i));
}
if (series.getTags() != null) {
for (String key : series.getTags().keySet()) {
map.put(key, series.getTags().get(key));
}
}
maps.add(map);
}
}
return maps;
}
注解:
@Scheduled(cron = "0/1 * * * * ? ")
拦截用户操作日志:谁在什么时间干了什么什么事最后结果是什么?没干成原因是什么?:
CREATE TABLE `sys_user_log` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`business_type` varchar(20) DEFAULT NULL COMMENT '业务类型',
`method_detail` varchar(50) DEFAULT NULL COMMENT '接口备注',
`ip` varchar(20) DEFAULT NULL COMMENT '请求ip',
`method_name` varchar(200) DEFAULT NULL COMMENT '接口方法',
`url` varchar(200) DEFAULT NULL COMMENT '接口url',
`params` text COMMENT '请求参数',
`sys_user_id` int(11) DEFAULT NULL COMMENT '用户id',
`sys_user_name` varchar(50) DEFAULT NULL COMMENT '操作人名称',
`excute_time` bigint(20) DEFAULT NULL COMMENT '执行耗时',
`create_time` datetime DEFAULT NULL COMMENT '操作时间',
`return_result` text COMMENT '返回结果',
`error_detail` text COMMENT '错误原因',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;
package cn.cnbuilder.manage.common.aspect;
import cn.cnbuilder.manage.busi.service.ISysUserLogService;
import cn.cnbuilder.manage.common.entity.R;
import cn.cnbuilder.manage.shiro.util.UserUtil;
import cn.cnbuilder.manage.busi.entity.SysUserLog;
import cn.cnbuilder.manage.common.annotation.SystemLogAnnotation;
import cn.cnbuilder.manage.common.utils.IpUtil;
import cn.cnbuilder.manage.shiro.model.entity.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Date;
/**
* 日志切面类
*
* @author 猿码优创 website:https://cnbuilder.cn/ Email:itw@tom.com
*/
@Aspect
@Component
public class LogAspect {
@Autowired
private UserUtil userUtil;
@Autowired
private ISysUserLogService sysUserLogService;
/**
* 定义一个切入点.
* 解释下:
* <p>
* ~ 第一个 * 代表任意修饰符及任意返回值.
* ~ 第二个 * 定义在web包或者子包
* ~ 第三个 * 任意方法
* ~ .. 匹配任意数量的参数.
*/
@Pointcut("execution(* cn.cnbuilder.manage.*.controller..*(..))")
public void logPointcut() {
}
@Pointcut("@annotation(cn.cnbuilder.manage.common.annotation.SystemLogAnnotation)")
public void executePointCut() {
}
@org.aspectj.lang.annotation.Around("logPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) {
//获取请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
SysUserLog sysUserLog = new SysUserLog();
long start = System.currentTimeMillis();
Object result = null;
// 操作模块和方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
SystemLogAnnotation annotation = signature.getMethod().getAnnotation(SystemLogAnnotation.class);
try {
//返回结果
result = joinPoint.proceed();
} catch (Throwable e) {
//保存
sysUserLog.setErrorDetail(e.getMessage());
e.printStackTrace();
//返回错误信息
result = R.error(500, e.getMessage());
} finally {
try {
//结束时间
long end = System.currentTimeMillis();
//执行时间
sysUserLog.setExcuteTime(end - start);
//y业务类型
sysUserLog.setBusinessType(annotation == null ? "" : annotation.businessType());
//操作时间
sysUserLog.setCreateTime(new Date());
//ip地址
sysUserLog.setIp(IpUtil.getIpAddr(request));
//接口方法
sysUserLog.setMethodName(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
//请求参数
sysUserLog.setParams(Arrays.toString(joinPoint.getArgs()));
//返回结果
sysUserLog.setReturnResult(result == null ? "" : result.toString());
User user = userUtil.aroundGetUser();
//用户id
sysUserLog.setSysUserId(user.getId());
//用戶名
sysUserLog.setSysUserName(user.getUsername());
//请求url
sysUserLog.setUrl(request.getRequestURI());
//方法备注
sysUserLog.setMethodDetail(annotation == null ? "" : annotation.methodDetail());
sysUserLogService.save(sysUserLog);
} catch (Exception e) {
e.printStackTrace();
} finally {
//不管保存不保存日志都需要返回结果
return result;
}
}
}
}
使用了自定义注解:
添加使用注解:
@SystemLogAnnotation(businessType = "用户", methodDetail = "获取菜单")
自定义注解代码(可以自行添加别的参数):
package cn.cnbuilder.manage.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解
*
* @author 猿码优创 website:https://cnbuilder.cn/ Email:itw@tom.com
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLogAnnotation {
// 业务类型
String businessType() default "";
//方法名称
String methodDetail() default "";
}
存值:
JedisUtil.setObject("name", "wangyifan");
取值:
JedisUtil.getObject("name").toString();
更多使用详见JedisUtil工具类,建议使用腾讯云版本的Redis地址:https://cloud.tencent.com/product/crs
正常发送消息:
群发消息:
具体配置信息我原来写过文章,请移步:https://blog.cnbuilder.cn/archives/SpringBoot_websocket
修改成自己的连接等配置,执行main方法,就会生成我们需要的controller、service、mapper、xml等文件
// ======================数据源配置========================
DataSourceConfig dsc = new DataSourceConfig();
dsc.setDbType(DbType.MYSQL); //设置数据库类型,
dsc.setUrl("jdbc:mysql://xxx.xxx.xxx.xxx:xx/ymyc_manage?serverTimezone=GMT%2B8"); //指定数据库
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
autoGenerator.setDataSource(dsc);
//设置生成的表名,这里使用exClude来进行排除,写入null,表示生成所有表。
strategyConfig.setExclude(null);
//设置表 可变个数的形参 String数组
strategyConfig.setInclude("t\_user",""xxx");
gc.setAuthor("猿码优创");// 作者
pc.setParent("cn.cnbuilder.manage.busi");
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。