1. 统一包名修改

This commit is contained in:
chenbowen
2025-09-22 11:55:27 +08:00
parent a001fc8f16
commit 0d46897482
2739 changed files with 512 additions and 512 deletions

View File

@@ -0,0 +1,19 @@
## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre
## 创建目录,并使用它作为工作目录
RUN mkdir -p /cloud-module-system-server
WORKDIR /cloud-module-system-server
## 将后端项目的 Jar 文件,复制到镜像中
COPY ./target/cloud-module-system-server.jar app.jar
## 设置 TZ 时区
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m"
## 暴露后端项目的 48080 端口
EXPOSE 48081
## 启动后端项目
CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar

View File

@@ -0,0 +1,193 @@
<?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.zt.plat</groupId>
<artifactId>zt-module-system</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>zt-module-system-server</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
system 模块下,我们放通用业务,支撑上层的核心业务。
例如说:用户、部门、权限、数据字典等等
</description>
<dependencies>
<!-- Spring Cloud 基础 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-env</artifactId>
</dependency>
<!-- 依赖服务 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-biz-data-permission</artifactId>
</dependency>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-biz-ip</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-security</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-redis</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-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>
<!-- Job 定时任务相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-job</artifactId>
</dependency>
<!-- 消息队列相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-mq</artifactId>
</dependency>
<!-- 服务保障相关 TODO 芋艿:暂时去掉 -->
<!-- <dependency>-->
<!-- <groupId>com.zt.plat</groupId>-->
<!-- <artifactId>zt-spring-boot-starter-protection</artifactId>-->
<!-- </dependency>-->
<!-- Test 测试相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-monitor</artifactId>
</dependency>
<!-- 三方云服务相关 -->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) -->
</dependency>
<dependency>
<groupId>com.xkcoding.justauth</groupId>
<artifactId>justauth-spring-boot-starter</artifactId>
<exclusions>
<!-- 移除,避免和项目里的 hutool-all 冲突 -->
<exclusion>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId> <!-- 微信登录(公众号) -->
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId> <!-- 微信登录(小程序) -->
</dependency>
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码,一般用于登录使用 -->
</dependency>
<dependency>
<groupId>org.dromara.hutool</groupId>
<artifactId>hutool-extra</artifactId> <!-- 邮件 -->
</dependency>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-spring-boot-starter-mq</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,30 @@
package com.zt.plat.module.system;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 项目的启动类
*
* 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
* 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
* 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
*
* @author ZT
*/
@SpringBootApplication
public class SystemServerApplication {
public static void main(String[] args) {
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
// 如果你碰到 启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
SpringApplication.run(SystemServerApplication.class, args);
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章
}
}

View File

@@ -0,0 +1,110 @@
package com.zt.plat.module.system.api.dept;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.CompanyDeptInfo;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.api.dept.dto.*;
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
import com.zt.plat.module.system.service.dept.DeptService;
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 java.util.Set;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class DeptApiImpl implements DeptApi {
@Resource
private DeptService deptService;
@Override
public CommonResult<Long> createDept(DeptSaveReqDTO createReqVO) {
DeptSaveReqVO reqVO = BeanUtils.toBean(createReqVO, DeptSaveReqVO.class);
Long deptId = deptService.createDept(reqVO);
return success(deptId);
}
@Override
public CommonResult<Boolean> updateDept(DeptSaveReqDTO updateReqVO) {
DeptSaveReqVO reqVO = BeanUtils.toBean(updateReqVO, DeptSaveReqVO.class);
deptService.updateDept(reqVO);
return success(true);
}
@Override
public CommonResult<Boolean> deleteDept(Long id) {
deptService.deleteDept(id);
return success(true);
}
@Override
public CommonResult<List<DeptDetailRespDTO>> getDeptList(DeptListReqDTO reqVO) {
DeptListReqVO listReqVO = BeanUtils.toBean(reqVO, DeptListReqVO.class);
List<DeptDO> depts = deptService.getDeptList(listReqVO);
return success(BeanUtils.toBean(depts, DeptDetailRespDTO.class));
}
@Override
public CommonResult<List<DeptSimpleRespDTO>> getSimpleDeptList() {
List<DeptDO> depts = deptService.getDeptList(
new DeptListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
return success(BeanUtils.toBean(depts, DeptSimpleRespDTO.class));
}
@Override
public CommonResult<List<DeptSimpleRespDTO>> getSimpleCompanyList() {
List<DeptDO> companies = deptService.getUserCompanyList();
return success(BeanUtils.toBean(companies, DeptSimpleRespDTO.class));
}
@Override
public CommonResult<List<DeptSimpleRespDTO>> getAllCompanyList() {
List<DeptDO> allCompanies = deptService.getAllCompanyList();
// 还需要把用户归属的公司加上
List<DeptDO> userCompanyList = deptService.getUserCompanyList();
allCompanies.addAll(userCompanyList);
// 去重
List<DeptDO> companies = allCompanies.stream().distinct().toList();
return success(BeanUtils.toBean(companies, DeptSimpleRespDTO.class));
}
@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));
}
@Override
public CommonResult<Set<CompanyDeptInfoRespDTO>> getCompanyDeptInfoListByUserId(Long userId) {
Set<CompanyDeptInfo> companyDeptInfos = deptService.getCompanyDeptInfoListByUserId(userId);
return success(BeanUtils.toBean(companyDeptInfos, CompanyDeptInfoRespDTO.class));
}
}

View File

@@ -0,0 +1,75 @@
package com.zt.plat.module.system.api.dept;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.api.dept.dto.PostRespDTO;
import com.zt.plat.module.system.api.dept.dto.PostSaveReqDTO;
import com.zt.plat.module.system.api.dept.dto.PostSimpleRespDTO;
import com.zt.plat.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.dept.PostDO;
import com.zt.plat.module.system.service.dept.PostService;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class PostApiImpl implements PostApi {
@Resource
private PostService postService;
@Override
public CommonResult<Long> createPost(PostSaveReqDTO createReqVO) {
PostSaveReqVO reqVO = BeanUtils.toBean(createReqVO, PostSaveReqVO.class);
Long postId = postService.createPost(reqVO);
return success(postId);
}
@Override
public CommonResult<Boolean> updatePost(PostSaveReqDTO updateReqVO) {
PostSaveReqVO reqVO = BeanUtils.toBean(updateReqVO, PostSaveReqVO.class);
postService.updatePost(reqVO);
return success(true);
}
@Override
public CommonResult<Boolean> deletePost(Long id) {
postService.deletePost(id);
return success(true);
}
@Override
public CommonResult<PostRespDTO> getPost(Long id) {
PostDO post = postService.getPost(id);
return success(BeanUtils.toBean(post, PostRespDTO.class));
}
@Override
public CommonResult<List<PostSimpleRespDTO>> getSimplePostList() {
List<PostDO> posts = postService.getPostList(null, Collections.singleton(CommonStatusEnum.ENABLE.getStatus()));
posts.sort(Comparator.comparing(PostDO::getSort));
return success(BeanUtils.toBean(posts, PostSimpleRespDTO.class));
}
@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,76 @@
package com.zt.plat.module.system.api.dict;
import com.zt.plat.framework.common.biz.system.dict.dto.DictDataRespDTO;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.api.dict.dto.DictDataDetailRespDTO;
import com.zt.plat.module.system.api.dict.dto.DictDataSaveReqDTO;
import com.zt.plat.module.system.api.dict.dto.DictDataSimpleRespDTO;
import com.zt.plat.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.dict.DictDataDO;
import com.zt.plat.module.system.service.dict.DictDataService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Primary;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
import java.util.List;
import static com.zt.plat.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<Long> createDictData(DictDataSaveReqDTO createReqVO) {
DictDataSaveReqVO reqVO = BeanUtils.toBean(createReqVO, DictDataSaveReqVO.class);
Long dictDataId = dictDataService.createDictData(reqVO);
return success(dictDataId);
}
@Override
public CommonResult<Boolean> updateDictData(DictDataSaveReqDTO updateReqVO) {
DictDataSaveReqVO reqVO = BeanUtils.toBean(updateReqVO, DictDataSaveReqVO.class);
dictDataService.updateDictData(reqVO);
return success(true);
}
@Override
public CommonResult<Boolean> deleteDictData(Long id) {
dictDataService.deleteDictData(id);
return success(true);
}
@Override
public CommonResult<List<DictDataSimpleRespDTO>> getSimpleDictDataList() {
List<DictDataDO> list = dictDataService.getDictDataList(
CommonStatusEnum.ENABLE.getStatus(), null);
return success(BeanUtils.toBean(list, DictDataSimpleRespDTO.class));
}
@Override
public CommonResult<DictDataDetailRespDTO> getDictData(Long id) {
DictDataDO dictData = dictDataService.getDictData(id);
return success(BeanUtils.toBean(dictData, DictDataDetailRespDTO.class));
}
@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.zt.plat.module.system.api.logger;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.api.logger.dto.LoginLogCreateReqDTO;
import com.zt.plat.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.zt.plat.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.zt.plat.module.system.api.logger;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO;
import com.zt.plat.module.system.api.logger.dto.OperateLogPageReqDTO;
import com.zt.plat.module.system.api.logger.dto.OperateLogRespDTO;
import com.zt.plat.module.system.dal.dataobject.logger.OperateLogDO;
import com.zt.plat.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.zt.plat.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.zt.plat.module.system.api.mail;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.api.mail.dto.MailSendSingleToUserReqDTO;
import com.zt.plat.module.system.service.mail.MailSendService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class MailSendApiImpl implements MailSendApi {
@Resource
private MailSendService mailSendService;
@Override
public CommonResult<Long> sendSingleMailToAdmin(MailSendSingleToUserReqDTO reqDTO) {
return success(mailSendService.sendSingleMailToAdmin(reqDTO.getMail(), reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams()));
}
@Override
public CommonResult<Long> sendSingleMailToMember(MailSendSingleToUserReqDTO reqDTO) {
return success(mailSendService.sendSingleMailToMember(reqDTO.getMail(), reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams()));
}
}

View File

@@ -0,0 +1,32 @@
package com.zt.plat.module.system.api.notify;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import com.zt.plat.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.zt.plat.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.zt.plat.module.system.api.oauth2;
import com.zt.plat.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
import com.zt.plat.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import com.zt.plat.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
import com.zt.plat.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO;
import com.zt.plat.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.zt.plat.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.zt.plat.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,83 @@
package com.zt.plat.module.system.api.permission;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.api.permission.dto.*;
import com.zt.plat.module.system.controller.admin.permission.vo.permission.PermissionAssignRoleDataScopeReqVO;
import com.zt.plat.module.system.controller.admin.permission.vo.permission.PermissionAssignUserRoleReqVO;
import com.zt.plat.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.zt.plat.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>> getRoleMenuList(Long roleId) {
return success(permissionService.getRoleMenuListByRoleId(roleId));
}
@Override
public CommonResult<Boolean> assignRoleMenu(PermissionAssignRoleMenuReqDTO reqVO) {
permissionService.assignRoleMenu(reqVO.getRoleId(), reqVO.getMenuIds());
return success(true);
}
@Override
public CommonResult<Boolean> assignRoleDataScope(PermissionAssignRoleDataScopeReqDTO reqVO) {
PermissionAssignRoleDataScopeReqVO reqVOInternal = BeanUtils.toBean(reqVO, PermissionAssignRoleDataScopeReqVO.class);
permissionService.assignRoleDataScope(reqVOInternal.getRoleId(), reqVOInternal.getDataScope(), reqVOInternal.getDataScopeDeptIds());
return success(true);
}
@Override
public CommonResult<Set<Long>> listAdminRoles(Long userId) {
return success(permissionService.getUserRoleIdListByUserId(userId));
}
@Override
public CommonResult<Boolean> assignUserRole(PermissionAssignUserRoleReqDTO reqVO) {
PermissionAssignUserRoleReqVO reqVOInternal = BeanUtils.toBean(reqVO, PermissionAssignUserRoleReqVO.class);
permissionService.assignUserRole(reqVOInternal.getUserId(), reqVOInternal.getRoleIds());
return success(true);
}
@Override
public CommonResult<Set<Long>> getUserRoleIdListByRoleIds(Collection<Long> roleIds) {
return success(permissionService.getUserRoleIdListByRoleId(roleIds));
}
@Override
public CommonResult<Set<Long>> getUserRoleIdListByUserId(Long userId) {
return success(permissionService.getUserRoleIdListByUserIdFromCache(userId));
}
@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,30 @@
package com.zt.plat.module.system.api.permission;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.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.zt.plat.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);
}
@Override
public CommonResult<Boolean> hasAnySuperAdmin(Collection<Long> roleIds) {
return success(roleService.hasAnySuperAdmin(roleIds));
}
}

View File

@@ -0,0 +1,26 @@
package com.zt.plat.module.system.api.sequence;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.service.sequence.SequenceService;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class SequenceApiImpl implements SequenceApi {
@Resource
private SequenceService sequenceService;
@Override
public CommonResult<String> getNextSequence(String sequenceCode, String circulationValue, List<String> inputStrs) {
String result = sequenceService.getNextSeq(sequenceCode, circulationValue, inputStrs);
return success(result);
}
}

View File

@@ -0,0 +1,40 @@
package com.zt.plat.module.system.api.sms;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
import com.zt.plat.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import com.zt.plat.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
import com.zt.plat.module.system.service.sms.SmsCodeService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class SmsCodeApiImpl implements SmsCodeApi {
@Resource
private SmsCodeService smsCodeService;
@Override
public CommonResult<Boolean> sendSmsCode(SmsCodeSendReqDTO reqDTO) {
smsCodeService.sendSmsCode(reqDTO);
return success(true);
}
@Override
public CommonResult<Boolean> useSmsCode(SmsCodeUseReqDTO reqDTO) {
smsCodeService.useSmsCode(reqDTO);
return success(true);
}
@Override
public CommonResult<Boolean> validateSmsCode(SmsCodeValidateReqDTO reqDTO) {
smsCodeService.validateSmsCode(reqDTO);
return success(true);
}
}

View File

@@ -0,0 +1,32 @@
package com.zt.plat.module.system.api.sms;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
import com.zt.plat.module.system.service.sms.SmsSendService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class SmsSendApiImpl implements SmsSendApi {
@Resource
private SmsSendService smsSendService;
@Override
public CommonResult<Long> sendSingleSmsToAdmin(SmsSendSingleToUserReqDTO reqDTO) {
return success(smsSendService.sendSingleSmsToAdmin(reqDTO.getMobile(), reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams()));
}
@Override
public CommonResult<Long> sendSingleSmsToMember(SmsSendSingleToUserReqDTO reqDTO) {
return success(smsSendService.sendSingleSmsToMember(reqDTO.getMobile(), reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams()));
}
}

View File

@@ -0,0 +1,111 @@
package com.zt.plat.module.system.api.social;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.api.social.dto.*;
import com.zt.plat.module.system.enums.social.SocialTypeEnum;
import com.zt.plat.module.system.service.social.SocialClientService;
import com.zt.plat.module.system.service.social.SocialUserService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.List;
import static cn.hutool.core.collection.CollUtil.findOne;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList;
/**
* 社交应用的 API 实现类
*
* @author ZT
*/
@RestController
@Validated
@Slf4j
public class SocialClientApiImpl implements SocialClientApi {
@Resource
private SocialClientService socialClientService;
@Resource
private SocialUserService socialUserService;
@Override
public CommonResult<String> getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) {
return success(socialClientService.getAuthorizeUrl(socialType, userType, redirectUri));
}
@Override
public CommonResult<SocialWxJsapiSignatureRespDTO> createWxMpJsapiSignature(Integer userType, String url) {
WxJsapiSignature signature = socialClientService.createWxMpJsapiSignature(userType, url);
return success(BeanUtils.toBean(signature, SocialWxJsapiSignatureRespDTO.class));
}
@Override
public CommonResult<SocialWxPhoneNumberInfoRespDTO> getWxMaPhoneNumberInfo(Integer userType, String phoneCode) {
WxMaPhoneNumberInfo info = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode);
return success(BeanUtils.toBean(info, SocialWxPhoneNumberInfoRespDTO.class));
}
@Override
public CommonResult<byte[]> getWxaQrcode(SocialWxQrcodeReqDTO reqVO) {
return success(socialClientService.getWxaQrcode(reqVO));
}
@Override
public CommonResult<List<SocialWxaSubscribeTemplateRespDTO>> getWxaSubscribeTemplateList(Integer userType) {
List<TemplateInfo> list = socialClientService.getSubscribeTemplateList(userType);
return success(convertList(list, item -> BeanUtils.toBean(item, SocialWxaSubscribeTemplateRespDTO.class).setId(item.getPriTmplId())));
}
@Override
public CommonResult<Boolean> sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO) {
// 1.1 获得订阅模版列表
List<TemplateInfo> templateList = socialClientService.getSubscribeTemplateList(reqDTO.getUserType());
if (CollUtil.isEmpty(templateList)) {
log.warn("[sendSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO);
return success(false);
}
// 1.2 获得需要使用的模版
TemplateInfo template = findOne(templateList, item ->
ObjUtil.equal(item.getTitle(), reqDTO.getTemplateTitle()));
if (template == null) {
log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO);
return success(false);
}
// 2. 获得社交用户
SocialUserRespDTO socialUser = socialUserService.getSocialUserByUserId(reqDTO.getUserType(), reqDTO.getUserId(),
SocialTypeEnum.WECHAT_MINI_PROGRAM.getType());
if (StrUtil.isBlankIfStr(socialUser.getOpenid())) {
log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:会员 openid 缺失]", reqDTO);
return success(false);
}
// 3. 发送订阅消息
socialClientService.sendSubscribeMessage(reqDTO, template.getPriTmplId(), socialUser.getOpenid());
return success(true);
}
@Override
public CommonResult<Boolean> uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) {
socialClientService.uploadWxaOrderShippingInfo(userType, reqDTO);
return success(true);
}
@Override
public CommonResult<Boolean> notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) {
socialClientService.notifyWxaOrderConfirmReceive(userType, reqDTO);
return success(true);
}
}

View File

@@ -0,0 +1,44 @@
package com.zt.plat.module.system.api.social;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.api.social.dto.SocialUserBindReqDTO;
import com.zt.plat.module.system.api.social.dto.SocialUserRespDTO;
import com.zt.plat.module.system.api.social.dto.SocialUserUnbindReqDTO;
import com.zt.plat.module.system.service.social.SocialUserService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class SocialUserApiImpl implements SocialUserApi {
@Resource
private SocialUserService socialUserService;
@Override
public CommonResult<String> bindSocialUser(SocialUserBindReqDTO reqDTO) {
return success(socialUserService.bindSocialUser(reqDTO));
}
@Override
public CommonResult<Boolean> unbindSocialUser(SocialUserUnbindReqDTO reqDTO) {
socialUserService.unbindSocialUser(reqDTO.getUserId(), reqDTO.getUserType(),
reqDTO.getSocialType(), reqDTO.getOpenid());
return success(true);
}
@Override
public CommonResult<SocialUserRespDTO> getSocialUserByUserId(Integer userType, Long userId, Integer socialType) {
return success(socialUserService.getSocialUserByUserId(userType, userId, socialType));
}
@Override
public CommonResult<SocialUserRespDTO> getSocialUserByCode(Integer userType, Integer socialType, String code, String state) {
return success(socialUserService.getSocialUserByCode(userType, socialType, code, state));
}
}

View File

@@ -0,0 +1,35 @@
package com.zt.plat.module.system.api.tenant;
import com.zt.plat.framework.common.biz.system.tenant.TenantCommonApi;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
import com.zt.plat.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.zt.plat.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,132 @@
package com.zt.plat.module.system.api.user;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.datapermission.core.util.DataPermissionUtils;
import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO;
import com.zt.plat.module.system.api.user.dto.AdminUserSaveReqDTO;
import com.zt.plat.module.system.api.user.dto.AdminUserUpdatePasswordReqDTO;
import com.zt.plat.module.system.api.user.dto.AdminUserUpdateStatusReqDTO;
import com.zt.plat.module.system.controller.admin.user.vo.user.UserSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
import com.zt.plat.module.system.dal.dataobject.user.AdminUserDO;
import com.zt.plat.module.system.service.dept.DeptService;
import com.zt.plat.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.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.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<Long> createUser(AdminUserSaveReqDTO reqVO) {
UserSaveReqVO createReqVO = BeanUtils.toBean(reqVO, UserSaveReqVO.class);
Long userId = userService.createUser(createReqVO);
return success(userId);
}
@Override
public CommonResult<Boolean> updateUser(AdminUserSaveReqDTO reqVO) {
UserSaveReqVO updateReqVO = BeanUtils.toBean(reqVO, UserSaveReqVO.class);
userService.updateUser(updateReqVO);
return success(true);
}
@Override
public CommonResult<Boolean> deleteUser(Long id) {
userService.deleteUser(id);
return success(true);
}
@Override
public CommonResult<Boolean> updateUserPassword(AdminUserUpdatePasswordReqDTO reqVO) {
userService.updateUserPassword(reqVO.getId(), reqVO.getPassword());
return success(true);
}
@Override
public CommonResult<Boolean> updateUserStatus(AdminUserUpdateStatusReqDTO reqVO) {
userService.updateUserStatus(reqVO.getId(), reqVO.getStatus());
return success(true);
}
@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<>();
List<DeptDO> deptListByLeaderUserId = deptService.getDeptListByLeaderUserId(id);
deptListByLeaderUserId.forEach(dept -> {
if (dept == null) {
return;
}
if (ObjUtil.notEqual(dept.getLeaderUserId(), id)) { // 校验为负责人
return;
}
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) {
return DataPermissionUtils.executeIgnore(() -> { // 禁用数据权限。原因是,一般基于指定 id 的 API 查询,都是数据拼接为主
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,33 @@
### 请求 /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 接口 => 成功(无验证码)
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,182 @@
package com.zt.plat.module.system.controller.admin.auth;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.framework.common.enums.UserTypeEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.security.config.SecurityProperties;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
import com.zt.plat.module.system.controller.admin.auth.vo.*;
import com.zt.plat.module.system.convert.auth.AuthConvert;
import com.zt.plat.module.system.dal.dataobject.permission.MenuDO;
import com.zt.plat.module.system.dal.dataobject.permission.RoleDO;
import com.zt.plat.module.system.dal.dataobject.user.AdminUserDO;
import com.zt.plat.module.system.enums.logger.LoginLogTypeEnum;
import com.zt.plat.module.system.service.auth.AdminAuthService;
import com.zt.plat.module.system.service.permission.MenuService;
import com.zt.plat.module.system.service.permission.PermissionService;
import com.zt.plat.module.system.service.permission.RoleService;
import com.zt.plat.module.system.service.social.SocialClientService;
import com.zt.plat.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.Parameters;
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.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet;
import static com.zt.plat.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 SocialClientService socialClientService;
@Resource
private SecurityProperties securityProperties;
@PostMapping("/login")
@PermitAll
@Operation(summary = "使用账号密码登录")
@TenantIgnore
public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
return success(authService.login(reqVO));
}
@PostMapping("/test-login")
@PermitAll
@Operation(summary = "测试登录(无验证码校验)", description = "仅用于测试环境和本地开发,生产环境不可用")
@TenantIgnore
public CommonResult<AuthLoginRespVO> testLogin(@RequestBody @Valid AuthTestLoginReqVO reqVO) {
return success(authService.testLogin(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));
}
// ========== 短信登录相关 ==========
@PostMapping("/sms-login")
@PermitAll
@Operation(summary = "使用短信验证码登录")
public CommonResult<AuthLoginRespVO> smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) {
return success(authService.smsLogin(reqVO));
}
@PostMapping("/send-sms-code")
@PermitAll
@Operation(summary = "发送手机验证码")
public CommonResult<Boolean> sendLoginSmsCode(@RequestBody @Valid AuthSmsSendReqVO reqVO) {
authService.sendSmsCode(reqVO);
return success(true);
}
@PostMapping("/reset-password")
@PermitAll
@Operation(summary = "重置密码")
public CommonResult<Boolean> resetPassword(@RequestBody @Valid AuthResetPasswordReqVO reqVO) {
authService.resetPassword(reqVO);
return success(true);
}
// ========== 社交登录相关 ==========
@GetMapping("/social-auth-redirect")
@PermitAll
@Operation(summary = "社交授权的跳转")
@Parameters({
@Parameter(name = "type", description = "社交类型", required = true),
@Parameter(name = "redirectUri", description = "回调路径")
})
public CommonResult<String> socialLogin(@RequestParam("type") Integer type,
@RequestParam("redirectUri") String redirectUri) {
return success(socialClientService.getAuthorizeUrl(
type, UserTypeEnum.ADMIN.getValue(), redirectUri));
}
@PostMapping("/social-login")
@PermitAll
@Operation(summary = "社交快捷登录,使用 code 授权码", description = "适合未登录的用户,但是社交账号已绑定用户")
public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) {
return success(authService.socialLogin(reqVO));
}
}

View File

@@ -0,0 +1,56 @@
package com.zt.plat.module.system.controller.admin.auth.vo;
import cn.hutool.core.util.StrUtil;
import com.zt.plat.framework.common.validation.InEnum;
import com.zt.plat.module.system.enums.social.SocialTypeEnum;
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 = "cloudyuanma")
@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 = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(SocialTypeEnum.class)
private Integer socialType;
@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;
@AssertTrue(message = "授权码不能为空")
public boolean isSocialCodeValid() {
return socialType == null || StrUtil.isNotEmpty(socialCode);
}
@AssertTrue(message = "授权 state 不能为空")
public boolean isSocialState() {
return socialType == null || StrUtil.isNotEmpty(socialState);
}
}

View File

@@ -0,0 +1,35 @@
package com.zt.plat.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;
/**
* @author chenbowen
*/
@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;
@Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED)
private Long tenantId;
}

View File

@@ -0,0 +1,53 @@
package com.zt.plat.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 = "ZT")
private Long id;
@Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long parentId;
@Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT")
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.zt.plat.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 = "ZT源码")
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 = "cloud")
private String username;
@Schema(description = "用户邮箱", example = "cloud@iocoder.cn")
private String email;
}
@Schema(description = "管理后台 - 登录用户的菜单信息 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class MenuVO {
@Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT")
private Long id;
@Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long parentId;
@Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT")
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.zt.plat.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 = "cloud")
@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.zt.plat.module.system.controller.admin.auth.vo;
import com.zt.plat.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,27 @@
package com.zt.plat.module.system.controller.admin.auth.vo;
import com.zt.plat.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;
@Schema(description = "管理后台 - 短信验证码的登录 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthSmsLoginReqVO {
@Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloudyuanma")
@NotEmpty(message = "手机号不能为空")
@Mobile
private String mobile;
@Schema(description = "短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "验证码不能为空")
private String code;
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.module.system.controller.admin.auth.vo;
import com.zt.plat.framework.common.validation.InEnum;
import com.zt.plat.framework.common.validation.Mobile;
import com.zt.plat.module.system.enums.sms.SmsSceneEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 发送手机验证码 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthSmsSendReqVO extends CaptchaVerificationReqVO {
@Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloudyuanma")
@NotEmpty(message = "手机号不能为空")
@Mobile
private String mobile;
@Schema(description = "短信场景", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "发送场景不能为空")
@InEnum(SmsSceneEnum.class)
private Integer scene;
}

View File

@@ -0,0 +1,33 @@
package com.zt.plat.module.system.controller.admin.auth.vo;
import com.zt.plat.framework.common.validation.InEnum;
import com.zt.plat.module.system.enums.social.SocialTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 社交绑定登录 Request VO使用 code 授权码 + 账号密码")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthSocialLoginReqVO {
@Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(SocialTypeEnum.class)
@NotNull(message = "社交平台的类型不能为空")
private Integer type;
@Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "授权码不能为空")
private String code;
@Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
@NotEmpty(message = "state 不能为空")
private String state;
}

View File

@@ -0,0 +1,30 @@
package com.zt.plat.module.system.controller.admin.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
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无验证码校验仅测试环境和本地开发可用")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthTestLoginReqVO {
@Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloudyuanma")
@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;
}

View File

@@ -0,0 +1,22 @@
package com.zt.plat.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.zt.plat.module.system.controller.admin.captcha;
import cn.hutool.core.util.StrUtil;
import com.zt.plat.framework.common.util.servlet.ServletUtils;
import com.zt.plat.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,148 @@
package com.zt.plat.module.system.controller.admin.dept;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.CompanyDeptInfo;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
import com.zt.plat.module.system.api.dept.dto.CompanyDeptInfoRespDTO;
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptRespVO;
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSimpleRespVO;
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
import com.zt.plat.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 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 java.util.Set;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
/**
* @author chenbowen
*/
@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')")
@TenantIgnore
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);
}
@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(value = {"/simple-user-dept-list"})
@Operation(summary = "获取当前用户归属部门精简信息列表", description = "只包含当前用户归属的启用部门,主要用于前端的下拉选项")
@Parameter(name = "companyId", description = "公司ID可选参数用于过滤指定公司下的部门", required = false, example = "1")
public CommonResult<List<DeptSimpleRespVO>> getCurrentUserDeptList(@RequestParam(value = "companyId", required = false) Long companyId) {
List<DeptDO> list = deptService.getCurrentUserDeptList(companyId);
return success(BeanUtils.toBean(list, DeptSimpleRespVO.class));
}
@GetMapping(value = {"/list-company-simple", "/simple-company-list"})
@Operation(summary = "获取用户所属公司精简信息列表", description = "只包含被开启的部门,主要用于前端的下拉选项")
public CommonResult<List<DeptSimpleRespVO>> getSimpleCompanyList() {
List<DeptDO> list = deptService.getUserCompanyList();
return success(BeanUtils.toBean(list, DeptSimpleRespVO.class));
}
@GetMapping(value = {"/list-all-company", "/all-company-list"})
@Operation(summary = "获取所有公司精简信息列表", description = "只包含被开启的公司部门,主要用于前端的下拉选项")
@PreAuthorize("@ss.hasPermission('system:dept:query')")
public CommonResult<List<DeptSimpleRespVO>> getAllCompanyList() {
List<DeptDO> list = deptService.getAllCompanyList();
// 还需要把用户归属的公司加上
List<DeptDO> userCompanyList = deptService.getUserCompanyList();
list.addAll(userCompanyList);
// 去重
list = list.stream().distinct().toList();
return success(BeanUtils.toBean(list, DeptSimpleRespVO.class));
}
@GetMapping("/top-level-list")
@Operation(summary = "获取顶级部门列表", description = "用于懒加载,只返回没有父部门的顶级部门")
@PreAuthorize("@ss.hasPermission('system:dept:query')")
public CommonResult<List<DeptRespVO>> getTopLevelDeptList() {
List<DeptDO> list = deptService.getTopLevelDeptList();
return success(BeanUtils.toBean(list, DeptRespVO.class));
}
@GetMapping("/children")
@Operation(summary = "根据父部门ID获取子部门列表", description = "用于懒加载根据父部门ID返回直接子部门")
@Parameter(name = "parentId", description = "父部门ID", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:dept:query')")
public CommonResult<List<DeptRespVO>> getChildrenDeptList(@RequestParam("parentId") Long parentId) {
List<DeptDO> list = deptService.getDirectChildDeptList(parentId);
return success(BeanUtils.toBean(list, DeptRespVO.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));
}
@GetMapping("/company-dept-info")
@Operation(summary = "获得指定用户的公司部门信息")
@Parameter(name = "userId", description = "用户编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('system:dept:query')")
public CommonResult<Set<CompanyDeptInfoRespDTO>> getCompanyDeptInfoListByUserId(@RequestParam("userId") Long userId) {
Set<CompanyDeptInfo> companyDeptInfos = deptService.getCompanyDeptInfoListByUserId(userId);
return success(BeanUtils.toBean(companyDeptInfos, CompanyDeptInfoRespDTO.class));
}
}

View File

@@ -0,0 +1,106 @@
package com.zt.plat.module.system.controller.admin.dept;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.system.controller.admin.dept.vo.post.PostPageReqVO;
import com.zt.plat.module.system.controller.admin.dept.vo.post.PostRespVO;
import com.zt.plat.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
import com.zt.plat.module.system.controller.admin.dept.vo.post.PostSimpleRespVO;
import com.zt.plat.module.system.dal.dataobject.dept.PostDO;
import com.zt.plat.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.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.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);
}
@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")
@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,21 @@
package com.zt.plat.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 = "ZT")
private String name;
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
private Integer status;
@Schema(description = "是否公司", example = "false")
private Boolean isCompany;
@Schema(description = "是否集团", example = "false")
private Boolean isGroup;
}

View File

@@ -0,0 +1,57 @@
package com.zt.plat.module.system.controller.admin.dept.vo.dept;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @author chenbowen
*/
@Schema(description = "管理后台 - 部门信息 Response VO")
@Data
public class DeptRespVO {
@Schema(description = "部门编号", example = "1024")
private Long id;
@Schema(description = "部门编码", example = "DEPT_001")
private String code;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT")
private String name;
@Schema(description = "部门简称", example = "ZT科技")
private String shortName;
@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 = "cloud@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;
@Schema(description = "租户编号", example = "1024")
private Long tenantId;
@Schema(description = "是否公司", example = "false")
private Boolean isCompany;
@Schema(description = "是否集团", example = "false")
private Boolean isGroup;
}

View File

@@ -0,0 +1,70 @@
package com.zt.plat.module.system.controller.admin.dept.vo.dept;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.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;
/**
* @author chenbowen
*/
@Schema(description = "管理后台 - 部门创建/修改 Request VO")
@Data
public class DeptSaveReqVO {
@Schema(description = "部门编号", example = "1024")
private Long id;
@Schema(description = "部门编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "DEPT_001")
@Size(max = 50, message = "部门编码长度不能超过 50 个字符")
private String code;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT")
@NotBlank(message = "部门名称不能为空")
@Size(max = 30, message = "部门名称长度不能超过 30 个字符")
private String name;
@Schema(description = "部门简称", example = "科技")
@Size(max = 20, message = "部门简称长度不能超过 20 个字符")
private String shortName;
@Schema(description = "父部门 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024(顶级部门父级 Id 为 0)")
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 = "cloud@iocoder.cn")
@Email(message = "邮箱格式不正确")
@Size(max = 50, message = "邮箱长度不能超过 50 个字符")
private String email;
@Schema(description = "状态,见 CommonStatusEnum 枚举0 开启 1 关闭", requiredMode = Schema.RequiredMode.REQUIRED, example = "0 开启 1 关闭")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
@Schema(description = "租户编号", example = "1024")
private Long tenantId;
@Schema(description = "是否公司", example = "false")
private Boolean isCompany;
@Schema(description = "是否集团", example = "false")
private Boolean isGroup;
@Schema(description = "部门来源类型", example = "1")
private Integer deptSource;
}

View File

@@ -0,0 +1,35 @@
package com.zt.plat.module.system.controller.admin.dept.vo.dept;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author chenbowen
*/
@Schema(description = "管理后台 - 部门精简信息 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeptSimpleRespVO {
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "部门编码", example = "DEPT_001")
private String code;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT")
private String name;
@Schema(description = "父部门 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long parentId;
@Schema(description = "是否公司", example = "false")
private Boolean isCompany;
@Schema(description = "是否集团", example = "false")
private Boolean isGroup;
}

View File

@@ -0,0 +1,22 @@
package com.zt.plat.module.system.controller.admin.dept.vo.post;
import com.zt.plat.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 = "cloud")
private String code;
@Schema(description = "岗位名称,模糊匹配", example = "ZT")
private String name;
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
private Integer status;
}

View File

@@ -0,0 +1,45 @@
package com.zt.plat.module.system.controller.admin.dept.vo.post;
import com.zt.plat.framework.excel.core.annotations.DictFormat;
import com.zt.plat.framework.excel.core.convert.DictConvert;
import com.zt.plat.module.system.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.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 = "cloud")
@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.zt.plat.module.system.controller.admin.dept.vo.post;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.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 = "cloud")
@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.zt.plat.module.system.controller.admin.dept.vo.post;
import com.alibaba.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,106 @@
package com.zt.plat.module.system.controller.admin.dict;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
import com.zt.plat.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
import com.zt.plat.module.system.controller.admin.dict.vo.data.DictDataRespVO;
import com.zt.plat.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO;
import com.zt.plat.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO;
import com.zt.plat.module.system.dal.dataobject.dict.DictDataDO;
import com.zt.plat.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.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.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(Long id) {
dictDataService.deleteDictData(id);
return success(true);
}
@TenantIgnore
@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")
@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,102 @@
package com.zt.plat.module.system.controller.admin.dict;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.system.controller.admin.dict.vo.type.DictTypePageReqVO;
import com.zt.plat.module.system.controller.admin.dict.vo.type.DictTypeRespVO;
import com.zt.plat.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO;
import com.zt.plat.module.system.controller.admin.dict.vo.type.DictTypeSimpleRespVO;
import com.zt.plat.module.system.dal.dataobject.dict.DictTypeDO;
import com.zt.plat.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.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.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);
}
@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")
@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.zt.plat.module.system.controller.admin.dict.vo.data;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.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 = "ZT")
@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.zt.plat.module.system.controller.admin.dict.vo.data;
import com.zt.plat.framework.excel.core.annotations.DictFormat;
import com.zt.plat.framework.excel.core.convert.DictConvert;
import com.zt.plat.module.system.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.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 = "ZT")
@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.zt.plat.module.system.controller.admin.dict.vo.data;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.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 = "ZT")
@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.zt.plat.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.zt.plat.module.system.controller.admin.dict.vo.type;
import com.zt.plat.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.zt.plat.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 = "ZT")
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.zt.plat.module.system.controller.admin.dict.vo.type;
import com.zt.plat.framework.excel.core.annotations.DictFormat;
import com.zt.plat.framework.excel.core.convert.DictConvert;
import com.zt.plat.module.system.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.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.zt.plat.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.zt.plat.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 = "ZT")
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,58 @@
package com.zt.plat.module.system.controller.admin.ip;
import cn.hutool.core.lang.Assert;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.ip.core.Area;
import com.zt.plat.framework.ip.core.utils.AreaUtils;
import com.zt.plat.framework.ip.core.utils.IPUtils;
import com.zt.plat.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.zt.plat.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()));
}
@GetMapping("/global")
@Operation(summary = "获得国家列表")
public CommonResult<List<AreaNodeRespVO>> getAreaTreeGlobal() {
Area area = AreaUtils.getArea(Area.ID_GLOBAL);
Assert.notNull(area, "获取不到国家信息");
return success(BeanUtils.toBean(area.getChildren(), AreaNodeRespVO.class));
}
}

View File

@@ -0,0 +1,23 @@
package com.zt.plat.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,59 @@
package com.zt.plat.module.system.controller.admin.logger;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO;
import com.zt.plat.module.system.controller.admin.logger.vo.loginlog.LoginLogRespVO;
import com.zt.plat.module.system.dal.dataobject.logger.LoginLogDO;
import com.zt.plat.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.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 登录日志")
@RestController
@RequestMapping("/system/login-log")
@Validated
public class LoginLogController {
@Resource
private LoginLogService loginLogService;
@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")
@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,59 @@
package com.zt.plat.module.system.controller.admin.logger;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.framework.translate.core.TranslateUtils;
import com.zt.plat.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
import com.zt.plat.module.system.controller.admin.logger.vo.operatelog.OperateLogRespVO;
import com.zt.plat.module.system.dal.dataobject.logger.OperateLogDO;
import com.zt.plat.module.system.service.logger.OperateLogService;
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.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 操作日志")
@RestController
@RequestMapping("/system/operate-log")
@Validated
public class OperateLogController {
@Resource
private OperateLogService operateLogService;
@GetMapping("/page")
@Operation(summary = "查看操作日志分页列表")
@PreAuthorize("@ss.hasPermission('system:operate-log:query')")
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")
@PreAuthorize("@ss.hasPermission('system:operate-log:export')")
@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.zt.plat.module.system.controller.admin.logger.vo.loginlog;
import com.zt.plat.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.zt.plat.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 = "ZT")
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.zt.plat.module.system.controller.admin.logger.vo.loginlog;
import com.zt.plat.framework.excel.core.annotations.DictFormat;
import com.zt.plat.framework.excel.core.convert.DictConvert;
import com.zt.plat.module.system.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.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 = "cloud")
@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.zt.plat.module.system.controller.admin.logger.vo.operatelog;
import com.zt.plat.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.zt.plat.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 = "ZT")
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,68 @@
package com.zt.plat.module.system.controller.admin.logger.vo.operatelog;
import com.zt.plat.module.system.dal.dataobject.user.AdminUserDO;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
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 = "订单")
@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 的用户信息将性别从男改成女将姓名从ZT改成源码。")
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,81 @@
package com.zt.plat.module.system.controller.admin.mail;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
import com.zt.plat.module.system.controller.admin.mail.vo.account.MailAccountRespVO;
import com.zt.plat.module.system.controller.admin.mail.vo.account.MailAccountSaveReqVO;
import com.zt.plat.module.system.controller.admin.mail.vo.account.MailAccountSimpleRespVO;
import com.zt.plat.module.system.dal.dataobject.mail.MailAccountDO;
import com.zt.plat.module.system.service.mail.MailAccountService;
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.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.List;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 邮箱账号")
@RestController
@RequestMapping("/system/mail-account")
public class MailAccountController {
@Resource
private MailAccountService mailAccountService;
@PostMapping("/create")
@Operation(summary = "创建邮箱账号")
@PreAuthorize("@ss.hasPermission('system:mail-account:create')")
public CommonResult<Long> createMailAccount(@Valid @RequestBody MailAccountSaveReqVO createReqVO) {
return success(mailAccountService.createMailAccount(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "修改邮箱账号")
@PreAuthorize("@ss.hasPermission('system:mail-account:update')")
public CommonResult<Boolean> updateMailAccount(@Valid @RequestBody MailAccountSaveReqVO updateReqVO) {
mailAccountService.updateMailAccount(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除邮箱账号")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('system:mail-account:delete')")
public CommonResult<Boolean> deleteMailAccount(@RequestParam Long id) {
mailAccountService.deleteMailAccount(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得邮箱账号")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:mail-account:query')")
public CommonResult<MailAccountRespVO> getMailAccount(@RequestParam("id") Long id) {
MailAccountDO account = mailAccountService.getMailAccount(id);
return success(BeanUtils.toBean(account, MailAccountRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得邮箱账号分页")
@PreAuthorize("@ss.hasPermission('system:mail-account:query')")
public CommonResult<PageResult<MailAccountRespVO>> getMailAccountPage(@Valid MailAccountPageReqVO pageReqVO) {
PageResult<MailAccountDO> pageResult = mailAccountService.getMailAccountPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, MailAccountRespVO.class));
}
@GetMapping({"/list-all-simple", "simple-list"})
@Operation(summary = "获得邮箱账号精简列表")
public CommonResult<List<MailAccountSimpleRespVO>> getSimpleMailAccountList() {
List<MailAccountDO> list = mailAccountService.getMailAccountList();
return success(BeanUtils.toBean(list, MailAccountSimpleRespVO.class));
}
}

View File

@@ -0,0 +1,49 @@
package com.zt.plat.module.system.controller.admin.mail;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
import com.zt.plat.module.system.controller.admin.mail.vo.log.MailLogRespVO;
import com.zt.plat.module.system.dal.dataobject.mail.MailLogDO;
import com.zt.plat.module.system.service.mail.MailLogService;
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.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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 邮件日志")
@RestController
@RequestMapping("/system/mail-log")
public class MailLogController {
@Resource
private MailLogService mailLogService;
@GetMapping("/page")
@Operation(summary = "获得邮箱日志分页")
@PreAuthorize("@ss.hasPermission('system:mail-log:query')")
public CommonResult<PageResult<MailLogRespVO>> getMailLogPage(@Valid MailLogPageReqVO pageVO) {
PageResult<MailLogDO> pageResult = mailLogService.getMailLogPage(pageVO);
return success(BeanUtils.toBean(pageResult, MailLogRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得邮箱日志")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:mail-log:query')")
public CommonResult<MailLogRespVO> getMailTemplate(@RequestParam("id") Long id) {
MailLogDO log = mailLogService.getMailLog(id);
return success(BeanUtils.toBean(log, MailLogRespVO.class));
}
}

View File

@@ -0,0 +1,14 @@
### 请求 /system/mail-template/send-mail 接口 => 成功
POST {{baseUrl}}/system/mail-template/send-mail
Authorization: Bearer {{token}}
Content-Type: application/json
tenant-id: {{adminTenantId}}
{
"templateCode": "test_01",
"mail": "7685413@qq.com",
"templateParams": {
"key01": "value01",
"key02": "value02"
}
}

View File

@@ -0,0 +1,89 @@
package com.zt.plat.module.system.controller.admin.mail;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.controller.admin.mail.vo.template.*;
import com.zt.plat.module.system.dal.dataobject.mail.MailTemplateDO;
import com.zt.plat.module.system.service.mail.MailSendService;
import com.zt.plat.module.system.service.mail.MailTemplateService;
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.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.List;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 邮件模版")
@RestController
@RequestMapping("/system/mail-template")
public class MailTemplateController {
@Resource
private MailTemplateService mailTempleService;
@Resource
private MailSendService mailSendService;
@PostMapping("/create")
@Operation(summary = "创建邮件模版")
@PreAuthorize("@ss.hasPermission('system:mail-template:create')")
public CommonResult<Long> createMailTemplate(@Valid @RequestBody MailTemplateSaveReqVO createReqVO){
return success(mailTempleService.createMailTemplate(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "修改邮件模版")
@PreAuthorize("@ss.hasPermission('system:mail-template:update')")
public CommonResult<Boolean> updateMailTemplate(@Valid @RequestBody MailTemplateSaveReqVO updateReqVO){
mailTempleService.updateMailTemplate(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除邮件模版")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:mail-template:delete')")
public CommonResult<Boolean> deleteMailTemplate(@RequestParam("id") Long id) {
mailTempleService.deleteMailTemplate(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得邮件模版")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:mail-template:query')")
public CommonResult<MailTemplateRespVO> getMailTemplate(@RequestParam("id") Long id) {
MailTemplateDO template = mailTempleService.getMailTemplate(id);
return success(BeanUtils.toBean(template, MailTemplateRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得邮件模版分页")
@PreAuthorize("@ss.hasPermission('system:mail-template:query')")
public CommonResult<PageResult<MailTemplateRespVO>> getMailTemplatePage(@Valid MailTemplatePageReqVO pageReqVO) {
PageResult<MailTemplateDO> pageResult = mailTempleService.getMailTemplatePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, MailTemplateRespVO.class));
}
@GetMapping({"/list-all-simple", "simple-list"})
@Operation(summary = "获得邮件模版精简列表")
public CommonResult<List<MailTemplateSimpleRespVO>> getSimpleTemplateList() {
List<MailTemplateDO> list = mailTempleService.getMailTemplateList();
return success(BeanUtils.toBean(list, MailTemplateSimpleRespVO.class));
}
@PostMapping("/send-mail")
@Operation(summary = "发送短信")
@PreAuthorize("@ss.hasPermission('system:mail-template:send-mail')")
public CommonResult<Long> sendMail(@Valid @RequestBody MailTemplateSendReqVO sendReqVO) {
return success(mailSendService.sendSingleMailToAdmin(sendReqVO.getMail(), getLoginUserId(),
sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
}
}

View File

@@ -0,0 +1,21 @@
package com.zt.plat.module.system.controller.admin.mail.vo.account;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 邮箱账号分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MailAccountPageReqVO extends PageParam {
@Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloudyuanma@123.com")
private String mail;
@Schema(description = "用户名" , requiredMode = Schema.RequiredMode.REQUIRED , example = "cloud")
private String username;
}

View File

@@ -0,0 +1,39 @@
package com.zt.plat.module.system.controller.admin.mail.vo.account;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 邮箱账号 Response VO")
@Data
public class MailAccountRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloudyuanma@123.com")
private String mail;
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloud")
private String username;
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
private String password;
@Schema(description = "SMTP 服务器域名", requiredMode = Schema.RequiredMode.REQUIRED, example = "www.iocoder.cn")
private String host;
@Schema(description = "SMTP 服务器端口", requiredMode = Schema.RequiredMode.REQUIRED, example = "80")
private Integer port;
@Schema(description = "是否开启 ssl", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean sslEnable;
@Schema(description = "是否开启 starttls", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean starttlsEnable;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,44 @@
package com.zt.plat.module.system.controller.admin.mail.vo.account;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - 邮箱账号创建/修改 Request VO")
@Data
public class MailAccountSaveReqVO {
@Schema(description = "编号", example = "1024")
private Long id;
@Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloudyuanma@123.com")
@NotNull(message = "邮箱不能为空")
@Email(message = "必须是 Email 格式")
private String mail;
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "cloud")
@NotNull(message = "用户名不能为空")
private String username;
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@NotNull(message = "密码必填")
private String password;
@Schema(description = "SMTP 服务器域名", requiredMode = Schema.RequiredMode.REQUIRED, example = "www.iocoder.cn")
@NotNull(message = "SMTP 服务器域名不能为空")
private String host;
@Schema(description = "SMTP 服务器端口", requiredMode = Schema.RequiredMode.REQUIRED, example = "80")
@NotNull(message = "SMTP 服务器端口不能为空")
private Integer port;
@Schema(description = "是否开启 ssl", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否开启 ssl 必填")
private Boolean sslEnable;
@Schema(description = "是否开启 starttls", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否开启 starttls 必填")
private Boolean starttlsEnable;
}

View File

@@ -0,0 +1,16 @@
package com.zt.plat.module.system.controller.admin.mail.vo.account;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 邮箱账号的精简 Response VO")
@Data
public class MailAccountSimpleRespVO {
@Schema(description = "邮箱编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "768541388@qq.com")
private String mail;
}

View File

@@ -0,0 +1,42 @@
package com.zt.plat.module.system.controller.admin.mail.vo.log;
import com.zt.plat.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.zt.plat.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 MailLogPageReqVO extends PageParam {
@Schema(description = "用户编号", example = "30883")
private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", example = "2")
private Integer userType;
@Schema(description = "接收邮箱地址,模糊匹配", example = "76854@qq.com")
private String toMail;
@Schema(description = "邮箱账号编号", example = "18107")
private Long accountId;
@Schema(description = "模板编号", example = "5678")
private Long templateId;
@Schema(description = "发送状态,参见 MailSendStatusEnum 枚举", example = "1")
private Integer sendStatus;
@Schema(description = "发送时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] sendTime;
}

View File

@@ -0,0 +1,64 @@
package com.zt.plat.module.system.controller.admin.mail.vo.log;
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 MailLogRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31020")
private Long id;
@Schema(description = "用户编号", example = "30883")
private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", example = "2")
private Byte userType;
@Schema(description = "接收邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "76854@qq.com")
private String toMail;
@Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18107")
private Long accountId;
@Schema(description = "发送邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "85757@qq.com")
private String fromMail;
@Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5678")
private Long templateId;
@Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01")
private String templateCode;
@Schema(description = "模版发送人名称", example = "李四")
private String templateNickname;
@Schema(description = "邮件标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试标题")
private String templateTitle;
@Schema(description = "邮件内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试内容")
private String templateContent;
@Schema(description = "邮件参数", requiredMode = Schema.RequiredMode.REQUIRED)
private Map<String, Object> templateParams;
@Schema(description = "发送状态,参见 MailSendStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Byte sendStatus;
@Schema(description = "发送时间")
private LocalDateTime sendTime;
@Schema(description = "发送返回的消息 ID", example = "28568")
private String sendMessageId;
@Schema(description = "发送异常")
private String sendException;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,36 @@
package com.zt.plat.module.system.controller.admin.mail.vo.template;
import com.zt.plat.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.zt.plat.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 MailTemplatePageReqVO extends PageParam {
@Schema(description = "状态,参见 CommonStatusEnum 枚举", example = "1")
private Integer status;
@Schema(description = "标识,模糊匹配", example = "code_1024")
private String code;
@Schema(description = "名称,模糊匹配", example = "芋头")
private String name;
@Schema(description = "账号编号", example = "2048")
private Long accountId;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,46 @@
package com.zt.plat.module.system.controller.admin.mail.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 邮件末班 Response VO")
@Data
public class MailTemplateRespVO {
@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 = "test")
private String code;
@Schema(description = "发送的邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long accountId;
@Schema(description = "发送人名称", example = "芋头")
private String nickname;
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "注册成功")
private String title;
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,注册成功啦")
private String content;
@Schema(description = "参数数组", example = "name,code")
private List<String> params;
@Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "备注", example = "奥特曼")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,46 @@
package com.zt.plat.module.system.controller.admin.mail.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 邮件模版创建/修改 Request VO")
@Data
public class MailTemplateSaveReqVO {
@Schema(description = "编号", example = "1024")
private Long id;
@Schema(description = "模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试名字")
@NotNull(message = "名称不能为空")
private String name;
@Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
@NotNull(message = "模版编号不能为空")
private String code;
@Schema(description = "发送的邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "发送的邮箱账号编号不能为空")
private Long accountId;
@Schema(description = "发送人名称", example = "芋头")
private String nickname;
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "注册成功")
@NotEmpty(message = "标题不能为空")
private String title;
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,注册成功啦")
@NotEmpty(message = "内容不能为空")
private String content;
@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,25 @@
package com.zt.plat.module.system.controller.admin.mail.vo.template;
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 = "管理后台 - 邮件发送 Req VO")
@Data
public class MailTemplateSendReqVO {
@Schema(description = "接收邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "7685413@qq.com")
@NotEmpty(message = "接收邮箱不能为空")
private String mail;
@Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01")
@NotNull(message = "模板编码不能为空")
private String templateCode;
@Schema(description = "模板参数")
private Map<String, Object> templateParams;
}

View File

@@ -0,0 +1,16 @@
package com.zt.plat.module.system.controller.admin.mail.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 邮件模版的精简 Response VO")
@Data
public class MailTemplateSimpleRespVO {
@Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "模版名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "哒哒哒")
private String name;
}

View File

@@ -0,0 +1,92 @@
package com.zt.plat.module.system.controller.admin.notice;
import cn.hutool.core.lang.Assert;
import com.zt.plat.framework.common.enums.UserTypeEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.infra.api.websocket.WebSocketSenderApi;
import com.zt.plat.module.system.controller.admin.notice.vo.NoticePageReqVO;
import com.zt.plat.module.system.controller.admin.notice.vo.NoticeRespVO;
import com.zt.plat.module.system.controller.admin.notice.vo.NoticeSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.notice.NoticeDO;
import com.zt.plat.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 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 static com.zt.plat.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);
}
@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.zt.plat.module.system.controller.admin.notice.vo;
import com.zt.plat.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 = "ZT")
private String title;
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
private Integer status;
}

View File

@@ -0,0 +1,30 @@
package com.zt.plat.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.zt.plat.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.zt.plat.module.system.controller.admin.notify;
import com.zt.plat.framework.apilog.core.annotation.ApiAccessLog;
import com.zt.plat.framework.common.enums.UserTypeEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import com.zt.plat.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import com.zt.plat.module.system.controller.admin.notify.vo.message.NotifyMessageRespVO;
import com.zt.plat.module.system.dal.dataobject.notify.NotifyMessageDO;
import com.zt.plat.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.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.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,91 @@
package com.zt.plat.module.system.controller.admin.notify;
import com.zt.plat.framework.common.enums.UserTypeEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import com.zt.plat.module.system.controller.admin.notify.vo.template.NotifyTemplateRespVO;
import com.zt.plat.module.system.controller.admin.notify.vo.template.NotifyTemplateSaveReqVO;
import com.zt.plat.module.system.controller.admin.notify.vo.template.NotifyTemplateSendReqVO;
import com.zt.plat.module.system.dal.dataobject.notify.NotifyTemplateDO;
import com.zt.plat.module.system.service.notify.NotifySendService;
import com.zt.plat.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 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 static com.zt.plat.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);
}
@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.zt.plat.module.system.controller.admin.notify.vo.message;
import com.zt.plat.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.zt.plat.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.zt.plat.module.system.controller.admin.notify.vo.message;
import com.zt.plat.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.zt.plat.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.zt.plat.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.zt.plat.module.system.controller.admin.notify.vo.template;
import com.zt.plat.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.zt.plat.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;
}

View File

@@ -0,0 +1,43 @@
package com.zt.plat.module.system.controller.admin.notify.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 站内信模版 Response VO")
@Data
public class NotifyTemplateRespVO {
@Schema(description = "ID", 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 = "SEND_TEST")
private String code;
@Schema(description = "模版类型,对应 system_notify_template_type 字典", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type;
@Schema(description = "发送人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆")
private String nickname;
@Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是模版内容")
private String content;
@Schema(description = "参数数组", example = "name,code")
private List<String> params;
@Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "备注", example = "我是备注")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,46 @@
package com.zt.plat.module.system.controller.admin.notify.vo.template;
import com.zt.plat.framework.common.enums.CommonStatusEnum;
import com.zt.plat.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 站内信模版创建/修改 Request VO")
@Data
public class NotifyTemplateSaveReqVO {
@Schema(description = "ID", example = "1024")
private Long id;
@Schema(description = "模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试模版")
@NotEmpty(message = "模版名称不能为空")
private String name;
@Schema(description = "模版编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "SEND_TEST")
@NotNull(message = "模版编码不能为空")
private String code;
@Schema(description = "模版类型,对应 system_notify_template_type 字典", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "模版类型不能为空")
private Integer type;
@Schema(description = "发送人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆")
@NotEmpty(message = "发送人名称不能为空")
private String nickname;
@Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是模版内容")
@NotEmpty(message = "模版内容不能为空")
private String content;
@Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
private Integer status;
@Schema(description = "备注", example = "我是备注")
private String remark;
}

View File

@@ -0,0 +1,29 @@
package com.zt.plat.module.system.controller.admin.notify.vo.template;
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 = "管理后台 - 站内信模板的发送 Request VO")
@Data
public class NotifyTemplateSendReqVO {
@Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "01")
@NotNull(message = "用户id不能为空")
private Long userId;
@Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "用户类型不能为空")
private Integer userType;
@Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "01")
@NotEmpty(message = "模板编码不能为空")
private String templateCode;
@Schema(description = "模板参数")
private Map<String, Object> templateParams;
}

View File

@@ -0,0 +1,23 @@
### 请求 /login 接口 => 成功
POST {{baseUrl}}/system/oauth2-client/create
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
{
"id": "1",
"secret": "admin123",
"name": "ZT源码",
"logo": "https://www.iocoder.cn/images/favicon.ico",
"description": "我是描述",
"status": 0,
"accessTokenValiditySeconds": 180,
"refreshTokenValiditySeconds": 8640,
"redirectUris": ["https://www.iocoder.cn"],
"autoApprove": true,
"authorizedGrantTypes": ["password"],
"scopes": ["user_info"],
"authorities": ["system:user:query"],
"resource_ids": ["1024"],
"additionalInformation": "{}"
}

View File

@@ -0,0 +1,73 @@
package com.zt.plat.module.system.controller.admin.oauth2;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO;
import com.zt.plat.module.system.controller.admin.oauth2.vo.client.OAuth2ClientRespVO;
import com.zt.plat.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
import com.zt.plat.module.system.service.oauth2.OAuth2ClientService;
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 static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - OAuth2 客户端")
@RestController
@RequestMapping("/system/oauth2-client")
@Validated
public class OAuth2ClientController {
@Resource
private OAuth2ClientService oAuth2ClientService;
@PostMapping("/create")
@Operation(summary = "创建 OAuth2 客户端")
@PreAuthorize("@ss.hasPermission('system:oauth2-client:create')")
public CommonResult<Long> createOAuth2Client(@Valid @RequestBody OAuth2ClientSaveReqVO createReqVO) {
return success(oAuth2ClientService.createOAuth2Client(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新 OAuth2 客户端")
@PreAuthorize("@ss.hasPermission('system:oauth2-client:update')")
public CommonResult<Boolean> updateOAuth2Client(@Valid @RequestBody OAuth2ClientSaveReqVO updateReqVO) {
oAuth2ClientService.updateOAuth2Client(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除 OAuth2 客户端")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('system:oauth2-client:delete')")
public CommonResult<Boolean> deleteOAuth2Client(@RequestParam("id") Long id) {
oAuth2ClientService.deleteOAuth2Client(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得 OAuth2 客户端")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:oauth2-client:query')")
public CommonResult<OAuth2ClientRespVO> getOAuth2Client(@RequestParam("id") Long id) {
OAuth2ClientDO client = oAuth2ClientService.getOAuth2Client(id);
return success(BeanUtils.toBean(client, OAuth2ClientRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得 OAuth2 客户端分页")
@PreAuthorize("@ss.hasPermission('system:oauth2-client:query')")
public CommonResult<PageResult<OAuth2ClientRespVO>> getOAuth2ClientPage(@Valid OAuth2ClientPageReqVO pageVO) {
PageResult<OAuth2ClientDO> pageResult = oAuth2ClientService.getOAuth2ClientPage(pageVO);
return success(BeanUtils.toBean(pageResult, OAuth2ClientRespVO.class));
}
}

View File

@@ -0,0 +1,54 @@
### 请求 /system/oauth2/authorize 接口 => 成功
GET {{baseUrl}}/system/oauth2/authorize?clientId=default
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
### 请求 /system/oauth2/authorize + token 接口 => 成功
POST {{baseUrl}}/system/oauth2/authorize
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true
### 请求 /system/oauth2/authorize + code 接口 => 成功
POST {{baseUrl}}/system/oauth2/authorize
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=false
### 请求 /system/oauth2/token + code 接口 => 成功
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}
grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07a174588a97157eabef2f93a
### 请求 /system/oauth2/token + password 接口 => 成功
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}
grant_type=password&username=admin&password=admin123&scope=user.read
### 请求 /system/oauth2/token + refresh_token 接口 => 成功
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}
grant_type=refresh_token&refresh_token=00895465d6994f72a9d926ceeed0f588
### 请求 /system/oauth2/token + DELETE 接口 => 成功
DELETE {{baseUrl}}/system/oauth2/token?token=ca8a188f464441d6949c51493a2b7596
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}
### 请求 /system/oauth2/check-token 接口 => 成功
POST {{baseUrl}}/system/oauth2/check-token?token=620d307c5b4148df8a98dd6c6c547106
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenantId}}

View File

@@ -0,0 +1,297 @@
package com.zt.plat.module.system.controller.admin.oauth2;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.zt.plat.framework.common.enums.UserTypeEnum;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.http.HttpUtils;
import com.zt.plat.framework.common.util.json.JsonUtils;
import com.zt.plat.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO;
import com.zt.plat.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO;
import com.zt.plat.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO;
import com.zt.plat.module.system.convert.oauth2.OAuth2OpenConvert;
import com.zt.plat.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.zt.plat.module.system.dal.dataobject.oauth2.OAuth2ApproveDO;
import com.zt.plat.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
import com.zt.plat.module.system.enums.oauth2.OAuth2GrantTypeEnum;
import com.zt.plat.module.system.service.oauth2.OAuth2ApproveService;
import com.zt.plat.module.system.service.oauth2.OAuth2ClientService;
import com.zt.plat.module.system.service.oauth2.OAuth2GrantService;
import com.zt.plat.module.system.service.oauth2.OAuth2TokenService;
import com.zt.plat.module.system.util.oauth2.OAuth2Utils;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.zt.plat.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList;
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* 提供给外部应用调用为主
*
* 一般来说,管理后台的 /system-api/* 是不直接提供给外部应用使用,主要是外部应用能够访问的数据与接口是有限的,而管理后台的 RBAC 无法很好的控制。
* 参考大量的开放平台,都是独立的一套 OpenAPI对应到【本系统】就是在 Controller 下新建 open 包,实现 /open-api/* 接口,然后通过 scope 进行控制。
* 另外,一个公司如果有多个管理后台,它们 client_id 产生的 access token 相互之间是无法互通的,即无法访问它们系统的 API 接口,直到两个 client_id 产生信任授权。
*
* 考虑到【本系统】暂时不想做的过于复杂,默认只有获取到 access token 之后,可以访问【本系统】管理后台的 /system-api/* 所有接口,除非手动添加 scope 控制。
* scope 的使用示例,可见 {@link OAuth2UserController} 类
*
* @author ZT
*/
@Tag(name = "管理后台 - OAuth2.0 授权")
@RestController
@RequestMapping("/system/oauth2")
@Validated
@Slf4j
public class OAuth2OpenController {
@Resource
private OAuth2GrantService oauth2GrantService;
@Resource
private OAuth2ClientService oauth2ClientService;
@Resource
private OAuth2ApproveService oauth2ApproveService;
@Resource
private OAuth2TokenService oauth2TokenService;
/**
* 对应 Spring Security OAuth 的 TokenEndpoint 类的 postAccessToken 方法
*
* 授权码 authorization_code 模式时code + redirectUri + state 参数
* 密码 password 模式时username + password + scope 参数
* 刷新 refresh_token 模式时refreshToken 参数
* 客户端 client_credentials 模式scope 参数
* 简化 implicit 模式时:不支持
*
* 注意,默认需要传递 client_id + client_secret 参数
*/
@PostMapping("/token")
@PermitAll
@Operation(summary = "获得访问令牌", description = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【获取】调用")
@Parameters({
@Parameter(name = "grant_type", required = true, description = "授权类型", example = "code"),
@Parameter(name = "code", description = "授权范围", example = "userinfo.read"),
@Parameter(name = "redirect_uri", description = "重定向 URI", example = "https://www.iocoder.cn"),
@Parameter(name = "state", description = "状态", example = "1"),
@Parameter(name = "username", example = "tudou"),
@Parameter(name = "password", example = "cai"), // 多个使用空格分隔
@Parameter(name = "scope", example = "user_info"),
@Parameter(name = "refresh_token", example = "123424233"),
})
public CommonResult<OAuth2OpenAccessTokenRespVO> postAccessToken(HttpServletRequest request,
@RequestParam("grant_type") String grantType,
@RequestParam(value = "code", required = false) String code, // 授权码模式
@RequestParam(value = "redirect_uri", required = false) String redirectUri, // 授权码模式
@RequestParam(value = "state", required = false) String state, // 授权码模式
@RequestParam(value = "username", required = false) String username, // 密码模式
@RequestParam(value = "password", required = false) String password, // 密码模式
@RequestParam(value = "scope", required = false) String scope, // 密码模式
@RequestParam(value = "refresh_token", required = false) String refreshToken) { // 刷新模式
List<String> scopes = OAuth2Utils.buildScopes(scope);
// 1.1 校验授权类型
OAuth2GrantTypeEnum grantTypeEnum = OAuth2GrantTypeEnum.getByGrantType(grantType);
if (grantTypeEnum == null) {
throw exception0(BAD_REQUEST.getCode(), StrUtil.format("未知授权类型({})", grantType));
}
if (grantTypeEnum == OAuth2GrantTypeEnum.IMPLICIT) {
throw exception0(BAD_REQUEST.getCode(), "Token 接口不支持 implicit 授权模式");
}
// 1.2 校验客户端
String[] clientIdAndSecret = obtainBasicAuthorization(request);
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1],
grantType, scopes, redirectUri);
// 2. 根据授权模式,获取访问令牌
OAuth2AccessTokenDO accessTokenDO;
switch (grantTypeEnum) {
case AUTHORIZATION_CODE:
accessTokenDO = oauth2GrantService.grantAuthorizationCodeForAccessToken(client.getClientId(), code, redirectUri, state);
break;
case PASSWORD:
accessTokenDO = oauth2GrantService.grantPassword(username, password, client.getClientId(), scopes);
break;
case CLIENT_CREDENTIALS:
accessTokenDO = oauth2GrantService.grantClientCredentials(client.getClientId(), scopes);
break;
case REFRESH_TOKEN:
accessTokenDO = oauth2GrantService.grantRefreshToken(refreshToken, client.getClientId());
break;
default:
throw new IllegalArgumentException("未知授权类型:" + grantType);
}
Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查
return success(OAuth2OpenConvert.INSTANCE.convert(accessTokenDO));
}
@DeleteMapping("/token")
@PermitAll
@Operation(summary = "删除访问令牌")
@Parameter(name = "token", required = true, description = "访问令牌", example = "biu")
public CommonResult<Boolean> revokeToken(HttpServletRequest request,
@RequestParam("token") String token) {
// 校验客户端
String[] clientIdAndSecret = obtainBasicAuthorization(request);
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1],
null, null, null);
// 删除访问令牌
return success(oauth2GrantService.revokeToken(client.getClientId(), token));
}
/**
* 对应 Spring Security OAuth 的 CheckTokenEndpoint 类的 checkToken 方法
*/
@PostMapping("/check-token")
@PermitAll
@Operation(summary = "校验访问令牌")
@Parameter(name = "token", required = true, description = "访问令牌", example = "biu")
public CommonResult<OAuth2OpenCheckTokenRespVO> checkToken(HttpServletRequest request,
@RequestParam("token") String token) {
// 校验客户端
String[] clientIdAndSecret = obtainBasicAuthorization(request);
oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1],
null, null, null);
// 校验令牌
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.checkAccessToken(token);
Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查
return success(OAuth2OpenConvert.INSTANCE.convert2(accessTokenDO));
}
/**
* 对应 Spring Security OAuth 的 AuthorizationEndpoint 类的 authorize 方法
*/
@GetMapping("/authorize")
@Operation(summary = "获得授权信息", description = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【获取】调用")
@Parameter(name = "clientId", required = true, description = "客户端编号", example = "tudou")
public CommonResult<OAuth2OpenAuthorizeInfoRespVO> authorize(@RequestParam("clientId") String clientId) {
// 0. 校验用户已经登录。通过 Spring Security 实现
// 1. 获得 Client 客户端的信息
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId);
// 2. 获得用户已经授权的信息
List<OAuth2ApproveDO> approves = oauth2ApproveService.getApproveList(getLoginUserId(), getUserType(), clientId);
// 拼接返回
return success(OAuth2OpenConvert.INSTANCE.convert(client, approves));
}
/**
* 对应 Spring Security OAuth 的 AuthorizationEndpoint 类的 approveOrDeny 方法
*
* 场景一:【自动授权 autoApprove = true】
* 刚进入 sso.vue 界面,调用该接口,用户历史已经给该应用做过对应的授权,或者 OAuth2Client 支持该 scope 的自动授权
* 场景二:【手动授权 autoApprove = false】
* 在 sso.vue 界面,用户选择好 scope 授权范围调用该接口进行授权。此时approved 为 true 或者 false
*
* 因为前后端分离Axios 无法很好的处理 302 重定向,所以和 Spring Security OAuth 略有不同,返回结果是重定向的 URL剩余交给前端处理
*/
@PostMapping("/authorize")
@Operation(summary = "申请授权", description = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【提交】调用")
@Parameters({
@Parameter(name = "response_type", required = true, description = "响应类型", example = "code"),
@Parameter(name = "client_id", required = true, description = "客户端编号", example = "tudou"),
@Parameter(name = "scope", description = "授权范围", example = "userinfo.read"), // 使用 Map<String, Boolean> 格式Spring MVC 暂时不支持这么接收参数
@Parameter(name = "redirect_uri", required = true, description = "重定向 URI", example = "https://www.iocoder.cn"),
@Parameter(name = "auto_approve", required = true, description = "用户是否接受", example = "true"),
@Parameter(name = "state", example = "1")
})
public CommonResult<String> approveOrDeny(@RequestParam("response_type") String responseType,
@RequestParam("client_id") String clientId,
@RequestParam(value = "scope", required = false) String scope,
@RequestParam("redirect_uri") String redirectUri,
@RequestParam(value = "auto_approve") Boolean autoApprove,
@RequestParam(value = "state", required = false) String state) {
@SuppressWarnings("unchecked")
Map<String, Boolean> scopes = JsonUtils.parseObject(scope, Map.class);
scopes = ObjectUtil.defaultIfNull(scopes, Collections.emptyMap());
// 0. 校验用户已经登录。通过 Spring Security 实现
// 1.1 校验 responseType 是否满足 code 或者 token 值
OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType);
// 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null,
grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri);
// 2.1 假设 approved 为 null说明是场景一
if (Boolean.TRUE.equals(autoApprove)) {
// 如果无法自动授权通过,则返回空 url前端不进行跳转
if (!oauth2ApproveService.checkForPreApproval(getLoginUserId(), getUserType(), clientId, scopes.keySet())) {
return success(null);
}
} else { // 2.2 假设 approved 非 null说明是场景二
// 如果计算后不通过,则跳转一个错误链接
if (!oauth2ApproveService.updateAfterApproval(getLoginUserId(), getUserType(), clientId, scopes)) {
return success(OAuth2Utils.buildUnsuccessfulRedirect(redirectUri, responseType, state,
"access_denied", "User denied access"));
}
}
// 3.1 如果是 code 授权码模式,则发放 code 授权码,并重定向
List<String> approveScopes = convertList(scopes.entrySet(), Map.Entry::getKey, Map.Entry::getValue);
if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) {
return success(getAuthorizationCodeRedirect(getLoginUserId(), client, approveScopes, redirectUri, state));
}
// 3.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向
return success(getImplicitGrantRedirect(getLoginUserId(), client, approveScopes, redirectUri, state));
}
private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) {
if (StrUtil.equals(responseType, "code")) {
return OAuth2GrantTypeEnum.AUTHORIZATION_CODE;
}
if (StrUtil.equalsAny(responseType, "token")) {
return OAuth2GrantTypeEnum.IMPLICIT;
}
throw exception0(BAD_REQUEST.getCode(), "response_type 参数值只允许 code 和 token");
}
private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client,
List<String> scopes, String redirectUri, String state) {
// 1. 创建 access token 访问令牌
OAuth2AccessTokenDO accessTokenDO = oauth2GrantService.grantImplicit(userId, getUserType(), client.getClientId(), scopes);
Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查
// 2. 拼接重定向的 URL
// noinspection unchecked
return OAuth2Utils.buildImplicitRedirectUri(redirectUri, accessTokenDO.getAccessToken(), state, accessTokenDO.getExpiresTime(),
scopes, JsonUtils.parseObject(client.getAdditionalInformation(), Map.class));
}
private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client,
List<String> scopes, String redirectUri, String state) {
// 1. 创建 code 授权码
String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId, getUserType(), client.getClientId(), scopes,
redirectUri, state);
// 2. 拼接重定向的 URL
return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state);
}
private Integer getUserType() {
return UserTypeEnum.ADMIN.getValue();
}
private String[] obtainBasicAuthorization(HttpServletRequest request) {
String[] clientIdAndSecret = HttpUtils.obtainBasicAuthorization(request);
if (ArrayUtil.isEmpty(clientIdAndSecret) || clientIdAndSecret.length != 2) {
throw exception0(BAD_REQUEST.getCode(), "client_id 或 client_secret 未正确传递");
}
return clientIdAndSecret;
}
}

View File

@@ -0,0 +1,50 @@
package com.zt.plat.module.system.controller.admin.oauth2;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO;
import com.zt.plat.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenRespVO;
import com.zt.plat.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.zt.plat.module.system.enums.logger.LoginLogTypeEnum;
import com.zt.plat.module.system.service.auth.AdminAuthService;
import com.zt.plat.module.system.service.oauth2.OAuth2TokenService;
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.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - OAuth2.0 令牌")
@RestController
@RequestMapping("/system/oauth2-token")
public class OAuth2TokenController {
@Resource
private OAuth2TokenService oauth2TokenService;
@Resource
private AdminAuthService authService;
@GetMapping("/page")
@Operation(summary = "获得访问令牌分页", description = "只返回有效期内的")
@PreAuthorize("@ss.hasPermission('system:oauth2-token:page')")
public CommonResult<PageResult<OAuth2AccessTokenRespVO>> getAccessTokenPage(@Valid OAuth2AccessTokenPageReqVO reqVO) {
PageResult<OAuth2AccessTokenDO> pageResult = oauth2TokenService.getAccessTokenPage(reqVO);
return success(BeanUtils.toBean(pageResult, OAuth2AccessTokenRespVO.class));
}
@DeleteMapping("/delete")
@Operation(summary = "删除访问令牌")
@Parameter(name = "accessToken", description = "访问令牌", required = true, example = "tudou")
@PreAuthorize("@ss.hasPermission('system:oauth2-token:delete')")
public CommonResult<Boolean> deleteAccessToken(@RequestParam("accessToken") String accessToken) {
authService.logout(accessToken, LoginLogTypeEnum.LOGOUT_DELETE.getType());
return success(true);
}
}

View File

@@ -0,0 +1,14 @@
### 请求 /system/oauth2/user/get 接口 => 成功
GET {{baseUrl}}/system/oauth2/user/get
Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d
tenant-id: {{adminTenantId}}
### 请求 /system/oauth2/user/update 接口 => 成功
PUT {{baseUrl}}/system/oauth2/user/update
Content-Type: application/json
Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d
tenant-id: {{adminTenantId}}
{
"nickname": "ZT源码"
}

View File

@@ -0,0 +1,88 @@
package com.zt.plat.module.system.controller.admin.oauth2;
import cn.hutool.core.collection.CollUtil;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.controller.admin.oauth2.vo.user.OAuth2UserInfoRespVO;
import com.zt.plat.module.system.controller.admin.oauth2.vo.user.OAuth2UserUpdateReqVO;
import com.zt.plat.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
import com.zt.plat.module.system.dal.dataobject.dept.PostDO;
import com.zt.plat.module.system.dal.dataobject.user.AdminUserDO;
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
import com.zt.plat.module.system.service.dept.DeptService;
import com.zt.plat.module.system.service.dept.PostService;
import com.zt.plat.module.system.service.user.AdminUserService;
import com.zt.plat.module.system.service.userdept.UserDeptService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
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 java.util.stream.Collectors;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* 提供给外部应用调用为主
*
* 1. 在 getUserInfo 方法上,添加 @PreAuthorize("@ss.hasScope('user.read')") 注解,声明需要满足 scope = user.read
* 2. 在 updateUserInfo 方法上,添加 @PreAuthorize("@ss.hasScope('user.write')") 注解,声明需要满足 scope = user.write
*
* @author ZT
*/
@Tag(name = "管理后台 - OAuth2.0 用户")
@RestController
@RequestMapping("/system/oauth2/user")
@Validated
@Slf4j
public class OAuth2UserController {
@Resource
private AdminUserService userService;
@Resource
private DeptService deptService;
@Resource
private PostService postService;
@Resource
private UserDeptService userDeptService;
@GetMapping("/get")
@Operation(summary = "获得用户基本信息")
@PreAuthorize("@ss.hasScope('user.read')") //
public CommonResult<OAuth2UserInfoRespVO> getUserInfo() {
// 获得用户基本信息
AdminUserDO user = userService.getUser(getLoginUserId());
OAuth2UserInfoRespVO resp = BeanUtils.toBean(user, OAuth2UserInfoRespVO.class);
// 获得部门信息
List<UserDeptDO> validUserDeptListByUserId = userDeptService.getValidUserDeptListByUserIds(Collections.singleton(user.getId()));
Set<Long> deptIds = validUserDeptListByUserId.stream().map(UserDeptDO::getDeptId).collect(Collectors.toSet());
List<DeptDO> deptList = deptService.getDeptList(deptIds);
resp.setDepts(BeanUtils.toBean(deptList, OAuth2UserInfoRespVO.Dept.class));
// 获得岗位信息
if (CollUtil.isNotEmpty(user.getPostIds())) {
List<PostDO> posts = postService.getPostList(user.getPostIds());
resp.setPosts(BeanUtils.toBean(posts, OAuth2UserInfoRespVO.Post.class));
}
return success(resp);
}
@PutMapping("/update")
@Operation(summary = "更新用户基本信息")
@PreAuthorize("@ss.hasScope('user.write')")
public CommonResult<Boolean> updateUserInfo(@Valid @RequestBody OAuth2UserUpdateReqVO reqVO) {
// 这里将 UserProfileUpdateReqVO =》UserProfileUpdateReqVO 对象,实现接口的复用。
// 主要是AdminUserService 没有自己的 BO 对象,所以复用只能这么做
userService.updateUserProfile(getLoginUserId(), BeanUtils.toBean(reqVO, UserProfileUpdateReqVO.class));
return success(true);
}
}

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