- admin 模块重命名 system 模块

This commit is contained in:
sin
2019-05-03 17:20:18 +08:00
parent 2dff5d63a9
commit fb96022676
124 changed files with 25 additions and 26 deletions

23
system/pom.xml Normal file
View File

@@ -0,0 +1,23 @@
<?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>
<artifactId>mall-parent</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>system</artifactId>
<name>system</name>
<packaging>pom</packaging>
<modules>
<module>system-application</module>
<module>system-sdk</module>
<module>system-service-api</module>
<module>system-service-impl</module>
</modules>
</project>

View File

@@ -0,0 +1,103 @@
<?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>
<artifactId>system</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>admin-application</artifactId>
<dependencies>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-impl</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-sdk</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--<dependency>-->
<!--<groupId>de.codecentric</groupId>-->
<!--<artifactId>spring-boot-admin-starter-server</artifactId>-->
<!--<version>2.1.3</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>de.codecentric</groupId>-->
<!--<artifactId>spring-boot-admin-server-ui</artifactId>-->
<!--<version>2.1.3</version>-->
<!--</dependency>-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 提供给 mapstruct 使用 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<!-- 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,22 @@
package cn.iocoder.mall.admin.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.admin"})
//@EnableAdminServer
public class AdminApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(AdminApplication.class, args);
// Object bean = ctx.getBean("test");
// System.out.println(AopUtils.getTargetClass(bean));
// System.out.println(bean);
// ConfigurableApplicationContext ctx =
// System.out.println(); // TODO 后面去掉,这里是临时的
}
}

View File

@@ -0,0 +1,60 @@
package cn.iocoder.mall.admin.application.config;
import cn.iocoder.common.framework.config.GlobalExceptionHandler;
import cn.iocoder.common.framework.servlet.CorsFilter;
import cn.iocoder.mall.admin.sdk.interceptor.AdminAccessLogInterceptor;
import cn.iocoder.mall.admin.sdk.interceptor.AdminSecurityInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Set;
@EnableWebMvc
@Configuration
@Import(value = {GlobalExceptionHandler.class, // 统一全局返回
AdminSecurityInterceptor.class})
public class MVCConfiguration implements WebMvcConfigurer {
// @Autowired
// private UserSecurityInterceptor securityInterceptor;
@Autowired
private AdminSecurityInterceptor adminSecurityInterceptor;
@Autowired
private AdminAccessLogInterceptor adminAccessLogInterceptor;
@Value("${auth.ignore-urls}")
private Set<String> ignoreUrls;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(securityInterceptor).addPathPatterns("/user/**", "/admin/**"); // 只拦截我们定义的接口
registry.addInterceptor(adminAccessLogInterceptor).addPathPatterns("/admins/**");
registry.addInterceptor(adminSecurityInterceptor.setIgnoreUrls(ignoreUrls)).addPathPatterns("/admins/**")
.excludePathPatterns("/admins/passport/login"); // 排除登陆接口
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 解决 swagger-ui.html 的访问,参考自 https://stackoverflow.com/questions/43545540/swagger-ui-no-mapping-found-for-http-request 解决
registry.addResourceHandler("swagger-ui.html**").addResourceLocations("classpath:/META-INF/resources/swagger-ui.html");
registry.addResourceHandler("webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new CorsFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.mall.admin.application.config;
import com.qiniu.util.Auth;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QiniuConfiguration {
@Value("${qiniu.access-key}")
private String accessKey;
@Value("${qiniu.secret-key}")
private String secretKey;
@Bean
public Auth auth() {
return Auth.create(accessKey, secretKey);
}
}

View File

@@ -0,0 +1,36 @@
package cn.iocoder.mall.admin.application.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2 // TODO 生产环境时,禁用掉。
public class SwaggerConfiguration {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.iocoder.mall.admin.application.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("管理员子系统")
.description("管理员子系统")
.termsOfServiceUrl("http://www.iocoder.cn")
.version("1.0.0")
.build();
}
}

View File

@@ -0,0 +1,178 @@
package cn.iocoder.mall.admin.application.controller.admins;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.AdminService;
import cn.iocoder.mall.admin.api.ResourceService;
import cn.iocoder.mall.admin.api.RoleService;
import cn.iocoder.mall.admin.api.bo.AdminPageBO;
import cn.iocoder.mall.admin.api.bo.ResourceBO;
import cn.iocoder.mall.admin.api.bo.RoleBO;
import cn.iocoder.mall.admin.api.constant.ResourceConstants;
import cn.iocoder.mall.admin.api.dto.AdminAddDTO;
import cn.iocoder.mall.admin.api.dto.AdminPageDTO;
import cn.iocoder.mall.admin.api.dto.AdminUpdateDTO;
import cn.iocoder.mall.admin.application.convert.AdminConvert;
import cn.iocoder.mall.admin.application.convert.ResourceConvert;
import cn.iocoder.mall.admin.application.vo.AdminMenuTreeNodeVO;
import cn.iocoder.mall.admin.application.vo.AdminPageVO;
import cn.iocoder.mall.admin.application.vo.AdminRoleVO;
import cn.iocoder.mall.admin.application.vo.AdminVO;
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
@RestController
@RequestMapping("admins/admin")
@Api("管理员模块")
public class AdminController {
@Reference(validation = "true")
@Autowired // TODO Dubbo 2.7.2 移除 bug
private ResourceService resourceService;
@Reference(validation = "true")
@Autowired // TODO Dubbo 2.7.2 移除 bug
private AdminService adminService;
@Reference(validation = "true")
@Autowired // TODO Dubbo 2.7.2 移除 bug
private RoleService roleService;
// =========== 当前管理员相关的资源 API ===========
// TODO 功能:当前管理员
@SuppressWarnings("Duplicates")
@GetMapping("/menu_resource_tree")
@ApiOperation(value = "获得当前登陆的管理员拥有的菜单权限", notes = "以树结构返回")
public CommonResult<List<AdminMenuTreeNodeVO>> menuResourceTree() {
List<ResourceBO> resources = resourceService.getResourcesByTypeAndRoleIds(ResourceConstants.TYPE_MENU, AdminSecurityContextHolder.getContext().getRoleIds());
// 创建 AdminMenuTreeNodeVO Map
// Map<Integer, AdminMenuTreeNodeVO> treeNodeMap = resources.stream().collect(Collectors.toMap(ResourceBO::getId, ResourceConvert.INSTANCE::convert));
Map<Integer, AdminMenuTreeNodeVO> treeNodeMap = new LinkedHashMap<>(); // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
resources.stream().sorted(Comparator.comparing(ResourceBO::getSort)).forEach(resourceBO -> treeNodeMap.put(resourceBO.getId(), ResourceConvert.INSTANCE.convert(resourceBO)));
// 处理父子关系
treeNodeMap.values().stream()
.filter(node -> !node.getPid().equals(ResourceConstants.PID_ROOT))
.forEach((childNode) -> {
// 获得父节点
AdminMenuTreeNodeVO parentNode = treeNodeMap.get(childNode.getPid());
if (parentNode.getChildren() == null) { // 初始化 children 数组
parentNode.setChildren(new ArrayList<>());
}
// 将自己添加到父节点中
parentNode.getChildren().add(childNode);
});
// 获得到所有的根节点
List<AdminMenuTreeNodeVO> rootNodes = treeNodeMap.values().stream()
.filter(node -> node.getPid().equals(ResourceConstants.PID_ROOT))
// .sorted(Comparator.comparing(AdminMenuTreeNodeVO::getSort))
.collect(Collectors.toList());
return CommonResult.success(rootNodes);
}
@GetMapping("/url_resource_list")
@ApiOperation(value = "获得当前登陆的管理员拥有的 URL 权限列表")
// @ApiModelProperty(value = "data", example = "['/admin/role/add', '/admin/role/update']") 没效果
public CommonResult<Set<String>> urlResourceList() {
List<ResourceBO> resources = resourceService.getResourcesByTypeAndRoleIds(ResourceConstants.TYPE_URL, AdminSecurityContextHolder.getContext().getRoleIds());
return CommonResult.success(resources.stream().map(ResourceBO::getHandler).collect(Collectors.toSet()));
}
// =========== 管理员管理 API ===========
@GetMapping("/page")
@ApiOperation(value = "管理员分页")
@ApiImplicitParams({
@ApiImplicitParam(name = "nickname", value = "昵称,模糊匹配", example = "小王"),
@ApiImplicitParam(name = "pageNo", value = "页码,从 1 开始", example = "1"),
@ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, example = "10"),
})
public CommonResult<AdminPageVO> page(@RequestParam(value = "nickname", required = false) String nickname,
@RequestParam(value = "pageNo", defaultValue = "0") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
CommonResult<AdminPageBO> result = adminService.getAdminPage(new AdminPageDTO().setNickname(nickname).setPageNo(pageNo).setPageSize(pageSize));
return AdminConvert.INSTANCE.convert(result);
}
@PostMapping("/add")
@ApiOperation(value = "创建管理员")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "账号", required = true, example = "15601691300"),
@ApiImplicitParam(name = "nickname", value = "昵称", required = true, example = "小王"),
@ApiImplicitParam(name = "password", value = "密码", required = true, example = "buzhidao"),
})
public CommonResult<AdminVO> add(@RequestParam("username") String username,
@RequestParam("nickname") String nickname,
@RequestParam("password") String password) {
AdminAddDTO adminAddDTO = new AdminAddDTO().setUsername(username).setNickname(nickname).setPassword(password);
return AdminConvert.INSTANCE.convert2(adminService.addAdmin(AdminSecurityContextHolder.getContext().getAdminId(), adminAddDTO));
}
@PostMapping("/update")
@ApiOperation(value = "更新管理员")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "管理员编号", required = true, example = "1"),
@ApiImplicitParam(name = "username", value = "账号", required = true, example = "15601691300"),
@ApiImplicitParam(name = "nickname", value = "昵称", required = true, example = "小王"),
@ApiImplicitParam(name = "password", value = "密码", example = "buzhidao"),
})
public CommonResult<Boolean> update(@RequestParam("id") Integer id,
@RequestParam("username") String username,
@RequestParam("nickname") String nickname,
@RequestParam(value = "password", required = false) String password) {
AdminUpdateDTO adminUpdateDTO = new AdminUpdateDTO().setId(id).setUsername(username).setNickname(nickname).setPassword(password);
return adminService.updateAdmin(AdminSecurityContextHolder.getContext().getAdminId(), adminUpdateDTO);
}
@PostMapping("/update_status")
@ApiOperation(value = "更新管理员状态")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "管理员编号", required = true, example = "1"),
@ApiImplicitParam(name = "status", value = "状态。1 - 开启2 - 禁用", required = true, example = "1"),
})
public CommonResult<Boolean> updateStatus(@RequestParam("id") Integer id,
@RequestParam("status") Integer status) {
return adminService.updateAdminStatus(AdminSecurityContextHolder.getContext().getAdminId(), id, status);
}
@PostMapping("/delete")
@ApiOperation(value = "删除管理员")
@ApiImplicitParam(name = "id", value = "管理员编号", required = true, example = "1")
public CommonResult<Boolean> delete(@RequestParam("id") Integer id) {
return adminService.deleteAdmin(AdminSecurityContextHolder.getContext().getAdminId(), id);
}
@GetMapping("/role_list")
@ApiOperation(value = "指定管理员拥有的角色列表")
@ApiImplicitParam(name = "id", value = "管理员编号", required = true, example = "1")
public CommonResult<List<AdminRoleVO>> roleList(@RequestParam("id") Integer id) {
// 获得管理员拥有的角色集合
Set<Integer> adminRoleIdSet = roleService.getRoleList(id).getData();
// 获得所有角色数组
List<RoleBO> allRoleList = roleService.getRoleList().getData();
// 转换出返回结果
List<AdminRoleVO> result = AdminConvert.INSTANCE.convert(allRoleList);
// 设置每个角色是否赋予给改管理员
result.forEach(adminRoleVO -> adminRoleVO.setAssigned(adminRoleIdSet.contains(adminRoleVO.getId())));
return CommonResult.success(result);
}
@PostMapping("/assign_role")
@ApiOperation(value = "分配给管理员角色")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "管理员编号", required = true, example = "1"),
@ApiImplicitParam(name = "roleIds", value = "角色编号集合", required = true, example = "1,2,3"),
})
public CommonResult<Boolean> assignRole(@RequestParam("id") Integer id,
@RequestParam("roleIds")Set<Integer> roleIds) {
return adminService.assignRole(AdminSecurityContextHolder.getContext().getAdminId(), id, roleIds);
}
}

View File

@@ -0,0 +1,112 @@
package cn.iocoder.mall.admin.application.controller.admins;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.DataDictService;
import cn.iocoder.mall.admin.api.bo.DataDictBO;
import cn.iocoder.mall.admin.api.dto.DataDictAddDTO;
import cn.iocoder.mall.admin.api.dto.DataDictUpdateDTO;
import cn.iocoder.mall.admin.application.convert.DataDictConvert;
import cn.iocoder.mall.admin.application.vo.DataDictEnumVO;
import cn.iocoder.mall.admin.application.vo.DataDictVO;
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder;
import org.apache.dubbo.config.annotation.Reference;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimaps;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("admins/data_dict")
@Api("数据字典模块")
public class DataDictController {
@Reference(validation = "true")
@Autowired // TODO Dubbo 2.7.2 移除 bug
private DataDictService dataDictService;
@GetMapping("/list")
@ApiOperation(value = "数据字典全列表")
public CommonResult<List<DataDictVO>> list() {
CommonResult<List<DataDictBO>> result = dataDictService.selectDataDictList();
return DataDictConvert.INSTANCE.convert(result);
}
@GetMapping("/tree")
@ApiOperation(value = "数据字典树结构", notes = "该接口返回的信息更为精简。一般用于前端缓存数据字典到本地。")
public CommonResult<List<DataDictEnumVO>> tree() {
// 查询数据字典全列表
CommonResult<List<DataDictBO>> result = dataDictService.selectDataDictList();
if (result.isError()) {
return CommonResult.error(result);
}
// 构建基于 enumValue 聚合的 Multimap
ImmutableListMultimap<String, DataDictBO> dataDictMap = Multimaps.index(result.getData(), DataDictBO::getEnumValue); // KEY 是 enumValue VALUE 是 DataDictBO 数组
// 构建返回结果
List<DataDictEnumVO> dataDictEnumVOs = new ArrayList<>(dataDictMap.size());
dataDictMap.keys().forEach(enumValue -> {
DataDictEnumVO dataDictEnumVO = new DataDictEnumVO().setEnumValue(enumValue)
.setValues(DataDictConvert.INSTANCE.convert2(dataDictMap.get(enumValue)));
dataDictEnumVOs.add(dataDictEnumVO);
});
return CommonResult.success(dataDictEnumVOs);
}
@PostMapping("/add")
@ApiOperation(value = "创建数据字典")
@ApiImplicitParams({
@ApiImplicitParam(name = "enumValue", value = "大类枚举值", required = true, example = "gender"),
@ApiImplicitParam(name = "value", value = "小类数值", required = true, example = "1"),
@ApiImplicitParam(name = "displayName", value = "展示名", required = true, example = ""),
@ApiImplicitParam(name = "sort", required = true, value = "排序值", defaultValue = "10"),
@ApiImplicitParam(name = "memo", value = "备注", example = "你猜我猜不猜"),
})
public CommonResult<DataDictVO> add(@RequestParam("enumValue") String enumValue,
@RequestParam("value") String value,
@RequestParam("displayName") String displayName,
@RequestParam("sort") Integer sort,
@RequestParam(value = "memo", required = false) String memo) {
// 创建 DataDictAddDTO 对象
DataDictAddDTO dataDictAddDTO = new DataDictAddDTO().setEnumValue(enumValue).setValue(value).setDisplayName(displayName)
.setSort(sort).setMemo(memo);
// 保存数据字典
CommonResult<DataDictBO> result = dataDictService.addDataDict(AdminSecurityContextHolder.getContext().getAdminId(), dataDictAddDTO);
// 返回结果
return DataDictConvert.INSTANCE.convert2(result);
}
@PostMapping("/update")
@ApiOperation(value = "更新数据字典")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "100"),
@ApiImplicitParam(name = "value", value = "小类数值", required = true, example = "1"),
@ApiImplicitParam(name = "displayName", value = "展示名", required = true, example = ""),
@ApiImplicitParam(name = "sort", required = true, value = "排序值", defaultValue = "10"),
@ApiImplicitParam(name = "memo", value = "备注", example = "你猜我猜不猜"),
})
public CommonResult<Boolean> update(@RequestParam("id") Integer id,
@RequestParam("value") String value,
@RequestParam("displayName") String displayName,
@RequestParam("sort") Integer sort,
@RequestParam(value = "memo", required = false) String memo) {
// 创建 DataDictAddDTO 对象
DataDictUpdateDTO dataDictUpdateDTO = new DataDictUpdateDTO().setId(id).setValue(value).setDisplayName(displayName)
.setSort(sort).setMemo(memo);
// 更新数据字典
return dataDictService.updateDataDict(AdminSecurityContextHolder.getContext().getAdminId(), dataDictUpdateDTO);
}
@PostMapping("/delete")
@ApiOperation(value = "删除数据字典")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "100")
public CommonResult<Boolean> delete(@RequestParam("id") Integer id) {
return dataDictService.deleteDataDict(AdminSecurityContextHolder.getContext().getAdminId(), id);
}
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.admin.application.controller.admins;
import cn.iocoder.common.framework.vo.CommonResult;
import com.qiniu.util.Auth;
import io.swagger.annotations.Api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("admins/file")
@Api("文件模块")
public class FileController {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private Auth auth;
@Value("${qiniu.bucket}")
private String bucket;
@GetMapping("/get_qiniu_token")
public CommonResult<String> getQiniuToken() {
String token = auth.uploadToken(bucket);
logger.info("[qiniu_token][token({}) get]", token);
return CommonResult.success(token);
}
}

View File

@@ -0,0 +1,44 @@
package cn.iocoder.mall.admin.application.controller.admins;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.OAuth2Service;
import cn.iocoder.mall.admin.api.bo.OAuth2AccessTokenBO;
import cn.iocoder.mall.admin.application.convert.PassportConvert;
import cn.iocoder.mall.admin.application.vo.PassportLoginVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("admins/passport")
@Api("Admin Passport 模块")
public class PassportController {
@Reference(validation = "true")
@Autowired // TODO Dubbo 2.7.2 移除 bug
private OAuth2Service oauth2Service;
@PostMapping("/login")
@ApiOperation(value = "手机号 + 密码登陆")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "账号", required = true, example = "15601691300"),
@ApiImplicitParam(name = "password", value = "密码", required = true, example = "future")
})
public CommonResult<PassportLoginVO> login(@RequestParam("username") String username,
@RequestParam("password") String password) {
CommonResult<OAuth2AccessTokenBO> result = oauth2Service.getAccessToken(username, password);
return PassportConvert.INSTANCE.convert(result);
}
// TODO 功能 logout
// TODO 功能 refresh_token
}

View File

@@ -0,0 +1,111 @@
package cn.iocoder.mall.admin.application.controller.admins;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.ResourceService;
import cn.iocoder.mall.admin.api.bo.ResourceBO;
import cn.iocoder.mall.admin.api.constant.ResourceConstants;
import cn.iocoder.mall.admin.api.dto.ResourceAddDTO;
import cn.iocoder.mall.admin.api.dto.ResourceUpdateDTO;
import cn.iocoder.mall.admin.application.convert.ResourceConvert;
import cn.iocoder.mall.admin.application.vo.ResourceTreeNodeVO;
import cn.iocoder.mall.admin.application.vo.ResourceVO;
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder;
import org.apache.dubbo.config.annotation.Reference;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequestMapping("admins/resource")
@Api("资源模块")
public class ResourceController {
@Reference(validation = "true")
@Autowired // TODO Dubbo 2.7.2 移除 bug
private ResourceService resourceService;
@SuppressWarnings("Duplicates")
@GetMapping("/tree")
@ApiOperation(value = "获得所有资源,按照树形结构返回")
public CommonResult<List<ResourceTreeNodeVO>> tree() {
List<ResourceBO> resources = resourceService.getResourcesByType(null);
// 创建 AdminMenuTreeNodeVO Map
Map<Integer, ResourceTreeNodeVO> treeNodeMap = resources.stream().collect(Collectors.toMap(ResourceBO::getId, ResourceConvert.INSTANCE::convert2));
// 处理父子关系
treeNodeMap.values().stream()
.filter(node -> !node.getPid().equals(ResourceConstants.PID_ROOT))
.forEach((childNode) -> {
// 获得父节点
ResourceTreeNodeVO parentNode = treeNodeMap.get(childNode.getPid());
if (parentNode.getChildren() == null) { // 初始化 children 数组
parentNode.setChildren(new ArrayList<>());
}
// 将自己添加到父节点中
parentNode.getChildren().add(childNode);
});
// 获得到所有的根节点
List<ResourceTreeNodeVO> rootNodes = treeNodeMap.values().stream()
.filter(node -> node.getPid().equals(ResourceConstants.PID_ROOT))
.sorted(Comparator.comparing(ResourceTreeNodeVO::getSort))
.collect(Collectors.toList());
return CommonResult.success(rootNodes);
}
@PostMapping("/add")
@ApiOperation(value = "创建资源", notes = "例如说菜单资源Url 资源")
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "资源名字(标识)", required = true, example = "admin/info"),
@ApiImplicitParam(name = "type", value = "资源类型。1 代表【菜单】2 代表【Url】", required = true, example = "1"),
@ApiImplicitParam(name = "sort", value = "排序", required = true, example = "1"),
@ApiImplicitParam(name = "displayName", value = "菜单展示名", required = true, example = "商品管理"),
@ApiImplicitParam(name = "pid", value = "父级资源编号", required = true, example = "1"),
@ApiImplicitParam(name = "handler", value = "操作", example = "/order/list"),
})
public CommonResult<ResourceVO> add(@RequestParam("name") String name,
@RequestParam("type") Integer type,
@RequestParam("sort") Integer sort,
@RequestParam("displayName") String displayName,
@RequestParam("pid") Integer pid,
@RequestParam(value = "handler", required = false) String handler) {
ResourceAddDTO resourceAddDTO = new ResourceAddDTO().setName(name).setType(type).setSort(sort)
.setDisplayName(displayName).setPid(pid).setHandler(handler);
return ResourceConvert.INSTANCE.convert3(resourceService.addResource(AdminSecurityContextHolder.getContext().getAdminId(), resourceAddDTO));
}
@PostMapping("/update")
@ApiOperation(value = "更新资源")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "资源编号", required = true, example = "1"),
@ApiImplicitParam(name = "name", value = "资源名字(标识)", required = true, example = "admin/info"),
@ApiImplicitParam(name = "sort", value = "排序", required = true, example = "1"),
@ApiImplicitParam(name = "displayName", value = "菜单展示名", required = true, example = "商品管理"),
@ApiImplicitParam(name = "pid", value = "父级资源编号", required = true, example = "1"),
@ApiImplicitParam(name = "handler", value = "操作", example = "/order/list"),
})
public CommonResult<Boolean> update(@RequestParam("id") Integer id,
@RequestParam("name") String name,
@RequestParam("sort") Integer sort,
@RequestParam("displayName") String displayName,
@RequestParam("pid") Integer pid,
@RequestParam(value = "handler", required = false) String handler) {
ResourceUpdateDTO resourceUpdateDTO = new ResourceUpdateDTO().setId(id).setName(name).setSort(sort).setDisplayName(displayName).setPid(pid).setHandler(handler);
return resourceService.updateResource(AdminSecurityContextHolder.getContext().getAdminId(), resourceUpdateDTO);
}
@PostMapping("/delete")
@ApiOperation(value = "删除资源")
@ApiImplicitParam(name = "id", value = "资源编号", required = true, example = "1")
public CommonResult<Boolean> delete(@RequestParam("id") Integer id) {
return resourceService.deleteResource(AdminSecurityContextHolder.getContext().getAdminId(), id);
}
}

View File

@@ -0,0 +1,130 @@
package cn.iocoder.mall.admin.application.controller.admins;
import cn.iocoder.common.framework.util.CollectionUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.ResourceService;
import cn.iocoder.mall.admin.api.RoleService;
import cn.iocoder.mall.admin.api.bo.ResourceBO;
import cn.iocoder.mall.admin.api.bo.RolePageBO;
import cn.iocoder.mall.admin.api.constant.ResourceConstants;
import cn.iocoder.mall.admin.api.dto.RoleAddDTO;
import cn.iocoder.mall.admin.api.dto.RolePageDTO;
import cn.iocoder.mall.admin.api.dto.RoleUpdateDTO;
import cn.iocoder.mall.admin.application.convert.ResourceConvert;
import cn.iocoder.mall.admin.application.convert.RoleConvert;
import cn.iocoder.mall.admin.application.vo.RolePageVO;
import cn.iocoder.mall.admin.application.vo.RoleResourceTreeNodeVO;
import cn.iocoder.mall.admin.application.vo.RoleVO;
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder;
import org.apache.dubbo.config.annotation.Reference;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
@RestController
@RequestMapping("admins/role")
public class RoleController {
@Reference(validation = "true")
@Autowired // TODO Dubbo 2.7.2 移除 bug
private RoleService roleService;
@Reference(validation = "true")
@Autowired // TODO Dubbo 2.7.2 移除 bug
private ResourceService resourceService;
@GetMapping("/page")
@ApiOperation(value = "角色分页")
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "角色名,模糊匹配", required = true, example = "系统管理员"),
@ApiImplicitParam(name = "pageNo", value = "页码,从 1 开始", example = "1"),
@ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, example = "10"),
})
public CommonResult<RolePageVO> page(@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "pageNo", defaultValue = "0") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
CommonResult<RolePageBO> result = roleService.getRolePage(new RolePageDTO().setName(name).setPageNo(pageNo).setPageSize(pageSize));
return RoleConvert.INSTANCE.convert2(result);
}
@PostMapping("/add")
@ApiOperation(value = "创建角色")
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "角色", required = true, example = "系统管理员"),
})
public CommonResult<RoleVO> add(@RequestParam("name") String name) {
RoleAddDTO roleAddDTO = new RoleAddDTO().setName(name);
return RoleConvert.INSTANCE.convert(roleService.addRole(AdminSecurityContextHolder.getContext().getAdminId(), roleAddDTO));
}
@PostMapping("/update")
@ApiOperation(value = "更新角色")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "角色编号", required = true, example = "1"),
@ApiImplicitParam(name = "name", value = "角色名", required = true, example = "系统管理员"),
})
public CommonResult<Boolean> update(@RequestParam("id") Integer id,
@RequestParam("name") String name) {
RoleUpdateDTO roleUpdateDTO = new RoleUpdateDTO().setId(id).setName(name);
return roleService.updateRole(AdminSecurityContextHolder.getContext().getAdminId(), roleUpdateDTO);
}
@PostMapping("/delete")
@ApiOperation(value = "删除角色")
@ApiImplicitParam(name = "id", value = "角色编号", required = true, example = "1")
public CommonResult<Boolean> delete(@RequestParam("id") Integer id) {
return roleService.deleteRole(AdminSecurityContextHolder.getContext().getAdminId(), id);
}
@SuppressWarnings("Duplicates")
@GetMapping("/resource_tree")
@ApiOperation(value = "获得角色拥有的菜单权限", notes = "以树结构返回")
@ApiImplicitParam(name = "id", value = "角色编号", required = true, example = "1")
public CommonResult<List<RoleResourceTreeNodeVO>> resourceTree(@RequestParam("id") Integer id) {
// 芋艿:此处,严格来说可以在校验下角色是否存在。不过呢,校验了也没啥意义,因为一般不存在这个情况,且不会有业务上的影响。并且,反倒多了一次 rpc 调用。
// 第一步,获得角色拥有的资源数组
Set<Integer> roleResources = resourceService.getResourcesByTypeAndRoleIds(null, CollectionUtil.asSet(id))
.stream().map(ResourceBO::getId).collect(Collectors.toSet());
// 第二步,获得资源树
List<ResourceBO> allResources = resourceService.getResourcesByType(null);
// 创建 AdminMenuTreeNodeVO Map
Map<Integer, RoleResourceTreeNodeVO> treeNodeMap = allResources.stream().collect(Collectors.toMap(ResourceBO::getId, ResourceConvert.INSTANCE::convert4));
// 处理父子关系
treeNodeMap.values().stream()
.filter(node -> !node.getPid().equals(ResourceConstants.PID_ROOT))
.forEach((childNode) -> {
// 获得父节点
RoleResourceTreeNodeVO parentNode = treeNodeMap.get(childNode.getPid());
if (parentNode.getChildren() == null) { // 初始化 children 数组
parentNode.setChildren(new ArrayList<>());
}
// 将自己添加到父节点中
parentNode.getChildren().add(childNode);
});
// 获得到所有的根节点
List<RoleResourceTreeNodeVO> rootNodes = treeNodeMap.values().stream()
.filter(node -> node.getPid().equals(ResourceConstants.PID_ROOT))
.sorted(Comparator.comparing(RoleResourceTreeNodeVO::getSort))
.collect(Collectors.toList());
// 第三步,设置角色是否有该角色
treeNodeMap.values().forEach(nodeVO -> nodeVO.setAssigned(roleResources.contains(nodeVO.getId())));
// 返回结果
return CommonResult.success(rootNodes);
}
@PostMapping("/assign_resource")
@ApiOperation(value = "分配角色资源")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "角色编号", required = true, example = "1"),
@ApiImplicitParam(name = "resourceIds", value = "资源数组", required = true, example = "1,2,3"),
})
public CommonResult<Boolean> assignResource(@RequestParam("id") Integer id,
@RequestParam(value = "resourceIds", required = false) Set<Integer> resourceIds) {
return roleService.assignResource(AdminSecurityContextHolder.getContext().getAdminId(), id, resourceIds);
}
}

View File

@@ -0,0 +1,38 @@
package cn.iocoder.mall.admin.application.convert;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.bo.AdminBO;
import cn.iocoder.mall.admin.api.bo.AdminPageBO;
import cn.iocoder.mall.admin.api.bo.RoleBO;
import cn.iocoder.mall.admin.application.vo.AdminInfoVO;
import cn.iocoder.mall.admin.application.vo.AdminPageVO;
import cn.iocoder.mall.admin.application.vo.AdminRoleVO;
import cn.iocoder.mall.admin.application.vo.AdminVO;
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContext;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface AdminConvert {
AdminConvert INSTANCE = Mappers.getMapper(AdminConvert.class);
@Mappings({})
AdminInfoVO convert(AdminSecurityContext adminSecurityContext);
@Mappings({})
AdminVO convert(AdminBO adminBO);
@Mappings({})
CommonResult<AdminVO> convert2(CommonResult<AdminBO> result);
@Mappings({})
CommonResult<AdminPageVO> convert(CommonResult<AdminPageBO> result);
@Mappings({})
List<AdminRoleVO> convert(List<RoleBO> roleList);
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.admin.application.convert;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.bo.DataDictBO;
import cn.iocoder.mall.admin.application.vo.DataDictVO;
import cn.iocoder.mall.admin.application.vo.DataDictValueVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface DataDictConvert {
DataDictConvert INSTANCE = Mappers.getMapper(DataDictConvert.class);
@Mappings({})
DataDictVO convert(DataDictBO dataDictBO);
@Mappings({})
List<DataDictVO> convert(List<DataDictBO> dataDictBOs);
@Mappings({})
CommonResult<List<DataDictVO>> convert(CommonResult<List<DataDictBO>> result);
@Mappings({})
CommonResult<DataDictVO> convert2(CommonResult<DataDictBO> result);
@Mappings({})
List<DataDictValueVO> convert2(List<DataDictBO> dataDictBOs);
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.mall.admin.application.convert;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.bo.OAuth2AccessTokenBO;
import cn.iocoder.mall.admin.application.vo.PassportLoginVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface PassportConvert {
PassportConvert INSTANCE = Mappers.getMapper(PassportConvert.class);
@Mappings({})
PassportLoginVO convert(OAuth2AccessTokenBO oauth2AccessTokenBO);
@Mappings({})
CommonResult<PassportLoginVO> convert(CommonResult<OAuth2AccessTokenBO> oauth2AccessTokenBO);
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.admin.application.convert;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.bo.ResourceBO;
import cn.iocoder.mall.admin.application.vo.AdminMenuTreeNodeVO;
import cn.iocoder.mall.admin.application.vo.ResourceTreeNodeVO;
import cn.iocoder.mall.admin.application.vo.ResourceVO;
import cn.iocoder.mall.admin.application.vo.RoleResourceTreeNodeVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface ResourceConvert {
ResourceConvert INSTANCE = Mappers.getMapper(ResourceConvert.class);
@Mappings({})
AdminMenuTreeNodeVO convert(ResourceBO resourceBO);
@Mappings({})
ResourceTreeNodeVO convert2(ResourceBO resourceBO);
@Mappings({})
RoleResourceTreeNodeVO convert4(ResourceBO resourceBO);
@Mappings({})
ResourceVO convert3(ResourceBO resourceBO);
@Mappings({})
CommonResult<ResourceVO> convert3(CommonResult<ResourceBO> resourceBO);
}

View File

@@ -0,0 +1,31 @@
package cn.iocoder.mall.admin.application.convert;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.bo.RoleBO;
import cn.iocoder.mall.admin.api.bo.RolePageBO;
import cn.iocoder.mall.admin.application.vo.RolePageVO;
import cn.iocoder.mall.admin.application.vo.RoleVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface RoleConvert {
RoleConvert INSTANCE = Mappers.getMapper(RoleConvert.class);
@Mappings({})
RoleVO convert(RoleBO roleBO);
@Mappings({})
List<RoleVO> convert(List<RoleBO> roleBO);
@Mappings({})
CommonResult<RoleVO> convert(CommonResult<RoleBO> resourceBO);
@Mappings({})
CommonResult<RolePageVO> convert2(CommonResult<RolePageBO> resourceBO);
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Set;
@ApiModel("管理员信息 VO")
@Data
@Accessors(chain = true)
public class AdminInfoVO {
@ApiModelProperty(value = "管理员比那好", required = true, example = "1")
private Integer adminId;
@ApiModelProperty(value = "角色编号的数组", required = true, example = "[1, 2]")
private Set<Integer> roleIds;
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@ApiModel("管理员拥有的菜单 VO")
@Data
@Accessors(chain = true)
public class AdminMenuTreeNodeVO {
@ApiModelProperty(value = "菜单编号", required = true, example = "1")
private Integer id;
// @ApiModelProperty(value = "菜单名", required = true, example = "商品管理")
// private String name;
@ApiModelProperty(value = "菜单操作", required = true, example = "/order/list")
private String handler;
@ApiModelProperty(value = "父菜单编号", required = true, example = "1", notes = "如果无父菜单,则值为 0")
private Integer pid;
@ApiModelProperty(value = "排序", required = true, example = "1")
private Integer sort;
@ApiModelProperty(value = "菜单展示名", required = true, example = "商品管理")
private String displayName;
@ApiModelProperty(value = "子节点数组")
private List<AdminMenuTreeNodeVO> children;
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@ApiModel("管理员分页 VO")
@Data
@Accessors(chain = true)
public class AdminPageVO {
@ApiModelProperty(value = "管理员数组")
private List<AdminVO> list;
@ApiModelProperty(value = "管理员总数")
private Integer total;
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
@ApiModel("管理员拥有的角色 VO")
@Data
@Accessors(chain = true)
public class AdminRoleVO {
@ApiModelProperty(value = "角色编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "角色名字", required = true, example = "系统管理员")
private String name;
@ApiModelProperty(value = "是否授权", required = true, example = "true")
private Boolean assigned;
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@ApiModel("管理员 VO")
@Data
@Accessors(chain = true)
public class AdminVO {
@ApiModelProperty(value = "管理员编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "登陆账号", required = true, example = "15601691300")
private String username;
@ApiModelProperty(value = "昵称", required = true, example = "小王")
private String nickname;
@ApiModelProperty(value = "账号状态", required = true, example = "1")
private Integer status;
@ApiModelProperty(value = "创建时间", required = true, example = "时间戳格式")
private Date createTime;
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@ApiModel("数据字典枚举 VO")
@Data
@Accessors(chain = true)
public class DataDictEnumVO {
@ApiModelProperty(value = "大类枚举值", required = true, example = "gender")
private String enumValue;
@ApiModelProperty(value = "小类数值数组", required = true)
private List<DataDictValueVO> values;
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
@ApiModel("数据字典 VO")
@Data
@Accessors(chain = true)
public class DataDictVO {
@ApiModelProperty(value = "编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "大类枚举值", required = true, example = "gender")
private String enumValue;
@ApiModelProperty(value = "小类数值", required = true, example = "1")
private String value;
@ApiModelProperty(value = "展示名", required = true, example = "")
private String displayName;
@ApiModelProperty(value = "排序值", required = true, example = "10")
private Integer sort;
@ApiModelProperty(value = "备注", example = "你猜")
private String memo;
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
@ApiModel("数据字典枚举值 VO")
@Data
@Accessors(chain = true)
public class DataDictValueVO {
@ApiModelProperty(value = "小类数值", required = true, example = "1")
private String value;
@ApiModelProperty(value = "展示名", required = true, example = "")
private String displayName;
}

View File

@@ -0,0 +1,19 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
@ApiModel("登陆结果 VO")
@Data
@Accessors(chain = true)
public class PassportLoginVO {
@ApiModelProperty(value = "访问令牌", required = true, example = "2e3d7635c15e47e997611707a237859f")
private String accessToken;
@ApiModelProperty(value = "刷新令牌", required = true, example = "d091e7c35bbb4313b0f557a6ef23d033")
private String refreshToken;
@ApiModelProperty(value = "过期时间,单位:秒", required = true, example = "2879")
private Integer expiresIn;
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.List;
@ApiModel("资源树结构 VO")
@Data
@Accessors(chain = true)
public class ResourceTreeNodeVO {
@ApiModelProperty(value = "资源编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "资源名字(标识)", required = true, example = "商品管理")
private String name;
@ApiModelProperty(value = "资源类型", required = true, example = "1")
private Integer type;
@ApiModelProperty(value = "排序", required = true, example = "1")
private Integer sort;
@ApiModelProperty(value = "菜单展示名", required = true, example = "商品管理")
private String displayName;
@ApiModelProperty(value = "创建时间", required = true, example = "时间戳格式")
private Date createTime;
@ApiModelProperty(value = "父级资源编号", required = true, example = "1", notes = "如果无父资源,则值为 0")
private Integer pid;
@ApiModelProperty(value = "操作", required = true, example = "/order/list")
private String handler;
@ApiModelProperty(value = "子节点数组")
private List<ResourceTreeNodeVO> children;
}

View File

@@ -0,0 +1,32 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@ApiModel("资源 VO")
@Data
@Accessors(chain = true)
public class ResourceVO {
@ApiModelProperty(value = "资源编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "资源名字(标识)", required = true, example = "商品管理")
private String name;
@ApiModelProperty(value = "资源类型", required = true, example = "1")
private Integer type;
@ApiModelProperty(value = "排序", required = true, example = "1")
private Integer sort;
@ApiModelProperty(value = "菜单展示名", required = true, example = "商品管理")
private String displayName;
@ApiModelProperty(value = "创建时间", required = true, example = "时间戳格式")
private Date createTime;
@ApiModelProperty(value = "父级资源编号", required = true, example = "1", notes = "如果无父资源,则值为 0")
private Integer pid;
@ApiModelProperty(value = "操作", required = true, example = "/order/list")
private String handler;
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@ApiModel("角色分页 VO")
@Data
@Accessors(chain = true)
public class RolePageVO {
@ApiModelProperty(value = "角色数组")
private List<RoleVO> roles;
@ApiModelProperty(value = "角色总数")
private Integer count;
}

View File

@@ -0,0 +1,32 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@ApiModel("角色拥有的资源 VO")
@Data
@Accessors(chain = true)
public class RoleResourceTreeNodeVO {
@ApiModelProperty(value = "菜单编号", required = true, example = "1")
private Integer id;
// @ApiModelProperty(value = "菜单名", required = true, example = "商品管理")
// private String name;
@ApiModelProperty(value = "菜单操作", required = true, example = "/order/list")
private String handler;
@ApiModelProperty(value = "父菜单编号", required = true, example = "1", notes = "如果无父菜单,则值为 0")
private Integer pid;
@ApiModelProperty(value = "排序", required = true, example = "1")
private Integer sort;
@ApiModelProperty(value = "菜单展示名", required = true, example = "商品管理")
private String displayName;
@ApiModelProperty(value = "子节点数组")
private List<RoleResourceTreeNodeVO> children;
@ApiModelProperty(value = "是否授权", required = true, example = "true")
private Boolean assigned;
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.mall.admin.application.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@ApiModel("角色 VO")
@Data
@Accessors(chain = true)
public class RoleVO {
@ApiModelProperty(value = "角色编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "角色名字", required = true, example = "系统管理员")
private String name;
@ApiModelProperty(value = "创建时间", required = true, example = "时间戳格式")
private Date createTime;
}

View File

@@ -0,0 +1,15 @@
spring:
boot:
admin:
client:
enabled: true
url: http://127.0.0.1:18097
management:
endpoints:
web:
exposure:
include: "*"
server:
port: 19083 # 配置独立端口。而该端口,不使用 nginx 对外暴露,从而不配置安全认证。也就是说,内网环境可访问,外网环境不可访问。当然,这么做的前提是,认为内网安全。

View File

@@ -0,0 +1,19 @@
spring:
application:
name: admin-application
# server
server:
port: 18083
servlet:
context-path: /admin-api/
# auth
auth:
ignore-urls: /admin-api/admins/admin/passport/login, /admin-api/admins/file/get_qiniu_token
# qiniu
qiniu:
access-key: YldfyUC7OewoWM63TPYTairqnq8GMJvNek9EGoID
secret-key: zZ7Q8wwZRyaklVvkyLmVydA4WygOBqtc_gTYzalS
bucket: onemall

52
system/system-sdk/pom.xml Normal file
View File

@@ -0,0 +1,52 @@
<?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>
<artifactId>system</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>system-sdk</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,26 @@
package cn.iocoder.mall.admin.sdk.context;
import java.util.Set;
/**
* Security 上下文
*/
public class AdminSecurityContext {
private final Integer adminId;
private final Set<Integer> roleIds;
public AdminSecurityContext(Integer adminId, Set<Integer> roleIds) {
this.adminId = adminId;
this.roleIds = roleIds;
}
public Integer getAdminId() {
return adminId;
}
public Set<Integer> getRoleIds() {
return roleIds;
}
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.mall.admin.sdk.context;
/**
* {@link AdminSecurityContext} Holder
*
* 参考 spring security 的 ThreadLocalSecurityContextHolderStrategy 类,简单实现。
*/
public class AdminSecurityContextHolder {
private static final ThreadLocal<AdminSecurityContext> securityContext = new ThreadLocal<AdminSecurityContext>();
public static void setContext(AdminSecurityContext context) {
securityContext.set(context);
}
public static AdminSecurityContext getContext() {
AdminSecurityContext ctx = securityContext.get();
// 为空时,设置一个空的进去
if (ctx == null) {
ctx = new AdminSecurityContext(null, null);
securityContext.set(ctx);
}
return ctx;
}
public static void clear() {
securityContext.remove();
}
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.mall.admin.sdk.dict;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author Sin
* @time 2019-04-16 20:43
*/
@Data
@Accessors(chain = true)
public class Bean {
@DictVal(dicKey = "gender", dicValue = "1")
private String gender;
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.mall.admin.sdk.dict;
import java.lang.annotation.*;
/**
* 字典转换
*
* @author Sin
* @time 2019-04-16 20:22
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DictVal {
/**
* 字典的 key
*
* @return
*/
String dicKey();
/**
* 字典 value
*
* @return
*/
String dicValue();
/**
* - 暂时只有 dictDisplayName 字典值转换为 dictDisplayName 给用户界面
*
* @return
*/
String mode() default "dictDisplayName";
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.mall.admin.sdk.dict;
import org.springframework.util.ReflectionUtils;
/**
* {@link DictVal} 处理器
*
* @author Sin
* @time 2019-04-16 20:43
*/
public class DictValProcessor {
public static void main(String[] args) {
}
public void processor(Class<?> clazz) {
// ReflectionUtils.ann
// clazz.getFi
// ReflectionUtils
}
}

View File

@@ -0,0 +1,83 @@
package cn.iocoder.mall.admin.sdk.interceptor;
import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.mall.admin.api.AdminAccessLogService;
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
/**
* 访问日志拦截器
*/
@Component
public class AdminAccessLogInterceptor extends HandlerInterceptorAdapter {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 开始时间
*/
private static final ThreadLocal<Date> START_TIME = new ThreadLocal<>();
/**
* 管理员编号
*/
private static final ThreadLocal<Integer> ADMIN_ID = new ThreadLocal<>();
@Reference(lazy = true)
@Autowired(required = false) // TODO 芋艿,初始化时,会存在 spring boot 启动时,服务无法引用的情况,先暂时这么解决。
private AdminAccessLogService adminAccessLogService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 记录当前时间
START_TIME.set(new Date());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (adminAccessLogService == null) {
throw new IllegalStateException("AdminAccessLogService 服务未引入成功");
}
AdminAccessLogAddDTO accessLog = new AdminAccessLogAddDTO();
try {
accessLog.setAdminId(ADMIN_ID.get());
if (accessLog.getAdminId() == null) {
accessLog.setAdminId(AdminAccessLogAddDTO.ADMIN_ID_NULL);
}
accessLog.setUri(request.getRequestURI()); // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。
accessLog.setQueryString(HttpUtil.buildQueryString(request));
accessLog.setMethod(request.getMethod());
accessLog.setUserAgent(HttpUtil.getUserAgent(request));
accessLog.setIp(HttpUtil.getIp(request));
accessLog.setStartTime(START_TIME.get());
accessLog.setResponseTime((int) (System.currentTimeMillis() - accessLog.getStartTime().getTime()));// 默认响应时间设为0
adminAccessLogService.addAdminAccessLog(accessLog);
// TODO 提升:暂时不考虑 ELK 的方案。而是基于 MySQL 存储。如果访问日志比较多,需要定期归档。
} catch (Throwable th) {
logger.error("[afterCompletion][插入管理员访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th));
} finally {
clear();
}
}
public static void setAdminId(Integer adminId) {
ADMIN_ID.set(adminId);
}
public static void clear() {
START_TIME.remove();
ADMIN_ID.remove();
}
}

View File

@@ -0,0 +1,88 @@
package cn.iocoder.mall.admin.sdk.interceptor;
import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.OAuth2Service;
import cn.iocoder.mall.admin.api.bo.OAuth2AuthenticationBO;
import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum;
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContext;
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Set;
/**
* 安全拦截器
*/
@Component
public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
@Reference(validation = "true")
@Autowired(required = false) // TODO 芋艿,初始化时,会存在 spring boot 启动时,服务无法引用的情况,先暂时这么解决。
private OAuth2Service oauth2Service;
/**
* 忽略的 URL 集合,即无需经过认证
*/
private Set<String> ignoreUrls;
public AdminSecurityInterceptor setIgnoreUrls(Set<String> ignoreUrls) {
this.ignoreUrls = ignoreUrls;
return this;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 校验访问令牌是否正确。若正确,返回授权信息
String accessToken = HttpUtil.obtainAccess(request);
OAuth2AuthenticationBO authentication = null;
if (accessToken != null) {
CommonResult<OAuth2AuthenticationBO> result = oauth2Service.checkToken(accessToken);
if (result.isError()) { // TODO 芋艿,如果访问的地址无需登录,这里也不用抛异常
throw new ServiceException(result.getCode(), result.getMessage());
}
authentication = result.getData();
// 添加到 AdminSecurityContext
AdminSecurityContext context = new AdminSecurityContext(authentication.getAdminId(), authentication.getRoleIds());
AdminSecurityContextHolder.setContext(context);
// 同时也记录管理员编号到 AdminAccessLogInterceptor 中。因为:
// AdminAccessLogInterceptor 需要在 AdminSecurityInterceptor 之前执行,这样记录的访问日志才健全
// AdminSecurityInterceptor 执行后,会移除 AdminSecurityContext 信息,这就导致 AdminAccessLogInterceptor 无法获得管理员编号
// 因此,这里需要进行记录
if (authentication.getAdminId() != null) {
AdminAccessLogInterceptor.setAdminId(authentication.getAdminId());
}
} else {
String url = request.getRequestURI();
if (ignoreUrls != null && !ignoreUrls.contains(url)) { // TODO 临时写死。非登陆接口,必须已经认证身份,不允许匿名访问
throw new ServiceException(AdminErrorCodeEnum.OAUTH_NOT_LOGIN.getCode(), AdminErrorCodeEnum.OAUTH_NOT_LOGIN.getMessage());
}
}
// 校验是否需要已授权
checkPermission(request, authentication);
// 返回成功
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 清空 SecurityContext
AdminSecurityContextHolder.clear();
}
private void checkPermission(HttpServletRequest request, OAuth2AuthenticationBO authentication) {
Integer adminId = authentication != null ? authentication.getAdminId() : null;
Set<Integer> roleIds = authentication != null ? authentication.getRoleIds() : null;
String url = request.getRequestURI();
CommonResult<Boolean> result = oauth2Service.checkPermission(adminId, roleIds, url);
if (result.isError()) {
throw new ServiceException(result.getCode(), result.getMessage());
}
}
}

View File

@@ -0,0 +1,6 @@
/**
* 提供 SDK 给其它服务,使用如下功能:
*
* 1. 通过 {@link cn.iocoder.mall.admin.sdk.interceptor.UserSecurityInterceptor} 拦截器,实现需要登陆 URL 的鉴权
*/
package cn.iocoder.mall.admin.sdk;

View File

@@ -0,0 +1,41 @@
<?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>
<artifactId>system</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>system-service-api</artifactId>
<dependencies>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,13 @@
package cn.iocoder.mall.admin.api;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
/**
* 管理员访问日志 Service 接口
*/
public interface AdminAccessLogService {
CommonResult<Boolean> addAdminAccessLog(AdminAccessLogAddDTO adminAccessLogAddDTO);
}

View File

@@ -0,0 +1,32 @@
package cn.iocoder.mall.admin.api;
import cn.iocoder.common.framework.constant.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.bo.AdminBO;
import cn.iocoder.mall.admin.api.bo.AdminPageBO;
import cn.iocoder.mall.admin.api.dto.AdminAddDTO;
import cn.iocoder.mall.admin.api.dto.AdminPageDTO;
import cn.iocoder.mall.admin.api.dto.AdminUpdateDTO;
import java.util.Set;
/**
* 管理员 Service 接口
*/
public interface AdminService {
CommonResult<AdminPageBO> getAdminPage(AdminPageDTO adminPageDTO);
CommonResult<AdminBO> addAdmin(Integer adminId, AdminAddDTO adminAddDTO);
CommonResult<Boolean> updateAdmin(Integer adminId, AdminUpdateDTO adminUpdateDTO);
CommonResult<Boolean> updateAdminStatus(Integer adminId, Integer updateAdminId,
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") Integer status);
CommonResult<Boolean> deleteAdmin(Integer adminId, Integer updateAdminId);
CommonResult<Boolean> assignRole(Integer adminId, Integer updateAdminId, Set<Integer> roleIds);
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.mall.admin.api;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.bo.DataDictBO;
import cn.iocoder.mall.admin.api.dto.DataDictAddDTO;
import cn.iocoder.mall.admin.api.dto.DataDictUpdateDTO;
import java.util.Collection;
import java.util.List;
public interface DataDictService {
CommonResult<List<DataDictBO>> selectDataDictList();
CommonResult<DataDictBO> addDataDict(Integer adminId, DataDictAddDTO dataDictAddDTO);
CommonResult<Boolean> updateDataDict(Integer adminId, DataDictUpdateDTO dataDictUpdateDTO);
CommonResult<Boolean> deleteDataDict(Integer adminId, Integer dataDictId);
/**
* 获取字典值 - 单个
*
* 注意: dictValue:Object 为了方便调用,会自动转换为 dictValue:String
*
* @param dictKey
* @param dictValue
* @return
*/
CommonResult<DataDictBO> getDataDict(String dictKey, Object dictValue);
CommonResult<List<DataDictBO>> getDataDict(String dictKey);
/**
* 获取字典值 - 多个
*
* 注意dictValueList:? 为了方便调用,会自动转换为 Set:String
*
* @param dictKey
* @param dictValueList
* @return
*/
CommonResult<List<DataDictBO>> getDataDictList(String dictKey, Collection<?> dictValueList);
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.admin.api;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.bo.OAuth2AccessTokenBO;
import cn.iocoder.mall.admin.api.bo.OAuth2AuthenticationBO;
import java.util.Set;
public interface OAuth2Service {
CommonResult<OAuth2AccessTokenBO> getAccessToken(String username, String password);
/**
* 校验访问令牌,获取身份信息( 不包括 accessToken 等等 )
*
* @param accessToken 访问令牌
* @return 授权信息
*/
CommonResult<OAuth2AuthenticationBO> checkToken(String accessToken);
/**
* 校验权限(鉴权)
*
* @param adminId 管理员编号
* @param roleIds 管理员拥有的角色编号的集合
* @param url 指定 URL
* @return 是否有权限
*/
CommonResult<Boolean> checkPermission(Integer adminId, Set<Integer> roleIds, String url);
// TODO @see 刷新 token
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.mall.admin.api;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.bo.ResourceBO;
import cn.iocoder.mall.admin.api.dto.ResourceAddDTO;
import cn.iocoder.mall.admin.api.dto.ResourceUpdateDTO;
import org.springframework.lang.Nullable;
import java.util.List;
import java.util.Set;
public interface ResourceService {
/**
* 查询指定类型 + 指定角色的资源列表
*
* @param type 指定类型。可以为空,此时不作为过滤条件
* @param roleIds 指定角色的数组。
* @return 资源列表
*/
List<ResourceBO> getResourcesByTypeAndRoleIds(@Nullable Integer type, Set<Integer> roleIds);
/**
* 查询指定类型的资源列表
*
* @param type 指定类型。可以为空,此时不做为过滤条件
* @return 资源列表
*/
List<ResourceBO> getResourcesByType(@Nullable Integer type);
CommonResult<ResourceBO> addResource(Integer adminId, ResourceAddDTO resourceAddDTO);
CommonResult<Boolean> updateResource(Integer adminId, ResourceUpdateDTO resourceUpdateDTO);
CommonResult<Boolean> deleteResource(Integer adminId, Integer resourceId);
}

View File

@@ -0,0 +1,38 @@
package cn.iocoder.mall.admin.api;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.bo.RoleBO;
import cn.iocoder.mall.admin.api.bo.RolePageBO;
import cn.iocoder.mall.admin.api.dto.RoleAddDTO;
import cn.iocoder.mall.admin.api.dto.RolePageDTO;
import cn.iocoder.mall.admin.api.dto.RoleUpdateDTO;
import java.util.List;
import java.util.Set;
public interface RoleService {
CommonResult<RolePageBO> getRolePage(RolePageDTO rolePageDTO);
/**
* 获得指定管理员拥有的角色编号数组
*
* @param adminId 指定管理员
* @return 角色编号数组
*/
CommonResult<Set<Integer>> getRoleList(Integer adminId);
/**
* @return 返回角色列表
*/
CommonResult<List<RoleBO>> getRoleList();
CommonResult<RoleBO> addRole(Integer adminId, RoleAddDTO roleAddDTO);
CommonResult<Boolean> updateRole(Integer adminId, RoleUpdateDTO roleUpdateDTO);
CommonResult<Boolean> deleteRole(Integer adminId, Integer roleId);
CommonResult<Boolean> assignResource(Integer adminId, Integer roleId, Set<Integer> resourceIds);
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.mall.admin.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 管理员 BO
*/
@Data
@Accessors(chain = true)
public class AdminBO implements Serializable {
/**
* 管理员编号
*/
private Integer id;
/**
* 登陆账号
*/
private String username;
/**
* 昵称
*/
private String nickname;
/**
* 账号状态
*/
private Integer status;
/**
* 创建时间
*/
private Date createTime;
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.mall.admin.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* 管理员分页 BO
*/
@Data
@Accessors(chain = true)
public class AdminPageBO implements Serializable {
/**
* 管理员数组
*/
private List<AdminBO> list;
/**
* 总量
*/
private Integer total;
}

View File

@@ -0,0 +1,45 @@
package cn.iocoder.mall.admin.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 数据字典 BO
*/
@Data
@Accessors(chain = true)
public class DataDictBO implements Serializable {
/**
* 编号
*/
private Integer id;
/**
* 大类枚举值
*/
private String enumValue;
/**
* 小类数值
*/
private String value;
/**
* 展示名
*/
private String displayName;
/**
* 排序值
*/
private Integer sort;
/**
* 备注
*/
private String memo;
/**
* 创建时间
*/
private Date createTime;
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.mall.admin.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* OAUTH2 AccessToken BO
*/
@Data
@Accessors(chain = true)
public class OAuth2AccessTokenBO implements Serializable {
/**
* 访问令牌
*/
private String accessToken;
/**
* 刷新令牌
*/
private String refreshToken;
/**
* 过期时间,单位:秒。
*/
private Integer expiresIn;
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.mall.admin.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Set;
/**
* OAUTH2 认证 BO
*/
@Data
@Accessors(chain = true)
public class OAuth2AuthenticationBO implements Serializable {
/**
* 管理员编号
*/
private Integer adminId;
/**
* 角色编号数组
*/
private Set<Integer> roleIds;
}

View File

@@ -0,0 +1,49 @@
package cn.iocoder.mall.admin.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 资源 BO
*/
@Data
@Accessors(chain = true)
public class ResourceBO implements Serializable {
/**
* 资源编号
*/
private Integer id;
/**
* 资源名字(标识)
*/
private String name;
/**
* 资源类型
*/
private Integer type;
/**
* 排序
*/
private Integer sort;
/**
* 展示名
*/
private String displayName;
/**
* 添加时间
*/
private Date createTime;
/**
* 父级资源编号
*/
private Integer pid;
/**
* 操作
*/
private String handler;
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.mall.admin.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 角色 BO
*/
@Data
@Accessors(chain = true)
public class RoleBO implements Serializable {
/**
* 角色编号
*/
private Integer id;
/**
* 角色名字
*/
private String name;
/**
* 添加时间
*/
private Date createTime;
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.mall.admin.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* 角色分页 BO
*/
@Data
@Accessors(chain = true)
public class RolePageBO implements Serializable {
/**
* 角色数组
*/
private List<RoleBO> roles;
/**
* 总量
*/
private Integer count;
}

View File

@@ -0,0 +1,7 @@
package cn.iocoder.mall.admin.api.constant;
public class AdminConstants {
public static final String USERNAME_ADMIN = "admin";
}

View File

@@ -0,0 +1,65 @@
package cn.iocoder.mall.admin.api.constant;
/**
* 错误码枚举类
*
* 管理员系统,使用 1-002-000-000 段
*/
public enum AdminErrorCodeEnum {
// ========== OAUTH2 模块 ==========
OAUTH2_UNKNOWN(1002001000, "未知错误"), // 预留
// OAUTH2_INVALID_GRANT_BAD_CREDENTIALS(1001001001, "密码不正确"), // 暂时没用到
// OAUTH2_INVALID_GRANT_USERNAME_NOT_FOUND(1001001002, "账号不存在"), // 暂时没用到
// OAUTH2_INVALID_GRANT(1001001010, ""), // 预留
OAUTH_INVALID_TOKEN_NOT_FOUND(1002001011, "访问令牌不存在"),
OAUTH_INVALID_TOKEN_EXPIRED(1002001012, "访问令牌已过期"),
OAUTH_INVALID_TOKEN_INVALID(1002001013, "访问令牌已失效"),
OAUTH_INVALID_PERMISSION(1002001014, "没有该操作权限"), // TODO 芋艿,临时放在 OAUTH2 模块理论来说OAUTH2 只做认证,不做鉴权。
OAUTH_NOT_LOGIN(1002001015, "账号未登陆"),
OAUTH_INVALID_TOKEN(1002001020, ""), // 预留
// ========== 管理员模块 1002002000 ==========
ADMIN_USERNAME_NOT_REGISTERED(1002002000, "账号不存在"),
ADMIN_PASSWORD_ERROR(1002002001, "密码不正确"),
ADMIN_IS_DISABLE(1002002002, "账号被禁用"),
ADMIN_USERNAME_EXISTS(1002002002, "账号已经存在"),
ADMIN_STATUS_EQUALS(1002002003, "账号已经是该状态"),
ADMIN_DELETE_ONLY_DISABLE(1002002004, "只有关闭的账号才可以删除"),
ADMIN_ADMIN_STATUS_CAN_NOT_UPDATE(1002002005, "管理员的账号状态不允许变更"),
// ========== 资源模块 1002003000 ==========
RESOURCE_NAME_DUPLICATE(1002003000, "已经存在该名字的资源"),
RESOURCE_PARENT_NOT_EXISTS(1002003001, "父资源不存在"),
RESOURCE_PARENT_ERROR(1002003002, "不能设置自己为父资源"),
RESOURCE_NOT_EXISTS(1002003003, "资源不存在"),
RESOURCE_EXISTS_CHILDREN(1002003004, "存在子资源,无法删除"),
// ========== 角色模块 1002004000 ==========
ROLE_NOT_EXISTS(1002004000, "角色不存在"),
ROLE_ASSIGN_RESOURCE_NOT_EXISTS(1002004001, "分配角色资源时,有资源不存在"),
// ========== 数据字典模块 1002005000 ==========
DATA_DICT_EXISTS(1002005000, "该数据字典已经存在"),
DATA_DICT_NOT_EXISTS(1002005001, "该数据字典不存在"),
;
private final int code;
private final String message;
AdminErrorCodeEnum(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.mall.admin.api.constant;
/**
* 资源类型
*/
public interface ResourceConstants {
/**
* 类型 - 菜单
*/
Integer TYPE_MENU = 1;
/**
* 类型 - URL
*/
Integer TYPE_URL = 2;
/**
* 父资源编号 - 根节点
*/
Integer PID_ROOT = 0;
}

View File

@@ -0,0 +1,66 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
/**
* 管理员访问日志添加 DTO
*/
@Data
@Accessors(chain = true)
public class AdminAccessLogAddDTO implements Serializable {
/**
* 管理员编号 - 空
*/
public static final Integer ADMIN_ID_NULL = 0;
/**
* 管理员编号.
*
* 当管理员为空时该值为0
*/
@NotNull(message = "管理员编号不能为空")
private Integer adminId;
/**
* 访问地址
*/
@NotNull(message = "访问地址不能为空")
private String uri;
/**
* 参数
*/
@NotNull(message = "请求参数不能为空")
private String queryString;
/**
* http 方法
*/
@NotNull(message = "http 请求方法不能为空")
private String method;
/**
* User Agent
*/
@NotNull(message = "User-Agent 不能为空")
private String userAgent;
/**
* ip
*/
@NotNull(message = "ip 不能为空")
private String ip;
/**
* 请求时间
*/
@NotNull(message = "请求时间不能为空")
private Date startTime;
/**
* 响应时长 -- 毫秒级
*/
@NotNull(message = "响应时长不能为空")
private Integer responseTime;
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
/**
* 管理员添加 DTO
*/
@Data
@Accessors(chain = true)
public class AdminAddDTO {
/**
* 登陆账号
*/
@NotEmpty(message = "登陆账号不能为空")
@Length(min = 6, max = 16, message = "账号长度为 6-16 位")
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
private String username;
/**
* 昵称
*/
@NotEmpty(message = "昵称不能为空")
@Length(max = 10, message = "昵称长度最大为 10 位")
private String nickname;
/**
* 密码
*/
@NotEmpty(message = "密码不能为空")
@Length(min = 6, max = 16, message = "密码长度为 6-16 位")
private String password;
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
/**
* 管理员分页 DTO
*/
@Data
@Accessors(chain = true)
public class AdminPageDTO {
/**
* 昵称,模糊匹配
*/
private String nickname;
@NotNull(message = "页码不能为空")
private Integer pageNo;
@NotNull(message = "每页条数不能为空")
private Integer pageSize;
}

View File

@@ -0,0 +1,42 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* 管理员更新 DTO
*/
@Data
@Accessors(chain = true)
public class AdminUpdateDTO {
/**
* 管理员编号
*/
@NotNull(message = "管理员编号不能为空")
private Integer id;
/**
* 登陆账号
*/
@NotEmpty(message = "登陆账号不能为空")
@Length(min = 6, max = 16, message = "账号长度为 6-16 位")
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
private String username;
/**
* 昵称
*/
@NotEmpty(message = "昵称不能为空")
@Length(max = 10, message = "昵称长度最大为 10 位")
private String nickname;
/**
* 密码
*/
@Length(min = 6, max = 16, message = "密码长度为 6-16 位")
private String password;
}

View File

@@ -0,0 +1,41 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 数据字典添加 DTO
*/
@Data
@Accessors(chain = true)
public class DataDictAddDTO {
/**
* 大类枚举值
*/
@NotEmpty(message = "大类枚举值不能为空")
private String enumValue;
/**
* 小类数值
*/
@NotEmpty(message = "小类数值不能为空")
private String value;
/**
* 展示名
*/
@NotEmpty(message = "展示名不能为空")
private String displayName;
/**
* 排序值
*/
@NotNull(message = "排序值不能为空")
private Integer sort;
/**
* 备注
*/
private String memo;
}

View File

@@ -0,0 +1,41 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 数据字典更新 DTO
*/
@Data
@Accessors(chain = true)
public class DataDictUpdateDTO {
/**
* 编号
*/
@NotNull(message = "编号不能为空")
private Integer id;
/**
* 小类数值
*/
@NotEmpty(message = "小类数值不能为空")
private String value;
/**
* 展示名
*/
@NotEmpty(message = "展示名不能为空")
private String displayName;
/**
* 排序值
*/
@NotNull(message = "排序值不能为空")
private Integer sort;
/**
* 备注
*/
private String memo;
}

View File

@@ -0,0 +1,45 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 资源添加 DTO
*/
@Data
@Accessors(chain = true)
public class ResourceAddDTO {
/**
* 资源名字(标识)
*/
@NotEmpty(message = "资源名字不能为空")
private String name;
/**
* 类型
*/
@NotNull(message = "类型不能为空")
private Integer type;
/**
* 排序值
*/
@NotNull(message = "类型不能为空")
private Integer sort;
/**
* 展示名
*/
@NotEmpty(message = "资源名字不能为空")
private String displayName;
/**
* 父资源编号
*/
private Integer pid;
/**
* 操作
*/
private String handler;
}

View File

@@ -0,0 +1,45 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 资源更新 DTO
*/
@Data
@Accessors(chain = true)
public class ResourceUpdateDTO {
/**
* 资源编号
*/
@NotNull(message = "资源编号不能为空")
private Integer id;
/**
* 资源名字(标识)
*/
@NotEmpty(message = "资源名字不能为空")
private String name;
/**
* 排序值
*/
@NotNull(message = "类型不能为空")
private Integer sort;
/**
* 展示名
*/
@NotEmpty(message = "资源名字不能为空")
private String displayName;
/**
* 父资源编号
*/
private Integer pid;
/**
* 操作
*/
private String handler;
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
/**
* 角色添加 DTO
*/
@Data
@Accessors(chain = true)
public class RoleAddDTO {
/**
* 角色名字(标识)
*/
@NotEmpty(message = "角色名字不能为空")
private String name;
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 角色分页 DTO
*/
@Data
@Accessors(chain = true)
public class RolePageDTO {
private Integer pageNo;
private Integer pageSize;
private String name;
public Integer getPageNo() {
return pageNo;
}
public RolePageDTO setPageNo(Integer pageNo) {
this.pageNo = pageNo;
return this;
}
public Integer getPageSize() {
return pageSize;
}
public RolePageDTO setPageSize(Integer pageSize) {
this.pageSize = pageSize;
return this;
}
public String getName() {
return name;
}
public RolePageDTO setName(String name) {
this.name = name;
return this;
}
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.mall.admin.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 角色添加 DTO
*/
@Data
@Accessors(chain = true)
public class RoleUpdateDTO {
/**
* 角色编号
*/
@NotNull(message = "角色编号不能为空")
private Integer id;
/**
* 角色名字(标识)
*/
@NotEmpty(message = "角色名字不能为空")
private String name;
}

View File

@@ -0,0 +1,69 @@
<?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>
<artifactId>system</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>system-service-impl</artifactId>
<dependencies>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 提供给 mapstruct 使用 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,14 @@
package cn.iocoder.mall.admin.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan("cn.iocoder.mall.admin.dao") // 扫描对应的 Mapper 接口
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理。为什么使用 proxyTargetClass 参数,参见 https://blog.csdn.net/huang_550/article/details/76492600
public class DatabaseConfiguration {
// 数据源,使用 HikariCP
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.mall.admin.config;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
@Configuration
public class ServiceExceptionConfiguration {
@EventListener(ApplicationReadyEvent.class) // 可参考 https://www.cnblogs.com/ssslinppp/p/7607509.html
public void initMessages() {
// 从 service_exception_message.properties 加载错误码的方案
// Properties properties;
// try {
// properties = PropertiesLoaderUtils.loadAllProperties("classpath:service_exception_message.properties");
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
for (AdminErrorCodeEnum item : AdminErrorCodeEnum.values()) {
ServiceExceptionUtil.put(item.getCode(), item.getMessage());
}
}
}

View File

@@ -0,0 +1,17 @@
package cn.iocoder.mall.admin.convert;
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
import cn.iocoder.mall.admin.dataobject.AdminAccessLogDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface AdminAccessLogConvert {
AdminAccessLogConvert INSTANCE = Mappers.getMapper(AdminAccessLogConvert.class);
@Mappings({})
AdminAccessLogDO convert(AdminAccessLogAddDTO adminAccessLogAddDTO);
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.mall.admin.convert;
import cn.iocoder.mall.admin.api.bo.AdminBO;
import cn.iocoder.mall.admin.api.dto.AdminAddDTO;
import cn.iocoder.mall.admin.api.dto.AdminUpdateDTO;
import cn.iocoder.mall.admin.dataobject.AdminDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface AdminConvert {
AdminConvert INSTANCE = Mappers.getMapper(AdminConvert.class);
@Mappings({})
AdminBO convert(AdminDO adminDO);
@Mappings({})
AdminDO convert(AdminAddDTO adminAddDTO);
@Mappings({})
AdminDO convert(AdminUpdateDTO adminUpdateDTO);
@Mappings({})
List<AdminBO> convert(List<AdminDO> adminBOs);
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.mall.admin.convert;
import cn.iocoder.mall.admin.api.bo.DataDictBO;
import cn.iocoder.mall.admin.api.dto.DataDictAddDTO;
import cn.iocoder.mall.admin.api.dto.DataDictUpdateDTO;
import cn.iocoder.mall.admin.dataobject.DataDictDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface DataDictConvert {
DataDictConvert INSTANCE = Mappers.getMapper(DataDictConvert.class);
DataDictDO convert(DataDictAddDTO dataDictAddDTO);
DataDictDO convert(DataDictUpdateDTO dataDictUpdateDTO);
DataDictBO convert(DataDictDO dataDictDO);
List<DataDictBO> convert(List<DataDictDO> dataDictDOs);
}

View File

@@ -0,0 +1,38 @@
package cn.iocoder.mall.admin.convert;
import cn.iocoder.mall.admin.api.bo.OAuth2AccessTokenBO;
import cn.iocoder.mall.admin.api.bo.OAuth2AuthenticationBO;
import cn.iocoder.mall.admin.dataobject.AdminRoleDO;
import cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.stream.Collectors;
@Mapper
public interface OAuth2Convert {
OAuth2Convert INSTANCE = Mappers.getMapper(OAuth2Convert.class);
@Mappings({
@Mapping(source = "id", target = "accessToken")
})
OAuth2AccessTokenBO convertToAccessToken(OAuth2AccessTokenDO oauth2AccessTokenDO);
default OAuth2AccessTokenBO convertToAccessTokenWithExpiresIn(OAuth2AccessTokenDO oauth2AccessTokenDO) {
return this.convertToAccessToken(oauth2AccessTokenDO)
.setExpiresIn(Math.max((int) ((oauth2AccessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis()) / 1000), 0));
}
@Mappings({})
OAuth2AuthenticationBO convertToAuthentication(OAuth2AccessTokenDO oauth2AccessTokenDO);
default OAuth2AuthenticationBO convertToAuthentication(OAuth2AccessTokenDO oauth2AccessTokenDO, List<AdminRoleDO> adminRoleDOs) {
return convertToAuthentication(oauth2AccessTokenDO)
.setRoleIds(adminRoleDOs.stream().map(AdminRoleDO::getRoleId).collect(Collectors.toSet()));
}
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.mall.admin.convert;
import cn.iocoder.mall.admin.api.bo.ResourceBO;
import cn.iocoder.mall.admin.api.dto.ResourceAddDTO;
import cn.iocoder.mall.admin.api.dto.ResourceUpdateDTO;
import cn.iocoder.mall.admin.dataobject.ResourceDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface ResourceConvert {
ResourceConvert INSTANCE = Mappers.getMapper(ResourceConvert.class);
@Mappings({})
ResourceBO convert(ResourceDO resourceDO);
@Mappings({})
List<ResourceBO> convert(List<ResourceDO> resourceDOs);
@Mappings({})
ResourceDO convert(ResourceAddDTO resourceAddDTO);
@Mappings({})
ResourceDO convert(ResourceUpdateDTO resourceUpdateDTO);
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.mall.admin.convert;
import cn.iocoder.mall.admin.api.bo.RoleBO;
import cn.iocoder.mall.admin.api.dto.RoleAddDTO;
import cn.iocoder.mall.admin.api.dto.RoleUpdateDTO;
import cn.iocoder.mall.admin.dataobject.RoleDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface RoleConvert {
RoleConvert INSTANCE = Mappers.getMapper(RoleConvert.class);
@Mappings({})
RoleDO convert(RoleAddDTO roleAddDTO);
@Mappings({})
RoleDO convert(RoleUpdateDTO roleUpdateDTO);
@Mappings({})
RoleBO convert(RoleDO roleDO);
@Mappings({})
List<RoleBO> convert(List<RoleDO> roleDOs);
}

View File

@@ -0,0 +1,11 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.AdminAccessLogDO;
import org.springframework.stereotype.Repository;
@Repository
public interface AdminAccessLogMapper {
void insert(AdminAccessLogDO entity);
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.AdminDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface AdminMapper {
AdminDO selectById(@Param("id") Integer id);
AdminDO selectByUsername(@Param("username") String username);
List<AdminDO> selectListByNicknameLike(@Param("nickname") String nickname,
@Param("offset") Integer offset,
@Param("limit") Integer limit);
Integer selectCountByNicknameLike(@Param("nickname") String nickname);
void insert(AdminDO admin);
int update(AdminDO admin);
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.AdminRoleDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface AdminRoleMapper {
List<AdminRoleDO> selectByAdminId(@Param("adminId") Integer adminId);
int updateToDeletedByAdminId(@Param("adminId") Integer adminId);
int updateToDeletedByRoleId(@Param("roleId") Integer roleId);
void insertList(@Param("adminRoleDOs") List<AdminRoleDO> adminRoleDOs);
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.DataDictDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
@Repository
public interface DataDictMapper {
DataDictDO selectById(@Param("id") Integer id);
DataDictDO selectByEnumValueAndValue(
@Param("enumValue") String enumValue,
@Param("value") String value
);
List<DataDictDO> selectByEnumValueAndValues(
@Param("enumValue") String enumValue,
@Param("values") Collection<String> values
);
List<DataDictDO> selectByEnumValue(
@Param("enumValue") String enumValue
);
List<DataDictDO> selectList();
void insert(DataDictDO dataDict);
int update(DataDictDO dataDict);
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface OAuth2AccessTokenMapper {
void insert(OAuth2AccessTokenDO entity);
OAuth2AccessTokenDO selectByTokenId(@Param("id") String id);
int updateToInvalidByAdminId(@Param("adminId") Integer adminId);
}

View File

@@ -0,0 +1,14 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.OAuth2RefreshTokenDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface OAuth2RefreshTokenMapper {
void insert(OAuth2RefreshTokenDO entity);
int updateToInvalidByAdminId(@Param("adminId") Integer adminId);
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.ResourceDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Set;
@Repository
public interface ResourceMapper {
ResourceDO selectByTypeAndHandler(@Param("type") Integer type,
@Param("handler") String handler);
List<ResourceDO> selectListByTypeAndRoleIds(@Param("type") Integer type,
@Param("roleIds") Set<Integer> roleIds);
List<ResourceDO> selectListByType(@Param("type") Integer type);
ResourceDO selectByName(@Param("name") String name);
ResourceDO selectById(@Param("id") Integer id);
List<ResourceDO> selectListByIds(@Param("ids") Set<Integer> ids);
void insert(ResourceDO resource);
int update(ResourceDO resource);
int selectCountByPid(@Param("pid") Integer pid);
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.RoleDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Set;
@Repository
public interface RoleMapper {
void insert(RoleDO roleDO);
int update(RoleDO roleDO);
RoleDO selectById(@Param("id") Integer id);
List<RoleDO> selectListByNameLike(@Param("name") String name,
@Param("offset") Integer offset,
@Param("limit") Integer limit);
Integer selectCountByNameLike(@Param("name") String name);
List<RoleDO> selectListByIds(@Param("ids") Set<Integer> ids);
List<RoleDO> selectList();
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.RoleResourceDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface RoleResourceMapper {
int insertList(@Param("roleResources") List<RoleResourceDO> resourceDOs);
List<RoleResourceDO> selectByResourceHandler(@Param("resourceHandler") String resourceHandler);
List<RoleResourceDO> selectByResourceId(@Param("resourceId") Integer resourceId);
int updateToDeletedByResourceId(@Param("resourceId") Integer resourceId);
int updateToDeletedByRoleId(@Param("roleId") Integer roleId);
}

View File

@@ -0,0 +1,55 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 管理员访问日志 DO
*/
@Data
@Accessors(chain = true)
public class AdminAccessLogDO extends DeletableDO {
/**
* 编号
*/
private Integer id;
/**
* 管理员编号.
*
* 当管理员为空时该值为0
*/
private Integer adminId;
/**
* 访问地址
*/
private String uri;
/**
* 参数
*/
private String queryString;
/**
* http 方法
*/
private String method;
/**
* userAgent
*/
private String userAgent;
/**
* ip
*/
private String ip;
/**
* 请求时间
*/
private Date startTime;
/**
* 响应时长 -- 毫秒级
*/
private Integer responseTime;
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 管理员实体
*/
@Data
@Accessors(chain = true)
public class AdminDO extends DeletableDO {
/**
* 管理员编号
*/
private Integer id;
/**
* 登陆账号
*/
private String username;
/**
* 昵称
*/
private String nickname;
/**
* 密码
*
* TODO 芋艿 暂时最简单的 MD5
*/
private String password;
/**
* 账号状态
*/
private Integer status;
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* {@link AdminDO} 和 {@link RoleDO} 的关联表
*/
@Data
@Accessors(chain = true)
public class AdminRoleDO extends DeletableDO {
/**
* 编号
*/
private Integer id;
/**
* 管理员编号(外键:{@link AdminDO}
*/
private Integer adminId;
/**
* 角色编号(外键:{@link RoleDO}
*/
private Integer roleId;
}

View File

@@ -0,0 +1,45 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 数据字典
*
* 使用 {@link #enumValue} 作为聚合。例如说:
*
* enumValue gender 性别
* value1 男
* value2 女
*/
@Data
@Accessors(chain = true)
public class DataDictDO extends DeletableDO {
/**
* 编号
*/
private Integer id;
/**
* 大类枚举值
*/
private String enumValue;
/**
* 小类数值
*/
private String value;
/**
* 展示名
*/
private String displayName;
/**
* 排序值
*/
private Integer sort;
/**
* 备注
*/
private String memo;
}

View File

@@ -0,0 +1,94 @@
package cn.iocoder.mall.admin.dataobject;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* OAUTH2 AccessToken
*/
@Data
@Accessors(chain = true)
public class OAuth2AccessTokenDO {
/**
* 访问令牌
*/
private String id;
/**
* 刷新令牌
*/
private String refreshToken;
/**
* 管理员比那好
*/
private Integer adminId;
/**
* 过期时间
*/
private Date expiresTime;
/**
* 是否有效
*/
private Boolean valid;
/**
* 创建时间
*/
private Date createTime;
public String getId() {
return id;
}
public OAuth2AccessTokenDO setId(String id) {
this.id = id;
return this;
}
public String getRefreshToken() {
return refreshToken;
}
public OAuth2AccessTokenDO setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
return this;
}
public Integer getAdminId() {
return adminId;
}
public OAuth2AccessTokenDO setAdminId(Integer adminId) {
this.adminId = adminId;
return this;
}
public Date getExpiresTime() {
return expiresTime;
}
public OAuth2AccessTokenDO setExpiresTime(Date expiresTime) {
this.expiresTime = expiresTime;
return this;
}
public Boolean getValid() {
return valid;
}
public OAuth2AccessTokenDO setValid(Boolean valid) {
this.valid = valid;
return this;
}
public Date getCreateTime() {
return createTime;
}
public OAuth2AccessTokenDO setCreateTime(Date createTime) {
this.createTime = createTime;
return this;
}
}

View File

@@ -0,0 +1,38 @@
package cn.iocoder.mall.admin.dataobject;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 刷新令牌
*
* idx_uid
*/
@Data
@Accessors(chain = true)
public class OAuth2RefreshTokenDO {
/**
* 刷新令牌
*/
private String id;
/**
* 用户编号
*/
private Integer adminId;
/**
* 是否有效
*/
private Boolean valid;
/**
* 过期时间
*/
private Date expiresTime;
/**
* 创建时间
*/
private Date createTime;
}

View File

@@ -0,0 +1,65 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 资源实体
*/
@Data
@Accessors(chain = true)
public class ResourceDO extends DeletableDO {
/**
* 资源类型 - 菜单
*/
@Deprecated
public static final Integer TYPE_MENU = 1;
/**
* 资源类型 - 操作
*
* 例如,按钮。
*/
@Deprecated
public static final Integer TYPE_OPERATION = 2;
/**
* 资源编号
*/
private Integer id;
/**
* 资源名字(标识)
*/
private String name;
/**
* 资源类型
*/
private Integer type;
/**
* 排序
*/
private Integer sort;
/**
* 展示名
*/
private String displayName;
/**
* 添加时间
*/
private Date createTime;
/**
* 父级资源编号(外键:{@link ResourceDO#id})
*/
private Integer pid;
/**
* 操作
*
* 当资源类型为【菜单】时handler 配置为界面 URL ,或者前端组件名
* 当资源类型为【URL】时handler 配置为后端 URL 。举个例子,如果有一个「创建管理员」的表单,那么前端界面上的按钮可以根据这个 url 判断是否展示,后端接收到该 url 的请求时会判断是否有权限。
*/
private String handler;
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 角色实体
*/
@Data
@Accessors(chain = true)
public class RoleDO extends DeletableDO {
/**
* 角色编号
*/
private Integer id;
/**
* 角色名
*/
private String name;
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* {@link RoleDO} 和 {@link ResourceDO} 的关联表
*/
@Data
@Accessors(chain = true)
public class RoleResourceDO extends DeletableDO {
/**
* 编号
*/
private Integer id;
/**
* 角色编号(外键:{@link RoleDO}
*/
private Integer roleId;
/**
* 资源编号(外键:{@link ResourceDO}
*/
private Integer resourceId;
}

View File

@@ -0,0 +1,56 @@
package cn.iocoder.mall.admin.service;
import cn.iocoder.common.framework.util.StringUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.AdminAccessLogService;
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
import cn.iocoder.mall.admin.convert.AdminAccessLogConvert;
import cn.iocoder.mall.admin.dao.AdminAccessLogMapper;
import cn.iocoder.mall.admin.dataobject.AdminAccessLogDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
@org.apache.dubbo.config.annotation.Service(validation = "true")
public class AdminAccessLogServiceImpl implements AdminAccessLogService {
/**
* 请求参数最大长度。
*/
private static final Integer QUERY_STRING_MAX_LENGTH = 4096;
/**
* 请求地址最大长度。
*/
private static final Integer URI_MAX_LENGTH = 4096;
/**
* User-Agent 最大长度。
*/
private static final Integer USER_AGENT_MAX_LENGTH = 1024;
@Autowired
private AdminAccessLogMapper adminAccessLogMapper;
@Override
public CommonResult<Boolean> addAdminAccessLog(AdminAccessLogAddDTO adminAccessLogAddDTO) {
// 创建 AdminAccessLogDO
AdminAccessLogDO accessLog = AdminAccessLogConvert.INSTANCE.convert(adminAccessLogAddDTO);
accessLog.setCreateTime(new Date());
// 截取最大长度
if (accessLog.getUri().length() > URI_MAX_LENGTH) {
accessLog.setUri(StringUtil.substring(accessLog.getUri(), URI_MAX_LENGTH));
}
if (accessLog.getQueryString().length() > QUERY_STRING_MAX_LENGTH) {
accessLog.setQueryString(StringUtil.substring(accessLog.getQueryString(), QUERY_STRING_MAX_LENGTH));
}
if (accessLog.getUserAgent().length() > USER_AGENT_MAX_LENGTH) {
accessLog.setUserAgent(StringUtil.substring(accessLog.getUserAgent(), USER_AGENT_MAX_LENGTH));
}
// 插入
adminAccessLogMapper.insert(accessLog);
// 返回成功
return CommonResult.success(true);
}
}

View File

@@ -0,0 +1,203 @@
package cn.iocoder.mall.admin.service;
import cn.iocoder.common.framework.constant.CommonStatusEnum;
import cn.iocoder.common.framework.constant.DeletedStatusEnum;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.AdminService;
import cn.iocoder.mall.admin.api.bo.AdminBO;
import cn.iocoder.mall.admin.api.bo.AdminPageBO;
import cn.iocoder.mall.admin.api.constant.AdminConstants;
import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum;
import cn.iocoder.mall.admin.api.dto.AdminAddDTO;
import cn.iocoder.mall.admin.api.dto.AdminPageDTO;
import cn.iocoder.mall.admin.api.dto.AdminUpdateDTO;
import cn.iocoder.mall.admin.convert.AdminConvert;
import cn.iocoder.mall.admin.dao.AdminMapper;
import cn.iocoder.mall.admin.dao.AdminRoleMapper;
import cn.iocoder.mall.admin.dataobject.AdminDO;
import cn.iocoder.mall.admin.dataobject.AdminRoleDO;
import cn.iocoder.mall.admin.dataobject.RoleDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@org.apache.dubbo.config.annotation.Service(validation = "true")
public class AdminServiceImpl implements AdminService {
@Autowired
private AdminMapper adminMapper;
@Autowired
private AdminRoleMapper adminRoleMapper;
@Autowired
private OAuth2ServiceImpl oAuth2Service;
@Autowired
private RoleServiceImpl roleService;
public CommonResult<AdminDO> validAdmin(String username, String password) {
AdminDO admin = adminMapper.selectByUsername(username);
// 账号不存在
if (admin == null) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode());
}
// 密码不正确
if (encodePassword(password).equals(admin.getPassword())) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_PASSWORD_ERROR.getCode());
}
// 账号被禁用
if (CommonStatusEnum.DISABLE.getValue().equals(admin.getStatus())) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_IS_DISABLE.getCode());
}
// 校验成功,返回管理员。并且,去掉一些非关键字段,考虑安全性。
admin.setPassword(null);
admin.setStatus(null);
return CommonResult.success(admin);
}
public List<AdminRoleDO> getAdminRoles(Integer adminId) {
return adminRoleMapper.selectByAdminId(adminId);
}
@Override
public CommonResult<AdminPageBO> getAdminPage(AdminPageDTO adminPageDTO) {
AdminPageBO adminPage = new AdminPageBO();
// 查询分页数据
int offset = (adminPageDTO.getPageNo() - 1) * adminPageDTO.getPageSize();
adminPage.setList(AdminConvert.INSTANCE.convert(adminMapper.selectListByNicknameLike(adminPageDTO.getNickname(),
offset, adminPageDTO.getPageSize())));
// 查询分页总数
adminPage.setTotal(adminMapper.selectCountByNicknameLike(adminPageDTO.getNickname()));
return CommonResult.success(adminPage);
}
@Override
public CommonResult<AdminBO> addAdmin(Integer adminId, AdminAddDTO adminAddDTO) {
// 校验账号唯一
if (adminMapper.selectByUsername(adminAddDTO.getUsername()) != null) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_USERNAME_EXISTS.getCode());
}
// 保存到数据库
AdminDO admin = AdminConvert.INSTANCE.convert(adminAddDTO)
.setPassword(encodePassword(adminAddDTO.getPassword())) // 加密密码
.setStatus(CommonStatusEnum.ENABLE.getValue());
admin.setCreateTime(new Date());
admin.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
adminMapper.insert(admin);
// TODO 插入操作日志
// 返回成功
return CommonResult.success(AdminConvert.INSTANCE.convert(admin));
}
@Override
public CommonResult<Boolean> updateAdmin(Integer adminId, AdminUpdateDTO adminUpdateDTO) {
// 校验账号存在
if (adminMapper.selectById(adminUpdateDTO.getId()) == null) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode());
}
// 校验账号唯一
AdminDO usernameAdmin = adminMapper.selectByUsername(adminUpdateDTO.getUsername());
if (usernameAdmin != null && !usernameAdmin.getId().equals(adminUpdateDTO.getId())) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_USERNAME_EXISTS.getCode());
}
// 更新到数据库
AdminDO updateAdmin = AdminConvert.INSTANCE.convert(adminUpdateDTO);
adminMapper.update(updateAdmin);
// TODO 插入操作日志
// 返回成功
return CommonResult.success(true);
}
@Override
@Transactional
public CommonResult<Boolean> updateAdminStatus(Integer adminId, Integer updateAdminId, Integer status) {
// 校验账号存在
AdminDO admin = adminMapper.selectById(updateAdminId);
if (admin == null) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode());
}
if (AdminConstants.USERNAME_ADMIN.equals(admin.getUsername())) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_ADMIN_STATUS_CAN_NOT_UPDATE.getCode());
}
// 如果状态相同,则返回错误
if (status.equals(admin.getStatus())) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_STATUS_EQUALS.getCode());
}
// 更新管理员状态
AdminDO updateAdmin = new AdminDO().setId(updateAdminId).setStatus(status);
adminMapper.update(updateAdmin);
// 如果是关闭管理员,则标记 token 失效。否则,管理员还可以继续蹦跶
if (CommonStatusEnum.DISABLE.getValue().equals(status)) {
oAuth2Service.removeToken(updateAdminId);
}
// TODO 插入操作日志
// 返回成功
return CommonResult.success(true);
}
@Override
@Transactional
public CommonResult<Boolean> deleteAdmin(Integer adminId, Integer updateAdminId) {
// 校验账号存在
AdminDO admin = adminMapper.selectById(updateAdminId);
if (admin == null) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode());
}
// 只有禁用的账号才可以删除
if (CommonStatusEnum.ENABLE.getValue().equals(admin.getStatus())) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_DELETE_ONLY_DISABLE.getCode());
}
// 标记删除 AdminDO
AdminDO updateAdmin = new AdminDO().setId(updateAdminId);
updateAdmin.setDeleted(DeletedStatusEnum.DELETED_YES.getValue());
adminMapper.update(updateAdmin);
// 标记删除 AdminRole
adminRoleMapper.updateToDeletedByAdminId(updateAdminId);
// TODO 插入操作日志
// 返回成功
return CommonResult.success(true);
}
@Override
@Transactional
public CommonResult<Boolean> assignRole(Integer adminId, Integer updateAdminId, Set<Integer> roleIds) {
// 校验账号存在
AdminDO admin = adminMapper.selectById(updateAdminId);
if (admin == null) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode());
}
// 校验是否有不存在的角色
List<RoleDO> roles = roleService.getRoles(roleIds);
if (roles.size() != roleIds.size()) {
return ServiceExceptionUtil.error(AdminErrorCodeEnum.ROLE_ASSIGN_RESOURCE_NOT_EXISTS.getCode());
}
// TODO 芋艿,这里先简单实现。即方式是,删除老的分配的角色关系,然后添加新的分配的角色关系
// 标记管理员角色源关系都为删除
adminRoleMapper.updateToDeletedByAdminId(updateAdminId);
// 创建 RoleResourceDO 数组,并插入到数据库
if (!roleIds.isEmpty()) {
List<AdminRoleDO> adminRoleDOs = roleIds.stream().map(roleId -> {
AdminRoleDO roleResource = new AdminRoleDO().setAdminId(updateAdminId).setRoleId(roleId);
roleResource.setCreateTime(new Date());
roleResource.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
return roleResource;
}).collect(Collectors.toList());
adminRoleMapper.insertList(adminRoleDOs);
}
// TODO 插入操作日志
// 返回成功
return CommonResult.success(true);
}
private String encodePassword(String password) {
return DigestUtils.md5DigestAsHex(password.getBytes());
}
}

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