feat: add job seeker notice settings API
This commit is contained in:
64
docs/mobile-notice-settings-api.md
Normal file
64
docs/mobile-notice-settings-api.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# 移动端消息接收设置接口
|
||||||
|
|
||||||
|
## 通用约定
|
||||||
|
|
||||||
|
- 基础地址:沿用移动端当前后端地址。
|
||||||
|
- 请求头:`Authorization: Bearer <移动端求职者登录 Token>`。
|
||||||
|
- 仅移动端**求职者**可调用;未携带或失效 Token 返回 HTTP 401,企业及其他角色返回业务码 `403`。
|
||||||
|
- 四个字段均为布尔值,`true` 表示接收该类消息,`false` 表示不接收。保存时必须一次传齐四项。
|
||||||
|
|
||||||
|
## 查看设置
|
||||||
|
|
||||||
|
`GET /app/notice/settings`
|
||||||
|
|
||||||
|
无需请求参数或请求体。首次查看(尚未保存过)会返回四项均开启的默认设置,且不会创建数据库记录。
|
||||||
|
|
||||||
|
成功响应示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"data": {
|
||||||
|
"jobUpdateEnabled": true,
|
||||||
|
"appointmentReminderEnabled": true,
|
||||||
|
"systemNotificationEnabled": true,
|
||||||
|
"interviewNotificationEnabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
| 字段 | 对应按钮 |
|
||||||
|
| --- | --- |
|
||||||
|
| `jobUpdateEnabled` | 职位上新 |
|
||||||
|
| `appointmentReminderEnabled` | 预约提醒 |
|
||||||
|
| `systemNotificationEnabled` | 系统通知 |
|
||||||
|
| `interviewNotificationEnabled` | 面试通知 |
|
||||||
|
|
||||||
|
## 保存设置
|
||||||
|
|
||||||
|
`PUT /app/notice/settings`
|
||||||
|
|
||||||
|
请求头 `Content-Type: application/json`,请求体示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jobUpdateEnabled": true,
|
||||||
|
"appointmentReminderEnabled": false,
|
||||||
|
"systemNotificationEnabled": true,
|
||||||
|
"interviewNotificationEnabled": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
成功响应:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
接口按登录 Token 自动识别当前用户,不接收也不允许客户端传入用户 ID。任一开关字段缺失或为 `null` 时,返回参数校验错误。
|
||||||
@@ -2,16 +2,27 @@ package com.ruoyi.cms.controller.app;
|
|||||||
|
|
||||||
import com.ruoyi.cms.domain.Job;
|
import com.ruoyi.cms.domain.Job;
|
||||||
import com.ruoyi.cms.domain.Notice;
|
import com.ruoyi.cms.domain.Notice;
|
||||||
|
import com.ruoyi.cms.domain.dto.NoticeSettingRequest;
|
||||||
|
import com.ruoyi.cms.domain.vo.NoticeSettingVO;
|
||||||
import com.ruoyi.cms.service.IAppNoticeService;
|
import com.ruoyi.cms.service.IAppNoticeService;
|
||||||
|
import com.ruoyi.cms.service.IAppUserNoticeSettingService;
|
||||||
import com.ruoyi.common.annotation.BussinessLog;
|
import com.ruoyi.common.annotation.BussinessLog;
|
||||||
|
import com.ruoyi.common.constant.HttpStatus;
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.core.domain.entity.AppUser;
|
||||||
|
import com.ruoyi.common.core.domain.model.LoginSiteUser;
|
||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
|
import com.ruoyi.cms.util.StringUtil;
|
||||||
|
import com.ruoyi.common.utils.SiteSecurityUtils;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -21,6 +32,35 @@ public class AppNoticeInfoController extends BaseController {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IAppNoticeService appNoticeService;
|
private IAppNoticeService appNoticeService;
|
||||||
|
@Autowired
|
||||||
|
private IAppUserNoticeSettingService appUserNoticeSettingService;
|
||||||
|
|
||||||
|
@ApiOperation("查看消息接收设置")
|
||||||
|
@GetMapping("/settings")
|
||||||
|
public AjaxResult getSettings()
|
||||||
|
{
|
||||||
|
Long userId = currentJobSeekerId();
|
||||||
|
if (userId == null)
|
||||||
|
{
|
||||||
|
return AjaxResult.error(HttpStatus.FORBIDDEN, "仅求职者可查看消息接收设置");
|
||||||
|
}
|
||||||
|
NoticeSettingVO settings = appUserNoticeSettingService.getSettings(userId);
|
||||||
|
return AjaxResult.success(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation("保存消息接收设置")
|
||||||
|
@PutMapping("/settings")
|
||||||
|
@BussinessLog(title = "保存消息接收设置")
|
||||||
|
public AjaxResult saveSettings(@Validated @RequestBody NoticeSettingRequest request)
|
||||||
|
{
|
||||||
|
Long userId = currentJobSeekerId();
|
||||||
|
if (userId == null)
|
||||||
|
{
|
||||||
|
return AjaxResult.error(HttpStatus.FORBIDDEN, "仅求职者可保存消息接收设置");
|
||||||
|
}
|
||||||
|
appUserNoticeSettingService.saveSettings(userId, request);
|
||||||
|
return AjaxResult.success();
|
||||||
|
}
|
||||||
@ApiOperation("消息列表")
|
@ApiOperation("消息列表")
|
||||||
@GetMapping("/info")
|
@GetMapping("/info")
|
||||||
public AjaxResult listNotRead(@RequestParam(required = false) Integer isRead)
|
public AjaxResult listNotRead(@RequestParam(required = false) Integer isRead)
|
||||||
@@ -60,4 +100,24 @@ public class AppNoticeInfoController extends BaseController {
|
|||||||
List<Notice> list = appNoticeService.sysNoticeList();
|
List<Notice> list = appNoticeService.sysNoticeList();
|
||||||
return getDataTable(list);
|
return getDataTable(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅允许经移动端 Token 认证的求职者读写自己的设置。
|
||||||
|
*/
|
||||||
|
private Long currentJobSeekerId()
|
||||||
|
{
|
||||||
|
Authentication authentication = SiteSecurityUtils.getAuthentication();
|
||||||
|
if (authentication == null || !(authentication.getPrincipal() instanceof LoginSiteUser))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LoginSiteUser loginUser = (LoginSiteUser) authentication.getPrincipal();
|
||||||
|
AppUser appUser = loginUser.getUser();
|
||||||
|
if (loginUser.getUserId() == null || appUser == null
|
||||||
|
|| !StringUtil.IS_JOB_REQUEST_USER.equals(appUser.getIsCompanyUser()))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return loginUser.getUserId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.ruoyi.cms.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.ruoyi.common.core.domain.BaseEntity;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端求职者消息接收设置。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("移动端求职者消息接收设置")
|
||||||
|
@TableName("app_user_notice_setting")
|
||||||
|
public class AppUserNoticeSetting extends BaseEntity
|
||||||
|
{
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** APP 用户 ID,每名用户仅有一条设置。 */
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/** 职位上新。 */
|
||||||
|
private Boolean jobUpdateEnabled;
|
||||||
|
|
||||||
|
/** 招聘会预约提醒。 */
|
||||||
|
private Boolean appointmentReminderEnabled;
|
||||||
|
|
||||||
|
/** 平台系统通知。 */
|
||||||
|
private Boolean systemNotificationEnabled;
|
||||||
|
|
||||||
|
/** 企业面试通知。 */
|
||||||
|
private Boolean interviewNotificationEnabled;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.ruoyi.cms.domain.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端消息接收设置保存请求。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("移动端消息接收设置保存请求")
|
||||||
|
public class NoticeSettingRequest
|
||||||
|
{
|
||||||
|
@NotNull(message = "职位上新开关不能为空")
|
||||||
|
@ApiModelProperty(value = "是否接收职位上新通知", required = true, example = "true")
|
||||||
|
private Boolean jobUpdateEnabled;
|
||||||
|
|
||||||
|
@NotNull(message = "预约提醒开关不能为空")
|
||||||
|
@ApiModelProperty(value = "是否接收招聘会预约提醒", required = true, example = "true")
|
||||||
|
private Boolean appointmentReminderEnabled;
|
||||||
|
|
||||||
|
@NotNull(message = "系统通知开关不能为空")
|
||||||
|
@ApiModelProperty(value = "是否接收系统通知", required = true, example = "true")
|
||||||
|
private Boolean systemNotificationEnabled;
|
||||||
|
|
||||||
|
@NotNull(message = "面试通知开关不能为空")
|
||||||
|
@ApiModelProperty(value = "是否接收面试通知", required = true, example = "true")
|
||||||
|
private Boolean interviewNotificationEnabled;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.ruoyi.cms.domain.vo;
|
||||||
|
|
||||||
|
import com.ruoyi.cms.domain.AppUserNoticeSetting;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端消息接收设置返回对象。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("移动端消息接收设置")
|
||||||
|
public class NoticeSettingVO
|
||||||
|
{
|
||||||
|
@ApiModelProperty(value = "是否接收职位上新通知", example = "true")
|
||||||
|
private Boolean jobUpdateEnabled;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否接收招聘会预约提醒", example = "true")
|
||||||
|
private Boolean appointmentReminderEnabled;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否接收系统通知", example = "true")
|
||||||
|
private Boolean systemNotificationEnabled;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否接收面试通知", example = "true")
|
||||||
|
private Boolean interviewNotificationEnabled;
|
||||||
|
|
||||||
|
public static NoticeSettingVO from(AppUserNoticeSetting setting)
|
||||||
|
{
|
||||||
|
NoticeSettingVO result = new NoticeSettingVO();
|
||||||
|
result.setJobUpdateEnabled(setting.getJobUpdateEnabled());
|
||||||
|
result.setAppointmentReminderEnabled(setting.getAppointmentReminderEnabled());
|
||||||
|
result.setSystemNotificationEnabled(setting.getSystemNotificationEnabled());
|
||||||
|
result.setInterviewNotificationEnabled(setting.getInterviewNotificationEnabled());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.ruoyi.cms.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.ruoyi.cms.domain.AppUserNoticeSetting;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端用户消息接收设置 Mapper。
|
||||||
|
*/
|
||||||
|
public interface AppUserNoticeSettingMapper extends BaseMapper<AppUserNoticeSetting>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 保存设置。user_id 唯一约束保证并发首次保存时只会保留一条记录。
|
||||||
|
*/
|
||||||
|
int upsert(AppUserNoticeSetting setting);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.ruoyi.cms.service;
|
||||||
|
|
||||||
|
import com.ruoyi.cms.domain.dto.NoticeSettingRequest;
|
||||||
|
import com.ruoyi.cms.domain.vo.NoticeSettingVO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端求职者消息接收设置服务。
|
||||||
|
*/
|
||||||
|
public interface IAppUserNoticeSettingService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 查询用户设置;尚未保存过时返回四项均开启的默认设置。
|
||||||
|
*/
|
||||||
|
NoticeSettingVO getSettings(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存用户的完整四项设置。
|
||||||
|
*/
|
||||||
|
void saveSettings(Long userId, NoticeSettingRequest request);
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.ruoyi.cms.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.ruoyi.cms.domain.AppUserNoticeSetting;
|
||||||
|
import com.ruoyi.cms.domain.dto.NoticeSettingRequest;
|
||||||
|
import com.ruoyi.cms.domain.vo.NoticeSettingVO;
|
||||||
|
import com.ruoyi.cms.mapper.AppUserNoticeSettingMapper;
|
||||||
|
import com.ruoyi.cms.service.IAppUserNoticeSettingService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端求职者消息接收设置服务实现。
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class AppUserNoticeSettingServiceImpl
|
||||||
|
extends ServiceImpl<AppUserNoticeSettingMapper, AppUserNoticeSetting>
|
||||||
|
implements IAppUserNoticeSettingService
|
||||||
|
{
|
||||||
|
private final AppUserNoticeSettingMapper noticeSettingMapper;
|
||||||
|
|
||||||
|
public AppUserNoticeSettingServiceImpl(AppUserNoticeSettingMapper noticeSettingMapper)
|
||||||
|
{
|
||||||
|
this.noticeSettingMapper = noticeSettingMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeSettingVO getSettings(Long userId)
|
||||||
|
{
|
||||||
|
AppUserNoticeSetting setting = noticeSettingMapper.selectOne(
|
||||||
|
Wrappers.<AppUserNoticeSetting>lambdaQuery()
|
||||||
|
.eq(AppUserNoticeSetting::getUserId, userId));
|
||||||
|
if (setting == null)
|
||||||
|
{
|
||||||
|
setting = defaultSetting(userId);
|
||||||
|
}
|
||||||
|
return NoticeSettingVO.from(setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveSettings(Long userId, NoticeSettingRequest request)
|
||||||
|
{
|
||||||
|
AppUserNoticeSetting setting = new AppUserNoticeSetting();
|
||||||
|
setting.setUserId(userId);
|
||||||
|
setting.setJobUpdateEnabled(request.getJobUpdateEnabled());
|
||||||
|
setting.setAppointmentReminderEnabled(request.getAppointmentReminderEnabled());
|
||||||
|
setting.setSystemNotificationEnabled(request.getSystemNotificationEnabled());
|
||||||
|
setting.setInterviewNotificationEnabled(request.getInterviewNotificationEnabled());
|
||||||
|
noticeSettingMapper.upsert(setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AppUserNoticeSetting defaultSetting(Long userId)
|
||||||
|
{
|
||||||
|
AppUserNoticeSetting setting = new AppUserNoticeSetting();
|
||||||
|
setting.setUserId(userId);
|
||||||
|
setting.setJobUpdateEnabled(Boolean.TRUE);
|
||||||
|
setting.setAppointmentReminderEnabled(Boolean.TRUE);
|
||||||
|
setting.setSystemNotificationEnabled(Boolean.TRUE);
|
||||||
|
setting.setInterviewNotificationEnabled(Boolean.TRUE);
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?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="com.ruoyi.cms.mapper.AppUserNoticeSettingMapper">
|
||||||
|
|
||||||
|
<insert id="upsert" parameterType="AppUserNoticeSetting">
|
||||||
|
INSERT INTO app_user_notice_setting
|
||||||
|
(user_id, job_update_enabled, appointment_reminder_enabled,
|
||||||
|
system_notification_enabled, interview_notification_enabled,
|
||||||
|
create_time, update_time, del_flag)
|
||||||
|
VALUES
|
||||||
|
(#{userId}, #{jobUpdateEnabled}, #{appointmentReminderEnabled},
|
||||||
|
#{systemNotificationEnabled}, #{interviewNotificationEnabled},
|
||||||
|
NOW(), NOW(), '0')
|
||||||
|
ON CONFLICT (user_id) DO UPDATE
|
||||||
|
SET job_update_enabled = EXCLUDED.job_update_enabled,
|
||||||
|
appointment_reminder_enabled = EXCLUDED.appointment_reminder_enabled,
|
||||||
|
system_notification_enabled = EXCLUDED.system_notification_enabled,
|
||||||
|
interview_notification_enabled = EXCLUDED.interview_notification_enabled,
|
||||||
|
update_time = NOW(),
|
||||||
|
del_flag = '0'
|
||||||
|
</insert>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package com.ruoyi.cms.controller.app;
|
||||||
|
|
||||||
|
import com.ruoyi.cms.domain.dto.NoticeSettingRequest;
|
||||||
|
import com.ruoyi.cms.domain.vo.NoticeSettingVO;
|
||||||
|
import com.ruoyi.cms.service.IAppUserNoticeSettingService;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.core.domain.entity.AppUser;
|
||||||
|
import com.ruoyi.common.core.domain.model.LoginSiteUser;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class AppNoticeInfoControllerTest
|
||||||
|
{
|
||||||
|
@AfterEach
|
||||||
|
void clearSecurityContext()
|
||||||
|
{
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jobSeekerCanReadOwnSettings() throws Exception
|
||||||
|
{
|
||||||
|
IAppUserNoticeSettingService service = mock(IAppUserNoticeSettingService.class);
|
||||||
|
NoticeSettingVO expected = new NoticeSettingVO();
|
||||||
|
expected.setJobUpdateEnabled(true);
|
||||||
|
expected.setAppointmentReminderEnabled(true);
|
||||||
|
expected.setSystemNotificationEnabled(false);
|
||||||
|
expected.setInterviewNotificationEnabled(true);
|
||||||
|
when(service.getSettings(12L)).thenReturn(expected);
|
||||||
|
authenticateSiteUser(12L, "1");
|
||||||
|
|
||||||
|
AppNoticeInfoController controller = controllerWith(service);
|
||||||
|
AjaxResult result = controller.getSettings();
|
||||||
|
|
||||||
|
assertEquals(200, result.get(AjaxResult.CODE_TAG));
|
||||||
|
assertEquals(expected, result.get(AjaxResult.DATA_TAG));
|
||||||
|
verify(service).getSettings(12L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nonJobSeekerCannotSaveSettings() throws Exception
|
||||||
|
{
|
||||||
|
IAppUserNoticeSettingService service = mock(IAppUserNoticeSettingService.class);
|
||||||
|
authenticateSiteUser(12L, "0");
|
||||||
|
NoticeSettingRequest request = new NoticeSettingRequest();
|
||||||
|
request.setJobUpdateEnabled(true);
|
||||||
|
request.setAppointmentReminderEnabled(true);
|
||||||
|
request.setSystemNotificationEnabled(true);
|
||||||
|
request.setInterviewNotificationEnabled(true);
|
||||||
|
|
||||||
|
AppNoticeInfoController controller = controllerWith(service);
|
||||||
|
AjaxResult result = controller.saveSettings(request);
|
||||||
|
|
||||||
|
assertEquals(403, result.get(AjaxResult.CODE_TAG));
|
||||||
|
verify(service, never()).saveSettings(eq(12L), any(NoticeSettingRequest.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AppNoticeInfoController controllerWith(IAppUserNoticeSettingService service) throws Exception
|
||||||
|
{
|
||||||
|
AppNoticeInfoController controller = new AppNoticeInfoController();
|
||||||
|
Field field = AppNoticeInfoController.class.getDeclaredField("appUserNoticeSettingService");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(controller, service);
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void authenticateSiteUser(Long userId, String userType)
|
||||||
|
{
|
||||||
|
AppUser appUser = new AppUser();
|
||||||
|
appUser.setIsCompanyUser(userType);
|
||||||
|
LoginSiteUser loginUser = new LoginSiteUser(userId, appUser);
|
||||||
|
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
||||||
|
loginUser, null, Collections.emptyList());
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package com.ruoyi.cms.service.impl;
|
||||||
|
|
||||||
|
import com.ruoyi.cms.domain.AppUserNoticeSetting;
|
||||||
|
import com.ruoyi.cms.domain.dto.NoticeSettingRequest;
|
||||||
|
import com.ruoyi.cms.domain.vo.NoticeSettingVO;
|
||||||
|
import com.ruoyi.cms.mapper.AppUserNoticeSettingMapper;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class AppUserNoticeSettingServiceImplTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
void returnsAllEnabledDefaultsWhenUserHasNotSavedSettings()
|
||||||
|
{
|
||||||
|
AppUserNoticeSettingMapper mapper = mock(AppUserNoticeSettingMapper.class);
|
||||||
|
when(mapper.selectOne(any())).thenReturn(null);
|
||||||
|
AppUserNoticeSettingServiceImpl service = new AppUserNoticeSettingServiceImpl(mapper);
|
||||||
|
|
||||||
|
NoticeSettingVO result = service.getSettings(12L);
|
||||||
|
|
||||||
|
assertTrue(result.getJobUpdateEnabled());
|
||||||
|
assertTrue(result.getAppointmentReminderEnabled());
|
||||||
|
assertTrue(result.getSystemNotificationEnabled());
|
||||||
|
assertTrue(result.getInterviewNotificationEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void returnsPersistedSettingsForCurrentUser()
|
||||||
|
{
|
||||||
|
AppUserNoticeSettingMapper mapper = mock(AppUserNoticeSettingMapper.class);
|
||||||
|
AppUserNoticeSetting setting = new AppUserNoticeSetting();
|
||||||
|
setting.setJobUpdateEnabled(false);
|
||||||
|
setting.setAppointmentReminderEnabled(true);
|
||||||
|
setting.setSystemNotificationEnabled(false);
|
||||||
|
setting.setInterviewNotificationEnabled(true);
|
||||||
|
when(mapper.selectOne(any())).thenReturn(setting);
|
||||||
|
AppUserNoticeSettingServiceImpl service = new AppUserNoticeSettingServiceImpl(mapper);
|
||||||
|
|
||||||
|
NoticeSettingVO result = service.getSettings(12L);
|
||||||
|
|
||||||
|
assertFalse(result.getJobUpdateEnabled());
|
||||||
|
assertTrue(result.getAppointmentReminderEnabled());
|
||||||
|
assertFalse(result.getSystemNotificationEnabled());
|
||||||
|
assertTrue(result.getInterviewNotificationEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void savesAllFourSettingsForTheAuthenticatedUser()
|
||||||
|
{
|
||||||
|
AppUserNoticeSettingMapper mapper = mock(AppUserNoticeSettingMapper.class);
|
||||||
|
AppUserNoticeSettingServiceImpl service = new AppUserNoticeSettingServiceImpl(mapper);
|
||||||
|
NoticeSettingRequest request = new NoticeSettingRequest();
|
||||||
|
request.setJobUpdateEnabled(true);
|
||||||
|
request.setAppointmentReminderEnabled(false);
|
||||||
|
request.setSystemNotificationEnabled(true);
|
||||||
|
request.setInterviewNotificationEnabled(false);
|
||||||
|
|
||||||
|
service.saveSettings(12L, request);
|
||||||
|
|
||||||
|
ArgumentCaptor<AppUserNoticeSetting> captor = ArgumentCaptor.forClass(AppUserNoticeSetting.class);
|
||||||
|
verify(mapper).upsert(captor.capture());
|
||||||
|
AppUserNoticeSetting saved = captor.getValue();
|
||||||
|
assertEquals(12L, saved.getUserId());
|
||||||
|
assertTrue(saved.getJobUpdateEnabled());
|
||||||
|
assertFalse(saved.getAppointmentReminderEnabled());
|
||||||
|
assertTrue(saved.getSystemNotificationEnabled());
|
||||||
|
assertFalse(saved.getInterviewNotificationEnabled());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,6 +118,9 @@ public class SecurityConfig
|
|||||||
"/cms/policyInfo/detail","/cms/policyInfo/list","/cms/industry/treeselect").permitAll()
|
"/cms/policyInfo/detail","/cms/policyInfo/list","/cms/industry/treeselect").permitAll()
|
||||||
// 静态资源,可匿名访问
|
// 静态资源,可匿名访问
|
||||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
||||||
|
// 移动端消息接收设置包含用户个人偏好,必须使用站点 Token 访问。
|
||||||
|
// 该规则需位于 /app/** 的公共查询规则之前。
|
||||||
|
.antMatchers("/app/notice/settings").authenticated()
|
||||||
// 移动端公用查询,可匿名访问
|
// 移动端公用查询,可匿名访问
|
||||||
.antMatchers("/app/common/**").permitAll()
|
.antMatchers("/app/common/**").permitAll()
|
||||||
.antMatchers("/app/**").permitAll()
|
.antMatchers("/app/**").permitAll()
|
||||||
|
|||||||
30
sql/migration_app_user_notice_setting.sql
Normal file
30
sql/migration_app_user_notice_setting.sql
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
-- 移动端求职者消息接收设置。
|
||||||
|
-- 瀚高 / PostgreSQL 兼容;重复执行安全。表放在 shz schema,应用通过 search_path 访问。
|
||||||
|
|
||||||
|
CREATE SEQUENCE IF NOT EXISTS shz.app_user_notice_setting_id_seq
|
||||||
|
START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS shz.app_user_notice_setting (
|
||||||
|
id BIGINT NOT NULL DEFAULT nextval('shz.app_user_notice_setting_id_seq'::regclass) PRIMARY KEY,
|
||||||
|
user_id BIGINT NOT NULL,
|
||||||
|
job_update_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
appointment_reminder_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
system_notification_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
interview_notification_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
create_by VARCHAR(64) DEFAULT '',
|
||||||
|
create_time TIMESTAMP(0),
|
||||||
|
update_by VARCHAR(64) DEFAULT '',
|
||||||
|
update_time TIMESTAMP(0),
|
||||||
|
remark VARCHAR(500),
|
||||||
|
del_flag CHAR(1) DEFAULT '0'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS uk_app_user_notice_setting_user_id
|
||||||
|
ON shz.app_user_notice_setting (user_id);
|
||||||
|
|
||||||
|
COMMENT ON TABLE shz.app_user_notice_setting IS '移动端求职者消息接收设置';
|
||||||
|
COMMENT ON COLUMN shz.app_user_notice_setting.user_id IS 'APP 用户 ID,每用户唯一';
|
||||||
|
COMMENT ON COLUMN shz.app_user_notice_setting.job_update_enabled IS '是否接收职位上新通知';
|
||||||
|
COMMENT ON COLUMN shz.app_user_notice_setting.appointment_reminder_enabled IS '是否接收招聘会预约提醒';
|
||||||
|
COMMENT ON COLUMN shz.app_user_notice_setting.system_notification_enabled IS '是否接收系统通知';
|
||||||
|
COMMENT ON COLUMN shz.app_user_notice_setting.interview_notification_enabled IS '是否接收面试通知';
|
||||||
Reference in New Issue
Block a user