初始化

This commit is contained in:
2026-02-03 10:18:40 +08:00
commit b4951cb721
1299 changed files with 100558 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.njcn</groupId>
<artifactId>msgpush-module-system</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-module-system-api</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
system 模块 API暴露给其它模块调用
</description>
<dependencies>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-common</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>org.springdoc</groupId> <!-- 接口文档:使用最新版本的 Swagger 模型 -->
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<scope>provided</scope>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,56 @@
package com.njcn.msgpush.module.system.api.dept;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.collection.CollectionUtils;
import com.njcn.msgpush.module.system.api.dept.dto.DeptRespDTO;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 部门")
public interface DeptApi {
String PREFIX = ApiConstants.PREFIX + "/dept";
@GetMapping(PREFIX + "/get")
@Operation(summary = "获得部门信息")
@Parameter(name = "id", description = "部门编号", example = "1024", required = true)
CommonResult<DeptRespDTO> getDept(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/list")
@Operation(summary = "获得部门信息数组")
@Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true)
CommonResult<List<DeptRespDTO>> getDeptList(@RequestParam("ids") Collection<Long> ids);
@GetMapping(PREFIX + "/valid")
@Operation(summary = "校验部门是否合法")
@Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true)
CommonResult<Boolean> validateDeptList(@RequestParam("ids") Collection<Long> ids);
/**
* 获得指定编号的部门 Map
*
* @param ids 部门编号数组
* @return 部门 Map
*/
default Map<Long, DeptRespDTO> getDeptMap(Collection<Long> ids) {
List<DeptRespDTO> list = getDeptList(ids).getCheckedData();
return CollectionUtils.convertMap(list, DeptRespDTO::getId);
}
@GetMapping(PREFIX + "/list-child")
@Operation(summary = "获得指定部门的所有子部门")
@Parameter(name = "id", description = "部门编号", example = "1024", required = true)
CommonResult<List<DeptRespDTO>> getChildDeptList(@RequestParam("id") Long id);
}

View File

@@ -0,0 +1,45 @@
package com.njcn.msgpush.module.system.api.dept;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.collection.CollectionUtils;
import com.njcn.msgpush.module.system.api.dept.dto.PostRespDTO;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 岗位")
public interface PostApi {
String PREFIX = ApiConstants.PREFIX + "/post";
@GetMapping(PREFIX + "/valid")
@Operation(summary = "校验岗位是否合法")
@Parameter(name = "ids", description = "岗位编号数组", example = "1,2", required = true)
CommonResult<Boolean> validPostList(@RequestParam("ids") Collection<Long> ids);
@GetMapping(PREFIX + "/list")
@Operation(summary = "获得岗位列表")
@Parameter(name = "ids", description = "岗位编号数组", example = "1,2", required = true)
CommonResult<List<PostRespDTO>> getPostList(@RequestParam("ids") Collection<Long> ids);
default Map<Long, PostRespDTO> getPostMap(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return MapUtil.empty();
}
List<PostRespDTO> list = getPostList(ids).getData();
return CollectionUtils.convertMap(list, PostRespDTO::getId);
}
}

View File

@@ -0,0 +1,25 @@
package com.njcn.msgpush.module.system.api.dept.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "RPC 服务 - 部门 Response DTO")
@Data
public class DeptRespDTO {
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部")
private String name;
@Schema(description = "父部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long parentId;
@Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long leaderUserId;
@Schema(description = "部门状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status; // 参见 CommonStatusEnum 枚举
}

View File

@@ -0,0 +1,30 @@
package com.njcn.msgpush.module.system.api.dept.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 岗位 Response DTO
*
* @author hongawen
*/
@Schema(description = "RPC 服务 - 岗位 Response DTO")
@Data
public class PostRespDTO {
@Schema(description = "岗位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小土豆")
private String name;
@Schema(description = "岗位编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "msgpush")
private String code;
@Schema(description = "岗位排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer sort;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status; // 参见 CommonStatusEnum 枚举
}

View File

@@ -0,0 +1,31 @@
package com.njcn.msgpush.module.system.api.dict;
import com.njcn.msgpush.framework.common.biz.system.dict.DictDataCommonApi;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.Operation;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 字典数据")
public interface DictDataApi extends DictDataCommonApi {
String PREFIX = ApiConstants.PREFIX + "/dict-data";
@GetMapping(PREFIX + "/valid")
@Operation(summary = "校验字典数据们是否有效")
@Parameters({
@Parameter(name = "dictType", description = "字典类型", example = "SEX", required = true),
@Parameter(name = "descriptions", description = "字典数据值的数组", example = "1,2", required = true)
})
CommonResult<Boolean> validateDictDataList(@RequestParam("dictType") String dictType,
@RequestParam("values") Collection<String> values);
}

View File

@@ -0,0 +1,24 @@
package com.njcn.msgpush.module.system.api.logger;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.api.logger.dto.LoginLogCreateReqDTO;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import jakarta.validation.Valid;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 登录日志")
public interface LoginLogApi {
String PREFIX = ApiConstants.PREFIX + "/login-log";
@PostMapping(PREFIX + "/create")
@Operation(summary = "创建登录日志")
CommonResult<Boolean> createLoginLog(@Valid @RequestBody LoginLogCreateReqDTO reqDTO);
}

View File

@@ -0,0 +1,25 @@
package com.njcn.msgpush.module.system.api.logger;
import com.njcn.msgpush.framework.common.biz.system.logger.OperateLogCommonApi;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.module.system.api.logger.dto.OperateLogPageReqDTO;
import com.njcn.msgpush.module.system.api.logger.dto.OperateLogRespDTO;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 操作日志")
public interface OperateLogApi extends OperateLogCommonApi {
String PREFIX = ApiConstants.PREFIX + "/operate-log";
@GetMapping(PREFIX + "/page")
@Operation(summary = "获取指定模块的指定数据的操作日志分页")
CommonResult<PageResult<OperateLogRespDTO>> getOperateLogPage(@SpringQueryMap OperateLogPageReqDTO pageReqDTO);
}

View File

@@ -0,0 +1,40 @@
package com.njcn.msgpush.module.system.api.logger.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "RPC 服务 - 登录日志创建 Request DTO")
@Data
public class LoginLogCreateReqDTO {
@Schema(description = "日志类型,参见 LoginLogTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1" )
@NotNull(message = "日志类型不能为空")
private Integer logType;
@Schema(description = "链路追踪编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "89aca178-a370-411c-ae02-3f0d672be4ab")
private String traceId;
@Schema(description = "用户编号", example = "666")
private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2" )
@NotNull(message = "用户类型不能为空")
private Integer userType;
@Schema(description = "用户账号", example = "msgpush")
@Size(max = 30, message = "用户账号长度不能超过30个字符")
private String username; // 不再强制校验 username 非空,因为 Member 社交登录时,此时暂时没有 username(mobile
@Schema(description = "登录结果,参见 LoginResultEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "登录结果不能为空")
private Integer result;
@Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
@NotEmpty(message = "用户 IP 不能为空")
private String userIp;
@Schema(description = "浏览器 UserAgent", requiredMode = Schema.RequiredMode.REQUIRED, example = "Mozilla/5.0")
private String userAgent;
}

View File

@@ -0,0 +1,20 @@
package com.njcn.msgpush.module.system.api.logger.dto;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(name = "RPC 服务 - 操作日志分页 Request DTO")
@Data
public class OperateLogPageReqDTO extends PageParam {
@Schema(description = "模块类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单")
private String type;
@Schema(description = "模块数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "188")
private Long bizId;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
private Long userId;
}

View File

@@ -0,0 +1,52 @@
package com.njcn.msgpush.module.system.api.logger.dto;
import com.njcn.msgpush.module.system.api.user.AdminUserApi;
import com.fhs.core.trans.anno.Trans;
import com.fhs.core.trans.constant.TransType;
import com.fhs.core.trans.vo.VO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(name = "RPC 服务 - 系统操作日志 Response DTO")
@Data
public class OperateLogRespDTO implements VO {
@Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "链路追踪编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "89aca178-a370-411c-ae02-3f0d672be4ab")
private String traceId;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
@Trans(type = TransType.AUTO_TRANS, key = AdminUserApi.PREFIX, fields = "nickname", ref = "userName")
private Long userId;
@Schema(description = "用户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private String userName;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2" )
private Integer userType;
@Schema(description = "操作模块类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单")
private String type;
@Schema(description = "操作名", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建订单")
private String subType;
@Schema(description = "操作模块业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "188")
private Long bizId;
@Schema(description = "操作内容", requiredMode = Schema.RequiredMode.REQUIRED,
example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从灿能改成源码")
private String action;
@Schema(description = "拓展字段", example = "{\"orderId\": \"1\"}")
private String extra;
@Schema(description = "请求方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "GET")
private String requestMethod;
@Schema(description = "请求地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "/order/get")
private String requestUrl;
@Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
private String userIp;
@Schema(description = "浏览器 UserAgent", requiredMode = Schema.RequiredMode.REQUIRED, example = "Mozilla/5.0")
private String userAgent;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,28 @@
package com.njcn.msgpush.module.system.api.mail;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.api.mail.dto.MailSendSingleToUserReqDTO;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 邮件发送")
public interface MailSendApi {
String PREFIX = ApiConstants.PREFIX + "/mail/send";
@PostMapping(PREFIX + "/send-single-admin")
@Operation(summary = "发送单条邮件给 Admin 用户", description = "在 mail 为空时,使用 userId 加载对应 Admin 的邮箱")
CommonResult<Long> sendSingleMailToAdmin(@Valid @RequestBody MailSendSingleToUserReqDTO reqDTO);
@PostMapping(PREFIX + "/send-single-member")
@Operation(summary = "发送单条邮件给 Member 用户", description = "在 mail 为空时,使用 userId 加载对应 Member 的邮箱")
CommonResult<Long> sendSingleMailToMember(@Valid @RequestBody MailSendSingleToUserReqDTO reqDTO);
}

View File

@@ -0,0 +1,50 @@
package com.njcn.msgpush.module.system.api.mail.dto;
import lombok.Data;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
/**
* 邮件发送 Request DTO
*
* @author wangjingqi
*/
@Data
public class MailSendSingleToUserReqDTO {
/**
* 用户编号
*
* 如果非空,则加载对应用户的邮箱,添加到 {@link #toMails} 中
*/
private Long userId;
/**
* 收件邮箱
*/
private List<@Email String> toMails;
/**
* 抄送邮箱
*/
private List<@Email String> ccMails;
/**
* 密送邮箱
*/
private List<@Email String> bccMails;
/**
* 邮件模板编号
*/
@NotNull(message = "邮件模板编号不能为空")
private String templateCode;
/**
* 邮件模板参数
*/
private Map<String, Object> templateParams;
}

View File

@@ -0,0 +1,28 @@
package com.njcn.msgpush.module.system.api.notify;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 站内信发送")
public interface NotifyMessageSendApi {
String PREFIX = ApiConstants.PREFIX + "/notify/send";
@PostMapping(PREFIX + "/send-single-admin")
@Operation(summary = "发送单条站内信给 Admin 用户")
CommonResult<Long> sendSingleMessageToAdmin(@Valid @RequestBody NotifySendSingleToUserReqDTO reqDTO);
@PostMapping(PREFIX + "/send-single-member")
@Operation(summary = "发送单条站内信给 Member 用户")
CommonResult<Long> sendSingleMessageToMember(@Valid @RequestBody NotifySendSingleToUserReqDTO reqDTO);
}

View File

@@ -0,0 +1,23 @@
package com.njcn.msgpush.module.system.api.notify.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
@Schema(description = "RPC 服务 - 站内信发送给 Admin 或者 Member 用户 Request DTO")
@Data
public class NotifySendSingleToUserReqDTO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "用户编号不能为空")
private Long userId;
@Schema(description = "站内信模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "USER_SEND")
@NotEmpty(message = "站内信模板编号不能为空")
private String templateCode;
@Schema(description = "邮件模板参数")
private Map<String, Object> templateParams;
}

View File

@@ -0,0 +1,4 @@
/**
* System API 包,定义暴露给其它模块的 API
*/
package com.njcn.msgpush.module.system.api;

View File

@@ -0,0 +1,27 @@
package com.njcn.msgpush.module.system.api.permission;
import com.njcn.msgpush.framework.common.biz.system.permission.PermissionCommonApi;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.Set;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 权限")
public interface PermissionApi extends PermissionCommonApi {
String PREFIX = ApiConstants.PREFIX + "/permission";
@GetMapping(PREFIX + "/user-role-id-list-by-role-id")
@Operation(summary = "获得拥有多个角色的用户编号集合")
@Parameter(name = "roleIds", description = "角色编号集合", example = "1,2", required = true)
CommonResult<Set<Long>> getUserRoleIdListByRoleIds(@RequestParam("roleIds") Collection<Long> roleIds);
}

View File

@@ -0,0 +1,25 @@
package com.njcn.msgpush.module.system.api.permission;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 角色")
public interface RoleApi {
String PREFIX = ApiConstants.PREFIX + "/role";
@GetMapping(PREFIX + "/valid")
@Operation(summary = "校验角色是否合法")
@Parameter(name = "ids", description = "角色编号数组", example = "1,2", required = true)
CommonResult<Boolean> validRoleList(@RequestParam("ids") Collection<Long> ids);
}

View File

@@ -0,0 +1,96 @@
package com.njcn.msgpush.module.system.api.user;
import cn.hutool.core.convert.Convert;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.collection.CollectionUtils;
import com.njcn.msgpush.module.system.api.user.dto.AdminUserRespDTO;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import com.fhs.core.trans.anno.AutoTrans;
import com.fhs.trans.service.AutoTransable;
import feign.FeignIgnore;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.njcn.msgpush.module.system.api.user.AdminUserApi.PREFIX;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 管理员用户")
@AutoTrans(namespace = PREFIX, fields = {"nickname"})
public interface AdminUserApi extends AutoTransable<AdminUserRespDTO> {
String PREFIX = ApiConstants.PREFIX + "/user";
@GetMapping(PREFIX + "/get")
@Operation(summary = "通过用户 ID 查询用户")
@Parameter(name = "id", description = "用户编号", example = "1", required = true)
CommonResult<AdminUserRespDTO> getUser(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/list-by-subordinate")
@Operation(summary = "通过用户 ID 查询用户下属")
@Parameter(name = "id", description = "用户编号", example = "1", required = true)
CommonResult<List<AdminUserRespDTO>> getUserListBySubordinate(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/list")
@Operation(summary = "通过用户 ID 查询用户们")
@Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true)
CommonResult<List<AdminUserRespDTO>> getUserList(@RequestParam("ids") Collection<Long> ids);
@GetMapping(PREFIX + "/list-by-dept-id")
@Operation(summary = "获得指定部门的用户数组")
@Parameter(name = "deptIds", description = "部门编号数组", example = "1,2", required = true)
CommonResult<List<AdminUserRespDTO>> getUserListByDeptIds(@RequestParam("deptIds") Collection<Long> deptIds);
@GetMapping(PREFIX + "/list-by-post-id")
@Operation(summary = "获得指定岗位的用户数组")
@Parameter(name = "postIds", description = "岗位编号数组", example = "2,3", required = true)
CommonResult<List<AdminUserRespDTO>> getUserListByPostIds(@RequestParam("postIds") Collection<Long> postIds);
/**
* 获得用户 Map
*
* @param ids 用户编号数组
* @return 用户 Map
*/
default Map<Long, AdminUserRespDTO> getUserMap(Collection<Long> ids) {
List<AdminUserRespDTO> users = getUserList(ids).getCheckedData();
return CollectionUtils.convertMap(users, AdminUserRespDTO::getId);
}
/**
* 校验用户是否有效。如下情况,视为无效:
* 1. 用户编号不存在
* 2. 用户被禁用
*
* @param id 用户编号
*/
default void validateUser(Long id) {
validateUserList(Collections.singleton(id));
}
@GetMapping(PREFIX + "/valid")
@Operation(summary = "校验用户们是否有效")
@Parameter(name = "ids", description = "用户编号数组", example = "3,5", required = true)
CommonResult<Boolean> validateUserList(@RequestParam("ids") Collection<Long> ids);
@Override
@FeignIgnore
default List<AdminUserRespDTO> selectByIds(List<?> ids) {
return getUserList(Convert.toList(Long.class, ids)).getCheckedData();
}
@Override
@FeignIgnore
default AdminUserRespDTO selectById(Object id) {
return getUser(Convert.toLong(id)).getCheckedData();
}
}

View File

@@ -0,0 +1,34 @@
package com.njcn.msgpush.module.system.api.user.dto;
import com.fhs.core.trans.vo.VO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Set;
@Schema(description = "RPC 服务 - Admin 用户 Response DTO")
@Data
public class AdminUserRespDTO implements VO {
@Schema(description = "用户 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小王")
private String nickname;
@Schema(description = "帐号状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status; // 参见 CommonStatusEnum 枚举
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long deptId;
@Schema(description = "岗位编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 3]")
private Set<Long> postIds;
@Schema(description = "手机号码", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300")
private String mobile;
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String avatar;
}

View File

@@ -0,0 +1,23 @@
package com.njcn.msgpush.module.system.enums;
import com.njcn.msgpush.framework.common.enums.RpcConstants;
/**
* API 相关的枚举
*
* @author hongawen
*/
public class ApiConstants {
/**
* 服务名
*
* 注意,需要保证和 spring.application.name 保持一致
*/
public static final String NAME = "system-server";
public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/system";
public static final String VERSION = "1.0.0";
}

View File

@@ -0,0 +1,26 @@
package com.njcn.msgpush.module.system.enums;
/**
* System 字典类型的枚举类
*
* @author hongawen
*/
public interface DictTypeConstants {
String USER_TYPE = "user_type"; // 用户类型
String COMMON_STATUS = "common_status"; // 系统状态
// ========== SYSTEM 模块 ==========
String USER_SEX = "system_user_sex"; // 用户性别
String DATA_SCOPE = "system_data_scope"; // 数据范围
String LOGIN_TYPE = "system_login_type"; // 登录日志的类型
String LOGIN_RESULT = "system_login_result"; // 登录结果
String SMS_CHANNEL_CODE = "system_sms_channel_code"; // 短信渠道编码
String SMS_TEMPLATE_TYPE = "system_sms_template_type"; // 短信模板类型
String SMS_SEND_STATUS = "system_sms_send_status"; // 短信发送状态
String SMS_RECEIVE_STATUS = "system_sms_receive_status"; // 短信接收状态
}

View File

@@ -0,0 +1,171 @@
package com.njcn.msgpush.module.system.enums;
import com.njcn.msgpush.framework.common.exception.ErrorCode;
/**
* System 错误码枚举类
*
* system 系统,使用 1-002-000-000 段
*/
public interface ErrorCodeConstants {
// ========== AUTH 模块 1-002-000-000 ==========
ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_002_000_000, "登录失败,账号密码不正确");
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_002_000_001, "登录失败,账号被禁用");
ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_004, "验证码不正确,原因:{}");
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "未绑定账号,需要进行绑定");
ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "手机号不存在");
ErrorCode AUTH_REGISTER_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_008, "验证码不正确,原因:{}");
// ========== 菜单模块 1-002-001-000 ==========
ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1_002_001_000, "已经存在该名字的菜单");
ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1_002_001_001, "父菜单不存在");
ErrorCode MENU_PARENT_ERROR = new ErrorCode(1_002_001_002, "不能设置自己为父菜单");
ErrorCode MENU_NOT_EXISTS = new ErrorCode(1_002_001_003, "菜单不存在");
ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1_002_001_004, "存在子菜单,无法删除");
ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1_002_001_005, "父菜单的类型必须是目录或者菜单");
ErrorCode MENU_COMPONENT_NAME_DUPLICATE = new ErrorCode(1_002_001_006, "已经存在该组件名的菜单");
// ========== 角色模块 1-002-002-000 ==========
ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1_002_002_000, "角色不存在");
ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1_002_002_001, "已经存在名为【{}】的角色");
ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1_002_002_002, "已经存在标识为【{}】的角色");
ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能操作类型为系统内置的角色");
ErrorCode ROLE_IS_DISABLE = new ErrorCode(1_002_002_004, "名字为【{}】的角色已被禁用");
ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1_002_002_005, "标识【{}】不能使用");
// ========== 用户模块 1-002-003-000 ==========
ErrorCode USER_USERNAME_EXISTS = new ErrorCode(1_002_003_000, "用户账号已经存在");
ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1_002_003_001, "手机号已经存在");
ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1_002_003_002, "邮箱已经存在");
ErrorCode USER_NOT_EXISTS = new ErrorCode(1_002_003_003, "用户不存在");
ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_002_003_004, "导入用户数据不能为空!");
ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1_002_003_005, "用户密码校验失败");
ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用");
ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})");
ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空");
ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册");
ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭");
// ========== 部门模块 1-002-004-000 ==========
ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");
ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1_002_004_001,"父级部门不存在");
ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "当前部门不存在");
ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1_002_004_003, "存在子部门,无法删除");
ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门");
ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择");
ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门");
// ========== 岗位模块 1-002-005-000 ==========
ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在");
ErrorCode POST_NOT_ENABLE = new ErrorCode(1_002_005_001, "岗位({}) 不处于开启状态,不允许选择");
ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1_002_005_002, "已经存在该名字的岗位");
ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1_002_005_003, "已经存在该标识的岗位");
// ========== 字典类型 1-002-006-000 ==========
ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1_002_006_001, "当前字典类型不存在");
ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1_002_006_002, "字典类型不处于开启状态,不允许选择");
ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1_002_006_003, "已经存在该名字的字典类型");
ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1_002_006_004, "已经存在该类型的字典类型");
ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1_002_006_005, "无法删除,该字典类型还有字典数据");
// ========== 字典数据 1-002-007-000 ==========
ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1_002_007_001, "当前字典数据不存在");
ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1_002_007_002, "字典数据({})不处于开启状态,不允许选择");
ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1_002_007_003, "已经存在该值的字典数据");
// ========== 通知公告 1-002-008-000 ==========
ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1_002_008_001, "当前通知公告不存在");
// ========== 短信渠道 1-002-011-000 ==========
ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1_002_011_000, "短信渠道不存在");
ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1_002_011_001, "短信渠道不处于开启状态,不允许选择");
ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1_002_011_002, "无法删除,该短信渠道还有短信模板");
// ========== 短信模板 1-002-012-000 ==========
ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在");
ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_012_001, "已经存在编码为【{}】的短信模板");
ErrorCode SMS_TEMPLATE_API_ERROR = new ErrorCode(1_002_012_002, "短信 API 模板调用失败,原因是:{}");
ErrorCode SMS_TEMPLATE_API_AUDIT_CHECKING = new ErrorCode(1_002_012_003, "短信 API 模版无法使用,原因:审批中");
ErrorCode SMS_TEMPLATE_API_AUDIT_FAIL = new ErrorCode(1_002_012_004, "短信 API 模版无法使用,原因:审批不通过,{}");
ErrorCode SMS_TEMPLATE_API_NOT_FOUND = new ErrorCode(1_002_012_005, "短信 API 模版无法使用,原因:模版不存在");
// ========== 短信发送 1-002-013-000 ==========
ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在");
ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_013_001, "模板参数({})缺失");
ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_013_002, "短信模板不存在");
// ========== 短信验证码 1-002-014-000 ==========
ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1_002_014_000, "验证码不存在");
ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1_002_014_001, "验证码已过期");
ErrorCode SMS_CODE_USED = new ErrorCode(1_002_014_002, "验证码已使用");
ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量");
ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频繁");
// ========== 租户信息 1-002-015-000 ==========
ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1_002_015_000, "租户不存在");
ErrorCode TENANT_DISABLE = new ErrorCode(1_002_015_001, "名字为【{}】的租户已被禁用");
ErrorCode TENANT_EXPIRE = new ErrorCode(1_002_015_002, "名字为【{}】的租户已过期");
ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1_002_015_003, "系统租户不能进行修改、删除等操作!");
ErrorCode TENANT_NAME_DUPLICATE = new ErrorCode(1_002_015_004, "名字为【{}】的租户已存在");
ErrorCode TENANT_WEBSITE_DUPLICATE = new ErrorCode(1_002_015_005, "域名为【{}】的租户已存在");
// ========== 租户套餐 1-002-016-000 ==========
ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1_002_016_000, "租户套餐不存在");
ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1_002_016_001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除");
ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1_002_016_002, "名字为【{}】的租户套餐已被禁用");
ErrorCode TENANT_PACKAGE_NAME_DUPLICATE = new ErrorCode(1_002_016_003, "已经存在该名字的租户套餐");
// ========== 社交用户 1-002-018-000 ==========
ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1_002_018_000, "社交授权失败,原因是:{}");
ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1_002_018_001, "社交授权失败,找不到对应的用户");
ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_002_018_200, "获得手机号失败");
ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR = new ErrorCode(1_002_018_201, "获得小程序码失败");
ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR = new ErrorCode(1_002_018_202, "获得小程序订阅消息模版失败");
ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR = new ErrorCode(1_002_018_203, "发送小程序订阅消息失败");
ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR = new ErrorCode(1_002_018_204, "上传微信小程序发货信息失败");
ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR = new ErrorCode(1_002_018_205, "上传微信小程序订单收货信息失败");
ErrorCode SOCIAL_CLIENT_NOT_EXISTS = new ErrorCode(1_002_018_210, "社交客户端不存在");
ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_211, "社交客户端已存在配置");
// ========== OAuth2 客户端 1-002-020-000 =========
ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在");
ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在");
ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1_002_020_002, "OAuth2 客户端已禁用");
ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1_002_020_003, "不支持该授权类型");
ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1_002_020_004, "授权范围过大");
ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1_002_020_005, "无效 redirect_uri: {}");
ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1_002_020_006, "无效 client_secret: {}");
// ========== OAuth2 授权 1-002-021-000 =========
ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1_002_021_000, "client_id 不匹配");
ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1_002_021_001, "redirect_uri 不匹配");
ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1_002_021_002, "state 不匹配");
// ========== OAuth2 授权 1-002-022-000 =========
ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在");
ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1_002_022_001, "code 已过期");
// ========== 邮箱账号 1-002-023-000 ==========
ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1_002_023_000, "邮箱账号不存在");
ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1_002_023_001, "无法删除,该邮箱账号还有邮件模板");
// ========== 邮件模版 1-002-024-000 ==========
ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_024_000, "邮件模版不存在");
ErrorCode MAIL_TEMPLATE_CODE_EXISTS = new ErrorCode(1_002_024_001, "邮件模版 code({}) 已存在");
// ========== 邮件发送 1-002-025-000 ==========
ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_025_000, "模板参数({})缺失");
ErrorCode MAIL_SEND_MAIL_NOT_EXISTS = new ErrorCode(1_002_025_001, "邮箱不存在");
// ========== 站内信模版 1-002-026-000 ==========
ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_026_000, "站内信模版不存在");
ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_026_001, "已经存在编码为【{}】的站内信模板");
// ========== 站内信模版 1-002-027-000 ==========
// ========== 站内信发送 1-002-028-000 ==========
ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失");
}

View File

@@ -0,0 +1,33 @@
package com.njcn.msgpush.module.system.enums;
/**
* System 操作日志枚举
* 目的:统一管理,也减少 Service 里各种“复杂”字符串
*
* @author hongawen
*/
public interface LogRecordConstants {
// ======================= SYSTEM_USER 用户 =======================
String SYSTEM_USER_TYPE = "SYSTEM 用户";
String SYSTEM_USER_CREATE_SUB_TYPE = "创建用户";
String SYSTEM_USER_CREATE_SUCCESS = "创建了用户【{{#user.nickname}}】";
String SYSTEM_USER_UPDATE_SUB_TYPE = "更新用户";
String SYSTEM_USER_UPDATE_SUCCESS = "更新了用户【{{#user.nickname}}】: {_DIFF{#updateReqVO}}";
String SYSTEM_USER_DELETE_SUB_TYPE = "删除用户";
String SYSTEM_USER_DELETE_SUCCESS = "删除了用户【{{#user.nickname}}】";
String SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE = "重置用户密码";
String SYSTEM_USER_UPDATE_PASSWORD_SUCCESS = "将用户【{{#user.nickname}}】的密码从【{{#user.password}}】重置为【{{#newPassword}}】";
// ======================= SYSTEM_ROLE 角色 =======================
String SYSTEM_ROLE_TYPE = "SYSTEM 角色";
String SYSTEM_ROLE_CREATE_SUB_TYPE = "创建角色";
String SYSTEM_ROLE_CREATE_SUCCESS = "创建了角色【{{#role.name}}】";
String SYSTEM_ROLE_UPDATE_SUB_TYPE = "更新角色";
String SYSTEM_ROLE_UPDATE_SUCCESS = "更新了角色【{{#role.name}}】: {_DIFF{#updateReqVO}}";
String SYSTEM_ROLE_DELETE_SUB_TYPE = "删除角色";
String SYSTEM_ROLE_DELETE_SUCCESS = "删除了角色【{{#role.name}}】";
}

View File

@@ -0,0 +1,27 @@
package com.njcn.msgpush.module.system.enums.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 性别的枚举值
*
* @author hongawen
*/
@Getter
@AllArgsConstructor
public enum SexEnum {
/** 男 */
MALE(1),
/** 女 */
FEMALE(2),
/* 未知 */
UNKNOWN(0);
/**
* 性别
*/
private final Integer sex;
}

View File

@@ -0,0 +1,27 @@
package com.njcn.msgpush.module.system.enums.logger;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登录日志的类型枚举
*/
@Getter
@AllArgsConstructor
public enum LoginLogTypeEnum {
LOGIN_USERNAME(100), // 使用账号登录
LOGIN_SOCIAL(101), // 使用社交登录
LOGIN_MOBILE(103), // 使用手机登陆
LOGIN_SMS(104), // 使用短信登陆
LOGOUT_SELF(200), // 自己主动登出
LOGOUT_DELETE(202), // 强制退出
;
/**
* 日志类型
*/
private final Integer type;
}

View File

@@ -0,0 +1,26 @@
package com.njcn.msgpush.module.system.enums.logger;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登录结果的枚举类
*/
@Getter
@AllArgsConstructor
public enum LoginResultEnum {
SUCCESS(0), // 成功
BAD_CREDENTIALS(10), // 账号或密码不正确
USER_DISABLED(20), // 用户被禁用
CAPTCHA_NOT_FOUND(30), // 图片验证码不存在
CAPTCHA_CODE_ERROR(31), // 图片验证码不正确
;
/**
* 结果
*/
private final Integer result;
}

View File

@@ -0,0 +1,24 @@
package com.njcn.msgpush.module.system.enums.mail;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 邮件的发送状态枚举
*
* @author wangjingyi
* @since 2022/4/10 13:39
*/
@Getter
@AllArgsConstructor
public enum MailSendStatusEnum {
INIT(0), // 初始化
SUCCESS(10), // 发送成功
FAILURE(20), // 发送失败
IGNORE(30), // 忽略,即不发送
;
private final int status;
}

View File

@@ -0,0 +1,23 @@
package com.njcn.msgpush.module.system.enums.notice;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 通知类型
*
* @author hongawen
*/
@Getter
@AllArgsConstructor
public enum NoticeTypeEnum {
NOTICE(1),
ANNOUNCEMENT(2);
/**
* 类型
*/
private final Integer type;
}

View File

@@ -0,0 +1,26 @@
package com.njcn.msgpush.module.system.enums.notify;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 通知模板类型枚举
*
* @author HUIHUI
*/
@Getter
@AllArgsConstructor
public enum NotifyTemplateTypeEnum {
/**
* 系统消息
*/
SYSTEM_MESSAGE(2),
/**
* 通知消息
*/
NOTIFICATION_MESSAGE(1);
private final Integer type;
}

View File

@@ -0,0 +1,12 @@
package com.njcn.msgpush.module.system.enums.oauth2;
/**
* OAuth2.0 客户端的通用枚举
*
* @author hongawen
*/
public interface OAuth2ClientConstants {
String CLIENT_ID_DEFAULT = "default";
}

View File

@@ -0,0 +1,29 @@
package com.njcn.msgpush.module.system.enums.oauth2;
import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* OAuth2 授权类型(模式)的枚举
*
* @author hongawen
*/
@AllArgsConstructor
@Getter
public enum OAuth2GrantTypeEnum {
PASSWORD("password"), // 密码模式
AUTHORIZATION_CODE("authorization_code"), // 授权码模式
IMPLICIT("implicit"), // 简化模式
CLIENT_CREDENTIALS("client_credentials"), // 客户端模式
REFRESH_TOKEN("refresh_token"), // 刷新模式
;
private final String grantType;
public static OAuth2GrantTypeEnum getByGrantType(String grantType) {
return ArrayUtil.firstMatch(o -> o.getGrantType().equals(grantType), values());
}
}

View File

@@ -0,0 +1,40 @@
package com.njcn.msgpush.module.system.enums.permission;
import com.njcn.msgpush.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 数据范围枚举类
*
* 用于实现数据级别的权限
*
* @author hongawen
*/
@Getter
@AllArgsConstructor
public enum DataScopeEnum implements ArrayValuable<Integer> {
ALL(1), // 全部数据权限
DEPT_CUSTOM(2), // 指定部门数据权限
DEPT_ONLY(3), // 部门数据权限
DEPT_AND_CHILD(4), // 部门及以下数据权限
SELF(5); // 仅本人数据权限
/**
* 范围
*/
private final Integer scope;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@@ -0,0 +1,25 @@
package com.njcn.msgpush.module.system.enums.permission;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 菜单类型枚举类
*
* @author hongawen
*/
@Getter
@AllArgsConstructor
public enum MenuTypeEnum {
DIR(1), // 目录
MENU(2), // 菜单
BUTTON(3) // 按钮
;
/**
* 类型
*/
private final Integer type;
}

View File

@@ -0,0 +1,32 @@
package com.njcn.msgpush.module.system.enums.permission;
import com.njcn.msgpush.framework.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 角色标识枚举
*/
@Getter
@AllArgsConstructor
public enum RoleCodeEnum {
SUPER_ADMIN("super_admin", "超级管理员"),
TENANT_ADMIN("tenant_admin", "租户管理员"),
CRM_ADMIN("crm_admin", "CRM 管理员"); // CRM 系统专用
;
/**
* 角色编码
*/
private final String code;
/**
* 名字
*/
private final String name;
public static boolean isSuperAdmin(String code) {
return ObjectUtils.equalsAny(code, SUPER_ADMIN.getCode());
}
}

View File

@@ -0,0 +1,21 @@
package com.njcn.msgpush.module.system.enums.permission;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum RoleTypeEnum {
/**
* 内置角色
*/
SYSTEM(1),
/**
* 自定义角色
*/
CUSTOM(2);
private final Integer type;
}

View File

@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.njcn</groupId>
<artifactId>msgpush-module-system</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-module-system-server</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
system 模块下,我们放通用业务,支撑上层的核心业务。
例如说:用户、部门、权限、数据字典等等
</description>
<dependencies>
<!-- Spring Cloud 基础 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-env</artifactId>
</dependency>
<!-- 依赖服务 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-biz-ip</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-security</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-redis</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-rpc</artifactId>
</dependency>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- 消息队列相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-mq</artifactId>
</dependency>
<!-- 服务保障相关 TODO 芋艿:暂时去掉 -->
<!-- <dependency>-->
<!-- <groupId>com.njcn</groupId>-->
<!-- <artifactId>msgpush-spring-boot-starter-protection</artifactId>-->
<!-- </dependency>-->
<!-- Test 测试相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码,一般用于登录使用 -->
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,18 @@
package com.njcn.msgpush.module.system;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 项目的启动类
*
* @author hongawen
*/
@SpringBootApplication
public class SystemServerApplication {
public static void main(String[] args) {
SpringApplication.run(SystemServerApplication.class, args);
}
}

View File

@@ -0,0 +1,49 @@
package com.njcn.msgpush.module.system.api.dept;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.system.api.dept.dto.DeptRespDTO;
import com.njcn.msgpush.module.system.dal.dataobject.dept.DeptDO;
import com.njcn.msgpush.module.system.service.dept.DeptService;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class DeptApiImpl implements DeptApi {
@Resource
private DeptService deptService;
@Override
public CommonResult<DeptRespDTO> getDept(Long id) {
DeptDO dept = deptService.getDept(id);
return success(BeanUtils.toBean(dept, DeptRespDTO.class));
}
@Override
public CommonResult<List<DeptRespDTO>> getDeptList(Collection<Long> ids) {
List<DeptDO> depts = deptService.getDeptList(ids);
return success(BeanUtils.toBean(depts, DeptRespDTO.class));
}
@Override
public CommonResult<Boolean> validateDeptList(Collection<Long> ids) {
deptService.validateDeptList(ids);
return success(true);
}
@Override
public CommonResult<List<DeptRespDTO>> getChildDeptList(Long id) {
List<DeptDO> depts = deptService.getChildDeptList(id);
return success(BeanUtils.toBean(depts, DeptRespDTO.class));
}
}

View File

@@ -0,0 +1,36 @@
package com.njcn.msgpush.module.system.api.dept;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.system.api.dept.dto.PostRespDTO;
import com.njcn.msgpush.module.system.dal.dataobject.dept.PostDO;
import com.njcn.msgpush.module.system.service.dept.PostService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class PostApiImpl implements PostApi {
@Resource
private PostService postService;
@Override
public CommonResult<Boolean> validPostList(Collection<Long> ids) {
postService.validatePostList(ids);
return success(true);
}
@Override
public CommonResult<List<PostRespDTO>> getPostList(Collection<Long> ids) {
List<PostDO> list = postService.getPostList(ids);
return success(BeanUtils.toBean(list, PostRespDTO.class));
}
}

View File

@@ -0,0 +1,38 @@
package com.njcn.msgpush.module.system.api.dict;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.common.biz.system.dict.dto.DictDataRespDTO;
import com.njcn.msgpush.module.system.dal.dataobject.dict.DictDataDO;
import com.njcn.msgpush.module.system.service.dict.DictDataService;
import org.springframework.context.annotation.Primary;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
@Primary // 由于 DictDataCommonApi 的存在,必须声明为 @Primary Bean
public class DictDataApiImpl implements DictDataApi {
@Resource
private DictDataService dictDataService;
@Override
public CommonResult<Boolean> validateDictDataList(String dictType, Collection<String> values) {
dictDataService.validateDictDataList(dictType, values);
return success(true);
}
@Override
public CommonResult<List<DictDataRespDTO>> getDictDataList(String dictType) {
List<DictDataDO> list = dictDataService.getDictDataListByDictType(dictType);
return success(BeanUtils.toBean(list, DictDataRespDTO.class));
}
}

View File

@@ -0,0 +1,26 @@
package com.njcn.msgpush.module.system.api.logger;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.api.logger.dto.LoginLogCreateReqDTO;
import com.njcn.msgpush.module.system.service.logger.LoginLogService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class LoginLogApiImpl implements LoginLogApi {
@Resource
private LoginLogService loginLogService;
@Override
public CommonResult<Boolean> createLoginLog(LoginLogCreateReqDTO reqDTO) {
loginLogService.createLoginLog(reqDTO);
return success(true);
}
}

View File

@@ -0,0 +1,38 @@
package com.njcn.msgpush.module.system.api.logger;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO;
import com.njcn.msgpush.module.system.api.logger.dto.OperateLogPageReqDTO;
import com.njcn.msgpush.module.system.api.logger.dto.OperateLogRespDTO;
import com.njcn.msgpush.module.system.dal.dataobject.logger.OperateLogDO;
import com.njcn.msgpush.module.system.service.logger.OperateLogService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Primary;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
@Primary // 由于 OperateLogCommonApi 的存在,必须声明为 @Primary Bean
public class OperateLogApiImpl implements OperateLogApi {
@Resource
private OperateLogService operateLogService;
@Override
public CommonResult<Boolean> createOperateLog(OperateLogCreateReqDTO createReqDTO) {
operateLogService.createOperateLog(createReqDTO);
return success(true);
}
@Override
public CommonResult<PageResult<OperateLogRespDTO>> getOperateLogPage(OperateLogPageReqDTO pageReqDTO) {
PageResult<OperateLogDO> operateLogPage = operateLogService.getOperateLogPage(pageReqDTO);
return success(BeanUtils.toBean(operateLogPage, OperateLogRespDTO.class));
}
}

View File

@@ -0,0 +1,32 @@
package com.njcn.msgpush.module.system.api.notify;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import com.njcn.msgpush.module.system.service.notify.NotifySendService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class NotifyMessageSendApiImpl implements NotifyMessageSendApi {
@Resource
private NotifySendService notifySendService;
@Override
public CommonResult<Long> sendSingleMessageToAdmin(NotifySendSingleToUserReqDTO reqDTO) {
return success(notifySendService.sendSingleNotifyToAdmin(reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams()));
}
@Override
public CommonResult<Long> sendSingleMessageToMember(NotifySendSingleToUserReqDTO reqDTO) {
return success(notifySendService.sendSingleNotifyToMember(reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams()));
}
}

View File

@@ -0,0 +1,51 @@
package com.njcn.msgpush.module.system.api.oauth2;
import com.njcn.msgpush.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.tenant.core.aop.TenantIgnore;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.njcn.msgpush.module.system.service.oauth2.OAuth2TokenService;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class OAuth2TokenApiImpl implements OAuth2TokenCommonApi {
@Resource
private OAuth2TokenService oauth2TokenService;
@Override
public CommonResult<OAuth2AccessTokenRespDTO> createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(
reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId(), reqDTO.getScopes());
return success(BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class));
}
@Override
@TenantIgnore // 访问令牌校验时,无需传递租户编号;主要解决上传文件的场景,前端不会传递 tenant-id
public CommonResult<OAuth2AccessTokenCheckRespDTO> checkAccessToken(String accessToken) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.checkAccessToken(accessToken);
return success(BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenCheckRespDTO.class));
}
@Override
public CommonResult<OAuth2AccessTokenRespDTO> removeAccessToken(String accessToken) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(accessToken);
return success(BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class));
}
@Override
public CommonResult<OAuth2AccessTokenRespDTO> refreshAccessToken(String refreshToken, String clientId) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId);
return success(BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class));
}
}

View File

@@ -0,0 +1,44 @@
package com.njcn.msgpush.module.system.api.permission;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
import com.njcn.msgpush.module.system.service.permission.PermissionService;
import org.springframework.context.annotation.Primary;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.Set;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
@Primary // 由于 PermissionCommonApi 的存在,必须声明为 @Primary Bean
public class PermissionApiImpl implements PermissionApi {
@Resource
private PermissionService permissionService;
@Override
public CommonResult<Set<Long>> getUserRoleIdListByRoleIds(Collection<Long> roleIds) {
return success(permissionService.getUserRoleIdListByRoleId(roleIds));
}
@Override
public CommonResult<Boolean> hasAnyPermissions(Long userId, String... permissions) {
return success(permissionService.hasAnyPermissions(userId, permissions));
}
@Override
public CommonResult<Boolean> hasAnyRoles(Long userId, String... roles) {
return success(permissionService.hasAnyRoles(userId, roles));
}
@Override
public CommonResult<DeptDataPermissionRespDTO> getDeptDataPermission(Long userId) {
return success(permissionService.getDeptDataPermission(userId));
}
}

View File

@@ -0,0 +1,25 @@
package com.njcn.msgpush.module.system.api.permission;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.service.permission.RoleService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.Collection;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class RoleApiImpl implements RoleApi {
@Resource
private RoleService roleService;
@Override
public CommonResult<Boolean> validRoleList(Collection<Long> ids) {
roleService.validateRoleList(ids);
return success(true);
}
}

View File

@@ -0,0 +1,35 @@
package com.njcn.msgpush.module.system.api.tenant;
import com.njcn.msgpush.framework.common.biz.system.tenant.TenantCommonApi;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.tenant.core.aop.TenantIgnore;
import com.njcn.msgpush.module.system.service.tenant.TenantService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class TenantApiImpl implements TenantCommonApi {
@Resource
private TenantService tenantService;
@Override
@TenantIgnore // 防止递归。避免调用 /rpc-api/system/tenant/valid 接口时,又去触发 /rpc-api/system/tenant/valid 去校验
public CommonResult<List<Long>> getTenantIdList() {
return success(tenantService.getTenantIdList());
}
@Override
@TenantIgnore // 获得租户列表的时候,无需传递租户编号
public CommonResult<Boolean> validTenant(Long id) {
tenantService.validTenant(id);
return success(true);
}
}

View File

@@ -0,0 +1,91 @@
package com.njcn.msgpush.module.system.api.user;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.system.api.user.dto.AdminUserRespDTO;
import com.njcn.msgpush.module.system.dal.dataobject.dept.DeptDO;
import com.njcn.msgpush.module.system.dal.dataobject.user.AdminUserDO;
import com.njcn.msgpush.module.system.service.dept.DeptService;
import com.njcn.msgpush.module.system.service.user.AdminUserService;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
import static com.njcn.msgpush.framework.common.util.collection.CollectionUtils.convertSet;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class AdminUserApiImpl implements AdminUserApi {
@Resource
private AdminUserService userService;
@Resource
private DeptService deptService;
@Override
public CommonResult<AdminUserRespDTO> getUser(Long id) {
AdminUserDO user = userService.getUser(id);
return success(BeanUtils.toBean(user, AdminUserRespDTO.class));
}
@Override
public CommonResult<List<AdminUserRespDTO>> getUserListBySubordinate(Long id) {
// 1.1 获取用户负责的部门
AdminUserDO user = userService.getUser(id);
if (user == null) {
return success(Collections.emptyList());
}
ArrayList<Long> deptIds = new ArrayList<>();
DeptDO dept = deptService.getDept(user.getDeptId());
if (dept == null) {
return success(Collections.emptyList());
}
if (ObjUtil.notEqual(dept.getLeaderUserId(), id)) { // 校验为负责人
return success(Collections.emptyList());
}
deptIds.add(dept.getId());
// 1.2 获取所有子部门
List<DeptDO> childDeptList = deptService.getChildDeptList(dept.getId());
if (CollUtil.isNotEmpty(childDeptList)) {
deptIds.addAll(convertSet(childDeptList, DeptDO::getId));
}
// 2. 获取部门对应的用户信息
List<AdminUserDO> users = userService.getUserListByDeptIds(deptIds);
users.removeIf(item -> ObjUtil.equal(item.getId(), id)); // 排除自己
return success(BeanUtils.toBean(users, AdminUserRespDTO.class));
}
@Override
public CommonResult<List<AdminUserRespDTO>> getUserList(Collection<Long> ids) {
List<AdminUserDO> users = userService.getUserList(ids);
return success(BeanUtils.toBean(users, AdminUserRespDTO.class));
}
@Override
public CommonResult<List<AdminUserRespDTO>> getUserListByDeptIds(Collection<Long> deptIds) {
List<AdminUserDO> users = userService.getUserListByDeptIds(deptIds);
return success(BeanUtils.toBean(users, AdminUserRespDTO.class));
}
@Override
public CommonResult<List<AdminUserRespDTO>> getUserListByPostIds(Collection<Long> postIds) {
List<AdminUserDO> users = userService.getUserListByPostIds(postIds);
return success(BeanUtils.toBean(users, AdminUserRespDTO.class));
}
@Override
public CommonResult<Boolean> validateUserList(Collection<Long> ids) {
userService.validateUserList(ids);
return success(true);
}
}

View File

@@ -0,0 +1,51 @@
### 请求 /login 接口 => 成功
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tenant-id: {{adminTenantId}}
tag: Yunai.local
{
"username": "admin",
"password": "admin123",
"uuid": "3acd87a09a4f48fb9118333780e94883",
"code": "1024"
}
### 请求 /login 接口【加密 AES】 => 成功
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tenant-id: {{adminTenantId}}
tag: Yunai.local
X-API-ENCRYPT: true
WvSX9MOrenyGfBhEM0g1/hHgq8ocktMZ9OwAJ6MOG5FUrzYF/rG5JF1eMptQM1wT73VgDS05l/37WeRtad+JrqChAul/sR/SdOsUKqjBhvvQx1JVhzxr6s8uUP67aKTSZ6Psv7O32ELxXrzSaQvG5CInzz3w6sLtbNNLd1kXe6Q=
### 请求 /login 接口【加密 RSA】 => 成功
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tenant-id: {{adminTenantId}}
tag: Yunai.local
X-API-ENCRYPT: true
e7QZTork9ZV5CmgZvSd+cHZk3xdUxKtowLM02kOha+gxHK2H/daU8nVBYS3+bwuDRy5abf+Pz1QJJGVAEd27wwrXBmupOOA/bhpuzzDwcRuJRD+z+YgiNoEXFDRHERxPYlPqAe9zAHtihD0ceub1AjybQsEsROew4C3Q602XYW0=
### 请求 /login 接口 => 成功(无验证码)
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tenant-id: {{adminTenantId}}
{
"username": "admin",
"password": "admin123"
}
### 请求 /get-permission-info 接口 => 成功
GET {{baseUrl}}/system/auth/get-permission-info
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
### 请求 /list-menus 接口 => 成功
GET {{baseUrl}}/system/list-menus
Authorization: Bearer {{token}}
#Authorization: Bearer a6aa7714a2e44c95aaa8a2c5adc2a67a
tenant-id: {{adminTenantId}}

View File

@@ -0,0 +1,121 @@
package com.njcn.msgpush.module.system.controller.admin.auth;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.security.config.SecurityProperties;
import com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.msgpush.module.system.controller.admin.auth.vo.*;
import com.njcn.msgpush.module.system.convert.auth.AuthConvert;
import com.njcn.msgpush.module.system.dal.dataobject.permission.MenuDO;
import com.njcn.msgpush.module.system.dal.dataobject.permission.RoleDO;
import com.njcn.msgpush.module.system.dal.dataobject.user.AdminUserDO;
import com.njcn.msgpush.module.system.enums.logger.LoginLogTypeEnum;
import com.njcn.msgpush.module.system.service.auth.AdminAuthService;
import com.njcn.msgpush.module.system.service.permission.MenuService;
import com.njcn.msgpush.module.system.service.permission.PermissionService;
import com.njcn.msgpush.module.system.service.permission.RoleService;
import com.njcn.msgpush.module.system.service.user.AdminUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
import static com.njcn.msgpush.framework.common.util.collection.CollectionUtils.convertSet;
import static com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 认证")
@RestController
@RequestMapping("/system/auth")
@Validated
@Slf4j
public class AuthController {
@Resource
private AdminAuthService authService;
@Resource
private AdminUserService userService;
@Resource
private RoleService roleService;
@Resource
private MenuService menuService;
@Resource
private PermissionService permissionService;
@Resource
private SecurityProperties securityProperties;
@PostMapping("/login")
@PermitAll
@Operation(summary = "使用账号密码登录")
public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
return success(authService.login(reqVO));
}
@PostMapping("/logout")
@PermitAll
@Operation(summary = "登出系统")
public CommonResult<Boolean> logout(HttpServletRequest request) {
String token = SecurityFrameworkUtils.obtainAuthorization(request,
securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
if (StrUtil.isNotBlank(token)) {
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
}
return success(true);
}
@PostMapping("/refresh-token")
@PermitAll
@Operation(summary = "刷新令牌")
@Parameter(name = "refreshToken", description = "刷新令牌", required = true)
public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
return success(authService.refreshToken(refreshToken));
}
@GetMapping("/get-permission-info")
@Operation(summary = "获取登录用户的权限信息")
public CommonResult<AuthPermissionInfoRespVO> getPermissionInfo() {
// 1.1 获得用户信息
AdminUserDO user = userService.getUser(getLoginUserId());
if (user == null) {
return success(null);
}
// 1.2 获得角色列表
Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId());
if (CollUtil.isEmpty(roleIds)) {
return success(AuthConvert.INSTANCE.convert(user, Collections.emptyList(), Collections.emptyList()));
}
List<RoleDO> roles = roleService.getRoleList(roleIds);
roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色
// 1.3 获得菜单列表
Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId));
List<MenuDO> menuList = menuService.getMenuList(menuIds);
menuList = menuService.filterDisableMenus(menuList);
// 2. 拼接结果返回
return success(AuthConvert.INSTANCE.convert(user, roles, menuList));
}
@PostMapping("/register")
@PermitAll
@Operation(summary = "注册用户")
public CommonResult<AuthLoginRespVO> register(@RequestBody @Valid AuthRegisterReqVO registerReqVO) {
return success(authService.register(registerReqVO));
}
}

View File

@@ -0,0 +1,41 @@
package com.njcn.msgpush.module.system.controller.admin.auth.vo;
import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
@Schema(description = "管理后台 - 账号密码登录 Request VO如果登录并绑定社交用户需要传递 social 开头的参数")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthLoginReqVO extends CaptchaVerificationReqVO {
@Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "msgpushyuanma")
@NotEmpty(message = "登录账号不能为空")
@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
private String username;
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
@NotEmpty(message = "密码不能为空")
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
// ========== 绑定社交登录时,需要传递如下参数 ==========
@Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String socialCode;
@Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
private String socialState;
}

View File

@@ -0,0 +1,30 @@
package com.njcn.msgpush.module.system.controller.admin.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 登录 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthLoginRespVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long userId;
@Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "happy")
private String accessToken;
@Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "nice")
private String refreshToken;
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime expiresTime;
}

View File

@@ -0,0 +1,53 @@
package com.njcn.msgpush.module.system.controller.admin.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Schema(description = "管理后台 - 登录用户的菜单信息 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthMenuRespVO {
@Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private Long id;
@Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long parentId;
@Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private String name;
@Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post")
private String path;
@Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index")
private String component;
@Schema(description = "组件名", example = "SystemUser")
private String componentName;
@Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list")
private String icon;
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean visible;
@Schema(description = "是否缓存", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean keepAlive;
@Schema(description = "是否总是显示", example = "false")
private Boolean alwaysShow;
/**
* 子路由
*/
private List<AuthMenuRespVO> children;
}

View File

@@ -0,0 +1,102 @@
package com.njcn.msgpush.module.system.controller.admin.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Set;
@Schema(description = "管理后台 - 登录用户的权限信息 Response VO额外包括用户信息和角色列表")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthPermissionInfoRespVO {
@Schema(description = "用户信息", requiredMode = Schema.RequiredMode.REQUIRED)
private UserVO user;
@Schema(description = "角色标识数组", requiredMode = Schema.RequiredMode.REQUIRED)
private Set<String> roles;
@Schema(description = "操作权限数组", requiredMode = Schema.RequiredMode.REQUIRED)
private Set<String> permissions;
@Schema(description = "菜单树", requiredMode = Schema.RequiredMode.REQUIRED)
private List<MenuVO> menus;
@Schema(description = "用户信息 VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class UserVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能源码")
private String nickname;
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.jpg")
private String avatar;
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long deptId;
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "msgpush")
private String username;
@Schema(description = "用户邮箱", example = "msgpush@iocoder.cn")
private String email;
}
@Schema(description = "管理后台 - 登录用户的菜单信息 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class MenuVO {
@Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private Long id;
@Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long parentId;
@Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private String name;
@Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post")
private String path;
@Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index")
private String component;
@Schema(description = "组件名", example = "SystemUser")
private String componentName;
@Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list")
private String icon;
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean visible;
@Schema(description = "是否缓存", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean keepAlive;
@Schema(description = "是否总是显示", example = "false")
private Boolean alwaysShow;
/**
* 子路由
*/
private List<MenuVO> children;
}
}

View File

@@ -0,0 +1,31 @@
package com.njcn.msgpush.module.system.controller.admin.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
@Schema(description = "管理后台 - Register Request VO")
@Data
public class AuthRegisterReqVO extends CaptchaVerificationReqVO {
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "msgpush")
@NotBlank(message = "用户账号不能为空")
@Pattern(regexp = "^[a-zA-Z0-9]{4,30}$", message = "用户账号由 数字、字母 组成")
@Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符")
private String username;
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@NotBlank(message = "用户昵称不能为空")
@Size(max = 30, message = "用户昵称长度不能超过 30 个字符")
private String nickname;
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@NotEmpty(message = "密码不能为空")
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
}

View File

@@ -0,0 +1,32 @@
package com.njcn.msgpush.module.system.controller.admin.auth.vo;
import com.njcn.msgpush.framework.common.validation.Mobile;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
@Schema(description = "管理后台 - 短信重置账号密码 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthResetPasswordReqVO {
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234")
@NotEmpty(message = "密码不能为空")
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
@Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13312341234")
@NotEmpty(message = "手机号不能为空")
@Mobile
private String mobile;
@Schema(description = "手机短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@NotEmpty(message = "手机手机短信验证码不能为空")
private String code;
}

View File

@@ -0,0 +1,22 @@
package com.njcn.msgpush.module.system.controller.admin.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
@Schema(description = "管理后台 - 验证码 Request VO")
@Data
public class CaptchaVerificationReqVO {
// ========== 图片验证码相关 ==========
@Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==")
@NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class)
private String captchaVerification;
/**
* 开启验证码的 Group
*/
public interface CodeEnableGroup {
}
}

View File

@@ -0,0 +1,55 @@
package com.njcn.msgpush.module.system.controller.admin.captcha;
import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.util.servlet.ServletUtils;
import com.njcn.msgpush.framework.tenant.core.aop.TenantIgnore;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "管理后台 - 验证码")
@RestController("adminCaptchaController")
@RequestMapping("/system/captcha")
public class CaptchaController {
@Resource
private CaptchaService captchaService;
@PostMapping({"/get"})
@Operation(summary = "获得验证码")
@PermitAll
@TenantIgnore
public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) {
assert request.getRemoteHost() != null;
data.setBrowserInfo(getRemoteId(request));
return captchaService.get(data);
}
@PostMapping("/check")
@Operation(summary = "校验验证码")
@PermitAll
@TenantIgnore
public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) {
data.setBrowserInfo(getRemoteId(request));
return captchaService.check(data);
}
public static String getRemoteId(HttpServletRequest request) {
String ip = ServletUtils.getClientIP(request);
String ua = request.getHeader("user-agent");
if (StrUtil.isNotBlank(ip)) {
return ip + ua;
}
return request.getRemoteAddr() + ua;
}
}

View File

@@ -0,0 +1,93 @@
package com.njcn.msgpush.module.system.controller.admin.dept;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.dept.DeptRespVO;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.dept.DeptSimpleRespVO;
import com.njcn.msgpush.module.system.dal.dataobject.dept.DeptDO;
import com.njcn.msgpush.module.system.service.dept.DeptService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 部门")
@RestController
@RequestMapping("/system/dept")
@Validated
public class DeptController {
@Resource
private DeptService deptService;
@PostMapping("create")
@Operation(summary = "创建部门")
@PreAuthorize("@ss.hasPermission('system:dept:create')")
public CommonResult<Long> createDept(@Valid @RequestBody DeptSaveReqVO createReqVO) {
Long deptId = deptService.createDept(createReqVO);
return success(deptId);
}
@PutMapping("update")
@Operation(summary = "更新部门")
@PreAuthorize("@ss.hasPermission('system:dept:update')")
public CommonResult<Boolean> updateDept(@Valid @RequestBody DeptSaveReqVO updateReqVO) {
deptService.updateDept(updateReqVO);
return success(true);
}
@DeleteMapping("delete")
@Operation(summary = "删除部门")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:dept:delete')")
public CommonResult<Boolean> deleteDept(@RequestParam("id") Long id) {
deptService.deleteDept(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除部门")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('system:dept:delete')")
public CommonResult<Boolean> deleteDeptList(@RequestParam("ids") List<Long> ids) {
deptService.deleteDeptList(ids);
return success(true);
}
@GetMapping("/list")
@Operation(summary = "获取部门列表")
@PreAuthorize("@ss.hasPermission('system:dept:query')")
public CommonResult<List<DeptRespVO>> getDeptList(DeptListReqVO reqVO) {
List<DeptDO> list = deptService.getDeptList(reqVO);
return success(BeanUtils.toBean(list, DeptRespVO.class));
}
@GetMapping(value = {"/list-all-simple", "/simple-list"})
@Operation(summary = "获取部门精简信息列表", description = "只包含被开启的部门,主要用于前端的下拉选项")
public CommonResult<List<DeptSimpleRespVO>> getSimpleDeptList() {
List<DeptDO> list = deptService.getDeptList(
new DeptListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
return success(BeanUtils.toBean(list, DeptSimpleRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得部门信息")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:dept:query')")
public CommonResult<DeptRespVO> getDept(@RequestParam("id") Long id) {
DeptDO dept = deptService.getDept(id);
return success(BeanUtils.toBean(dept, DeptRespVO.class));
}
}

View File

@@ -0,0 +1,114 @@
package com.njcn.msgpush.module.system.controller.admin.dept;
import com.njcn.msgpush.framework.apilog.core.annotation.ApiAccessLog;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.excel.core.util.ExcelUtils;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.post.PostPageReqVO;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.post.PostRespVO;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.post.PostSimpleRespVO;
import com.njcn.msgpush.module.system.dal.dataobject.dept.PostDO;
import com.njcn.msgpush.module.system.service.dept.PostService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static com.njcn.msgpush.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 岗位")
@RestController
@RequestMapping("/system/post")
@Validated
public class PostController {
@Resource
private PostService postService;
@PostMapping("/create")
@Operation(summary = "创建岗位")
@PreAuthorize("@ss.hasPermission('system:post:create')")
public CommonResult<Long> createPost(@Valid @RequestBody PostSaveReqVO createReqVO) {
Long postId = postService.createPost(createReqVO);
return success(postId);
}
@PutMapping("/update")
@Operation(summary = "修改岗位")
@PreAuthorize("@ss.hasPermission('system:post:update')")
public CommonResult<Boolean> updatePost(@Valid @RequestBody PostSaveReqVO updateReqVO) {
postService.updatePost(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除岗位")
@PreAuthorize("@ss.hasPermission('system:post:delete')")
public CommonResult<Boolean> deletePost(@RequestParam("id") Long id) {
postService.deletePost(id);
return success(true);
}
@DeleteMapping("delete-list")
@Operation(summary = "批量删除岗位")
@PreAuthorize("@ss.hasPermission('system:post:delete')")
public CommonResult<Boolean> deletePostList(@RequestParam("ids") List<Long> ids) {
postService.deletePostList(ids);
return success(true);
}
@GetMapping(value = "/get")
@Operation(summary = "获得岗位信息")
@Parameter(name = "id", description = "岗位编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:post:query')")
public CommonResult<PostRespVO> getPost(@RequestParam("id") Long id) {
PostDO post = postService.getPost(id);
return success(BeanUtils.toBean(post, PostRespVO.class));
}
@GetMapping(value = {"/list-all-simple", "simple-list"})
@Operation(summary = "获取岗位全列表", description = "只包含被开启的岗位,主要用于前端的下拉选项")
public CommonResult<List<PostSimpleRespVO>> getSimplePostList() {
// 获得岗位列表,只要开启状态的
List<PostDO> list = postService.getPostList(null, Collections.singleton(CommonStatusEnum.ENABLE.getStatus()));
// 排序后,返回给前端
list.sort(Comparator.comparing(PostDO::getSort));
return success(BeanUtils.toBean(list, PostSimpleRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得岗位分页列表")
@PreAuthorize("@ss.hasPermission('system:post:query')")
public CommonResult<PageResult<PostRespVO>> getPostPage(@Validated PostPageReqVO pageReqVO) {
PageResult<PostDO> pageResult = postService.getPostPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, PostRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "岗位管理")
@PreAuthorize("@ss.hasPermission('system:post:export')")
@ApiAccessLog(operateType = EXPORT)
public void export(HttpServletResponse response, @Validated PostPageReqVO reqVO) throws IOException {
reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<PostDO> list = postService.getPostPage(reqVO).getList();
// 输出
ExcelUtils.write(response, "岗位数据.xls", "岗位列表", PostRespVO.class,
BeanUtils.toBean(list, PostRespVO.class));
}
}

View File

@@ -0,0 +1,16 @@
package com.njcn.msgpush.module.system.controller.admin.dept.vo.dept;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 部门列表 Request VO")
@Data
public class DeptListReqVO {
@Schema(description = "部门名称,模糊匹配", example = "灿能")
private String name;
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
private Integer status;
}

View File

@@ -0,0 +1,39 @@
package com.njcn.msgpush.module.system.controller.admin.dept.vo.dept;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 部门信息 Response VO")
@Data
public class DeptRespVO {
@Schema(description = "部门编号", example = "1024")
private Long id;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private String name;
@Schema(description = "父部门 ID", example = "1024")
private Long parentId;
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer sort;
@Schema(description = "负责人的用户编号", example = "2048")
private Long leaderUserId;
@Schema(description = "联系电话", example = "15601691000")
private String phone;
@Schema(description = "邮箱", example = "msgpush@iocoder.cn")
private String email;
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,48 @@
package com.njcn.msgpush.module.system.controller.admin.dept.vo.dept;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - 部门创建/修改 Request VO")
@Data
public class DeptSaveReqVO {
@Schema(description = "部门编号", example = "1024")
private Long id;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
@NotBlank(message = "部门名称不能为空")
@Size(max = 30, message = "部门名称长度不能超过 30 个字符")
private String name;
@Schema(description = "父部门 ID", example = "1024")
private Long parentId;
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "显示顺序不能为空")
private Integer sort;
@Schema(description = "负责人的用户编号", example = "2048")
private Long leaderUserId;
@Schema(description = "联系电话", example = "15601691000")
@Size(max = 11, message = "联系电话长度不能超过11个字符")
private String phone;
@Schema(description = "邮箱", example = "msgpush@iocoder.cn")
@Email(message = "邮箱格式不正确")
@Size(max = 50, message = "邮箱长度不能超过 50 个字符")
private String email;
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
}

View File

@@ -0,0 +1,23 @@
package com.njcn.msgpush.module.system.controller.admin.dept.vo.dept;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 部门精简信息 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeptSimpleRespVO {
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private String name;
@Schema(description = "父部门 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long parentId;
}

View File

@@ -0,0 +1,22 @@
package com.njcn.msgpush.module.system.controller.admin.dept.vo.post;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - 岗位分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class PostPageReqVO extends PageParam {
@Schema(description = "岗位编码,模糊匹配", example = "msgpush")
private String code;
@Schema(description = "岗位名称,模糊匹配", example = "灿能")
private String name;
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
private Integer status;
}

View File

@@ -0,0 +1,45 @@
package com.njcn.msgpush.module.system.controller.admin.dept.vo.post;
import com.njcn.msgpush.framework.excel.core.annotations.DictFormat;
import com.njcn.msgpush.framework.excel.core.convert.DictConvert;
import com.njcn.msgpush.module.system.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 岗位信息 Response VO")
@Data
@ExcelIgnoreUnannotated
public class PostRespVO {
@Schema(description = "岗位序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("岗位序号")
private Long id;
@Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小土豆")
@ExcelProperty("岗位名称")
private String name;
@Schema(description = "岗位编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "msgpush")
@ExcelProperty("岗位编码")
private String code;
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("岗位排序")
private Integer sort;
@Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.COMMON_STATUS)
private Integer status;
@Schema(description = "备注", example = "快乐的备注")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,39 @@
package com.njcn.msgpush.module.system.controller.admin.dept.vo.post;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - 岗位创建/修改 Request VO")
@Data
public class PostSaveReqVO {
@Schema(description = "岗位编号", example = "1024")
private Long id;
@Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小土豆")
@NotBlank(message = "岗位名称不能为空")
@Size(max = 50, message = "岗位名称长度不能超过 50 个字符")
private String name;
@Schema(description = "岗位编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "msgpush")
@NotBlank(message = "岗位编码不能为空")
@Size(max = 64, message = "岗位编码长度不能超过64个字符")
private String code;
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "显示顺序不能为空")
private Integer sort;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(CommonStatusEnum.class)
private Integer status;
@Schema(description = "备注", example = "快乐的备注")
private String remark;
}

View File

@@ -0,0 +1,19 @@
package com.njcn.msgpush.module.system.controller.admin.dept.vo.post;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 岗位信息的精简 Response VO")
@Data
public class PostSimpleRespVO {
@Schema(description = "岗位序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("岗位序号")
private Long id;
@Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小土豆")
@ExcelProperty("岗位名称")
private String name;
}

View File

@@ -0,0 +1,4 @@
### 请求 /menu/list 接口 => 成功
GET {{baseUrl}}/system/dict-data/list-all-simple
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -0,0 +1,113 @@
package com.njcn.msgpush.module.system.controller.admin.dict;
import com.njcn.msgpush.framework.apilog.core.annotation.ApiAccessLog;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.excel.core.util.ExcelUtils;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.data.DictDataRespVO;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO;
import com.njcn.msgpush.module.system.dal.dataobject.dict.DictDataDO;
import com.njcn.msgpush.module.system.service.dict.DictDataService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.njcn.msgpush.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 字典数据")
@RestController
@RequestMapping("/system/dict-data")
@Validated
public class DictDataController {
@Resource
private DictDataService dictDataService;
@PostMapping("/create")
@Operation(summary = "新增字典数据")
@PreAuthorize("@ss.hasPermission('system:dict:create')")
public CommonResult<Long> createDictData(@Valid @RequestBody DictDataSaveReqVO createReqVO) {
Long dictDataId = dictDataService.createDictData(createReqVO);
return success(dictDataId);
}
@PutMapping("/update")
@Operation(summary = "修改字典数据")
@PreAuthorize("@ss.hasPermission('system:dict:update')")
public CommonResult<Boolean> updateDictData(@Valid @RequestBody DictDataSaveReqVO updateReqVO) {
dictDataService.updateDictData(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除字典数据")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:dict:delete')")
public CommonResult<Boolean> deleteDictData(@RequestParam("id") Long id) {
dictDataService.deleteDictData(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除字典数据")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('system:dict:delete')")
public CommonResult<Boolean> deleteDictDataList(@RequestParam("ids") List<Long> ids) {
dictDataService.deleteDictDataList(ids);
return success(true);
}
@GetMapping(value = {"/list-all-simple", "simple-list"})
@Operation(summary = "获得全部字典数据列表", description = "一般用于管理后台缓存字典数据在本地")
// 无需添加权限认证,因为前端全局都需要
public CommonResult<List<DictDataSimpleRespVO>> getSimpleDictDataList() {
List<DictDataDO> list = dictDataService.getDictDataList(
CommonStatusEnum.ENABLE.getStatus(), null);
return success(BeanUtils.toBean(list, DictDataSimpleRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得字典类型的分页")
@PreAuthorize("@ss.hasPermission('system:dict:query')")
public CommonResult<PageResult<DictDataRespVO>> getDictTypePage(@Valid DictDataPageReqVO pageReqVO) {
PageResult<DictDataDO> pageResult = dictDataService.getDictDataPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, DictDataRespVO.class));
}
@GetMapping(value = "/get")
@Operation(summary = "/查询字典数据详细")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:dict:query')")
public CommonResult<DictDataRespVO> getDictData(@RequestParam("id") Long id) {
DictDataDO dictData = dictDataService.getDictData(id);
return success(BeanUtils.toBean(dictData, DictDataRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出字典数据")
@PreAuthorize("@ss.hasPermission('system:dict:export')")
@ApiAccessLog(operateType = EXPORT)
public void export(HttpServletResponse response, @Valid DictDataPageReqVO exportReqVO) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<DictDataDO> list = dictDataService.getDictDataPage(exportReqVO).getList();
// 输出
ExcelUtils.write(response, "字典数据.xls", "数据", DictDataRespVO.class,
BeanUtils.toBean(list, DictDataRespVO.class));
}
}

View File

@@ -0,0 +1,111 @@
package com.njcn.msgpush.module.system.controller.admin.dict;
import com.njcn.msgpush.framework.apilog.core.annotation.ApiAccessLog;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.excel.core.util.ExcelUtils;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.type.DictTypePageReqVO;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.type.DictTypeRespVO;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.type.DictTypeSimpleRespVO;
import com.njcn.msgpush.module.system.dal.dataobject.dict.DictTypeDO;
import com.njcn.msgpush.module.system.service.dict.DictTypeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.njcn.msgpush.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 字典类型")
@RestController
@RequestMapping("/system/dict-type")
@Validated
public class DictTypeController {
@Resource
private DictTypeService dictTypeService;
@PostMapping("/create")
@Operation(summary = "创建字典类型")
@PreAuthorize("@ss.hasPermission('system:dict:create')")
public CommonResult<Long> createDictType(@Valid @RequestBody DictTypeSaveReqVO createReqVO) {
Long dictTypeId = dictTypeService.createDictType(createReqVO);
return success(dictTypeId);
}
@PutMapping("/update")
@Operation(summary = "修改字典类型")
@PreAuthorize("@ss.hasPermission('system:dict:update')")
public CommonResult<Boolean> updateDictType(@Valid @RequestBody DictTypeSaveReqVO updateReqVO) {
dictTypeService.updateDictType(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除字典类型")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:dict:delete')")
public CommonResult<Boolean> deleteDictType(Long id) {
dictTypeService.deleteDictType(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除字典类型")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('system:dict:delete')")
public CommonResult<Boolean> deleteDictTypeList(@RequestParam("ids") List<Long> ids) {
dictTypeService.deleteDictTypeList(ids);
return success(true);
}
@GetMapping("/page")
@Operation(summary = "获得字典类型的分页列表")
@PreAuthorize("@ss.hasPermission('system:dict:query')")
public CommonResult<PageResult<DictTypeRespVO>> pageDictTypes(@Valid DictTypePageReqVO pageReqVO) {
PageResult<DictTypeDO> pageResult = dictTypeService.getDictTypePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, DictTypeRespVO.class));
}
@Operation(summary = "/查询字典类型详细")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@GetMapping(value = "/get")
@PreAuthorize("@ss.hasPermission('system:dict:query')")
public CommonResult<DictTypeRespVO> getDictType(@RequestParam("id") Long id) {
DictTypeDO dictType = dictTypeService.getDictType(id);
return success(BeanUtils.toBean(dictType, DictTypeRespVO.class));
}
@GetMapping(value = {"/list-all-simple", "simple-list"})
@Operation(summary = "获得全部字典类型列表", description = "包括开启 + 禁用的字典类型,主要用于前端的下拉选项")
// 无需添加权限认证,因为前端全局都需要
public CommonResult<List<DictTypeSimpleRespVO>> getSimpleDictTypeList() {
List<DictTypeDO> list = dictTypeService.getDictTypeList();
return success(BeanUtils.toBean(list, DictTypeSimpleRespVO.class));
}
@Operation(summary = "导出数据类型")
@GetMapping("/export-excel")
@PreAuthorize("@ss.hasPermission('system:dict:query')")
@ApiAccessLog(operateType = EXPORT)
public void export(HttpServletResponse response, @Valid DictTypePageReqVO exportReqVO) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<DictTypeDO> list = dictTypeService.getDictTypePage(exportReqVO).getList();
// 导出
ExcelUtils.write(response, "字典类型.xls", "数据", DictTypeRespVO.class,
BeanUtils.toBean(list, DictTypeRespVO.class));
}
}

View File

@@ -0,0 +1,28 @@
package com.njcn.msgpush.module.system.controller.admin.dict.vo.data;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - 字典类型分页列表 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class DictDataPageReqVO extends PageParam {
@Schema(description = "字典标签", example = "灿能")
@Size(max = 100, message = "字典标签长度不能超过100个字符")
private String label;
@Schema(description = "字典类型,模糊匹配", example = "sys_common_sex")
@Size(max = 100, message = "字典类型类型长度不能超过100个字符")
private String dictType;
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
}

View File

@@ -0,0 +1,55 @@
package com.njcn.msgpush.module.system.controller.admin.dict.vo.data;
import com.njcn.msgpush.framework.excel.core.annotations.DictFormat;
import com.njcn.msgpush.framework.excel.core.convert.DictConvert;
import com.njcn.msgpush.module.system.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 字典数据信息 Response VO")
@Data
@ExcelIgnoreUnannotated
public class DictDataRespVO {
@Schema(description = "字典数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("字典编码")
private Long id;
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("字典排序")
private Integer sort;
@Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
@ExcelProperty("字典标签")
private String label;
@Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder")
@ExcelProperty("字典键值")
private String value;
@Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex")
@ExcelProperty("字典类型")
private String dictType;
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.COMMON_STATUS)
private Integer status;
@Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default")
private String colorType;
@Schema(description = "css 样式", example = "btn-visible")
private String cssClass;
@Schema(description = "备注", example = "我是一个角色")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,51 @@
package com.njcn.msgpush.module.system.controller.admin.dict.vo.data;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - 字典数据创建/修改 Request VO")
@Data
public class DictDataSaveReqVO {
@Schema(description = "字典数据编号", example = "1024")
private Long id;
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "显示顺序不能为空")
private Integer sort;
@Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
@NotBlank(message = "字典标签不能为空")
@Size(max = 100, message = "字典标签长度不能超过100个字符")
private String label;
@Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder")
@NotBlank(message = "字典键值不能为空")
@Size(max = 100, message = "字典键值长度不能超过100个字符")
private String value;
@Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex")
@NotBlank(message = "字典类型不能为空")
@Size(max = 100, message = "字典类型长度不能超过100个字符")
private String dictType;
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
@Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default")
private String colorType;
@Schema(description = "css 样式", example = "btn-visible")
private String cssClass;
@Schema(description = "备注", example = "我是一个角色")
private String remark;
}

View File

@@ -0,0 +1,25 @@
package com.njcn.msgpush.module.system.controller.admin.dict.vo.data;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 数据字典精简 Response VO")
@Data
public class DictDataSimpleRespVO {
@Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "gender")
private String dictType;
@Schema(description = "字典键值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String value;
@Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
private String label;
@Schema(description = "颜色类型default、primary、success、info、warning、danger", example = "default")
private String colorType;
@Schema(description = "css 样式", example = "btn-visible")
private String cssClass;
}

View File

@@ -0,0 +1,33 @@
package com.njcn.msgpush.module.system.controller.admin.dict.vo.type;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import static com.njcn.msgpush.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 字典类型分页列表 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class DictTypePageReqVO extends PageParam {
@Schema(description = "字典类型名称,模糊匹配", example = "灿能")
private String name;
@Schema(description = "字典类型,模糊匹配", example = "sys_common_sex")
@Size(max = 100, message = "字典类型类型长度不能超过100个字符")
private String type;
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
private Integer status;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,41 @@
package com.njcn.msgpush.module.system.controller.admin.dict.vo.type;
import com.njcn.msgpush.framework.excel.core.annotations.DictFormat;
import com.njcn.msgpush.framework.excel.core.convert.DictConvert;
import com.njcn.msgpush.module.system.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 字典类型信息 Response VO")
@Data
@ExcelIgnoreUnannotated
public class DictTypeRespVO {
@Schema(description = "字典类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("字典主键")
private Long id;
@Schema(description = "字典名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "性别")
@ExcelProperty("字典名称")
private String name;
@Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex")
@ExcelProperty("字典类型")
private String type;
@Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.COMMON_STATUS)
private Integer status;
@Schema(description = "备注", example = "快乐的备注")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,34 @@
package com.njcn.msgpush.module.system.controller.admin.dict.vo.type;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@Schema(description = "管理后台 - 字典类型创建/修改 Request VO")
@Data
public class DictTypeSaveReqVO {
@Schema(description = "字典类型编号", example = "1024")
private Long id;
@Schema(description = "字典名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "性别")
@NotBlank(message = "字典名称不能为空")
@Size(max = 100, message = "字典类型名称长度不能超过100个字符")
private String name;
@Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex")
@NotNull(message = "字典类型不能为空")
@Size(max = 100, message = "字典类型类型长度不能超过 100 个字符")
private String type;
@Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "备注", example = "快乐的备注")
private String remark;
}

View File

@@ -0,0 +1,19 @@
package com.njcn.msgpush.module.system.controller.admin.dict.vo.type;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 字典类型精简信息 Response VO")
@Data
public class DictTypeSimpleRespVO {
@Schema(description = "字典类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "字典类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private String name;
@Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex")
private String type;
}

View File

@@ -0,0 +1,5 @@
### 获得地区树
GET {{baseUrl}}/system/area/tree
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -0,0 +1,50 @@
package com.njcn.msgpush.module.system.controller.admin.ip;
import cn.hutool.core.lang.Assert;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.ip.core.Area;
import com.njcn.msgpush.framework.ip.core.utils.AreaUtils;
import com.njcn.msgpush.framework.ip.core.utils.IPUtils;
import com.njcn.msgpush.module.system.controller.admin.ip.vo.AreaNodeRespVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 地区")
@RestController
@RequestMapping("/system/area")
@Validated
public class AreaController {
@GetMapping("/tree")
@Operation(summary = "获得地区树")
public CommonResult<List<AreaNodeRespVO>> getAreaTree() {
Area area = AreaUtils.getArea(Area.ID_CHINA);
Assert.notNull(area, "获取不到中国");
return success(BeanUtils.toBean(area.getChildren(), AreaNodeRespVO.class));
}
@GetMapping("/get-by-ip")
@Operation(summary = "获得 IP 对应的地区名")
@Parameter(name = "ip", description = "IP", required = true)
public CommonResult<String> getAreaByIp(@RequestParam("ip") String ip) {
// 获得城市
Area area = IPUtils.getArea(ip);
if (area == null) {
return success("未知");
}
// 格式化返回
return success(AreaUtils.format(area.getId()));
}
}

View File

@@ -0,0 +1,23 @@
package com.njcn.msgpush.module.system.controller.admin.ip.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 地区节点 Response VO")
@Data
public class AreaNodeRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "110000")
private Integer id;
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "北京")
private String name;
/**
* 子节点
*/
private List<AreaNodeRespVO> children;
}

View File

@@ -0,0 +1,67 @@
package com.njcn.msgpush.module.system.controller.admin.logger;
import com.njcn.msgpush.framework.apilog.core.annotation.ApiAccessLog;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.excel.core.util.ExcelUtils;
import com.njcn.msgpush.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO;
import com.njcn.msgpush.module.system.controller.admin.logger.vo.loginlog.LoginLogRespVO;
import com.njcn.msgpush.module.system.dal.dataobject.logger.LoginLogDO;
import com.njcn.msgpush.module.system.service.logger.LoginLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List;
import static com.njcn.msgpush.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 登录日志")
@RestController
@RequestMapping("/system/login-log")
@Validated
public class LoginLogController {
@Resource
private LoginLogService loginLogService;
@GetMapping("/get")
@Operation(summary = "获得登录日志")
@PreAuthorize("@ss.hasPermission('system:login-log:query')")
public CommonResult<LoginLogRespVO> getLoginLog(Long id) {
LoginLogDO loginLog = loginLogService.getLoginLog(id);
return success(BeanUtils.toBean(loginLog, LoginLogRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得登录日志分页列表")
@PreAuthorize("@ss.hasPermission('system:login-log:query')")
public CommonResult<PageResult<LoginLogRespVO>> getLoginLogPage(@Valid LoginLogPageReqVO pageReqVO) {
PageResult<LoginLogDO> pageResult = loginLogService.getLoginLogPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, LoginLogRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出登录日志 Excel")
@PreAuthorize("@ss.hasPermission('system:login-log:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportLoginLog(HttpServletResponse response, @Valid LoginLogPageReqVO exportReqVO) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<LoginLogDO> list = loginLogService.getLoginLogPage(exportReqVO).getList();
// 输出
ExcelUtils.write(response, "登录日志.xls", "数据列表", LoginLogRespVO.class,
BeanUtils.toBean(list, LoginLogRespVO.class));
}
}

View File

@@ -0,0 +1,4 @@
### 请求 /system/operate-log/page 接口 => 成功
GET {{baseUrl}}/system/operate-log/page
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -0,0 +1,73 @@
package com.njcn.msgpush.module.system.controller.admin.logger;
import com.njcn.msgpush.framework.apilog.core.annotation.ApiAccessLog;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.excel.core.util.ExcelUtils;
import com.njcn.msgpush.framework.translate.core.TranslateUtils;
import com.njcn.msgpush.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
import com.njcn.msgpush.module.system.controller.admin.logger.vo.operatelog.OperateLogRespVO;
import com.njcn.msgpush.module.system.dal.dataobject.logger.OperateLogDO;
import com.njcn.msgpush.module.system.service.logger.OperateLogService;
import com.fhs.core.trans.anno.TransMethodResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List;
import static com.njcn.msgpush.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 操作日志")
@RestController
@RequestMapping("/system/operate-log")
@Validated
public class OperateLogController {
@Resource
private OperateLogService operateLogService;
@GetMapping("/get")
@Operation(summary = "查看操作日志")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:operate-log:query')")
public CommonResult<OperateLogRespVO> getOperateLog(@RequestParam("id") Long id) {
OperateLogDO operateLog = operateLogService.getOperateLog(id);
return success(BeanUtils.toBean(operateLog, OperateLogRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "查看操作日志分页列表")
@PreAuthorize("@ss.hasPermission('system:operate-log:query')")
@TransMethodResult
public CommonResult<PageResult<OperateLogRespVO>> pageOperateLog(@Valid OperateLogPageReqVO pageReqVO) {
PageResult<OperateLogDO> pageResult = operateLogService.getOperateLogPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, OperateLogRespVO.class));
}
@Operation(summary = "导出操作日志")
@GetMapping("/export-excel")
@PreAuthorize("@ss.hasPermission('system:operate-log:export')")
@TransMethodResult
@ApiAccessLog(operateType = EXPORT)
public void exportOperateLog(HttpServletResponse response, @Valid OperateLogPageReqVO exportReqVO) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<OperateLogDO> list = operateLogService.getOperateLogPage(exportReqVO).getList();
ExcelUtils.write(response, "操作日志.xls", "数据列表", OperateLogRespVO.class,
TranslateUtils.translate(BeanUtils.toBean(list, OperateLogRespVO.class)));
}
}

View File

@@ -0,0 +1,31 @@
package com.njcn.msgpush.module.system.controller.admin.logger.vo.loginlog;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.njcn.msgpush.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 登录日志分页列表 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class LoginLogPageReqVO extends PageParam {
@Schema(description = "用户 IP模拟匹配", example = "127.0.0.1")
private String userIp;
@Schema(description = "用户账号,模拟匹配", example = "灿能")
private String username;
@Schema(description = "操作状态", example = "true")
private Boolean status;
@Schema(description = "登录时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,57 @@
package com.njcn.msgpush.module.system.controller.admin.logger.vo.loginlog;
import com.njcn.msgpush.framework.excel.core.annotations.DictFormat;
import com.njcn.msgpush.framework.excel.core.convert.DictConvert;
import com.njcn.msgpush.module.system.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 登录日志 Response VO")
@Data
@ExcelIgnoreUnannotated
public class LoginLogRespVO {
@Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("日志主键")
private Long id;
@Schema(description = "日志类型,参见 LoginLogTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "日志类型", converter = DictConvert.class)
@DictFormat(DictTypeConstants.LOGIN_TYPE)
private Integer logType;
@Schema(description = "用户编号", example = "666")
private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer userType;
@Schema(description = "链路追踪编号", example = "89aca178-a370-411c-ae02-3f0d672be4ab")
private String traceId;
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "msgpush")
@ExcelProperty("用户账号")
private String username;
@Schema(description = "登录结果,参见 LoginResultEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "登录结果", converter = DictConvert.class)
@DictFormat(DictTypeConstants.LOGIN_RESULT)
private Integer result;
@Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
@ExcelProperty("登录 IP")
private String userIp;
@Schema(description = "浏览器 UserAgent", example = "Mozilla/5.0")
@ExcelProperty("浏览器 UA")
private String userAgent;
@Schema(description = "登录时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("登录时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,35 @@
package com.njcn.msgpush.module.system.controller.admin.logger.vo.operatelog;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.njcn.msgpush.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 操作日志分页列表 Request VO")
@Data
public class OperateLogPageReqVO extends PageParam {
@Schema(description = "用户编号", example = "灿能")
private Long userId;
@Schema(description = "操作模块业务编号", example = "1")
private Long bizId;
@Schema(description = "操作模块,模拟匹配", example = "订单")
private String type;
@Schema(description = "操作名,模拟匹配", example = "创建订单")
private String subType;
@Schema(description = "操作明细,模拟匹配", example = "修改编号为 1 的用户信息")
private String action;
@Schema(description = "开始时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,75 @@
package com.njcn.msgpush.module.system.controller.admin.logger.vo.operatelog;
import com.njcn.msgpush.framework.excel.core.annotations.DictFormat;
import com.njcn.msgpush.module.system.dal.dataobject.user.AdminUserDO;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import com.njcn.msgpush.module.system.enums.DictTypeConstants;
import com.fhs.core.trans.anno.Trans;
import com.fhs.core.trans.constant.TransType;
import com.fhs.core.trans.vo.VO;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 操作日志 Response VO")
@Data
@ExcelIgnoreUnannotated
public class OperateLogRespVO implements VO {
@Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("日志编号")
private Long id;
@Schema(description = "链路追踪编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "89aca178-a370-411c-ae02-3f0d672be4ab")
private String traceId;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@Trans(type = TransType.SIMPLE, target = AdminUserDO.class, fields = "nickname", ref = "userName")
private Long userId;
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@ExcelProperty("操作人")
private String userName;
@Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1", implementation = Integer.class)
@ExcelProperty("用户类型")
@DictFormat(DictTypeConstants.USER_TYPE)
private Integer userType;
@Schema(description = "操作模块类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单")
@ExcelProperty("操作模块类型")
private String type;
@Schema(description = "操作名", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建订单")
@ExcelProperty("操作名")
private String subType;
@Schema(description = "操作模块业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("操作模块业务编号")
private Long bizId;
@Schema(description = "操作明细", example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从灿能改成源码。")
private String action;
@Schema(description = "拓展字段", example = "{'orderId': 1}")
private String extra;
@Schema(description = "请求方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "GET")
@NotEmpty(message = "请求方法名不能为空")
private String requestMethod;
@Schema(description = "请求地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "/xxx/yyy")
private String requestUrl;
@Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
private String userIp;
@Schema(description = "浏览器 UserAgent", requiredMode = Schema.RequiredMode.REQUIRED, example = "Mozilla/5.0")
private String userAgent;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,102 @@
package com.njcn.msgpush.module.system.controller.admin.notice;
import cn.hutool.core.lang.Assert;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.infra.api.websocket.WebSocketSenderApi;
import com.njcn.msgpush.module.system.controller.admin.notice.vo.NoticePageReqVO;
import com.njcn.msgpush.module.system.controller.admin.notice.vo.NoticeRespVO;
import com.njcn.msgpush.module.system.controller.admin.notice.vo.NoticeSaveReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.notice.NoticeDO;
import com.njcn.msgpush.module.system.service.notice.NoticeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 通知公告")
@RestController
@RequestMapping("/system/notice")
@Validated
public class NoticeController {
@Resource
private NoticeService noticeService;
@Resource
private WebSocketSenderApi webSocketSenderApi;
@PostMapping("/create")
@Operation(summary = "创建通知公告")
@PreAuthorize("@ss.hasPermission('system:notice:create')")
public CommonResult<Long> createNotice(@Valid @RequestBody NoticeSaveReqVO createReqVO) {
Long noticeId = noticeService.createNotice(createReqVO);
return success(noticeId);
}
@PutMapping("/update")
@Operation(summary = "修改通知公告")
@PreAuthorize("@ss.hasPermission('system:notice:update')")
public CommonResult<Boolean> updateNotice(@Valid @RequestBody NoticeSaveReqVO updateReqVO) {
noticeService.updateNotice(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除通知公告")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:notice:delete')")
public CommonResult<Boolean> deleteNotice(@RequestParam("id") Long id) {
noticeService.deleteNotice(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除通知公告")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('system:notice:delete')")
public CommonResult<Boolean> deleteNoticeList(@RequestParam("ids") List<Long> ids) {
noticeService.deleteNoticeList(ids);
return success(true);
}
@GetMapping("/page")
@Operation(summary = "获取通知公告列表")
@PreAuthorize("@ss.hasPermission('system:notice:query')")
public CommonResult<PageResult<NoticeRespVO>> getNoticePage(@Validated NoticePageReqVO pageReqVO) {
PageResult<NoticeDO> pageResult = noticeService.getNoticePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, NoticeRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得通知公告")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:notice:query')")
public CommonResult<NoticeRespVO> getNotice(@RequestParam("id") Long id) {
NoticeDO notice = noticeService.getNotice(id);
return success(BeanUtils.toBean(notice, NoticeRespVO.class));
}
@PostMapping("/push")
@Operation(summary = "推送通知公告", description = "只发送给 websocket 连接在线的用户")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:notice:update')")
public CommonResult<Boolean> push(@RequestParam("id") Long id) {
NoticeDO notice = noticeService.getNotice(id);
Assert.notNull(notice, "公告不能为空");
// 通过 websocket 推送给在线的用户
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), "notice-push", notice);
return success(true);
}
}

View File

@@ -0,0 +1,19 @@
package com.njcn.msgpush.module.system.controller.admin.notice.vo;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - 通知公告分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class NoticePageReqVO extends PageParam {
@Schema(description = "通知公告名称,模糊匹配", example = "灿能")
private String title;
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
private Integer status;
}

View File

@@ -0,0 +1,30 @@
package com.njcn.msgpush.module.system.controller.admin.notice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 通知公告信息 Response VO")
@Data
public class NoticeRespVO {
@Schema(description = "通知公告序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主")
private String title;
@Schema(description = "公告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主")
private Integer type;
@Schema(description = "公告内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "半生编码")
private String content;
@Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,32 @@
package com.njcn.msgpush.module.system.controller.admin.notice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@Schema(description = "管理后台 - 通知公告创建/修改 Request VO")
@Data
public class NoticeSaveReqVO {
@Schema(description = "岗位公告编号", example = "1024")
private Long id;
@Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主")
@NotBlank(message = "公告标题不能为空")
@Size(max = 50, message = "公告标题不能超过50个字符")
private String title;
@Schema(description = "公告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主")
@NotNull(message = "公告类型不能为空")
private Integer type;
@Schema(description = "公告内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "半生编码")
private String content;
@Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
}

View File

@@ -0,0 +1,98 @@
package com.njcn.msgpush.module.system.controller.admin.notify;
import com.njcn.msgpush.framework.apilog.core.annotation.ApiAccessLog;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.message.NotifyMessageRespVO;
import com.njcn.msgpush.module.system.dal.dataobject.notify.NotifyMessageDO;
import com.njcn.msgpush.module.system.service.notify.NotifyMessageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
import static com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 我的站内信")
@RestController
@RequestMapping("/system/notify-message")
@Validated
public class NotifyMessageController {
@Resource
private NotifyMessageService notifyMessageService;
// ========== 管理所有的站内信 ==========
@GetMapping("/get")
@Operation(summary = "获得站内信")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:notify-message:query')")
public CommonResult<NotifyMessageRespVO> getNotifyMessage(@RequestParam("id") Long id) {
NotifyMessageDO message = notifyMessageService.getNotifyMessage(id);
return success(BeanUtils.toBean(message, NotifyMessageRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得站内信分页")
@PreAuthorize("@ss.hasPermission('system:notify-message:query')")
public CommonResult<PageResult<NotifyMessageRespVO>> getNotifyMessagePage(@Valid NotifyMessagePageReqVO pageVO) {
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getNotifyMessagePage(pageVO);
return success(BeanUtils.toBean(pageResult, NotifyMessageRespVO.class));
}
// ========== 查看自己的站内信 ==========
@GetMapping("/my-page")
@Operation(summary = "获得我的站内信分页")
public CommonResult<PageResult<NotifyMessageRespVO>> getMyMyNotifyMessagePage(@Valid NotifyMessageMyPageReqVO pageVO) {
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getMyMyNotifyMessagePage(pageVO,
getLoginUserId(), UserTypeEnum.ADMIN.getValue());
return success(BeanUtils.toBean(pageResult, NotifyMessageRespVO.class));
}
@PutMapping("/update-read")
@Operation(summary = "标记站内信为已读")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
public CommonResult<Boolean> updateNotifyMessageRead(@RequestParam("ids") List<Long> ids) {
notifyMessageService.updateNotifyMessageRead(ids, getLoginUserId(), UserTypeEnum.ADMIN.getValue());
return success(Boolean.TRUE);
}
@PutMapping("/update-all-read")
@Operation(summary = "标记所有站内信为已读")
public CommonResult<Boolean> updateAllNotifyMessageRead() {
notifyMessageService.updateAllNotifyMessageRead(getLoginUserId(), UserTypeEnum.ADMIN.getValue());
return success(Boolean.TRUE);
}
@GetMapping("/get-unread-list")
@Operation(summary = "获取当前用户的最新站内信列表,默认 10 条")
@Parameter(name = "size", description = "10")
public CommonResult<List<NotifyMessageRespVO>> getUnreadNotifyMessageList(
@RequestParam(name = "size", defaultValue = "10") Integer size) {
List<NotifyMessageDO> list = notifyMessageService.getUnreadNotifyMessageList(
getLoginUserId(), UserTypeEnum.ADMIN.getValue(), size);
return success(BeanUtils.toBean(list, NotifyMessageRespVO.class));
}
@GetMapping("/get-unread-count")
@Operation(summary = "获得当前用户的未读站内信数量")
@ApiAccessLog(enable = false) // 由于前端会不断轮询该接口,记录日志没有意义
public CommonResult<Long> getUnreadNotifyMessageCount() {
return success(notifyMessageService.getUnreadNotifyMessageCount(
getLoginUserId(), UserTypeEnum.ADMIN.getValue()));
}
}

View File

@@ -0,0 +1,101 @@
package com.njcn.msgpush.module.system.controller.admin.notify;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.template.NotifyTemplateRespVO;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.template.NotifyTemplateSaveReqVO;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.template.NotifyTemplateSendReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.notify.NotifyTemplateDO;
import com.njcn.msgpush.module.system.service.notify.NotifySendService;
import com.njcn.msgpush.module.system.service.notify.NotifyTemplateService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 站内信模版")
@RestController
@RequestMapping("/system/notify-template")
@Validated
public class NotifyTemplateController {
@Resource
private NotifyTemplateService notifyTemplateService;
@Resource
private NotifySendService notifySendService;
@PostMapping("/create")
@Operation(summary = "创建站内信模版")
@PreAuthorize("@ss.hasPermission('system:notify-template:create')")
public CommonResult<Long> createNotifyTemplate(@Valid @RequestBody NotifyTemplateSaveReqVO createReqVO) {
return success(notifyTemplateService.createNotifyTemplate(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新站内信模版")
@PreAuthorize("@ss.hasPermission('system:notify-template:update')")
public CommonResult<Boolean> updateNotifyTemplate(@Valid @RequestBody NotifyTemplateSaveReqVO updateReqVO) {
notifyTemplateService.updateNotifyTemplate(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除站内信模版")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('system:notify-template:delete')")
public CommonResult<Boolean> deleteNotifyTemplate(@RequestParam("id") Long id) {
notifyTemplateService.deleteNotifyTemplate(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除站内信模版")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('system:notify-template:delete')")
public CommonResult<Boolean> deleteNotifyTemplateList(@RequestParam("ids") List<Long> ids) {
notifyTemplateService.deleteNotifyTemplateList(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得站内信模版")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:notify-template:query')")
public CommonResult<NotifyTemplateRespVO> getNotifyTemplate(@RequestParam("id") Long id) {
NotifyTemplateDO template = notifyTemplateService.getNotifyTemplate(id);
return success(BeanUtils.toBean(template, NotifyTemplateRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得站内信模版分页")
@PreAuthorize("@ss.hasPermission('system:notify-template:query')")
public CommonResult<PageResult<NotifyTemplateRespVO>> getNotifyTemplatePage(@Valid NotifyTemplatePageReqVO pageVO) {
PageResult<NotifyTemplateDO> pageResult = notifyTemplateService.getNotifyTemplatePage(pageVO);
return success(BeanUtils.toBean(pageResult, NotifyTemplateRespVO.class));
}
@PostMapping("/send-notify")
@Operation(summary = "发送站内信")
@PreAuthorize("@ss.hasPermission('system:notify-template:send-notify')")
public CommonResult<Long> sendNotify(@Valid @RequestBody NotifyTemplateSendReqVO sendReqVO) {
if (UserTypeEnum.MEMBER.getValue().equals(sendReqVO.getUserType())) {
return success(notifySendService.sendSingleNotifyToMember(sendReqVO.getUserId(),
sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
} else {
return success(notifySendService.sendSingleNotifyToAdmin(sendReqVO.getUserId(),
sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
}
}
}

View File

@@ -0,0 +1,27 @@
package com.njcn.msgpush.module.system.controller.admin.notify.vo.message;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.njcn.msgpush.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 站内信分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyMessageMyPageReqVO extends PageParam {
@Schema(description = "是否已读", example = "true")
private Boolean readStatus;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,36 @@
package com.njcn.msgpush.module.system.controller.admin.notify.vo.message;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.njcn.msgpush.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 站内信分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyMessagePageReqVO extends PageParam {
@Schema(description = "用户编号", example = "25025")
private Long userId;
@Schema(description = "用户类型", example = "1")
private Integer userType;
@Schema(description = "模板编码", example = "test_01")
private String templateCode;
@Schema(description = "模版类型", example = "2")
private Integer templateType;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,49 @@
package com.njcn.msgpush.module.system.controller.admin.notify.vo.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Map;
@Schema(description = "管理后台 - 站内信 Response VO")
@Data
public class NotifyMessageRespVO {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25025")
private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Byte userType;
@Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13013")
private Long templateId;
@Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01")
private String templateCode;
@Schema(description = "模版发送人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
private String templateNickname;
@Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试内容")
private String templateContent;
@Schema(description = "模版类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer templateType;
@Schema(description = "模版参数", requiredMode = Schema.RequiredMode.REQUIRED)
private Map<String, Object> templateParams;
@Schema(description = "是否已读", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean readStatus;
@Schema(description = "阅读时间")
private LocalDateTime readTime;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,33 @@
package com.njcn.msgpush.module.system.controller.admin.notify.vo.template;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.njcn.msgpush.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 站内信模版分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyTemplatePageReqVO extends PageParam {
@Schema(description = "模版编码", example = "test_01")
private String code;
@Schema(description = "模版名称", example = "我是名称")
private String name;
@Schema(description = "状态,参见 CommonStatusEnum 枚举类", example = "1")
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

Some files were not shown because too many files have changed in this diff Show More