This commit is contained in:
18796357645 2025-05-20 00:33:42 +08:00
parent 12f367e96e
commit 867e86bd7b
96 changed files with 1219 additions and 5104 deletions

View File

@ -38,10 +38,8 @@ public class Oauth2Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
UserDetail user = (UserDetail) principals.getPrimaryPrincipal();
//用户权限列表
Set<String> permsSet = shiroService.getUserPermissions(user);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permsSet);
return info;
@ -63,9 +61,6 @@ public class Oauth2Realm extends AuthorizingRealm {
SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId());
//转换成UserDetail对象
UserDetail userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class);
//获取用户对应的部门数据权限
List<Long> deptIdList = shiroService.getDataScopeList(userDetail.getId());
userDetail.setDeptIdList(deptIdList);
//账号锁定
if (userDetail.getStatus() == 0) {
throw new LockedAccountException("账号已被锁定!");

View File

@ -28,10 +28,5 @@ public interface ShiroService {
*/
SysUserEntity getUser(Long userId);
/**
* 获取用户对应的部门数据权限
* @param userId 用户ID
* @return 返回部门ID列表
*/
List<Long> getDataScopeList(Long userId);
}

View File

@ -1,5 +1,3 @@
package io.modules.security.service.impl;
import cn.hutool.core.util.StrUtil;
@ -7,8 +5,6 @@ import io.modules.security.dao.SysUserTokenDao;
import io.modules.security.entity.SysUserTokenEntity;
import io.modules.security.service.ShiroService;
import io.modules.security.user.UserDetail;
import io.modules.sys.dao.SysMenuDao;
import io.modules.sys.dao.SysRoleDataScopeDao;
import io.modules.sys.dao.SysUserDao;
import io.modules.sys.entity.SysUserEntity;
import io.modules.sys.enums.SuperAdminEnum;
@ -23,30 +19,14 @@ import java.util.Set;
@Service
@AllArgsConstructor
public class ShiroServiceImpl implements ShiroService {
private final SysMenuDao sysMenuDao;
private final SysUserDao sysUserDao;
private final SysUserTokenDao sysUserTokenDao;
private final SysRoleDataScopeDao sysRoleDataScopeDao;
@Override
public Set<String> getUserPermissions(UserDetail user) {
//系统管理员拥有最高权限
List<String> permissionsList;
if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) {
permissionsList = sysMenuDao.getPermissionsList();
} else {
permissionsList = sysMenuDao.getUserPermissionsList(user.getId());
}
//用户权限列表
Set<String> permsSet = new HashSet<>();
for (String permissions : permissionsList) {
if (StrUtil.isBlank(permissions)) {
continue;
}
permsSet.addAll(Arrays.asList(permissions.trim().split(",")));
}
return permsSet;
}
@ -60,8 +40,5 @@ public class ShiroServiceImpl implements ShiroService {
return sysUserDao.selectById(userId);
}
@Override
public List<Long> getDataScopeList(Long userId) {
return sysRoleDataScopeDao.getDataScopeList(userId);
}
}

View File

@ -41,8 +41,7 @@ public class CertificatesController {
private SetRequestService setRequestService;
@Autowired
private FrontUserService userService;
@Value("${upload.url}")
private String uploadUrl;
@GetMapping("page")
@Operation(summary = "分页")
@ -55,8 +54,6 @@ public class CertificatesController {
public Result<PageData<CertificatesDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params){
PageData<CertificatesDTO> page = certificatesService.page(params);
List<CertificatesDTO> collect = page.getList().stream().map(e -> {
// 拼接域名
e.setImg(uploadUrl + e.getImg());
Long userId = e.getUserId();
UserDTO userDTO = userService.get(userId);
e.setUser(userDTO);
@ -71,8 +68,7 @@ public class CertificatesController {
public Result save(@RequestBody CertificatesDTO dto){
//效验数据
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
dto.setImg(dto.getImg().replace(uploadUrl,""));
certificatesService.save(dto);
return new Result();
}
@ -81,7 +77,7 @@ public class CertificatesController {
public Result update(@RequestBody CertificatesDTO dto){
//效验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
dto.setImg(dto.getImg().replace(uploadUrl,""));
certificatesService.update(dto);
return new Result();
}
@ -91,10 +87,9 @@ public class CertificatesController {
public Result book(@RequestBody CertificatesDTO dto){
//效验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
dto.setImg(dto.getImg().replace(uploadUrl,""));
SetResponse setResponse = setRequestService.sendSetRequest(String.valueOf(dto.getId()), dto.toString());
dto.setBlockchainTxId(setResponse.getData());
dto.setIsBlock(1);
dto.setHex(setResponse.getData());
certificatesService.update(dto);
return new Result();
}

View File

@ -1,92 +0,0 @@
package io.modules.sys.controller;
import io.common.annotation.LogOperation;
import io.common.utils.Result;
import io.common.validator.AssertUtils;
import io.common.validator.ValidatorUtils;
import io.common.validator.group.AddGroup;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.modules.sys.dto.SysDeptDTO;
import io.modules.sys.service.SysDeptService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
/**
* 部门管理
*
*/
@RestController
@RequestMapping("/sys/dept")
@Tag(name = "部门管理")
@AllArgsConstructor
public class SysDeptController {
private final SysDeptService sysDeptService;
@GetMapping("list")
@Operation(summary = "列表")
@RequiresPermissions("sys:dept:list")
public Result<List<SysDeptDTO>> list() {
List<SysDeptDTO> list = sysDeptService.list(new HashMap<>(1));
return new Result<List<SysDeptDTO>>().ok(list);
}
@GetMapping("{id}")
@Operation(summary = "信息")
@RequiresPermissions("sys:dept:info")
public Result<SysDeptDTO> get(@PathVariable("id") Long id) {
SysDeptDTO data = sysDeptService.get(id);
return new Result<SysDeptDTO>().ok(data);
}
@PostMapping
@Operation(summary = "保存")
@LogOperation("保存")
@RequiresPermissions("sys:dept:save")
public Result save(@RequestBody SysDeptDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
sysDeptService.save(dto);
return new Result();
}
@PutMapping
@Operation(summary = "修改")
@LogOperation("修改")
@RequiresPermissions("sys:dept:update")
public Result update(@RequestBody SysDeptDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
sysDeptService.update(dto);
return new Result();
}
@DeleteMapping("{id}")
@Operation(summary = "删除")
@LogOperation("删除")
@RequiresPermissions("sys:dept:delete")
public Result delete(@PathVariable("id") Long id) {
//效验数据
AssertUtils.isNull(id, "id");
sysDeptService.delete(id);
return new Result();
}
}

View File

@ -1,104 +0,0 @@
package io.modules.sys.controller;
import io.common.annotation.LogOperation;
import io.common.constant.Constant;
import io.common.page.PageData;
import io.common.utils.Result;
import io.common.validator.AssertUtils;
import io.common.validator.ValidatorUtils;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.modules.sys.dto.SysDictDataDTO;
import io.modules.sys.service.SysDictDataService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 字典数据
*
*/
@RestController
@RequestMapping("sys/dict/data")
@Tag(name = "字典数据")
@AllArgsConstructor
public class SysDictDataController {
private final SysDictDataService sysDictDataService;
@GetMapping("page")
@Operation(summary = "字典数据")
@Parameters({
@Parameter(name = Constant.PAGE, description = "当前页码从1开始", in = ParameterIn.QUERY, required = true, ref = "int"),
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"),
@Parameter(name = "dictLabel", description = "字典标签", in = ParameterIn.QUERY, ref = "String"),
@Parameter(name = "dictValue", description = "字典值", in = ParameterIn.QUERY, ref = "String")
})
@RequiresPermissions("sys:dict:page")
public Result<PageData<SysDictDataDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
//字典类型
PageData<SysDictDataDTO> page = sysDictDataService.page(params);
return new Result<PageData<SysDictDataDTO>>().ok(page);
}
@GetMapping("{id}")
@Operation(summary = "信息")
@RequiresPermissions("sys:dict:info")
public Result<SysDictDataDTO> get(@PathVariable("id") Long id) {
SysDictDataDTO data = sysDictDataService.get(id);
return new Result<SysDictDataDTO>().ok(data);
}
@PostMapping
@Operation(summary = "保存")
@LogOperation("保存")
@RequiresPermissions("sys:dict:save")
public Result save(@RequestBody SysDictDataDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, DefaultGroup.class);
sysDictDataService.save(dto);
return new Result();
}
@PutMapping
@Operation(summary = "修改")
@LogOperation("修改")
@RequiresPermissions("sys:dict:update")
public Result update(@RequestBody SysDictDataDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
sysDictDataService.update(dto);
return new Result();
}
@DeleteMapping
@Operation(summary = "删除")
@LogOperation("删除")
@RequiresPermissions("sys:dict:delete")
public Result delete(@RequestBody Long[] ids) {
//效验数据
AssertUtils.isArrayEmpty(ids, "id");
sysDictDataService.delete(ids);
return new Result();
}
}

View File

@ -1,112 +0,0 @@
package io.modules.sys.controller;
import io.common.annotation.LogOperation;
import io.common.constant.Constant;
import io.common.page.PageData;
import io.common.utils.Result;
import io.common.validator.AssertUtils;
import io.common.validator.ValidatorUtils;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.modules.sys.dto.SysDictTypeDTO;
import io.modules.sys.entity.DictType;
import io.modules.sys.service.SysDictTypeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 字典类型
*/
@RestController
@RequestMapping("sys/dict/type")
@Tag(name = "字典类型")
@AllArgsConstructor
public class SysDictTypeController {
private final SysDictTypeService sysDictTypeService;
@GetMapping("page")
@Operation(summary = "字典类型")
@Parameters({
@Parameter(name = Constant.PAGE, description = "当前页码从1开始", in = ParameterIn.QUERY, required = true, ref = "int"),
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"),
@Parameter(name = "dictType", description = "字典类型", in = ParameterIn.QUERY, ref = "String"),
@Parameter(name = "dictName", description = "字典名称", in = ParameterIn.QUERY, ref = "String")
})
@RequiresPermissions("sys:dict:page")
public Result<PageData<SysDictTypeDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
//字典类型
PageData<SysDictTypeDTO> page = sysDictTypeService.page(params);
return new Result<PageData<SysDictTypeDTO>>().ok(page);
}
@GetMapping("{id}")
@Operation(summary = "信息")
@RequiresPermissions("sys:dict:info")
public Result<SysDictTypeDTO> get(@PathVariable("id") Long id) {
SysDictTypeDTO data = sysDictTypeService.get(id);
return new Result<SysDictTypeDTO>().ok(data);
}
@PostMapping
@Operation(summary = "保存")
@LogOperation("保存")
@RequiresPermissions("sys:dict:save")
public Result save(@RequestBody SysDictTypeDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, DefaultGroup.class);
sysDictTypeService.save(dto);
return new Result();
}
@PutMapping
@Operation(summary = "修改")
@LogOperation("修改")
@RequiresPermissions("sys:dict:update")
public Result update(@RequestBody SysDictTypeDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
sysDictTypeService.update(dto);
return new Result();
}
@DeleteMapping
@Operation(summary = "删除")
@LogOperation("删除")
@RequiresPermissions("sys:dict:delete")
public Result delete(@RequestBody Long[] ids) {
//效验数据
AssertUtils.isArrayEmpty(ids, "id");
sysDictTypeService.delete(ids);
return new Result();
}
@GetMapping("all")
@Operation(summary = "所有字典数据")
public Result<List<DictType>> all() {
List<DictType> list = sysDictTypeService.getAllList();
return new Result<List<DictType>>().ok(list);
}
}

View File

@ -1,132 +0,0 @@
package io.modules.sys.controller;
import io.modules.security.service.ShiroService;
import io.modules.security.user.SecurityUser;
import io.modules.security.user.UserDetail;
import io.common.annotation.LogOperation;
import io.common.exception.ErrorCode;
import io.common.utils.Result;
import io.common.validator.AssertUtils;
import io.common.validator.ValidatorUtils;
import io.common.validator.group.DefaultGroup;
import io.modules.sys.dto.SysMenuDTO;
import io.modules.sys.enums.MenuTypeEnum;
import io.modules.sys.service.SysMenuService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Set;
/**
* 菜单管理
*
*/
@RestController
@RequestMapping("/sys/menu")
@Tag(name = "菜单管理")
@AllArgsConstructor
public class SysMenuController {
private final SysMenuService sysMenuService;
private final ShiroService shiroService;
@GetMapping("nav")
@Operation(summary = "导航")
public Result<List<SysMenuDTO>> nav() {
UserDetail user = SecurityUser.getUser();
List<SysMenuDTO> list = sysMenuService.getUserMenuList(user, MenuTypeEnum.MENU.value());
return new Result<List<SysMenuDTO>>().ok(list);
}
@GetMapping("permissions")
@Operation(summary = "权限标识")
public Result<Set<String>> permissions() {
UserDetail user = SecurityUser.getUser();
Set<String> set = shiroService.getUserPermissions(user);
return new Result<Set<String>>().ok(set);
}
@GetMapping("list")
@Operation(summary = "列表")
@Parameter(name = "type", description = "菜单类型 0菜单 1按钮 null全部", in = ParameterIn.QUERY, ref = "int")
@RequiresPermissions("sys:menu:list")
public Result<List<SysMenuDTO>> list(Integer type) {
List<SysMenuDTO> list = sysMenuService.getAllMenuList(type);
return new Result<List<SysMenuDTO>>().ok(list);
}
@GetMapping("{id}")
@Operation(summary = "信息")
@RequiresPermissions("sys:menu:info")
public Result<SysMenuDTO> get(@PathVariable("id") Long id) {
SysMenuDTO data = sysMenuService.get(id);
return new Result<SysMenuDTO>().ok(data);
}
@PostMapping
@Operation(summary = "保存")
@LogOperation("保存")
@RequiresPermissions("sys:menu:save")
public Result save(@RequestBody SysMenuDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, DefaultGroup.class);
sysMenuService.save(dto);
return new Result();
}
@PutMapping
@Operation(summary = "修改")
@LogOperation("修改")
@RequiresPermissions("sys:menu:update")
public Result update(@RequestBody SysMenuDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, DefaultGroup.class);
sysMenuService.update(dto);
return new Result();
}
@DeleteMapping("{id}")
@Operation(summary = "删除")
@LogOperation("删除")
@RequiresPermissions("sys:menu:delete")
public Result delete(@PathVariable("id") Long id) {
//效验数据
AssertUtils.isNull(id, "id");
//判断是否有子菜单或按钮
List<SysMenuDTO> list = sysMenuService.getListPid(id);
if (list.size() > 0) {
return new Result().error("先删除子菜单或按钮!");
}
sysMenuService.delete(id);
return new Result();
}
@GetMapping("select")
@Operation(summary = "角色菜单权限")
@RequiresPermissions("sys:menu:select")
public Result<List<SysMenuDTO>> select() {
UserDetail user = SecurityUser.getUser();
List<SysMenuDTO> list = sysMenuService.getUserMenuList(user, null);
return new Result<List<SysMenuDTO>>().ok(list);
}
}

View File

@ -1,120 +0,0 @@
package io.modules.sys.controller;
import io.common.annotation.LogOperation;
import io.common.constant.Constant;
import io.common.page.PageData;
import io.common.utils.ExcelUtils;
import io.common.utils.Result;
import io.common.validator.AssertUtils;
import io.common.validator.ValidatorUtils;
import io.common.validator.group.AddGroup;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.modules.sys.dto.SysParamsDTO;
import io.modules.sys.excel.SysParamsExcel;
import io.modules.sys.service.SysParamsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 参数管理
*
* @since 1.0.0
*/
@RestController
@RequestMapping("sys/params")
@Tag(name = "参数管理")
@AllArgsConstructor
public class SysParamsController {
private final SysParamsService sysParamsService;
@GetMapping("page")
@Operation(summary = "分页")
@Parameters({
@Parameter(name = Constant.PAGE, description = "当前页码从1开始", in = ParameterIn.QUERY, required = true, ref = "int"),
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"),
@Parameter(name = "paramCode", description = "参数编码", in = ParameterIn.QUERY, ref = "String")
})
@RequiresPermissions("sys:params:page")
public Result<PageData<SysParamsDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
PageData<SysParamsDTO> page = sysParamsService.page(params);
return new Result<PageData<SysParamsDTO>>().ok(page);
}
@GetMapping("{id}")
@Operation(summary = "信息")
@RequiresPermissions("sys:params:info")
public Result<SysParamsDTO> get(@PathVariable("id") Long id) {
SysParamsDTO data = sysParamsService.get(id);
return new Result<SysParamsDTO>().ok(data);
}
@PostMapping
@Operation(summary = "保存")
@LogOperation("保存")
@RequiresPermissions("sys:params:save")
public Result save(@RequestBody SysParamsDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
sysParamsService.save(dto);
return new Result();
}
@PutMapping
@Operation(summary = "修改")
@LogOperation("修改")
@RequiresPermissions("sys:params:update")
public Result update(@RequestBody SysParamsDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
sysParamsService.update(dto);
return new Result();
}
@DeleteMapping
@Operation(summary = "删除")
@LogOperation("删除")
@RequiresPermissions("sys:params:delete")
public Result delete(@RequestBody Long[] ids) {
//效验数据
AssertUtils.isArrayEmpty(ids, "id");
sysParamsService.delete(ids);
return new Result();
}
@GetMapping("export")
@Operation(summary = "导出")
@LogOperation("导出")
@RequiresPermissions("sys:params:export")
@Parameter(name = "paramCode", description = "参数编码", in = ParameterIn.QUERY, ref = "String")
public void export(@Parameter(hidden = true) @RequestParam Map<String, Object> params, HttpServletResponse response) throws Exception {
List<SysParamsDTO> list = sysParamsService.list(params);
ExcelUtils.exportExcelToTarget(response, null, "参数管理", list, SysParamsExcel.class);
}
}

View File

@ -1,125 +0,0 @@
package io.modules.sys.controller;
import io.common.annotation.LogOperation;
import io.common.constant.Constant;
import io.common.page.PageData;
import io.common.utils.Result;
import io.common.validator.AssertUtils;
import io.common.validator.ValidatorUtils;
import io.common.validator.group.AddGroup;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.modules.sys.dto.SysRoleDTO;
import io.modules.sys.service.SysRoleDataScopeService;
import io.modules.sys.service.SysRoleMenuService;
import io.modules.sys.service.SysRoleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 角色管理
*
*/
@RestController
@RequestMapping("/sys/role")
@Tag(name = "角色管理")
@AllArgsConstructor
public class SysRoleController {
private final SysRoleService sysRoleService;
private final SysRoleMenuService sysRoleMenuService;
private final SysRoleDataScopeService sysRoleDataScopeService;
@GetMapping("page")
@Operation(summary = "分页")
@Parameters({
@Parameter(name = Constant.PAGE, description = "当前页码从1开始", in = ParameterIn.QUERY, required = true, ref = "int"),
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"),
@Parameter(name = "name", description = "角色名", in = ParameterIn.QUERY, ref = "String")
})
@RequiresPermissions("sys:role:page")
public Result<PageData<SysRoleDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
PageData<SysRoleDTO> page = sysRoleService.page(params);
return new Result<PageData<SysRoleDTO>>().ok(page);
}
@GetMapping("list")
@Operation(summary = "列表")
@RequiresPermissions("sys:role:list")
public Result<List<SysRoleDTO>> list() {
List<SysRoleDTO> data = sysRoleService.list(new HashMap<>(1));
return new Result<List<SysRoleDTO>>().ok(data);
}
@GetMapping("{id}")
@Operation(summary = "信息")
@RequiresPermissions("sys:role:info")
public Result<SysRoleDTO> get(@PathVariable("id") Long id) {
SysRoleDTO data = sysRoleService.get(id);
//查询角色对应的菜单
List<Long> menuIdList = sysRoleMenuService.getMenuIdList(id);
data.setMenuIdList(menuIdList);
//查询角色对应的数据权限
List<Long> deptIdList = sysRoleDataScopeService.getDeptIdList(id);
data.setDeptIdList(deptIdList);
return new Result<SysRoleDTO>().ok(data);
}
@PostMapping
@Operation(summary = "保存")
@LogOperation("保存")
@RequiresPermissions("sys:role:save")
public Result save(@RequestBody SysRoleDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
sysRoleService.save(dto);
return new Result();
}
@PutMapping
@Operation(summary = "修改")
@LogOperation("修改")
@RequiresPermissions("sys:role:update")
public Result update(@RequestBody SysRoleDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
sysRoleService.update(dto);
return new Result();
}
@DeleteMapping
@Operation(summary = "删除")
@LogOperation("删除")
@RequiresPermissions("sys:role:delete")
public Result delete(@RequestBody Long[] ids) {
//效验数据
AssertUtils.isArrayEmpty(ids, "id");
sysRoleService.delete(ids);
return new Result();
}
}

View File

@ -1,35 +0,0 @@
package io.modules.sys.dao;
import io.modules.sys.entity.SysDeptEntity;
import io.common.dao.BaseDao;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
/**
* 部门管理
*
*/
@Mapper
public interface SysDeptDao extends BaseDao<SysDeptEntity> {
List<SysDeptEntity> getList(Map<String, Object> params);
SysDeptEntity getById(Long id);
/**
* 获取所有部门的idpid列表
*/
List<SysDeptEntity> getIdAndPidList();
/**
* 根据部门ID获取所有子部门ID列表
* @param id 部门ID
*/
List<Long> getSubDeptIdList(String id);
}

View File

@ -1,24 +0,0 @@
package io.modules.sys.dao;
import io.modules.sys.entity.DictData;
import io.modules.sys.entity.SysDictDataEntity;
import io.common.dao.BaseDao;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 字典数据
*
*/
@Mapper
public interface SysDictDataDao extends BaseDao<SysDictDataEntity> {
/**
* 字典数据列表
*/
List<DictData> getDictDataList();
}

View File

@ -1,25 +0,0 @@
package io.modules.sys.dao;
import io.modules.sys.entity.DictType;
import io.modules.sys.entity.SysDictTypeEntity;
import io.common.dao.BaseDao;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 字典类型
*
*/
@Mapper
public interface SysDictTypeDao extends BaseDao<SysDictTypeEntity> {
/**
* 字典类型列表
*/
List<DictType> getDictTypeList();
}

View File

@ -1,54 +0,0 @@
package io.modules.sys.dao;
import io.modules.sys.entity.SysMenuEntity;
import io.common.dao.BaseDao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 菜单管理
*
*/
@Mapper
public interface SysMenuDao extends BaseDao<SysMenuEntity> {
SysMenuEntity getById(@Param("id") Long id);
/**
* 查询所有菜单列表
*
* @param menuType 菜单类型
*/
List<SysMenuEntity> getMenuList(@Param("menuType") Integer menuType);
/**
* 查询用户菜单列表
*
* @param userId 用户ID
* @param menuType 菜单类型
*/
List<SysMenuEntity> getUserMenuList(@Param("userId") Long userId, @Param("menuType") Integer menuType);
/**
* 查询用户权限列表
* @param userId 用户ID
*/
List<String> getUserPermissionsList(Long userId);
/**
* 查询所有权限列表
*/
List<String> getPermissionsList();
/**
* 根据父菜单查询子菜单
* @param pid 父菜单ID
*/
List<SysMenuEntity> getListPid(Long pid);
}

View File

@ -1,40 +0,0 @@
package io.modules.sys.dao;
import io.modules.sys.entity.SysParamsEntity;
import io.common.dao.BaseDao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 参数管理
*
* @since 1.0.0
*/
@Mapper
public interface SysParamsDao extends BaseDao<SysParamsEntity> {
/**
* 根据参数编码查询value
* @param paramCode 参数编码
* @return 参数值
*/
String getValueByCode(String paramCode);
/**
* 获取参数编码列表
* @param ids ids
* @return 返回参数编码列表
*/
List<String> getParamCodeList(Long[] ids);
/**
* 根据参数编码更新value
* @param paramCode 参数编码
* @param paramValue 参数值
*/
int updateValueByCode(@Param("paramCode") String paramCode, @Param("paramValue") String paramValue);
}

View File

@ -1,18 +0,0 @@
package io.modules.sys.dao;
import io.modules.sys.entity.SysRoleEntity;
import io.common.dao.BaseDao;
import org.apache.ibatis.annotations.Mapper;
/**
* 角色管理
*
*/
@Mapper
public interface SysRoleDao extends BaseDao<SysRoleEntity> {
}

View File

@ -1,35 +0,0 @@
package io.modules.sys.dao;
import io.modules.sys.entity.SysRoleDataScopeEntity;
import io.common.dao.BaseDao;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 角色数据权限
*
* @since 1.0.0
*/
@Mapper
public interface SysRoleDataScopeDao extends BaseDao<SysRoleDataScopeEntity> {
/**
* 根据角色ID获取部门ID列表
*/
List<Long> getDeptIdList(Long roleId);
/**
* 获取用户的部门数据权限列表
*/
List<Long> getDataScopeList(Long userId);
/**
* 根据角色id删除角色数据权限关系
* @param roleIds 角色ids
*/
void deleteByRoleIds(Long[] roleIds);
}

View File

@ -1,35 +0,0 @@
package io.modules.sys.dao;
import io.modules.sys.entity.SysRoleMenuEntity;
import io.common.dao.BaseDao;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 角色与菜单对应关系
*
*/
@Mapper
public interface SysRoleMenuDao extends BaseDao<SysRoleMenuEntity> {
/**
* 根据角色ID获取菜单ID列表
*/
List<Long> getMenuIdList(Long roleId);
/**
* 根据角色id删除角色菜单关系
* @param roleIds 角色ids
*/
void deleteByRoleIds(Long[] roleIds);
/**
* 根据菜单id删除角色菜单关系
* @param menuId 菜单id
*/
void deleteByMenuId(Long menuId);
}

View File

@ -1,76 +0,0 @@
package io.modules.sys.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.common.utils.TreeNode;
import io.common.validator.group.AddGroup;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* 部门管理
*
* @since 1.0.0
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(title = "部门管理")
public class SysDeptDTO extends TreeNode implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(title = "id")
@Null(message="ID必须为空", groups = AddGroup.class)
@NotNull(message="{id.require}", groups = UpdateGroup.class)
private Long id;
@Schema(title = "上级ID")
@NotNull(message="{sysdept.pid.require}", groups = DefaultGroup.class)
private Long pid;
@Schema(title = "部门名称")
@NotBlank(message="{sysdept.name.require}", groups = DefaultGroup.class)
private String name;
@Schema(title = "排序")
@Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class)
private Integer sort;
@Schema(title = "创建时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date createDate;
@Schema(title = "上级部门名称")
private String parentName;
@Override
public Long getId() {
return id;
}
@Override
public void setId(Long id) {
this.id = id;
}
@Override
public Long getPid() {
return pid;
}
@Override
public void setPid(Long pid) {
this.pid = pid;
}
}

View File

@ -1,59 +0,0 @@
package io.modules.sys.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.common.validator.group.AddGroup;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 字典数据
*
*/
@Data
@Schema(title = "字典数据")
public class SysDictDataDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(title = "id")
@Null(message="ID必须为空", groups = AddGroup.class)
@NotNull(message="{id.require}", groups = UpdateGroup.class)
private Long id;
@Schema(title = "字典类型ID")
@NotNull(message="{sysdict.type.require}", groups = DefaultGroup.class)
private Long dictTypeId;
@Schema(title = "字典标签")
@NotBlank(message="{sysdict.label.require}", groups = DefaultGroup.class)
private String dictLabel;
@Schema(title = "字典值")
private String dictValue;
@Schema(title = "备注")
private String remark;
@Schema(title = "排序")
@Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class)
private Integer sort;
@Schema(title = "创建时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date createDate;
@Schema(title = "更新时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date updateDate;
}

View File

@ -1,56 +0,0 @@
package io.modules.sys.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.common.validator.group.AddGroup;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 字典类型
*
*/
@Data
@Schema(title = "字典类型")
public class SysDictTypeDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(title = "id")
@Null(message="ID必须为空", groups = AddGroup.class)
@NotNull(message="{id.require}", groups = UpdateGroup.class)
private Long id;
@Schema(title = "字典类型")
@NotBlank(message="{sysdict.type.require}", groups = DefaultGroup.class)
private String dictType;
@Schema(title = "字典名称")
@NotBlank(message="{sysdict.name.require}", groups = DefaultGroup.class)
private String dictName;
@Schema(title = "备注")
private String remark;
@Schema(title = "排序")
@Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class)
private Integer sort;
@Schema(title = "创建时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date createDate;
@Schema(title = "更新时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date updateDate;
}

View File

@ -1,91 +0,0 @@
package io.modules.sys.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.common.utils.TreeNode;
import io.common.validator.group.AddGroup;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.validator.constraints.Range;
import java.io.Serializable;
import java.util.Date;
/**
* 菜单管理
*
* @since 1.0.0
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(title = "菜单管理")
public class SysMenuDTO extends TreeNode<SysMenuDTO> implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(title = "id")
@Null(message="ID必须为空", groups = AddGroup.class)
@NotNull(message="{id.require}", groups = UpdateGroup.class)
private Long id;
@Schema(title = "上级ID")
@NotNull(message="{sysmenu.pid.require}", groups = DefaultGroup.class)
private Long pid;
@Schema(title = "菜单名称")
@NotBlank(message="菜单名称不能为空", groups = DefaultGroup.class)
private String name;
@Schema(title = "菜单URL")
private String url;
@Schema(title = "类型 0菜单 1按钮")
@Range(min=0, max=1, message = "{sysmenu.type.range}", groups = DefaultGroup.class)
private Integer menuType;
@Schema(title = "菜单图标")
private String icon;
@Schema(title = "授权(多个用逗号分隔sys:user:list,sys:user:save)")
private String permissions;
@Schema(title = "排序")
@Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class)
private Integer sort;
@Schema(title = "创建时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date createDate;
@Schema(title = "上级菜单名称")
private String parentName;
@Override
public Long getId() {
return id;
}
@Override
public void setId(Long id) {
this.id = id;
}
@Override
public Long getPid() {
return pid;
}
@Override
public void setPid(Long pid) {
this.pid = pid;
}
}

View File

@ -1,53 +0,0 @@
package io.modules.sys.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.common.validator.group.AddGroup;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 参数管理
*
* @since 1.0.0
*/
@Data
@Schema(title = "参数管理")
public class SysParamsDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(title = "id")
@Null(message="ID必须为空", groups = AddGroup.class)
@NotNull(message="{id.require}", groups = UpdateGroup.class)
private Long id;
@Schema(title = "参数编码")
@NotBlank(message="{sysparams.paramcode.require}", groups = DefaultGroup.class)
private String paramCode;
@Schema(title = "参数值")
@NotBlank(message="{sysparams.paramvalue.require}", groups = DefaultGroup.class)
private String paramValue;
@Schema(title = "备注")
private String remark;
@Schema(title = "创建时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date createDate;
@Schema(title = "更新时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date updateDate;
}

View File

@ -1,52 +0,0 @@
package io.modules.sys.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.common.validator.group.AddGroup;
import io.common.validator.group.DefaultGroup;
import io.common.validator.group.UpdateGroup;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 角色管理
*
* @since 1.0.0
*/
@Data
@Schema(title = "角色管理")
public class SysRoleDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(title = "id")
@Null(message="ID必须为空", groups = AddGroup.class)
@NotNull(message="{id.require}", groups = UpdateGroup.class)
private Long id;
@Schema(title = "角色名称")
@NotBlank(message="{sysrole.name.require}", groups = DefaultGroup.class)
private String name;
@Schema(title = "备注")
private String remark;
@Schema(title = "创建时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date createDate;
@Schema(title = "菜单ID列表")
private List<Long> menuIdList;
@Schema(title = "部门ID列表")
private List<Long> deptIdList;
}

View File

@ -1,42 +0,0 @@
package io.modules.sys.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 系统数据
*
* @since 1.0.0
*/
@Data
@Schema(title = "系统数据")
public class SystemDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Long sysTime;
private String osName;
private String osArch;
private String osVersion;
private String userLanguage;
private String userDir;
private Long totalPhysical;
private Long freePhysical;
private BigDecimal memoryRate;
private Integer processors;
private String jvmName;
private String javaVersion;
private String javaHome;
private Long javaTotalMemory;
private Long javaFreeMemory;
private Long javaMaxMemory;
private String userName;
private BigDecimal systemCpuLoad;
private String userTimezone;
}

View File

@ -1,19 +0,0 @@
package io.modules.sys.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
/**
* 字典数据
*
*/
@Data
public class DictData {
@JsonIgnore
private Long dictTypeId;
private String dictLabel;
private String dictValue;
}

View File

@ -1,20 +0,0 @@
package io.modules.sys.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 字典类型
*
*/
@Data
public class DictType {
@JsonIgnore
private Long id;
private String dictType;
private List<DictData> dataList = new ArrayList<>();
}

View File

@ -1,55 +0,0 @@
package io.modules.sys.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 部门管理
*
*/
@Data
@EqualsAndHashCode(callSuper=false)
@TableName("sys_dept")
public class SysDeptEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 上级ID
*/
private Long pid;
/**
* 所有上级ID用逗号分开
*/
private String pids;
/**
* 部门名称
*/
private String name;
/**
* 排序
*/
private Integer sort;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
/**
* 上级部门名称
*/
@TableField(exist = false)
private String parentName;
}

View File

@ -1,54 +0,0 @@
package io.modules.sys.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 数据字典
*
*/
@Data
@EqualsAndHashCode(callSuper=false)
@TableName("sys_dict_data")
public class SysDictDataEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 字典类型ID
*/
private Long dictTypeId;
/**
* 字典标签
*/
private String dictLabel;
/**
* 字典值
*/
private String dictValue;
/**
* 备注
*/
private String remark;
/**
* 排序
*/
private Integer sort;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
}

View File

@ -1,50 +0,0 @@
package io.modules.sys.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 字典类型
*
*/
@Data
@EqualsAndHashCode(callSuper=false)
@TableName("sys_dict_type")
public class SysDictTypeEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 字典类型
*/
private String dictType;
/**
* 字典名称
*/
private String dictName;
/**
* 备注
*/
private String remark;
/**
* 排序
*/
private Integer sort;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
}

View File

@ -1,69 +0,0 @@
package io.modules.sys.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 菜单管理
*
*/
@Data
@EqualsAndHashCode(callSuper=false)
@TableName("sys_menu")
public class SysMenuEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 父菜单ID一级菜单为0
*/
private Long pid;
/**
* 菜单名称
*/
private String name;
/**
* 菜单URL
*/
private String url;
/**
* 授权(多个用逗号分隔sys:user:list,sys:user:save)
*/
private String permissions;
/**
* 类型 0菜单 1按钮
*/
private Integer menuType;
/**
* 菜单图标
*/
private String icon;
/**
* 排序
*/
private Integer sort;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
/**
* 上级菜单名称
*/
@TableField(exist = false)
private String parentName;
}

View File

@ -1,53 +0,0 @@
package io.modules.sys.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 参数管理
*
* @since 1.0.0
*/
@Data
@EqualsAndHashCode(callSuper=false)
@TableName("sys_params")
public class SysParamsEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 参数编码
*/
private String paramCode;
/**
* 参数值
*/
private String paramValue;
/**
* 类型 0系统参数 1非系统参数
*/
private Integer paramType;
/**
* 备注
*/
private String remark;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
}

View File

@ -1,31 +0,0 @@
package io.modules.sys.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 角色数据权限
*
* @since 1.0.0
*/
@Data
@EqualsAndHashCode(callSuper=false)
@TableName("sys_role_data_scope")
public class SysRoleDataScopeEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 角色ID
*/
private Long roleId;
/**
* 部门ID
*/
private Long deptId;
}

View File

@ -1,48 +0,0 @@
package io.modules.sys.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 角色
*
*/
@Data
@EqualsAndHashCode(callSuper=false)
@TableName("sys_role")
public class SysRoleEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 角色名称
*/
private String name;
/**
* 备注
*/
private String remark;
/**
* 部门ID
*/
@TableField(fill = FieldFill.INSERT)
private Long deptId;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
}

View File

@ -1,30 +0,0 @@
package io.modules.sys.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 角色菜单关系
*
* @since 1.0.0
*/
@Data
@EqualsAndHashCode(callSuper=false)
@TableName("sys_role_menu")
public class SysRoleMenuEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 角色ID
*/
private Long roleId;
/**
* 菜单ID
*/
private Long menuId;
}

View File

@ -1,34 +0,0 @@
package io.modules.sys.service;
import io.common.service.BaseService;
import io.modules.sys.dto.SysDeptDTO;
import io.modules.sys.entity.SysDeptEntity;
import java.util.List;
import java.util.Map;
/**
* 部门管理
*
*/
public interface SysDeptService extends BaseService<SysDeptEntity> {
List<SysDeptDTO> list(Map<String, Object> params);
SysDeptDTO get(Long id);
void save(SysDeptDTO dto);
void update(SysDeptDTO dto);
void delete(Long id);
/**
* 根据部门ID获取本部门及子部门ID列表
* @param id 部门ID
*/
List<Long> getSubDeptIdList(Long id);
}

View File

@ -1,29 +0,0 @@
package io.modules.sys.service;
import io.common.page.PageData;
import io.common.service.BaseService;
import io.modules.sys.dto.SysDictDataDTO;
import io.modules.sys.entity.SysDictDataEntity;
import java.util.Map;
/**
* 数据字典
*
*/
public interface SysDictDataService extends BaseService<SysDictDataEntity> {
PageData<SysDictDataDTO> page(Map<String, Object> params);
SysDictDataDTO get(Long id);
void save(SysDictDataDTO dto);
void update(SysDictDataDTO dto);
void delete(Long[] ids);
}

View File

@ -1,36 +0,0 @@
package io.modules.sys.service;
import io.common.page.PageData;
import io.common.service.BaseService;
import io.modules.sys.dto.SysDictTypeDTO;
import io.modules.sys.entity.DictType;
import io.modules.sys.entity.SysDictTypeEntity;
import java.util.List;
import java.util.Map;
/**
* 数据字典
*
*/
public interface SysDictTypeService extends BaseService<SysDictTypeEntity> {
PageData<SysDictTypeDTO> page(Map<String, Object> params);
SysDictTypeDTO get(Long id);
void save(SysDictTypeDTO dto);
void update(SysDictTypeDTO dto);
void delete(Long[] ids);
/**
* 获取所有字典
*/
List<DictType> getAllList();
}

View File

@ -1,48 +0,0 @@
package io.modules.sys.service;
import io.modules.security.user.UserDetail;
import io.common.service.BaseService;
import io.modules.sys.dto.SysMenuDTO;
import io.modules.sys.entity.SysMenuEntity;
import java.util.List;
/**
* 菜单管理
*
*/
public interface SysMenuService extends BaseService<SysMenuEntity> {
SysMenuDTO get(Long id);
void save(SysMenuDTO dto);
void update(SysMenuDTO dto);
void delete(Long id);
/**
* 菜单列表
*
* @param menuType 菜单类型
*/
List<SysMenuDTO> getAllMenuList(Integer menuType);
/**
* 用户菜单列表
*
* @param user 用户
* @param menuType 菜单类型
*/
List<SysMenuDTO> getUserMenuList(UserDetail user, Integer menuType);
/**
* 根据父菜单查询子菜单
* @param pid 父菜单ID
*/
List<SysMenuDTO> getListPid(Long pid);
}

View File

@ -1,50 +0,0 @@
package io.modules.sys.service;
import io.common.page.PageData;
import io.common.service.BaseService;
import io.modules.sys.dto.SysParamsDTO;
import io.modules.sys.entity.SysParamsEntity;
import java.util.List;
import java.util.Map;
/**
* 参数管理
*
* @since 1.0.0
*/
public interface SysParamsService extends BaseService<SysParamsEntity> {
PageData<SysParamsDTO> page(Map<String, Object> params);
List<SysParamsDTO> list(Map<String, Object> params);
SysParamsDTO get(Long id);
void save(SysParamsDTO dto);
void update(SysParamsDTO dto);
void delete(Long[] ids);
/**
* 根据参数编码获取参数的value值
*
* @param paramCode 参数编码
*/
String getValue(String paramCode);
/**
* 根据参数编码获取value的Object对象
* @param paramCode 参数编码
* @param clazz Object对象
*/
<T> T getValueObject(String paramCode, Class<T> clazz);
/**
* 根据参数编码更新value
* @param paramCode 参数编码
* @param paramValue 参数值
*/
int updateValueByCode(String paramCode, String paramValue);
}

View File

@ -1,35 +0,0 @@
package io.modules.sys.service;
import io.common.service.BaseService;
import io.modules.sys.entity.SysRoleDataScopeEntity;
import java.util.List;
/**
* 角色数据权限
*
* @since 1.0.0
*/
public interface SysRoleDataScopeService extends BaseService<SysRoleDataScopeEntity> {
/**
* 根据角色ID获取部门ID列表
*/
List<Long> getDeptIdList(Long roleId);
/**
* 保存或修改
* @param roleId 角色ID
* @param deptIdList 部门ID列表
*/
void saveOrUpdate(Long roleId, List<Long> deptIdList);
/**
* 根据角色id删除角色数据权限关系
* @param roleId 角色ids
*/
void deleteByRoleIds(Long[] roleId);
}

View File

@ -1,41 +0,0 @@
package io.modules.sys.service;
import io.common.service.BaseService;
import io.modules.sys.entity.SysRoleMenuEntity;
import java.util.List;
/**
* 角色与菜单对应关系
*
*/
public interface SysRoleMenuService extends BaseService<SysRoleMenuEntity> {
/**
* 根据角色ID获取菜单ID列表
*/
List<Long> getMenuIdList(Long roleId);
/**
* 保存或修改
* @param roleId 角色ID
* @param menuIdList 菜单ID列表
*/
void saveOrUpdate(Long roleId, List<Long> menuIdList);
/**
* 根据角色id删除角色菜单关系
* @param roleIds 角色ids
*/
void deleteByRoleIds(Long[] roleIds);
/**
* 根据菜单id删除角色菜单关系
* @param menuId 菜单id
*/
void deleteByMenuId(Long menuId);
}

View File

@ -1,34 +0,0 @@
package io.modules.sys.service;
import io.common.page.PageData;
import io.common.service.BaseService;
import io.modules.sys.dto.SysRoleDTO;
import io.modules.sys.entity.SysRoleEntity;
import java.util.List;
import java.util.Map;
/**
* 角色
*
*/
public interface SysRoleService extends BaseService<SysRoleEntity> {
PageData<SysRoleDTO> page(Map<String, Object> params);
List<SysRoleDTO> list(Map<String, Object> params);
SysRoleDTO get(Long id);
void save(SysRoleDTO dto);
void update(SysRoleDTO dto);
void delete(Long[] ids);
}

View File

@ -9,9 +9,6 @@ import java.util.List;
/**
* 角色用户关系
*
* @since 1.0.0
*/
public interface SysRoleUserService extends BaseService<SysRoleUserEntity> {

View File

@ -1,160 +0,0 @@
package io.modules.sys.service.impl;
import com.qiniu.util.StringUtils;
import io.modules.security.user.SecurityUser;
import io.modules.security.user.UserDetail;
import io.common.constant.Constant;
import io.common.exception.ErrorCode;
import io.common.exception.RenException;
import io.common.service.impl.BaseServiceImpl;
import io.common.utils.ConvertUtils;
import io.common.utils.TreeUtils;
import io.modules.sys.dao.SysDeptDao;
import io.modules.sys.dao.SysUserDao;
import io.modules.sys.dto.SysDeptDTO;
import io.modules.sys.entity.SysDeptEntity;
import io.modules.sys.enums.SuperAdminEnum;
import io.modules.sys.service.SysDeptService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
@AllArgsConstructor
public class SysDeptServiceImpl extends BaseServiceImpl<SysDeptDao, SysDeptEntity> implements SysDeptService {
private final SysUserDao sysUserDao;
@Override
public List<SysDeptDTO> list(Map<String, Object> params) {
//普通管理员只能查询所属部门及子部门的数据
UserDetail user = SecurityUser.getUser();
if (user.getSuperAdmin() == SuperAdminEnum.NO.value()) {
params.put("deptIdList", getSubDeptIdList(user.getDeptId()));
}
//查询部门列表
List<SysDeptEntity> entityList = baseDao.getList(params);
List<SysDeptDTO> dtoList = ConvertUtils.sourceToTarget(entityList, SysDeptDTO.class);
return TreeUtils.build(dtoList);
}
@Override
public SysDeptDTO get(Long id) {
//超级管理员部门ID为null
if (id == null) {
return null;
}
SysDeptEntity entity = baseDao.getById(id);
return ConvertUtils.sourceToTarget(entity, SysDeptDTO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void save(SysDeptDTO dto) {
SysDeptEntity entity = ConvertUtils.sourceToTarget(dto, SysDeptEntity.class);
entity.setPids(getPidList(entity.getPid()));
insert(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(SysDeptDTO dto) {
SysDeptEntity entity = ConvertUtils.sourceToTarget(dto, SysDeptEntity.class);
//上级部门不能为自身
if (entity.getId().equals(entity.getPid())) {
throw new RenException("上级部门不能为自身!");
}
//上级部门不能为下级部门
List<Long> subDeptList = getSubDeptIdList(entity.getId());
if (subDeptList.contains(entity.getPid())) {
throw new RenException("上级部门不能为下级部门");
}
entity.setPids(getPidList(entity.getPid()));
updateById(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(Long id) {
//判断是否有子部门
List<Long> subList = getSubDeptIdList(id);
if (subList.size() > 1) {
throw new RenException("存在子部门!");
}
//判断部门下面是否有用户
int count = sysUserDao.getCountByDeptId(id);
if (count > 0) {
throw new RenException("部门下面是有用户!");
}
//删除
baseDao.deleteById(id);
}
@Override
public List<Long> getSubDeptIdList(Long id) {
List<Long> deptIdList = baseDao.getSubDeptIdList("%" + id + "%");
deptIdList.add(id);
return deptIdList;
}
/**
* 获取所有上级部门ID
*
* @param pid 上级ID
*/
private String getPidList(Long pid) {
//顶级部门无上级部门
if (Constant.DEPT_ROOT.equals(pid)) {
return Constant.DEPT_ROOT + "";
}
//所有部门的idpid列表
List<SysDeptEntity> deptList = baseDao.getIdAndPidList();
//list转map
Map<Long, SysDeptEntity> map = new HashMap<>(deptList.size());
for (SysDeptEntity entity : deptList) {
map.put(entity.getId(), entity);
}
//递归查询所有上级部门ID列表
List<Long> pidList = new ArrayList<>();
getPidTree(pid, map, pidList);
return StringUtils.join(pidList, ",");
}
private void getPidTree(Long pid, Map<Long, SysDeptEntity> map, List<Long> pidList) {
//顶级部门无上级部门
if (Constant.DEPT_ROOT.equals(pid)) {
return;
}
//上级部门存在
SysDeptEntity parent = map.get(pid);
if (parent != null) {
getPidTree(parent.getPid(), map, pidList);
}
pidList.add(pid);
}
}

View File

@ -1,82 +0,0 @@
package io.modules.sys.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.common.page.PageData;
import io.common.service.impl.BaseServiceImpl;
import io.common.utils.ConvertUtils;
import io.modules.sys.dao.SysDictDataDao;
import io.modules.sys.dto.SysDictDataDTO;
import io.modules.sys.entity.SysDictDataEntity;
import io.modules.sys.service.SysDictDataService;
import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.Map;
/**
* 字典类型
*
*/
@Service
public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysDictDataEntity> implements SysDictDataService {
@Override
public PageData<SysDictDataDTO> page(Map<String, Object> params) {
IPage<SysDictDataEntity> page = baseDao.selectPage(
getPage(params, "sort", true),
getWrapper(params)
);
return getPageData(page, SysDictDataDTO.class);
}
private QueryWrapper<SysDictDataEntity> getWrapper(Map<String, Object> params){
Long dictTypeId = Long.parseLong((String) params.get("dictTypeId"));
String dictLabel = (String) params.get("dictLabel");
String dictValue = (String) params.get("dictValue");
QueryWrapper<SysDictDataEntity> wrapper = new QueryWrapper<>();
wrapper.eq("dict_type_id", dictTypeId);
wrapper.like(StrUtil.isNotBlank(dictLabel), "dict_label", dictLabel);
wrapper.like(StrUtil.isNotBlank(dictValue), "dict_value", dictValue);
return wrapper;
}
@Override
public SysDictDataDTO get(Long id) {
SysDictDataEntity entity = baseDao.selectById(id);
return ConvertUtils.sourceToTarget(entity, SysDictDataDTO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void save(SysDictDataDTO dto) {
SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class);
insert(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(SysDictDataDTO dto) {
SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class);
updateById(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(Long[] ids) {
//删除
deleteBatchIds(Arrays.asList(ids));
}
}

View File

@ -1,101 +0,0 @@
package io.modules.sys.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.common.page.PageData;
import io.common.service.impl.BaseServiceImpl;
import io.common.utils.ConvertUtils;
import io.modules.sys.dao.SysDictDataDao;
import io.modules.sys.dao.SysDictTypeDao;
import io.modules.sys.dto.SysDictTypeDTO;
import io.modules.sys.entity.DictData;
import io.modules.sys.entity.DictType;
import io.modules.sys.entity.SysDictTypeEntity;
import io.modules.sys.service.SysDictTypeService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* 字典类型
*
*/
@Service
@AllArgsConstructor
public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysDictTypeEntity> implements SysDictTypeService {
private final SysDictDataDao sysDictDataDao;
@Override
public PageData<SysDictTypeDTO> page(Map<String, Object> params) {
IPage<SysDictTypeEntity> page = baseDao.selectPage(
getPage(params, "sort", true),
getWrapper(params)
);
return getPageData(page, SysDictTypeDTO.class);
}
private QueryWrapper<SysDictTypeEntity> getWrapper(Map<String, Object> params) {
String dictType = (String) params.get("dictType");
String dictName = (String) params.get("dictName");
QueryWrapper<SysDictTypeEntity> wrapper = new QueryWrapper<>();
wrapper.like(StrUtil.isNotBlank(dictType), "dict_type", dictType);
wrapper.like(StrUtil.isNotBlank(dictName), "dict_name", dictName);
return wrapper;
}
@Override
public SysDictTypeDTO get(Long id) {
SysDictTypeEntity entity = baseDao.selectById(id);
return ConvertUtils.sourceToTarget(entity, SysDictTypeDTO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void save(SysDictTypeDTO dto) {
SysDictTypeEntity entity = ConvertUtils.sourceToTarget(dto, SysDictTypeEntity.class);
insert(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(SysDictTypeDTO dto) {
SysDictTypeEntity entity = ConvertUtils.sourceToTarget(dto, SysDictTypeEntity.class);
updateById(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(Long[] ids) {
//删除
deleteBatchIds(Arrays.asList(ids));
}
@Override
public List<DictType> getAllList() {
List<DictType> typeList = baseDao.getDictTypeList();
List<DictData> dataList = sysDictDataDao.getDictDataList();
for (DictType type : typeList) {
for (DictData data : dataList) {
if (type.getId().equals(data.getDictTypeId())) {
type.getDataList().add(data);
}
}
}
return typeList;
}
}

View File

@ -1,102 +0,0 @@
package io.modules.sys.service.impl;
import io.modules.security.user.UserDetail;
import io.common.constant.Constant;
import io.common.exception.ErrorCode;
import io.common.exception.RenException;
import io.common.service.impl.BaseServiceImpl;
import io.common.utils.ConvertUtils;
import io.common.utils.TreeUtils;
import io.modules.sys.dao.SysMenuDao;
import io.modules.sys.dto.SysMenuDTO;
import io.modules.sys.entity.SysMenuEntity;
import io.modules.sys.enums.SuperAdminEnum;
import io.modules.sys.service.SysMenuService;
import io.modules.sys.service.SysRoleMenuService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@AllArgsConstructor
public class SysMenuServiceImpl extends BaseServiceImpl<SysMenuDao, SysMenuEntity> implements SysMenuService {
private final SysRoleMenuService sysRoleMenuService;
@Override
public SysMenuDTO get(Long id) {
SysMenuEntity entity = baseDao.getById(id);
SysMenuDTO dto = ConvertUtils.sourceToTarget(entity, SysMenuDTO.class);
return dto;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void save(SysMenuDTO dto) {
SysMenuEntity entity = ConvertUtils.sourceToTarget(dto, SysMenuEntity.class);
//保存菜单
insert(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(SysMenuDTO dto) {
SysMenuEntity entity = ConvertUtils.sourceToTarget(dto, SysMenuEntity.class);
//上级菜单不能为自身
if (entity.getId().equals(entity.getPid())) {
throw new RenException("上级菜单不能为自身!");
}
//更新菜单
updateById(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(Long id) {
//删除菜单
deleteById(id);
//删除角色菜单关系
sysRoleMenuService.deleteByMenuId(id);
}
@Override
public List<SysMenuDTO> getAllMenuList(Integer menuType) {
List<SysMenuEntity> menuList = baseDao.getMenuList(menuType);
List<SysMenuDTO> dtoList = ConvertUtils.sourceToTarget(menuList, SysMenuDTO.class);
return TreeUtils.build(dtoList, Constant.MENU_ROOT);
}
@Override
public List<SysMenuDTO> getUserMenuList(UserDetail user, Integer menuType) {
List<SysMenuEntity> menuList;
//系统管理员拥有最高权限
if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) {
menuList = baseDao.getMenuList(menuType);
} else {
menuList = baseDao.getUserMenuList(user.getId(), menuType);
}
List<SysMenuDTO> dtoList = ConvertUtils.sourceToTarget(menuList, SysMenuDTO.class);
return TreeUtils.build(dtoList);
}
@Override
public List<SysMenuDTO> getListPid(Long pid) {
List<SysMenuEntity> menuList = baseDao.getListPid(pid);
return ConvertUtils.sourceToTarget(menuList, SysMenuDTO.class);
}
}

View File

@ -1,123 +0,0 @@
package io.modules.sys.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.common.constant.Constant;
import io.common.exception.ErrorCode;
import io.common.exception.RenException;
import io.common.page.PageData;
import io.common.service.impl.BaseServiceImpl;
import io.common.utils.ConvertUtils;
import io.common.utils.JsonUtils;
import io.modules.sys.dao.SysParamsDao;
import io.modules.sys.dto.SysParamsDTO;
import io.modules.sys.entity.SysParamsEntity;
import io.modules.sys.service.SysParamsService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* 参数管理
*
* @since 1.0.0
*/
@Service
@AllArgsConstructor
public class SysParamsServiceImpl extends BaseServiceImpl<SysParamsDao, SysParamsEntity> implements SysParamsService {
@Override
public PageData<SysParamsDTO> page(Map<String, Object> params) {
IPage<SysParamsEntity> page = baseDao.selectPage(
getPage(params, Constant.CREATE_DATE, false),
getWrapper(params)
);
return getPageData(page, SysParamsDTO.class);
}
@Override
public List<SysParamsDTO> list(Map<String, Object> params) {
List<SysParamsEntity> entityList = baseDao.selectList(getWrapper(params));
return ConvertUtils.sourceToTarget(entityList, SysParamsDTO.class);
}
private QueryWrapper<SysParamsEntity> getWrapper(Map<String, Object> params) {
String paramCode = (String) params.get("paramCode");
QueryWrapper<SysParamsEntity> wrapper = new QueryWrapper<>();
wrapper.eq("param_type", 1);
wrapper.like(StrUtil.isNotBlank(paramCode), "param_code", paramCode);
return wrapper;
}
@Override
public SysParamsDTO get(Long id) {
SysParamsEntity entity = baseDao.selectById(id);
return ConvertUtils.sourceToTarget(entity, SysParamsDTO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void save(SysParamsDTO dto) {
SysParamsEntity entity = ConvertUtils.sourceToTarget(dto, SysParamsEntity.class);
insert(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(SysParamsDTO dto) {
SysParamsEntity entity = ConvertUtils.sourceToTarget(dto, SysParamsEntity.class);
updateById(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(Long[] ids) {
//删除Redis数据
List<String> paramCodeList = baseDao.getParamCodeList(ids);
String[] paramCodes = paramCodeList.toArray(new String[paramCodeList.size()]);
//删除
deleteBatchIds(Arrays.asList(ids));
}
@Override
public String getValue(String paramCode) {
return null;
}
@Override
public <T> T getValueObject(String paramCode, Class<T> clazz) {
String paramValue = getValue(paramCode);
if (StrUtil.isNotBlank(paramValue)) {
return JsonUtils.parseObject(paramValue, clazz);
}
try {
return clazz.newInstance();
} catch (Exception e) {
throw new RenException("参数错误!");
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public int updateValueByCode(String paramCode, String paramValue) {
int count = baseDao.updateValueByCode(paramCode, paramValue);
return count;
}
}

View File

@ -1,56 +0,0 @@
package io.modules.sys.service.impl;
import cn.hutool.core.collection.CollUtil;
import io.common.service.impl.BaseServiceImpl;
import io.modules.sys.dao.SysRoleDataScopeDao;
import io.modules.sys.entity.SysRoleDataScopeEntity;
import io.modules.sys.service.SysRoleDataScopeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 角色数据权限
*
* @since 1.0.0
*/
@Service
public class SysRoleDataScopeServiceImpl extends BaseServiceImpl<SysRoleDataScopeDao, SysRoleDataScopeEntity>
implements SysRoleDataScopeService {
@Override
public List<Long> getDeptIdList(Long roleId) {
return baseDao.getDeptIdList(roleId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveOrUpdate(Long roleId, List<Long> deptIdList) {
//先删除角色数据权限关系
deleteByRoleIds(new Long[]{roleId});
//角色没有一个数据权限的情况
if(CollUtil.isEmpty(deptIdList)){
return ;
}
//保存角色数据权限关系
for(Long deptId : deptIdList){
SysRoleDataScopeEntity sysRoleDataScopeEntity = new SysRoleDataScopeEntity();
sysRoleDataScopeEntity.setDeptId(deptId);
sysRoleDataScopeEntity.setRoleId(roleId);
//保存
insert(sysRoleDataScopeEntity);
}
}
@Override
public void deleteByRoleIds(Long[] roleIds) {
baseDao.deleteByRoleIds(roleIds);
}
}

View File

@ -1,63 +0,0 @@
package io.modules.sys.service.impl;
import cn.hutool.core.collection.CollUtil;
import io.common.service.impl.BaseServiceImpl;
import io.modules.sys.dao.SysRoleMenuDao;
import io.modules.sys.entity.SysRoleMenuEntity;
import io.modules.sys.service.SysRoleMenuService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 角色与菜单对应关系
*
*/
@Service
public class SysRoleMenuServiceImpl extends BaseServiceImpl<SysRoleMenuDao, SysRoleMenuEntity> implements SysRoleMenuService {
@Override
@Transactional(rollbackFor = Exception.class)
public void saveOrUpdate(Long roleId, List<Long> menuIdList) {
//先删除角色菜单关系
deleteByRoleIds(new Long[]{roleId});
//角色没有一个菜单权限的情况
if(CollUtil.isEmpty(menuIdList)){
return ;
}
//保存角色菜单关系
for(Long menuId : menuIdList){
SysRoleMenuEntity sysRoleMenuEntity = new SysRoleMenuEntity();
sysRoleMenuEntity.setMenuId(menuId);
sysRoleMenuEntity.setRoleId(roleId);
//保存
insert(sysRoleMenuEntity);
}
}
@Override
public List<Long> getMenuIdList(Long roleId){
return baseDao.getMenuIdList(roleId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteByRoleIds(Long[] roleIds) {
baseDao.deleteByRoleIds(roleIds);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteByMenuId(Long menuId) {
baseDao.deleteByMenuId(menuId);
}
}

View File

@ -1,126 +0,0 @@
package io.modules.sys.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.modules.security.user.SecurityUser;
import io.modules.security.user.UserDetail;
import io.modules.sys.service.*;
import io.common.constant.Constant;
import io.common.page.PageData;
import io.common.service.impl.BaseServiceImpl;
import io.common.utils.ConvertUtils;
import io.modules.sys.dao.SysRoleDao;
import io.modules.sys.dto.SysRoleDTO;
import io.modules.sys.entity.SysRoleEntity;
import io.modules.sys.enums.SuperAdminEnum;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* 角色
*
*/
@Service
@AllArgsConstructor
public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleDao, SysRoleEntity> implements SysRoleService {
private final SysRoleMenuService sysRoleMenuService;
private final SysRoleDataScopeService sysRoleDataScopeService;
private final SysRoleUserService sysRoleUserService;
private final SysDeptService sysDeptService;
@Override
public PageData<SysRoleDTO> page(Map<String, Object> params) {
IPage<SysRoleEntity> page = baseDao.selectPage(
getPage(params, Constant.CREATE_DATE, false),
getWrapper(params)
);
return getPageData(page, SysRoleDTO.class);
}
@Override
public List<SysRoleDTO> list(Map<String, Object> params) {
List<SysRoleEntity> entityList = baseDao.selectList(getWrapper(params));
return ConvertUtils.sourceToTarget(entityList, SysRoleDTO.class);
}
private QueryWrapper<SysRoleEntity> getWrapper(Map<String, Object> params) {
String name = (String) params.get("name");
QueryWrapper<SysRoleEntity> wrapper = new QueryWrapper<>();
wrapper.like(StrUtil.isNotBlank(name), "name", name);
//普通管理员只能查询所属部门及子部门的数据
UserDetail user = SecurityUser.getUser();
if (user.getSuperAdmin() == SuperAdminEnum.NO.value()) {
List<Long> deptIdList = sysDeptService.getSubDeptIdList(user.getDeptId());
wrapper.in(deptIdList != null, "dept_id", deptIdList);
}
return wrapper;
}
@Override
public SysRoleDTO get(Long id) {
SysRoleEntity entity = baseDao.selectById(id);
return ConvertUtils.sourceToTarget(entity, SysRoleDTO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void save(SysRoleDTO dto) {
SysRoleEntity entity = ConvertUtils.sourceToTarget(dto, SysRoleEntity.class);
//保存角色
insert(entity);
//保存角色菜单关系
sysRoleMenuService.saveOrUpdate(entity.getId(), dto.getMenuIdList());
//保存角色数据权限关系
sysRoleDataScopeService.saveOrUpdate(entity.getId(), dto.getDeptIdList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(SysRoleDTO dto) {
SysRoleEntity entity = ConvertUtils.sourceToTarget(dto, SysRoleEntity.class);
//更新角色
updateById(entity);
//更新角色菜单关系
sysRoleMenuService.saveOrUpdate(entity.getId(), dto.getMenuIdList());
//更新角色数据权限关系
sysRoleDataScopeService.saveOrUpdate(entity.getId(), dto.getDeptIdList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(Long[] ids) {
//删除角色
baseDao.deleteBatchIds(Arrays.asList(ids));
//删除角色用户关系
sysRoleUserService.deleteByRoleIds(ids);
//删除角色菜单关系
sysRoleMenuService.deleteByRoleIds(ids);
//删除角色数据权限关系
sysRoleDataScopeService.deleteByRoleIds(ids);
}
}

View File

@ -1,5 +1,3 @@
package io.modules.sys.service.impl;
import cn.hutool.core.util.StrUtil;
@ -15,7 +13,6 @@ import io.modules.sys.dao.SysUserDao;
import io.modules.sys.dto.SysUserDTO;
import io.modules.sys.entity.SysUserEntity;
import io.modules.sys.enums.SuperAdminEnum;
import io.modules.sys.service.SysDeptService;
import io.modules.sys.service.SysRoleUserService;
import io.modules.sys.service.SysUserService;
import lombok.AllArgsConstructor;
@ -36,7 +33,6 @@ import java.util.Map;
@AllArgsConstructor
public class SysUserServiceImpl extends BaseServiceImpl<SysUserDao, SysUserEntity> implements SysUserService {
private final SysRoleUserService sysRoleUserService;
private final SysDeptService sysDeptService;
@Override
public PageData<SysUserDTO> page(Map<String, Object> params) {
@ -46,12 +42,6 @@ public class SysUserServiceImpl extends BaseServiceImpl<SysUserDao, SysUserEntit
//分页
IPage<SysUserEntity> page = getPage(params, Constant.CREATE_DATE, false);
//普通管理员只能查询所属部门及子部门的数据
UserDetail user = SecurityUser.getUser();
if (user.getSuperAdmin() == SuperAdminEnum.NO.value()) {
params.put("deptIdList", sysDeptService.getSubDeptIdList(user.getDeptId()));
}
//查询
List<SysUserEntity> list = baseDao.getList(params);
@ -60,12 +50,6 @@ public class SysUserServiceImpl extends BaseServiceImpl<SysUserDao, SysUserEntit
@Override
public List<SysUserDTO> list(Map<String, Object> params) {
//普通管理员只能查询所属部门及子部门的数据
UserDetail user = SecurityUser.getUser();
if (user.getSuperAdmin() == SuperAdminEnum.NO.value()) {
params.put("deptIdList", sysDeptService.getSubDeptIdList(user.getDeptId()));
}
List<SysUserEntity> entityList = baseDao.getList(params);
return ConvertUtils.sourceToTarget(entityList, SysUserDTO.class);

View File

@ -3,7 +3,7 @@ spring:
druid:
#MySQL
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:33060/block_auth?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
url: jdbc:mysql://localhost:3306/block_auth?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
username: root
password: 123456
initial-size: 10

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.job.dao.ScheduleJobDao">
<!-- 批量更新状态 -->
<update id="updateBatch">
update schedule_job set status = #{status} where id in
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</update>
</mapper>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.job.dao.ScheduleJobLogDao">
</mapper>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.log.dao.SysLogErrorDao">
</mapper>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.log.dao.SysLogLoginDao">
</mapper>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.log.dao.SysLogOperationDao">
</mapper>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.oss.dao.SysOssDao">
</mapper>

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.sys.dao.SysDeptDao">
<select id="getList" resultType="io.modules.sys.entity.SysDeptEntity">
select t1.*,(select t2.name from sys_dept t2 where t2.id=t1.pid)parentName from sys_dept t1
<where>
<if test="deptIdList != null">
t1.id in
<foreach item="id" collection="deptIdList" open="(" separator="," close=")">
#{id}
</foreach>
</if>
</where>
order by t1.sort asc
</select>
<select id="getById" resultType="io.modules.sys.entity.SysDeptEntity">
select t1.*,(select t2.name from sys_dept t2 where t2.id=t1.pid)parentName from sys_dept t1
where t1.id = #{value}
</select>
<select id="getIdAndPidList" resultType="io.modules.sys.entity.SysDeptEntity">
select t1.id, t1.pid from sys_dept t1
</select>
<select id="getSubDeptIdList" resultType="long">
select id from sys_dept where pids like #{id}
</select>
</mapper>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.sys.dao.SysDictDataDao">
<select id="getDictDataList" resultType="io.modules.sys.entity.DictData">
select dict_type_id, dict_label, dict_value from sys_dict_data order by dict_type_id, sort
</select>
</mapper>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.sys.dao.SysDictTypeDao">
<select id="getDictTypeList" resultType="io.modules.sys.entity.DictType">
select id, dict_type from sys_dict_type order by dict_type, sort
</select>
</mapper>

View File

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.sys.dao.SysMenuDao">
<select id="getById" resultType="io.modules.sys.entity.SysMenuEntity">
select t1.*, (select name from sys_menu t2 where t2.id=t1.pid) as parentName from sys_menu t1
where t1.id = #{id}
</select>
<select id="getMenuList" resultType="io.modules.sys.entity.SysMenuEntity">
select t1.* from sys_menu t1
<where>
<if test="menuType != null">
t1.menu_type = #{menuType}
</if>
</where>
order by t1.sort asc
</select>
<select id="getUserMenuList" resultType="io.modules.sys.entity.SysMenuEntity">
select t3.* from sys_role_user t1
left join sys_role_menu t2 on t1.role_id = t2.role_id
left join sys_menu t3 on t2.menu_id = t3.id
where t1.user_id = #{userId}
<if test="menuType != null">
and t3.menu_type = #{menuType}
</if>
order by t3.sort asc
</select>
<select id="getUserPermissionsList" resultType="string">
select t3.permissions from sys_role_user t1 left join sys_role_menu t2 on t1.role_id = t2.role_id
left join sys_menu t3 on t2.menu_id = t3.id
where t1.user_id = #{userId} order by t3.sort asc
</select>
<select id="getPermissionsList" resultType="string">
select permissions from sys_menu
</select>
<select id="getListPid" resultType="io.modules.sys.entity.SysMenuEntity">
select * from sys_menu where pid = #{value}
</select>
</mapper>

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.sys.dao.SysParamsDao">
<!-- 根据参数编码查询value -->
<select id="getValueByCode" resultType="String">
select param_value from sys_params where param_code = #{value}
</select>
<!-- 获取参数编码列表 -->
<select id="getParamCodeList" resultType="String">
select param_code from sys_params where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 根据参数编码更新value -->
<update id="updateValueByCode">
update sys_params set param_value = #{paramValue} where param_code = #{paramCode}
</update>
</mapper>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.sys.dao.SysRoleDao">
</mapper>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.sys.dao.SysRoleDataScopeDao">
<select id="getDeptIdList" resultType="long">
select dept_id from sys_role_data_scope where role_id = #{value}
</select>
<select id="getDataScopeList" resultType="long">
select t2.dept_id from sys_role_user t1, sys_role_data_scope t2
where t1.user_id = #{value} and t1.role_id = t2.role_id
</select>
<delete id="deleteByRoleIds">
delete from sys_role_data_scope where role_id in
<foreach item="roleId" collection="array" open="(" separator="," close=")">
#{roleId}
</foreach>
</delete>
</mapper>

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.modules.sys.dao.SysRoleUserDao">
<delete id="deleteByRoleIds">
delete from sys_role_user where role_id in
<foreach item="roleId" collection="array" open="(" separator="," close=")">
#{roleId}
</foreach>
</delete>
<delete id="deleteByUserIds">
delete from sys_role_user where user_id in
<foreach item="userId" collection="array" open="(" separator="," close=")">
#{userId}
</foreach>
</delete>
<select id="getRoleIdList" resultType="long">
select role_id from sys_role_user where user_id = #{value}
</select>
</mapper>

View File

@ -0,0 +1,18 @@
package io.common.utils;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class KeyUtil {
public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024); // 2048位安全性高
return keyGen.generateKeyPair();
}
public static String encodeKeyToString(byte[] keyBytes) {
return Base64.getEncoder().encodeToString(keyBytes);
}
}

View File

@ -20,25 +20,21 @@ public class CertificatesDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Long id; // 编号
private Integer isBlock;
private Long certificateNumber; // 证书编号
private Long userId; // 用户 ID
private Date issueDate; // 签发日期
private Date expireDate; // 过期日期
private String certificateData; // 详情JSON 字符串
private Long userId;
private String name;
private String company;
private String department;
private Date issueDate;
private Date expireDate;
private Integer status;
private String img;
private String blockchainTxId; // 区块链交易 ID
private Integer status; // 1: 有效0: 撤销2: 过期
private Date createdAt; // 创建时间
private Integer auditStatus;
private String role;
private String certificateData;
private String auditStatus;
private String auditComments;
private String hex;
private Date createdAt;
private UserDTO user;
}

View File

@ -1,5 +1,6 @@
package io.modules.item.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.SchemaProperty;
import lombok.Data;
@ -9,32 +10,17 @@ import java.util.Date;
/**
* 用户
*
* @author Mark #
* @since 1.0.0 2025-01-14
*/
@Data
@Schema(name = "用户")
public class UserDTO implements Serializable {
private static final long serialVersionUID = 1L;
@SchemaProperty(name = "id")
private Long id;
@SchemaProperty(name = "手机号")
private String username;
@SchemaProperty(name = "密码")
private String password;
@SchemaProperty(name = "创建时间")
private Date createDate;
@SchemaProperty(name = "昵称")
private String nickName;
@SchemaProperty(name = "介绍")
private String introduce;
private String publicKey;
private String privateKey;
private String username;
private String password;
private Date createDate;
}

View File

@ -1,37 +1,25 @@
package io.modules.item.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 主表
*/
@Data
@TableName("tb_certificates")
public class CertificatesEntity {
private Long id; // 编号
private Integer isBlock;
private Integer auditStatus;
private String auditComments;
private Long certificateNumber; // 证书编号
private Long userId; // 用户 ID
private Date issueDate; // 签发日期
private Date expireDate; // 过期日期
private String certificateData; // 详情JSON 字符串
private Long id;
private Long userId;
private String name;
private String company;
private String department;
private Date issueDate;
private Date expireDate;
private Integer status;
private String img;
private String blockchainTxId; // 区块链交易 ID
private Integer status; // 1: 有效0: 撤销2: 过期
private Date createdAt; // 创建时间
private String role;
private String auditStatus;
private String auditComments;
private String hex;
private Date createdAt;
}

View File

@ -1,6 +1,7 @@
package io.modules.item.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.util.Date;
@ -14,24 +15,12 @@ public class FrontUserEntity {
* id
*/
private Long id;
/**
* 手机号
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 创建时间
*/
private Date createDate;
/**
* 昵称
*/
private String nickName;
/**
* 介绍
*/
private String introduce;
private String nickName;
private String publicKey;
private String privateKey;
private String username;
@JsonIgnore
private String password;
private Date createDate;
}

View File

@ -13,38 +13,20 @@ import java.util.Date;
/**
* 用户
*
*/
@Data
@TableName("tb_user")
public class UserEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
@TableId
private Long id;
/**
* 昵称
*/
private String nickName;
/**
* 介绍
*/
private String introduce;
/**
* 用户名
*/
private String publicKey;
private String privateKey;
private String username;
/**
* 密码
*/
@JsonIgnore
private String password;
/**
* 创建时间
*/
private Date createDate;
}

View File

@ -7,9 +7,6 @@ import io.modules.item.entity.CertificatesEntity;
/**
* 主表
*
* @author Mark #
* @since 1.0.0 2025-01-24
*/
public interface CertificatesService extends CrudService<CertificatesEntity, CertificatesDTO> {

View File

@ -15,29 +15,20 @@ import java.util.Map;
/**
* 主表
*
* @author Mark #
* @since 1.0.0 2025-01-24
*/
@Service
public class CertificatesServiceImpl extends CrudServiceImpl<CertificatesDao, CertificatesEntity, CertificatesDTO> implements CertificatesService {
@Autowired
private UserDao userService;
@Override
public QueryWrapper<CertificatesEntity> getWrapper(Map<String, Object> params){
String id = (String)params.get("id");
String status = (String)params.get("status");
String isBlock = (String)params.get("isBlock");
String auditStatus = (String)params.get("auditStatus");
QueryWrapper<CertificatesEntity> wrapper = new QueryWrapper<>();
wrapper.eq(StrUtil.isNotBlank(id), "id", id);
wrapper.eq(StrUtil.isNotBlank(status), "status", status);
wrapper.eq(StrUtil.isNotBlank(isBlock), "is_block", isBlock);
wrapper.eq(StrUtil.isNotBlank(auditStatus) && !auditStatus.equals("all"), "audit_status", auditStatus);
return wrapper;
}

View File

@ -4,11 +4,13 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* front
*/
@SpringBootApplication
@EnableScheduling
public class FrontApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(FrontApplication.class, args);

View File

@ -0,0 +1,85 @@
package io.config;
import io.modules.item.dao.CertificatesDao;
import io.modules.item.entity.CertificatesEntity;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.internal.util.Contracts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.security.SecureRandom;
import java.util.HexFormat;
import java.util.List;
/**
*
* 1.在主方法加@EnableScheduling
*/
@Component
@Slf4j
public class MyScheduledTask {
@Autowired
CertificatesDao contractsService;
// 每5秒执行一次
@Scheduled(fixedRate = 5000)
public void runEveryFiveSeconds() {
System.out.println("⏰ 定时任务执行时间: " + java.time.LocalDateTime.now());
//需要检测的hex字段
List<CertificatesEntity> list = contractsService.selectList(null);
for (CertificatesEntity itemEntity : list) {
if (!isValidEthereumHexId(itemEntity.getHex(),64)){
itemEntity.setHex(sendSetRequest(itemEntity.getId().toString(),itemEntity.toString()));
//更新数据
contractsService.updateById(itemEntity);
}
}
}
public static String sendSetRequest(String key, String value) {
try {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/set?key=" + key + "&value=" + value;
// 发送 GET 请求并解析返回 JSON SetResponse 对象
ResponseEntity<SetResponse> response = restTemplate.getForEntity(url, SetResponse.class);
return response.getBody().data;
}catch (Exception e){
SetResponse setRequestService = new SetResponse();
setRequestService.setData(generate(40));
return setRequestService.data;
}
}
/**
* 判断是否为合法的以太坊Hex ID例如交易哈希区块哈希地址
* @param hexId 要校验的字符串
* @param length 字符长度40 = 钱包地址64 = 交易哈希/区块哈希
* @return true 表示合法否则 false
*/
public static boolean isValidEthereumHexId(String hexId, int length) {
if (hexId == null || !hexId.startsWith("0x")) {
return false;
}
String hexBody = hexId.substring(2);
String pattern = "^[0-9a-fA-F]{" + length + "}$";
return hexBody.matches(pattern);
}
// 内嵌的SetResponse类
@Data
public static class SetResponse {
private String msg;
private String data;
}
private static final SecureRandom secureRandom = new SecureRandom();
private static final HexFormat hexFormat = HexFormat.of();
public static String generate(int byteLength) {
byte[] randomBytes = new byte[byteLength];
secureRandom.nextBytes(randomBytes);
return "0x" + hexFormat.formatHex(randomBytes);
}
}

View File

@ -76,7 +76,7 @@ public class CertificatesController {
@Operation(summary = "信息")
public Result<CertificatesEntity> get(@PathVariable("id") String id){
LambdaQueryWrapper<CertificatesEntity> lwq = new LambdaQueryWrapper<>();
lwq.eq(CertificatesEntity::getBlockchainTxId,id);
lwq.eq(CertificatesEntity::getHex,id);
CertificatesEntity list = certificatesDao.selectOne(lwq);
if (list == null){
return new Result<CertificatesEntity>().error("没有查询到");
@ -90,7 +90,6 @@ public class CertificatesController {
@Operation(summary = "保存")
public Result save(@RequestBody CertificatesDTO dto,@Parameter(hidden = true) @RequestAttribute("userId") Long userId){
LambdaQueryWrapper<CertificatesEntity> lwq = new LambdaQueryWrapper<>();
lwq.eq(CertificatesEntity::getCertificateNumber,dto.getCertificateNumber());
List<CertificatesEntity> list = certificatesDao.selectList(lwq);
if (!list.isEmpty()){
return new Result().error("证书编号有重复");

View File

@ -1,8 +1,10 @@
package io.controller;
import cn.hutool.crypto.digest.DigestUtil;
import io.annotation.Login;
import io.annotation.LoginUser;
import io.common.utils.KeyUtil;
import io.common.utils.Result;
import io.common.validator.ValidatorUtils;
import io.modules.item.dto.LoginDTO;
@ -17,6 +19,9 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Map;
@ -34,7 +39,7 @@ public class UserController {
@PostMapping("register")
@Operation(summary = "注册")
public Result register(@RequestBody RegisterDTO dto) {
public Result register(@RequestBody RegisterDTO dto) throws NoSuchAlgorithmException {
if (dto.getUsername().equals("admin")){
return new Result().error("admin用户禁止创建~");
@ -49,15 +54,23 @@ public class UserController {
if (userService.getByUsername(dto.getUsername()) != null) {
return new Result().error("用户名已经存在~");
}
// 2. 生成RSA密钥对
KeyPair keyPair = KeyUtil.generateKeyPair();
String publicKey = KeyUtil.encodeKeyToString(keyPair.getPublic().getEncoded());
//公钥
String privateKey = KeyUtil.encodeKeyToString(keyPair.getPrivate().getEncoded());
//表单校验
ValidatorUtils.validateEntity(dto);
UserEntity user = new UserEntity();
user.setUsername(dto.getUsername());
user.setNickName(dto.getUsername());
user.setPassword(DigestUtil.sha256Hex(dto.getPassword()));
user.setCreateDate(new Date());
user.setPublicKey(publicKey);
user.setPrivateKey(privateKey);
userService.insert(user);
return new Result();
return new Result().ok(user);
}
@ -81,7 +94,6 @@ public class UserController {
user.setId(userId);
user.setUsername(dto.getUsername());
user.setNickName(dto.getNickName());
user.setIntroduce(dto.getIntroduce());
if (StringUtils.isNotEmpty(dto.getPassword())){
user.setPassword(DigestUtil.sha256Hex(dto.getPassword()));
}

View File

@ -6,7 +6,7 @@ spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:33060/block_auth?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
url: jdbc:mysql://localhost:3306/block_auth?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
username: root
password: 123456
initial-size: 10

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

View File

@ -1,11 +1,9 @@
<template>
<div class="head">
<div class="head_l">
<!-- <img src="/icoimg.png" alt="收缩" />-->
</div>
<el-dropdown>
<div class="head_r">
<!-- <img :src="userStore().adminUserInfo.avatar" alt="头像" class="profile" />-->
<div class="head_user">
<div class="head_user_name">{{ userStore().adminUserInfo.username }}</div>
<div class="head_user_desc">管理员</div>
@ -13,61 +11,21 @@
</div>
<template #dropdown>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click="drawer = true" >个人中心</el-dropdown-item>
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-drawer
v-model="drawer"
title="个人中心"
>
<el-form
ref="formRef"
:model="state.dynamicValidateForm"
label-position="top"
>
<el-form-item
prop="password"
label="原始密码"
:rules="[
{
required: true,
message: '原始密码不能为空',
trigger: 'blur',
},
]"
>
<el-input v-model="state.dynamicValidateForm.password" />
</el-form-item>
<el-form-item
prop="newPassword"
label="新密码"
:rules="[
{
required: true,
message: '新密码不能为空',
trigger: 'blur',
},
]"
>
<el-input v-model="state.dynamicValidateForm.newPassword" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(formRef)">确定修改</el-button>
</el-form-item>
</el-form>
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { useRouter } from "vue-router";
import type { FormInstance } from 'element-plus'
const formRef = ref<FormInstance>()
import { updatePasswordAdmin } from '~/api/user/adminUserApi'
const router = useRouter();
const drawer = ref(false)
const state = reactive(<any>{
dynamicValidateForm:{}
})

View File

@ -47,16 +47,28 @@
</div>
</div>
</div>
<!-- <Navigation />-->
<!-- <div class="w-screen flex flex-col items-center justify-center">-->
<!-- <router-view v-slot="{ Component }">-->
<!-- <transition name="fade" mode="out-in">-->
<!-- <component :is="Component" />-->
<!-- </transition>-->
<!-- </router-view>-->
<!-- </div>-->
</template>
<script setup lang="ts">
import Heads from '~/components/Heads.vue'
import { getRoutes } from '@/plugins/router'
const { te, t } = useI18n()
const routes = getRoutes()
.filter((r) => !r.path.includes('notFound'))
.map((r) => {
let { path, name } = r
if (path === safeResolve('/')) {
return { path, name: 'home' }
}
if (!name) {
name = path
}
return { path, name: name.toString().slice(1).replaceAll('/', ' · ') }
})
const $route = useRoute()
</script>
<style scoped>
.main{
width: 100%;
@ -144,21 +156,3 @@
}
}
</style>
<script setup lang="ts">
import Heads from '~/components/Heads.vue'
import { getRoutes } from '@/plugins/router'
const { te, t } = useI18n()
const routes = getRoutes()
.filter((r) => !r.path.includes('notFound'))
.map((r) => {
let { path, name } = r
if (path === safeResolve('/')) {
return { path, name: 'home' }
}
if (!name) {
name = path
}
return { path, name: name.toString().slice(1).replaceAll('/', ' · ') }
})
const $route = useRoute()
</script>

View File

@ -1,29 +1,35 @@
<template>
<el-container>
<el-header>
<heads></heads>
</el-header>
<el-container>
<el-aside width="200px" >
<el-menu
:default-active="navStore().adminPath"
router
@select="handleSelect"
<el-aside width="200px" >
<el-menu
style="height: 100vh"
:default-active="navStore().adminPath"
router
@select="handleSelect"
background-color="#001020"
text-color="#fff"
>
<div style="width: 100%;height: 50px"></div>
<el-menu-item
v-for="r in getAdminList()"
:key="r.name"
:index="r.path"
>
<el-menu-item
v-for="r in getAdminList()"
:key="r.name"
:index="r.path"
>
<component class="icons" :is="r.icon" />
<template #title>{{ r.name }}</template>
</el-menu-item>
</el-menu>
</el-aside>
<component class="icons" :is="r.icon" />
<template #title>{{ r.name }}</template>
</el-menu-item>
</el-menu>
</el-aside>
<el-container>
<el-header>
<heads></heads>
</el-header>
<el-main class="main">
<router-view ></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script setup lang="ts">
@ -47,4 +53,5 @@ const handleSelect = (key: string, keyPath: string[]) => {
height: 18px;
margin-right: 5px;
}
</style>

View File

@ -3,32 +3,240 @@
<template>
<div class="common-layout">
<el-container>
<el-header>
<nav-navigation></nav-navigation>
</el-header>
<el-main class="main">
<div class="container">
<router-view></router-view>
</div>
</el-main>
<el-aside width="200px">
<el-menu
style="height: 100vh"
router
background-color="#001020"
text-color="#fff"
>
<div style="width: 100%;height: 30px"></div>
<el-menu-item
v-for="r in getFrontList()"
:key="r.name"
:index="r.path"
>
<component class="icons" :is="r.icon" />
<template #title>{{ r.name }}</template>
</el-menu-item>
</el-menu>
</el-aside>
<el-container>
<el-header>
<el-row :gutter="20">
<el-col :span="22"><div class="grid-content ep-bg-purple" /></el-col>
<el-col :span="2">
<div style="height: 20px"></div>
<el-dropdown>
<div class="head_r">
<div class="head_user">
<div class="head_user_name">{{ state.userInfo.nickName }}</div>
<!-- <div class="head_user_desc">管理员</div>-->
</div>
</div>
<template #dropdown>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col>
</el-row>
</el-header>
<el-main class="main">
<div class="certificate-container">
<router-view></router-view>
</div>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script setup lang="ts">
import {getFrontList } from '~/utils/utils'
import { useRouter } from "vue-router";
const router = useRouter();
const state = reactive({
userInfo:{}
})
function init() {
frontRequest.get("/api/user/userInfo").then(res =>{
console.log(res)
state.userInfo = res.data
})
}
onMounted(()=>{
init()
})
/**
* 退出登录
*/
const logout = () => {
const user = userStore()
user.frontToken = {}
user.frontIsLogin = false
ElMessage.success("退出成功~")
router.push('/login');
}
</script>
<style scoped>
.main{
width: 100%;
height: calc(100vh - 60px);
background-color: #f3f3f4;
height: calc(100vh - 80px);
background-color: #eeeeee;
}
.container{
width: 1200px;
margin: 0 auto;
.icons{
width: 18px;
height: 18px;
margin-right: 5px;
}
.certificate-container {
position: relative;
margin: 1rem auto;
padding: 2.5rem;
background: rgba(255,255,255,0.95);
border-radius: 0.2rem;
z-index: 1;
.blockchain-decor {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.page-title {
text-align: center;
margin-bottom: 2rem;
color: #1a237e;
font-size: 2rem;
position: relative;
.el-icon-connection {
font-size: 2.5rem;
vertical-align: middle;
margin-right: 0.5rem;
}
.sub-title {
font-size: 1rem;
color: #666;
margin-top: 0.5rem;
font-weight: normal;
}
}
.certificate-form {
position: relative;
z-index: 2;
.form-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
}
:deep(.block-input) {
.el-input__inner {
background: rgba(245, 247, 250, 0.8);
border: 1px solid #e0e0e0;
border-radius: 0.75rem;
padding-left: 2.5rem;
transition: all 0.3s ease;
&:hover {
border-color: #409EFF;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}
}
.el-input__prefix {
left: 12px;
color: #409EFF;
}
}
.block-textarea {
:deep(.el-textarea__inner) {
background: rgba(245, 247, 250, 0.8);
border-radius: 0.75rem;
min-height: 100px;
resize: vertical;
}
}
.form-actions {
margin-top: 2rem;
text-align: center;
.submit-btn {
padding: 12px 30px;
font-size: 1rem;
background: linear-gradient(135deg, #409EFF, #1a237e);
border: none;
border-radius: 0.75rem;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
}
}
.reset-btn {
padding: 12px 30px;
background: rgba(245, 247, 250, 0.8);
border-color: #e0e0e0;
border-radius: 0.75rem;
}
}
}
.blockchain-features {
margin-top: 3rem;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
.feature-item {
text-align: center;
padding: 1.5rem;
background: rgba(245, 247, 250, 0.8);
border-radius: 1rem;
transition: all 0.3s ease;
&:hover {
transform: translateY(-5px);
box-shadow: 0 6px 16px rgba(0,0,0,0.08);
}
i {
font-size: 2.5rem;
color: #409EFF;
margin-bottom: 1rem;
}
h3 {
color: #1a237e;
margin: 0.5rem 0;
}
p {
color: #666;
font-size: 0.9rem;
margin: 0;
}
}
}
}
</style>

View File

@ -1,16 +1,8 @@
<template>
<el-row>
<!-- <el-col :span="12">-->
<!-- <el-button type="primary" @click="openForm(true)">添加证书</el-button>-->
<!-- </el-col>-->
<el-col :span="12" class="text-right">
<el-button type="info" @click="openChainInstructions">上链须知</el-button>
</el-col>
</el-row>
<el-table :data="state.certificateList" v-loading="loading" class="el-table">
<el-table-column prop="id" label="编号" />
<el-table-column prop="certificateNumber" label="证书编号" />
<el-table-column prop="user.nickName" label="用户" />
<el-table-column prop="issueDate" label="签发日期" />
<el-table-column prop="expireDate" label="过期日期" />

View File

@ -1,240 +1,254 @@
<template>
<el-row>
<!-- <el-col :span="12">-->
<!-- <el-button type="primary" @click="openForm(true)">添加证书</el-button>-->
<!-- </el-col>-->
<el-col :span="12" class="text-right">
<el-button type="info" @click="openChainInstructions">上链须知</el-button>
</el-col>
</el-row>
<div class="certificate-container">
<!-- 查询表单 -->
<el-row>
<el-col :span="24">
<el-form :inline="true" :model="state.query" class="demo-form-inline">
<el-form-item label="证书编号">
<el-input v-model="state.query.id" placeholder="请输入证书编号" clearable />
</el-form-item>
<el-form-item label="状态">
<el-select
style="width: 120px"
v-model="state.query.auditStatus"
placeholder="请选择状态"
clearable
>
<el-option label="待审核" value="0" />
<el-option label="已通过" value="1" />
<el-option label="已拒绝" value="2" />
</el-select>
</el-form-item>
<!-- 新增Tabs标签 -->
<el-tabs v-model="state.activeTab" @tab-click="handleClick">
<el-tab-pane label="全部" name="all"></el-tab-pane>
<el-tab-pane label="待审核" name="0"></el-tab-pane>
<el-tab-pane label="已通过" name="1"></el-tab-pane>
<el-tab-pane label="已拒绝" name="2"></el-tab-pane>
</el-tabs>
<el-form-item label="过期时间">
<el-date-picker
v-model="state.query.expireDate"
type="date"
placeholder="请选择日期"
clearable
/>
</el-form-item>
<el-table :data="state.certificateList" v-loading="loading" class="el-table">
<el-table-column prop="id" label="编号" />
<el-table-column prop="certificateNumber" label="证书编号" />
<el-table-column prop="path" label="证书" align="center">
<template #default="scope">
<el-image
style="width: 50px; height: 50px"
:src="scope.row.img"
:preview-src-list="scope.row.img.toString().split(',')"
fit="cover"
:preview-teleported="true"
/>
</template>
</el-table-column>
<el-table-column prop="user.nickName" label="用户" />
<el-table-column prop="issueDate" label="签发日期" />
<el-table-column prop="expireDate" label="过期日期" />
<el-table-column prop="status" label="证书状态" :formatter="statusText" />
<!-- 新增审核状态列 -->
<el-table-column prop="auditStatus" label="审核状态">
<template #default="scope">
<el-tag :type="auditTagType(scope.row.auditStatus)">
{{ auditStatusText(scope.row.auditStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="auditComments" label="审核备注" />
<el-table-column prop="createdAt" label="创建时间" />
<el-table-column label="操作" fixed="right" align="center">
<template #default="scope">
<el-button link type="primary" @click="openForm(false, scope.row)">编辑/审核</el-button>
<el-button
v-if="scope.row.auditStatus == 1"
link
type="primary"
@click="onChain(scope.row)"
:disabled="scope.row.auditStatus !== 1"
>
上链
</el-button>
<el-button link type="danger" @click="del(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-form-item>
<el-button type="primary" @click="loadCertificates()">查询</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
<!-- 分页控件 -->
<div class="pagination-container">
<el-pagination
v-if="state.query.total > 0"
:current-page="state.query.page"
:page-size="state.query.limit"
:total="state.query.total"
background
layout="prev, pager, next"
@current-change="handlePageChange"
/>
</div>
<!-- 添加/编辑弹窗 -->
<el-dialog
v-model="state.dialogVisible"
:title="state.dialogTitle"
width="600px"
:append-to-body="false"
>
<el-form :model="state.form" :rules="rules" ref="formRef" label-position="top">
<el-form-item label="证书编号" prop="certificateNumber">
<el-input v-model="state.form.certificateNumber" />
</el-form-item>
<el-form-item label="证书" prop="certificateNumber">
<image-upload @update:imageUrl="handleImageUrl" :image-url="state.form.img"/>
</el-form-item>
<el-form-item label="用户" prop="userId">
<el-select v-model="state.form.userId" placeholder="请选择" >
<el-option
v-for="item in state.userList"
:key="item.id"
:label="`${item.nickName} (${item.username})`"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="签发日期" prop="issueDate">
<el-date-picker
v-model="state.form.issueDate"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item label="过期日期" prop="expireDate">
<el-date-picker
v-model="state.form.expireDate"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item label="证书详情" prop="certificateData">
<el-input type="textarea" v-model="state.form.certificateData" />
</el-form-item>
<el-form-item label="证书状态" prop="status">
<el-select v-model="state.form.status">
<el-option label="有效" :value="1" />
<el-option label="撤销" :value="0" />
<el-option label="过期" :value="2" />
</el-select>
</el-form-item>
<!-- 新增审核相关字段 -->
<el-form-item label="审核状态" prop="auditStatus">
<el-select v-model="state.form.auditStatus">
<el-option label="待审核" :value="0" />
<el-option label="审核通过" :value="1" />
<el-option label="审核拒绝" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="审核备注" prop="auditComments">
<el-input type="textarea" v-model="state.form.auditComments" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm">提交</el-button>
</template>
</el-dialog>
<!-- 证书列表 -->
<el-table :data="state.certificateList" v-loading="loading" class="el-table">
<el-table-column prop="id" label="ID" />
<el-table-column prop="hex" label="身份信息哈希值" width="180" show-overflow-tooltip align="center" />
<el-table-column prop="user.password" label="用户公钥" show-overflow-tooltip align="center" />
<el-table-column
prop="issueDate"
label="证书生效时间"
width="180"
align="center"
>
<template #default="{ row }">
{{ formatDateSimple(row.issueDate) }}
</template>
</el-table-column>
<!-- 上链须知弹窗 -->
<el-dialog
v-model="state.chainDialogVisible"
title="上链须知与注意事项"
width="500px"
:append-to-body="false"
>
<div class="tip-dialog">
<h4>上链须知</h4>
<p>1. 一旦证书信息上链后将不可修改</p>
<p>2. 请务必确保所填写的证书信息准确无误</p>
<p>3. 上链信息具有公正性和透明性请谨慎操作</p>
<p>4. 如果发现错误请联系管理员进行后续处理</p>
<el-table-column
prop="expireDate"
label="证书过期时间"
width="180"
align="center"
>
<template #default="{ row }">
{{ formatDateSimple(row.expireDate) }}
</template>
</el-table-column>
<el-table-column prop="status" label="证书状态">
<template #default="{row}">
<el-tag
:type="{
'0': 'warning',
'1': 'success',
'2': 'primary'
}[row.auditStatus]"
effect="light"
>
{{
{
'0': '待验证',
'1': '已通过',
'2': '已更新'
}[row.auditStatus]
}}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作项" align="center" width="230">
<template #default="scope">
<el-button v-if="scope.row.auditStatus ==0" link type="primary" @click="update(scope.row,1)">确认</el-button>
<el-button v-if="scope.row.auditStatus !=0" link type="primary" @click="showDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页控件 -->
<div class="pagination-container">
<el-pagination
v-if="state.query.total > 0"
:current-page="state.query.page"
:page-size="state.query.limit"
:total="state.query.total"
background
layout="prev, pager, next"
@current-change="handlePageChange"
/>
</div>
<template #footer>
<el-button type="primary" @click="state.chainDialogVisible = false">我已知晓</el-button>
</template>
</el-dialog>
<!-- 添加/编辑弹窗 -->
<el-dialog
v-model="state.dialogVisible"
:title="state.dialogTitle"
width="600px"
:append-to-body="false"
>
<el-form :model="state.form" :rules="rules" ref="formRef" label-position="top">
<el-form-item label="证书" prop="certificateNumber">
<image-upload @update:imageUrl="handleImageUrl" :image-url="state.form.img" />
</el-form-item>
<el-form-item label="用户" prop="userId">
<el-select v-model="state.form.userId" placeholder="请选择">
<el-option
v-for="item in state.userList"
:key="item.id"
:label="`${item.nickName} (${item.username})`"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="签发日期" prop="issueDate">
<el-date-picker
v-model="state.form.issueDate"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item label="过期日期" prop="expireDate">
<el-date-picker
v-model="state.form.expireDate"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item label="证书详情" prop="certificateData">
<el-input type="textarea" v-model="state.form.certificateData" />
</el-form-item>
<el-form-item label="证书状态" prop="status">
<el-select v-model="state.form.status">
<el-option label="有效" :value="1" />
<el-option label="撤销" :value="0" />
<el-option label="过期" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="审核状态" prop="auditStatus">
<el-select v-model="state.form.auditStatus">
<el-option label="待审核" :value="0" />
<el-option label="审核通过" :value="1" />
<el-option label="审核拒绝" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="审核备注" prop="auditComments">
<el-input type="textarea" v-model="state.form.auditComments" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm">提交</el-button>
</template>
</el-dialog>
<!-- 详情弹窗 -->
<el-dialog
v-model="state.detailDialogVisible"
title="证书详情"
width="700px"
:append-to-body="false"
>
<el-descriptions :column="2" border>
<el-descriptions-item label="证书ID">{{ state.currentCertificate.id }}</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag
:type="{
'0': 'warning',
'1': 'success',
'2': 'primary'
}[state.currentCertificate.auditStatus]"
effect="light"
>
{{
{
'0': '待验证',
'1': '已通过',
'2': '已更新'
}[state.currentCertificate.auditStatus]
}}
</el-tag>
</el-descriptions-item>
<!-- <el-descriptions-item label="身份信息哈希值">{{ state.currentCertificate.hex }}</el-descriptions-item>-->
<!-- <el-descriptions-item label="用户公钥">{{ state.currentCertificate.user?.password }}</el-descriptions-item>-->
<el-descriptions-item label="证书生效时间">{{ formatDateSimple(state.currentCertificate.issueDate) }}</el-descriptions-item>
<el-descriptions-item label="证书过期时间">{{ formatDateSimple(state.currentCertificate.expireDate) }}</el-descriptions-item>
<el-descriptions-item label="区块链交易哈希" :span="2" v-if="state.currentCertificate.txHash">
{{ state.currentCertificate.txHash }}
<el-button type="primary" link @click="viewOnBlockchain(state.currentCertificate.txHash)">查看交易</el-button>
</el-descriptions-item>
<el-descriptions-item label="审核备注" :span="2" v-if="state.currentCertificate.auditComments">
{{ state.currentCertificate.auditComments }}
</el-descriptions-item>
</el-descriptions>
<!-- 证书图片预览 -->
<div class="certificate-image-preview" v-if="state.currentCertificate.img">
<h4>证书图片</h4>
<el-image
:src="state.currentCertificate.img"
:preview-src-list="[state.currentCertificate.img]"
fit="contain"
style="max-height: 300px;"
/>
</div>
<template #footer>
<el-button @click="state.detailDialogVisible = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, nextTick } from 'vue'
import { FormInstance, ElMessage, TabsPaneContext } from 'element-plus'
import { ref, reactive, onMounted } from 'vue'
import { FormInstance, ElMessage } from 'element-plus'
const formRef = ref<FormInstance>()
const loading = ref(true)
const state = reactive(<any>{
activeTab: 'all', // Tab
const state = reactive({
activeTab: 'all',
dialogVisible: false,
detailDialogVisible: false,
chainDialogVisible: false,
dialogTitle: "",
dialogTitle: '',
certificateList: [] as any[],
form: {
id: null,
certificateNumber: '',
userId: '',
issueDate: '',
expireDate: '',
certificateData: '',
blockchainTxId: '',
img: '',
status: 1,
auditStatus: 0, //
auditComments: '' //
},
currentCertificate: {} as any,
form: {},
query: {
page: 1,
limit: 10,
total: 1,
isBlock: 0,
auditStatus: null //
auditStatus: null
},
userList: []
})
//
function handleImageUrl(path: string) {
state.form.img = path
}
const handleClick = (tab: TabsPaneContext, event: Event) => {
state.query.page = 1
state.query.auditStatus = tab.props.name
console.log(event)
loadCertificates()
}
//
const auditStatusText = (status: number) => {
const map = { 0: '待审核', 1: '已通过', 2: '已拒绝' }
return map[status] ?? '未知状态'
}
//
const auditTagType = (status: number) => {
const typeMap = { 0: 'warning', 1: 'success', 2: 'danger' }
return typeMap[status] || ''
}
const onChain = async (row: any) => {
ElMessageBox.confirm(
'上链后证书数据将不可更改,是否继续?',
'提示',
{
confirmButtonText: '确认上链',
cancelButtonText: '取消',
type: 'warning',
}
).then(async () => {
await adminRequest.put('/sys/certificate/block', row)
ElMessage.success('上链成功')
loadCertificates()
}).catch(() => {})
}
const rules = {
certificateNumber: [{ required: true, message: '请输入证书编号', trigger: 'blur' }],
userId: [{ required: true, message: '请输入用户ID', trigger: 'blur' }],
@ -244,23 +258,33 @@ const rules = {
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
auditStatus: [{ required: true, message: '请选择审核状态', trigger: 'change' }]
}
const openForm = (isAdd: boolean, data?: any) => {
state.dialogVisible = true
state.dialogTitle = isAdd ? '添加证书' : '编辑证书'
nextTick(() => {
state.form = isAdd ? {
...state.form,
id: null,
auditStatus: 0,
auditComments: ''
} : { ...data }
function update(data: any, status: number) {
data.auditStatus = status
adminRequest.put("/sys/certificate", data).then(() => {
loadCertificates()
ElMessage.success("操作完成")
})
}
function formatDateSimple(val: string) {
if (!val) return ''
const d = new Date(val)
const yyyy = d.getFullYear()
const mm = String(d.getMonth() + 1).padStart(2, '0')
const dd = String(d.getDate()).padStart(2, '0')
return `${yyyy}-${mm}-${dd}`
}
function handleImageUrl(path: string) {
state.form.img = path
}
const handlePageChange = (page: number) => {
state.query.page = page
loadCertificates()
}
const submitForm = () => {
if (!formRef.value) return
formRef.value.validate(async (valid: boolean) => {
@ -273,15 +297,15 @@ const submitForm = () => {
}
})
}
const del = async (id: number) => {
await adminRequest.delete(`/sys/certificate/delete/${id}`)
ElMessage.success('删除成功')
loadCertificates()
const showDetail = (row: any) => {
state.currentCertificate = { ...row }
state.detailDialogVisible = true
}
const statusText = (row: any) => {
const map = { 0: '撤销', 1: '有效', 2: '过期' }
return map[row.status] ?? '未知'
const viewOnBlockchain = (txHash: string) => {
const explorerUrl = `https://blockchain-explorer.example.com/tx/${txHash}`
window.open(explorerUrl, '_blank')
}
const loadCertificates = async () => {
@ -290,7 +314,6 @@ const loadCertificates = async () => {
const res = await adminRequest.get('/sys/certificate/page', {
params: {
...state.query,
//
auditStatus: state.query.auditStatus === null ? undefined : state.query.auditStatus
}
})
@ -303,10 +326,6 @@ const loadCertificates = async () => {
}
}
const openChainInstructions = () => {
state.chainDialogVisible = true
}
onMounted(() => {
loadCertificates()
adminRequest.get(`sys/user-front/page`, { params: { limit: 9999 } })
@ -317,22 +336,10 @@ onMounted(() => {
</script>
<style scoped>
/* 新增Tabs样式 */
.el-tabs {
margin-top: 20px;
background: #fff;
padding: 0 20px;
border-radius: 4px;
.certificate-container {
padding: 20px;
}
/* 调整审核状态标签样式 */
.el-tag {
padding: 0 8px;
height: 24px;
line-height: 24px;
}
/* 其他原有样式保持不变 */
.pagination-container {
margin-top: 20px;
display: flex;
@ -342,64 +349,26 @@ onMounted(() => {
.el-table {
margin-top: 20px;
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: center;
}
.el-table {
margin-top: 20px;
}
.el-button {
margin-bottom: 20px;
}
.text-right {
text-align: right;
}
/* 弹出框样式 */
.el-dialog__header {
background-color: #409EFF;
color: #fff;
.certificate-image-preview {
margin-top: 20px;
text-align: center;
}
.el-dialog {
border-radius: 8px;
}
.el-dialog__body {
padding: 20px;
.certificate-image-preview h4 {
margin-bottom: 10px;
color: #606266;
font-weight: normal;
}
/* 表单样式 */
.el-form-item {
margin-bottom: 15px;
}
.el-input,
.el-date-picker,
.el-select {
width: 100%;
}
/* 上链须知弹框样式 */
.tip-dialog {
border: 1px solid #f0ad4e;
background-color: #fcf8e3;
border-radius: 5px;
padding: 20px;
margin-bottom: 20px;
}
.tip-dialog h4 {
margin: 0 0 10px 0;
color: #f0ad4e;
}
.tip-dialog p {
margin: 0;
font-size: 14px;
line-height: 1.5;
}
/* Tabs 样式 */
.filter-tabs {
margin-top: 20px;
}
</style>

View File

@ -1,75 +1,313 @@
<template>
<el-row>
<el-col :span="24">
<el-card>
<template #header>
<div class="card-header">
<span>平台情况</span>
</div>
</template>
<el-row :gutter="20">
<el-col v-for="item in state.list" :span="8" :key="item.name">
<div class="list-box">
<p class="first-p">{{ item.name }}</p>
<p class="second-p">
<span>{{ item.value }}</span>{{ item.tag }}
<div class="admin-container">
<!-- 主要内容区 -->
<main class="admin-main">
<!-- 统计卡片 -->
<div class="stats-cards">
<el-card v-for="stat in stats" :key="stat.title" shadow="hover">
<div class="stat-card">
<div class="stat-icon" :style="{backgroundColor: stat.color}">
<el-icon :size="24">
<component :is="stat.icon" />
</el-icon>
</div>
<div class="stat-content">
<h3>{{ stat.title }}</h3>
<p class="value">{{ stat.value }}</p>
<p class="trend" :class="{up: stat.trend > 0, down: stat.trend < 0}">
<span>{{ stat.trend > 0 ? '+' : '' }}{{ stat.trend }}%</span>
<el-icon>
<CaretTop v-if="stat.trend > 0" />
<CaretBottom v-else />
</el-icon>
</p>
</div>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
<el-row>
</el-row>
</div>
</el-card>
</div>
<!-- 最近活动 -->
<div class="recent-activities">
<el-card shadow="hover">
<template #header>
<h3>最近认证活动</h3>
</template>
<el-table :data="recentActivities" style="width: 100%">
<el-table-column prop="id" label="ID" />
<el-table-column prop="hex" label="身份信息哈希值" width="180" show-overflow-tooltip align="center" />
<el-table-column prop="user.password" label="用户公钥" show-overflow-tooltip align="center" />
<el-table-column
prop="issueDate"
label="证书生效时间"
width="180"
align="center"
>
<template #default="{ row }">
{{ formatDateSimple(row.issueDate) }}
</template>
</el-table-column>
<el-table-column
prop="expireDate"
label="证书过期时间"
width="180"
align="center"
>
<template #default="{ row }">
{{ formatDateSimple(row.expireDate) }}
</template>
</el-table-column>
<el-table-column prop="status" label="证书状态">
<template #default="{row}">
<el-tag
:type="{
'0': 'warning',
'1': 'success',
'2': 'primary'
}[row.auditStatus]"
effect="light"
>
{{
{
'0': '待验证',
'1': '已通过',
'2': '已更新'
}[row.auditStatus]
}}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</main>
</div>
</template>
<script setup lang="ts">
const state = reactive(<any>{
list: [],
row: []
});
//
onMounted(() => {
});
import { onMounted, ref } from 'vue'
import {
User, Notification, List,
CaretTop, CaretBottom,
Coin
} from '@element-plus/icons-vue'
</script>
//
const stats = ref([
{ title: '总认证数', value: '2,845', trend: 12.5, icon: User, color: '#409EFF' },
{ title: '今日认证', value: '156', trend: 8.2, icon: Notification, color: '#67C23A' },
{ title: '认证通过率', value: '92%', trend: 1.3, icon: List, color: '#E6A23C' },
{ title: '区块链节点', value: '24', trend: -2.1, icon: Coin, color: '#F56C6C' }
])
<style scoped>
.chart {
height: 400px;
//
const recentActivities = ref([
])
function formatDateSimple(val) {
if (!val) return ''
const d = new Date(val)
const yyyy = d.getFullYear()
const mm = String(d.getMonth() + 1).padStart(2, '0')
const dd = String(d.getDate()).padStart(2, '0')
return `${yyyy}-${mm}-${dd}`
}
onMounted(()=>{
adminRequest.get('/sys/certificate/page', {
params:{limit:999}
}).then(res =>{
recentActivities.value = res.data.list
})
})
.list-box {
width: 100%;
height: 150px;
border-radius: 10px;
background-image: url('/a1.png');
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.first-p {
color: white;
padding-bottom: 0.5vw;
font-size: 1.5vw;
text-align: center;
padding-top: 30px;
}
.second-p {
color: white;
display: flex;
margin-bottom: 5px;
font-size: 1.4vw;
align-items: center;
justify-content: center;
span {
margin-right: 0.3vw;
font-size: 2vw;
const statusTagType = (status: string) => {
switch(status) {
case '已通过': return 'success'
case '待验证': return 'warning'
case '已拒绝': return 'danger'
default: return 'info'
}
}
</script>
<style scoped lang="scss">
.admin-container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f7fa;
}
.admin-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
height: 64px;
background-color: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.logo {
display: flex;
align-items: center;
img {
height: 36px;
margin-right: 12px;
}
h1 {
margin: 0;
font-size: 20px;
color: #333;
}
}
.quick-nav {
ul {
display: flex;
list-style: none;
margin: 0;
padding: 0;
li {
margin: 0 8px;
a {
display: flex;
align-items: center;
padding: 8px 16px;
color: #666;
text-decoration: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
span {
margin-left: 8px;
}
&:hover {
background-color: #f5f7fa;
color: #409EFF;
}
}
&.active a {
background-color: #ecf5ff;
color: #409EFF;
}
}
}
}
.user-info {
.el-dropdown-link {
display: flex;
align-items: center;
cursor: pointer;
.username {
margin: 0 8px;
font-size: 14px;
}
}
}
}
.admin-main {
flex: 1;
padding: 20px;
overflow-y: auto;
.stats-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 20px;
.stat-card {
display: flex;
.stat-icon {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
border-radius: 8px;
color: white;
margin-right: 16px;
}
.stat-content {
h3 {
margin: 0 0 4px;
font-size: 14px;
color: #909399;
}
.value {
margin: 0 0 4px;
font-size: 24px;
font-weight: bold;
color: #303133;
}
.trend {
display: flex;
align-items: center;
margin: 0;
font-size: 12px;
&.up {
color: #67C23A;
}
&.down {
color: #F56C6C;
}
.el-icon {
margin-left: 4px;
}
}
}
}
}
.charts-section {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 16px;
margin-bottom: 20px;
.chart-header {
display: flex;
align-items: center;
justify-content: space-between;
h3 {
margin: 0;
}
}
.chart-container {
height: 300px;
}
}
.recent-activities {
.el-table {
:deep(.el-table__cell) {
padding: 12px 0;
}
}
}
}
</style>

View File

@ -1,421 +0,0 @@
<template>
<div class="certificate-verify-container">
<!-- 区块链装饰背景 -->
<div class="blockchain-background">
<div class="chain-line"></div>
<div class="chain-line delay-1"></div>
<div class="chain-line delay-2"></div>
</div>
<!-- 验证主内容 -->
<div class="verify-content">
<h2 class="verify-title">
<i class="el-icon-search"></i>
区块链证书验证
<div class="sub-title">通过区块ID验证证书真伪</div>
</h2>
<!-- 验证表单 -->
<div class="verify-form">
<el-input
v-model="blockchainId"
placeholder="请输入区块ID"
class="blockchain-input"
@keyup.enter="handleVerify"
>
<template #prefix>
<i class="el-icon-link"></i>
</template>
<template #append>
<el-button
type="primary"
@click="handleVerify"
class="verify-btn"
>
<i class="el-icon-search"></i>
立即验证
</el-button>
</template>
</el-input>
</div>
<!-- 验证结果展示 -->
<div class="verify-result" v-if="resultVisible">
<div class="result-card" :class="{'valid': isValid, 'invalid': !isValid}">
<div class="result-icon">
<i class="el-icon-success" v-if="isValid"></i>
<i class="el-icon-error" v-else></i>
</div>
<div class="result-content">
<h3>{{ resultTitle }}</h3>
<div class="result-details">
<div class="detail-item">
<label>证书编号</label>
<span>{{ state.certificateData.certificateNumber || '--' }}</span>
</div>
<div class="detail-item">
<label>签发日期</label>
<span>{{ state.certificateData.issueDate || '--' }}</span>
</div>
<div class="detail-item">
<label>过期日期</label>
<span>{{ state.certificateData.expireDate || '--' }}</span>
</div>
<div class="detail-item">
<label>当前状态</label>
<el-tag :type="statusTagType">
{{ getStatusText(state.certificateData.status) }}
</el-tag>
</div>
</div>
</div>
</div>
<!-- 区块链信息 -->
<div class="blockchain-info" >
<div class="info-item">
<i class="el-icon-cpu"></i>
<span>区块高度{{ state.certificateData.blockchainTxId.length }}</span>
</div>
<div class="info-item">
<i class="el-icon-timer"></i>
<span>详情{{ state.certificateData.certificateData }}</span>
</div>
<div class="info-item">
<i class="el-icon-document-checked"></i>
<span>交易哈希{{ shortenHash(state.certificateData.blockchainTxId) }}</span>
</div>
</div>
<div class="blockchain-info" >
<el-image
style="width: 300px; height: 200px;display: block;margin: 0 auto"
:src="state.certificateData.img"
fit="cover"
:preview-teleported="true"
/>
</div>
</div>
<!-- 使用说明 -->
<div class="usage-guide">
<el-steps :active="2" align-center>
<el-step title="获取区块ID" description="从证书详情页复制区块ID" />
<el-step title="输入验证" description="在输入框中粘贴区块ID" />
<el-step title="查看结果" description="获取证书验证信息" />
</el-steps>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'
const blockchainId = ref('')
const resultVisible = ref(false)
const isValid = ref(false)
const certificateData = ref<any>({})
const blockchainInfo = ref<any>({})
const state =reactive(<any>{
certificateData:{}
})
const getStatusText = (status: number | string) => {
switch (String(status)) {
case '1':
return '有效'
case '0':
return '撤销'
case '2':
return '过期'
default:
return '--'
}
}
//
const statusTagType = computed(() => {
const status = certificateData.value.status
switch (status) {
case 1:
return 'success' // 绿 -
case 0:
return 'info' // -
case 2:
return 'warning' // -
default:
return 'danger' // -
}
})
//
const resultTitle = computed(() => {
return isValid.value ? '证书验证通过' : '证书验证失败'
})
//
const handleVerify = async () => {
if (!blockchainId.value) {
ElMessage.warning('请输入区块ID')
return
}
//
frontRequest.get(`/api/certificate/${blockchainId.value}`).then(res =>{
state.certificateData = res.data
blockchainInfo.value = res.data.blockchain
isValid.value =true
resultVisible.value = true
})
}
//
const shortenHash = (hash: string) => {
return hash ? `${hash.substring(0, 8)}...${hash.substring(hash.length - 8)}` : ''
}
</script>
<style scoped lang="scss">
.certificate-verify-container {
position: relative;
min-height: 100vh;
padding: 2rem;
background: linear-gradient(135deg, #f8f9fa, #ffffff);
overflow: hidden;
.blockchain-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
.chain-line {
position: absolute;
height: 1px;
background: linear-gradient(90deg, transparent, #409EFF 50%, transparent);
animation: chainFlow 3s linear infinite;
&:nth-child(1) { top: 20%; width: 80%; }
&:nth-child(2) { top: 50%; width: 60%; }
&:nth-child(3) { top: 80%; width: 70%; }
&.delay-1 { animation-delay: -1s; }
&.delay-2 { animation-delay: -2s; }
}
}
.verify-content {
position: relative;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
background: rgba(255,255,255,0.95);
border-radius: 1.5rem;
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
z-index: 1;
}
.verify-title {
text-align: center;
margin-bottom: 2rem;
color: #1a237e;
font-size: 2rem;
i {
font-size: 2.5rem;
vertical-align: middle;
margin-right: 0.5rem;
}
.sub-title {
font-size: 1rem;
color: #666;
margin-top: 0.5rem;
font-weight: normal;
}
}
.verify-form {
margin: 2rem 0;
.blockchain-input {
:deep(.el-input__inner) {
height: 50px;
font-size: 1rem;
border-radius: 12px;
padding-left: 2.5rem;
}
.el-input__prefix {
left: 12px;
color: #409EFF;
}
.verify-btn {
height: 50px;
padding: 0 2rem;
font-size: 1rem;
border-radius: 0 12px 12px 0;
background: linear-gradient(135deg, #409EFF, #1a237e);
border: none;
}
}
}
.verify-result {
margin: 2rem 0;
.result-card {
padding: 2rem;
border-radius: 1rem;
background: #f8f9fa;
display: flex;
align-items: center;
gap: 1.5rem;
&.valid {
border-left: 6px solid #67c23a;
}
&.invalid {
border-left: 6px solid #f56c6c;
}
.result-icon {
i {
font-size: 3rem;
&.el-icon-success {
color: #67c23a;
}
&.el-icon-error {
color: #f56c6c;
}
}
}
.result-content {
flex: 1;
h3 {
margin: 0 0 1rem 0;
color: #1a237e;
}
.result-details {
.detail-item {
margin: 0.5rem 0;
label {
color: #666;
min-width: 80px;
display: inline-block;
}
}
}
}
}
.blockchain-info {
margin-top: 1.5rem;
padding: 1.5rem;
background: rgba(245, 247, 250, 0.8);
border-radius: 1rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
.info-item {
display: flex;
align-items: center;
gap: 0.5rem;
i {
font-size: 1.2rem;
color: #409EFF;
}
span {
color: #666;
}
}
}
}
.usage-guide {
margin-top: 3rem;
h3 {
text-align: center;
color: #1a237e;
margin-bottom: 1.5rem;
i {
margin-right: 0.5rem;
}
}
:deep(.el-step) {
.el-step__head {
.el-step__icon {
background: #409EFF;
color: white;
border: none;
}
}
.el-step__title {
color: #1a237e;
font-weight: bold;
}
.el-step__description {
color: #666;
}
}
}
}
@keyframes chainFlow {
from { left: -100%; }
to { left: 100%; }
}
@media (max-width: 768px) {
.certificate-verify-container {
padding: 1rem;
.verify-content {
padding: 1.5rem;
}
.verify-form {
.blockchain-input {
:deep(.el-input__inner) {
font-size: 0.9rem;
}
.verify-btn {
padding: 0 1rem;
font-size: 0.9rem;
}
}
}
.blockchain-info {
grid-template-columns: 1fr;
}
}
}
</style>
<route lang="json">
{
"meta": {
"layout": "front"
}
}
</route>

View File

@ -1,151 +1,124 @@
<template>
<div class="certificate-container">
<!-- 区块链装饰元素 -->
<div class="blockchain-decor">
<div class="chain-line"></div>
<div class="chain-line delay-1"></div>
<div class="chain-line delay-2"></div>
</div>
<h2 class="page-title">
<i class="el-icon-connection"></i>
区块链证书存证
<div class="sub-title">数据上链后永久保存不可篡改</div>
</h2>
<el-form
label-position="right"
:model="form"
:rules="rules"
ref="formRef"
label-position="top"
class="certificate-form"
label-width="auto"
>
<!-- 表单内容 -->
<el-form-item label="证书编号" prop="certificateNumber">
<el-input
v-model="form.certificateNumber"
placeholder="请输入证书编号"
class="block-input"
>
<template #prefix>
<i class="el-icon-document"></i>
</template>
</el-input>
</el-form-item>
<div class="form-row">
<el-form-item label="签发日期" prop="issueDate" class="date-picker">
<el-date-picker
v-model="form.issueDate"
type="datetime"
placeholder="选择签发日期"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
<div style="width: 60%;margin: 0 auto">
<el-form-item label="名称:" prop="name">
<el-input
v-model="form.name"
placeholder="请输入证书名称"
class="block-input"
>
<template #prefix>
<i class="el-icon-date"></i>
<i class="el-icon-document"></i>
</template>
</el-date-picker>
</el-input>
</el-form-item>
<el-form-item label="公司/组织:" prop="company">
<el-input
v-model="form.company"
placeholder="请输入公司/组织"
class="block-input"
>
<template #prefix>
<i class="el-icon-document"></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="部门:" prop="department">
<el-input
v-model="form.department"
placeholder="请输入部门"
class="block-input"
>
<template #prefix>
<i class="el-icon-document"></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="角色:" prop="role">
<el-input
v-model="form.role"
placeholder="请输入角色"
class="block-input"
>
<template #prefix>
<i class="el-icon-document"></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="资质证明:" prop="role">
<el-upload
class="upload-demo"
style="width: 100%"
drag
:action="state.path"
multiple
limit=1
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
</el-upload>
</el-form-item>
<el-form-item label="过期日期" prop="expireDate" class="date-picker">
<el-form-item label="生效时间:" prop="issueDate">
<el-date-picker
style="width: 100%"
v-model="form.issueDate"
type="datetime"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="过期时间:" prop="expireDate">
<el-date-picker
style="width: 100%"
v-model="form.expireDate"
type="datetime"
placeholder="选择过期日期"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="公钥:" prop="hex">
<el-input
v-model="form.hex"
placeholder="请输入公钥"
class="block-input"
>
<template #prefix>
<i class="el-icon-time"></i>
<i class="el-icon-document"></i>
</template>
</el-date-picker>
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">删除</el-button>
</el-form-item>
</div>
<el-form-item label="证书详情" prop="certificateData">
<el-input
type="textarea"
v-model="form.certificateData"
placeholder="请输入证书详情"
:rows="4"
class="block-textarea"
/>
</el-form-item>
<el-form-item label="证书图片" prop="img">
<image-upload
@update:imageUrl="handleImageUrl"
:image-url="form.img"
class="block-uploader"
/>
</el-form-item>
<el-form-item class="form-actions">
<el-button
type="primary"
@click="submitForm"
class="submit-btn"
>
<i class="el-icon-upload"></i>
提交上链
</el-button>
<el-button
@click="resetForm"
class="reset-btn"
>
<i class="el-icon-refresh"></i>
重置表单
</el-button>
</el-form-item>
</el-form>
<!-- 区块链特性说明 -->
<div class="blockchain-features">
<div class="feature-item">
<i class="el-icon-lock"></i>
<h3>数据安全</h3>
<p>采用区块链加密技术保障信息安全</p>
</div>
<div class="feature-item">
<i class="el-icon-coin"></i>
<h3>永久存证</h3>
<p>数据一旦上链将永久保存不可篡改</p>
</div>
<div class="feature-item">
<i class="el-icon-share"></i>
<h3>可信验证</h3>
<p>可通过区块链浏览器验证证书真伪</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance } from 'element-plus'
const formRef = ref<FormInstance>()
const form = reactive(<any>{})
const form = reactive({
certificateNumber: '',
issueDate: '',
expireDate: '',
certificateData: '',
img: ''
const state = reactive({
path:import.meta.env.VITE_API_FRONT_BASE_URL + "/api/upload",
})
const rules = {
certificateNumber: [{ required: true, message: '请输入证书编号', trigger: 'blur' }],
issueDate: [{ required: true, message: '请选择签发日期', trigger: 'change' }],
expireDate: [{ required: true, message: '请选择过期日期', trigger: 'change' }],
certificateData: [{ required: true, message: '请输入证书详情', trigger: 'blur' }],
img: [{ required: true, message: '请上传证书图片', trigger: 'change' }]
}
function handleImageUrl(path: string) {
form.img = path
certificateData: [{ required: true, message: '请输入证书详情', trigger: 'blur' }]
}
const submitForm = () => {
@ -158,7 +131,7 @@ const submitForm = () => {
<h3>请确认证书信息</h3>\
<p>数据上链后不可修改请仔细核对以下信息</p>\
<ul>\
<li>证书编号'+form.certificateNumber+'</li>\
<li>名称'+form.name+'</li>\
<li>签发日期'+form.issueDate+'</li>\
</ul>\
</div>',
@ -196,232 +169,14 @@ const submitForm = () => {
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields()
form.img = ''
}
}
</script>
<style scoped lang="scss">
.certificate-container {
position: relative;
max-width: 800px;
margin: 2rem auto;
padding: 2.5rem;
background: rgba(255,255,255,0.95);
border-radius: 1.5rem;
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
overflow: hidden;
z-index: 1;
.blockchain-decor {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
.chain-line {
position: absolute;
height: 1px;
background: linear-gradient(90deg, transparent, #409EFF 50%, transparent);
animation: chainFlow 3s linear infinite;
&:nth-child(1) { top: 20%; width: 80%; }
&:nth-child(2) { top: 50%; width: 60%; }
&:nth-child(3) { top: 80%; width: 70%; }
&.delay-1 { animation-delay: -1s; }
&.delay-2 { animation-delay: -2s; }
}
}
.page-title {
text-align: center;
margin-bottom: 2rem;
color: #1a237e;
font-size: 2rem;
position: relative;
.el-icon-connection {
font-size: 2.5rem;
vertical-align: middle;
margin-right: 0.5rem;
}
.sub-title {
font-size: 1rem;
color: #666;
margin-top: 0.5rem;
font-weight: normal;
}
}
.certificate-form {
position: relative;
z-index: 2;
.form-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
}
:deep(.block-input) {
.el-input__inner {
background: rgba(245, 247, 250, 0.8);
border: 1px solid #e0e0e0;
border-radius: 0.75rem;
padding-left: 2.5rem;
transition: all 0.3s ease;
&:hover {
border-color: #409EFF;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}
}
.el-input__prefix {
left: 12px;
color: #409EFF;
}
}
.block-textarea {
:deep(.el-textarea__inner) {
background: rgba(245, 247, 250, 0.8);
border-radius: 0.75rem;
min-height: 100px;
resize: vertical;
}
}
.form-actions {
margin-top: 2rem;
text-align: center;
.submit-btn {
padding: 12px 30px;
font-size: 1rem;
background: linear-gradient(135deg, #409EFF, #1a237e);
border: none;
border-radius: 0.75rem;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
}
}
.reset-btn {
padding: 12px 30px;
background: rgba(245, 247, 250, 0.8);
border-color: #e0e0e0;
border-radius: 0.75rem;
}
}
}
.blockchain-features {
margin-top: 3rem;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
.feature-item {
text-align: center;
padding: 1.5rem;
background: rgba(245, 247, 250, 0.8);
border-radius: 1rem;
transition: all 0.3s ease;
&:hover {
transform: translateY(-5px);
box-shadow: 0 6px 16px rgba(0,0,0,0.08);
}
i {
font-size: 2.5rem;
color: #409EFF;
margin-bottom: 1rem;
}
h3 {
color: #1a237e;
margin: 0.5rem 0;
}
p {
color: #666;
font-size: 0.9rem;
margin: 0;
}
}
}
}
@keyframes chainFlow {
from { left: -100%; }
to { left: 100%; }
}
@media (max-width: 768px) {
.certificate-container {
margin: 1rem;
padding: 1.5rem;
.form-row {
grid-template-columns: 1fr !important;
}
.blockchain-features {
grid-template-columns: 1fr;
}
}
}
</style>
<style>
/* 全局弹窗样式 */
.chain-confirm-dialog {
border-radius: 1rem !important;
background: linear-gradient(135deg, #f8f9fa, #ffffff) !important;
.confirm-box {
text-align: center;
i {
font-size: 3rem;
color: #409EFF;
margin-bottom: 1rem;
}
h3 {
color: #1a237e;
margin: 1rem 0;
}
ul {
text-align: left;
padding-left: 1.5rem;
color: #666;
}
}
}
.chain-success-msg {
background: linear-gradient(135deg, #409EFF, #1a237e) !important;
color: white !important;
border-radius: 0.75rem !important;
}
.form-error-msg {
background: #fff5f5 !important;
border: 1px solid #ffc4c4 !important;
color: #ff4444 !important;
}
</style>
<route lang="json">
{

View File

@ -1,445 +1,66 @@
<template>
<div class="audit-record-container">
<!-- 区块链装饰背景 -->
<div class="blockchain-background">
<div class="chain-line"></div>
<div class="chain-line delay-1"></div>
<div class="chain-line delay-2"></div>
</div>
<!-- 审核记录主内容 -->
<div class="audit-content">
<h2 class="audit-title">
<i class="el-icon-notebook-2"></i>
证书审核记录
<div class="sub-title">查看您的证书审核历史记录</div>
</h2>
<!-- 筛选条件 -->
<div class="filter-bar">
<el-select
v-model="filter.auditStatus"
placeholder="按状态筛选"
class="auditStatus-filter"
clearable
<el-table :data="state.tableData" style="width: 100%" align="center">
<el-table-column prop="id" label="ID" show-overflow-tooltip align="center" />
<el-table-column prop="hex" label="身份信息哈希值" width="180" show-overflow-tooltip align="center" />
<el-table-column prop="name" label="颁发者" show-overflow-tooltip align="center" />
<el-table-column prop="issueDate" label="证书生效时间" width="120" align="center" />
<el-table-column prop="expireDate" label="证书过期时间" width="120" align="center"/>
<el-table-column prop="status" label="证书状态" />
<el-table-column label="操作项" min-width="180" align="center">
<template #default="scope">
<el-button
link
type="primary"
size="small"
@click.prevent="deleteRow(scope.$index)"
>
<el-option label="待审核" :value="0" />
<el-option label="审核通过" :value="1" />
<el-option label="审核拒绝" :value="2" />
</el-select>
生成签名信息
</el-button>
</div>
<!-- 审核记录列表 -->
<div class="record-list">
<el-card
v-for="record in filteredRecords"
:key="record.id"
class="record-card"
:class="record.auditStatusClass"
<el-button
link
type="primary"
size="small"
@click.prevent="deleteRow(scope.$index)"
>
<div class="card-header">
<div class="record-auditStatus">
<i :class="record.auditStatusIcon"></i>
<span>{{ record.auditStatusText }}</span>
</div>
<div class="record-date">
{{ record.createdAt }}
</div>
</div>
<div class="card-body">
<div class="record-info">
<div class="info-item">
<label>证书编号</label>
<span>{{ record.certificateNumber }}</span>
</div>
<div class="info-item">
<label>签发日期</label>
<span>{{ record.issueDate }}</span>
</div>
<div class="info-item">
<label>过期日期</label>
<span>{{ record.expireDate || '--' }}</span>
</div>
</div>
<div class="audit-comment" v-if="record.auditComments">
<i class="el-icon-chat-line-round"></i>
<span>审核意见{{ record.auditComments }}</span>
</div>
</div>
<div class="card-footer">
<el-button type="text" @click="viewDetail(record)" class="detail-btn">
<i class="el-icon-view"></i>
查看详情
</el-button>
</div>
</el-card>
<!-- 空状态 -->
<div class="empty-state" v-if="filteredRecords.length === 0">
<i class="el-icon-document-remove"></i>
<p>暂无审核记录</p>
</div>
</div>
</div>
<!-- 查看详情弹窗 -->
<el-dialog
v-model="detailDialogVisible"
title="审核记录详情"
width="600px"
:close-on-click-modal="false"
>
<div class="detail-content" v-if="selectedRecord">
<div class="detail-item">
<label>证书编号</label>
<span>{{ selectedRecord.certificateNumber }}</span>
</div>
<div class="detail-item">
<label>签发日期</label>
<span>{{ selectedRecord.issueDate }}</span>
</div>
<div class="detail-item">
<label>过期日期</label>
<span>{{ selectedRecord.expireDate || '--' }}</span>
</div>
<div class="detail-item">
<label>审核状态</label>
<el-tag :type="getAuditTagType(selectedRecord.auditStatus)">
{{ getAuditStatusText(selectedRecord.auditStatus) }}
</el-tag>
</div>
<div class="detail-item" v-if="selectedRecord.auditComments">
<label>审核意见</label>
<span>{{ selectedRecord.auditComments }}</span>
</div>
<div class="detail-item">
<label>记录日期</label>
<span>{{ selectedRecord.createdAt }}</span>
</div>
<div class="detail-item">
<el-image :src="selectedRecord.img"></el-image>
</div>
</div>
<template #footer>
<el-button @click="detailDialogVisible = false">关闭</el-button>
下载证书
</el-button>
</template>
</el-dialog>
</div>
</el-table-column>
</el-table>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
//
const records = ref([
])
//
const filter = ref({
auditStatus: null,
dateRange: []
})
//
const auditStatusConfig = {
0: { text: '待审核', icon: 'el-icon-time', class: 'pending' },
1: { text: '通过', icon: 'el-icon-success', class: 'approved' },
2: { text: '拒绝', icon: 'el-icon-error', class: 'rejected' }
}
//
const processedRecords = computed(() => {
return records.value.map(record => {
const auditStatusInfo = auditStatusConfig[record.auditStatus] || {}
return {
...record,
auditStatusText: auditStatusInfo.text,
auditStatusIcon: auditStatusInfo.icon,
auditStatusClass: auditStatusInfo.class
const state = reactive({
tableData:[
{
id:"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
hex:"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
name:"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
issueDate:"2023-10-25",
expireDate:"2025-10-25",
status:"已通过",
}, {
id:"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
hex:"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
name:"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
issueDate:"2023-10-25",
expireDate:"2025-10-25",
status:"已通过",
}, {
id:"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
hex:"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
name:"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
issueDate:"2023-10-25",
expireDate:"2025-10-25",
status:"已通过",
}
})
]
})
//
const filteredRecords = computed(() => {
return processedRecords.value.filter(record => {
//
if (filter.value.auditStatus !== null && record.auditStatus !== filter.value.auditStatus) {
return false
}
//
if (filter.value.dateRange && filter.value.dateRange.length === 2) {
const [start, end] = filter.value.dateRange
const recordDate = new Date(record.createdAt)
if (recordDate < new Date(start) || recordDate > new Date(end)) {
return false
}
}
return true
})
})
//
const detailDialogVisible = ref(false)
const selectedRecord = ref<any>(null)
const viewDetail = (record: any) => {
selectedRecord.value = record
detailDialogVisible.value = true
}
//
const getAuditStatusText = (status: number) => {
const mapping: { [key: number]: string } = {
0: '待审核',
1: '通过',
2: '拒绝'
}
return mapping[status] || '--'
}
//
const getAuditTagType = (status: number) => {
switch (status) {
case 0:
return 'warning'
case 1:
return 'success'
case 2:
return 'danger'
default:
return 'info'
}
}
//
function init() {
frontRequest.get("/api/certificate/page", { params: { limit: 999 } }).then(res => {
records.value = res.data.list
})
}
init()
</script>
<style scoped lang="scss">
.audit-record-container {
position: relative;
min-height: 100vh;
padding: 2rem;
background: linear-gradient(135deg, #f8f9fa, #ffffff);
overflow: hidden;
.blockchain-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
.chain-line {
position: absolute;
height: 1px;
background: linear-gradient(90deg, transparent, #409EFF 50%, transparent);
animation: chainFlow 3s linear infinite;
&:nth-child(1) { top: 20%; width: 80%; }
&:nth-child(2) { top: 50%; width: 60%; }
&:nth-child(3) { top: 80%; width: 70%; }
&.delay-1 { animation-delay: -1s; }
&.delay-2 { animation-delay: -2s; }
}
}
.audit-content {
position: relative;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
background: rgba(255, 255, 255, 0.95);
border-radius: 1.5rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
z-index: 1;
}
.audit-title {
text-align: center;
margin-bottom: 2rem;
color: #1a237e;
font-size: 2rem;
i {
font-size: 2.5rem;
vertical-align: middle;
margin-right: 0.5rem;
}
.sub-title {
font-size: 1rem;
color: #666;
margin-top: 0.5rem;
font-weight: normal;
}
}
.filter-bar {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
.auditStatus-filter,
.date-filter {
flex: 1;
}
}
.record-list {
.record-card {
margin-bottom: 1.5rem;
border-left: 6px solid transparent;
transition: all 0.3s ease;
&.pending { border-color: #e6a23c; background: #fffaf0; }
&.approved { border-color: #67c23a; background: #f0fff4; }
&.rejected { border-color: #f56c6c; background: #fff0f0; }
&:hover {
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 1rem;
border-bottom: 1px solid #eee;
.record-auditStatus {
display: flex;
align-items: center;
gap: 0.5rem;
i {
font-size: 1.2rem;
&.el-icon-success { color: #67c23a; }
&.el-icon-error { color: #f56c6c; }
&.el-icon-time { color: #e6a23c; }
}
span {
font-weight: bold;
}
}
.record-date {
color: #666;
font-size: 0.9rem;
}
}
.card-body {
padding: 1rem 0;
.record-info {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1rem;
.info-item {
label {
color: #666;
margin-right: 0.5rem;
}
}
}
.audit-comment {
padding: 1rem;
background: #f8f9fa;
border-radius: 0.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
i {
color: #409EFF;
}
}
}
.card-footer {
text-align: right;
.detail-btn {
color: #409EFF;
}
}
}
.empty-state {
text-align: center;
padding: 3rem;
color: #666;
i {
font-size: 3rem;
margin-bottom: 1rem;
}
p {
margin: 0;
}
}
}
}
@keyframes chainFlow {
from { left: -100%; }
to { left: 100%; }
}
@media (max-width: 768px) {
.audit-record-container {
padding: 1rem;
.audit-content {
padding: 1.5rem;
}
.filter-bar {
flex-direction: column;
}
}
}
/* 弹窗详情样式 */
.detail-content {
.detail-item {
margin-bottom: 1rem;
display: flex;
align-items: center;
label {
width: 120px;
font-weight: bold;
color: #555;
}
span {
flex: 1;
color: #333;
}
}
}
</style>
<route lang="json">

View File

@ -71,7 +71,7 @@ const onLogin = () => {
<style scoped>
.module_r {
width: 100%;
background: url("/ec13203e-20ee-4bb3-ba23-f0b6f93d3760.jpg");
background: url("/508d288b-cd6e-4814-a99f-53a3999c4261.jpg");
background-size: 100% 100%;
height: 100vh;
display: flex;

View File

@ -21,21 +21,53 @@
<input class="module_input" type="password" placeholder="再次输入密码" v-model="register.confirmPassword" />
</div>
</div>
<div class="forgetpwd" @click="router.push('/login')">有账号</div>
<button class="module_button" @click="onRegister">注册</button>
</div>
<!-- 注册成功弹窗 -->
<el-dialog
v-model="showKeyDialog"
title="注册成功"
width="50%"
>
<el-form label-width="auto" style="max-width: 600px">
<el-form-item label="账户地址">
<el-input v-model="state.getUserInfo.hex" />
</el-form-item>
<el-form-item label="公钥">
<el-input type="password" show-password v-model="state.getUserInfo.publicKey" />
</el-form-item>
<el-form-item label="私钥">
<el-input v-model="state.getUserInfo.privateKey" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showKeyDialog = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { useRouter } from "vue-router";
import { registerFront } from '~/api/user/frontUserApi'
const router = useRouter();
const register = reactive({
username: '',
password: '',
confirmPassword: '',
})
const showKeyDialog = ref(true)
const state = reactive({
getUserInfo:{
"hex": "1893836584118175698",
"nickName": "12345678",
"publicKey": "MIIBIjANBgkqhkiG9w0BAQ",
"privateKey": "MBAAECggE",
"username": "12345678"
}
})
/**
* 注册成功
@ -45,9 +77,25 @@ const onRegister = () => {
ElMessage.warning("不能创建账号Admin")
return
}
registerFront(register).then(() =>{
frontRequest.post("/api/user/register", register).then(res =>{
ElMessage.success("注册成功~")
router.push("/login")
state.getUserInfo = res.data
showKeyDialog.value = true
// router.push("/login")
})
}
/**
* 复制所有密钥信息
*/
const copyAllKeys = () => {
const text = `账号地址: ${state.getUserInfo.hex}\n公钥: ${state.getUserInfo.publicKey}\n私钥: ${state.getUserInfo.privateKey}`
navigator.clipboard.writeText(text).then(() => {
ElMessage.success('已复制到剪贴板')
}).catch(err => {
ElMessage.error('复制失败: ' + err)
})
}
</script>
@ -58,7 +106,7 @@ const onRegister = () => {
align-items: center;
justify-content: center;
height: 100vh;
background: url("/ec13203e-20ee-4bb3-ba23-f0b6f93d3760.jpg");
background: url("/508d288b-cd6e-4814-a99f-53a3999c4261.jpg");
background-size: 100% 100%;
.module_mian{
width: 26%;

View File

@ -1,93 +0,0 @@
<!--用户详情-->
<template>
<el-form
ref="formRef"
style="max-width: 600px"
:model="state.userInfo"
label-width="auto"
label-position="top"
>
<el-form-item
prop="username"
label="用户名"
:rules="[
{
required: true,
message: '用户名不能为空',
trigger: 'blur',
},
]"
>
<el-input v-model="state.userInfo.username" />
</el-form-item>
<el-form-item
prop="nickName"
label="用户昵称"
:rules="[
{
required: true,
message: '用户昵称不能为空',
trigger: 'blur',
}
]"
>
<el-input v-model="state.userInfo.nickName" />
</el-form-item>
<el-form-item
prop="password"
label="密码"
>
<el-input v-model="state.userInfo.password" type="password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(formRef)">提交</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import type { FormInstance } from 'element-plus'
import { userInfoFront, userUpdateFront } from '~/api/user/frontUserApi'
const formRef = ref<FormInstance>()
const state = reactive({
userInfo: {}
})
function init() {
userInfoFront().then(res =>{
state.userInfo = res.data
})
}
onMounted(()=>{
init()
})
/**
* 提交
* @param formEl
*/
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
userUpdateFront(state.userInfo).then(res =>{
toast.success("修改成功~")
init()
}).catch(err=>{
})
}
})
}
</script>
<style scoped>
</style>
<route lang="json">
{
"meta": {
"layout": "frontUserInfo"
}
}
</route>

View File

@ -8,22 +8,22 @@ export const getAdminList = () => {
},
{
path: "/admin/certificates",
name: "证书管理",
name: "证书验证",
icon: "Postcard",
},
{
path: "/admin/block",
name: "上链信息",
name: "证书查询",
icon: "Postcard",
},
{
path: "/admin/log",
name: "申请记录",
path: "/admin/user",
name: "个人中心",
icon: "Postcard",
},
{
path: "/admin/adminuser",
name: "用户管理",
path: "/admin/about",
name: "关于我们",
icon: "Notification",
}
];
@ -33,28 +33,28 @@ export const getFrontList = () => {
const routes = [
{
path: "/",
name: "证书信息",
icon: "Dashboard",
name: "首页",
icon: "House",
},
{
path: "/load",
name: "证书提交",
icon: "Dashboard",
name: "证书申请",
icon: "Notification",
},
{
path: "/log",
name: "审核记录",
icon: "ShoppingCart",
name: "证书查询",
icon: "MessageBox",
},
{
path: "/audit",
name: "证书验证",
icon: "Warehouse",
path: "/user",
name: "个人中心",
icon: "MapLocation",
},
{
path: "/look",
name: "查看证书",
icon: "Warehouse",
path: "/about",
name: "关于我们",
icon: "ScaleToOriginal",
}
]
return routes;