增加站内消息系统——评论、回复、点赞、系统通知的消息,优化前端。
This commit is contained in:
parent
8074bf8e9e
commit
15ba1c69b3
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
## 一、前言
|
## 一、前言
|
||||||
|
|
||||||
基于前后端分离,分布式架构的在线测评平台(hoj),前端使用vue,后端主要使用springboot,redis,mysql,nacos等技术,**支持HDU、POJ、Codeforces的vjudge判题,同时适配手机端、电脑端浏览。**
|
基于前后端分离,分布式架构的在线测评平台(hoj),前端使用vue,后端主要使用springboot,redis,mysql,nacos等技术,**支持HDU、POJ、Codeforces的vjudge判题,同时适配手机端、电脑端浏览,拥有讨论区与站内消息系统。**
|
||||||
|
|
||||||
| 在线Demo | 在线文档 | Github&Gitee仓库地址 | QQ群 |
|
| 在线Demo | 在线文档 | Github&Gitee仓库地址 | QQ群 |
|
||||||
| :--------------------------------: | :-------------------------------------------------------: | :----------------------------------------------------------: | :-------: |
|
| :--------------------------------: | :-------------------------------------------------------: | :----------------------------------------------------------: | :-------: |
|
||||||
|
@ -94,6 +94,7 @@ docker ps # 查看当前运行的容器状态
|
||||||
| 2021-06-25 | 丰富前端操作,增加POJ的vjudge判题 | Himit_ZH |
|
| 2021-06-25 | 丰富前端操作,增加POJ的vjudge判题 | Himit_ZH |
|
||||||
| 2021-08-14 | 增加spj对使用testlib的支持 | Himit_ZH |
|
| 2021-08-14 | 增加spj对使用testlib的支持 | Himit_ZH |
|
||||||
| 2021-09-21 | 增加比赛打印功能、账号限制功能 | Himit_ZH |
|
| 2021-09-21 | 增加比赛打印功能、账号限制功能 | Himit_ZH |
|
||||||
|
| 2021-10-05 | 增加站内消息系统——评论、回复、点赞、系统通知的消息,优化前端。 | Himit_ZH |
|
||||||
|
|
||||||
## 五、部分截图
|
## 五、部分截图
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import top.hcode.hoj.dao.*;
|
||||||
import top.hcode.hoj.pojo.dto.LoginDto;
|
import top.hcode.hoj.pojo.dto.LoginDto;
|
||||||
import top.hcode.hoj.pojo.entity.*;
|
import top.hcode.hoj.pojo.entity.*;
|
||||||
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||||
|
import top.hcode.hoj.service.impl.SessionServiceImpl;
|
||||||
import top.hcode.hoj.utils.IpUtils;
|
import top.hcode.hoj.utils.IpUtils;
|
||||||
import top.hcode.hoj.utils.JwtUtils;
|
import top.hcode.hoj.utils.JwtUtils;
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ public class AdminAccountController {
|
||||||
private UserRoleMapper userRoleDao;
|
private UserRoleMapper userRoleDao;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SessionMapper sessionDao;
|
private SessionServiceImpl sessionService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JwtUtils jwtUtils;
|
private JwtUtils jwtUtils;
|
||||||
|
@ -65,8 +66,10 @@ public class AdminAccountController {
|
||||||
response.setHeader("Authorization", jwt); //放到信息头部
|
response.setHeader("Authorization", jwt); //放到信息头部
|
||||||
response.setHeader("Access-Control-Expose-Headers", "Authorization");
|
response.setHeader("Access-Control-Expose-Headers", "Authorization");
|
||||||
// 会话记录
|
// 会话记录
|
||||||
sessionDao.insert(new Session().setUid(userRoles.getUid())
|
sessionService.save(new Session().setUid(userRoles.getUid())
|
||||||
.setIp(IpUtils.getUserIpAddr(request)).setUserAgent(request.getHeader("User-Agent")));
|
.setIp(IpUtils.getUserIpAddr(request)).setUserAgent(request.getHeader("User-Agent")));
|
||||||
|
// 异步检查是否异地登录
|
||||||
|
sessionService.checkRemoteLogin(userRoles.getUid());
|
||||||
return CommonResult.successResponse(MapUtil.builder()
|
return CommonResult.successResponse(MapUtil.builder()
|
||||||
.put("uid", userRoles.getUid())
|
.put("uid", userRoles.getUid())
|
||||||
.put("username", userRoles.getUsername())
|
.put("username", userRoles.getUsername())
|
||||||
|
|
|
@ -382,6 +382,7 @@ public class AdminContestController {
|
||||||
public CommonResult setContestProblem(@RequestBody ContestProblem contestProblem) {
|
public CommonResult setContestProblem(@RequestBody ContestProblem contestProblem) {
|
||||||
boolean result = contestProblemService.saveOrUpdate(contestProblem);
|
boolean result = contestProblemService.saveOrUpdate(contestProblem);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
contestProblemService.syncContestRecord(contestProblem.getPid(), contestProblem.getCid(), contestProblem.getDisplayId());
|
||||||
return CommonResult.successResponse(contestProblem, "更新成功!");
|
return CommonResult.successResponse(contestProblem, "更新成功!");
|
||||||
} else {
|
} else {
|
||||||
return CommonResult.errorResponse("更新失败", CommonResult.STATUS_FAIL);
|
return CommonResult.errorResponse("更新失败", CommonResult.STATUS_FAIL);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.core.util.RandomUtil;
|
import cn.hutool.core.util.RandomUtil;
|
||||||
import cn.hutool.crypto.SecureUtil;
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||||
|
@ -17,13 +18,20 @@ import top.hcode.hoj.pojo.entity.UserInfo;
|
||||||
import top.hcode.hoj.pojo.entity.UserRecord;
|
import top.hcode.hoj.pojo.entity.UserRecord;
|
||||||
import top.hcode.hoj.pojo.entity.UserRole;
|
import top.hcode.hoj.pojo.entity.UserRole;
|
||||||
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||||
|
import top.hcode.hoj.service.AdminSysNoticeService;
|
||||||
import top.hcode.hoj.service.UserInfoService;
|
import top.hcode.hoj.service.UserInfoService;
|
||||||
import top.hcode.hoj.service.UserRecordService;
|
import top.hcode.hoj.service.UserRecordService;
|
||||||
import top.hcode.hoj.service.impl.UserRoleServiceImpl;
|
import top.hcode.hoj.service.impl.UserRoleServiceImpl;
|
||||||
import top.hcode.hoj.utils.RedisUtils;
|
import top.hcode.hoj.utils.RedisUtils;
|
||||||
import top.hcode.hoj.utils.ShiroUtils;
|
import top.hcode.hoj.utils.ShiroUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static oshi.util.GlobalConfig.set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author: Himit_ZH
|
* @Author: Himit_ZH
|
||||||
|
@ -46,6 +54,9 @@ public class AdminUserController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtils redisUtils;
|
private RedisUtils redisUtils;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AdminSysNoticeService adminSysNoticeService;
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/get-user-list")
|
@GetMapping("/get-user-list")
|
||||||
@RequiresAuthentication
|
@RequiresAuthentication
|
||||||
|
@ -72,7 +83,8 @@ public class AdminUserController {
|
||||||
@RequiresPermissions("user_admin")
|
@RequiresPermissions("user_admin")
|
||||||
@RequiresAuthentication
|
@RequiresAuthentication
|
||||||
@Transactional
|
@Transactional
|
||||||
public CommonResult editUser(@RequestBody Map<String, Object> params) {
|
public CommonResult editUser(@RequestBody Map<String, Object> params,
|
||||||
|
HttpServletRequest request) {
|
||||||
String username = (String) params.get("username");
|
String username = (String) params.get("username");
|
||||||
String uid = (String) params.get("uid");
|
String uid = (String) params.get("uid");
|
||||||
String realname = (String) params.get("realname");
|
String realname = (String) params.get("realname");
|
||||||
|
@ -91,9 +103,15 @@ public class AdminUserController {
|
||||||
.set("status", status);
|
.set("status", status);
|
||||||
boolean result1 = userInfoService.update(updateWrapper1);
|
boolean result1 = userInfoService.update(updateWrapper1);
|
||||||
|
|
||||||
UpdateWrapper<UserRole> updateWrapper2 = new UpdateWrapper<>();
|
QueryWrapper<UserRole> userRoleQueryWrapper = new QueryWrapper<>();
|
||||||
updateWrapper2.eq("uid", uid).set("role_id", type);
|
userRoleQueryWrapper.eq("uid", uid);
|
||||||
boolean result2 = userRoleService.update(updateWrapper2);
|
UserRole userRole = userRoleService.getOne(userRoleQueryWrapper, false);
|
||||||
|
boolean result2 = false;
|
||||||
|
int oldType = userRole.getRoleId().intValue();
|
||||||
|
if (userRole.getRoleId().intValue() != type) {
|
||||||
|
userRole.setRoleId(Long.valueOf(type));
|
||||||
|
result2 = userRoleService.updateById(userRole);
|
||||||
|
}
|
||||||
if (result1) {
|
if (result1) {
|
||||||
// 需要重新登录
|
// 需要重新登录
|
||||||
userRoleService.deleteCache(uid, true);
|
userRoleService.deleteCache(uid, true);
|
||||||
|
@ -102,10 +120,16 @@ public class AdminUserController {
|
||||||
userRoleService.deleteCache(uid, false);
|
userRoleService.deleteCache(uid, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result1 && result2) {
|
if (result2) {
|
||||||
return CommonResult.successResponse(null, "修改成功!");
|
// 获取当前登录的用户
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||||
|
String title = "权限变更通知(Authority Change Notice)";
|
||||||
|
String content = userRoleService.getAuthChangeContent(oldType, type);
|
||||||
|
adminSysNoticeService.addSingleNoticeToUser(userRolesVo.getUid(), uid, title, content, "Sys");
|
||||||
}
|
}
|
||||||
return CommonResult.errorResponse("修改失败!");
|
|
||||||
|
return CommonResult.successResponse(null, "修改成功!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/delete-user")
|
@DeleteMapping("/delete-user")
|
||||||
|
@ -148,6 +172,9 @@ public class AdminUserController {
|
||||||
boolean result2 = userRoleService.saveBatch(userRoleList);
|
boolean result2 = userRoleService.saveBatch(userRoleList);
|
||||||
boolean result3 = userRecordService.saveBatch(userRecordList);
|
boolean result3 = userRecordService.saveBatch(userRecordList);
|
||||||
if (result1 && result2 && result3) {
|
if (result1 && result2 && result3) {
|
||||||
|
// 异步同步系统通知
|
||||||
|
List<String> uidList = userInfoList.stream().map(UserInfo::getUuid).collect(Collectors.toList());
|
||||||
|
adminSysNoticeService.syncNoticeToNewRegisterBatchUser(uidList);
|
||||||
return CommonResult.successResponse(null, "添加成功!");
|
return CommonResult.successResponse(null, "添加成功!");
|
||||||
} else {
|
} else {
|
||||||
return CommonResult.errorResponse("删除失败");
|
return CommonResult.errorResponse("删除失败");
|
||||||
|
@ -193,6 +220,9 @@ public class AdminUserController {
|
||||||
if (result1 && result2 && result3) {
|
if (result1 && result2 && result3) {
|
||||||
String key = IdUtil.simpleUUID();
|
String key = IdUtil.simpleUUID();
|
||||||
redisUtils.hmset(key, userInfo, 1800); // 存储半小时
|
redisUtils.hmset(key, userInfo, 1800); // 存储半小时
|
||||||
|
// 异步同步系统通知
|
||||||
|
List<String> uidList = userInfoList.stream().map(UserInfo::getUuid).collect(Collectors.toList());
|
||||||
|
adminSysNoticeService.syncNoticeToNewRegisterBatchUser(uidList);
|
||||||
return CommonResult.successResponse(MapUtil.builder()
|
return CommonResult.successResponse(MapUtil.builder()
|
||||||
.put("key", key).map(), "生成指定用户成功!");
|
.put("key", key).map(), "生成指定用户成功!");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
package top.hcode.hoj.controller.msg;
|
||||||
|
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import top.hcode.hoj.common.result.CommonResult;
|
||||||
|
import top.hcode.hoj.pojo.entity.AdminSysNotice;
|
||||||
|
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||||
|
import top.hcode.hoj.service.impl.AdminSysNoticeServiceImpl;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:38
|
||||||
|
* @Description: 负责管理员发送系统通知
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/admin/msg")
|
||||||
|
public class AdminSysNoticeController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AdminSysNoticeServiceImpl adminSysNoticeService;
|
||||||
|
|
||||||
|
@GetMapping("/notice")
|
||||||
|
@RequiresAuthentication
|
||||||
|
@RequiresRoles("root")
|
||||||
|
public CommonResult getSysNotice(@RequestParam(value = "limit", required = false) Integer limit,
|
||||||
|
@RequestParam(value = "currentPage", required = false) Integer currentPage,
|
||||||
|
@RequestParam(value = "type", required = false) String type) {
|
||||||
|
|
||||||
|
// 页数,每页题数若为空,设置默认值
|
||||||
|
if (currentPage == null || currentPage < 1) currentPage = 1;
|
||||||
|
if (limit == null || limit < 1) limit = 5;
|
||||||
|
|
||||||
|
return CommonResult.successResponse(adminSysNoticeService.getSysNotice(limit, currentPage, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/notice")
|
||||||
|
@RequiresAuthentication
|
||||||
|
@RequiresRoles("root")
|
||||||
|
public CommonResult addSysNotice(@RequestBody AdminSysNotice adminSysNotice) {
|
||||||
|
|
||||||
|
boolean isOk = adminSysNoticeService.saveOrUpdate(adminSysNotice);
|
||||||
|
if (isOk) {
|
||||||
|
return CommonResult.successResponse("发布成功");
|
||||||
|
} else {
|
||||||
|
return CommonResult.errorResponse("发布失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@DeleteMapping("/notice")
|
||||||
|
@RequiresAuthentication
|
||||||
|
@RequiresRoles("root")
|
||||||
|
public CommonResult deleteSysNotice(@RequestParam("id") Long id) {
|
||||||
|
boolean isOk = adminSysNoticeService.removeById(id);
|
||||||
|
if (isOk) {
|
||||||
|
return CommonResult.successResponse("删除成功");
|
||||||
|
} else {
|
||||||
|
return CommonResult.errorResponse("删除失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PutMapping("/notice")
|
||||||
|
@RequiresAuthentication
|
||||||
|
@RequiresRoles("root")
|
||||||
|
public CommonResult updateSysNotice(@RequestBody AdminSysNotice adminSysNotice) {
|
||||||
|
boolean isOk = adminSysNoticeService.saveOrUpdate(adminSysNotice);
|
||||||
|
if (isOk) {
|
||||||
|
return CommonResult.successResponse("更新成功");
|
||||||
|
} else {
|
||||||
|
return CommonResult.errorResponse("更新失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
package top.hcode.hoj.controller.msg;
|
||||||
|
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import top.hcode.hoj.common.result.CommonResult;
|
||||||
|
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||||
|
import top.hcode.hoj.pojo.vo.UserUnreadMsgCountVo;
|
||||||
|
import top.hcode.hoj.service.impl.MsgRemindServiceImpl;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:40
|
||||||
|
* @Description: 获取用户 评论我的、回复我的、收到的赞的消息
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/msg")
|
||||||
|
public class MsgRemindController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MsgRemindServiceImpl msgRemindService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @MethodName getUnreadMsgCount
|
||||||
|
* @Description 获取用户的未读消息数量,包括评论我的、回复我的、收到的赞、系统通知、我的消息
|
||||||
|
* @Return
|
||||||
|
* @Since 2021/10/1
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/unread", method = RequestMethod.GET)
|
||||||
|
@RequiresAuthentication
|
||||||
|
public CommonResult getUnreadMsgCount(HttpServletRequest request) {
|
||||||
|
// 获取当前登录的用户
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||||
|
UserUnreadMsgCountVo userUnreadMsgCount = msgRemindService.getUserUnreadMsgCount(userRolesVo.getUid());
|
||||||
|
if (userUnreadMsgCount == null) {
|
||||||
|
userUnreadMsgCount = new UserUnreadMsgCountVo(0, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
return CommonResult.successResponse(userUnreadMsgCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param type Discuss Reply Like Sys Mine
|
||||||
|
* @param request
|
||||||
|
* @MethodName cleanMsg
|
||||||
|
* @Description 根据type,清空各个消息模块的消息或单个消息
|
||||||
|
* @Return
|
||||||
|
* @Since 2021/10/3
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/clean", method = RequestMethod.DELETE)
|
||||||
|
@RequiresAuthentication
|
||||||
|
public CommonResult cleanMsg(@RequestParam("type") String type,
|
||||||
|
@RequestParam(value = "id", required = false) Long id,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
// 获取当前登录的用户
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||||
|
boolean isOk = msgRemindService.cleanMsgByType(type, id, userRolesVo.getUid());
|
||||||
|
if (isOk) {
|
||||||
|
return CommonResult.successResponse(null, "清空全部成功");
|
||||||
|
} else {
|
||||||
|
return CommonResult.errorResponse("清空失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param limit
|
||||||
|
* @param currentPage
|
||||||
|
* @MethodName getCommentMsg
|
||||||
|
* @Description 获取评论我的讨论贴的消息,按未读的在前、时间晚的在前进行排序
|
||||||
|
* @Return
|
||||||
|
* @Since 2021/10/1
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/comment", method = RequestMethod.GET)
|
||||||
|
@RequiresAuthentication
|
||||||
|
public CommonResult getCommentMsg(@RequestParam(value = "limit", required = false) Integer limit,
|
||||||
|
@RequestParam(value = "currentPage", required = false) Integer currentPage,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
// 页数,每页题数若为空,设置默认值
|
||||||
|
if (currentPage == null || currentPage < 1) currentPage = 1;
|
||||||
|
if (limit == null || limit < 1) limit = 5;
|
||||||
|
// 获取当前登录的用户
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||||
|
|
||||||
|
return CommonResult.successResponse(msgRemindService.getUserMsgList(userRolesVo.getUid(), "Discuss", limit, currentPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param limit
|
||||||
|
* @param currentPage
|
||||||
|
* @MethodName getReplyMsg
|
||||||
|
* @Description 获取回复我的评论的消息,按未读的在前、时间晚的在前进行排序
|
||||||
|
* @Return
|
||||||
|
* @Since 2021/10/1
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/reply", method = RequestMethod.GET)
|
||||||
|
@RequiresAuthentication
|
||||||
|
public CommonResult getReplyMsg(@RequestParam(value = "limit", required = false) Integer limit,
|
||||||
|
@RequestParam(value = "currentPage", required = false) Integer currentPage,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
// 页数,每页题数若为空,设置默认值
|
||||||
|
if (currentPage == null || currentPage < 1) currentPage = 1;
|
||||||
|
if (limit == null || limit < 1) limit = 5;
|
||||||
|
|
||||||
|
// 获取当前登录的用户
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||||
|
|
||||||
|
return CommonResult.successResponse(msgRemindService.getUserMsgList(userRolesVo.getUid(), "Reply", limit, currentPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param limit
|
||||||
|
* @param currentPage
|
||||||
|
* @MethodName getLikeMsg
|
||||||
|
* @Description 获取点赞我的的消息,按未读的在前、时间晚的在前进行排序
|
||||||
|
* @Return
|
||||||
|
* @Since 2021/10/1
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/like", method = RequestMethod.GET)
|
||||||
|
@RequiresAuthentication
|
||||||
|
public CommonResult getLikeMsg(@RequestParam(value = "limit", required = false) Integer limit,
|
||||||
|
@RequestParam(value = "currentPage", required = false) Integer currentPage,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
// 页数,每页题数若为空,设置默认值
|
||||||
|
if (currentPage == null || currentPage < 1) currentPage = 1;
|
||||||
|
if (limit == null || limit < 1) limit = 5;
|
||||||
|
|
||||||
|
// 获取当前登录的用户
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||||
|
|
||||||
|
return CommonResult.successResponse(msgRemindService.getUserMsgList(userRolesVo.getUid(), "Like", limit, currentPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package top.hcode.hoj.controller.msg;
|
||||||
|
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import top.hcode.hoj.common.result.CommonResult;
|
||||||
|
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||||
|
import top.hcode.hoj.service.impl.UserSysNoticeServiceImpl;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:42
|
||||||
|
* @Description: 负责用户的 系统消息模块、我的消息模块
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/msg")
|
||||||
|
public class UserSysNoticeController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserSysNoticeServiceImpl userSysNoticeService;
|
||||||
|
|
||||||
|
@RequestMapping(value = "/sys", method = RequestMethod.GET)
|
||||||
|
@RequiresAuthentication
|
||||||
|
public CommonResult getSysNotice(@RequestParam(value = "limit", required = false) Integer limit,
|
||||||
|
@RequestParam(value = "currentPage", required = false) Integer currentPage,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
// 页数,每页题数若为空,设置默认值
|
||||||
|
if (currentPage == null || currentPage < 1) currentPage = 1;
|
||||||
|
if (limit == null || limit < 1) limit = 5;
|
||||||
|
// 获取当前登录的用户
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||||
|
|
||||||
|
return CommonResult.successResponse(userSysNoticeService.getSysNotice(limit, currentPage, userRolesVo.getUid()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@RequestMapping(value = "/mine", method = RequestMethod.GET)
|
||||||
|
@RequiresAuthentication
|
||||||
|
public CommonResult getMineNotice(@RequestParam(value = "limit", required = false) Integer limit,
|
||||||
|
@RequestParam(value = "currentPage", required = false) Integer currentPage,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
// 页数,每页题数若为空,设置默认值
|
||||||
|
if (currentPage == null || currentPage < 1) currentPage = 1;
|
||||||
|
if (limit == null || limit < 1) limit = 5;
|
||||||
|
// 获取当前登录的用户
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||||
|
|
||||||
|
return CommonResult.successResponse(userSysNoticeService.getMineNotice(limit, currentPage, userRolesVo.getUid()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import top.hcode.hoj.utils.IpUtils;
|
||||||
import top.hcode.hoj.utils.JwtUtils;
|
import top.hcode.hoj.utils.JwtUtils;
|
||||||
import top.hcode.hoj.utils.RedisUtils;
|
import top.hcode.hoj.utils.RedisUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
@ -72,11 +73,14 @@ public class AccountController {
|
||||||
private ProblemServiceImpl problemService;
|
private ProblemServiceImpl problemService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SessionMapper sessionDao;
|
private SessionServiceImpl sessionService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ConfigVo configVo;
|
private ConfigVo configVo;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AdminSysNoticeServiceImpl adminSysNoticeService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @MethodName getRegisterCode
|
* @MethodName getRegisterCode
|
||||||
* @Params * @param null
|
* @Params * @param null
|
||||||
|
@ -89,7 +93,7 @@ public class AccountController {
|
||||||
if (!configVo.getRegister()) { // 需要判断一下网站是否开启注册
|
if (!configVo.getRegister()) { // 需要判断一下网站是否开启注册
|
||||||
return CommonResult.errorResponse("对不起!本站暂未开启注册功能!", CommonResult.STATUS_ACCESS_DENIED);
|
return CommonResult.errorResponse("对不起!本站暂未开启注册功能!", CommonResult.STATUS_ACCESS_DENIED);
|
||||||
}
|
}
|
||||||
if (!emailService.isOk()){
|
if (!emailService.isOk()) {
|
||||||
return CommonResult.errorResponse("对不起!本站邮箱系统未配置,暂不支持注册!", CommonResult.STATUS_ACCESS_DENIED);
|
return CommonResult.errorResponse("对不起!本站邮箱系统未配置,暂不支持注册!", CommonResult.STATUS_ACCESS_DENIED);
|
||||||
}
|
}
|
||||||
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
|
||||||
|
@ -164,7 +168,7 @@ public class AccountController {
|
||||||
if (StringUtils.isEmpty(captcha) || StringUtils.isEmpty(email) || StringUtils.isEmpty(captchaKey)) {
|
if (StringUtils.isEmpty(captcha) || StringUtils.isEmpty(email) || StringUtils.isEmpty(captchaKey)) {
|
||||||
return CommonResult.errorResponse("邮箱或验证码不能为空");
|
return CommonResult.errorResponse("邮箱或验证码不能为空");
|
||||||
}
|
}
|
||||||
if (!emailService.isOk()){
|
if (!emailService.isOk()) {
|
||||||
return CommonResult.errorResponse("对不起!本站邮箱系统未配置,暂不支持重置密码!", CommonResult.STATUS_ACCESS_DENIED);
|
return CommonResult.errorResponse("对不起!本站邮箱系统未配置,暂不支持重置密码!", CommonResult.STATUS_ACCESS_DENIED);
|
||||||
}
|
}
|
||||||
// 获取redis中的验证码
|
// 获取redis中的验证码
|
||||||
|
@ -264,6 +268,7 @@ public class AccountController {
|
||||||
int result3 = userRecordDao.insert(new UserRecord().setUid(uuid));
|
int result3 = userRecordDao.insert(new UserRecord().setUid(uuid));
|
||||||
if (result1 == 1 && result2 == 1 && result3 == 1) {
|
if (result1 == 1 && result2 == 1 && result3 == 1) {
|
||||||
redisUtils.del(registerDto.getEmail());
|
redisUtils.del(registerDto.getEmail());
|
||||||
|
adminSysNoticeService.syncNoticeToNewRegisterUser(uuid);
|
||||||
return CommonResult.successResponse(null, "谢谢你的注册!");
|
return CommonResult.successResponse(null, "谢谢你的注册!");
|
||||||
} else {
|
} else {
|
||||||
return CommonResult.errorResponse("注册失败!", CommonResult.STATUS_ERROR); // 插入数据库失败,返回500
|
return CommonResult.errorResponse("注册失败!", CommonResult.STATUS_ERROR); // 插入数据库失败,返回500
|
||||||
|
@ -304,10 +309,12 @@ public class AccountController {
|
||||||
response.setHeader("Access-Control-Expose-Headers", "Authorization");
|
response.setHeader("Access-Control-Expose-Headers", "Authorization");
|
||||||
|
|
||||||
// 会话记录
|
// 会话记录
|
||||||
sessionDao.insert(new Session()
|
sessionService.save(new Session()
|
||||||
.setUid(userRoles.getUid())
|
.setUid(userRoles.getUid())
|
||||||
.setIp(IpUtils.getUserIpAddr(request))
|
.setIp(IpUtils.getUserIpAddr(request))
|
||||||
.setUserAgent(request.getHeader("User-Agent")));
|
.setUserAgent(request.getHeader("User-Agent")));
|
||||||
|
// 异步检查是否异地登录
|
||||||
|
sessionService.checkRemoteLogin(userRoles.getUid());
|
||||||
|
|
||||||
return CommonResult.successResponse(MapUtil.builder()
|
return CommonResult.successResponse(MapUtil.builder()
|
||||||
.put("uid", userRoles.getUid())
|
.put("uid", userRoles.getUid())
|
||||||
|
@ -352,7 +359,8 @@ public class AccountController {
|
||||||
* @Since 2021/01/07
|
* @Since 2021/01/07
|
||||||
*/
|
*/
|
||||||
@GetMapping("/get-user-home-info")
|
@GetMapping("/get-user-home-info")
|
||||||
public CommonResult getUserHomeInfo(@RequestParam(value = "uid", required = false) String uid, HttpServletRequest request) {
|
public CommonResult getUserHomeInfo(@RequestParam(value = "uid", required = false) String uid,
|
||||||
|
HttpServletRequest request) {
|
||||||
HttpSession session = request.getSession();
|
HttpSession session = request.getSession();
|
||||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||||
// 如果没有uid,默认查询当前登录用户的
|
// 如果没有uid,默认查询当前登录用户的
|
||||||
|
@ -360,6 +368,9 @@ public class AccountController {
|
||||||
uid = userRolesVo.getUid();
|
uid = userRolesVo.getUid();
|
||||||
}
|
}
|
||||||
UserHomeVo userHomeInfo = userRecordDao.getUserHomeInfo(uid);
|
UserHomeVo userHomeInfo = userRecordDao.getUserHomeInfo(uid);
|
||||||
|
if (userHomeInfo == null) {
|
||||||
|
return CommonResult.errorResponse("用户不存在");
|
||||||
|
}
|
||||||
QueryWrapper<UserAcproblem> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<UserAcproblem> queryWrapper = new QueryWrapper<>();
|
||||||
queryWrapper.eq("uid", uid).select("distinct pid");
|
queryWrapper.eq("uid", uid).select("distinct pid");
|
||||||
List<Long> pidList = new LinkedList<>();
|
List<Long> pidList = new LinkedList<>();
|
||||||
|
@ -383,7 +394,7 @@ public class AccountController {
|
||||||
QueryWrapper<Session> sessionQueryWrapper = new QueryWrapper<>();
|
QueryWrapper<Session> sessionQueryWrapper = new QueryWrapper<>();
|
||||||
sessionQueryWrapper.eq("uid", uid).orderByDesc("gmt_create").last("limit 1");
|
sessionQueryWrapper.eq("uid", uid).orderByDesc("gmt_create").last("limit 1");
|
||||||
|
|
||||||
Session recentSession = sessionDao.selectOne(sessionQueryWrapper);
|
Session recentSession = sessionService.getOne(sessionQueryWrapper, false);
|
||||||
if (recentSession != null) {
|
if (recentSession != null) {
|
||||||
userHomeInfo.setRecentLoginTime(recentSession.getGmtCreate());
|
userHomeInfo.setRecentLoginTime(recentSession.getGmtCreate());
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,7 @@ public class CommentController {
|
||||||
@PostMapping("/comment")
|
@PostMapping("/comment")
|
||||||
@RequiresPermissions("comment_add")
|
@RequiresPermissions("comment_add")
|
||||||
@RequiresAuthentication
|
@RequiresAuthentication
|
||||||
|
@Transactional
|
||||||
public CommonResult addComment(@RequestBody Comment comment, HttpServletRequest request) {
|
public CommonResult addComment(@RequestBody Comment comment, HttpServletRequest request) {
|
||||||
// 获取当前登录的用户
|
// 获取当前登录的用户
|
||||||
HttpSession session = request.getSession();
|
HttpSession session = request.getSession();
|
||||||
|
@ -137,10 +138,17 @@ public class CommentController {
|
||||||
commentsVo.setReplyList(new LinkedList<>());
|
commentsVo.setReplyList(new LinkedList<>());
|
||||||
// 如果是讨论区的回复,发布成功需要添加统计该讨论的回复数
|
// 如果是讨论区的回复,发布成功需要添加统计该讨论的回复数
|
||||||
if (comment.getDid() != null) {
|
if (comment.getDid() != null) {
|
||||||
UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
|
Discussion discussion = discussionService.getById(comment.getDid());
|
||||||
discussionUpdateWrapper.eq("id", comment.getDid())
|
if (discussion != null) {
|
||||||
.setSql("comment_num=comment_num+1");
|
discussion.setCommentNum(discussion.getCommentNum() + 1);
|
||||||
discussionService.update(discussionUpdateWrapper);
|
discussionService.updateById(discussion);
|
||||||
|
// 更新消息
|
||||||
|
commentService.updateCommentMsg(discussion.getUid(),
|
||||||
|
userRolesVo.getUid(),
|
||||||
|
comment.getContent(),
|
||||||
|
comment.getDid());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return CommonResult.successResponse(commentsVo, "评论成功");
|
return CommonResult.successResponse(commentsVo, "评论成功");
|
||||||
} else {
|
} else {
|
||||||
|
@ -192,6 +200,8 @@ public class CommentController {
|
||||||
@Transactional
|
@Transactional
|
||||||
public CommonResult addDiscussionLike(@RequestParam("cid") Integer cid,
|
public CommonResult addDiscussionLike(@RequestParam("cid") Integer cid,
|
||||||
@RequestParam("toLike") Boolean toLike,
|
@RequestParam("toLike") Boolean toLike,
|
||||||
|
@RequestParam("sourceId") Integer sourceId,
|
||||||
|
@RequestParam("sourceType") String sourceType,
|
||||||
HttpServletRequest request) {
|
HttpServletRequest request) {
|
||||||
|
|
||||||
// 获取当前登录的用户
|
// 获取当前登录的用户
|
||||||
|
@ -213,9 +223,12 @@ public class CommentController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 点赞+1
|
// 点赞+1
|
||||||
UpdateWrapper<Comment> commentUpdateWrapper = new UpdateWrapper<>();
|
Comment comment = commentService.getById(cid);
|
||||||
commentUpdateWrapper.setSql("like_num=like_num+1").eq("id", cid);
|
if (comment != null) {
|
||||||
commentService.update(commentUpdateWrapper);
|
comment.setLikeNum(comment.getLikeNum() + 1);
|
||||||
|
commentService.updateById(comment);
|
||||||
|
commentService.updateCommentLikeMsg(comment.getFromUid(), userRolesVo.getUid(), sourceId, sourceType);
|
||||||
|
}
|
||||||
return CommonResult.successResponse(null, "点赞成功");
|
return CommonResult.successResponse(null, "点赞成功");
|
||||||
} else { // 取消点赞
|
} else { // 取消点赞
|
||||||
if (commentLike != null) { // 如果存在就删除
|
if (commentLike != null) { // 如果存在就删除
|
||||||
|
@ -280,6 +293,14 @@ public class CommentController {
|
||||||
discussionUpdateWrapper.eq("id", replyDto.getDid())
|
discussionUpdateWrapper.eq("id", replyDto.getDid())
|
||||||
.setSql("comment_num=comment_num+1");
|
.setSql("comment_num=comment_num+1");
|
||||||
discussionService.update(discussionUpdateWrapper);
|
discussionService.update(discussionUpdateWrapper);
|
||||||
|
// 更新消息
|
||||||
|
replyService.updateReplyMsg(replyDto.getDid(),
|
||||||
|
"Discussion",
|
||||||
|
reply.getContent(),
|
||||||
|
replyDto.getQuoteId(),
|
||||||
|
replyDto.getQuoteType(),
|
||||||
|
reply.getToUid(),
|
||||||
|
reply.getFromUid());
|
||||||
}
|
}
|
||||||
return CommonResult.successResponse(reply, "评论成功");
|
return CommonResult.successResponse(reply, "评论成功");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -300,10 +300,16 @@ public class ContestController {
|
||||||
return CommonResult.errorResponse("该比赛题目当前不可访问!", CommonResult.STATUS_FORBIDDEN);
|
return CommonResult.errorResponse("该比赛题目当前不可访问!", CommonResult.STATUS_FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置比赛题目的标题为设置展示标题
|
||||||
|
problem.setTitle(contestProblem.getDisplayTitle());
|
||||||
|
|
||||||
List<String> tagsStr = new LinkedList<>();
|
List<String> tagsStr = new LinkedList<>();
|
||||||
// 比赛结束后才开放标签和source
|
|
||||||
|
// 比赛结束后才开放标签和source、出题人、难度
|
||||||
if (contest.getStatus().intValue() != Constants.Contest.STATUS_ENDED.getCode()) {
|
if (contest.getStatus().intValue() != Constants.Contest.STATUS_ENDED.getCode()) {
|
||||||
problem.setSource(null);
|
problem.setSource(null);
|
||||||
|
problem.setAuthor(null);
|
||||||
|
problem.setDifficulty(null);
|
||||||
QueryWrapper<ProblemTag> problemTagQueryWrapper = new QueryWrapper<>();
|
QueryWrapper<ProblemTag> problemTagQueryWrapper = new QueryWrapper<>();
|
||||||
problemTagQueryWrapper.eq("pid", contestProblem.getPid());
|
problemTagQueryWrapper.eq("pid", contestProblem.getPid());
|
||||||
// 获取该题号对应的标签id
|
// 获取该题号对应的标签id
|
||||||
|
@ -361,11 +367,6 @@ public class ContestController {
|
||||||
LangNameAndCode.put(tmpMap.get(codeTemplate.getLid()), codeTemplate.getCode());
|
LangNameAndCode.put(tmpMap.get(codeTemplate.getLid()), codeTemplate.getCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (DateUtil.isIn(now, contest.getStartTime(), contest.getEndTime())) {
|
|
||||||
// 比赛过程中隐藏出题人
|
|
||||||
problem.setAuthor(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将数据统一写入到一个Vo返回数据实体类中
|
// 将数据统一写入到一个Vo返回数据实体类中
|
||||||
ProblemInfoVo problemInfoVo = new ProblemInfoVo(problem, tagsStr, languagesStr, problemCount, LangNameAndCode);
|
ProblemInfoVo problemInfoVo = new ProblemInfoVo(problem, tagsStr, languagesStr, problemCount, LangNameAndCode);
|
||||||
return CommonResult.successResponse(problemInfoVo, "获取成功");
|
return CommonResult.successResponse(problemInfoVo, "获取成功");
|
||||||
|
|
|
@ -227,9 +227,13 @@ public class DiscussionController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 点赞+1
|
// 点赞+1
|
||||||
UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
|
Discussion discussion = discussionService.getById(did);
|
||||||
discussionUpdateWrapper.setSql("like_num=like_num+1").eq("id", did);
|
if (discussion != null) {
|
||||||
discussionService.update(discussionUpdateWrapper);
|
discussion.setLikeNum(discussion.getLikeNum() + 1);
|
||||||
|
discussionService.updateById(discussion);
|
||||||
|
// 更新点赞消息
|
||||||
|
discussionService.updatePostLikeMsg(discussion.getUid(), userRolesVo.getUid(), did);
|
||||||
|
}
|
||||||
return CommonResult.successResponse(null, "点赞成功");
|
return CommonResult.successResponse(null, "点赞成功");
|
||||||
} else { // 取消点赞
|
} else { // 取消点赞
|
||||||
if (discussionLike != null) { // 如果存在就删除
|
if (discussionLike != null) { // 如果存在就删除
|
||||||
|
|
|
@ -15,7 +15,7 @@ import top.hcode.hoj.utils.JsoupUtils;
|
||||||
*/
|
*/
|
||||||
public class HDUProblemStrategy extends ProblemStrategy {
|
public class HDUProblemStrategy extends ProblemStrategy {
|
||||||
public static final String JUDGE_NAME = "HDU";
|
public static final String JUDGE_NAME = "HDU";
|
||||||
public static final String HOST = "https://acm.hdu.edu.cn";
|
public static final String HOST = "https://acm.dingbacode.com";
|
||||||
public static final String PROBLEM_URL = "/showproblem.php?pid=%s";
|
public static final String PROBLEM_URL = "/showproblem.php?pid=%s";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package top.hcode.hoj.dao;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import top.hcode.hoj.pojo.entity.AdminSysNotice;
|
||||||
|
import top.hcode.hoj.pojo.vo.AdminSysNoticeVo;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
@Repository
|
||||||
|
public interface AdminSysNoticeMapper extends BaseMapper<AdminSysNotice> {
|
||||||
|
IPage<AdminSysNoticeVo> getAdminSysNotice(Page<AdminSysNoticeVo> page, @Param("type") String type);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package top.hcode.hoj.dao;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import top.hcode.hoj.pojo.entity.MsgRemind;
|
||||||
|
import top.hcode.hoj.pojo.vo.UserMsgVo;
|
||||||
|
import top.hcode.hoj.pojo.vo.UserUnreadMsgCountVo;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
@Repository
|
||||||
|
public interface MsgRemindMapper extends BaseMapper<MsgRemind> {
|
||||||
|
UserUnreadMsgCountVo getUserUnreadMsgCount(@Param("uid") String uid);
|
||||||
|
|
||||||
|
IPage<UserMsgVo> getUserMsg(Page<UserMsgVo> page, @Param("uid") String uid,
|
||||||
|
@Param("action") String action);
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package top.hcode.hoj.dao;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import top.hcode.hoj.pojo.entity.UserSysNotice;
|
||||||
|
import top.hcode.hoj.pojo.vo.SysMsgVo;
|
||||||
|
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
@Repository
|
||||||
|
public interface UserSysNoticeMapper extends BaseMapper<UserSysNotice> {
|
||||||
|
IPage<SysMsgVo> getSysOrMineNotice(Page<SysMsgVo> page, @Param("uid") String uid, @Param("type") String type);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="top.hcode.hoj.dao.AdminSysNoticeMapper">
|
||||||
|
|
||||||
|
<resultMap id="map_AdminSysNoticeList" type="top.hcode.hoj.pojo.vo.AdminSysNoticeVo">
|
||||||
|
<id column="id" property="id"></id>
|
||||||
|
<result column="title" property="title"></result>
|
||||||
|
<result column="content" property="content"></result>
|
||||||
|
<result column="type" property="type"></result>
|
||||||
|
<result column="state" property="state"></result>
|
||||||
|
<result column="username" property="adminUsername"></result>
|
||||||
|
<result column="gmt_create" property="gmtCreate"></result>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="getAdminSysNotice" resultMap="map_AdminSysNoticeList">
|
||||||
|
select
|
||||||
|
a.id as id,
|
||||||
|
a.title as title,
|
||||||
|
a.content as content,
|
||||||
|
a.type as type,
|
||||||
|
a.state as state,
|
||||||
|
a.gmt_create as gmt_create,
|
||||||
|
u.username as username
|
||||||
|
from admin_sys_notice a, user_info u
|
||||||
|
<where>
|
||||||
|
a.admin_id = u.uuid
|
||||||
|
<if test="type!=null">
|
||||||
|
and a.type = #{type}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
order by a.state asc, a.gmt_create desc
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="top.hcode.hoj.dao.MsgRemindMapper">
|
||||||
|
|
||||||
|
<select id="getUserUnreadMsgCount" resultType="top.hcode.hoj.pojo.vo.UserUnreadMsgCountVo" useCache="true">
|
||||||
|
SELECT
|
||||||
|
(SELECT COUNT(1) FROM msg_remind WHERE recipient_id=#{uid} AND state=0 AND `action`='Discuss') AS 'comment',
|
||||||
|
(SELECT COUNT(1) FROM msg_remind WHERE recipient_id=#{uid} AND state=0 AND `action`='Reply') AS 'reply',
|
||||||
|
(SELECT COUNT(1) FROM msg_remind WHERE recipient_id=#{uid} AND state=0 AND `action` LIKE 'Like%') AS 'like',
|
||||||
|
(SELECT COUNT(1) FROM user_sys_notice WHERE recipient_id=#{uid} AND state=0 AND `type`='Sys') AS 'sys',
|
||||||
|
(SELECT COUNT(1) FROM user_sys_notice WHERE recipient_id=#{uid} AND state=0 AND `type`='Mine') AS 'mine'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
<resultMap id="map_UserMsgList" type="top.hcode.hoj.pojo.vo.UserMsgVo">
|
||||||
|
<id column="id" property="id"></id>
|
||||||
|
<result column="sender_id" property="senderId"></result>
|
||||||
|
<result column="action" property="action"></result>
|
||||||
|
<result column="source_type" property="sourceType"></result>
|
||||||
|
<result column="source_id" property="sourceId"></result>
|
||||||
|
<result column="source_content" property="sourceContent"></result>
|
||||||
|
<result column="quote_id" property="quoteId"></result>
|
||||||
|
<result column="quote_type" property="quoteType"></result>
|
||||||
|
<result column="url" property="url"></result>
|
||||||
|
<result column="state" property="state"></result>
|
||||||
|
<result column="gmt_create" property="gmtCreate"></result>
|
||||||
|
<result column="username" property="senderUsername"></result>
|
||||||
|
<result column="avatar" property="senderAvatar"></result>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="getUserMsg" resultMap="map_UserMsgList">
|
||||||
|
select
|
||||||
|
m.id as id,
|
||||||
|
m.sender_id as sender_id,
|
||||||
|
m.action as 'action',
|
||||||
|
m.source_id as source_id,
|
||||||
|
m.source_type as source_type,
|
||||||
|
m.source_content as source_content,
|
||||||
|
m.quote_id as quote_id,
|
||||||
|
m.quote_type as quote_type,
|
||||||
|
m.url as url,
|
||||||
|
m.state as state,
|
||||||
|
m.gmt_create as gmt_create,
|
||||||
|
u.username as username,
|
||||||
|
u.avatar as avatar
|
||||||
|
from msg_remind m,user_info u
|
||||||
|
<where>
|
||||||
|
m.sender_id = u.uuid
|
||||||
|
and m.recipient_id = #{uid}
|
||||||
|
<choose>
|
||||||
|
<when test="action == 'Like'">
|
||||||
|
and (m.action = 'Like_Post' OR m.action = 'Like_Discuss')
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
and m.action = #{action}
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
</where>
|
||||||
|
order by m.state asc, m.gmt_create desc
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="top.hcode.hoj.dao.UserSysNoticeMapper">
|
||||||
|
|
||||||
|
<resultMap id="map_SysMsgList" type="top.hcode.hoj.pojo.vo.SysMsgVo">
|
||||||
|
<id column="id" property="id"></id>
|
||||||
|
<result column="type" property="type"></result>
|
||||||
|
<result column="state" property="state"></result>
|
||||||
|
<result column="gmt_create" property="gmtCreate"></result>
|
||||||
|
<result column="title" property="title"></result>
|
||||||
|
<result column="content" property="content"></result>
|
||||||
|
<result column="admin_id" property="adminId"></result>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="getSysOrMineNotice" resultMap="map_SysMsgList">
|
||||||
|
select
|
||||||
|
u.id as id,
|
||||||
|
u.type as type,
|
||||||
|
u.state as state,
|
||||||
|
a.gmt_create as gmt_create,
|
||||||
|
a.title as title,
|
||||||
|
a.content as content,
|
||||||
|
a.admin_id as admin_id
|
||||||
|
from user_sys_notice u,admin_sys_notice a
|
||||||
|
<where>
|
||||||
|
u.sys_notice_id = a.id
|
||||||
|
<if test="uid!=null">
|
||||||
|
and u.recipient_id = #{uid}
|
||||||
|
</if>
|
||||||
|
<if test="type!=null">
|
||||||
|
and u.type = #{type}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
order by u.state asc,a.gmt_create desc
|
||||||
|
</select>
|
||||||
|
</mapper>
|
|
@ -15,5 +15,9 @@ public class ReplyDto {
|
||||||
|
|
||||||
private Reply reply;
|
private Reply reply;
|
||||||
|
|
||||||
private Long did;
|
private Integer did;
|
||||||
|
|
||||||
|
private Integer quoteId;
|
||||||
|
|
||||||
|
private String quoteType;
|
||||||
}
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package top.hcode.hoj.pojo.vo;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/4 14:03
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value="系统通知消息", description="")
|
||||||
|
public class AdminSysNoticeVo {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "通知标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "通知内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "发给哪些用户类型,例如全部用户All,指定单个用户Single,管理员Admin")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否已被拉取过,如果已经拉取过,就无需再次拉取")
|
||||||
|
private Boolean state;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "发布通知的管理员用户名")
|
||||||
|
private String adminUsername;
|
||||||
|
|
||||||
|
private Date gmtCreate;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package top.hcode.hoj.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/3 16:36
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
@ApiModel(value="用户的系统消息", description="")
|
||||||
|
@Data
|
||||||
|
public class SysMsgVo {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "通知标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "通知内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "发布通知的管理员id")
|
||||||
|
private String adminId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "消息类型,系统通知Sys、我的信息Mine")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否已读")
|
||||||
|
private Boolean state;
|
||||||
|
|
||||||
|
private Date gmtCreate;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package top.hcode.hoj.pojo.vo;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/2 20:50
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
@ApiModel(value="用户的讨论贴被评论的、被点赞、评论被回复的消息Vo", description="")
|
||||||
|
@Data
|
||||||
|
public class UserMsgVo {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "动作类型,如点赞讨论帖Like_Post、点赞评论Like_Discuss、评论Discuss、回复Reply等")
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "消息来源id,讨论id或比赛id")
|
||||||
|
private Integer sourceId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件源类型:'Discussion'、'Contest'等")
|
||||||
|
private String sourceType;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件源的标题,讨论帖子的标题或者比赛的标题")
|
||||||
|
private String sourceTitle;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件源的内容,比如回复的内容,回复的评论等等,不超过250字符,超过使用...")
|
||||||
|
private String sourceContent;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件引用上一级评论或回复id")
|
||||||
|
private Integer quoteId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件引用上一级的类型:Comment、Reply")
|
||||||
|
private String quoteType;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件引用上一级的内容,例如回复你的源评论内容")
|
||||||
|
private String quoteContent;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件所发生的地点链接 url")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否已读")
|
||||||
|
private Boolean state;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "动作发出者的uid")
|
||||||
|
private String senderId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "动作发出者的用户名")
|
||||||
|
private String senderUsername;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "动作发出者的头像")
|
||||||
|
private String senderAvatar;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date gmtCreate;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package top.hcode.hoj.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:59
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
@ApiModel(value="用户未读消息统计", description="")
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class UserUnreadMsgCountVo {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "未读评论")
|
||||||
|
private Integer comment;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "未读回复")
|
||||||
|
private Integer reply;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "未读点赞")
|
||||||
|
private Integer like;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "未读系统通知")
|
||||||
|
private Integer sys;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "未读我的消息")
|
||||||
|
private Integer mine;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package top.hcode.hoj.service;
|
package top.hcode.hoj.schedule;
|
||||||
|
|
||||||
public interface ScheduleService {
|
public interface ScheduleService {
|
||||||
void deleteAvatar();
|
void deleteAvatar();
|
||||||
|
@ -12,4 +12,6 @@ public interface ScheduleService {
|
||||||
void getCodeforcesRating();
|
void getCodeforcesRating();
|
||||||
|
|
||||||
void deleteUserSession();
|
void deleteUserSession();
|
||||||
|
|
||||||
|
void syncNoticeToRecentHalfYearUser();
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package top.hcode.hoj.service.impl;
|
package top.hcode.hoj.schedule;
|
||||||
|
|
||||||
import cn.hutool.core.date.DateTime;
|
import cn.hutool.core.date.DateTime;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
@ -12,25 +12,18 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.retry.annotation.Backoff;
|
import org.springframework.retry.annotation.Backoff;
|
||||||
import org.springframework.retry.annotation.Retryable;
|
import org.springframework.retry.annotation.Retryable;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import top.hcode.hoj.pojo.entity.File;
|
import top.hcode.hoj.pojo.entity.*;
|
||||||
import top.hcode.hoj.pojo.entity.Session;
|
|
||||||
import top.hcode.hoj.pojo.entity.UserInfo;
|
|
||||||
import top.hcode.hoj.pojo.entity.UserRecord;
|
|
||||||
import top.hcode.hoj.service.ScheduleService;
|
|
||||||
import top.hcode.hoj.service.UserInfoService;
|
import top.hcode.hoj.service.UserInfoService;
|
||||||
import top.hcode.hoj.service.UserRecordService;
|
import top.hcode.hoj.service.UserRecordService;
|
||||||
|
import top.hcode.hoj.service.impl.*;
|
||||||
import top.hcode.hoj.utils.Constants;
|
import top.hcode.hoj.utils.Constants;
|
||||||
import top.hcode.hoj.utils.JsoupUtils;
|
import top.hcode.hoj.utils.JsoupUtils;
|
||||||
import top.hcode.hoj.utils.RedisUtils;
|
import top.hcode.hoj.utils.RedisUtils;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.io.IOException;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,6 +69,13 @@ public class ScheduleServiceImpl implements ScheduleService {
|
||||||
@Resource
|
@Resource
|
||||||
private SessionServiceImpl sessionService;
|
private SessionServiceImpl sessionService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AdminSysNoticeServiceImpl adminSysNoticeService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserSysNoticeServiceImpl userSysNoticeService;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @MethodName deleteAvatar
|
* @MethodName deleteAvatar
|
||||||
* @Params * @param null
|
* @Params * @param null
|
||||||
|
@ -265,7 +265,7 @@ public class ScheduleServiceImpl implements ScheduleService {
|
||||||
/**
|
/**
|
||||||
* @MethodName deleteUserSession
|
* @MethodName deleteUserSession
|
||||||
* @Params * @param null
|
* @Params * @param null
|
||||||
* @Description 每天3点定时删除用户三个月前的session表记录
|
* @Description 每天3点定时删除用户半年的session表记录
|
||||||
* @Return
|
* @Return
|
||||||
* @Since 2021/9/6
|
* @Since 2021/9/6
|
||||||
*/
|
*/
|
||||||
|
@ -274,7 +274,7 @@ public class ScheduleServiceImpl implements ScheduleService {
|
||||||
public void deleteUserSession() {
|
public void deleteUserSession() {
|
||||||
QueryWrapper<Session> sessionQueryWrapper = new QueryWrapper<>();
|
QueryWrapper<Session> sessionQueryWrapper = new QueryWrapper<>();
|
||||||
|
|
||||||
DateTime dateTime = DateUtil.offsetMonth(new Date(), -3);
|
DateTime dateTime = DateUtil.offsetMonth(new Date(), -6);
|
||||||
String threeMonthsBeforeDate = dateTime.toString("yyyy-MM-dd HH:mm:ss");
|
String threeMonthsBeforeDate = dateTime.toString("yyyy-MM-dd HH:mm:ss");
|
||||||
sessionQueryWrapper.select("distinct uid");
|
sessionQueryWrapper.select("distinct uid");
|
||||||
sessionQueryWrapper.apply("UNIX_TIMESTAMP(gmt_create) >= UNIX_TIMESTAMP('{0}')", threeMonthsBeforeDate);
|
sessionQueryWrapper.apply("UNIX_TIMESTAMP(gmt_create) >= UNIX_TIMESTAMP('{0}')", threeMonthsBeforeDate);
|
||||||
|
@ -288,9 +288,70 @@ public class ScheduleServiceImpl implements ScheduleService {
|
||||||
|
|
||||||
boolean isSuccess = sessionService.remove(sessionUpdateWrapper);
|
boolean isSuccess = sessionService.remove(sessionUpdateWrapper);
|
||||||
if (!isSuccess) {
|
if (!isSuccess) {
|
||||||
log.error("=============数据库session表定时删除用户三个月前的记录失败===============");
|
log.error("=============数据库session表定时删除用户6个月前的记录失败===============");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @MethodName syncNoticeToUser
|
||||||
|
* @Description 每一小时拉取系统通知表admin_sys_notice到表user_sys_notice(只推送给半年内有登录过的用户)
|
||||||
|
* @Return
|
||||||
|
* @Since 2021/10/3
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Scheduled(cron = "0 0 0/1 * * *")
|
||||||
|
public void syncNoticeToRecentHalfYearUser() {
|
||||||
|
QueryWrapper<AdminSysNotice> adminSysNoticeQueryWrapper = new QueryWrapper<>();
|
||||||
|
adminSysNoticeQueryWrapper.eq("state", false);
|
||||||
|
List<AdminSysNotice> adminSysNotices = adminSysNoticeService.list(adminSysNoticeQueryWrapper);
|
||||||
|
if (adminSysNotices.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryWrapper<Session> sessionQueryWrapper = new QueryWrapper<>();
|
||||||
|
sessionQueryWrapper.select("DISTINCT uid");
|
||||||
|
List<Session> sessionList = sessionService.list(sessionQueryWrapper);
|
||||||
|
List<String> userIds = sessionList.stream().map(Session::getUid).collect(Collectors.toList());
|
||||||
|
|
||||||
|
for (AdminSysNotice adminSysNotice : adminSysNotices) {
|
||||||
|
switch (adminSysNotice.getType()) {
|
||||||
|
case "All":
|
||||||
|
List<UserSysNotice> userSysNoticeList = new ArrayList<>();
|
||||||
|
for (String uid : userIds) {
|
||||||
|
UserSysNotice userSysNotice = new UserSysNotice();
|
||||||
|
userSysNotice.setRecipientId(uid)
|
||||||
|
.setType("Sys")
|
||||||
|
.setSysNoticeId(adminSysNotice.getId());
|
||||||
|
userSysNoticeList.add(userSysNotice);
|
||||||
|
}
|
||||||
|
boolean isOk1 = userSysNoticeService.saveOrUpdateBatch(userSysNoticeList);
|
||||||
|
if (isOk1) {
|
||||||
|
adminSysNotice.setState(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Single":
|
||||||
|
UserSysNotice userSysNotice = new UserSysNotice();
|
||||||
|
userSysNotice.setRecipientId(adminSysNotice.getRecipientId())
|
||||||
|
.setType("Mine")
|
||||||
|
.setSysNoticeId(adminSysNotice.getId());
|
||||||
|
boolean isOk2 = userSysNoticeService.saveOrUpdate(userSysNotice);
|
||||||
|
if (isOk2) {
|
||||||
|
adminSysNotice.setState(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Admin":
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isUpdateNoticeOk = adminSysNoticeService.saveOrUpdateBatch(adminSysNotices);
|
||||||
|
if (!isUpdateNoticeOk) {
|
||||||
|
log.error("=============推送系统通知更新状态失败===============");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package top.hcode.hoj.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import top.hcode.hoj.pojo.entity.AdminSysNotice;
|
||||||
|
import top.hcode.hoj.pojo.vo.AdminSysNoticeVo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:33
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
public interface AdminSysNoticeService extends IService<AdminSysNotice> {
|
||||||
|
public IPage<AdminSysNoticeVo> getSysNotice(int limit,int currentPage,String type);
|
||||||
|
|
||||||
|
public void syncNoticeToNewRegisterUser(String uid);
|
||||||
|
|
||||||
|
public void syncNoticeToNewRegisterBatchUser(List<String> uidList);
|
||||||
|
|
||||||
|
public void addSingleNoticeToUser(String adminId, String recipientId, String title, String content,String type);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package top.hcode.hoj.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import top.hcode.hoj.pojo.entity.MsgRemind;
|
||||||
|
import top.hcode.hoj.pojo.vo.UserMsgVo;
|
||||||
|
import top.hcode.hoj.pojo.vo.UserUnreadMsgCountVo;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:32
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
public interface MsgRemindService extends IService<MsgRemind> {
|
||||||
|
UserUnreadMsgCountVo getUserUnreadMsgCount(String uid);
|
||||||
|
|
||||||
|
boolean cleanMsgByType(String type, Long id, String uid);
|
||||||
|
|
||||||
|
IPage<UserMsgVo> getUserMsgList(String uid, String action, int limit, int currentPage);
|
||||||
|
}
|
|
@ -4,4 +4,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import top.hcode.hoj.pojo.entity.Session;
|
import top.hcode.hoj.pojo.entity.Session;
|
||||||
|
|
||||||
public interface SessionService extends IService<Session> {
|
public interface SessionService extends IService<Session> {
|
||||||
|
public void checkRemoteLogin(String uid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,4 +16,6 @@ import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||||
public interface UserRoleService extends IService<UserRole> {
|
public interface UserRoleService extends IService<UserRole> {
|
||||||
IPage<UserRolesVo> getUserList(int limit, int currentPage, String keyword,Boolean onlyAdmin);
|
IPage<UserRolesVo> getUserList(int limit, int currentPage, String keyword,Boolean onlyAdmin);
|
||||||
void deleteCache(String uid, boolean isRemoveSession);
|
void deleteCache(String uid, boolean isRemoveSession);
|
||||||
|
|
||||||
|
String getAuthChangeContent(int oldType,int newType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package top.hcode.hoj.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import top.hcode.hoj.pojo.entity.UserSysNotice;
|
||||||
|
import top.hcode.hoj.pojo.vo.SysMsgVo;
|
||||||
|
|
||||||
|
public interface UserSysNoticeService extends IService<UserSysNotice> {
|
||||||
|
IPage<SysMsgVo> getSysNotice(int limit, int currentPage, String uid);
|
||||||
|
|
||||||
|
IPage<SysMsgVo> getMineNotice(int limit, int currentPage, String uid);
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package top.hcode.hoj.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import top.hcode.hoj.dao.AdminSysNoticeMapper;
|
||||||
|
|
||||||
|
import top.hcode.hoj.pojo.entity.AdminSysNotice;
|
||||||
|
import top.hcode.hoj.pojo.entity.UserSysNotice;
|
||||||
|
import top.hcode.hoj.pojo.vo.AdminSysNoticeVo;
|
||||||
|
import top.hcode.hoj.service.AdminSysNoticeService;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:34
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class AdminSysNoticeServiceImpl extends ServiceImpl<AdminSysNoticeMapper, AdminSysNotice> implements AdminSysNoticeService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AdminSysNoticeMapper adminSysNoticeMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserSysNoticeServiceImpl userSysNoticeService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<AdminSysNoticeVo> getSysNotice(int limit, int currentPage, String type) {
|
||||||
|
Page<AdminSysNoticeVo> page = new Page<>(currentPage, limit);
|
||||||
|
return adminSysNoticeMapper.getAdminSysNotice(page, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Async
|
||||||
|
public void syncNoticeToNewRegisterUser(String uid) {
|
||||||
|
QueryWrapper<AdminSysNotice> adminSysNoticeQueryWrapper = new QueryWrapper<>();
|
||||||
|
adminSysNoticeQueryWrapper
|
||||||
|
.eq("type", "All")
|
||||||
|
.le("gmt_create", new Date())
|
||||||
|
.eq("state", true);
|
||||||
|
List<AdminSysNotice> adminSysNotices = adminSysNoticeMapper.selectList(adminSysNoticeQueryWrapper);
|
||||||
|
if (adminSysNotices.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<UserSysNotice> userSysNoticeList = new ArrayList<>();
|
||||||
|
for (AdminSysNotice adminSysNotice : adminSysNotices) {
|
||||||
|
UserSysNotice userSysNotice = new UserSysNotice();
|
||||||
|
userSysNotice.setType("Sys")
|
||||||
|
.setSysNoticeId(adminSysNotice.getId())
|
||||||
|
.setRecipientId(uid);
|
||||||
|
userSysNoticeList.add(userSysNotice);
|
||||||
|
}
|
||||||
|
userSysNoticeService.saveOrUpdateBatch(userSysNoticeList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Async
|
||||||
|
public void syncNoticeToNewRegisterBatchUser(List<String> uidList) {
|
||||||
|
QueryWrapper<AdminSysNotice> adminSysNoticeQueryWrapper = new QueryWrapper<>();
|
||||||
|
adminSysNoticeQueryWrapper
|
||||||
|
.eq("type", "All")
|
||||||
|
.le("gmt_create", new Date())
|
||||||
|
.eq("state", true);
|
||||||
|
List<AdminSysNotice> adminSysNotices = adminSysNoticeMapper.selectList(adminSysNoticeQueryWrapper);
|
||||||
|
if (adminSysNotices.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<UserSysNotice> userSysNoticeList = new ArrayList<>();
|
||||||
|
for (String uid : uidList) {
|
||||||
|
for (AdminSysNotice adminSysNotice : adminSysNotices) {
|
||||||
|
UserSysNotice userSysNotice = new UserSysNotice();
|
||||||
|
userSysNotice.setType("Sys")
|
||||||
|
.setSysNoticeId(adminSysNotice.getId())
|
||||||
|
.setRecipientId(uid);
|
||||||
|
userSysNoticeList.add(userSysNotice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userSysNoticeService.saveOrUpdateBatch(userSysNoticeList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
@Async
|
||||||
|
public void addSingleNoticeToUser(String adminId, String recipientId, String title, String content, String type) {
|
||||||
|
AdminSysNotice adminSysNotice = new AdminSysNotice();
|
||||||
|
adminSysNotice.setAdminId(adminId)
|
||||||
|
.setType("Single")
|
||||||
|
.setTitle(title)
|
||||||
|
.setContent(content)
|
||||||
|
.setState(true)
|
||||||
|
.setRecipientId(recipientId);
|
||||||
|
boolean isOk = adminSysNoticeMapper.insert(adminSysNotice) > 0;
|
||||||
|
if (isOk) {
|
||||||
|
UserSysNotice userSysNotice = new UserSysNotice();
|
||||||
|
userSysNotice.setRecipientId(recipientId)
|
||||||
|
.setSysNoticeId(adminSysNotice.getId())
|
||||||
|
.setType(type);
|
||||||
|
userSysNoticeService.save(userSysNotice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,17 +4,16 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import top.hcode.hoj.pojo.entity.Comment;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import top.hcode.hoj.pojo.entity.*;
|
||||||
import top.hcode.hoj.dao.CommentMapper;
|
import top.hcode.hoj.dao.CommentMapper;
|
||||||
import top.hcode.hoj.pojo.entity.Contest;
|
|
||||||
import top.hcode.hoj.pojo.entity.Reply;
|
|
||||||
import top.hcode.hoj.pojo.entity.UserInfo;
|
|
||||||
import top.hcode.hoj.pojo.vo.CommentsVo;
|
import top.hcode.hoj.pojo.vo.CommentsVo;
|
||||||
import top.hcode.hoj.service.CommentService;
|
import top.hcode.hoj.service.CommentService;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import top.hcode.hoj.utils.Constants;
|
import top.hcode.hoj.utils.Constants;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -41,6 +40,9 @@ public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> impl
|
||||||
@Autowired
|
@Autowired
|
||||||
private ReplyServiceImpl replyService;
|
private ReplyServiceImpl replyService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MsgRemindServiceImpl msgRemindService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<CommentsVo> getCommentList(int limit, int currentPage, Long cid, Integer did, Boolean isRoot, String uid) {
|
public IPage<CommentsVo> getCommentList(int limit, int currentPage, Long cid, Integer did, Boolean isRoot, String uid) {
|
||||||
//新建分页
|
//新建分页
|
||||||
|
@ -84,4 +86,36 @@ public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> impl
|
||||||
replyQueryWrapper.orderByDesc("gmt_create");
|
replyQueryWrapper.orderByDesc("gmt_create");
|
||||||
return replyService.list(replyQueryWrapper);
|
return replyService.list(replyQueryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void updateCommentMsg(String recipientId, String senderId, String content, Integer discussionId) {
|
||||||
|
|
||||||
|
if (content.length() > 200) {
|
||||||
|
content = content.substring(0, 200) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
MsgRemind msgRemind = new MsgRemind();
|
||||||
|
msgRemind.setAction("Discuss")
|
||||||
|
.setRecipientId(recipientId)
|
||||||
|
.setSenderId(senderId)
|
||||||
|
.setSourceContent(content)
|
||||||
|
.setSourceId(discussionId)
|
||||||
|
.setSourceType("Discussion")
|
||||||
|
.setUrl("/discussion-detail/" + discussionId);
|
||||||
|
msgRemindService.saveOrUpdate(msgRemind);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void updateCommentLikeMsg(String recipientId, String senderId, Integer sourceId, String sourceType) {
|
||||||
|
|
||||||
|
MsgRemind msgRemind = new MsgRemind();
|
||||||
|
msgRemind.setAction("Like_Discuss")
|
||||||
|
.setRecipientId(recipientId)
|
||||||
|
.setSenderId(senderId)
|
||||||
|
.setSourceId(sourceId)
|
||||||
|
.setSourceType(sourceType)
|
||||||
|
.setUrl(sourceType.equals("Discussion") ? "/discussion-detail/" + sourceId : "/contest/" + sourceId + "/comment");
|
||||||
|
msgRemindService.saveOrUpdate(msgRemind);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package top.hcode.hoj.service.impl;
|
package top.hcode.hoj.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.retry.annotation.Retryable;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import top.hcode.hoj.pojo.entity.ContestProblem;
|
import top.hcode.hoj.pojo.entity.ContestProblem;
|
||||||
import top.hcode.hoj.dao.ContestProblemMapper;
|
import top.hcode.hoj.dao.ContestProblemMapper;
|
||||||
|
import top.hcode.hoj.pojo.entity.ContestRecord;
|
||||||
import top.hcode.hoj.pojo.entity.UserInfo;
|
import top.hcode.hoj.pojo.entity.UserInfo;
|
||||||
import top.hcode.hoj.pojo.vo.ContestProblemVo;
|
import top.hcode.hoj.pojo.vo.ContestProblemVo;
|
||||||
import top.hcode.hoj.service.ContestProblemService;
|
import top.hcode.hoj.service.ContestProblemService;
|
||||||
|
@ -39,4 +43,14 @@ public class ContestProblemServiceImpl extends ServiceImpl<ContestProblemMapper,
|
||||||
|
|
||||||
return contestProblemMapper.getContestProblemList(cid, startTime, endTime, sealTime, isAdmin, superAdminUidList);
|
return contestProblemMapper.getContestProblemList(cid, startTime, endTime, sealTime, isAdmin, superAdminUidList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void syncContestRecord(Long pid, Long cid, String displayId) {
|
||||||
|
|
||||||
|
UpdateWrapper<ContestRecord> updateWrapper = new UpdateWrapper<>();
|
||||||
|
updateWrapper.eq("pid", pid)
|
||||||
|
.eq("cid", cid)
|
||||||
|
.set("display_id", displayId);
|
||||||
|
contestRecordService.update(updateWrapper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ public class ContestRecordServiceImpl extends ServiceImpl<ContestRecordMapper, C
|
||||||
|
|
||||||
|
|
||||||
public Page<OIContestRankVo> getOIContestRank(Long cid, String contestAuthor, Boolean isOpenSealRank, Date sealTime,
|
public Page<OIContestRankVo> getOIContestRank(Long cid, String contestAuthor, Boolean isOpenSealRank, Date sealTime,
|
||||||
Date startTime, Date endTime, int currentPage, int limit) {
|
Date startTime, Date endTime, int currentPage, int limit) {
|
||||||
|
|
||||||
List<ContestRecord> oiContestRecord = contestRecordMapper.getOIContestRecord(cid, contestAuthor, isOpenSealRank, sealTime, startTime, endTime);
|
List<ContestRecord> oiContestRecord = contestRecordMapper.getOIContestRecord(cid, contestAuthor, isOpenSealRank, sealTime, startTime, endTime);
|
||||||
// 计算排名
|
// 计算排名
|
||||||
|
@ -353,12 +353,18 @@ public class ContestRecordServiceImpl extends ServiceImpl<ContestRecordMapper, C
|
||||||
oiContestRankVo = result.get(uidMapIndex.get(contestRecord.getUid())); // 根据记录的index进行获取
|
oiContestRankVo = result.get(uidMapIndex.get(contestRecord.getUid())); // 根据记录的index进行获取
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记录已经是每道題最新的提交了
|
// 记录总分
|
||||||
oiContestRankVo.getSubmissionInfo().put(contestRecord.getDisplayId(), contestRecord.getScore());
|
HashMap<String, Integer> submissionInfo = oiContestRankVo.getSubmissionInfo();
|
||||||
|
Integer score = submissionInfo.get(contestRecord.getDisplayId());
|
||||||
|
|
||||||
if (contestRecord.getScore() != null) { // 一般來说不可能出现,status已经筛掉没有评分的提交记录
|
if (contestRecord.getScore() != null) {
|
||||||
oiContestRankVo.setTotalScore(oiContestRankVo.getTotalScore() + contestRecord.getScore());
|
if (score != null) { // 为了避免同个提交时间的重复计算
|
||||||
|
oiContestRankVo.setTotalScore(oiContestRankVo.getTotalScore() - score + contestRecord.getScore());
|
||||||
|
} else {
|
||||||
|
oiContestRankVo.setTotalScore(oiContestRankVo.getTotalScore() + contestRecord.getScore());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
submissionInfo.put(contestRecord.getDisplayId(), contestRecord.getScore());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,16 @@ package top.hcode.hoj.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import top.hcode.hoj.dao.DiscussionMapper;
|
import top.hcode.hoj.dao.DiscussionMapper;
|
||||||
import top.hcode.hoj.pojo.entity.Discussion;
|
import top.hcode.hoj.pojo.entity.Discussion;
|
||||||
|
import top.hcode.hoj.pojo.entity.MsgRemind;
|
||||||
import top.hcode.hoj.pojo.vo.DiscussionVo;
|
import top.hcode.hoj.pojo.vo.DiscussionVo;
|
||||||
import top.hcode.hoj.service.DiscussionService;
|
import top.hcode.hoj.service.DiscussionService;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author: Himit_ZH
|
* @Author: Himit_ZH
|
||||||
* @Date: 2021/5/4 22:31
|
* @Date: 2021/5/4 22:31
|
||||||
|
@ -23,4 +27,20 @@ public class DiscussionServiceImpl extends ServiceImpl<DiscussionMapper, Discuss
|
||||||
public DiscussionVo getDiscussion(Integer did, String uid) {
|
public DiscussionVo getDiscussion(Integer did, String uid) {
|
||||||
return discussionMapper.getDiscussion(did, uid);
|
return discussionMapper.getDiscussion(did, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MsgRemindServiceImpl msgRemindService;
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void updatePostLikeMsg(String recipientId, String senderId, Integer discussionId) {
|
||||||
|
|
||||||
|
MsgRemind msgRemind = new MsgRemind();
|
||||||
|
msgRemind.setAction("Like_Post")
|
||||||
|
.setRecipientId(recipientId)
|
||||||
|
.setSenderId(senderId)
|
||||||
|
.setSourceId(discussionId)
|
||||||
|
.setSourceType("Discussion")
|
||||||
|
.setUrl("/discussion-detail/" + discussionId);
|
||||||
|
msgRemindService.saveOrUpdate(msgRemind);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
package top.hcode.hoj.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import top.hcode.hoj.dao.CommentMapper;
|
||||||
|
import top.hcode.hoj.dao.DiscussionMapper;
|
||||||
|
import top.hcode.hoj.dao.MsgRemindMapper;
|
||||||
|
import top.hcode.hoj.dao.ReplyMapper;
|
||||||
|
import top.hcode.hoj.pojo.entity.*;
|
||||||
|
|
||||||
|
import top.hcode.hoj.pojo.vo.UserMsgVo;
|
||||||
|
import top.hcode.hoj.pojo.vo.UserUnreadMsgCountVo;
|
||||||
|
import top.hcode.hoj.service.MsgRemindService;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:36
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class MsgRemindServiceImpl extends ServiceImpl<MsgRemindMapper, MsgRemind> implements MsgRemindService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MsgRemindMapper msgRemindMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DiscussionMapper discussionMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ContestServiceImpl contestService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ReplyMapper replyMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CommentMapper commentMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserSysNoticeServiceImpl userSysNoticeService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserUnreadMsgCountVo getUserUnreadMsgCount(String uid) {
|
||||||
|
return msgRemindMapper.getUserUnreadMsgCount(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cleanMsgByType(String type, Long id, String uid) {
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "Like":
|
||||||
|
case "Discuss":
|
||||||
|
case "Reply":
|
||||||
|
UpdateWrapper<MsgRemind> updateWrapper1 = new UpdateWrapper<>();
|
||||||
|
updateWrapper1
|
||||||
|
.eq(id != null, "id", id)
|
||||||
|
.eq("recipient_id", uid);
|
||||||
|
return msgRemindMapper.delete(updateWrapper1) > 0;
|
||||||
|
case "Sys":
|
||||||
|
case "Mine":
|
||||||
|
UpdateWrapper<UserSysNotice> updateWrapper2 = new UpdateWrapper<>();
|
||||||
|
updateWrapper2
|
||||||
|
.eq(id != null, "id", id)
|
||||||
|
.eq("recipient_id", uid);
|
||||||
|
return userSysNoticeService.remove(updateWrapper2);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<UserMsgVo> getUserMsgList(String uid, String action, int limit, int currentPage) {
|
||||||
|
Page<UserMsgVo> page = new Page<>(currentPage, limit);
|
||||||
|
IPage<UserMsgVo> userMsgList = msgRemindMapper.getUserMsg(page, uid, action);
|
||||||
|
if (userMsgList.getTotal() > 0) {
|
||||||
|
switch (action) {
|
||||||
|
case "Discuss": // 评论我的
|
||||||
|
return getUserDiscussMsgList(userMsgList);
|
||||||
|
case "Reply": // 回复我的
|
||||||
|
return getUserReplyMsgList(userMsgList);
|
||||||
|
case "Like":
|
||||||
|
return getUserLikeMsgList(userMsgList);
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("invalid action:" + action);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return userMsgList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void updateUserMsgRead(IPage<UserMsgVo> userMsgList) {
|
||||||
|
List<Long> idList = userMsgList.getRecords().stream()
|
||||||
|
.filter(userMsgVo -> !userMsgVo.getState())
|
||||||
|
.map(UserMsgVo::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (idList.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UpdateWrapper<MsgRemind> updateWrapper = new UpdateWrapper<>();
|
||||||
|
updateWrapper.in("id", idList)
|
||||||
|
.set("state", true);
|
||||||
|
msgRemindMapper.update(null, updateWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPage<UserMsgVo> getUserDiscussMsgList(IPage<UserMsgVo> userMsgList) {
|
||||||
|
|
||||||
|
List<Integer> discussionIds = userMsgList.getRecords()
|
||||||
|
.stream()
|
||||||
|
.map(UserMsgVo::getSourceId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Collection<Discussion> discussions = discussionMapper.selectBatchIds(discussionIds);
|
||||||
|
for (Discussion discussion : discussions) {
|
||||||
|
for (UserMsgVo userMsgVo : userMsgList.getRecords()) {
|
||||||
|
if (Objects.equals(discussion.getId(), userMsgVo.getSourceId())) {
|
||||||
|
userMsgVo.setSourceTitle(discussion.getTitle());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applicationContext.getBean(MsgRemindServiceImpl.class).updateUserMsgRead(userMsgList);
|
||||||
|
return userMsgList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPage<UserMsgVo> getUserReplyMsgList(IPage<UserMsgVo> userMsgList) {
|
||||||
|
|
||||||
|
for (UserMsgVo userMsgVo : userMsgList.getRecords()) {
|
||||||
|
if ("Discussion".equals(userMsgVo.getSourceType())) {
|
||||||
|
Discussion discussion = discussionMapper.selectById(userMsgVo.getSourceId());
|
||||||
|
userMsgVo.setSourceTitle(discussion.getTitle());
|
||||||
|
} else if ("Contest".equals(userMsgVo.getSourceType())) {
|
||||||
|
Contest contest = contestService.getById(userMsgVo.getSourceId());
|
||||||
|
userMsgVo.setSourceTitle(contest.getTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("Comment".equals(userMsgVo.getQuoteType())) {
|
||||||
|
Comment comment = commentMapper.selectById(userMsgVo.getQuoteId());
|
||||||
|
String content;
|
||||||
|
if (comment.getContent().length() < 100) {
|
||||||
|
content = comment.getFromName() + " : "
|
||||||
|
+ comment.getContent();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
content = comment.getFromName() + " : "
|
||||||
|
+ comment.getContent().substring(0, 100) + "...";
|
||||||
|
}
|
||||||
|
userMsgVo.setQuoteContent(content);
|
||||||
|
|
||||||
|
} else if ("Reply".equals(userMsgVo.getQuoteType())) {
|
||||||
|
Reply reply = replyMapper.selectById(userMsgVo.getQuoteId());
|
||||||
|
|
||||||
|
String content;
|
||||||
|
if (reply.getContent().length() < 100) {
|
||||||
|
content = reply.getFromName() + " : @" + reply.getToName() + ":"
|
||||||
|
+ reply.getContent();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
content = reply.getFromName() + " : @" + reply.getToName() + ":"
|
||||||
|
+ reply.getContent().substring(0, 100) + "...";
|
||||||
|
}
|
||||||
|
userMsgVo.setQuoteContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
applicationContext.getBean(MsgRemindServiceImpl.class).updateUserMsgRead(userMsgList);
|
||||||
|
return userMsgList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPage<UserMsgVo> getUserLikeMsgList(IPage<UserMsgVo> userMsgList) {
|
||||||
|
for (UserMsgVo userMsgVo : userMsgList.getRecords()) {
|
||||||
|
if ("Discussion".equals(userMsgVo.getSourceType())) {
|
||||||
|
Discussion discussion = discussionMapper.selectById(userMsgVo.getSourceId());
|
||||||
|
userMsgVo.setSourceTitle(discussion.getTitle());
|
||||||
|
} else if ("Contest".equals(userMsgVo.getSourceType())) {
|
||||||
|
Contest contest = contestService.getById(userMsgVo.getSourceId());
|
||||||
|
userMsgVo.setSourceTitle(contest.getTitle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applicationContext.getBean(MsgRemindServiceImpl.class).updateUserMsgRead(userMsgList);
|
||||||
|
return userMsgList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,8 @@ import top.hcode.hoj.utils.Constants;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -680,10 +682,15 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
|
||||||
return importProblemVo;
|
return importProblemVo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去除末尾的空白符
|
// 去除每行末尾的空白符
|
||||||
public static String rtrim(String value) {
|
public static String rtrim(String value) {
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
return value.replaceAll("\\s+$", "");
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String[] strArr = value.split("\n");
|
||||||
|
for (String str : strArr) {
|
||||||
|
sb.append(str.replaceAll("\\s+$", "")).append("\n");
|
||||||
|
}
|
||||||
|
return sb.toString().replaceAll("\\s+$", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package top.hcode.hoj.service.impl;
|
package top.hcode.hoj.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import top.hcode.hoj.dao.ReplyMapper;
|
import top.hcode.hoj.dao.ReplyMapper;
|
||||||
|
import top.hcode.hoj.pojo.entity.MsgRemind;
|
||||||
import top.hcode.hoj.pojo.entity.Reply;
|
import top.hcode.hoj.pojo.entity.Reply;
|
||||||
import top.hcode.hoj.service.ReplyService;
|
import top.hcode.hoj.service.ReplyService;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author: Himit_ZH
|
* @Author: Himit_ZH
|
||||||
* @Date: 2021/5/5 22:09
|
* @Date: 2021/5/5 22:09
|
||||||
|
@ -13,4 +17,28 @@ import top.hcode.hoj.service.ReplyService;
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class ReplyServiceImpl extends ServiceImpl<ReplyMapper, Reply> implements ReplyService {
|
public class ReplyServiceImpl extends ServiceImpl<ReplyMapper, Reply> implements ReplyService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MsgRemindServiceImpl msgRemindService;
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void updateReplyMsg(Integer sourceId, String sourceType, String content, Integer quoteId, String quoteType,
|
||||||
|
String recipientId,String senderId) {
|
||||||
|
if (content.length() > 200) {
|
||||||
|
content = content.substring(0, 200) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
MsgRemind msgRemind = new MsgRemind();
|
||||||
|
msgRemind.setAction("Reply")
|
||||||
|
.setSourceId(sourceId)
|
||||||
|
.setSourceType(sourceType)
|
||||||
|
.setSourceContent(content)
|
||||||
|
.setQuoteId(quoteId)
|
||||||
|
.setQuoteType(quoteType)
|
||||||
|
.setUrl(sourceType.equals("Discussion") ? "/discussion-detail/" + sourceId : "/contest/" + sourceId + "/comment")
|
||||||
|
.setRecipientId(recipientId)
|
||||||
|
.setSenderId(senderId);
|
||||||
|
|
||||||
|
msgRemindService.saveOrUpdate(msgRemind);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,26 @@
|
||||||
package top.hcode.hoj.service.impl;
|
package top.hcode.hoj.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import top.hcode.hoj.dao.SessionMapper;
|
import top.hcode.hoj.dao.SessionMapper;
|
||||||
|
import top.hcode.hoj.pojo.entity.AdminSysNotice;
|
||||||
import top.hcode.hoj.pojo.entity.Session;
|
import top.hcode.hoj.pojo.entity.Session;
|
||||||
|
import top.hcode.hoj.pojo.entity.UserSysNotice;
|
||||||
|
import top.hcode.hoj.service.AdminSysNoticeService;
|
||||||
import top.hcode.hoj.service.SessionService;
|
import top.hcode.hoj.service.SessionService;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author: Himit_ZH
|
* @Author: Himit_ZH
|
||||||
* @Date: 2020/12/3 22:46
|
* @Date: 2020/12/3 22:46
|
||||||
|
@ -13,4 +28,86 @@ import top.hcode.hoj.service.SessionService;
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class SessionServiceImpl extends ServiceImpl<SessionMapper, Session> implements SessionService {
|
public class SessionServiceImpl extends ServiceImpl<SessionMapper, Session> implements SessionService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SessionMapper sessionMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AdminSysNoticeServiceImpl adminSysNoticeService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserSysNoticeServiceImpl userSysNoticeService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Async
|
||||||
|
@Transactional
|
||||||
|
public void checkRemoteLogin(String uid) {
|
||||||
|
QueryWrapper<Session> sessionQueryWrapper = new QueryWrapper<>();
|
||||||
|
sessionQueryWrapper.eq("uid", uid)
|
||||||
|
.orderByDesc("gmt_create")
|
||||||
|
.last("limit 2");
|
||||||
|
List<Session> sessionList = sessionMapper.selectList(sessionQueryWrapper);
|
||||||
|
if (sessionList.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Session nowSession = sessionList.get(0);
|
||||||
|
Session lastSession = sessionList.get(1);
|
||||||
|
// 如果两次登录的ip不相同,需要发通知给用户
|
||||||
|
if (!nowSession.getIp().equals(lastSession.getIp())) {
|
||||||
|
AdminSysNotice adminSysNotice = new AdminSysNotice();
|
||||||
|
adminSysNotice
|
||||||
|
.setType("Single")
|
||||||
|
.setContent(getRemoteLoginContent(nowSession.getIp(), nowSession.getGmtCreate()))
|
||||||
|
.setTitle("账号异地登录通知(Account Remote Login Notice)")
|
||||||
|
.setAdminId("1")
|
||||||
|
.setState(false)
|
||||||
|
.setRecipientId(uid);
|
||||||
|
boolean isSaveOk = adminSysNoticeService.save(adminSysNotice);
|
||||||
|
if (isSaveOk) {
|
||||||
|
UserSysNotice userSysNotice = new UserSysNotice();
|
||||||
|
userSysNotice.setType("Sys")
|
||||||
|
.setSysNoticeId(adminSysNotice.getId())
|
||||||
|
.setRecipientId(uid)
|
||||||
|
.setState(false);
|
||||||
|
boolean isOk = userSysNoticeService.save(userSysNotice);
|
||||||
|
if (isOk) {
|
||||||
|
adminSysNotice.setState(true);
|
||||||
|
adminSysNoticeService.saveOrUpdate(adminSysNotice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRemoteLoginContent(String newIp, Date loginDate) {
|
||||||
|
String dateStr = DateUtil.format(loginDate, "yyyy-MM-dd HH:mm:ss");
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("亲爱的用户,您好!您的账号于").append(dateStr);
|
||||||
|
String addr = null;
|
||||||
|
try {
|
||||||
|
String res = HttpUtil.get("https://whois.pconline.com.cn/ipJson.jsp?ip=" + newIp + "&json=true");
|
||||||
|
JSONObject resJson = JSONUtil.parseObj(res);
|
||||||
|
addr = resJson.getStr("addr");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(addr)) {
|
||||||
|
sb.append("在【")
|
||||||
|
.append(addr)
|
||||||
|
.append("】");
|
||||||
|
}
|
||||||
|
sb.append("登录,登录IP为:【")
|
||||||
|
.append(newIp)
|
||||||
|
.append("】,若非本人操作,请立即修改密码。")
|
||||||
|
.append("\n\n")
|
||||||
|
.append("Hello! Dear user, Your account was logged in in");
|
||||||
|
|
||||||
|
if (!StringUtils.isEmpty(addr)) {
|
||||||
|
sb.append(" 【")
|
||||||
|
.append(addr)
|
||||||
|
.append("】 on ")
|
||||||
|
.append(dateStr)
|
||||||
|
.append(". If you do not operate by yourself, please change your password immediately.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -22,12 +22,14 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import top.hcode.hoj.shiro.AccountProfile;
|
import top.hcode.hoj.shiro.AccountProfile;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* 服务实现类
|
* 服务实现类
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Himit_ZH
|
* @author Himit_ZH
|
||||||
|
@ -42,32 +44,32 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
|
||||||
RedisSessionDAO redisSessionDAO;
|
RedisSessionDAO redisSessionDAO;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<UserRolesVo> getUserList(int limit, int currentPage, String keyword,Boolean onlyAdmin) {
|
public IPage<UserRolesVo> getUserList(int limit, int currentPage, String keyword, Boolean onlyAdmin) {
|
||||||
//新建分页
|
//新建分页
|
||||||
Page<UserRolesVo> page = new Page<>(currentPage, limit);
|
Page<UserRolesVo> page = new Page<>(currentPage, limit);
|
||||||
if (onlyAdmin){
|
if (onlyAdmin) {
|
||||||
return userRoleMapper.getAdminUserList(page,limit, currentPage,keyword);
|
return userRoleMapper.getAdminUserList(page, limit, currentPage, keyword);
|
||||||
}else {
|
} else {
|
||||||
return userRoleMapper.getUserList(page, limit, currentPage, keyword);
|
return userRoleMapper.getUserList(page, limit, currentPage, keyword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @MethodName deleteCache
|
* @param uid 当前需要操作的用户id
|
||||||
* @param uid 当前需要操作的用户id
|
|
||||||
* @param isRemoveSession 如果为true则会强行删除该用户session,必须重新登陆,false的话 在访问受限接口时会重新授权
|
* @param isRemoveSession 如果为true则会强行删除该用户session,必须重新登陆,false的话 在访问受限接口时会重新授权
|
||||||
|
* @MethodName deleteCache
|
||||||
* @Description TODO
|
* @Description TODO
|
||||||
* @Return
|
* @Return
|
||||||
* @Since 2021/6/12
|
* @Since 2021/6/12
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void deleteCache(String uid, boolean isRemoveSession){
|
public void deleteCache(String uid, boolean isRemoveSession) {
|
||||||
//从缓存中获取Session
|
//从缓存中获取Session
|
||||||
Session session = null;
|
Session session = null;
|
||||||
Collection<Session> sessions = redisSessionDAO.getActiveSessions();
|
Collection<Session> sessions = redisSessionDAO.getActiveSessions();
|
||||||
AccountProfile accountProfile;
|
AccountProfile accountProfile;
|
||||||
Object attribute = null;
|
Object attribute = null;
|
||||||
for(Session sessionInfo : sessions){
|
for (Session sessionInfo : sessions) {
|
||||||
//遍历Session,找到该用户名称对应的Session
|
//遍历Session,找到该用户名称对应的Session
|
||||||
attribute = sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
|
attribute = sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
|
||||||
if (attribute == null) {
|
if (attribute == null) {
|
||||||
|
@ -78,11 +80,11 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (Objects.equals(accountProfile.getUid(), uid)) {
|
if (Objects.equals(accountProfile.getUid(), uid)) {
|
||||||
session=sessionInfo;
|
session = sessionInfo;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (session == null||attribute == null) {
|
if (session == null || attribute == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//删除session 会强制退出!主要是在禁用用户或角色时,强制用户退出的
|
//删除session 会强制退出!主要是在禁用用户或角色时,强制用户退出的
|
||||||
|
@ -96,4 +98,29 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
|
||||||
((LogoutAware) authc).onLogout((SimplePrincipalCollection) attribute);
|
((LogoutAware) authc).onLogout((SimplePrincipalCollection) attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static List<String> ChineseRole = Arrays.asList("超级管理员", "普通管理员",
|
||||||
|
"普通用户(默认)", "普通用户(禁止提交)", "普通用户(禁止发讨论)", "普通用户(禁言)", "普通用户(禁止提交&禁止发讨论)",
|
||||||
|
"用户(禁止提交&禁言)", "题目管理员");
|
||||||
|
|
||||||
|
private final static List<String> EnglishRole = Arrays.asList("Super Administrator", "General Administrator",
|
||||||
|
"Normal User(Default)", "Normal User(No Submission)", "Normal User(No Discussion)", "Normal User(Forbidden Words)",
|
||||||
|
"Normal User(No Submission & No Discussion)",
|
||||||
|
"Normal User(No Submission & Forbidden Words)", "Problem Administrator");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthChangeContent(int oldType, int newType) {
|
||||||
|
String stringBuffer = "您好,您的权限产生了变更,由【" +
|
||||||
|
ChineseRole.get(oldType - 1000) +
|
||||||
|
"】变更为【" +
|
||||||
|
ChineseRole.get(newType - 1000) +
|
||||||
|
"】。部分权限可能与之前有所不同,请您注意!" +
|
||||||
|
"\n\n" +
|
||||||
|
"Hello, your permission has been changed from 【" +
|
||||||
|
EnglishRole.get(oldType - 1000) +
|
||||||
|
"】 to 【" +
|
||||||
|
EnglishRole.get(newType - 1000) +
|
||||||
|
"】. Some permissions may be different from before. Please note!";
|
||||||
|
return stringBuffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package top.hcode.hoj.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import top.hcode.hoj.dao.UserSysNoticeMapper;
|
||||||
|
import top.hcode.hoj.pojo.entity.MsgRemind;
|
||||||
|
import top.hcode.hoj.pojo.entity.UserSysNotice;
|
||||||
|
import top.hcode.hoj.pojo.vo.SysMsgVo;
|
||||||
|
import top.hcode.hoj.pojo.vo.UserMsgVo;
|
||||||
|
import top.hcode.hoj.service.UserSysNoticeService;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:35
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class UserSysNoticeServiceImpl extends ServiceImpl<UserSysNoticeMapper, UserSysNotice> implements UserSysNoticeService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserSysNoticeMapper userSysNoticeMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<SysMsgVo> getSysNotice(int limit, int currentPage, String uid) {
|
||||||
|
Page<SysMsgVo> page = new Page<>(currentPage, limit);
|
||||||
|
IPage<SysMsgVo> sysNotice = userSysNoticeMapper.getSysOrMineNotice(page, uid, "Sys");
|
||||||
|
applicationContext.getBean(UserSysNoticeServiceImpl.class).updateSysOrMineMsgRead(sysNotice);
|
||||||
|
return sysNotice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<SysMsgVo> getMineNotice(int limit, int currentPage, String uid) {
|
||||||
|
Page<SysMsgVo> page = new Page<>(currentPage, limit);
|
||||||
|
IPage<SysMsgVo> mineNotice = userSysNoticeMapper.getSysOrMineNotice(page, uid, "Mine");
|
||||||
|
applicationContext.getBean(UserSysNoticeServiceImpl.class).updateSysOrMineMsgRead(mineNotice);
|
||||||
|
return mineNotice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void updateSysOrMineMsgRead(IPage<SysMsgVo> userMsgList) {
|
||||||
|
List<Long> idList = userMsgList.getRecords().stream()
|
||||||
|
.filter(userMsgVo -> !userMsgVo.getState())
|
||||||
|
.map(SysMsgVo::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (idList.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UpdateWrapper<UserSysNotice> updateWrapper = new UpdateWrapper<>();
|
||||||
|
updateWrapper.in("id", idList)
|
||||||
|
.set("state", true);
|
||||||
|
userSysNoticeMapper.update(null, updateWrapper);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
import top.hcode.hoj.common.CommonResult;
|
import top.hcode.hoj.common.CommonResult;
|
||||||
|
@ -73,7 +74,7 @@ public class JudgeController {
|
||||||
|
|
||||||
HashMap<String, Object> res = new HashMap<>();
|
HashMap<String, Object> res = new HashMap<>();
|
||||||
|
|
||||||
res.put("version", "1.5.0");
|
res.put("version", "1.6.0");
|
||||||
res.put("currentTime", new Date());
|
res.put("currentTime", new Date());
|
||||||
res.put("judgeServerName", name);
|
res.put("judgeServerName", name);
|
||||||
res.put("cpu", Runtime.getRuntime().availableProcessors());
|
res.put("cpu", Runtime.getRuntime().availableProcessors());
|
||||||
|
|
|
@ -82,9 +82,9 @@ public class JudgeRun {
|
||||||
List<FutureTask<JSONObject>> futureTasks = new ArrayList<>();
|
List<FutureTask<JSONObject>> futureTasks = new ArrayList<>();
|
||||||
JSONArray testcaseList = (JSONArray) testCasesInfo.get("testCases");
|
JSONArray testcaseList = (JSONArray) testCasesInfo.get("testCases");
|
||||||
Boolean isSpj = testCasesInfo.getBool("isSpj");
|
Boolean isSpj = testCasesInfo.getBool("isSpj");
|
||||||
// 默认给1.1倍题目限制时间用来测评
|
|
||||||
Double time = maxTime * 1.1;
|
// 默认给题目限制时间+200ms用来测评
|
||||||
final Long testTime = time.longValue();
|
final Long testTime = maxTime + 200;
|
||||||
|
|
||||||
// 用户输出的文件夹
|
// 用户输出的文件夹
|
||||||
String runDir = Constants.JudgeDir.RUN_WORKPLACE_DIR.getContent() + File.separator + submitId;
|
String runDir = Constants.JudgeDir.RUN_WORKPLACE_DIR.getContent() + File.separator + submitId;
|
||||||
|
@ -115,7 +115,8 @@ public class JudgeRun {
|
||||||
testCaseId,
|
testCaseId,
|
||||||
runDir,
|
runDir,
|
||||||
testCaseInputPath,
|
testCaseInputPath,
|
||||||
testTime,// 默认给1.1倍题目限制时间用来测评
|
testTime,// 默认给题目限制时间+200ms用来测评
|
||||||
|
maxTime,
|
||||||
maxMemory,
|
maxMemory,
|
||||||
maxStack,
|
maxStack,
|
||||||
maxOutputSize,
|
maxOutputSize,
|
||||||
|
@ -138,7 +139,8 @@ public class JudgeRun {
|
||||||
runDir,
|
runDir,
|
||||||
testCaseInputPath,
|
testCaseInputPath,
|
||||||
testCaseOutputPath,
|
testCaseOutputPath,
|
||||||
testTime,// 默认给1.1倍题目限制时间用来测评
|
testTime,// 默认给题目限制时间+200ms用来测评
|
||||||
|
maxTime,
|
||||||
maxMemory,
|
maxMemory,
|
||||||
maxOutputSize,
|
maxOutputSize,
|
||||||
maxStack,
|
maxStack,
|
||||||
|
@ -184,6 +186,7 @@ public class JudgeRun {
|
||||||
String runDir,
|
String runDir,
|
||||||
String testCaseInputFilePath,
|
String testCaseInputFilePath,
|
||||||
String testCaseOutputFilePath,
|
String testCaseOutputFilePath,
|
||||||
|
Long testTime,
|
||||||
Long maxTime,
|
Long maxTime,
|
||||||
Long maxMemory,
|
Long maxMemory,
|
||||||
Long maxOutputSize,
|
Long maxOutputSize,
|
||||||
|
@ -195,7 +198,7 @@ public class JudgeRun {
|
||||||
parseRunCommand(runConfig.getCommand(), runConfig, null, null, null),
|
parseRunCommand(runConfig.getCommand(), runConfig, null, null, null),
|
||||||
runConfig.getEnvs(),
|
runConfig.getEnvs(),
|
||||||
testCaseInputFilePath,
|
testCaseInputFilePath,
|
||||||
maxTime,
|
testTime,
|
||||||
maxOutputSize,
|
maxOutputSize,
|
||||||
maxStack,
|
maxStack,
|
||||||
runConfig.getExeName(),
|
runConfig.getExeName(),
|
||||||
|
@ -388,6 +391,7 @@ public class JudgeRun {
|
||||||
Integer testCaseId,
|
Integer testCaseId,
|
||||||
String runDir,
|
String runDir,
|
||||||
String testCasePath,
|
String testCasePath,
|
||||||
|
Long testTime,
|
||||||
Long maxTime,
|
Long maxTime,
|
||||||
Long maxMemory,
|
Long maxMemory,
|
||||||
Integer maxStack,
|
Integer maxStack,
|
||||||
|
@ -399,7 +403,7 @@ public class JudgeRun {
|
||||||
JSONArray judgeResultList = SandboxRun.testCase(parseRunCommand(runConfig.getCommand(), runConfig, null, null, null),
|
JSONArray judgeResultList = SandboxRun.testCase(parseRunCommand(runConfig.getCommand(), runConfig, null, null, null),
|
||||||
runConfig.getEnvs(),
|
runConfig.getEnvs(),
|
||||||
testCasePath,
|
testCasePath,
|
||||||
maxTime,
|
testTime,
|
||||||
maxOutputSize,
|
maxOutputSize,
|
||||||
maxStack,
|
maxStack,
|
||||||
runConfig.getExeName(),
|
runConfig.getExeName(),
|
||||||
|
@ -522,10 +526,15 @@ public class JudgeRun {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去除末尾空白符
|
// 去除行末尾空白符
|
||||||
public static String rtrim(String value) {
|
public static String rtrim(String value) {
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
return value.replaceAll("\\s+$", "");
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String[] strArr = value.split("\n");
|
||||||
|
for (String str : strArr) {
|
||||||
|
sb.append(str.replaceAll("\\s+$", "")).append("\n");
|
||||||
|
}
|
||||||
|
return sb.toString().replaceAll("\\s+$", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class ProblemTestCaseUtils {
|
||||||
testCaseList.add(jsonObject);
|
testCaseList.add(jsonObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.set("testCases",testCaseList);
|
result.set("testCases", testCaseList);
|
||||||
|
|
||||||
FileWriter infoFile = new FileWriter(testCasesDir + File.separator + "info", CharsetUtil.UTF_8);
|
FileWriter infoFile = new FileWriter(testCasesDir + File.separator + "info", CharsetUtil.UTF_8);
|
||||||
// 写入记录文件
|
// 写入记录文件
|
||||||
|
@ -194,9 +194,14 @@ public class ProblemTestCaseUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去除末尾的空白符
|
// 去除每行末尾的空白符
|
||||||
public static String rtrim(String value) {
|
public static String rtrim(String value) {
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
return value.replaceAll("\\s+$", "");
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String[] strArr = value.split("\n");
|
||||||
|
for (String str : strArr) {
|
||||||
|
sb.append(str.replaceAll("\\s+$", "")).append("\n");
|
||||||
|
}
|
||||||
|
return sb.toString().replaceAll("\\s+$", "");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -70,7 +70,7 @@ public class SandboxRun {
|
||||||
|
|
||||||
private static final int maxProcessNumber = 128;
|
private static final int maxProcessNumber = 128;
|
||||||
|
|
||||||
private static final int TIME_LIMIT_MS = 8000;
|
private static final int TIME_LIMIT_MS = 16000;
|
||||||
|
|
||||||
private static final int MEMORY_LIMIT_MB = 512;
|
private static final int MEMORY_LIMIT_MB = 512;
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
@Slf4j(topic = "hoj")
|
@Slf4j(topic = "hoj")
|
||||||
public class HduJudge implements RemoteJudgeStrategy {
|
public class HduJudge implements RemoteJudgeStrategy {
|
||||||
public static final String HOST = "https://acm.hdu.edu.cn";
|
public static final String HOST = "https://acm.dingbacode.com";
|
||||||
public static final String LOGIN_URL = "/userloginex.php?action=login";
|
public static final String LOGIN_URL = "/userloginex.php?action=login";
|
||||||
public static final String SUBMIT_URL = "/submit.php?action=submit";
|
public static final String SUBMIT_URL = "/submit.php?action=submit";
|
||||||
public static final String STATUS_URL = "/status.php?user=%s&pid=%s";
|
public static final String STATUS_URL = "/status.php?user=%s&pid=%s";
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package top.hcode.hoj.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:11
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel(value="AdminSysNotice", description="")
|
||||||
|
public class AdminSysNotice {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "通知标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "通知内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "发给哪些用户类型,例如全部用户All,指定单个用户Single,管理员Admin")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否已被拉取过,如果已经拉取过,就无需再次拉取")
|
||||||
|
private Boolean state;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "接受通知的用户的id,如果type为single,那么recipient 为该用户的id;否则recipient为null")
|
||||||
|
private String recipientId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "发布通知的管理员id")
|
||||||
|
private String adminId;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date gmtCreate;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date gmtModified;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package top.hcode.hoj.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:21
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel(value="MsgRemind", description="")
|
||||||
|
public class MsgRemind {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "动作类型,如点赞讨论帖Like_Post、点赞评论Like_Discuss、评论Discuss、回复Reply等")
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "消息来源id,讨论id或比赛id")
|
||||||
|
private Integer sourceId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件源类型:'Discussion'、'Contest'等")
|
||||||
|
private String sourceType;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件源的内容,比如回复的内容,回复的评论等等,不超过250字符,超过使用...")
|
||||||
|
private String sourceContent;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件引用上一级评论或回复id")
|
||||||
|
private Integer quoteId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件引用上一级的类型:Comment、Reply")
|
||||||
|
private String quoteType;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件所发生的地点链接 url")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "接受通知的用户的id")
|
||||||
|
private String recipientId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "动作执行者的id")
|
||||||
|
private String senderId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否已读")
|
||||||
|
private Boolean state;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date gmtCreate;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date gmtModified;
|
||||||
|
|
||||||
|
}
|
|
@ -92,7 +92,7 @@ public class Problem implements Serializable {
|
||||||
@TableField(value="spj_language",updateStrategy = FieldStrategy.IGNORED)
|
@TableField(value="spj_language",updateStrategy = FieldStrategy.IGNORED)
|
||||||
private String spjLanguage;
|
private String spjLanguage;
|
||||||
|
|
||||||
@ApiModelProperty(value = "是否默认去除用户代码的文末空格")
|
@ApiModelProperty(value = "是否默认去除用户代码的每行末尾空白符")
|
||||||
private Boolean isRemoveEndBlank;
|
private Boolean isRemoveEndBlank;
|
||||||
|
|
||||||
@ApiModelProperty(value = "是否默认开启该题目的测试样例结果查看")
|
@ApiModelProperty(value = "是否默认开启该题目的测试样例结果查看")
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package top.hcode.hoj.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: Himit_ZH
|
||||||
|
* @Date: 2021/10/1 20:18
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel(value="UserSysNotice", description="")
|
||||||
|
public class UserSysNotice {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "系统通知的id")
|
||||||
|
private Long sysNoticeId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "接受通知的用户的id")
|
||||||
|
private String recipientId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "消息类型,系统通知Sys、我的信息Mine")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否已读")
|
||||||
|
private Boolean state;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date gmtCreate;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date gmtModified;
|
||||||
|
|
||||||
|
}
|
|
@ -2565,7 +2565,7 @@
|
||||||
},
|
},
|
||||||
"async-validator": {
|
"async-validator": {
|
||||||
"version": "1.8.5",
|
"version": "1.8.5",
|
||||||
"resolved": "https://registry.npm.taobao.org/async-validator/download/async-validator-1.8.5.tgz?cache=0&sync_timestamp=1596623572478&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fasync-validator%2Fdownload%2Fasync-validator-1.8.5.tgz",
|
"resolved": "https://registry.nlark.com/async-validator/download/async-validator-1.8.5.tgz",
|
||||||
"integrity": "sha1-3D4I7B/Q3dtn5ghC8CwM0c7G1/A=",
|
"integrity": "sha1-3D4I7B/Q3dtn5ghC8CwM0c7G1/A=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"babel-runtime": "6.x"
|
"babel-runtime": "6.x"
|
||||||
|
@ -2647,7 +2647,7 @@
|
||||||
},
|
},
|
||||||
"babel-runtime": {
|
"babel-runtime": {
|
||||||
"version": "6.26.0",
|
"version": "6.26.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/babel-runtime/download/babel-runtime-6.26.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/babel-runtime/download/babel-runtime-6.26.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-runtime%2Fdownload%2Fbabel-runtime-6.26.0.tgz",
|
||||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"core-js": "^2.4.0",
|
"core-js": "^2.4.0",
|
||||||
|
@ -2655,13 +2655,13 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": {
|
"core-js": {
|
||||||
"version": "2.6.11",
|
"version": "2.6.12",
|
||||||
"resolved": "https://registry.npm.taobao.org/core-js/download/core-js-2.6.11.tgz?cache=0&sync_timestamp=1586450269267&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcore-js%2Fdownload%2Fcore-js-2.6.11.tgz",
|
"resolved": "https://registry.npmmirror.com/core-js/download/core-js-2.6.12.tgz",
|
||||||
"integrity": "sha1-OIMUafmSK97Y7iHJ3EaYXgOZMIw="
|
"integrity": "sha1-2TM9+nsGXjR8xWgiGdb2kIWcwuw="
|
||||||
},
|
},
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.11.1",
|
"version": "0.11.1",
|
||||||
"resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz?cache=0&sync_timestamp=1595456311465&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.11.1.tgz",
|
"resolved": "https://registry.nlark.com/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz?cache=0&sync_timestamp=1626993702812&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.11.1.tgz",
|
||||||
"integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk="
|
"integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4902,9 +4902,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"element-ui": {
|
"element-ui": {
|
||||||
"version": "2.14.0",
|
"version": "2.15.6",
|
||||||
"resolved": "https://registry.npm.taobao.org/element-ui/download/element-ui-2.14.0.tgz?cache=0&sync_timestamp=1603958155882&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felement-ui%2Fdownload%2Felement-ui-2.14.0.tgz",
|
"resolved": "https://registry.nlark.com/element-ui/download/element-ui-2.15.6.tgz",
|
||||||
"integrity": "sha1-3P8nGPtIVULqW1ofXA2vERNHeyQ=",
|
"integrity": "sha1-yWCa3TWvWmhqS3aF3B11fHXgHfM=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"async-validator": "~1.8.1",
|
"async-validator": "~1.8.1",
|
||||||
"babel-helper-vue-jsx-merge-props": "^2.0.0",
|
"babel-helper-vue-jsx-merge-props": "^2.0.0",
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
"compression-webpack-plugin": "^5.0.1",
|
"compression-webpack-plugin": "^5.0.1",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"echarts": "^4.9.0",
|
"echarts": "^4.9.0",
|
||||||
"element-ui": "^2.14.0",
|
"element-ui": "^2.15.3",
|
||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"highlight.js": "^10.3.2",
|
"highlight.js": "^10.3.2",
|
||||||
"jquery": "^3.5.1",
|
"jquery": "^3.5.1",
|
||||||
|
|
|
@ -515,6 +515,15 @@ footer h1 {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.el-empty {
|
||||||
|
max-width: 256px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.el-empty__description {
|
||||||
|
text-align: center;
|
||||||
|
color: #3498db;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<style>
|
<style>
|
||||||
.markdown-body pre {
|
.markdown-body pre {
|
||||||
|
|
|
@ -556,11 +556,13 @@ const ojApi = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
toLikeComment(cid,toLike){
|
toLikeComment(cid,toLike,sourceId,sourceType){
|
||||||
return ajax("/api/comment-like",'get',{
|
return ajax("/api/comment-like",'get',{
|
||||||
params:{
|
params:{
|
||||||
cid,
|
cid,
|
||||||
toLike
|
toLike,
|
||||||
|
sourceId,
|
||||||
|
sourceType
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -584,6 +586,71 @@ const ojApi = {
|
||||||
cid
|
cid
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// 站内消息
|
||||||
|
|
||||||
|
getUnreadMsgCount(){
|
||||||
|
return ajax("/api/msg/unread",'get')
|
||||||
|
},
|
||||||
|
|
||||||
|
getMsgList(routerName,searchParams){
|
||||||
|
let params ={};
|
||||||
|
Object.keys(searchParams).forEach((element) => {
|
||||||
|
if (searchParams[element]!==''&&searchParams[element]!==null&&searchParams[element]!==undefined) {
|
||||||
|
params[element] = searchParams[element]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
switch(routerName){
|
||||||
|
case "DiscussMsg":
|
||||||
|
return ajax("/api/msg/comment",'get',{
|
||||||
|
params
|
||||||
|
});
|
||||||
|
case "ReplyMsg":
|
||||||
|
return ajax("/api/msg/reply",'get',{
|
||||||
|
params
|
||||||
|
});
|
||||||
|
case "LikeMsg":
|
||||||
|
return ajax("/api/msg/like",'get',{
|
||||||
|
params
|
||||||
|
});
|
||||||
|
case "SysMsg":
|
||||||
|
return ajax("/api/msg/sys",'get',{
|
||||||
|
params
|
||||||
|
});
|
||||||
|
case "MineMsg":
|
||||||
|
return ajax("/api/msg/mine",'get',{
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cleanMsg(routerName,id){
|
||||||
|
let params ={};
|
||||||
|
if(id){
|
||||||
|
params.id=id;
|
||||||
|
}
|
||||||
|
switch(routerName){
|
||||||
|
case "DiscussMsg":
|
||||||
|
params.type = 'Discuss';
|
||||||
|
break;
|
||||||
|
case "ReplyMsg":
|
||||||
|
params.type = 'Reply';
|
||||||
|
break;
|
||||||
|
case "LikeMsg":
|
||||||
|
params.type = 'Like';
|
||||||
|
break;
|
||||||
|
case "SysMsg":
|
||||||
|
params.type = 'Sys';
|
||||||
|
break;
|
||||||
|
case "MineMsg":
|
||||||
|
params.type = 'Mine';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ajax("/api/msg/clean",'delete',{
|
||||||
|
params
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,6 +165,7 @@
|
||||||
id="commentbox"
|
id="commentbox"
|
||||||
v-for="(item, commentIndex) in comments"
|
v-for="(item, commentIndex) in comments"
|
||||||
:key="commentIndex"
|
:key="commentIndex"
|
||||||
|
v-loading="loading"
|
||||||
>
|
>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<span
|
<span
|
||||||
|
@ -322,7 +323,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="view-more item" v-if="item.totalReplyNum > 3">
|
<div class="view-more item" v-if="item.totalReplyNum > 3">
|
||||||
{{ $t('m.Reply_Total') }}<b>{{ item.totalReplyNum }}</b
|
{{ $t('m.Reply_Total') }}<b> {{ item.totalReplyNum }} </b
|
||||||
>{{ $t('m.Replies') }},
|
>{{ $t('m.Replies') }},
|
||||||
<a
|
<a
|
||||||
class="btn-more"
|
class="btn-more"
|
||||||
|
@ -541,6 +542,9 @@ export default {
|
||||||
toName: '',
|
toName: '',
|
||||||
toAvatar: '',
|
toAvatar: '',
|
||||||
},
|
},
|
||||||
|
replyQuoteId: null,
|
||||||
|
replyQuoteType: 'Comment',
|
||||||
|
loading: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -568,20 +572,27 @@ export default {
|
||||||
this.replyPlaceholder = this.$i18n.t(
|
this.replyPlaceholder = this.$i18n.t(
|
||||||
'm.Come_and_write_down_your_comments'
|
'm.Come_and_write_down_your_comments'
|
||||||
);
|
);
|
||||||
api.getCommentList(queryParams).then((res) => {
|
this.loading = true;
|
||||||
let moreCommentList = res.data.data.commentList.records;
|
api.getCommentList(queryParams).then(
|
||||||
for (let i = 0; i < moreCommentList.length; i++) {
|
(res) => {
|
||||||
this.totalComment += 1 + moreCommentList[i].totalReplyNum;
|
let moreCommentList = res.data.data.commentList.records;
|
||||||
|
for (let i = 0; i < moreCommentList.length; i++) {
|
||||||
|
this.totalComment += 1 + moreCommentList[i].totalReplyNum;
|
||||||
|
}
|
||||||
|
this.comments = this.comments.concat(moreCommentList);
|
||||||
|
this.total = res.data.data.commentList.total;
|
||||||
|
this.commentLikeMap = res.data.data.commentLikeMap;
|
||||||
|
if (this.comments.length > 0) {
|
||||||
|
this.$nextTick((_) => {
|
||||||
|
addCodeBtn();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.loading = false;
|
||||||
}
|
}
|
||||||
this.comments = this.comments.concat(moreCommentList);
|
);
|
||||||
this.total = res.data.data.commentList.total;
|
|
||||||
this.commentLikeMap = res.data.data.commentLikeMap;
|
|
||||||
if (this.comments.length > 0) {
|
|
||||||
this.$nextTick((_) => {
|
|
||||||
addCodeBtn();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -590,7 +601,16 @@ export default {
|
||||||
likeClick(item) {
|
likeClick(item) {
|
||||||
// 没有赞过则表明想去进行点赞
|
// 没有赞过则表明想去进行点赞
|
||||||
let toLike = this.commentLikeMap[item.id] != true;
|
let toLike = this.commentLikeMap[item.id] != true;
|
||||||
api.toLikeComment(item.id, toLike).then((res) => {
|
|
||||||
|
let sourceId = this.did;
|
||||||
|
let sourceType = 'Discussion';
|
||||||
|
|
||||||
|
if (this.cid != null) {
|
||||||
|
sourceId = this.cid;
|
||||||
|
sourceType = 'Contest';
|
||||||
|
}
|
||||||
|
|
||||||
|
api.toLikeComment(item.id, toLike, sourceId, sourceType).then((res) => {
|
||||||
if (toLike) {
|
if (toLike) {
|
||||||
this.commentLikeMap[item.id] = true;
|
this.commentLikeMap[item.id] = true;
|
||||||
item.likeNum++;
|
item.likeNum++;
|
||||||
|
@ -648,6 +668,8 @@ export default {
|
||||||
let replyData = {
|
let replyData = {
|
||||||
reply: this.replyObj,
|
reply: this.replyObj,
|
||||||
did: this.did,
|
did: this.did,
|
||||||
|
quoteId: this.replyQuoteId,
|
||||||
|
quoteType: this.replyQuoteType,
|
||||||
};
|
};
|
||||||
api.addReply(replyData).then((res) => {
|
api.addReply(replyData).then((res) => {
|
||||||
for (let i = 0; i < this.comments.length; i++) {
|
for (let i = 0; i < this.comments.length; i++) {
|
||||||
|
@ -690,6 +712,8 @@ export default {
|
||||||
this.replyObj.toUid = reply.fromUid;
|
this.replyObj.toUid = reply.fromUid;
|
||||||
this.replyObj.toName = reply.fromName;
|
this.replyObj.toName = reply.fromName;
|
||||||
this.replyObj.toAvatar = reply.fromAvatar;
|
this.replyObj.toAvatar = reply.fromAvatar;
|
||||||
|
this.replyQuoteId = reply.id;
|
||||||
|
this.replyQuoteType = 'Reply';
|
||||||
} else {
|
} else {
|
||||||
this.replyPlaceholder = this.$i18n.t(
|
this.replyPlaceholder = this.$i18n.t(
|
||||||
'm.Come_and_write_down_your_comments'
|
'm.Come_and_write_down_your_comments'
|
||||||
|
@ -698,6 +722,8 @@ export default {
|
||||||
this.replyObj.toUid = item.fromUid;
|
this.replyObj.toUid = item.fromUid;
|
||||||
this.replyObj.toName = item.fromName;
|
this.replyObj.toName = item.fromName;
|
||||||
this.replyObj.toAvatar = item.fromAvatar;
|
this.replyObj.toAvatar = item.fromAvatar;
|
||||||
|
this.replyQuoteId = item.id;
|
||||||
|
this.replyQuoteType = 'Comment';
|
||||||
}
|
}
|
||||||
this.showItemId = item.id;
|
this.showItemId = item.id;
|
||||||
},
|
},
|
||||||
|
@ -965,6 +991,8 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
height: 200px;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
.emotionItem {
|
.emotionItem {
|
||||||
width: 10%;
|
width: 10%;
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
v-if="!announcements.length"
|
v-if="!announcements.length"
|
||||||
key="no-announcement"
|
key="no-announcement"
|
||||||
>
|
>
|
||||||
<p>{{ $t('m.No_Announcements') }}</p>
|
<el-empty :description="$t('m.No_Announcements')"></el-empty>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="listVisible">
|
<template v-if="listVisible">
|
||||||
<ul class="announcements-container" key="list">
|
<ul class="announcements-container" key="list">
|
||||||
|
|
|
@ -111,6 +111,62 @@
|
||||||
:src="avatar"
|
:src="avatar"
|
||||||
class="drop-avatar"
|
class="drop-avatar"
|
||||||
></avatar>
|
></avatar>
|
||||||
|
<el-dropdown
|
||||||
|
class="drop-msg"
|
||||||
|
@command="handleRoute"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
<i class="el-icon-message-solid"></i>
|
||||||
|
<svg
|
||||||
|
v-if="
|
||||||
|
unreadMessage.comment > 0 ||
|
||||||
|
unreadMessage.reply > 0 ||
|
||||||
|
unreadMessage.like > 0 ||
|
||||||
|
unreadMessage.sys > 0 ||
|
||||||
|
unreadMessage.mine > 0
|
||||||
|
"
|
||||||
|
width="10"
|
||||||
|
height="10"
|
||||||
|
style="vertical-align: top;margin-left: -11px;margin-top: 3px;"
|
||||||
|
>
|
||||||
|
<circle cx="5" cy="5" r="5" style="fill: red;"></circle>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item command="/message/discuss">
|
||||||
|
<span>{{ $t('m.DiscussMsg') }}</span>
|
||||||
|
<span class="drop-msg-count" v-if="unreadMessage.comment > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.comment"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="/message/reply">
|
||||||
|
<span>{{ $t('m.ReplyMsg') }}</span>
|
||||||
|
<span class="drop-msg-count" v-if="unreadMessage.reply > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.reply"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="/message/like">
|
||||||
|
<span>{{ $t('m.LikeMsg') }}</span>
|
||||||
|
<span class="drop-msg-count" v-if="unreadMessage.like > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.like"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="/message/sys">
|
||||||
|
<span>{{ $t('m.SysMsg') }}</span>
|
||||||
|
<span class="drop-msg-count" v-if="unreadMessage.sys > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.sys"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="/message/mine">
|
||||||
|
<span>{{ $t('m.MineMsg') }}</span>
|
||||||
|
<span class="drop-msg-count" v-if="unreadMessage.mine > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.mine"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</div>
|
</div>
|
||||||
|
@ -138,13 +194,98 @@
|
||||||
>{{ $t('m.NavBar_Register') }}</mu-button
|
>{{ $t('m.NavBar_Register') }}</mu-button
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<mu-menu slot="right" v-show="isAuthenticated" :open.sync="openmsgmenu">
|
||||||
|
<mu-button flat>
|
||||||
|
<mu-icon value=":el-icon-message-solid" size="24"></mu-icon>
|
||||||
|
<svg
|
||||||
|
v-if="
|
||||||
|
unreadMessage.comment > 0 ||
|
||||||
|
unreadMessage.reply > 0 ||
|
||||||
|
unreadMessage.like > 0 ||
|
||||||
|
unreadMessage.sys > 0 ||
|
||||||
|
unreadMessage.mine > 0
|
||||||
|
"
|
||||||
|
width="10"
|
||||||
|
height="10"
|
||||||
|
style="margin-left: -11px;margin-top: -13px;"
|
||||||
|
>
|
||||||
|
<circle cx="5" cy="5" r="5" style="fill: red;"></circle>
|
||||||
|
</svg>
|
||||||
|
</mu-button>
|
||||||
|
<mu-list slot="content" @change="handleCommand">
|
||||||
|
<mu-list-item button value="/message/discuss">
|
||||||
|
<mu-list-item-content>
|
||||||
|
<mu-list-item-title>
|
||||||
|
{{ $t('m.DiscussMsg') }}
|
||||||
|
<span class="drop-msg-count" v-if="unreadMessage.comment > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.comment"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</mu-list-item-title>
|
||||||
|
</mu-list-item-content>
|
||||||
|
</mu-list-item>
|
||||||
|
<mu-divider></mu-divider>
|
||||||
|
<mu-list-item button value="/message/reply">
|
||||||
|
<mu-list-item-content>
|
||||||
|
<mu-list-item-title>
|
||||||
|
{{ $t('m.ReplyMsg') }}
|
||||||
|
<span class="drop-msg-count" v-if="unreadMessage.reply > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.reply"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</mu-list-item-title>
|
||||||
|
</mu-list-item-content>
|
||||||
|
</mu-list-item>
|
||||||
|
<mu-divider></mu-divider>
|
||||||
|
<mu-list-item button value="/message/like">
|
||||||
|
<mu-list-item-content>
|
||||||
|
<mu-list-item-title>
|
||||||
|
{{ $t('m.LikeMsg') }}
|
||||||
|
<span class="drop-msg-count" v-if="unreadMessage.like > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.like"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</mu-list-item-title>
|
||||||
|
</mu-list-item-content>
|
||||||
|
</mu-list-item>
|
||||||
|
<mu-divider></mu-divider>
|
||||||
|
<mu-list-item button value="/message/sys">
|
||||||
|
<mu-list-item-content>
|
||||||
|
<mu-list-item-title>
|
||||||
|
{{ $t('m.SysMsg') }}
|
||||||
|
<span class="drop-msg-count" v-if="unreadMessage.sys > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.sys"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</mu-list-item-title>
|
||||||
|
</mu-list-item-content>
|
||||||
|
</mu-list-item>
|
||||||
|
<mu-divider></mu-divider>
|
||||||
|
|
||||||
|
<mu-list-item button value="/message/mine">
|
||||||
|
<mu-list-item-content>
|
||||||
|
<mu-list-item-title>
|
||||||
|
{{ $t('m.MineMsg') }}
|
||||||
|
<span class="drop-msg-count" v-if="unreadMessage.mine > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.mine"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</mu-list-item-title>
|
||||||
|
</mu-list-item-content>
|
||||||
|
</mu-list-item>
|
||||||
|
</mu-list>
|
||||||
|
</mu-menu>
|
||||||
|
|
||||||
<mu-menu
|
<mu-menu
|
||||||
slot="right"
|
slot="right"
|
||||||
v-show="isAuthenticated"
|
v-show="isAuthenticated"
|
||||||
:open.sync="openusermenu"
|
:open.sync="openusermenu"
|
||||||
>
|
>
|
||||||
<mu-button flat>
|
<mu-button flat>
|
||||||
{{ userInfo.username }}<i class="el-icon-caret-bottom"></i>
|
<avatar
|
||||||
|
:username="userInfo.username"
|
||||||
|
:inline="true"
|
||||||
|
:size="30"
|
||||||
|
color="#FFF"
|
||||||
|
:src="userInfo.avatar"
|
||||||
|
:title="userInfo.username"
|
||||||
|
></avatar>
|
||||||
|
<i class="el-icon-caret-bottom"></i>
|
||||||
</mu-button>
|
</mu-button>
|
||||||
<mu-list slot="content" @change="handleCommand">
|
<mu-list slot="content" @change="handleCommand">
|
||||||
<mu-list-item button value="/user-home">
|
<mu-list-item button value="/user-home">
|
||||||
|
@ -154,7 +295,7 @@
|
||||||
}}</mu-list-item-title>
|
}}</mu-list-item-title>
|
||||||
</mu-list-item-content>
|
</mu-list-item-content>
|
||||||
</mu-list-item>
|
</mu-list-item>
|
||||||
|
<mu-divider></mu-divider>
|
||||||
<mu-list-item button value="/status?onlyMine=true">
|
<mu-list-item button value="/status?onlyMine=true">
|
||||||
<mu-list-item-content>
|
<mu-list-item-content>
|
||||||
<mu-list-item-title>{{
|
<mu-list-item-title>{{
|
||||||
|
@ -162,6 +303,7 @@
|
||||||
}}</mu-list-item-title>
|
}}</mu-list-item-title>
|
||||||
</mu-list-item-content>
|
</mu-list-item-content>
|
||||||
</mu-list-item>
|
</mu-list-item>
|
||||||
|
<mu-divider></mu-divider>
|
||||||
<mu-list-item button value="/setting">
|
<mu-list-item button value="/setting">
|
||||||
<mu-list-item-content>
|
<mu-list-item-content>
|
||||||
<mu-list-item-title>{{
|
<mu-list-item-title>{{
|
||||||
|
@ -169,7 +311,7 @@
|
||||||
}}</mu-list-item-title>
|
}}</mu-list-item-title>
|
||||||
</mu-list-item-content>
|
</mu-list-item-content>
|
||||||
</mu-list-item>
|
</mu-list-item>
|
||||||
|
<mu-divider></mu-divider>
|
||||||
<mu-list-item button value="/admin" v-show="isAdminRole">
|
<mu-list-item button value="/admin" v-show="isAdminRole">
|
||||||
<mu-list-item-content>
|
<mu-list-item-content>
|
||||||
<mu-list-item-title>{{
|
<mu-list-item-title>{{
|
||||||
|
@ -367,21 +509,32 @@
|
||||||
import Login from '@/components/oj/common/Login';
|
import Login from '@/components/oj/common/Login';
|
||||||
import Register from '@/components/oj/common/Register';
|
import Register from '@/components/oj/common/Register';
|
||||||
import ResetPwd from '@/components/oj/common/ResetPassword';
|
import ResetPwd from '@/components/oj/common/ResetPassword';
|
||||||
|
import MsgSvg from '@/components/oj/msg/msgSvg';
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import Avatar from 'vue-avatar';
|
import Avatar from 'vue-avatar';
|
||||||
|
import api from '@/common/api';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Login,
|
Login,
|
||||||
Register,
|
Register,
|
||||||
ResetPwd,
|
ResetPwd,
|
||||||
Avatar,
|
Avatar,
|
||||||
|
MsgSvg,
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
window.onresize = () => {
|
window.onresize = () => {
|
||||||
this.page_width();
|
this.page_width();
|
||||||
};
|
};
|
||||||
this.page_width();
|
this.page_width();
|
||||||
|
if (this.isAuthenticated) {
|
||||||
|
this.getUnreadMsgCount();
|
||||||
|
this.msgTimer = setInterval(() => {
|
||||||
|
this.getUnreadMsgCount();
|
||||||
|
}, 120 * 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
clearInterval(this.msgTimer);
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -389,8 +542,10 @@ export default {
|
||||||
mobileNar: false,
|
mobileNar: false,
|
||||||
opendrawer: false,
|
opendrawer: false,
|
||||||
openusermenu: false,
|
openusermenu: false,
|
||||||
|
openmsgmenu: false,
|
||||||
openSideMenu: '',
|
openSideMenu: '',
|
||||||
imgUrl: require('@/assets/logo.png'),
|
imgUrl: require('@/assets/logo.png'),
|
||||||
|
|
||||||
avatarStyle:
|
avatarStyle:
|
||||||
'display: inline-flex;width: 30px;height: 30px;border-radius: 50%;align-items: center;justify-content: center;text-align: center;user-select: none;',
|
'display: inline-flex;width: 30px;height: 30px;border-radius: 50%;align-items: center;justify-content: center;text-align: center;user-select: none;',
|
||||||
};
|
};
|
||||||
|
@ -422,12 +577,18 @@ export default {
|
||||||
handleCommand(route) {
|
handleCommand(route) {
|
||||||
// 移动端导航栏路由跳转事件
|
// 移动端导航栏路由跳转事件
|
||||||
this.openusermenu = false;
|
this.openusermenu = false;
|
||||||
|
this.openmsgmenu = false;
|
||||||
if (route && route.split('/')[1] != 'admin') {
|
if (route && route.split('/')[1] != 'admin') {
|
||||||
this.$router.push(route);
|
this.$router.push(route);
|
||||||
} else {
|
} else {
|
||||||
window.open('/admin/');
|
window.open('/admin/');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getUnreadMsgCount() {
|
||||||
|
api.getUnreadMsgCount().then((res) => {
|
||||||
|
this.$store.dispatch('updateUnreadMessageCount', res.data.data);
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
|
@ -437,6 +598,7 @@ export default {
|
||||||
'isAdminRole',
|
'isAdminRole',
|
||||||
'token',
|
'token',
|
||||||
'websiteConfig',
|
'websiteConfig',
|
||||||
|
'unreadMessage',
|
||||||
]),
|
]),
|
||||||
avatar() {
|
avatar() {
|
||||||
return this.$store.getters.userInfo.avatar;
|
return this.$store.getters.userInfo.avatar;
|
||||||
|
@ -535,6 +697,16 @@ export default {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
.drop-msg {
|
||||||
|
float: right;
|
||||||
|
font-size: 25px;
|
||||||
|
margin-right: 10px;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 13px;
|
||||||
|
}
|
||||||
|
.drop-msg-count {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
.btn-menu {
|
.btn-menu {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
float: right;
|
float: right;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
:layout="layout"
|
:layout="layout"
|
||||||
:page-sizes="[10, 15, 30, 50, 100]"
|
:page-sizes="[10, 15, 30, 50, 100]"
|
||||||
:current-page="current"
|
:current-page="current"
|
||||||
|
:hide-on-single-page="total == 0"
|
||||||
></el-pagination>
|
></el-pagination>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<template>
|
||||||
|
<svg width="20" height="20">
|
||||||
|
<circle cx="10" cy="10" r="10" style="fill: red;"></circle>
|
||||||
|
<text x="2" dy="15" style="fill: white" v-if="total >= 10">
|
||||||
|
{{ total > 99 ? 99 : total }}
|
||||||
|
</text>
|
||||||
|
<text x="6" dy="15" style="fill: white" v-else-if="total > 0">
|
||||||
|
{{ total }}
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'msgSvg',
|
||||||
|
props: {
|
||||||
|
total: {
|
||||||
|
required: true,
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -192,7 +192,7 @@ export const m = {
|
||||||
Use_Manual_Input:'Use Manual Input',
|
Use_Manual_Input:'Use Manual Input',
|
||||||
Hint: 'Hint',
|
Hint: 'Hint',
|
||||||
Source: 'Source',
|
Source: 'Source',
|
||||||
Auto_Remove_the_Blank_at_the_End_of_Code:'Auto Remove the Blank at the End of Code',
|
Auto_Remove_the_Blank_at_the_End_of_Code:'Automatically Remove Whitespace at The End of Each Line of Code',
|
||||||
Publish_the_Judging_Result_of_Test_Data:'Publish the Judging Result of Test Data',
|
Publish_the_Judging_Result_of_Test_Data:'Publish the Judging Result of Test Data',
|
||||||
Edit_Problem: 'Edit Problme',
|
Edit_Problem: 'Edit Problme',
|
||||||
Create_Problme: 'Create Problem',
|
Create_Problme: 'Create Problem',
|
||||||
|
|
|
@ -102,6 +102,16 @@ export const m = {
|
||||||
Announcement_Content: '公告内容',
|
Announcement_Content: '公告内容',
|
||||||
Announcement_visible: '是否可见',
|
Announcement_visible: '是否可见',
|
||||||
Delete_Announcement_Tips:'你确实是否删除该公告?',
|
Delete_Announcement_Tips:'你确实是否删除该公告?',
|
||||||
|
|
||||||
|
// /views/admin/general/SysNotice.vue
|
||||||
|
SysNotice: '系统通知',
|
||||||
|
Edit_Notice:'编辑通知',
|
||||||
|
Create_Notice:'创建通知',
|
||||||
|
Delete_Notice:'删除通知',
|
||||||
|
Notice_Title: '通知标题',
|
||||||
|
Notice_Content: '通知内容',
|
||||||
|
Notice_Push:'已推送',
|
||||||
|
Delete_Notice_Tips:'你确实是否删除该通知?',
|
||||||
|
|
||||||
// /views/admin/general/SystemConfig.vue
|
// /views/admin/general/SystemConfig.vue
|
||||||
Website_Config:'网站设置',
|
Website_Config:'网站设置',
|
||||||
|
@ -190,7 +200,7 @@ export const m = {
|
||||||
Use_Manual_Input:'使用手动输入',
|
Use_Manual_Input:'使用手动输入',
|
||||||
Hint: '提示',
|
Hint: '提示',
|
||||||
Source: '来源',
|
Source: '来源',
|
||||||
Auto_Remove_the_Blank_at_the_End_of_Code:'自动去除代码末尾空白符',
|
Auto_Remove_the_Blank_at_the_End_of_Code:'自动去除代码每行末尾空白符',
|
||||||
Publish_the_Judging_Result_of_Test_Data:'公开评测点数据结果',
|
Publish_the_Judging_Result_of_Test_Data:'公开评测点数据结果',
|
||||||
Edit_Problem: '编辑题目',
|
Edit_Problem: '编辑题目',
|
||||||
Create_Problme: '创建题目',
|
Create_Problme: '创建题目',
|
||||||
|
|
|
@ -435,5 +435,24 @@ export const m = {
|
||||||
Delete_Reply_Tips:'This operation will delete the reply. Do you want to continue?',
|
Delete_Reply_Tips:'This operation will delete the reply. Do you want to continue?',
|
||||||
|
|
||||||
|
|
||||||
|
// /views/oj/message/message.vue
|
||||||
|
Message_Center:'Message Center',
|
||||||
|
No_Data:'No Data',
|
||||||
|
|
||||||
|
// /views/oj/message/UserMsg.vue
|
||||||
|
Msg_Total:'Total',
|
||||||
|
Msg_Messages:'messages',
|
||||||
|
DiscussMsg:'Discuss',
|
||||||
|
ReplyMsg:'Reply',
|
||||||
|
LikeMsg:'Likes',
|
||||||
|
SysMsg:'System',
|
||||||
|
MineMsg:'Mine',
|
||||||
|
Clean_All:'Clean All',
|
||||||
|
Action_Like_Discuss:'Praised My Comment',
|
||||||
|
Action_Like_Post:'Praised My Discussion Post',
|
||||||
|
Action_Discuss:'Commented on My Discussion Post',
|
||||||
|
Action_Reply:'Responded to My Comment',
|
||||||
|
From_Discussion_Post:'From Discussion Post',
|
||||||
|
From_the_Contest:'From the Contest',
|
||||||
|
Delete_Msg_Tips:'Are you sure you want to delete the message?'
|
||||||
}
|
}
|
||||||
|
|
|
@ -417,7 +417,7 @@ export const m = {
|
||||||
|
|
||||||
// /components/oj/comment/comment.vue
|
// /components/oj/comment/comment.vue
|
||||||
Announcement_of_contest_Q_and_A_area:'比赛评论区公告',
|
Announcement_of_contest_Q_and_A_area:'比赛评论区公告',
|
||||||
Announcement_of_contest_Q_and_A_area_tips1:'请不要在提问与比赛无关的问题,禁止灌水!',
|
Announcement_of_contest_Q_and_A_area_tips1:'请不要提问与比赛无关的问题,禁止灌水!',
|
||||||
Announcement_of_contest_Q_and_A_area_tips2:'比赛过程中,仅自己与比赛管理员的评论可见!',
|
Announcement_of_contest_Q_and_A_area_tips2:'比赛过程中,仅自己与比赛管理员的评论可见!',
|
||||||
Announcement_of_contest_Q_and_A_area_tips3:'比赛管理员评论不可回复,比赛结束评论恢复正常!',
|
Announcement_of_contest_Q_and_A_area_tips3:'比赛管理员评论不可回复,比赛结束评论恢复正常!',
|
||||||
Come_and_write_down_your_comments:'快来写下你的评论吧',
|
Come_and_write_down_your_comments:'快来写下你的评论吧',
|
||||||
|
@ -436,4 +436,26 @@ export const m = {
|
||||||
Load_More:'加载更多',
|
Load_More:'加载更多',
|
||||||
Delete_Comment_Tips:'此操作将删除该评论及其所有回复, 是否继续?',
|
Delete_Comment_Tips:'此操作将删除该评论及其所有回复, 是否继续?',
|
||||||
Delete_Reply_Tips:'此操作将删除该回复, 是否继续?',
|
Delete_Reply_Tips:'此操作将删除该回复, 是否继续?',
|
||||||
|
|
||||||
|
|
||||||
|
// /views/oj/message/message.vue
|
||||||
|
Message_Center:'消息中心',
|
||||||
|
No_Data:'暂无数据',
|
||||||
|
|
||||||
|
// /views/oj/message/UserMsg.vue
|
||||||
|
Msg_Total:'共',
|
||||||
|
Msg_Messages:'条',
|
||||||
|
DiscussMsg:'评论我的',
|
||||||
|
ReplyMsg:'回复我的',
|
||||||
|
LikeMsg:'收到的赞',
|
||||||
|
SysMsg:'系统通知',
|
||||||
|
MineMsg:'我的消息',
|
||||||
|
Clean_All:'清空全部',
|
||||||
|
Action_Like_Discuss:'赞了我的评论',
|
||||||
|
Action_Like_Post:'赞了我的讨论帖',
|
||||||
|
Action_Discuss:'评论了我的讨论帖',
|
||||||
|
Action_Reply:'回复了我的评论',
|
||||||
|
From_Discussion_Post:'来自讨论帖',
|
||||||
|
From_the_Contest:'来自比赛',
|
||||||
|
Delete_Msg_Tips:'你是否确定要删除或清空消息?'
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ import DiscussionList from "@/views/oj/discussion/discussionList.vue"
|
||||||
import Discussion from "@/views/oj/discussion/discussion.vue"
|
import Discussion from "@/views/oj/discussion/discussion.vue"
|
||||||
import Introduction from "@/views/oj/about/Introduction.vue"
|
import Introduction from "@/views/oj/about/Introduction.vue"
|
||||||
import Developer from "@/views/oj/about/Developer.vue"
|
import Developer from "@/views/oj/about/Developer.vue"
|
||||||
|
import Message from "@/views/oj/message/message.vue"
|
||||||
|
import UserMsg from "@/views/oj/message/UserMsg.vue"
|
||||||
|
import SysMsg from "@/views/oj/message/SysMsg.vue"
|
||||||
import NotFound from "@/views/404.vue"
|
import NotFound from "@/views/404.vue"
|
||||||
|
|
||||||
const ojRoutes = [
|
const ojRoutes = [
|
||||||
|
@ -206,6 +209,44 @@ const ojRoutes = [
|
||||||
meta: {title: 'Developer'},
|
meta: {title: 'Developer'},
|
||||||
component:Developer,
|
component:Developer,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name:'Message',
|
||||||
|
path:'/message/',
|
||||||
|
component:Message,
|
||||||
|
meta: { requireAuth: false, title: 'Message' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'DiscussMsg',
|
||||||
|
path: 'discuss',
|
||||||
|
component: UserMsg,
|
||||||
|
meta: { requireAuth: false,title: 'Discuss Message' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ReplyMsg',
|
||||||
|
path: 'reply',
|
||||||
|
component: UserMsg,
|
||||||
|
meta: { requireAuth: false,title: 'Reply Message' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LikeMsg',
|
||||||
|
path: 'like',
|
||||||
|
component: UserMsg,
|
||||||
|
meta: { requireAuth: false,title: 'Like Message' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SysMsg',
|
||||||
|
path: 'sys',
|
||||||
|
component: SysMsg,
|
||||||
|
meta: { requireAuth: false,title: 'System Message' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MineMsg',
|
||||||
|
path: 'mine',
|
||||||
|
component: SysMsg,
|
||||||
|
meta: { requireAuth: false,title: 'Mine Message' }
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '*',
|
path: '*',
|
||||||
meta: {title: '404'},
|
meta: {title: '404'},
|
||||||
|
|
|
@ -4,11 +4,19 @@ const state = {
|
||||||
userInfo: storage.get('userInfo'),
|
userInfo: storage.get('userInfo'),
|
||||||
token: localStorage.getItem('token'),
|
token: localStorage.getItem('token'),
|
||||||
loginFailNum:0,
|
loginFailNum:0,
|
||||||
|
unreadMessage:{
|
||||||
|
comment:0,
|
||||||
|
reply:0,
|
||||||
|
like:0,
|
||||||
|
sys:0,
|
||||||
|
mine:0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getters = {
|
const getters = {
|
||||||
userInfo: state => state.userInfo || {},
|
userInfo: state => state.userInfo || {},
|
||||||
token: state => state.token ||'',
|
token: state => state.token ||'',
|
||||||
|
unreadMessage:state => state.unreadMessage || {},
|
||||||
loginFailNum:state=>state.loginFailNum || 0,
|
loginFailNum:state=>state.loginFailNum || 0,
|
||||||
isAuthenticated: (state, getters) => {
|
isAuthenticated: (state, getters) => {
|
||||||
return !!getters.token
|
return !!getters.token
|
||||||
|
@ -60,6 +68,13 @@ const mutations = {
|
||||||
state.userInfo = {}
|
state.userInfo = {}
|
||||||
state.loginFailNum = 0
|
state.loginFailNum = 0
|
||||||
storage.clear()
|
storage.clear()
|
||||||
|
},
|
||||||
|
updateUnreadMessageCount(state, {unreadMessage}){
|
||||||
|
state.unreadMessage = unreadMessage
|
||||||
|
},
|
||||||
|
substractUnreadMessageCount(state,{needSubstractMsg}){
|
||||||
|
// 负数也没关系
|
||||||
|
state.unreadMessage[needSubstractMsg.name] = state.unreadMessage[needSubstractMsg.name]-needSubstractMsg.num;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +90,16 @@ const actions = {
|
||||||
clearUserInfoAndToken ({commit}) {
|
clearUserInfoAndToken ({commit}) {
|
||||||
commit('clearUserInfoAndToken')
|
commit('clearUserInfoAndToken')
|
||||||
},
|
},
|
||||||
|
updateUnreadMessageCount({commit},unreadMessage){
|
||||||
|
commit('updateUnreadMessageCount', {
|
||||||
|
unreadMessage: unreadMessage
|
||||||
|
})
|
||||||
|
},
|
||||||
|
substractUnreadMessageCount({commit},needSubstractMsg){
|
||||||
|
commit('substractUnreadMessageCount', {
|
||||||
|
needSubstractMsg: needSubstractMsg
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -0,0 +1,338 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-card>
|
||||||
|
<div slot="header">
|
||||||
|
<span class="panel-title home-title">{{ $t('m.SysNotice') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="create">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="openNoticeDialog(null)"
|
||||||
|
icon="el-icon-plus"
|
||||||
|
>{{ $t('m.Create') }}</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="list">
|
||||||
|
<vxe-table
|
||||||
|
:loading="loading"
|
||||||
|
ref="table"
|
||||||
|
:data="noticeList"
|
||||||
|
auto-resize
|
||||||
|
stripe
|
||||||
|
>
|
||||||
|
<vxe-table-column min-width="50" field="id" title="ID">
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
min-width="150"
|
||||||
|
field="title"
|
||||||
|
show-overflow
|
||||||
|
:title="$t('m.Notice_Title')"
|
||||||
|
>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
min-width="150"
|
||||||
|
field="gmtCreate"
|
||||||
|
:title="$t('m.Created_Time')"
|
||||||
|
>
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
{{ row.gmtCreate | localtime }}
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
min-width="150"
|
||||||
|
field="gmtModified"
|
||||||
|
:title="$t('m.Modified_Time')"
|
||||||
|
>
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
{{ row.gmtModified | localtime }}
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
min-width="150"
|
||||||
|
field="username"
|
||||||
|
show-overflow
|
||||||
|
:title="$t('m.Author')"
|
||||||
|
>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
min-width="100"
|
||||||
|
field="state"
|
||||||
|
:title="$t('m.Notice_Push')"
|
||||||
|
>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column title="Option" min-width="150">
|
||||||
|
<template v-slot="row">
|
||||||
|
<el-tooltip
|
||||||
|
class="item"
|
||||||
|
effect="dark"
|
||||||
|
:content="$t('m.Edit_Notice')"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
icon="el-icon-edit-outline"
|
||||||
|
@click.native="openNoticeDialog(row.row)"
|
||||||
|
size="mini"
|
||||||
|
type="primary"
|
||||||
|
></el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip
|
||||||
|
class="item"
|
||||||
|
effect="dark"
|
||||||
|
:content="$t('m.Delete_Notice')"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
icon="el-icon-delete-solid"
|
||||||
|
@click.native="deleteNotice(row.row.id)"
|
||||||
|
size="mini"
|
||||||
|
type="danger"
|
||||||
|
></el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
</vxe-table>
|
||||||
|
|
||||||
|
<div class="panel-options">
|
||||||
|
<el-pagination
|
||||||
|
v-if="!contestID"
|
||||||
|
class="page"
|
||||||
|
layout="prev, pager, next"
|
||||||
|
@current-change="currentChange"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:total="total"
|
||||||
|
>
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!--编辑公告对话框-->
|
||||||
|
<el-dialog
|
||||||
|
:title="noticeDialogTitle"
|
||||||
|
:visible.sync="showEditNoticeDialog"
|
||||||
|
:fullscreen="true"
|
||||||
|
@open="onOpenEditDialog"
|
||||||
|
>
|
||||||
|
<el-form label-position="top" :model="notice">
|
||||||
|
<el-form-item :label="$t('m.Notice_Title')" required>
|
||||||
|
<el-input
|
||||||
|
v-model="notice.title"
|
||||||
|
:placeholder="$t('m.Notice_Title')"
|
||||||
|
class="title-input"
|
||||||
|
>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('m.Notice_Content')" required>
|
||||||
|
<Editor :value.sync="notice.content"></Editor>
|
||||||
|
</el-form-item>
|
||||||
|
<div class="visible-box">
|
||||||
|
<span>{{ $t('m.Notice_visible') }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="notice.status"
|
||||||
|
:active-value="0"
|
||||||
|
:inactive-value="1"
|
||||||
|
active-text=""
|
||||||
|
inactive-text=""
|
||||||
|
>
|
||||||
|
</el-switch>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="danger" @click.native="showEditNoticeDialog = false">{{
|
||||||
|
$t('m.Cancel')
|
||||||
|
}}</el-button>
|
||||||
|
<el-button type="primary" @click.native="submitNotice">{{
|
||||||
|
$t('m.OK')
|
||||||
|
}}</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import api from '@/common/api';
|
||||||
|
import myMessage from '@/common/message';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
const Editor = () => import('@/components/admin/Editor.vue');
|
||||||
|
export default {
|
||||||
|
name: 'notice',
|
||||||
|
components: {
|
||||||
|
Editor,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
contestID: '',
|
||||||
|
// 显示编辑公告对话框
|
||||||
|
showEditNoticeDialog: false,
|
||||||
|
// 公告列表
|
||||||
|
noticeList: [],
|
||||||
|
// 一页显示的公告数
|
||||||
|
pageSize: 15,
|
||||||
|
// 总公告数
|
||||||
|
total: 0,
|
||||||
|
mode: 'create',
|
||||||
|
// 公告 (new | edit) model
|
||||||
|
|
||||||
|
notice: {
|
||||||
|
id: null,
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
status: 0,
|
||||||
|
uid: '',
|
||||||
|
},
|
||||||
|
// 对话框标题
|
||||||
|
noticeDialogTitle: 'Edit Notice',
|
||||||
|
// 是否显示loading
|
||||||
|
loading: false,
|
||||||
|
// 当前页码
|
||||||
|
currentPage: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
this.getNoticeList(1);
|
||||||
|
},
|
||||||
|
// 切换页码回调
|
||||||
|
currentChange(page) {
|
||||||
|
this.currentPage = page;
|
||||||
|
if (this.contestID) {
|
||||||
|
this.getContestNoticeList(page);
|
||||||
|
} else {
|
||||||
|
this.getNoticeList(page);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getNoticeList(page) {
|
||||||
|
this.loading = true;
|
||||||
|
api.admin_getNoticeList(page, this.pageSize).then(
|
||||||
|
(res) => {
|
||||||
|
this.loading = false;
|
||||||
|
this.total = res.data.data.total;
|
||||||
|
this.noticeList = res.data.data.records;
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// 打开编辑对话框的回调
|
||||||
|
onOpenEditDialog() {
|
||||||
|
// todo 优化
|
||||||
|
// 暂时解决 文本编辑器显示异常bug
|
||||||
|
setTimeout(() => {
|
||||||
|
if (document.createEvent) {
|
||||||
|
let event = document.createEvent('HTMLEvents');
|
||||||
|
event.initEvent('resize', true, true);
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
} else if (document.createEventObject) {
|
||||||
|
window.fireEvent('onresize');
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
// 提交编辑
|
||||||
|
// 默认传入MouseEvent
|
||||||
|
submitNotice(data = undefined) {
|
||||||
|
if (!data.id) {
|
||||||
|
data = this.notice;
|
||||||
|
}
|
||||||
|
let funcName =
|
||||||
|
this.mode === 'edit' ? 'admin_updateNotice' : 'admin_createNotice';
|
||||||
|
let = requestData = data;
|
||||||
|
|
||||||
|
api[funcName](requestData)
|
||||||
|
.then((res) => {
|
||||||
|
this.showEditNoticeDialog = false;
|
||||||
|
myMessage.success(this.$i18n.t('m.Post_successfully'));
|
||||||
|
this.init();
|
||||||
|
})
|
||||||
|
.catch();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除通知
|
||||||
|
deleteNotice(noticeId) {
|
||||||
|
this.$confirm(this.$i18n.t('m.Delete_Notice_Tips'), 'Warning', {
|
||||||
|
confirmButtonText: this.$i18n.t('m.OK'),
|
||||||
|
cancelButtonText: this.$i18n.t('m.Cancel'),
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// then 为确定
|
||||||
|
this.loading = true;
|
||||||
|
let funcName = 'admin_deleteNotice';
|
||||||
|
api[funcName](noticeId).then((res) => {
|
||||||
|
this.loading = true;
|
||||||
|
myMessage.success(res.data.msg);
|
||||||
|
this.init();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// catch 为取消
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
openNoticeDialog(row) {
|
||||||
|
this.showEditNoticeDialog = true;
|
||||||
|
if (row !== null) {
|
||||||
|
this.noticeDialogTitle = this.$i18n.t('m.Edit_Notice');
|
||||||
|
this.notice = Object.assign({}, row);
|
||||||
|
this.mode = 'edit';
|
||||||
|
} else {
|
||||||
|
this.noticeDialogTitle = this.$i18n.t('m.Create_Notice');
|
||||||
|
this.notice.title = '';
|
||||||
|
this.notice.status = 0;
|
||||||
|
this.notice.content = '';
|
||||||
|
this.notice.uid = this.userInfo.uid;
|
||||||
|
this.notice.username = this.userInfo.username;
|
||||||
|
this.mode = 'create';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleVisibleSwitch(row) {
|
||||||
|
this.mode = 'edit';
|
||||||
|
this.submitNotice({
|
||||||
|
id: row.id,
|
||||||
|
title: row.title,
|
||||||
|
content: row.content,
|
||||||
|
status: row.status,
|
||||||
|
uid: row.uid,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route() {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['userInfo']),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.title-input {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visible-box {
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 205px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.visible-box span {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 2px !important;
|
||||||
|
}
|
||||||
|
/deep/.el-dialog__body {
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
.create {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -80,9 +80,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p id="no-contest" v-show="contests.length == 0">
|
<p id="no-contest" v-show="contests.length == 0">
|
||||||
{{ $t('m.No_contest') }}
|
<el-empty :description="$t('m.No_contest')"></el-empty>
|
||||||
</p>
|
</p>
|
||||||
<ol id="contest-list">
|
<ol id="contest-list" v-loading="loading">
|
||||||
<li
|
<li
|
||||||
v-for="contest in contests"
|
v-for="contest in contests"
|
||||||
:key="contest.title"
|
:key="contest.title"
|
||||||
|
@ -227,6 +227,7 @@ export default {
|
||||||
CONTEST_TYPE_REVERSE: CONTEST_TYPE_REVERSE,
|
CONTEST_TYPE_REVERSE: CONTEST_TYPE_REVERSE,
|
||||||
acmSrc: require('@/assets/acm.jpg'),
|
acmSrc: require('@/assets/acm.jpg'),
|
||||||
oiSrc: require('@/assets/oi.jpg'),
|
oiSrc: require('@/assets/oi.jpg'),
|
||||||
|
loading: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -244,10 +245,17 @@ export default {
|
||||||
this.getContestList();
|
this.getContestList();
|
||||||
},
|
},
|
||||||
getContestList(page = 1) {
|
getContestList(page = 1) {
|
||||||
api.getContestList(page, this.limit, this.query).then((res) => {
|
this.loading = true;
|
||||||
this.contests = res.data.data.records;
|
api.getContestList(page, this.limit, this.query).then(
|
||||||
this.total = res.data.data.total;
|
(res) => {
|
||||||
});
|
this.contests = res.data.data.records;
|
||||||
|
this.total = res.data.data.total;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
filterByChange() {
|
filterByChange() {
|
||||||
let query = Object.assign({}, this.query);
|
let query = Object.assign({}, this.query);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="container">
|
<div class="container" v-loading="loading">
|
||||||
<div class="title-article" style="text-align: left">
|
<div class="title-article" style="text-align: left">
|
||||||
<h1 class="title" id="sharetitle">
|
<h1 class="title" id="sharetitle">
|
||||||
<span>{{ discussion.title }}</span>
|
<span>{{ discussion.title }}</span>
|
||||||
|
@ -236,6 +236,7 @@ export default {
|
||||||
tagList: [],
|
tagList: [],
|
||||||
content: '',
|
content: '',
|
||||||
},
|
},
|
||||||
|
loading: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -246,14 +247,20 @@ export default {
|
||||||
init() {
|
init() {
|
||||||
this.routeName = this.$route.name;
|
this.routeName = this.$route.name;
|
||||||
this.discussionID = this.$route.params.discussionID || '';
|
this.discussionID = this.$route.params.discussionID || '';
|
||||||
|
this.loading = true;
|
||||||
api.getDiscussion(this.discussionID).then((res) => {
|
api.getDiscussion(this.discussionID).then(
|
||||||
this.discussion = res.data.data;
|
(res) => {
|
||||||
this.changeDomTitle({ title: this.discussion.title });
|
this.discussion = res.data.data;
|
||||||
this.$nextTick((_) => {
|
this.changeDomTitle({ title: this.discussion.title });
|
||||||
addCodeBtn();
|
this.$nextTick((_) => {
|
||||||
});
|
addCodeBtn();
|
||||||
});
|
});
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getInfoByUsername(uid, username) {
|
getInfoByUsername(uid, username) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :md="18" :xs="24">
|
<el-col :md="18" :xs="24" v-loading="loading.discussion">
|
||||||
<div class="discussion-header">
|
<div class="discussion-header">
|
||||||
<span style="padding: 16px;float:left;">
|
<span style="padding: 16px;float:left;">
|
||||||
<el-breadcrumb separator-class="el-icon-arrow-right">
|
<el-breadcrumb separator-class="el-icon-arrow-right">
|
||||||
|
@ -32,170 +32,150 @@
|
||||||
></vxe-input
|
></vxe-input
|
||||||
></span>
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<template v-if="discussionList.length > 0">
|
||||||
class="title-article"
|
<div
|
||||||
v-for="(discussion, index) in discussionList"
|
class="title-article"
|
||||||
:key="index"
|
v-for="(discussion, index) in discussionList"
|
||||||
>
|
:key="index"
|
||||||
<el-card shadow="hover" class="list-card">
|
>
|
||||||
<span class="svg-top" v-if="discussion.topPriority">
|
<el-card shadow="hover" class="list-card">
|
||||||
<svg
|
<span class="svg-top" v-if="discussion.topPriority">
|
||||||
t="1620283436433"
|
<svg
|
||||||
class="icon"
|
t="1620283436433"
|
||||||
viewBox="0 0 1024 1024"
|
class="icon"
|
||||||
version="1.1"
|
viewBox="0 0 1024 1024"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
version="1.1"
|
||||||
p-id="10095"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="48"
|
p-id="10095"
|
||||||
height="48"
|
width="48"
|
||||||
>
|
height="48"
|
||||||
<path
|
|
||||||
d="M989.9222626666667 444.3410103333334L580.1490096666668 34.909091333333336H119.41107066666666l870.511192 870.596525V444.3410103333334z"
|
|
||||||
fill="#F44336"
|
|
||||||
p-id="10096"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
d="M621.3675956666667 219.39846433333332l-43.832889-43.770828-126.663111 126.841535-32.826182-32.780929 126.663112-126.841535-43.734627-43.673859 26.739071-26.775273 120.396283 120.224324-26.741657 26.776565zM582.6055756666667 284.67587833333334c24.030384-24.065293 50.614303-36.636444 79.751758-37.71604 29.134869-1.07701 55.240404 9.903838 78.31402 32.945131 21.950061 21.91903 32.323232 46.86998 31.120808 74.851556s-13.257697 53.441939-36.167111 76.383677c-23.901091 23.934707-50.254869 36.406303-79.057455 37.41608-28.806465 1.012364-54.481455-9.739636-77.024969-32.252121-22.016-21.98497-32.689131-47.067798-32.014223-75.244606 0.672323-28.179394 12.365576-53.638465 35.077172-76.383677z m36.196849 32.57794c-14.921697 14.943677-23.517091 30.756202-25.783596 47.438869-2.269091 16.68396 2.880646 31.297939 15.441454 43.841939 12.825859 12.807758 27.34804 18.234182 43.566546 16.271515 16.217212-1.960081 31.985778-10.608485 47.303111-25.947798 15.976727-15.998707 25.133253-32.109899 27.46699-48.332283 2.333737-16.221091-2.813414-30.637253-15.441455-43.247192-12.827152-12.809051-27.67903-18.133333-44.558222-15.972848-16.879192 2.157899-32.877899 10.808889-47.994828 25.947798zM780.1276766666667 524.3048083333333l-53.476848 53.553131-32.726627-32.681374 153.400889-153.616808 52.858829 52.783839c38.213818 38.159515 41.146182 73.44097 8.79709 105.83402-15.71297 15.737535-34.076444 22.586182-55.086545 20.552404-21.012687-2.032485-39.97996-11.897535-56.905697-29.591273l-16.861091-16.833939z m74.572283-74.67701l-49.516606 49.586424 14.182141 14.161454c19.240081 19.211636 37.209212 20.455434 53.913859 3.728809 16.305131-16.329697 14.941091-34.002747-4.101172-53.016566L854.6999596666667 449.6277983333334z"
|
|
||||||
fill="#FFFFFF"
|
|
||||||
p-id="10097"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<h1 class="article-hlink">
|
|
||||||
<a @click="toDiscussionDetail(discussion.id)">{{
|
|
||||||
discussion.title
|
|
||||||
}}</a>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
size="mini"
|
|
||||||
style="margin-left:5px;"
|
|
||||||
v-if="discussion.pid"
|
|
||||||
@click="
|
|
||||||
pushRouter(
|
|
||||||
null,
|
|
||||||
{ problemID: discussion.pid },
|
|
||||||
'ProblemDetails'
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>{{ $t('m.Go_to_problem') }}</el-button
|
|
||||||
>
|
|
||||||
</h1>
|
|
||||||
<a
|
|
||||||
@click="toDiscussionDetail(discussion.id)"
|
|
||||||
class="article-hlink2"
|
|
||||||
>
|
|
||||||
<p>{{ discussion.description }}</p>
|
|
||||||
</a>
|
|
||||||
<div class="title-msg">
|
|
||||||
<span>
|
|
||||||
<a
|
|
||||||
@click="getInfoByUsername(discussion.uid, discussion.author)"
|
|
||||||
:title="discussion.author"
|
|
||||||
>
|
|
||||||
<avatar
|
|
||||||
:username="discussion.author"
|
|
||||||
:inline="true"
|
|
||||||
:size="24"
|
|
||||||
color="#FFF"
|
|
||||||
class="user-avatar"
|
|
||||||
:src="discussion.avatar"
|
|
||||||
></avatar>
|
|
||||||
<span class="pl">{{ discussion.author }}</span></a
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="role-root role"
|
|
||||||
title="Super Administrator"
|
|
||||||
v-if="discussion.role == 'root'"
|
|
||||||
>SPA</span
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="role-admin role"
|
|
||||||
title="Administrator"
|
|
||||||
v-if="discussion.role == 'admin'"
|
|
||||||
>ADM</span
|
|
||||||
>
|
>
|
||||||
|
<path
|
||||||
|
d="M989.9222626666667 444.3410103333334L580.1490096666668 34.909091333333336H119.41107066666666l870.511192 870.596525V444.3410103333334z"
|
||||||
|
fill="#F44336"
|
||||||
|
p-id="10096"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="M621.3675956666667 219.39846433333332l-43.832889-43.770828-126.663111 126.841535-32.826182-32.780929 126.663112-126.841535-43.734627-43.673859 26.739071-26.775273 120.396283 120.224324-26.741657 26.776565zM582.6055756666667 284.67587833333334c24.030384-24.065293 50.614303-36.636444 79.751758-37.71604 29.134869-1.07701 55.240404 9.903838 78.31402 32.945131 21.950061 21.91903 32.323232 46.86998 31.120808 74.851556s-13.257697 53.441939-36.167111 76.383677c-23.901091 23.934707-50.254869 36.406303-79.057455 37.41608-28.806465 1.012364-54.481455-9.739636-77.024969-32.252121-22.016-21.98497-32.689131-47.067798-32.014223-75.244606 0.672323-28.179394 12.365576-53.638465 35.077172-76.383677z m36.196849 32.57794c-14.921697 14.943677-23.517091 30.756202-25.783596 47.438869-2.269091 16.68396 2.880646 31.297939 15.441454 43.841939 12.825859 12.807758 27.34804 18.234182 43.566546 16.271515 16.217212-1.960081 31.985778-10.608485 47.303111-25.947798 15.976727-15.998707 25.133253-32.109899 27.46699-48.332283 2.333737-16.221091-2.813414-30.637253-15.441455-43.247192-12.827152-12.809051-27.67903-18.133333-44.558222-15.972848-16.879192 2.157899-32.877899 10.808889-47.994828 25.947798zM780.1276766666667 524.3048083333333l-53.476848 53.553131-32.726627-32.681374 153.400889-153.616808 52.858829 52.783839c38.213818 38.159515 41.146182 73.44097 8.79709 105.83402-15.71297 15.737535-34.076444 22.586182-55.086545 20.552404-21.012687-2.032485-39.97996-11.897535-56.905697-29.591273l-16.861091-16.833939z m74.572283-74.67701l-49.516606 49.586424 14.182141 14.161454c19.240081 19.211636 37.209212 20.455434 53.913859 3.728809 16.305131-16.329697 14.941091-34.002747-4.101172-53.016566L854.6999596666667 449.6277983333334z"
|
||||||
|
fill="#FFFFFF"
|
||||||
|
p-id="10097"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
|
<h1 class="article-hlink">
|
||||||
<span class="pr pl"
|
<a @click="toDiscussionDetail(discussion.id)">{{
|
||||||
><label class="fw"><i class="el-icon-chat-round"></i></label
|
discussion.title
|
||||||
><span>
|
}}</a>
|
||||||
<span class="hidden-xs-only"> {{ $t('m.Comment') }}:</span>
|
<el-button
|
||||||
{{ discussion.commentNum }}</span
|
type="primary"
|
||||||
></span
|
size="mini"
|
||||||
>
|
style="margin-left:5px;"
|
||||||
|
v-if="discussion.pid"
|
||||||
<span class="pr"
|
|
||||||
><label class="fw"><i class="fa fa-thumbs-o-up"></i></label
|
|
||||||
><span>
|
|
||||||
<span class="hidden-xs-only"> {{ $t('m.Likes') }}:</span>
|
|
||||||
{{ discussion.likeNum }}</span
|
|
||||||
></span
|
|
||||||
>
|
|
||||||
<span class="pr"
|
|
||||||
><label class="fw"><i class="fa fa-eye"></i></label
|
|
||||||
><span>
|
|
||||||
<span class="hidden-xs-only"> {{ $t('m.Views') }}:</span>
|
|
||||||
{{ discussion.viewNum }}</span
|
|
||||||
></span
|
|
||||||
>
|
|
||||||
<span class="pr"
|
|
||||||
><label class="fw"><i class="el-icon-folder-opened"></i></label>
|
|
||||||
<a
|
|
||||||
@click="
|
@click="
|
||||||
pushRouter(
|
pushRouter(
|
||||||
{ cid: discussion.categoryId, onlyMine: query.onlyMine },
|
null,
|
||||||
{ problemID: query.pid },
|
{ problemID: discussion.pid },
|
||||||
routeName
|
'ProblemDetails'
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
>{{ $t('m.Go_to_problem') }}</el-button
|
||||||
>
|
>
|
||||||
{{ cidMapName[discussion.categoryId] }}</a
|
</h1>
|
||||||
>
|
<a
|
||||||
</span>
|
@click="toDiscussionDetail(discussion.id)"
|
||||||
|
class="article-hlink2"
|
||||||
<span class="pr pl hidden-xs-only">
|
|
||||||
<label class="fw"><i class="fa fa-clock-o"></i></label
|
|
||||||
><span>
|
|
||||||
{{ $t('m.Release_Time') }}:<el-tooltip
|
|
||||||
:content="discussion.gmtCreate | localtime"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<span>{{ discussion.gmtCreate | fromNow }}</span>
|
|
||||||
</el-tooltip></span
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<el-dropdown
|
|
||||||
style="float:right;"
|
|
||||||
class="hidden-xs-only"
|
|
||||||
v-show="
|
|
||||||
isAuthenticated &&
|
|
||||||
(discussion.uid === userInfo.uid || isAdminRole)
|
|
||||||
"
|
|
||||||
@command="handleCommand"
|
|
||||||
>
|
>
|
||||||
<span class="el-dropdown-link">
|
<p>{{ discussion.description }}</p>
|
||||||
<i class="el-icon-more"></i>
|
</a>
|
||||||
|
<div class="title-msg">
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
@click="
|
||||||
|
getInfoByUsername(discussion.uid, discussion.author)
|
||||||
|
"
|
||||||
|
:title="discussion.author"
|
||||||
|
>
|
||||||
|
<avatar
|
||||||
|
:username="discussion.author"
|
||||||
|
:inline="true"
|
||||||
|
:size="24"
|
||||||
|
color="#FFF"
|
||||||
|
class="user-avatar"
|
||||||
|
:src="discussion.avatar"
|
||||||
|
></avatar>
|
||||||
|
<span class="pl">{{ discussion.author }}</span></a
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="role-root role"
|
||||||
|
title="Super Administrator"
|
||||||
|
v-if="discussion.role == 'root'"
|
||||||
|
>SPA</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="role-admin role"
|
||||||
|
title="Administrator"
|
||||||
|
v-if="discussion.role == 'admin'"
|
||||||
|
>ADM</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="pr pl"
|
||||||
|
><label class="fw"><i class="el-icon-chat-round"></i></label
|
||||||
|
><span>
|
||||||
|
<span class="hidden-xs-only"> {{ $t('m.Comment') }}:</span>
|
||||||
|
{{ discussion.commentNum }}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
|
||||||
|
<span class="pr"
|
||||||
|
><label class="fw"><i class="fa fa-thumbs-o-up"></i></label
|
||||||
|
><span>
|
||||||
|
<span class="hidden-xs-only"> {{ $t('m.Likes') }}:</span>
|
||||||
|
{{ discussion.likeNum }}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
<span class="pr"
|
||||||
|
><label class="fw"><i class="fa fa-eye"></i></label
|
||||||
|
><span>
|
||||||
|
<span class="hidden-xs-only"> {{ $t('m.Views') }}:</span>
|
||||||
|
{{ discussion.viewNum }}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
<span class="pr"
|
||||||
|
><label class="fw"
|
||||||
|
><i class="el-icon-folder-opened"></i
|
||||||
|
></label>
|
||||||
|
<a
|
||||||
|
@click="
|
||||||
|
pushRouter(
|
||||||
|
{
|
||||||
|
cid: discussion.categoryId,
|
||||||
|
onlyMine: query.onlyMine,
|
||||||
|
},
|
||||||
|
{ problemID: query.pid },
|
||||||
|
routeName
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ cidMapName[discussion.categoryId] }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="pr pl hidden-xs-only">
|
||||||
|
<label class="fw"><i class="fa fa-clock-o"></i></label
|
||||||
|
><span>
|
||||||
|
{{ $t('m.Release_Time') }}:<el-tooltip
|
||||||
|
:content="discussion.gmtCreate | localtime"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<span>{{ discussion.gmtCreate | fromNow }}</span>
|
||||||
|
</el-tooltip></span
|
||||||
|
>
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown-menu slot="dropdown">
|
|
||||||
<el-dropdown-item
|
|
||||||
icon="el-icon-edit-outline"
|
|
||||||
:command="'edit:' + index"
|
|
||||||
v-show="discussion.uid === userInfo.uid"
|
|
||||||
>{{ $t('m.Edit') }}</el-dropdown-item
|
|
||||||
>
|
|
||||||
<el-dropdown-item
|
|
||||||
icon="el-icon-delete"
|
|
||||||
:command="'delete:' + index"
|
|
||||||
v-show="discussion.uid === userInfo.uid || isAdminRole"
|
|
||||||
>{{ $t('m.Delete') }}</el-dropdown-item
|
|
||||||
>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</el-dropdown>
|
|
||||||
|
|
||||||
<div class="hidden-sm-and-up">
|
|
||||||
<el-dropdown
|
<el-dropdown
|
||||||
style="float:right;margin-top:10px; "
|
style="float:right;"
|
||||||
|
class="hidden-xs-only"
|
||||||
v-show="
|
v-show="
|
||||||
isAuthenticated &&
|
isAuthenticated &&
|
||||||
(discussion.uid === userInfo.uid || isAdminRole)
|
(discussion.uid === userInfo.uid || isAdminRole)
|
||||||
|
@ -221,19 +201,51 @@
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
|
|
||||||
<span class="pr" style="float:right;margin-top:10px; "
|
<div class="hidden-sm-and-up">
|
||||||
><label class="fw"><i class="fa fa-clock-o"></i></label
|
<el-dropdown
|
||||||
><span> {{ discussion.gmtCreate | localtime }}</span></span
|
style="float:right;margin-top:10px; "
|
||||||
>
|
v-show="
|
||||||
|
isAuthenticated &&
|
||||||
|
(discussion.uid === userInfo.uid || isAdminRole)
|
||||||
|
"
|
||||||
|
@command="handleCommand"
|
||||||
|
>
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
<i class="el-icon-more"></i>
|
||||||
|
</span>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item
|
||||||
|
icon="el-icon-edit-outline"
|
||||||
|
:command="'edit:' + index"
|
||||||
|
v-show="discussion.uid === userInfo.uid"
|
||||||
|
>{{ $t('m.Edit') }}</el-dropdown-item
|
||||||
|
>
|
||||||
|
<el-dropdown-item
|
||||||
|
icon="el-icon-delete"
|
||||||
|
:command="'delete:' + index"
|
||||||
|
v-show="discussion.uid === userInfo.uid || isAdminRole"
|
||||||
|
>{{ $t('m.Delete') }}</el-dropdown-item
|
||||||
|
>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
|
||||||
|
<span class="pr" style="float:right;margin-top:10px; "
|
||||||
|
><label class="fw"><i class="fa fa-clock-o"></i></label
|
||||||
|
><span> {{ discussion.gmtCreate | localtime }}</span></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</el-card>
|
||||||
</el-card>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-empty :description="$t('m.No_Data')"></el-empty>
|
||||||
|
</template>
|
||||||
<Pagination
|
<Pagination
|
||||||
:total="total"
|
:total="total"
|
||||||
:page-size="limit"
|
:page-size="query.limit"
|
||||||
@on-change="changeRoute"
|
@on-change="changeRoute"
|
||||||
:current.sync="currentPage"
|
:current.sync="query.currentPage"
|
||||||
></Pagination>
|
></Pagination>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :md="6" :xs="24">
|
<el-col :md="6" :xs="24">
|
||||||
|
@ -296,7 +308,7 @@
|
||||||
><i class="el-icon-folder-opened"></i> {{ $t('m.Category') }}</a
|
><i class="el-icon-folder-opened"></i> {{ $t('m.Category') }}</a
|
||||||
>
|
>
|
||||||
</h3>
|
</h3>
|
||||||
<el-row>
|
<el-row v-loading="loading.category">
|
||||||
<el-col
|
<el-col
|
||||||
:span="24"
|
:span="24"
|
||||||
class="category-item"
|
class="category-item"
|
||||||
|
@ -401,8 +413,6 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
total: 0,
|
total: 0,
|
||||||
limit: 10,
|
|
||||||
currentPage: 1,
|
|
||||||
showEditDiscussionDialog: false,
|
showEditDiscussionDialog: false,
|
||||||
discussion: {
|
discussion: {
|
||||||
id: null,
|
id: null,
|
||||||
|
@ -427,22 +437,33 @@ export default {
|
||||||
keyword: '',
|
keyword: '',
|
||||||
cid: '',
|
cid: '',
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
limit: 15,
|
limit: 8,
|
||||||
pid: '',
|
pid: '',
|
||||||
onlyMine: false,
|
onlyMine: false,
|
||||||
},
|
},
|
||||||
routeName: '',
|
routeName: '',
|
||||||
|
loading: {
|
||||||
|
discussion: true,
|
||||||
|
category: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.discussionDialogTitle = this.$i18n.t('m.Edit_Discussion');
|
this.discussionDialogTitle = this.$i18n.t('m.Edit_Discussion');
|
||||||
api.getCategoryList().then((res) => {
|
this.loading.category = true;
|
||||||
this.categoryList = res.data.data;
|
api.getCategoryList().then(
|
||||||
for (let i = 0; i < this.categoryList.length; i++) {
|
(res) => {
|
||||||
this.cidMapName[this.categoryList[i].id] = this.categoryList[i].name;
|
this.categoryList = res.data.data;
|
||||||
|
for (let i = 0; i < this.categoryList.length; i++) {
|
||||||
|
this.cidMapName[this.categoryList[i].id] = this.categoryList[i].name;
|
||||||
|
}
|
||||||
|
this.loading.category = false;
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.loading.category = false;
|
||||||
}
|
}
|
||||||
this.init();
|
);
|
||||||
});
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['changeDomTitle']),
|
...mapActions(['changeDomTitle']),
|
||||||
|
@ -475,10 +496,17 @@ export default {
|
||||||
|
|
||||||
getDiscussionList() {
|
getDiscussionList() {
|
||||||
let queryParams = Object.assign({}, this.query);
|
let queryParams = Object.assign({}, this.query);
|
||||||
api.getDiscussionList(this.limit, queryParams).then((res) => {
|
this.loading.discussion = true;
|
||||||
this.total = res.data.data.total;
|
api.getDiscussionList(this.limit, queryParams).then(
|
||||||
this.discussionList = res.data.data.records;
|
(res) => {
|
||||||
});
|
this.total = res.data.data.total;
|
||||||
|
this.discussionList = res.data.data.records;
|
||||||
|
this.loading.discussion = false;
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.loading.discussion = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
changeRoute(page) {
|
changeRoute(page) {
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
<template>
|
||||||
|
<div class="msg-wrap" v-loading="loading">
|
||||||
|
<h3 class="msg-list-header">
|
||||||
|
<span class="ft">{{ $t('m.' + route_name) }}</span>
|
||||||
|
<span class="fr"
|
||||||
|
>{{ $t('m.Msg_Total') + ' ' + total + ' ' + $t('m.Msg_Messages') }}
|
||||||
|
<span class="clear-all" @click="deleteMsg()">{{
|
||||||
|
$t('m.Clean_All')
|
||||||
|
}}</span></span
|
||||||
|
>
|
||||||
|
</h3>
|
||||||
|
<template v-if="dataList.length > 0">
|
||||||
|
<el-card class="box-card" v-for="(item, index) in dataList" :key="index">
|
||||||
|
<div class="msg-list-item">
|
||||||
|
<span class="svg-lt" v-if="!item.state">
|
||||||
|
<svg
|
||||||
|
t="1633158277197"
|
||||||
|
class="icon"
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="4745"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M512 322c-104.92 0-190 85.08-190 190s85.08 190 190 190 190-85.06 190-190-85.08-190-190-190z"
|
||||||
|
p-id="4746"
|
||||||
|
fill="#d81e06"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<div class="top">
|
||||||
|
<span class="title">{{ item.title }}</span>
|
||||||
|
<span class="extra"
|
||||||
|
><el-tooltip
|
||||||
|
:content="item.gmtCreate | localtime"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<span> {{ item.gmtCreate | fromNow }}</span>
|
||||||
|
</el-tooltip></span
|
||||||
|
>
|
||||||
|
<span class="extra delete"
|
||||||
|
><i class="el-icon-delete" @click="deleteMsg(item.id)">
|
||||||
|
{{ $t('m.Delete') }}</i
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bottom">
|
||||||
|
<span
|
||||||
|
class="content markdown-body"
|
||||||
|
v-highlight
|
||||||
|
v-html="$markDown.render(item.content)"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
<template v-else
|
||||||
|
><el-empty :description="$t('m.No_Data')"></el-empty>
|
||||||
|
</template>
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
:page-size="query.limit"
|
||||||
|
@on-change="changeRoute"
|
||||||
|
:current.sync="query.currentPage"
|
||||||
|
></Pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import Pagination from '@/components/oj/common/Pagination';
|
||||||
|
import api from '@/common/api';
|
||||||
|
import myMessage from '@/common/message';
|
||||||
|
export default {
|
||||||
|
components: { Pagination },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dataList: [],
|
||||||
|
loading: false,
|
||||||
|
total: 0,
|
||||||
|
query: { limit: 8, currentPage: 1 },
|
||||||
|
route_name: 'SysMsg',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.route_name = this.$route.name;
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
let query = this.$route.query;
|
||||||
|
this.query.currentPage = parseInt(query.currentPage) || 1;
|
||||||
|
if (this.query.currentPage < 1) {
|
||||||
|
this.query.currentPage = 1;
|
||||||
|
}
|
||||||
|
this.getMsgList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getMsgList() {
|
||||||
|
let queryParams = Object.assign({}, this.query);
|
||||||
|
this.loading = true;
|
||||||
|
api.getMsgList(this.route_name, queryParams).then(
|
||||||
|
(res) => {
|
||||||
|
this.total = res.data.data.total;
|
||||||
|
this.dataList = res.data.data.records;
|
||||||
|
this.loading = false;
|
||||||
|
this.substractUnreadMsgNum();
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
changeRoute(page) {
|
||||||
|
this.query.currentPage = page;
|
||||||
|
this.getMsgList();
|
||||||
|
},
|
||||||
|
goMsgSourceUrl(url) {
|
||||||
|
this.$router.push({
|
||||||
|
path: url,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getInfoByUsername(uid, username) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/user-home',
|
||||||
|
query: { uid, username },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteMsg(id = undefined) {
|
||||||
|
this.$confirm(this.$i18n.t('m.Delete_Msg_Tips'), 'Tips', {
|
||||||
|
confirmButtonText: this.$i18n.t('m.OK'),
|
||||||
|
cancelButtonText: this.$i18n.t('m.Cancel'),
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
api.cleanMsg(this.route_name, id).then((res) => {
|
||||||
|
myMessage.success(this.$i18n.t('m.Delete_successfully'));
|
||||||
|
this.getMsgList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
substractUnreadMsgNum() {
|
||||||
|
let countName;
|
||||||
|
switch (this.route_name) {
|
||||||
|
case 'SysMsg':
|
||||||
|
countName = 'sys';
|
||||||
|
break;
|
||||||
|
case 'MineMsg':
|
||||||
|
countName = 'mine';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let needSubstractMsg = {
|
||||||
|
name: countName,
|
||||||
|
num: this.limit,
|
||||||
|
};
|
||||||
|
this.$store.dispatch('substractUnreadMessageCount', needSubstractMsg);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.box-card {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.clear-all {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
.clear-all:hover {
|
||||||
|
color: red;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
.msg-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
padding-top: 0px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 767px) {
|
||||||
|
.msg-wrap {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.msg-list-header {
|
||||||
|
height: 35px;
|
||||||
|
border-bottom: 3px solid #eff3f5;
|
||||||
|
position: relative;
|
||||||
|
top: -10px;
|
||||||
|
}
|
||||||
|
.svg-lt {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
.fl {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.fr {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.msg-list-item {
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
color: #333;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.extra {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 22px;
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
.bottom {
|
||||||
|
color: #666;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
.delete:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: red;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,343 @@
|
||||||
|
<template>
|
||||||
|
<div class="msg-wrap" v-loading="loading">
|
||||||
|
<h3 class="msg-list-header">
|
||||||
|
<span class="ft">{{ $t('m.' + route_name) }}</span>
|
||||||
|
<span class="fr"
|
||||||
|
>{{ $t('m.Msg_Total') + ' ' + total + ' ' + $t('m.Msg_Messages') }}
|
||||||
|
<span class="clear-all" @click="deleteMsg()">{{
|
||||||
|
$t('m.Clean_All')
|
||||||
|
}}</span></span
|
||||||
|
>
|
||||||
|
</h3>
|
||||||
|
<template v-if="dataList.length > 0">
|
||||||
|
<el-card class="box-card" v-for="(item, index) in dataList" :key="index">
|
||||||
|
<div class="msg-list-item">
|
||||||
|
<span class="svg-lt" v-if="!item.state">
|
||||||
|
<svg
|
||||||
|
t="1633158277197"
|
||||||
|
class="icon"
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="4745"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M512 322c-104.92 0-190 85.08-190 190s85.08 190 190 190 190-85.06 190-190-85.08-190-190-190z"
|
||||||
|
p-id="4746"
|
||||||
|
fill="#d81e06"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
@click="getInfoByUsername(item.senderId, item.senderUsername)"
|
||||||
|
style="cursor: pointer;"
|
||||||
|
>
|
||||||
|
<avatar
|
||||||
|
:username="item.senderUsername"
|
||||||
|
:inline="true"
|
||||||
|
:size="40"
|
||||||
|
color="#FFF"
|
||||||
|
:src="item.senderAvatar"
|
||||||
|
:title="item.senderUsername"
|
||||||
|
></avatar>
|
||||||
|
</span>
|
||||||
|
<div class="title">
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
style="margin-right:3px;"
|
||||||
|
class="user-name"
|
||||||
|
@click="getInfoByUsername(item.senderId, item.senderUsername)"
|
||||||
|
:title="item.senderUsername"
|
||||||
|
>{{ item.senderUsername }}</span
|
||||||
|
>
|
||||||
|
<span class="msg-action">
|
||||||
|
{{ $t('m.Action_' + item.action) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div @click="goMsgSourceUrl(item.url)" style="cursor: pointer;">
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
v-if="item.sourceContent != null"
|
||||||
|
v-html="item.sourceContent"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="orginal-reply"
|
||||||
|
v-if="item.quoteContent != null"
|
||||||
|
v-html="item.quoteContent"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div class="extra-info">
|
||||||
|
<span
|
||||||
|
><i class="el-icon-time">
|
||||||
|
<el-tooltip
|
||||||
|
:content="item.gmtCreate | localtime"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<span> {{ item.gmtCreate | fromNow }}</span>
|
||||||
|
</el-tooltip></i
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
<span class="delete" @click="deleteMsg(item.id)"
|
||||||
|
><i class="el-icon-delete"> {{ $t('m.Delete') }}</i></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="link-discussion">
|
||||||
|
<span
|
||||||
|
>{{
|
||||||
|
item.sourceType == 'Discussion'
|
||||||
|
? $t('m.From_Discussion_Post')
|
||||||
|
: $t('m.From_the_Contest')
|
||||||
|
}}
|
||||||
|
<span class="title" @click="goMsgSourceUrl(item.url)"
|
||||||
|
>“{{ item.sourceTitle }}”</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
<template v-else
|
||||||
|
><el-empty :description="$t('m.No_Data')"></el-empty>
|
||||||
|
</template>
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
:page-size="query.limit"
|
||||||
|
@on-change="changeRoute"
|
||||||
|
:current.sync="query.currentPage"
|
||||||
|
></Pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import Avatar from 'vue-avatar';
|
||||||
|
import api from '@/common/api';
|
||||||
|
import myMessage from '@/common/message';
|
||||||
|
import Pagination from '@/components/oj/common/Pagination';
|
||||||
|
export default {
|
||||||
|
components: { Avatar, Pagination },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dataList: [],
|
||||||
|
query: {
|
||||||
|
currentPage: 1,
|
||||||
|
limit: 6,
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
total: 0,
|
||||||
|
route_name: 'DiscussMsg',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.route_name = this.$route.name;
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
let query = this.$route.query;
|
||||||
|
this.query.currentPage = parseInt(query.currentPage) || 1;
|
||||||
|
if (this.query.currentPage < 1) {
|
||||||
|
this.query.currentPage = 1;
|
||||||
|
}
|
||||||
|
this.getMsgList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getMsgList() {
|
||||||
|
let queryParams = Object.assign({}, this.query);
|
||||||
|
this.loading = true;
|
||||||
|
api.getMsgList(this.route_name, queryParams).then(
|
||||||
|
(res) => {
|
||||||
|
this.total = res.data.data.total;
|
||||||
|
this.dataList = res.data.data.records;
|
||||||
|
this.loading = false;
|
||||||
|
this.substractUnreadMsgNum();
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
changeRoute(page) {
|
||||||
|
this.query.currentPage = page;
|
||||||
|
this.getMsgList();
|
||||||
|
},
|
||||||
|
goMsgSourceUrl(url) {
|
||||||
|
this.$router.push({
|
||||||
|
path: url,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getInfoByUsername(uid, username) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/user-home',
|
||||||
|
query: { uid, username },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteMsg(id = undefined) {
|
||||||
|
this.$confirm(this.$i18n.t('m.Delete_Msg_Tips'), 'Tips', {
|
||||||
|
confirmButtonText: this.$i18n.t('m.OK'),
|
||||||
|
cancelButtonText: this.$i18n.t('m.Cancel'),
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
api.cleanMsg(this.route_name, id).then((res) => {
|
||||||
|
myMessage.success(this.$i18n.t('m.Delete_successfully'));
|
||||||
|
this.getMsgList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
substractUnreadMsgNum() {
|
||||||
|
let countName;
|
||||||
|
switch (this.route_name) {
|
||||||
|
case 'DiscussMsg':
|
||||||
|
countName = 'comment';
|
||||||
|
break;
|
||||||
|
case 'ReplyMsg':
|
||||||
|
countName = 'reply';
|
||||||
|
break;
|
||||||
|
case 'LikeMsg':
|
||||||
|
countName = 'like';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let needSubstractMsg = {
|
||||||
|
name: countName,
|
||||||
|
num: this.limit,
|
||||||
|
};
|
||||||
|
this.$store.dispatch('substractUnreadMessageCount', needSubstractMsg);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.box-card {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.clear-all {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
.clear-all:hover {
|
||||||
|
color: red;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
.msg-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
padding-top: 0px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 767px) {
|
||||||
|
.msg-wrap {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.msg-list-header {
|
||||||
|
height: 35px;
|
||||||
|
border-bottom: 3px solid #eff3f5;
|
||||||
|
position: relative;
|
||||||
|
top: -10px;
|
||||||
|
}
|
||||||
|
.svg-lt {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
.fl {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.fr {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.msg-list-item {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.msg-list-item .title {
|
||||||
|
color: #99a;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-left: 13px;
|
||||||
|
}
|
||||||
|
.msg-list-item .title .content {
|
||||||
|
color: #222;
|
||||||
|
word-break: break-word;
|
||||||
|
margin: 10px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-break: break-word;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
line-height: 20px;
|
||||||
|
max-height: 2.6em;
|
||||||
|
}
|
||||||
|
.user-name {
|
||||||
|
color: #666;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.user-name:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
.msg-action {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-list-item .orginal-reply {
|
||||||
|
color: #999;
|
||||||
|
border-left: 2px solid #e7e7e7;
|
||||||
|
margin: 8px 0 5px;
|
||||||
|
padding-left: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-break: break-word;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
max-height: 2.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-list-item .extra-info {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
.msg-list-item .extra-info span {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.msg-list-item .extra-info .delete:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.link-discussion {
|
||||||
|
color: #999;
|
||||||
|
font-size: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.link-discussion .title {
|
||||||
|
color: #409eff;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 767px) {
|
||||||
|
.link-discussion {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.msg-action {
|
||||||
|
font-size: 13px;
|
||||||
|
margin-left: 0px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.msg-list-item .title .content {
|
||||||
|
margin-left: -47px;
|
||||||
|
}
|
||||||
|
.msg-list-item .extra-info {
|
||||||
|
margin-left: -47px;
|
||||||
|
}
|
||||||
|
.msg-list-item .orginal-reply {
|
||||||
|
margin-left: -47px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,147 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-alert
|
||||||
|
type="success"
|
||||||
|
:closable="false"
|
||||||
|
center
|
||||||
|
class="msg-title"
|
||||||
|
effect="dark"
|
||||||
|
>
|
||||||
|
<template slot="title">
|
||||||
|
<span
|
||||||
|
><i class="el-icon-s-promotion">
|
||||||
|
{{ $t('m.Message_Center') }}</i
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
<el-tabs
|
||||||
|
tab-position="left"
|
||||||
|
type="border-card"
|
||||||
|
style="min-height: 500px;"
|
||||||
|
v-model="route_name"
|
||||||
|
@tab-click="handleRouter"
|
||||||
|
>
|
||||||
|
<el-tab-pane name="DiscussMsg">
|
||||||
|
<span slot="label">
|
||||||
|
<span>{{ $t('m.DiscussMsg') }}</span>
|
||||||
|
<span style=" margin-left: 2px;" v-if="unreadMessage.comment > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.comment"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<transition name="fadeInUp" mode="out-in">
|
||||||
|
<router-view v-if="route_name === 'DiscussMsg'"></router-view>
|
||||||
|
</transition>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane name="ReplyMsg">
|
||||||
|
<span slot="label">
|
||||||
|
<span>{{ $t('m.ReplyMsg') }}</span>
|
||||||
|
<span style=" margin-left: 2px;" v-if="unreadMessage.reply > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.reply"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<transition name="fadeInUp" mode="out-in">
|
||||||
|
<router-view v-if="route_name === 'ReplyMsg'"></router-view>
|
||||||
|
</transition>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane name="LikeMsg">
|
||||||
|
<span slot="label">
|
||||||
|
<span>{{ $t('m.LikeMsg') }}</span>
|
||||||
|
<span style=" margin-left: 2px;" v-if="unreadMessage.like > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.like"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<transition name="fadeInUp" mode="out-in">
|
||||||
|
<router-view v-if="route_name === 'LikeMsg'"></router-view>
|
||||||
|
</transition>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane name="SysMsg">
|
||||||
|
<span slot="label">
|
||||||
|
<span>{{ $t('m.SysMsg') }}</span>
|
||||||
|
<span style=" margin-left: 2px;" v-if="unreadMessage.sys > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.sys"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<transition name="fadeInUp" mode="out-in">
|
||||||
|
<router-view v-if="route_name === 'SysMsg'"></router-view>
|
||||||
|
</transition>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane name="MineMsg">
|
||||||
|
<span slot="label">
|
||||||
|
<span>{{ $t('m.MineMsg') }}</span>
|
||||||
|
<span style=" margin-left: 2px;" v-if="unreadMessage.mine > 0">
|
||||||
|
<MsgSvg :total="unreadMessage.mine"></MsgSvg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<transition name="fadeInUp" mode="out-in">
|
||||||
|
<router-view v-if="route_name === 'MineMsg'"></router-view>
|
||||||
|
</transition>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import MsgSvg from '@/components/oj/msg/msgSvg';
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
MsgSvg,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
route_name: 'DiscussMsg',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.route_name = this.$route.name;
|
||||||
|
if (this.route_name === 'Message') {
|
||||||
|
this.route_name = 'DiscussMsg';
|
||||||
|
}
|
||||||
|
this.$router.push({ name: this.route_name });
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleRouter(tab) {
|
||||||
|
let name = tab.name;
|
||||||
|
if (name !== this.$route.name) {
|
||||||
|
this.$router.push({ name: name });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['unreadMessage']),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.msg-title {
|
||||||
|
background-image: linear-gradient(135deg, #2afadf 10%, #4c83ff 100%);
|
||||||
|
}
|
||||||
|
/deep/.el-alert__title {
|
||||||
|
font-size: 18px !important;
|
||||||
|
line-height: 18px !important;
|
||||||
|
}
|
||||||
|
/deep/.el-tabs__item {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
/deep/.el-tabs__item {
|
||||||
|
padding: 0 40px;
|
||||||
|
line-height: 53px;
|
||||||
|
height: 53px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
/deep/.el-card__body {
|
||||||
|
padding: 15px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 767px) {
|
||||||
|
/deep/.el-tabs__item {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
/deep/.el-tabs__content {
|
||||||
|
padding: 12px;
|
||||||
|
padding-left: 0px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -118,13 +118,14 @@
|
||||||
}}MB</span
|
}}MB</span
|
||||||
><br />
|
><br />
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="problemData.problem.difficulty != null">
|
||||||
<span
|
<span
|
||||||
>{{ $t('m.Level') }}:{{
|
>{{ $t('m.Level') }}:{{
|
||||||
PROBLEM_LEVEL[problemData.problem.difficulty]['name']
|
PROBLEM_LEVEL[problemData.problem.difficulty]['name']
|
||||||
}}</span
|
}}</span
|
||||||
>
|
>
|
||||||
<br />
|
<br />
|
||||||
|
</template>
|
||||||
<template v-if="problemData.problem.type == 1">
|
<template v-if="problemData.problem.type == 1">
|
||||||
<span
|
<span
|
||||||
>{{ $t('m.Score') }}:{{ problemData.problem.ioScore }}
|
>{{ $t('m.Score') }}:{{ problemData.problem.ioScore }}
|
||||||
|
|
|
@ -237,21 +237,25 @@
|
||||||
<div slot="header" style="text-align: center;">
|
<div slot="header" style="text-align: center;">
|
||||||
<span class="taglist-title">{{ OJName + ' ' + $t('m.Tags') }}</span>
|
<span class="taglist-title">{{ OJName + ' ' + $t('m.Tags') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<el-button
|
<template v-if="tagList.length > 0" v-loading="loadings.tag">
|
||||||
v-for="tag in tagList"
|
<el-button
|
||||||
:key="tag.id"
|
v-for="tag in tagList"
|
||||||
@click="filterByTag(tag.id)"
|
:key="tag.id"
|
||||||
type="ghost"
|
@click="filterByTag(tag.id)"
|
||||||
:disabled="query.tagId == tag.id"
|
type="ghost"
|
||||||
size="mini"
|
:disabled="query.tagId == tag.id"
|
||||||
class="tag-btn"
|
size="mini"
|
||||||
>{{ tag.name }}
|
class="tag-btn"
|
||||||
</el-button>
|
>{{ tag.name }}
|
||||||
|
</el-button>
|
||||||
<el-button long id="pick-one" @click="pickone">
|
<el-button long id="pick-one" @click="pickone">
|
||||||
<i class="fa fa-random"></i>
|
<i class="fa fa-random"></i>
|
||||||
{{ $t('m.Pick_a_random_question') }}
|
{{ $t('m.Pick_a_random_question') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template v-else
|
||||||
|
><el-empty :description="$t('m.No_Data')"></el-empty>
|
||||||
|
</template>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
@ -460,6 +464,7 @@ export default {
|
||||||
if (oj == 'Mine') {
|
if (oj == 'Mine') {
|
||||||
oj = 'ME';
|
oj = 'ME';
|
||||||
}
|
}
|
||||||
|
this.loadings.tag = true;
|
||||||
api.getProblemTagList(oj).then(
|
api.getProblemTagList(oj).then(
|
||||||
(res) => {
|
(res) => {
|
||||||
this.tagList = res.data.data;
|
this.tagList = res.data.data;
|
||||||
|
|
|
@ -35,7 +35,7 @@ const cdn = {
|
||||||
"https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js",
|
"https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js",
|
||||||
"https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js",
|
"https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js",
|
||||||
"https://cdn.bootcdn.net/ajax/libs/vuex/3.5.1/vuex.min.js",
|
"https://cdn.bootcdn.net/ajax/libs/vuex/3.5.1/vuex.min.js",
|
||||||
"https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.0/index.min.js",
|
"https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.3/index.min.js",
|
||||||
"https://cdn.bootcdn.net/ajax/libs/highlight.js/10.3.2/highlight.min.js",
|
"https://cdn.bootcdn.net/ajax/libs/highlight.js/10.3.2/highlight.min.js",
|
||||||
"https://cdn.jsdelivr.net/npm/xe-utils",
|
"https://cdn.jsdelivr.net/npm/xe-utils",
|
||||||
"https://cdn.jsdelivr.net/npm/vxe-table@2.9.26",
|
"https://cdn.jsdelivr.net/npm/vxe-table@2.9.26",
|
||||||
|
|
|
@ -132,3 +132,82 @@ CALL Add_contest_print ;
|
||||||
|
|
||||||
DROP PROCEDURE Add_contest_print;
|
DROP PROCEDURE Add_contest_print;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2021.10.04 增加站内消息系统,包括评论我的、收到的赞、回复我的、系统通知、我的消息五个模块
|
||||||
|
*/
|
||||||
|
|
||||||
|
DROP PROCEDURE
|
||||||
|
IF EXISTS Add_msg_table;
|
||||||
|
DELIMITER $$
|
||||||
|
|
||||||
|
CREATE PROCEDURE Add_msg_table ()
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT
|
||||||
|
1
|
||||||
|
FROM
|
||||||
|
information_schema.`COLUMNS`
|
||||||
|
WHERE
|
||||||
|
table_name = 'msg_remind'
|
||||||
|
) THEN
|
||||||
|
CREATE TABLE `admin_sys_notice` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`title` varchar(255) DEFAULT NULL COMMENT '标题',
|
||||||
|
`content` longtext COMMENT '内容',
|
||||||
|
`type` varchar(255) DEFAULT NULL COMMENT '发给哪些用户类型',
|
||||||
|
`state` tinyint(1) DEFAULT '0' COMMENT '是否已拉取给用户',
|
||||||
|
`recipient_id` varchar(32) DEFAULT NULL COMMENT '接受通知的用户id',
|
||||||
|
`admin_id` varchar(32) DEFAULT NULL COMMENT '发送通知的管理员id',
|
||||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `recipient_id` (`recipient_id`),
|
||||||
|
KEY `admin_id` (`admin_id`),
|
||||||
|
CONSTRAINT `admin_sys_notice_ibfk_1` FOREIGN KEY (`recipient_id`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `admin_sys_notice_ibfk_2` FOREIGN KEY (`admin_id`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
CREATE TABLE `msg_remind` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`action` varchar(255) NOT NULL COMMENT '动作类型,如点赞讨论帖Like_Post、点赞评论Like_Discuss、评论Discuss、回复Reply等',
|
||||||
|
`source_id` int(10) unsigned DEFAULT NULL COMMENT '消息来源id,讨论id或比赛id',
|
||||||
|
`source_type` varchar(255) DEFAULT NULL COMMENT '事件源类型:''Discussion''、''Contest''等',
|
||||||
|
`source_content` varchar(255) DEFAULT NULL COMMENT '事件源的内容,比如回复的内容,评论的帖子标题等等',
|
||||||
|
`quote_id` int(10) unsigned DEFAULT NULL COMMENT '事件引用上一级评论或回复id',
|
||||||
|
`quote_type` varchar(255) DEFAULT NULL COMMENT '事件引用上一级的类型:Comment、Reply',
|
||||||
|
`url` varchar(255) DEFAULT NULL COMMENT '事件所发生的地点链接 url',
|
||||||
|
`state` tinyint(1) DEFAULT '0' COMMENT '是否已读',
|
||||||
|
`sender_id` varchar(32) DEFAULT NULL COMMENT '操作者的id',
|
||||||
|
`recipient_id` varchar(32) DEFAULT NULL COMMENT '接受消息的用户id',
|
||||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `sender_id` (`sender_id`),
|
||||||
|
KEY `recipient_id` (`recipient_id`),
|
||||||
|
CONSTRAINT `msg_remind_ibfk_1` FOREIGN KEY (`sender_id`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `msg_remind_ibfk_2` FOREIGN KEY (`recipient_id`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
CREATE TABLE `user_sys_notice` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`sys_notice_id` bigint(20) unsigned DEFAULT NULL COMMENT '系统通知的id',
|
||||||
|
`recipient_id` varchar(32) DEFAULT NULL COMMENT '接受通知的用户id',
|
||||||
|
`type` varchar(255) DEFAULT NULL COMMENT '消息类型,系统通知sys、我的信息mine',
|
||||||
|
`state` tinyint(1) DEFAULT '0' COMMENT '是否已读',
|
||||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '读取时间',
|
||||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `sys_notice_id` (`sys_notice_id`),
|
||||||
|
KEY `recipient_id` (`recipient_id`),
|
||||||
|
CONSTRAINT `user_sys_notice_ibfk_1` FOREIGN KEY (`sys_notice_id`) REFERENCES `admin_sys_notice` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `user_sys_notice_ibfk_2` FOREIGN KEY (`recipient_id`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
END
|
||||||
|
IF ; END$$
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
CALL Add_msg_table;
|
||||||
|
|
||||||
|
DROP PROCEDURE Add_msg_table;
|
|
@ -776,6 +776,69 @@ CREATE TABLE `remote_judge_account` (
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `admin_sys_notice`;
|
||||||
|
|
||||||
|
CREATE TABLE `admin_sys_notice` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`title` varchar(255) DEFAULT NULL COMMENT '标题',
|
||||||
|
`content` longtext COMMENT '内容',
|
||||||
|
`type` varchar(255) DEFAULT NULL COMMENT '发给哪些用户类型',
|
||||||
|
`state` tinyint(1) DEFAULT '0' COMMENT '是否已拉取给用户',
|
||||||
|
`recipient_id` varchar(32) DEFAULT NULL COMMENT '接受通知的用户id',
|
||||||
|
`admin_id` varchar(32) DEFAULT NULL COMMENT '发送通知的管理员id',
|
||||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `recipient_id` (`recipient_id`),
|
||||||
|
KEY `admin_id` (`admin_id`),
|
||||||
|
CONSTRAINT `admin_sys_notice_ibfk_1` FOREIGN KEY (`recipient_id`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `admin_sys_notice_ibfk_2` FOREIGN KEY (`admin_id`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
/*Table structure for table `msg_remind` */
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `msg_remind`;
|
||||||
|
|
||||||
|
CREATE TABLE `msg_remind` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`action` varchar(255) NOT NULL COMMENT '动作类型,如点赞讨论帖Like_Post、点赞评论Like_Discuss、评论Discuss、回复Reply等',
|
||||||
|
`source_id` int(10) unsigned DEFAULT NULL COMMENT '消息来源id,讨论id或比赛id',
|
||||||
|
`source_type` varchar(255) DEFAULT NULL COMMENT '事件源类型:''Discussion''、''Contest''等',
|
||||||
|
`source_content` varchar(255) DEFAULT NULL COMMENT '事件源的内容,比如回复的内容,评论的帖子标题等等',
|
||||||
|
`quote_id` int(10) unsigned DEFAULT NULL COMMENT '事件引用上一级评论或回复id',
|
||||||
|
`quote_type` varchar(255) DEFAULT NULL COMMENT '事件引用上一级的类型:Comment、Reply',
|
||||||
|
`url` varchar(255) DEFAULT NULL COMMENT '事件所发生的地点链接 url',
|
||||||
|
`state` tinyint(1) DEFAULT '0' COMMENT '是否已读',
|
||||||
|
`sender_id` varchar(32) DEFAULT NULL COMMENT '操作者的id',
|
||||||
|
`recipient_id` varchar(32) DEFAULT NULL COMMENT '接受消息的用户id',
|
||||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `sender_id` (`sender_id`),
|
||||||
|
KEY `recipient_id` (`recipient_id`),
|
||||||
|
CONSTRAINT `msg_remind_ibfk_1` FOREIGN KEY (`sender_id`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `msg_remind_ibfk_2` FOREIGN KEY (`recipient_id`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
/*Table structure for table `user_sys_notice` */
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `user_sys_notice`;
|
||||||
|
|
||||||
|
CREATE TABLE `user_sys_notice` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`sys_notice_id` bigint(20) unsigned DEFAULT NULL COMMENT '系统通知的id',
|
||||||
|
`recipient_id` varchar(32) DEFAULT NULL COMMENT '接受通知的用户id',
|
||||||
|
`type` varchar(255) DEFAULT NULL COMMENT '消息类型,系统通知sys、我的信息mine',
|
||||||
|
`state` tinyint(1) DEFAULT '0' COMMENT '是否已读',
|
||||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '读取时间',
|
||||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `sys_notice_id` (`sys_notice_id`),
|
||||||
|
KEY `recipient_id` (`recipient_id`),
|
||||||
|
CONSTRAINT `user_sys_notice_ibfk_1` FOREIGN KEY (`sys_notice_id`) REFERENCES `admin_sys_notice` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `user_sys_notice_ibfk_2` FOREIGN KEY (`recipient_id`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|
||||||
/* Trigger structure for table `contest` */
|
/* Trigger structure for table `contest` */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue