mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-08 12:39:05 +08:00
feat(pigx-upms-biz): 新增密码规则预设功能
This commit is contained in:
1 parent
1b87ae9787
commit
3132cc3784
97 files changed
+2721
-462
No files matched your search
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.api.constant;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Clarity 数据拉取状态枚举
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-31
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum ClarityFetchStatus {
|
||||
|
||||
/**
|
||||
* 占位中:已插入占位行,API 调用尚未完成
|
||||
*/
|
||||
PENDING("pending", "占位中"),
|
||||
|
||||
/**
|
||||
* 成功:API 调用成功,数据已写入
|
||||
*/
|
||||
SUCCESS("success", "成功"),
|
||||
|
||||
/**
|
||||
* 失败:API 调用失败,下次调度可重试
|
||||
*/
|
||||
FAILED("failed", "失败");
|
||||
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private final String value;
|
||||
|
||||
private final String desc;
|
||||
|
||||
}
|
||||
+1
-1
@@ -48,6 +48,6 @@ public enum SystemConfigTypeEnum {
|
||||
WETALK("wetalk");
|
||||
|
||||
@Getter
|
||||
private final String value;
|
||||
private final String value;
|
||||
|
||||
}
|
||||
+10
@@ -59,11 +59,21 @@ public interface UpmsErrorCodes {
|
||||
|
||||
String SYS_MENU_BUTTON_CANNOT_HOME = "sys.menu.button.cannot.home";
|
||||
|
||||
String SYS_MENU_SORT_DUPLICATE = "sys.menu.sort.duplicate";
|
||||
|
||||
String SYS_MENU_SORT_SCOPE_INVALID = "sys.menu.sort.scopeInvalid";
|
||||
|
||||
// === 字典相关 ===
|
||||
String SYS_DICT_DELETE_SYSTEM = "sys.dict.delete.system";
|
||||
|
||||
String SYS_DICT_UPDATE_SYSTEM = "sys.dict.update.system";
|
||||
|
||||
String SYS_DICT_NOT_FOUND = "sys.dict.notFound";
|
||||
|
||||
String SYS_DICT_ITEM_SORT_DUPLICATE = "sys.dict.item.sort.duplicate";
|
||||
|
||||
String SYS_DICT_ITEM_SORT_SCOPE_INVALID = "sys.dict.item.sort.scopeInvalid";
|
||||
|
||||
// === 参数相关 ===
|
||||
String SYS_PARAM_CONFIG_ERROR = "sys.param.config.error";
|
||||
|
||||
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package com.pig4cloud.pigx.admin.api.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* AI 聊天消息 DTO
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2025/02/20
|
||||
*/
|
||||
@Data
|
||||
public class AiChatMessageDTO {
|
||||
|
||||
/**
|
||||
* 消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 网络搜索
|
||||
*/
|
||||
private boolean webSearch;
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 创建 API 密钥请求体
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-23
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "创建API密钥请求")
|
||||
public class ApiKeyCreateDTO {
|
||||
|
||||
@Schema(description = "Key名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "IP白名单,多个以逗号分隔")
|
||||
private String allowedIps;
|
||||
|
||||
@Schema(description = "过期时间")
|
||||
private LocalDateTime expiresAt;
|
||||
|
||||
@NotBlank(message = "当前密码不能为空")
|
||||
@Schema(description = "当前登录用户密码(用于二次验证)")
|
||||
private String password;
|
||||
|
||||
}
|
||||
+10
@@ -72,4 +72,14 @@ public class MessageEmailDTO {
|
||||
*/
|
||||
private String bizCode;
|
||||
|
||||
/**
|
||||
* 图形验证码类型
|
||||
*/
|
||||
private String captchaType;
|
||||
|
||||
/**
|
||||
* 图形验证码凭据
|
||||
*/
|
||||
private String captchaVerification;
|
||||
|
||||
}
|
||||
+2
-2
@@ -53,7 +53,7 @@ public class MessageHookDTO {
|
||||
|
||||
/**
|
||||
* 图片链接
|
||||
*/
|
||||
private String picUrl;
|
||||
*/
|
||||
private String picUrl;
|
||||
|
||||
}
|
||||
+16
-2
@@ -40,6 +40,20 @@ public class MessageSmsDTO {
|
||||
@Setter
|
||||
private LinkedHashMap<String, String> params;
|
||||
|
||||
/**
|
||||
* 图形验证码类型
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private String captchaType;
|
||||
|
||||
/**
|
||||
* 图形验证码凭据
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private String captchaVerification;
|
||||
|
||||
/**
|
||||
* ext 参数
|
||||
*/
|
||||
@@ -57,8 +71,8 @@ public class MessageSmsDTO {
|
||||
|
||||
// 静态方法用于创建Builder实例
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
// Builder类
|
||||
public static class Builder {
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
package com.pig4cloud.pigx.admin.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字典项排序DTO
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026/5/21
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "字典项排序")
|
||||
public class SysDictItemSortDTO {
|
||||
|
||||
/**
|
||||
* 字典ID
|
||||
*/
|
||||
@NotNull(message = "字典ID不能为空")
|
||||
@Schema(description = "字典ID")
|
||||
private Long dictId;
|
||||
|
||||
/**
|
||||
* 当前页字典项ID列表,按目标顺序排列
|
||||
*/
|
||||
@NotEmpty(message = "字典项ID列表不能为空")
|
||||
@Schema(description = "字典项ID列表")
|
||||
private List<Long> itemIds;
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
package com.pig4cloud.pigx.admin.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 菜单排序DTO
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026/5/21
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "菜单排序")
|
||||
public class SysMenuSortDTO {
|
||||
|
||||
/**
|
||||
* 父菜单ID
|
||||
*/
|
||||
@NotNull(message = "菜单父ID不能为空")
|
||||
@Schema(description = "父菜单ID")
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 同级菜单ID列表,按目标顺序排列
|
||||
*/
|
||||
@NotEmpty(message = "菜单ID列表不能为空")
|
||||
@Schema(description = "菜单ID列表")
|
||||
private List<Long> menuIds;
|
||||
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.api.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.pig4cloud.pigx.common.core.util.TenantTable;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* API密钥管理
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-23
|
||||
*/
|
||||
@Data
|
||||
@TenantTable
|
||||
@Schema(description = "API密钥")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SysApiKey extends Model<SysApiKey> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "主键")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "所属用户ID")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "所属用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "Key名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "SHA-256哈希值")
|
||||
private String apiKeyHash;
|
||||
|
||||
@Schema(description = "IP白名单")
|
||||
private String allowedIps;
|
||||
|
||||
@Schema(description = "过期时间")
|
||||
private LocalDateTime expiresAt;
|
||||
|
||||
@Schema(description = "状态:0-正常,1-禁用")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "最后使用时间")
|
||||
private LocalDateTime lastUsedAt;
|
||||
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "删除标记,1:已删除,0:正常")
|
||||
private String delFlag;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建者")
|
||||
private String createBy;
|
||||
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
@Schema(description = "更新者")
|
||||
private String updateBy;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.api.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.pig4cloud.pigx.admin.api.constant.ClarityFetchStatus;
|
||||
import com.pig4cloud.pigx.common.core.util.TenantTable;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Clarity 监控数据缓存
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@Data
|
||||
@TenantTable
|
||||
@Schema(description = "Clarity 监控数据缓存")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SysClarityData extends Model<SysClarityData> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "主键")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "数据日期")
|
||||
private LocalDate dataDate;
|
||||
|
||||
@Schema(description = "总会话数(Traffic.totalSessionCount)")
|
||||
private Integer totalSessions;
|
||||
|
||||
@Schema(description = "独立访客数 UV(Traffic.distinctUserCount)")
|
||||
private Integer distinctUsers;
|
||||
|
||||
@Schema(description = "每会话页面数(Traffic.pagesPerSessionPercentage)")
|
||||
private BigDecimal pagesPerSession;
|
||||
|
||||
@Schema(description = "平均滚动深度 %(ScrollDepth.averageScrollDepth)")
|
||||
private BigDecimal scrollDepth;
|
||||
|
||||
@Schema(description = "死点击率 %(DeadClickCount.sessionsWithMetricPercentage)")
|
||||
private BigDecimal deadClickRate;
|
||||
|
||||
@Schema(description = "激怒点击率 %(RageClickCount.sessionsWithMetricPercentage)")
|
||||
private BigDecimal rageClickRate;
|
||||
|
||||
@Schema(description = "设备分布 JSON 文本,格式:[{name,value}]")
|
||||
private String deviceData;
|
||||
|
||||
@Schema(description = "热门页面 Top10 JSON 文本,格式:[{name,value}]")
|
||||
private String topUrls;
|
||||
|
||||
@Schema(description = "来源页面 Top5 JSON,格式:[{name,value}]")
|
||||
private String referrerUrlData;
|
||||
|
||||
@Schema(description = "页面标题 Top5 JSON,格式:[{name,value}]")
|
||||
private String pageTitleData;
|
||||
|
||||
@Schema(description = "浏览器分布 JSON,格式:[{name,value}]")
|
||||
private String browserData;
|
||||
|
||||
@Schema(description = "Clarity API numOfDays 参数:1=24h, 2=48h, 3=72h")
|
||||
private Integer numOfDays;
|
||||
|
||||
@Schema(description = "数据拉取状态:pending=占位中, success=成功, failed=失败")
|
||||
private ClarityFetchStatus fetchStatus;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "删除标记,1:已删除,0:正常")
|
||||
private String delFlag;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -132,7 +132,7 @@ public class SysFile extends Model<SysFile> {
|
||||
*/
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "删除标记,1:已删除,0:正常")
|
||||
private String delFlag;
|
||||
@Schema(description = "删除标记,1:已删除,0:正常")
|
||||
private String delFlag;
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -19,8 +19,8 @@
|
||||
|
||||
package com.pig4cloud.pigx.admin.api.entity;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnore;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.pig4cloud.pigx.common.core.util.TenantTable;
|
||||
import com.pig4cloud.pigx.common.sensitive.annotation.Sensitive;
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.api.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.pig4cloud.pigx.common.core.util.TenantTable;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 角色首页组件配置
|
||||
*
|
||||
* @author lengleng
|
||||
*/
|
||||
@Data
|
||||
@TenantTable
|
||||
@Schema(description = "角色首页组件配置")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SysRoleWidget extends Model<SysRoleWidget> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "主键")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "角色ID")
|
||||
private Long roleId;
|
||||
|
||||
@Schema(description = "允许的组件key列表,逗号分隔")
|
||||
private String widgetKeys;
|
||||
|
||||
@Schema(description = "默认布局JSON,格式同前端grid对象")
|
||||
private String layoutConfig;
|
||||
|
||||
/** 创建人 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
/** 修改人 */
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
/** 创建时间 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 修改时间 */
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
@Schema(description = "修改时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 删除标识(0-正常,1-删除) */
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "删除标记,1:已删除,0:正常")
|
||||
private String delFlag;
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -117,6 +117,6 @@ public class SysRouteConf extends Model<SysRouteConf> {
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "删除标记,1:已删除,0:正常")
|
||||
private String delFlag;
|
||||
private String delFlag;
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -120,7 +120,7 @@ public class SysScheduleEntity extends Model<SysScheduleEntity> {
|
||||
*/
|
||||
@TableLogic
|
||||
@Schema(description = "删除标记")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private String delFlag;
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private String delFlag;
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package com.pig4cloud.pigx.admin.api.entity;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.api.feign;
|
||||
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysApiKey;
|
||||
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
|
||||
/**
|
||||
* API密钥远程调用接口
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-23
|
||||
*/
|
||||
@FeignClient(contextId = "remoteApiKeyService", value = ServiceNameConstants.UPMS_SERVICE)
|
||||
public interface RemoteApiKeyService {
|
||||
|
||||
/**
|
||||
* 根据 SHA-256 哈希值查询 API Key(仅限内部调用)
|
||||
*
|
||||
* @param hash API Key 的 SHA-256 哈希值
|
||||
* @return 对应的 SysApiKey 记录,不存在时 data 为 null
|
||||
*/
|
||||
@NoToken
|
||||
@GetMapping("/api-key/inner/hash/{hash}")
|
||||
R<SysApiKey> getByHash(@PathVariable String hash);
|
||||
|
||||
/**
|
||||
* 更新 API Key 的最后使用时间(仅限内部调用)
|
||||
*
|
||||
* @param id API Key 主键
|
||||
* @return 更新是否成功
|
||||
*/
|
||||
@NoToken
|
||||
@PutMapping("/api-key/inner/last-used/{id}")
|
||||
R<Boolean> updateLastUsed(@PathVariable Long id);
|
||||
|
||||
}
|
||||
+15
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -38,22 +38,20 @@ import java.util.List;
|
||||
@FeignClient(contextId = "remoteDataScopeService", value = ServiceNameConstants.UPMS_SERVICE)
|
||||
public interface RemoteDataScopeService {
|
||||
|
||||
/**
|
||||
* 通过角色ID 查询角色列表
|
||||
*
|
||||
* @param roleIdList 角色ID
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/role/getRoleList")
|
||||
R<List<SysRole>> getRoleList(@RequestBody List<String> roleIdList);
|
||||
/**
|
||||
* 通过角色ID 查询角色列表
|
||||
* @param roleIdList 角色ID
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/role/getRoleList")
|
||||
R<List<SysRole>> getRoleList(@RequestBody List<String> roleIdList);
|
||||
|
||||
/**
|
||||
* 获取子级部门
|
||||
*
|
||||
* @param deptId 部门ID
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/dept/getDescendantList/{deptId}")
|
||||
R<List<SysDept>> getDescendantList(@PathVariable("deptId") Long deptId);
|
||||
/**
|
||||
* 获取子级部门
|
||||
* @param deptId 部门ID
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/dept/getDescendantList/{deptId}")
|
||||
R<List<SysDept>> getDescendantList(@PathVariable("deptId") Long deptId);
|
||||
|
||||
}
|
||||
+1
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,7 +22,6 @@ package com.pig4cloud.pigx.admin.api.feign;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysDept;
|
||||
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@@ -50,7 +49,6 @@ public interface RemoteDeptService {
|
||||
* @param deptId 部门ID
|
||||
* @return 负责人ID列表
|
||||
*/
|
||||
@NoToken
|
||||
@GetMapping("/dept/leader/{deptId}")
|
||||
R<List<Long>> getAllDeptLeader(@PathVariable("deptId") Long deptId);
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.pig4cloud.pigx.admin.api.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnore;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.pig4cloud.pigx.admin.api.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnore;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.pig4cloud.pigx.admin.api.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnore;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.pig4cloud.pigx.admin.api.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnore;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
package com.pig4cloud.pigx.admin.api.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.idev.excel.annotation.write.style.ColumnWidth;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnore;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.write.style.ColumnWidth;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
package com.pig4cloud.pigx.admin.api.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.idev.excel.annotation.write.style.ColumnWidth;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnore;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.write.style.ColumnWidth;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
package com.pig4cloud.pigx.admin.api.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.idev.excel.annotation.write.style.ColumnWidth;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnore;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.write.style.ColumnWidth;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.pig4cloud.pigx.admin.api.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnore;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.pig4cloud.pigx.admin.api.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnore;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
+1
@@ -13,3 +13,4 @@ com.pig4cloud.pigx.admin.api.feign.RemoteAuditLogService
|
||||
com.pig4cloud.pigx.admin.api.feign.RemoteMenuService
|
||||
com.pig4cloud.pigx.admin.api.feign.RemoteRouteConfService
|
||||
com.pig4cloud.pigx.admin.api.feign.RemoteMessageService
|
||||
com.pig4cloud.pigx.admin.api.feign.RemoteApiKeyService
|
||||
@@ -32,10 +32,15 @@ sys.menu.notFound=Menu does not exist
|
||||
sys.menu.home.set.succeed=Homepage set successfully
|
||||
sys.menu.home.cancel.succeed=Homepage removed successfully
|
||||
sys.menu.button.cannot.home=Button-type menu cannot be set as homepage
|
||||
sys.menu.sort.duplicate=Menu ID list cannot contain duplicates
|
||||
sys.menu.sort.scopeInvalid=Menu sort data does not belong to the same parent node
|
||||
|
||||
# Dictionary related
|
||||
sys.dict.delete.system=Built-in dictionary items cannot be deleted
|
||||
sys.dict.update.system=Built-in dictionary items cannot be modified
|
||||
sys.dict.notFound=Dictionary does not exist
|
||||
sys.dict.item.sort.duplicate=Dictionary item ID list cannot contain duplicates
|
||||
sys.dict.item.sort.scopeInvalid=Dictionary item sort data does not belong to the current dictionary
|
||||
|
||||
# Parameter related
|
||||
sys.param.config.error=System parameter configuration error: {0}
|
||||
|
||||
@@ -32,10 +32,15 @@ sys.menu.notFound=\u83DC\u5355\u4E0D\u5B58\u5728
|
||||
sys.menu.home.set.succeed=\u8BBE\u7F6E\u9996\u9875\u6210\u529F
|
||||
sys.menu.home.cancel.succeed=\u53D6\u6D88\u9996\u9875\u6210\u529F
|
||||
sys.menu.button.cannot.home=\u6309\u94AE\u7C7B\u578B\u4E0D\u80FD\u8BBE\u7F6E\u4E3A\u9996\u9875
|
||||
sys.menu.sort.duplicate=\u83DC\u5355ID\u5217\u8868\u4E0D\u80FD\u91CD\u590D
|
||||
sys.menu.sort.scopeInvalid=\u83DC\u5355\u6392\u5E8F\u6570\u636E\u4E0D\u5C5E\u4E8E\u540C\u4E00\u7236\u8282\u70B9
|
||||
|
||||
# 字典相关
|
||||
sys.dict.delete.system=\u7CFB\u7EDF\u5185\u7F6E\u5B57\u5178\u9879\u76EE\u4E0D\u80FD\u5220\u9664
|
||||
sys.dict.update.system=\u7CFB\u7EDF\u5185\u7F6E\u5B57\u5178\u9879\u76EE\u4E0D\u80FD\u4FEE\u6539
|
||||
sys.dict.notFound=\u5B57\u5178\u4E0D\u5B58\u5728
|
||||
sys.dict.item.sort.duplicate=\u5B57\u5178\u9879ID\u5217\u8868\u4E0D\u80FD\u91CD\u590D
|
||||
sys.dict.item.sort.scopeInvalid=\u5B57\u5178\u9879\u6392\u5E8F\u6570\u636E\u4E0D\u5C5E\u4E8E\u5F53\u524D\u5B57\u5178
|
||||
|
||||
# 参数相关
|
||||
sys.param.config.error={0} \u7CFB\u7EDF\u53C2\u6570\u914D\u7F6E\u9519\u8BEF
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -37,8 +37,8 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
@SpringBootApplication
|
||||
public class PigxAdminApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PigxAdminApplication.class, args);
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PigxAdminApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -9,7 +9,7 @@ import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.web.context.WebServerInitializedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -38,11 +38,11 @@ public class ClientDetailsInitRunner implements InitializingBean {
|
||||
private final SysTenantService tenantService;
|
||||
|
||||
/**
|
||||
* WebServerInitializedEvent 使用 TransactionalEventListener 时启动时无法获取到事件
|
||||
* ApplicationReadyEvent 使用 TransactionalEventListener 时启动时无法获取到事件
|
||||
*/
|
||||
@Async
|
||||
@Order
|
||||
@EventListener({ WebServerInitializedEvent.class })
|
||||
@EventListener({ApplicationReadyEvent.class})
|
||||
public void webServerInit() {
|
||||
this.initClientDetails();
|
||||
}
|
||||
@@ -63,7 +63,7 @@ public class ClientDetailsInitRunner implements InitializingBean {
|
||||
// 3. 拼接key 1:client_config_flag:clinetId
|
||||
String key = String.format("%s:%s:%s", tenantId, CacheConstants.CLIENT_FLAG, client.getClientId());
|
||||
// 4. hashkey clientId 保存客户端信息
|
||||
RedisUtils.set(key, client.getAdditionalInformation());
|
||||
RedisUtils.set(key, client.getAdditionalInformation());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+6
-5
@@ -26,7 +26,7 @@ import com.pig4cloud.pigx.common.gateway.vo.RouteDefinitionVo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.web.context.WebServerInitializedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
|
||||
import org.springframework.cloud.gateway.filter.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
|
||||
@@ -37,7 +37,6 @@ import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
|
||||
@@ -54,6 +53,7 @@ import java.util.Map;
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnDiscoveryEnabled
|
||||
@SuppressWarnings("removal")
|
||||
public class DynamicRouteInitRunner implements InitializingBean {
|
||||
|
||||
private final RedisTemplate<String, String> redisTemplate;
|
||||
@@ -63,11 +63,11 @@ public class DynamicRouteInitRunner implements InitializingBean {
|
||||
private final RedisMessageListenerContainer listenerContainer;
|
||||
|
||||
/**
|
||||
* WebServerInitializedEvent 使用 TransactionalEventListener 时启动时无法获取到事件
|
||||
* ApplicationReadyEvent 使用 TransactionalEventListener 时启动时无法获取到事件
|
||||
*/
|
||||
@Async
|
||||
@Order
|
||||
@EventListener({WebServerInitializedEvent.class})
|
||||
@EventListener({ApplicationReadyEvent.class})
|
||||
public void WebServerInit() {
|
||||
this.initRoute();
|
||||
}
|
||||
@@ -93,7 +93,8 @@ public class DynamicRouteInitRunner implements InitializingBean {
|
||||
vo.setMetadata(JSONUtil.toBean(route.getMetadata(), Map.class));
|
||||
log.info("加载路由ID:{},{}", route.getRouteId(), vo);
|
||||
|
||||
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinitionVo.class));
|
||||
redisTemplate.setHashValueSerializer(
|
||||
new org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer<>(RouteDefinitionVo.class));
|
||||
HashOperations<String, String, RouteDefinitionVo> stringStringValueOperations = redisTemplate.opsForHash();
|
||||
stringStringValueOperations.put(CacheConstants.ROUTE_KEY, route.getRouteId(), vo);
|
||||
});
|
||||
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
package com.pig4cloud.pigx.admin.controller;
|
||||
|
||||
import com.pig4cloud.pigx.admin.api.dto.AiChatMessageDTO;
|
||||
import io.github.pigmesh.ai.deepseek.core.DeepSeekClient;
|
||||
import io.github.pigmesh.ai.deepseek.core.chat.ChatCompletionResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* AI 聊天控制器
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2025/02/20
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/ai")
|
||||
@RequiredArgsConstructor
|
||||
public class AiChatController {
|
||||
|
||||
private final Optional<DeepSeekClient> deepSeekClientOptional;
|
||||
|
||||
/**
|
||||
* 聊天
|
||||
*
|
||||
* @param message 消息
|
||||
* @return {@link Flux }<{@link ChatCompletionResponse }>
|
||||
*/
|
||||
@PostMapping("/chat")
|
||||
public Flux<ChatCompletionResponse> chat(@RequestBody AiChatMessageDTO message) {
|
||||
|
||||
if (deepSeekClientOptional.isEmpty()) {
|
||||
log.warn("DeepSeek 大模型聊天未开启,请检查配置");
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
if (message.isWebSearch()) {
|
||||
return deepSeekClientOptional.get().chatSearchCompletion(message.getMessage());
|
||||
}
|
||||
return deepSeekClientOptional.get().chatFluxCompletion(message.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.pig4cloud.pigx.admin.api.dto.ApiKeyCreateDTO;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysApiKey;
|
||||
import com.pig4cloud.pigx.admin.service.SysApiKeyService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.YesNoEnum;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.log.annotation.SysLog;
|
||||
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
|
||||
import com.pig4cloud.pigx.common.security.annotation.Inner;
|
||||
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springdoc.core.annotations.ParameterObject;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* API密钥管理
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-23
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/api-key")
|
||||
@Tag(description = "api-key", name = "API密钥管理")
|
||||
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
||||
public class SysApiKeyController {
|
||||
|
||||
private final SysApiKeyService apiKeyService;
|
||||
|
||||
/**
|
||||
* 创建 API Key
|
||||
* <p>
|
||||
* 业务逻辑由 Service 完成:校验当前密码(防 CSRF)、数量上限(每用户最多 5 个)、
|
||||
* 生成 sk- 前缀密钥并只存储 SHA-256 哈希。明文 Key 仅在本次响应中返回,之后不可再取。
|
||||
*
|
||||
* @param dto 创建请求(name、allowedIps、expiresAt、password)
|
||||
* @return 明文 API Key,格式:sk-xxx
|
||||
*/
|
||||
@SysLog("创建API密钥")
|
||||
@PostMapping
|
||||
public R<String> create(@Valid @RequestBody ApiKeyCreateDTO dto) {
|
||||
return apiKeyService.createApiKey(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户的 API Key 列表
|
||||
* <p>
|
||||
* 仅返回当前登录用户自己的 Key,按创建时间倒序排列。
|
||||
* 列表中不包含明文 Key 和哈希值,仅展示元数据。
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public R list() {
|
||||
Long userId = SecurityUtils.getUser().getId();
|
||||
return R.ok(apiKeyService.list(Wrappers.<SysApiKey>lambdaQuery()
|
||||
.eq(SysApiKey::getUserId, userId)
|
||||
.orderByDesc(SysApiKey::getCreateTime)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员分页查询所有用户的 API Key
|
||||
* <p>
|
||||
* 需要 admin_apikey_view 权限。支持按 username 模糊搜索。
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param sysApiKey 查询条件(username 字段用于模糊匹配)
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
@HasPermission("sys_social_details_view")
|
||||
public R page(@ParameterObject Page page, @ParameterObject SysApiKey sysApiKey) {
|
||||
return apiKeyService.page(page, sysApiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 API Key 元数据
|
||||
* <p>
|
||||
* 仅允许修改 name、allowedIps、expiresAt、status,不可修改 apiKeyHash。
|
||||
* Service 层校验归属权,并在更新后精确清除对应缓存。
|
||||
*
|
||||
* @param apiKey 待更新字段(必须包含 id)
|
||||
*/
|
||||
@SysLog("更新API密钥")
|
||||
@PutMapping
|
||||
public R update(@RequestBody SysApiKey apiKey) {
|
||||
return apiKeyService.updateApiKey(apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前用户自己的 API Key
|
||||
* <p>
|
||||
* Service 层校验归属权后删除,并精确清除该 Key 的缓存。
|
||||
*
|
||||
* @param id API Key 主键
|
||||
*/
|
||||
@SysLog("删除API密钥")
|
||||
@DeleteMapping("/{id}")
|
||||
@HasPermission("sys_social_details_del")
|
||||
public R delete(@PathVariable Long id) {
|
||||
return apiKeyService.deleteApiKey(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员批量删除 API Key
|
||||
* <p>
|
||||
* 需要 admin_apikey_del 权限。Service 层逐条清除缓存后执行批量删除。
|
||||
*
|
||||
* @param ids 待删除的 API Key 主键数组
|
||||
*/
|
||||
@SysLog("批量删除API密钥")
|
||||
@DeleteMapping
|
||||
@HasPermission("sys_social_details_del")
|
||||
public R deleteBatch(@RequestBody Long[] ids) {
|
||||
return apiKeyService.deleteBatchApiKey(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部接口:根据 SHA-256 哈希值查询有效的 API Key
|
||||
* <p>
|
||||
* 仅供 pigx-common-security 的 token 校验流程调用,不对外暴露。
|
||||
* 结果会被 Spring Cache 缓存(key 为 hash),避免每次请求都查库。
|
||||
*
|
||||
* @param hash API Key 的 SHA-256 哈希值
|
||||
* @return 状态正常的 SysApiKey,不存在或已禁用时 data 为 null
|
||||
*/
|
||||
@Inner
|
||||
@GetMapping("/inner/hash/{hash}")
|
||||
@Cacheable(value = CacheConstants.API_KEY_DETAILS, key = "#hash", unless = "#result.data == null")
|
||||
public R<SysApiKey> getByHash(@PathVariable String hash) {
|
||||
return R.ok(apiKeyService.getOne(Wrappers.<SysApiKey>lambdaQuery()
|
||||
.eq(SysApiKey::getApiKeyHash, hash)
|
||||
.eq(SysApiKey::getStatus, YesNoEnum.NO.getCode())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部接口:更新 API Key 最后使用时间
|
||||
* <p>
|
||||
* 由 token 校验流程在认证成功后异步调用,记录 Key 的活跃时间。
|
||||
* 该操作不影响认证结果,失败时仅打印警告日志。
|
||||
*
|
||||
* @param id API Key 主键
|
||||
*/
|
||||
@Inner
|
||||
@PutMapping("/inner/last-used/{id}")
|
||||
public R<Boolean> updateLastUsed(@PathVariable Long id) {
|
||||
SysApiKey update = new SysApiKey();
|
||||
update.setId(id);
|
||||
update.setLastUsedAt(LocalDateTime.now());
|
||||
return R.ok(apiKeyService.updateById(update));
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -129,7 +129,7 @@ public class SysAreaController {
|
||||
@HasPermission("sys_sysArea_export")
|
||||
public List<SysAreaEntity> export(SysAreaEntity sysArea, Long[] ids) {
|
||||
return sysAreaService.list(Wrappers.lambdaQuery(sysArea)
|
||||
.in(ArrayUtil.isNotEmpty(ids), SysAreaEntity::getId, CollUtil.toList(ids)));
|
||||
}
|
||||
.in(ArrayUtil.isNotEmpty(ids), SysAreaEntity::getId, CollUtil.toList(ids)));
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -111,8 +111,8 @@ public class SysAuditLogController {
|
||||
@GetMapping("/export")
|
||||
@HasPermission("sys_audit_export")
|
||||
public List<SysAuditLog> export(SysAuditLog sysAuditLog, Long[] ids) {
|
||||
return sysAuditLogService.list(Wrappers.lambdaQuery(sysAuditLog)
|
||||
.in(ArrayUtil.isNotEmpty(ids), SysAuditLog::getId, CollUtil.toList(ids)));
|
||||
return sysAuditLogService.list(Wrappers.lambdaQuery(sysAuditLog)
|
||||
.in(ArrayUtil.isNotEmpty(ids), SysAuditLog::getId, CollUtil.toList(ids)));
|
||||
}
|
||||
|
||||
}
|
||||
+1
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -27,7 +27,6 @@ import com.pig4cloud.pigx.common.excel.annotation.RequestExcel;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.log.annotation.SysLog;
|
||||
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
|
||||
import com.pig4cloud.pigx.common.security.annotation.Inner;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
@@ -130,7 +129,6 @@ public class SysDeptController {
|
||||
return R.ok(sysDeptService.listDescendant(deptId));
|
||||
}
|
||||
|
||||
@Inner
|
||||
@GetMapping(value = "/leader/{deptId}")
|
||||
public R getAllDeptLeader(@PathVariable Long deptId) {
|
||||
return R.ok(sysDeptService.listDeptLeader(deptId));
|
||||
|
||||
+25
-9
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -23,6 +23,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.pig4cloud.pigx.admin.api.dto.SysDictItemSortDTO;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysDict;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysDictItem;
|
||||
import com.pig4cloud.pigx.admin.service.SysDictItemService;
|
||||
@@ -112,7 +113,8 @@ public class SysDictController {
|
||||
return R.ok(sysDictItemService.list(Wrappers.<SysDictItem>query()
|
||||
.lambda()
|
||||
.eq(SysDictItem::getDictType, type)
|
||||
.orderByDesc(SysDictItem::getSortOrder)));
|
||||
.orderByAsc(SysDictItem::getSortOrder)
|
||||
.orderByAsc(SysDictItem::getId)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +182,9 @@ public class SysDictController {
|
||||
*/
|
||||
@GetMapping("/item/page")
|
||||
public R getSysDictItemPage(Page page, SysDictItem sysDictItem) {
|
||||
return R.ok(sysDictItemService.page(page, Wrappers.query(sysDictItem)));
|
||||
return R.ok(sysDictItemService.page(page, Wrappers.<SysDictItem>lambdaQuery(sysDictItem)
|
||||
.orderByAsc(SysDictItem::getSortOrder)
|
||||
.orderByAsc(SysDictItem::getId)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,6 +231,18 @@ public class SysDictController {
|
||||
return sysDictItemService.updateDictItem(sysDictItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新字典项排序
|
||||
*
|
||||
* @param sortDTO 字典项排序信息
|
||||
* @return R
|
||||
*/
|
||||
@SysLog("更新字典项排序")
|
||||
@PutMapping("/item/sort")
|
||||
public R sortDictItem(@Valid @RequestBody SysDictItemSortDTO sortDTO) {
|
||||
return sysDictItemService.updateDictItemSort(sortDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id删除字典项
|
||||
* @param id id
|
||||
@@ -247,12 +263,12 @@ public class SysDictController {
|
||||
@PutMapping("/sync")
|
||||
public R sync() {
|
||||
return sysDictService.syncDictCache();
|
||||
}
|
||||
}
|
||||
|
||||
@ResponseExcel
|
||||
@GetMapping("/export")
|
||||
public List<SysDictItem> export(SysDictItem sysDictItem) {
|
||||
return sysDictItemService.list(Wrappers.query(sysDictItem));
|
||||
}
|
||||
@ResponseExcel
|
||||
@GetMapping("/export")
|
||||
public List<SysDictItem> export(SysDictItem sysDictItem) {
|
||||
return sysDictItemService.list(Wrappers.query(sysDictItem));
|
||||
}
|
||||
|
||||
}
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -125,7 +125,7 @@ public class SysFileController {
|
||||
* @param fileName 自定义文件名
|
||||
* @return 文件上传结果
|
||||
*/
|
||||
@Inner
|
||||
@Inner(value = false)
|
||||
@PostMapping(value = "/inner/upload")
|
||||
public R innerUpload(@RequestPart("file") MultipartFile file,
|
||||
@RequestParam(value = "dir", required = false) String dir,
|
||||
@@ -208,10 +208,10 @@ public class SysFileController {
|
||||
* 移动文件组
|
||||
* @param fileGroupDTO SysFileGroupDTO对象,要移动的文件组信息
|
||||
* @return 包含移动结果的R对象
|
||||
*/
|
||||
@PutMapping("/group/move")
|
||||
public R moveFileGroup(@RequestBody SysFileGroupDTO fileGroupDTO) {
|
||||
return R.ok(sysFileService.moveFileGroup(fileGroupDTO));
|
||||
}
|
||||
*/
|
||||
@PutMapping("/group/move")
|
||||
public R moveFileGroup(@RequestBody SysFileGroupDTO fileGroupDTO) {
|
||||
return R.ok(sysFileService.moveFileGroup(fileGroupDTO));
|
||||
}
|
||||
|
||||
}
|
||||
+98
-104
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -30,7 +30,6 @@ import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.log.annotation.SysLog;
|
||||
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
|
||||
import com.pig4cloud.pigx.common.security.annotation.Inner;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -55,115 +54,110 @@ import java.util.List;
|
||||
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
||||
public class SysI18nController {
|
||||
|
||||
private final SysI18nService sysI18nService;
|
||||
private final SysI18nService sysI18nService;
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
* @param page 分页对象
|
||||
* @param sysI18n 系统表-国际化
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "分页查询", description = "分页查询")
|
||||
@GetMapping("/page")
|
||||
@HasPermission("sys_i18n_view")
|
||||
public R getsysI18nPage(@ParameterObject Page page, @ParameterObject SysI18nEntity sysI18n) {
|
||||
LambdaQueryWrapper<SysI18nEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.like(StrUtil.isNotBlank(sysI18n.getName()), SysI18nEntity::getName, sysI18n.getName());
|
||||
wrapper.like(StrUtil.isNotBlank(sysI18n.getZhCn()), SysI18nEntity::getZhCn, sysI18n.getZhCn());
|
||||
wrapper.like(StrUtil.isNotBlank(sysI18n.getEn()), SysI18nEntity::getEn, sysI18n.getEn());
|
||||
return R.ok(sysI18nService.page(page, wrapper));
|
||||
}
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param sysI18n 系统表-国际化
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "分页查询", description = "分页查询")
|
||||
@GetMapping("/page")
|
||||
@HasPermission("sys_i18n_view")
|
||||
public R getsysI18nPage(@ParameterObject Page page, @ParameterObject SysI18nEntity sysI18n) {
|
||||
LambdaQueryWrapper<SysI18nEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.like(StrUtil.isNotBlank(sysI18n.getName()), SysI18nEntity::getName, sysI18n.getName());
|
||||
wrapper.like(StrUtil.isNotBlank(sysI18n.getZhCn()), SysI18nEntity::getZhCn, sysI18n.getZhCn());
|
||||
wrapper.like(StrUtil.isNotBlank(sysI18n.getEn()), SysI18nEntity::getEn, sysI18n.getEn());
|
||||
return R.ok(sysI18nService.page(page, wrapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id查询系统表-国际化
|
||||
* @param id id
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "通过id查询", description = "通过id查询")
|
||||
@GetMapping("/details/{id}")
|
||||
public R getById(@PathVariable("id") Long id) {
|
||||
return R.ok(sysI18nService.getById(id));
|
||||
}
|
||||
/**
|
||||
* 通过id查询系统表-国际化
|
||||
*
|
||||
* @param id id
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "通过id查询", description = "通过id查询")
|
||||
@GetMapping("/details/{id}")
|
||||
public R getById(@PathVariable("id") Long id) {
|
||||
return R.ok(sysI18nService.getById(id));
|
||||
}
|
||||
|
||||
@GetMapping("/details")
|
||||
public R getDetails(@ParameterObject SysI18nEntity entity) {
|
||||
return R.ok(sysI18nService.getOne(Wrappers.query(entity)));
|
||||
}
|
||||
@GetMapping("/details")
|
||||
public R getDetails(@ParameterObject SysI18nEntity entity) {
|
||||
return R.ok(sysI18nService.getOne(Wrappers.query(entity)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增系统表-国际化
|
||||
* @param sysI18n 系统表-国际化
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "新增系统表-国际化", description = "新增系统表-国际化")
|
||||
@SysLog("新增系统表-国际化")
|
||||
@PostMapping
|
||||
@CacheEvict(value = CacheConstants.I18N_DETAILS, allEntries = true)
|
||||
@HasPermission("sys_i18n_add")
|
||||
public R save(@RequestBody SysI18nEntity sysI18n) {
|
||||
return R.ok(sysI18nService.save(sysI18n));
|
||||
}
|
||||
/**
|
||||
* 新增系统表-国际化
|
||||
*
|
||||
* @param sysI18n 系统表-国际化
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "新增系统表-国际化", description = "新增系统表-国际化")
|
||||
@SysLog("新增系统表-国际化")
|
||||
@PostMapping
|
||||
@HasPermission("sys_i18n_add")
|
||||
@CacheEvict(value = {CacheConstants.SITE_CONFIG_DETAILS}, allEntries = true)
|
||||
public R save(@RequestBody SysI18nEntity sysI18n) {
|
||||
return R.ok(sysI18nService.save(sysI18n));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改系统表-国际化
|
||||
* @param sysI18n 系统表-国际化
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "修改系统表-国际化", description = "修改系统表-国际化")
|
||||
@SysLog("修改系统表-国际化")
|
||||
@PutMapping
|
||||
@CacheEvict(value = CacheConstants.I18N_DETAILS, allEntries = true)
|
||||
@HasPermission("sys_i18n_edit")
|
||||
public R updateById(@RequestBody SysI18nEntity sysI18n) {
|
||||
return R.ok(sysI18nService.updateById(sysI18n));
|
||||
}
|
||||
/**
|
||||
* 修改系统表-国际化
|
||||
*
|
||||
* @param sysI18n 系统表-国际化
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "修改系统表-国际化", description = "修改系统表-国际化")
|
||||
@SysLog("修改系统表-国际化")
|
||||
@PutMapping
|
||||
@CacheEvict(value = {CacheConstants.SITE_CONFIG_DETAILS}, allEntries = true)
|
||||
@HasPermission("sys_i18n_edit")
|
||||
public R updateById(@RequestBody SysI18nEntity sysI18n) {
|
||||
return R.ok(sysI18nService.updateById(sysI18n));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id删除系统表-国际化
|
||||
* @param ids id 列表
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "通过id删除系统表-国际化", description = "通过id删除系统表-国际化")
|
||||
@SysLog("通过id删除系统表-国际化")
|
||||
@DeleteMapping
|
||||
@CacheEvict(value = CacheConstants.I18N_DETAILS, allEntries = true)
|
||||
@HasPermission("sys_i18n_del")
|
||||
public R removeById(@RequestBody Long[] ids) {
|
||||
return R.ok(sysI18nService.removeBatchByIds(CollUtil.toList(ids)));
|
||||
}
|
||||
/**
|
||||
* 通过id删除系统表-国际化
|
||||
*
|
||||
* @param ids id 列表
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "通过id删除系统表-国际化", description = "通过id删除系统表-国际化")
|
||||
@SysLog("通过id删除系统表-国际化")
|
||||
@DeleteMapping
|
||||
@HasPermission("sys_i18n_del")
|
||||
@CacheEvict(value = {CacheConstants.SITE_CONFIG_DETAILS}, allEntries = true)
|
||||
public R removeById(@RequestBody Long[] ids) {
|
||||
return R.ok(sysI18nService.removeBatchByIds(CollUtil.toList(ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出excel 表格
|
||||
* @param sysI18n 查询条件
|
||||
* @return excel 文件流
|
||||
*/
|
||||
@ResponseExcel
|
||||
@GetMapping("/export")
|
||||
@HasPermission("sys_i18n_export")
|
||||
public List<SysI18nEntity> export(SysI18nEntity sysI18n, Long[] ids) {
|
||||
return sysI18nService.list(Wrappers.lambdaQuery(sysI18n)
|
||||
.in(ArrayUtil.isNotEmpty(ids), SysI18nEntity::getId, CollUtil.toList(ids)));
|
||||
}
|
||||
/**
|
||||
* 导出excel 表格
|
||||
*
|
||||
* @param sysI18n 查询条件
|
||||
* @return excel 文件流
|
||||
*/
|
||||
@ResponseExcel
|
||||
@GetMapping("/export")
|
||||
@HasPermission("sys_i18n_export")
|
||||
public List<SysI18nEntity> export(SysI18nEntity sysI18n, Long[] ids) {
|
||||
return sysI18nService.list(Wrappers.lambdaQuery(sysI18n).in(ArrayUtil.isNotEmpty(ids), SysI18nEntity::getId, CollUtil.toList(ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统I18N配置
|
||||
* @return I18N 配置
|
||||
*/
|
||||
@Operation(summary = "获取系统配置-国际化", description = "获取系统配置-国际化")
|
||||
@Inner(false)
|
||||
@GetMapping("/info")
|
||||
public R list() {
|
||||
return R.ok(sysI18nService.listMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步数据
|
||||
* @return R
|
||||
*/
|
||||
@SysLog("同步数据")
|
||||
@PutMapping("/sync")
|
||||
public R sync() {
|
||||
return sysI18nService.syncI18nCache();
|
||||
}
|
||||
/**
|
||||
* 同步数据
|
||||
*
|
||||
* @return R
|
||||
*/
|
||||
@SysLog("同步数据")
|
||||
@PutMapping("/sync")
|
||||
public R sync() {
|
||||
return sysI18nService.syncI18nCache();
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -120,8 +120,8 @@ public class SysLogController {
|
||||
@GetMapping("/export")
|
||||
@HasPermission("sys_log_export")
|
||||
public List<SysLog> export(SysLog sysLog, Long[] ids) {
|
||||
return sysLogService
|
||||
.list(Wrappers.lambdaQuery(sysLog).in(Objects.nonNull(ids), SysLog::getId, CollUtil.toList(ids)));
|
||||
return sysLogService
|
||||
.list(Wrappers.lambdaQuery(sysLog).in(Objects.nonNull(ids), SysLog::getId, CollUtil.toList(ids)));
|
||||
}
|
||||
|
||||
}
|
||||
+34
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -20,8 +20,10 @@
|
||||
package com.pig4cloud.pigx.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.pig4cloud.pigx.admin.api.dto.SysMenuSortDTO;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysMenu;
|
||||
import com.pig4cloud.pigx.admin.service.SysMenuService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.log.annotation.SysLog;
|
||||
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
|
||||
@@ -31,6 +33,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springdoc.core.annotations.ParameterObject;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -139,4 +142,34 @@ public class SysMenuController {
|
||||
return R.ok(sysMenuService.updateMenuById(sysMenu));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新菜单同级排序
|
||||
*
|
||||
* @param sortDTO 菜单排序信息
|
||||
* @return success/false
|
||||
*/
|
||||
@SysLog("更新菜单排序")
|
||||
@PutMapping("/sort")
|
||||
@HasPermission("sys_menu_edit")
|
||||
public R sort(@Valid @RequestBody SysMenuSortDTO sortDTO) {
|
||||
return sysMenuService.updateMenuSort(sortDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新指定角色的菜单权限缓存
|
||||
* <p>
|
||||
* menu_details 缓存 key = roleId,精确清除该角色的菜单缓存,
|
||||
* 与 SysRoleMenuServiceImpl.saveRoleMenus 的 @CacheEvict(key = "#roleId") 逻辑保持一致。
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @return success/fail
|
||||
*/
|
||||
@SysLog("刷新角色菜单缓存")
|
||||
@DeleteMapping("/cache/{roleId}")
|
||||
@HasPermission("sys_role_edit")
|
||||
@CacheEvict(value = CacheConstants.MENU_DETAILS, key = "#roleId")
|
||||
public R clearMenuCache(@PathVariable Long roleId) {
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
+8
-4
@@ -185,14 +185,18 @@ public class SysMessageController {
|
||||
/**
|
||||
* 发送短信验证码
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param registered 是否已注册,默认true
|
||||
* @param mobile 手机号
|
||||
* @param registered 是否已注册,默认true
|
||||
* @param captchaType 图形验证码类型
|
||||
* @param captchaVerification 图形验证码凭据
|
||||
* @return 发送结果
|
||||
*/
|
||||
@Inner(value = false)
|
||||
@GetMapping("/send/smsCode")
|
||||
public R sendSmsCode(@RequestParam String mobile, @RequestParam(required = false, defaultValue = "true") boolean registered) {
|
||||
return sysMessageService.sendSmsCode(mobile, registered);
|
||||
public R sendSmsCode(@RequestParam String mobile, @RequestParam(required = false, defaultValue = "true") boolean registered,
|
||||
@RequestParam(required = false) String captchaType,
|
||||
@RequestParam(required = false) String captchaVerification) {
|
||||
return sysMessageService.sendSmsCode(mobile, registered, captchaType, captchaVerification);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -144,7 +144,7 @@ public class SysPublicParamController {
|
||||
|
||||
/**
|
||||
* 通过id删除公共参数
|
||||
* @param publicId id
|
||||
* @param ids id
|
||||
* @return R
|
||||
*/
|
||||
@Operation(description = "删除公共参数", summary = "删除公共参数")
|
||||
|
||||
+6
-1
@@ -2,7 +2,9 @@ package com.pig4cloud.pigx.admin.controller;
|
||||
|
||||
import com.pig4cloud.pigx.admin.api.dto.RegisterUserDTO;
|
||||
import com.pig4cloud.pigx.admin.service.SysUserService;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.YesNoEnum;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.data.resolver.ParamResolver;
|
||||
import com.pig4cloud.pigx.common.log.annotation.SysLog;
|
||||
import com.pig4cloud.pigx.common.security.annotation.Inner;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -33,6 +35,9 @@ public class SysRegisterController {
|
||||
@PostMapping("/user")
|
||||
@ConditionalOnProperty(name = "register.user", matchIfMissing = true)
|
||||
public R<Boolean> registerUser(@RequestBody RegisterUserDTO userDto) {
|
||||
if (!YesNoEnum.YES.getCode().equals(ParamResolver.getStr("SITE_REGISTER_ENABLE", YesNoEnum.NO.getCode()))) {
|
||||
return R.failed("系统已关闭注册功能");
|
||||
}
|
||||
return userService.registerUser(userDto);
|
||||
}
|
||||
|
||||
@@ -59,6 +64,6 @@ public class SysRegisterController {
|
||||
@PostMapping("/forget/{code}")
|
||||
public R<Boolean> forgetUserPassword(@RequestBody RegisterUserDTO userDto, @PathVariable String code) {
|
||||
return userService.forgetUserPassword(userDto, code);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+37
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -24,9 +24,11 @@ import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysRole;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysRoleWidget;
|
||||
import com.pig4cloud.pigx.admin.api.vo.RoleExcelVO;
|
||||
import com.pig4cloud.pigx.admin.api.vo.RoleMenuVO;
|
||||
import com.pig4cloud.pigx.admin.service.SysRoleService;
|
||||
import com.pig4cloud.pigx.admin.service.SysRoleWidgetService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.RequestExcel;
|
||||
@@ -58,6 +60,8 @@ public class SysRoleController {
|
||||
|
||||
private final SysRoleService sysRoleService;
|
||||
|
||||
private final SysRoleWidgetService sysRoleWidgetService;
|
||||
|
||||
/**
|
||||
* 通过ID查询角色信息
|
||||
* @param id ID
|
||||
@@ -186,4 +190,36 @@ public class SysRoleController {
|
||||
return sysRoleService.importRole(excelVOList, bindingResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户角色的首页widget配置(取 widgetKeys 最多的那条)
|
||||
* @return widget配置,无配置时返回 null
|
||||
*/
|
||||
@GetMapping("/widget/current")
|
||||
public R getCurrentUserWidget() {
|
||||
return R.ok(sysRoleWidgetService.getByCurrentUser());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询角色的首页widget配置
|
||||
* @param roleId 角色ID
|
||||
* @return widget配置,无配置时返回 null
|
||||
*/
|
||||
@GetMapping("/widget/{roleId}")
|
||||
@HasPermission("sys_role_view")
|
||||
public R getRoleWidget(@PathVariable Long roleId) {
|
||||
return R.ok(sysRoleWidgetService.getByRoleId(roleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存角色的首页widget配置
|
||||
* @param sysRoleWidget widget配置对象
|
||||
* @return success/fail
|
||||
*/
|
||||
@SysLog("保存角色首页配置")
|
||||
@PutMapping("/widget")
|
||||
@HasPermission("sys_role_edit")
|
||||
public R saveRoleWidget(@RequestBody SysRoleWidget sysRoleWidget) {
|
||||
return R.ok(sysRoleWidgetService.saveOrUpdateByRoleId(sysRoleWidget));
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -82,6 +82,6 @@ public class SysRouteConfController {
|
||||
@HasPermission("sys_route_manage")
|
||||
public R saveRoute(@RequestBody SysRouteConf routeConf) {
|
||||
return R.ok(sysRouteConfService.saveRoute(routeConf));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+5
-5
@@ -175,10 +175,10 @@ public class SysSensitiveWordController {
|
||||
@HasPermission("admin_sysSensitiveWord_del")
|
||||
public R refresh() {
|
||||
RedisUtils.execute((RedisCallback<Void>) connection -> {
|
||||
connection.publish(CacheConstants.SENSITIVE_REDIS_RELOAD_TOPIC.getBytes(), "刷新敏感词缓存".getBytes());
|
||||
return null;
|
||||
});
|
||||
return R.ok();
|
||||
}
|
||||
connection.publish(CacheConstants.SENSITIVE_REDIS_RELOAD_TOPIC.getBytes(), "刷新敏感词缓存".getBytes());
|
||||
return null;
|
||||
});
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -72,7 +72,7 @@ public class SysSocialDetailsController {
|
||||
*/
|
||||
@Inner(value = false)
|
||||
@GetMapping("/getLoginAppList")
|
||||
public R getLoginAppList() {
|
||||
public R getLoginAppList() {
|
||||
return R.ok(sysSocialDetailsService.selectList());
|
||||
}
|
||||
|
||||
@@ -156,8 +156,8 @@ public class SysSocialDetailsController {
|
||||
*/
|
||||
@GetMapping("/export")
|
||||
public List<SysSocialDetails> export(SysSocialDetails sysSocialDetails, Long[] ids) {
|
||||
return sysSocialDetailsService.list(Wrappers.lambdaQuery(sysSocialDetails)
|
||||
.in(ArrayUtil.isNotEmpty(ids), SysSocialDetails::getId, CollUtil.toList(ids)));
|
||||
return sysSocialDetailsService.list(Wrappers.lambdaQuery(sysSocialDetails)
|
||||
.in(ArrayUtil.isNotEmpty(ids), SysSocialDetails::getId, CollUtil.toList(ids)));
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -117,7 +117,7 @@ public class SysSystemConfigController {
|
||||
@HasPermission("sys_message_export")
|
||||
public List<SysSystemConfigEntity> export(SysSystemConfigEntity sysSystemConfig, Long[] ids) {
|
||||
return sysSystemConfigService.list(Wrappers.lambdaQuery(sysSystemConfig)
|
||||
.in(ArrayUtil.isNotEmpty(ids), SysSystemConfigEntity::getId, CollUtil.toList(ids)));
|
||||
}
|
||||
.in(ArrayUtil.isNotEmpty(ids), SysSystemConfigEntity::getId, CollUtil.toList(ids)));
|
||||
}
|
||||
|
||||
}
|
||||
+63
-35
@@ -1,21 +1,18 @@
|
||||
package com.pig4cloud.pigx.admin.controller;
|
||||
|
||||
import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes;
|
||||
import com.pig4cloud.pigx.common.core.util.MsgUtils;
|
||||
import com.pig4cloud.pigx.admin.dto.SiteConfigDTO;
|
||||
import com.pig4cloud.pigx.admin.service.SysSiteConfigService;
|
||||
import com.pig4cloud.pigx.admin.service.SysSystemInfoService;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.data.cache.RedisUtils;
|
||||
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
|
||||
import com.pig4cloud.pigx.common.security.annotation.Inner;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.connection.RedisServerCommands;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/system")
|
||||
@@ -24,37 +21,68 @@ import java.util.*;
|
||||
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
||||
public class SysSystemInfoController {
|
||||
|
||||
private final SysSystemInfoService sysSystemInfoService;
|
||||
|
||||
private final SysSiteConfigService sysSiteConfigService;
|
||||
|
||||
/**
|
||||
* 缓存监控
|
||||
*
|
||||
* @return R<Object>
|
||||
*/
|
||||
@HasPermission("sys_cache_view")
|
||||
@GetMapping("/cache")
|
||||
public R cache() {
|
||||
Properties info = RedisUtils.execute((RedisCallback<Properties>) RedisServerCommands::info);
|
||||
Properties commandStats = RedisUtils
|
||||
.execute((RedisCallback<Properties>) connection -> connection.serverCommands().info("commandstats"));
|
||||
Object dbSize = RedisUtils.execute((RedisCallback<Object>) RedisServerCommands::dbSize);
|
||||
|
||||
if (commandStats == null) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_SYSTEM_CACHE_FETCH_ERROR));
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>(3);
|
||||
result.put("info", info);
|
||||
result.put("dbSize", dbSize);
|
||||
|
||||
List<Map<String, String>> pieList = new ArrayList<>();
|
||||
commandStats.stringPropertyNames().forEach(key -> {
|
||||
Map<String, String> data = new HashMap<>(2);
|
||||
String property = commandStats.getProperty(key);
|
||||
data.put("name", StringUtils.removeStart(key, "cmdstat_"));
|
||||
data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
|
||||
pieList.add(data);
|
||||
});
|
||||
|
||||
result.put("commandStats", pieList);
|
||||
return R.ok(result);
|
||||
public R<Map<String, Object>> cache() {
|
||||
return sysSystemInfoService.getCacheInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clarity 站点监控数据
|
||||
*
|
||||
* @return R<?>
|
||||
*/
|
||||
@HasPermission("sys_clarity_view")
|
||||
@GetMapping("/clarity")
|
||||
public R<?> clarity(@RequestParam(defaultValue = "1") Integer numOfDays) {
|
||||
return sysSystemInfoService.getClarityData(numOfDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合配置(i18n + site 配置)- 公开接口
|
||||
*/
|
||||
@Inner(false)
|
||||
@GetMapping("/config")
|
||||
public R<Map<String, Object>> config() {
|
||||
return R.ok(sysSiteConfigService.getAggregatedConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网站配置(管理端用)
|
||||
*/
|
||||
@HasPermission("sys_site_config_view")
|
||||
@GetMapping("/site-config")
|
||||
public R<SiteConfigDTO> getSiteConfig() {
|
||||
return R.ok(sysSiteConfigService.getSiteConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新网站配置
|
||||
*/
|
||||
@HasPermission("sys_site_config_edit")
|
||||
@PutMapping("/site-config")
|
||||
public R<Void> updateSiteConfig(@RequestBody SiteConfigDTO dto) {
|
||||
sysSiteConfigService.updateSiteConfig(dto);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新网站配置缓存(清空 Redis)
|
||||
*/
|
||||
@HasPermission("sys_site_config_edit")
|
||||
@DeleteMapping("/site-config/refresh")
|
||||
public R<Void> refreshSiteConfig() {
|
||||
sysSiteConfigService.refreshCache();
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 站点配置DTO
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-27
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "站点配置")
|
||||
public class SiteConfigDTO {
|
||||
|
||||
@Schema(description = "Clarity 统计ID")
|
||||
private String clarityId;
|
||||
|
||||
@Schema(description = "验证码类型:clickWord=点按,blockPuzzle=滑动,math=图形,none=关闭")
|
||||
private String captchaType;
|
||||
|
||||
@Schema(description = "密码规则预设:letterNumber=英文+数字,letterNumberSymbol=英文+数字+符号,upperLowerNumberSymbol=大小写+数字+符号")
|
||||
private String passwordRule;
|
||||
|
||||
@Schema(description = "是否强制重置密码")
|
||||
private Boolean forceResetPwd;
|
||||
|
||||
@Schema(description = "是否强制下线")
|
||||
private Boolean forceLogout;
|
||||
|
||||
@Schema(description = "是否开启短信登录")
|
||||
private Boolean smsLoginEnable;
|
||||
|
||||
@Schema(description = "是否开启社交登录")
|
||||
private Boolean socialLoginEnable;
|
||||
|
||||
@Schema(description = "是否开启注册")
|
||||
private Boolean registerEnable;
|
||||
|
||||
@Schema(description = "是否开启找回/重置密码")
|
||||
private Boolean resetPassword;
|
||||
|
||||
@Schema(description = "是否开启国际化")
|
||||
private Boolean i18nEnable;
|
||||
|
||||
@Schema(description = "是否开启暗黑模式")
|
||||
private Boolean darkModeEnable;
|
||||
|
||||
@Schema(description = "是否开启反调试保护")
|
||||
private Boolean antiDebugEnable;
|
||||
|
||||
@Schema(description = "反调试绕过密钥")
|
||||
private String antiDebugKey;
|
||||
|
||||
@Schema(description = "是否开启钉钉导入")
|
||||
private Boolean syncDingtalkEnabled;
|
||||
|
||||
@Schema(description = "是否开启企业微信导入")
|
||||
private Boolean syncWechatEnabled;
|
||||
|
||||
@Schema(description = "站点标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "页脚信息")
|
||||
private String footer;
|
||||
|
||||
@Schema(description = "隐私提示")
|
||||
private String privacyTip;
|
||||
|
||||
@Schema(description = "网站 Logo 图片路径")
|
||||
private String logo;
|
||||
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.handler;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.pig4cloud.pigx.admin.api.constant.ClarityFetchStatus;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysClarityData;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysSocialDetails;
|
||||
import com.pig4cloud.pigx.admin.mapper.SysClarityDataMapper;
|
||||
import com.pig4cloud.pigx.admin.mapper.SysSocialDetailsMapper;
|
||||
import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Clarity API 异步数据拉取任务
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ClarityFetchTaskHandler {
|
||||
|
||||
private static final String CLARITY_API_BASE = "https://www.clarity.ms/export-data/api/v1/project-live-insights?numOfDays=";
|
||||
|
||||
private static final String CLARITY_TYPE = "clarity";
|
||||
|
||||
private final SysClarityDataMapper clarityDataMapper;
|
||||
|
||||
private final SysSocialDetailsMapper socialDetailsMapper;
|
||||
|
||||
/**
|
||||
* 异步从 Clarity API 拉取数据并存库
|
||||
* 策略:先插入占位行(PENDING),调用成功后更新数据(SUCCESS),失败则标记(FAILED)。
|
||||
* 并发时第二个请求查到 PENDING/SUCCESS 即跳过,彻底避免重复调用。
|
||||
* 使用 TenantBroker.runAs 恢复调用线程的租户上下文(@Async 新线程不继承 ThreadLocal)
|
||||
*
|
||||
* @param tenantId 调用方当前租户 ID
|
||||
* @param numOfDays Clarity API numOfDays 参数(1=24h, 2=48h, 3=72h)
|
||||
*/
|
||||
@Async
|
||||
public void fetchAndSave(Long tenantId, int numOfDays) {
|
||||
try {
|
||||
TenantBroker.runAs(tenantId, t -> {
|
||||
// 幂等检查:今日已有 PENDING 或 SUCCESS 记录则跳过(FAILED 允许重试)
|
||||
boolean alreadyRunning = clarityDataMapper.exists(Wrappers.<SysClarityData>lambdaQuery()
|
||||
.eq(SysClarityData::getDataDate, LocalDate.now())
|
||||
.eq(SysClarityData::getNumOfDays, numOfDays)
|
||||
.in(SysClarityData::getFetchStatus, ClarityFetchStatus.PENDING, ClarityFetchStatus.SUCCESS));
|
||||
if (alreadyRunning) {
|
||||
log.debug("[Clarity] 今日 numOfDays={} 已有进行中或成功记录,跳过", numOfDays);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取 Clarity 配置(type='clarity')
|
||||
SysSocialDetails config = socialDetailsMapper.selectOne(
|
||||
Wrappers.<SysSocialDetails>lambdaQuery().eq(SysSocialDetails::getType, CLARITY_TYPE));
|
||||
if (config == null) {
|
||||
log.warn("[Clarity] 未找到 type='clarity' 的配置,跳过拉取");
|
||||
return;
|
||||
}
|
||||
|
||||
// 插入占位行,防止并发重复调用
|
||||
SysClarityData placeholder = new SysClarityData();
|
||||
placeholder.setDataDate(LocalDate.now());
|
||||
placeholder.setNumOfDays(numOfDays);
|
||||
placeholder.setFetchStatus(ClarityFetchStatus.PENDING);
|
||||
clarityDataMapper.insert(placeholder);
|
||||
log.info("[Clarity] 占位行已插入,id={},numOfDays={}", placeholder.getId(), numOfDays);
|
||||
|
||||
// 调用 Clarity API
|
||||
HttpResponse response = HttpRequest.get(CLARITY_API_BASE + numOfDays)
|
||||
.header("Authorization", "Bearer " + config.getAppSecret())
|
||||
.header("Content-Type", "application/json")
|
||||
.execute();
|
||||
|
||||
if (!response.isOk()) {
|
||||
log.error("[Clarity] API 调用失败,状态码:{}", response.getStatus());
|
||||
// 标记失败,下次调度可重试
|
||||
updateStatus(placeholder.getId(), ClarityFetchStatus.FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析响应并填充数据
|
||||
JSONArray body = JSONUtil.parseArray(response.body());
|
||||
SysClarityData data = parseResponse(body);
|
||||
data.setId(placeholder.getId());
|
||||
data.setFetchStatus(ClarityFetchStatus.SUCCESS);
|
||||
clarityDataMapper.updateById(data);
|
||||
log.info("[Clarity] 数据拉取并存库成功,日期:{},numOfDays:{}", LocalDate.now(), numOfDays);
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("[Clarity] 数据拉取异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStatus(Long id, ClarityFetchStatus status) {
|
||||
SysClarityData update = new SysClarityData();
|
||||
update.setId(id);
|
||||
update.setFetchStatus(status);
|
||||
clarityDataMapper.updateById(update);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Clarity API 响应体(JSONArray 结构)到实体
|
||||
* 字段映射已通过真实 API 调用确认(2026-03-26)
|
||||
* 所有 count 类字段为 String 类型,使用 Convert.toInt 安全转换(null/空串返回 0)
|
||||
*/
|
||||
private SysClarityData parseResponse(JSONArray responseArray) {
|
||||
SysClarityData data = new SysClarityData();
|
||||
|
||||
for (Object item : responseArray) {
|
||||
if (!(item instanceof JSONObject metric)) {
|
||||
continue;
|
||||
}
|
||||
String metricName = metric.getStr("metricName");
|
||||
JSONArray info = metric.getJSONArray("information");
|
||||
if (info == null || info.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
JSONObject first = info.getJSONObject(0);
|
||||
|
||||
switch (metricName) {
|
||||
case "Traffic" -> {
|
||||
data.setTotalSessions(Convert.toInt(first.getStr("totalSessionCount"), 0));
|
||||
data.setDistinctUsers(Convert.toInt(first.getStr("distinctUserCount"), 0));
|
||||
data.setPagesPerSession(first.getBigDecimal("pagesPerSessionPercentage"));
|
||||
}
|
||||
case "ScrollDepth" -> data.setScrollDepth(first.getBigDecimal("averageScrollDepth"));
|
||||
case "DeadClickCount" -> data.setDeadClickRate(first.getBigDecimal("sessionsWithMetricPercentage"));
|
||||
case "RageClickCount" -> data.setRageClickRate(first.getBigDecimal("sessionsWithMetricPercentage"));
|
||||
case "Device" -> data.setDeviceData(toJsonRanking(info.stream(), dev ->
|
||||
dev.getStr("name"), dev -> Convert.toInt(dev.getStr("sessionsCount"), 0)));
|
||||
case "PopularPages" -> data.setTopUrls(toJsonRanking(info.stream().limit(5), page ->
|
||||
page.getStr("url"), page -> Convert.toInt(page.getStr("visitsCount"), 0)));
|
||||
case "ReferrerUrl" -> data.setReferrerUrlData(toJsonRanking(info.stream().limit(5), ref ->
|
||||
StrUtil.blankToDefault(ref.getStr("url"), ref.getStr("name", "unknown")),
|
||||
ref -> Convert.toInt(ref.getStr("sessionsCount"), 0)));
|
||||
case "PageTitle" -> data.setPageTitleData(toJsonRanking(info.stream().limit(5), pt ->
|
||||
StrUtil.blankToDefault(pt.getStr("name"), pt.getStr("title", "unknown")),
|
||||
pt -> Convert.toInt(pt.getStr("sessionsCount"), 0)));
|
||||
case "Browser" -> data.setBrowserData(toJsonRanking(info.stream(), br ->
|
||||
StrUtil.blankToDefault(br.getStr("name"), "unknown"),
|
||||
br -> Convert.toInt(br.getStr("sessionsCount"), 0)));
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 JSONArray 流转换为 [{name, value}] 格式的 JSON 字符串
|
||||
*/
|
||||
private String toJsonRanking(Stream<?> stream,
|
||||
Function<JSONObject, String> nameExtractor,
|
||||
Function<JSONObject, Integer> valueExtractor) {
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
stream.forEach(item -> {
|
||||
if (item instanceof JSONObject obj) {
|
||||
list.add(Map.of("name", nameExtractor.apply(obj), "value", valueExtractor.apply(obj)));
|
||||
}
|
||||
});
|
||||
return JSONUtil.toJsonStr(list);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -121,7 +121,7 @@ public class DingTalkLoginHandler extends AbstractLoginHandler {
|
||||
public Boolean bind(SysUser user, String identify) {
|
||||
user.setWxDingUserid(identify);
|
||||
sysUserService.updateById(user);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+39
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -34,6 +34,7 @@ import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.exception.CheckedException;
|
||||
import com.pig4cloud.pigx.common.core.util.MsgUtils;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -100,8 +101,8 @@ public class MiniAppLoginHandler extends AbstractLoginHandler {
|
||||
R<UserInfo> userInfoR = sysUserService.getUserInfo(userDTO);
|
||||
|
||||
if (userInfoR.getData() == null) {
|
||||
log.info("小程序不存在用户:{}", openId);
|
||||
return null;
|
||||
log.info("小程序不存在用户:{}, 开始自动创建", openId);
|
||||
return createMiniAppUser(openId);
|
||||
}
|
||||
|
||||
return userInfoR.getData();
|
||||
@@ -121,4 +122,39 @@ public class MiniAppLoginHandler extends AbstractLoginHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动创建小程序用户
|
||||
*
|
||||
* @param openId 小程序openId
|
||||
* @return 创建成功返回用户信息,失败返回null
|
||||
*/
|
||||
private UserInfo createMiniAppUser(String openId) {
|
||||
try {
|
||||
log.info("开始为openId[{}]创建小程序用户", openId);
|
||||
|
||||
// 构造用户DTO
|
||||
UserDTO newUser = new UserDTO();
|
||||
newUser.setMiniOpenid(openId);
|
||||
newUser.setUsername(openId);
|
||||
newUser.setPassword(openId);
|
||||
newUser.setTenantId(TenantContextHolder.getTenantId());
|
||||
// 保存用户
|
||||
sysUserService.saveUser(newUser);
|
||||
log.info("小程序用户创建成功,openId: {}", openId);
|
||||
|
||||
// 重新获取用户信息
|
||||
UserDTO queryDTO = new UserDTO();
|
||||
queryDTO.setMiniOpenid(openId);
|
||||
R<UserInfo> userInfoR = sysUserService.getUserInfo(queryDTO);
|
||||
|
||||
if (userInfoR.getData() != null) {
|
||||
return userInfoR.getData();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("创建小程序用户异常,openId: {}, 错误信息: {}", openId, e.getMessage(), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -127,7 +127,7 @@ public class OscChinaLoginHandler extends AbstractLoginHandler {
|
||||
public Boolean bind(SysUser user, String identify) {
|
||||
user.setOscId(identify);
|
||||
sysUserService.updateById(user);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.mapper;
|
||||
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysApiKey;
|
||||
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* API密钥 Mapper
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-23
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysApiKeyMapper extends PigxBaseMapper<SysApiKey> {
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.mapper;
|
||||
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysClarityData;
|
||||
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* Clarity 监控数据 Mapper
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysClarityDataMapper extends PigxBaseMapper<SysClarityData> {
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysRoleWidget;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 角色首页组件配置 Mapper
|
||||
*
|
||||
* @author lengleng
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysRoleWidgetMapper extends BaseMapper<SysRoleWidget> {
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.pig4cloud.pigx.admin.api.dto.ApiKeyCreateDTO;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysApiKey;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
|
||||
/**
|
||||
* API密钥管理 Service
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-23
|
||||
*/
|
||||
public interface SysApiKeyService extends IService<SysApiKey> {
|
||||
|
||||
/**
|
||||
* 校验密码、数量上限,生成并持久化 API Key
|
||||
* @return 明文 Key(仅此一次)
|
||||
*/
|
||||
R<String> createApiKey(ApiKeyCreateDTO dto);
|
||||
|
||||
/**
|
||||
* 校验归属权,白名单更新字段,清除缓存
|
||||
*/
|
||||
R<Void> updateApiKey(SysApiKey apiKey);
|
||||
|
||||
/**
|
||||
* 校验归属权,删除记录,清除缓存
|
||||
*/
|
||||
R<Void> deleteApiKey(Long id);
|
||||
|
||||
/**
|
||||
* 批量清除缓存后批量删除
|
||||
*/
|
||||
R<Boolean> deleteBatchApiKey(Long[] ids);
|
||||
|
||||
/**
|
||||
* 管理员分页查询,按 username 模糊过滤
|
||||
*/
|
||||
R page(Page page, SysApiKey query);
|
||||
|
||||
/**
|
||||
* 精确清除指定 hash 的缓存
|
||||
*/
|
||||
void evictApiKeyCache(String hash);
|
||||
|
||||
}
|
||||
+57
-39
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -40,50 +40,68 @@ import java.util.Map;
|
||||
*/
|
||||
public interface SysDeptService extends IService<SysDept> {
|
||||
|
||||
/**
|
||||
* 查询部门树菜单
|
||||
* @param deptName 部门名称
|
||||
* @return 树
|
||||
*/
|
||||
List<Tree<Long>> selectTree(String deptName, Long parentId);
|
||||
/**
|
||||
* 查询部门树菜单
|
||||
*
|
||||
* @param deptName 部门名称
|
||||
* @return 树
|
||||
*/
|
||||
List<Tree<Long>> selectTree(String deptName, Long parentId);
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
* @param id 部门 ID
|
||||
* @return 成功、失败
|
||||
*/
|
||||
Boolean removeDeptById(Long id);
|
||||
/**
|
||||
* 删除部门
|
||||
*
|
||||
* @param id 部门 ID
|
||||
* @return 成功、失败
|
||||
*/
|
||||
Boolean removeDeptById(Long id);
|
||||
|
||||
List<DeptExcelVO> listExcelVo();
|
||||
/**
|
||||
* 获取部门Excel视图对象列表
|
||||
*
|
||||
* @return 部门Excel视图对象列表
|
||||
*/
|
||||
List<DeptExcelVO> listExcelVo();
|
||||
|
||||
R importDept(List<DeptExcelVO> excelVOList, BindingResult bindingResult);
|
||||
/**
|
||||
* 导入部门数据
|
||||
*
|
||||
* @param excelVOList 部门Excel数据列表
|
||||
* @param bindingResult 数据绑定结果
|
||||
* @return 导入结果
|
||||
*/
|
||||
R importDept(List<DeptExcelVO> excelVOList, BindingResult bindingResult);
|
||||
|
||||
/**
|
||||
* 获取部门的所有后代部门列表
|
||||
* @param deptId 部门ID
|
||||
* @return 后代部门列表
|
||||
*/
|
||||
List<SysDept> listDescendant(Long deptId);
|
||||
/**
|
||||
* 获取部门的所有后代部门列表
|
||||
*
|
||||
* @param deptId 部门ID
|
||||
* @return 后代部门列表
|
||||
*/
|
||||
List<SysDept> listDescendant(Long deptId);
|
||||
|
||||
/**
|
||||
* 获取部门负责人
|
||||
* @param deptId deptId
|
||||
* @return user id list
|
||||
*/
|
||||
List<Long> listDeptLeader(Long deptId);
|
||||
/**
|
||||
* 获取部门负责人
|
||||
*
|
||||
* @param deptId deptId
|
||||
* @return user id list
|
||||
*/
|
||||
List<Long> listDeptLeader(Long deptId);
|
||||
|
||||
/**
|
||||
* 查询全部部门包含用户
|
||||
* @param parentDeptId 父部门ID
|
||||
* @param type 查询类型
|
||||
*/
|
||||
Map<String, Object> listOrgTree(Long parentDeptId, String type);
|
||||
/**
|
||||
* 查询全部部门包含用户
|
||||
*
|
||||
* @param parentDeptId 父部门ID
|
||||
* @param type 查询类型
|
||||
*/
|
||||
Map<String, Object> listOrgTree(Long parentDeptId, String type);
|
||||
|
||||
/**
|
||||
* 根据用户名模糊搜索用户
|
||||
* @param username 用户名
|
||||
* @return List user
|
||||
*/
|
||||
List<OrgTreeVO> getOrgTreeUser(String username);
|
||||
/**
|
||||
* 根据用户名模糊搜索用户
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return List user
|
||||
*/
|
||||
List<OrgTreeVO> getOrgTreeUser(String username);
|
||||
|
||||
}
|
||||
+10
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.pig4cloud.pigx.admin.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.pig4cloud.pigx.admin.api.dto.SysDictItemSortDTO;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysDictItem;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
|
||||
@@ -42,4 +43,12 @@ public interface SysDictItemService extends IService<SysDictItem> {
|
||||
*/
|
||||
R updateDictItem(SysDictItem item);
|
||||
|
||||
/**
|
||||
* 更新字典项排序
|
||||
*
|
||||
* @param sortDTO 字典项排序信息(包含字典 ID 和按目标顺序排列的字典项 ID 列表)
|
||||
* @return 成功 / 失败
|
||||
*/
|
||||
R updateDictItemSort(SysDictItemSortDTO sortDTO);
|
||||
|
||||
}
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -38,11 +38,11 @@ public interface SysFileService extends IService<SysFile> {
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param file 文件流
|
||||
* @param file 文件流
|
||||
* @param fileName 文件名
|
||||
* @param dir 存储目录
|
||||
* @param groupId 文件分组ID
|
||||
* @param type 文件类型
|
||||
* @param dir 存储目录
|
||||
* @param groupId 文件分组ID
|
||||
* @param type 文件类型
|
||||
* @return 上传结果
|
||||
*/
|
||||
R uploadFile(MultipartFile file, String fileName, String dir, Long groupId, String type);
|
||||
|
||||
+10
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -21,6 +21,7 @@ package com.pig4cloud.pigx.admin.service;
|
||||
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.pig4cloud.pigx.admin.api.dto.SysMenuSortDTO;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysMenu;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
|
||||
@@ -58,6 +59,14 @@ public interface SysMenuService extends IService<SysMenu> {
|
||||
*/
|
||||
Boolean updateMenuById(SysMenu sysMenu);
|
||||
|
||||
/**
|
||||
* 更新同级菜单排序
|
||||
*
|
||||
* @param sortDTO 菜单排序信息
|
||||
* @return 成功、失败
|
||||
*/
|
||||
R updateMenuSort(SysMenuSortDTO sortDTO);
|
||||
|
||||
/**
|
||||
* 构建树
|
||||
* @param parentId 父节点ID
|
||||
|
||||
+8
-6
@@ -65,11 +65,13 @@ public interface SysMessageService extends IService<SysMessageEntity> {
|
||||
/**
|
||||
* 发送手机验证码
|
||||
*
|
||||
* @param mobile 手机号码
|
||||
* @param registered 是否已注册
|
||||
* @param mobile 手机号码
|
||||
* @param registered 是否已注册
|
||||
* @param captchaType 图形验证码类型
|
||||
* @param captchaVerification 图形验证码凭据
|
||||
* @return 发送结果
|
||||
*/
|
||||
R<Boolean> sendSmsCode(String mobile, boolean registered);
|
||||
R<Boolean> sendSmsCode(String mobile, boolean registered, String captchaType, String captchaVerification);
|
||||
|
||||
/**
|
||||
* 发送短信
|
||||
@@ -99,9 +101,9 @@ public interface SysMessageService extends IService<SysMessageEntity> {
|
||||
* 列出 Hook 业务代码
|
||||
*
|
||||
* @param messageHookDTO 消息钩子 DTO
|
||||
* @return {@link R }
|
||||
*/
|
||||
R listHookBizCode(MessageHookDTO messageHookDTO);
|
||||
* @return {@link R }
|
||||
*/
|
||||
R listHookBizCode(MessageHookDTO messageHookDTO);
|
||||
|
||||
/**
|
||||
* 发送站内消息/公告
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysRoleWidget;
|
||||
|
||||
/**
|
||||
* 角色首页组件配置 Service
|
||||
*
|
||||
* @author lengleng
|
||||
*/
|
||||
public interface SysRoleWidgetService extends IService<SysRoleWidget> {
|
||||
|
||||
/**
|
||||
* 根据角色ID查询widget配置
|
||||
* @param roleId 角色ID
|
||||
* @return widget配置,无配置时返回 null
|
||||
*/
|
||||
SysRoleWidget getByRoleId(Long roleId);
|
||||
|
||||
/**
|
||||
* 根据当前登录用户的角色列表,返回 widgetKeys 最多的那条配置
|
||||
* @return widget配置,无配置时返回 null
|
||||
*/
|
||||
SysRoleWidget getByCurrentUser();
|
||||
|
||||
/**
|
||||
* 保存或更新角色widget配置
|
||||
* @param sysRoleWidget widget配置
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean saveOrUpdateByRoleId(SysRoleWidget sysRoleWidget);
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.service;
|
||||
|
||||
import com.pig4cloud.pigx.admin.dto.SiteConfigDTO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 站点配置服务
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-27
|
||||
*/
|
||||
public interface SysSiteConfigService {
|
||||
|
||||
/**
|
||||
* 获取聚合配置(i18n + 站点配置)
|
||||
* @return Map
|
||||
*/
|
||||
Map<String, Object> getAggregatedConfig();
|
||||
|
||||
/**
|
||||
* 获取站点配置
|
||||
* @return SiteConfigDTO
|
||||
*/
|
||||
SiteConfigDTO getSiteConfig();
|
||||
|
||||
/**
|
||||
* 更新站点配置
|
||||
* @param dto 站点配置DTO
|
||||
*/
|
||||
void updateSiteConfig(SiteConfigDTO dto);
|
||||
|
||||
/**
|
||||
* 刷新站点配置缓存(清空 Redis 中的 site_config_details)
|
||||
*/
|
||||
void refreshCache();
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -47,6 +47,6 @@ public interface SysSocialDetailsService extends IService<SysSocialDetails> {
|
||||
*/
|
||||
UserInfo getUserInfo(String inStr);
|
||||
|
||||
List<SysSocialDetailsVO> selectList();
|
||||
List<SysSocialDetailsVO> selectList();
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysClarityData;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 系统监控服务
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
public interface SysSystemInfoService extends IService<SysClarityData> {
|
||||
|
||||
/**
|
||||
* 获取 Redis 缓存监控信息
|
||||
* @return R
|
||||
*/
|
||||
R<Map<String, Object>> getCacheInfo();
|
||||
|
||||
/**
|
||||
* 获取今日 Clarity 数据
|
||||
* 查库优先:有数据直接返回;无数据触发异步拉取并返回 loading 状态
|
||||
* @param numOfDays Clarity API numOfDays 参数(1=24h, 2=48h, 3=72h)
|
||||
* @return R
|
||||
*/
|
||||
R<?> getClarityData(int numOfDays);
|
||||
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.service.impl;
|
||||
|
||||
import cn.hutool.core.codec.Base62;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.pig4cloud.pigx.admin.api.dto.ApiKeyCreateDTO;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysApiKey;
|
||||
import com.pig4cloud.pigx.admin.mapper.SysApiKeyMapper;
|
||||
import com.pig4cloud.pigx.admin.service.SysApiKeyService;
|
||||
import com.pig4cloud.pigx.admin.service.SysUserService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HexFormat;
|
||||
|
||||
/**
|
||||
* API密钥管理 Service 实现
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-23
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SysApiKeyServiceImpl extends ServiceImpl<SysApiKeyMapper, SysApiKey> implements SysApiKeyService {
|
||||
|
||||
private static final String API_KEY_PREFIX = "sk-";
|
||||
|
||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||
|
||||
private static final int MAX_KEYS_PER_USER = 5;
|
||||
|
||||
private final SysUserService userService;
|
||||
|
||||
/**
|
||||
* 校验密码、数量上限,生成并持久化 API Key
|
||||
*/
|
||||
@Override
|
||||
public R<String> createApiKey(ApiKeyCreateDTO dto) {
|
||||
String username = SecurityUtils.getUser().getUsername();
|
||||
R checkResult = userService.checkPassword(username, dto.getPassword());
|
||||
if (!(boolean) checkResult.getData()) {
|
||||
return R.failed("密码验证失败");
|
||||
}
|
||||
|
||||
Long userId = SecurityUtils.getUser().getId();
|
||||
long count = this.count(Wrappers.<SysApiKey>lambdaQuery().eq(SysApiKey::getUserId, userId));
|
||||
if (count >= MAX_KEYS_PER_USER) {
|
||||
return R.failed("每个用户最多创建" + MAX_KEYS_PER_USER + "个API密钥");
|
||||
}
|
||||
|
||||
byte[] randomBytes = new byte[32];
|
||||
SECURE_RANDOM.nextBytes(randomBytes);
|
||||
String rawKey = API_KEY_PREFIX + Base62.encode(randomBytes);
|
||||
|
||||
SysApiKey apiKey = new SysApiKey();
|
||||
apiKey.setName(dto.getName());
|
||||
apiKey.setAllowedIps(dto.getAllowedIps());
|
||||
apiKey.setExpiresAt(dto.getExpiresAt());
|
||||
apiKey.setApiKeyHash(sha256(rawKey));
|
||||
apiKey.setUserId(userId);
|
||||
apiKey.setUsername(username);
|
||||
apiKey.setStatus("0");
|
||||
this.save(apiKey);
|
||||
|
||||
return R.ok(rawKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验归属权,白名单更新字段,清除缓存
|
||||
*/
|
||||
@Override
|
||||
public R<Void> updateApiKey(SysApiKey apiKey) {
|
||||
SysApiKey existing = this.getById(apiKey.getId());
|
||||
if (existing == null || !existing.getUserId().equals(SecurityUtils.getUser().getId())) {
|
||||
return R.failed("无权操作");
|
||||
}
|
||||
SysApiKey update = new SysApiKey();
|
||||
update.setId(apiKey.getId());
|
||||
update.setName(apiKey.getName());
|
||||
update.setAllowedIps(apiKey.getAllowedIps());
|
||||
update.setExpiresAt(apiKey.getExpiresAt());
|
||||
update.setStatus(apiKey.getStatus());
|
||||
this.updateById(update);
|
||||
evictApiKeyCache(existing.getApiKeyHash());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验归属权,删除记录,清除缓存
|
||||
*/
|
||||
@Override
|
||||
public R<Void> deleteApiKey(Long id) {
|
||||
SysApiKey existing = this.getById(id);
|
||||
if (existing == null || !existing.getUserId().equals(SecurityUtils.getUser().getId())) {
|
||||
return R.failed("无权操作");
|
||||
}
|
||||
this.removeById(id);
|
||||
evictApiKeyCache(existing.getApiKeyHash());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量清除缓存后批量删除
|
||||
*/
|
||||
@Override
|
||||
public R<Boolean> deleteBatchApiKey(Long[] ids) {
|
||||
CollUtil.toList(ids).forEach(id -> {
|
||||
SysApiKey existing = this.getById(id);
|
||||
if (existing != null) {
|
||||
evictApiKeyCache(existing.getApiKeyHash());
|
||||
}
|
||||
});
|
||||
return R.ok(this.removeBatchByIds(CollUtil.toList(ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员分页查询,按 username 模糊过滤
|
||||
*/
|
||||
@Override
|
||||
public R page(Page page, SysApiKey query) {
|
||||
return R.ok(this.page(page, Wrappers.<SysApiKey>lambdaQuery()
|
||||
.like(StrUtil.isNotBlank(query.getUsername()), SysApiKey::getUsername, query.getUsername())
|
||||
.orderByDesc(SysApiKey::getCreateTime)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = CacheConstants.API_KEY_DETAILS, key = "#hash")
|
||||
public void evictApiKeyCache(String hash) {
|
||||
// 缓存清除由 @CacheEvict 注解驱动
|
||||
}
|
||||
|
||||
public static String sha256(String input) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
return HexFormat.of().formatHex(hash);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("SHA-256 algorithm not available", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -80,6 +80,6 @@ public class SysAreaServiceImpl extends ServiceImpl<SysAreaMapper, SysAreaEntity
|
||||
wrapper.orderByAsc(SysAreaEntity::getAreaType);
|
||||
wrapper.orderByDesc(SysAreaEntity::getAreaSort);
|
||||
return baseMapper.selectPage(page, wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+59
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -16,8 +16,10 @@
|
||||
*/
|
||||
package com.pig4cloud.pigx.admin.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes;
|
||||
import com.pig4cloud.pigx.admin.api.dto.SysDictItemSortDTO;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysDict;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysDictItem;
|
||||
import com.pig4cloud.pigx.admin.mapper.SysDictItemMapper;
|
||||
@@ -30,6 +32,11 @@ import com.pig4cloud.pigx.common.core.util.R;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 字典项
|
||||
@@ -80,4 +87,55 @@ public class SysDictItemServiceImpl extends ServiceImpl<SysDictItemMapper, SysDi
|
||||
return R.ok(this.updateById(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@CacheEvict(value = CacheConstants.DICT_DETAILS, allEntries = true)
|
||||
public R updateDictItemSort(SysDictItemSortDTO sortDTO) {
|
||||
SysDict dict = dictService.getById(sortDTO.getDictId());
|
||||
if (dict == null) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_DICT_NOT_FOUND));
|
||||
}
|
||||
if (DictTypeEnum.SYSTEM.getType().equals(dict.getSystemFlag())) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_DICT_UPDATE_SYSTEM));
|
||||
}
|
||||
|
||||
List<Long> itemIds = sortDTO.getItemIds();
|
||||
Set<Long> uniqueItemIds = new HashSet<>(itemIds);
|
||||
if (uniqueItemIds.size() != itemIds.size()) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_DICT_ITEM_SORT_DUPLICATE));
|
||||
}
|
||||
|
||||
List<SysDictItem> sortedItems = this.list(Wrappers.<SysDictItem>lambdaQuery()
|
||||
.eq(SysDictItem::getDictId, sortDTO.getDictId())
|
||||
.orderByAsc(SysDictItem::getSortOrder)
|
||||
.orderByAsc(SysDictItem::getId));
|
||||
Map<Long, SysDictItem> itemMap = sortedItems.stream().collect(Collectors.toMap(SysDictItem::getId, Function.identity()));
|
||||
|
||||
if (!itemMap.keySet().containsAll(itemIds)) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_DICT_ITEM_SORT_SCOPE_INVALID));
|
||||
}
|
||||
|
||||
// 兼容分页排序:以当前页字典项在全量列表中的最小位置作为插入点,将整页按新顺序回插
|
||||
Map<Long, Integer> itemIndexMap = new HashMap<>(sortedItems.size());
|
||||
for (int index = 0; index < sortedItems.size(); index++) {
|
||||
itemIndexMap.put(sortedItems.get(index).getId(), index);
|
||||
}
|
||||
int insertIndex = itemIds.stream().map(itemIndexMap::get).min(Integer::compareTo).orElse(sortedItems.size());
|
||||
|
||||
List<SysDictItem> orderedPageItems = itemIds.stream().map(itemMap::get).toList();
|
||||
List<SysDictItem> reorderedItems = new ArrayList<>(sortedItems);
|
||||
reorderedItems.removeAll(orderedPageItems);
|
||||
reorderedItems.addAll(insertIndex, orderedPageItems);
|
||||
|
||||
List<SysDictItem> updateList = new ArrayList<>(reorderedItems.size());
|
||||
for (int index = 0; index < reorderedItems.size(); index++) {
|
||||
SysDictItem updateItem = new SysDictItem();
|
||||
updateItem.setId(reorderedItems.get(index).getId());
|
||||
updateItem.setSortOrder(index);
|
||||
updateList.add(updateItem);
|
||||
}
|
||||
|
||||
return R.ok(this.updateBatchById(updateList));
|
||||
}
|
||||
|
||||
}
|
||||
+10
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -28,7 +28,6 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.amazonaws.services.s3.model.S3Object;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.pig4cloud.pigx.admin.api.dto.SysFileGroupDTO;
|
||||
@@ -40,6 +39,7 @@ import com.pig4cloud.pigx.admin.service.SysFileService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
|
||||
import com.pig4cloud.pigx.common.file.core.FileObject;
|
||||
import com.pig4cloud.pigx.common.file.core.FileProperties;
|
||||
import com.pig4cloud.pigx.common.file.core.FileTemplate;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@@ -128,13 +128,13 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> impl
|
||||
return;
|
||||
}
|
||||
|
||||
try (S3Object s3Object = fileTemplate.getObject(sysFile.getBucketName(), sysFile.getDir(), fileName)) {
|
||||
try (FileObject fileObject = fileTemplate.getObject(sysFile.getBucketName(), sysFile.getDir(), fileName)) {
|
||||
response.setContentType("application/octet-stream; charset=UTF-8");
|
||||
// 文件唯一hash
|
||||
response.addHeader(SysFile.Fields.hash, sysFile.getHash());
|
||||
response.addHeader(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=" + URLUtil.encode(sysFile.getOriginal()));
|
||||
IoUtil.copy(s3Object.getObjectContent(), response.getOutputStream());
|
||||
IoUtil.copy(fileObject.getObjectContent(), response.getOutputStream());
|
||||
} catch (Exception e) {
|
||||
log.error("文件读取异常: {}", e.getLocalizedMessage());
|
||||
}
|
||||
@@ -250,12 +250,12 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> impl
|
||||
sysFile.setOriginal(file.getOriginalFilename());
|
||||
sysFile.setFileSize(file.getSize());
|
||||
sysFile.setBucketName(properties.getBucketName());
|
||||
sysFile.setType(type);
|
||||
sysFile.setGroupId(groupId);
|
||||
sysFile.setType(type);
|
||||
sysFile.setGroupId(groupId);
|
||||
|
||||
sysFile.setHash(SecureUtil.md5(file.getInputStream()));
|
||||
// 调用save方法保存SysFile对象
|
||||
this.save(sysFile);
|
||||
}
|
||||
sysFile.setHash(SecureUtil.md5(file.getInputStream()));
|
||||
// 调用save方法保存SysFile对象
|
||||
this.save(sysFile);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -23,7 +23,6 @@ import com.pig4cloud.pigx.admin.service.SysI18nService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -45,7 +44,6 @@ public class SysI18nServiceImpl extends ServiceImpl<SysI18nMapper, SysI18nEntity
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@Cacheable(value = CacheConstants.I18N_DETAILS)
|
||||
public Map listMap() {
|
||||
List<SysI18nEntity> sysI18nEntities = baseMapper.selectList(null);
|
||||
HashMap<String, List<Map<String, String>>> stringListHashMap = new HashMap<>();
|
||||
@@ -69,7 +67,7 @@ public class SysI18nServiceImpl extends ServiceImpl<SysI18nMapper, SysI18nEntity
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(value = CacheConstants.I18N_DETAILS, allEntries = true)
|
||||
@CacheEvict(value = CacheConstants.SITE_CONFIG_DETAILS, allEntries = true)
|
||||
public R syncI18nCache() {
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -144,7 +144,7 @@ public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> impleme
|
||||
})
|
||||
// 按createTime升序排序
|
||||
.sorted(Comparator.comparing(map -> map.get(SysLog.Fields.createTime).toString()))
|
||||
.toList();
|
||||
}
|
||||
.toList();
|
||||
}
|
||||
|
||||
}
|
||||
+40
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -28,6 +28,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes;
|
||||
import com.pig4cloud.pigx.admin.api.dto.SysMenuSortDTO;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysI18nEntity;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysMenu;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysRoleMenu;
|
||||
@@ -38,10 +39,8 @@ import com.pig4cloud.pigx.admin.service.SysMenuService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.MenuTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.YesNoEnum;
|
||||
import com.pig4cloud.pigx.common.core.util.MsgUtils;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -54,6 +53,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -125,6 +125,37 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
||||
return this.updateById(sysMenu);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true)
|
||||
public R updateMenuSort(SysMenuSortDTO sortDTO) {
|
||||
List<Long> menuIds = sortDTO.getMenuIds();
|
||||
Set<Long> uniqueMenuIds = new HashSet<>(menuIds);
|
||||
if (uniqueMenuIds.size() != menuIds.size()) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_SORT_DUPLICATE));
|
||||
}
|
||||
|
||||
List<SysMenu> menus = this.list(Wrappers.<SysMenu>lambdaQuery()
|
||||
.in(SysMenu::getMenuId, menuIds)
|
||||
.eq(SysMenu::getParentId, sortDTO.getParentId()));
|
||||
|
||||
if (menus.size() != menuIds.size()) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_SORT_SCOPE_INVALID));
|
||||
}
|
||||
|
||||
Map<Long, SysMenu> menuMap = menus.stream().collect(Collectors.toMap(SysMenu::getMenuId, Function.identity()));
|
||||
List<SysMenu> updateList = new ArrayList<>(menuIds.size());
|
||||
for (int index = 0; index < menuIds.size(); index++) {
|
||||
SysMenu menu = menuMap.get(menuIds.get(index));
|
||||
SysMenu updateMenu = new SysMenu();
|
||||
updateMenu.setMenuId(menu.getMenuId());
|
||||
updateMenu.setSortOrder(index);
|
||||
updateList.add(updateMenu);
|
||||
}
|
||||
|
||||
return R.ok(this.updateBatchById(updateList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建树查询 1. 不是懒加载情况,查询全部 2. 是懒加载,根据parentId 查询 2.1 父节点为空,则查询ID -1
|
||||
*
|
||||
@@ -136,7 +167,11 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
||||
public List<Tree<Long>> treeMenu(Long parentId, String menuName, String type) {
|
||||
Long parent = parentId == null ? CommonConstants.MENU_TREE_ROOT_ID : parentId;
|
||||
|
||||
List<TreeNode<Long>> collect = baseMapper.selectList(Wrappers.<SysMenu>lambdaQuery().eq(Objects.nonNull(parentId), SysMenu::getParentId, parentId).like(StrUtil.isNotBlank(menuName), SysMenu::getName, menuName).eq(StrUtil.isNotBlank(type), SysMenu::getMenuType, type).orderByAsc(SysMenu::getSortOrder)).stream().map(getNodeFunction()).toList();
|
||||
List<TreeNode<Long>> collect = baseMapper.selectList(Wrappers.<SysMenu>lambdaQuery()
|
||||
.eq(Objects.nonNull(parentId), SysMenu::getParentId, parentId)
|
||||
.like(StrUtil.isNotBlank(menuName), SysMenu::getName, menuName)
|
||||
.eq(StrUtil.isNotBlank(type), SysMenu::getMenuType, type)
|
||||
.orderByAsc(SysMenu::getSortOrder)).stream().map(getNodeFunction()).toList();
|
||||
|
||||
// 模糊查询 不组装树结构 直接返回 表格方便编辑
|
||||
if (StrUtil.isNotBlank(menuName)) {
|
||||
@@ -215,7 +250,7 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
||||
return MenuTypeEnum.TOP_MENU.getType().equals(vo.getMenuType());
|
||||
}
|
||||
// 其他查询 左侧 + 顶部
|
||||
return !MenuTypeEnum.BUTTON.getType().equals(vo.getMenuType());
|
||||
return StrUtil.isNotBlank(vo.getMenuType()) && !MenuTypeEnum.BUTTON.getType().equals(vo.getMenuType());
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+52
-9
@@ -14,7 +14,6 @@ import cn.hutool.extra.template.TemplateEngine;
|
||||
import cn.hutool.extra.template.TemplateUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.amazonaws.services.s3.model.S3Object;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
@@ -44,10 +43,13 @@ import com.pig4cloud.pigx.common.data.cache.RedisUtils;
|
||||
import com.pig4cloud.pigx.common.data.resolver.ParamResolver;
|
||||
import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
|
||||
import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
|
||||
import com.pig4cloud.pigx.common.file.core.FileObject;
|
||||
import com.pig4cloud.pigx.common.file.core.FileTemplate;
|
||||
import com.pig4cloud.pigx.common.log.util.JacksonSensitiveFieldUtil;
|
||||
import com.pig4cloud.pigx.common.log.util.LogTypeEnum;
|
||||
import com.pig4cloud.pigx.common.log.util.SysLogUtils;
|
||||
import com.pig4cloud.pigx.common.security.captcha.CaptchaResult;
|
||||
import com.pig4cloud.pigx.common.security.captcha.CaptchaValidator;
|
||||
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -117,6 +119,8 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
|
||||
|
||||
private final FileTemplate fileTemplate;
|
||||
|
||||
private final CaptchaValidator captchaValidator;
|
||||
|
||||
/**
|
||||
* 发送信息
|
||||
*
|
||||
@@ -274,13 +278,15 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
|
||||
/**
|
||||
* 发送手机验证码
|
||||
*
|
||||
* @param mobile 手机号码
|
||||
* @param registered 是否已注册
|
||||
* @param mobile 手机号码
|
||||
* @param registered 是否已注册
|
||||
* @param captchaType 图形验证码类型
|
||||
* @param captchaVerification 图形验证码凭据
|
||||
* @return 发送结果
|
||||
*/
|
||||
@Override
|
||||
public R<Boolean> sendSmsCode(String mobile, boolean registered) {
|
||||
// IP 限制校验,单位时间内同一IP发送次数限制
|
||||
public R<Boolean> sendSmsCode(String mobile, boolean registered, String captchaType, String captchaVerification) {
|
||||
// IP 限制校验前置,避免恶意请求反向放大图形验证码服务压力
|
||||
String clientIp = WebUtils.getIP();
|
||||
String ipLimitKey = CacheConstants.DEFAULT_CODE_KEY + "IP_LIMIT:" + clientIp;
|
||||
if (StrUtil.isNotBlank(clientIp)) {
|
||||
@@ -295,6 +301,11 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
|
||||
}
|
||||
}
|
||||
|
||||
CaptchaResult captchaResult = captchaValidator.validate(captchaType, captchaVerification);
|
||||
if (!captchaResult.isOk()) {
|
||||
return R.failed(Boolean.FALSE, captchaResult.getErrorMessage());
|
||||
}
|
||||
|
||||
List<SysUser> userList = TenantBroker
|
||||
.noneAs(() -> userMapper.selectList(Wrappers.<SysUser>query().lambda().eq(SysUser::getPhone, mobile)));
|
||||
|
||||
@@ -332,6 +343,13 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
|
||||
*/
|
||||
@Override
|
||||
public R sendSms(MessageSmsDTO messageSmsDTO) {
|
||||
R captchaResult = validateCaptchaIfPresent(messageSmsDTO.getCaptchaType(),
|
||||
messageSmsDTO.getCaptchaVerification());
|
||||
if (captchaResult != null) {
|
||||
return captchaResult;
|
||||
}
|
||||
messageSmsDTO.setCaptchaType(null);
|
||||
messageSmsDTO.setCaptchaVerification(null);
|
||||
|
||||
// 根据业务编码获取短信通道配置
|
||||
List<SysSystemConfigEntity> configEntityList = sysSystemConfigMapper
|
||||
@@ -475,6 +493,14 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public R sendEmail(MessageEmailDTO messageEmailDTO) {
|
||||
R captchaResult = validateCaptchaIfPresent(messageEmailDTO.getCaptchaType(),
|
||||
messageEmailDTO.getCaptchaVerification());
|
||||
if (captchaResult != null) {
|
||||
return captchaResult;
|
||||
}
|
||||
messageEmailDTO.setCaptchaType(null);
|
||||
messageEmailDTO.setCaptchaVerification(null);
|
||||
|
||||
// 根据业务编码获取短信通道配置
|
||||
List<SysSystemConfigEntity> configEntityList = sysSystemConfigMapper
|
||||
.selectList(Wrappers.<SysSystemConfigEntity>lambdaQuery()
|
||||
@@ -493,10 +519,11 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
|
||||
for (String fileName : messageEmailDTO.getAttachmentList()) {
|
||||
SysFile sysFile = fileMapper
|
||||
.selectOne(Wrappers.<SysFile>lambdaQuery().eq(SysFile::getFileName, fileName));
|
||||
S3Object s3Object = fileTemplate.getObject(sysFile.getBucketName(), sysFile.getDir(), fileName);
|
||||
File file = FileUtil.file(FileUtil.getTmpDirPath(), sysFile.getOriginal());
|
||||
tempFileList.add(file);
|
||||
IoUtil.copy(s3Object.getObjectContent(), FileUtil.getOutputStream(file));
|
||||
try (FileObject fileObject = fileTemplate.getObject(sysFile.getBucketName(), sysFile.getDir(), fileName)) {
|
||||
File file = FileUtil.file(FileUtil.getTmpDirPath(), sysFile.getOriginal());
|
||||
tempFileList.add(file);
|
||||
IoUtil.copy(fileObject.getObjectContent(), FileUtil.getOutputStream(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,6 +560,22 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验图形验证码(如提供)。
|
||||
*
|
||||
* @param captchaType 验证码类型,为空表示跳过校验
|
||||
* @param captchaVerification 验证码凭据
|
||||
* @return 通过返回 null;失败返回带错误信息的 R
|
||||
*/
|
||||
private R validateCaptchaIfPresent(String captchaType, String captchaVerification) {
|
||||
if (StrUtil.isAllBlank(captchaType, captchaVerification)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CaptchaResult captchaResult = captchaValidator.validate(captchaType, captchaVerification);
|
||||
return captchaResult.isOk() ? null : R.failed(captchaResult.getErrorMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出 Hook 业务代码
|
||||
*
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package com.pig4cloud.pigx.admin.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysRoleWidget;
|
||||
import com.pig4cloud.pigx.admin.mapper.SysRoleWidgetMapper;
|
||||
import com.pig4cloud.pigx.admin.service.SysRoleWidgetService;
|
||||
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 角色首页组件配置 Service 实现
|
||||
*
|
||||
* @author lengleng
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class SysRoleWidgetServiceImpl extends ServiceImpl<SysRoleWidgetMapper, SysRoleWidget>
|
||||
implements SysRoleWidgetService {
|
||||
|
||||
/**
|
||||
* 根据角色ID查询widget配置
|
||||
* @param roleId 角色ID
|
||||
* @return widget配置,无配置时返回 null
|
||||
*/
|
||||
@Override
|
||||
public SysRoleWidget getByRoleId(Long roleId) {
|
||||
return getOne(Wrappers.<SysRoleWidget>lambdaQuery()
|
||||
.eq(SysRoleWidget::getRoleId, roleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前登录用户的角色列表查询widget配置
|
||||
* <p>遍历用户所有角色对应的配置,返回 widgetKeys 数量最多的那条,
|
||||
* 以保证多角色场景下首页显示最完整的widget集合。</p>
|
||||
* @return widget配置,用户无角色或角色均无配置时返回 null
|
||||
*/
|
||||
@Override
|
||||
public SysRoleWidget getByCurrentUser() {
|
||||
List<Long> roleIds = SecurityUtils.getRoleIds();
|
||||
if (roleIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// 查询当前用户所有角色的 widget 配置,取 widgetKeys 数量最多的
|
||||
return list(Wrappers.<SysRoleWidget>lambdaQuery()
|
||||
.in(SysRoleWidget::getRoleId, roleIds))
|
||||
.stream()
|
||||
.filter(w -> StringUtils.hasText(w.getWidgetKeys()))
|
||||
.max(Comparator.comparingInt(
|
||||
w -> w.getWidgetKeys().split(StrUtil.COMMA).length))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存或更新角色widget配置
|
||||
* <p>若该角色已存在配置则更新,否则新增。</p>
|
||||
* @param sysRoleWidget widget配置(roleId 必填)
|
||||
* @return 操作成功返回 true
|
||||
*/
|
||||
@Override
|
||||
public boolean saveOrUpdateByRoleId(SysRoleWidget sysRoleWidget) {
|
||||
SysRoleWidget existing = getOne(Wrappers.<SysRoleWidget>lambdaQuery()
|
||||
.eq(SysRoleWidget::getRoleId, sysRoleWidget.getRoleId()));
|
||||
if (existing != null) {
|
||||
sysRoleWidget.setId(existing.getId());
|
||||
return updateById(sysRoleWidget);
|
||||
}
|
||||
return save(sysRoleWidget);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -141,6 +141,6 @@ public class SysRouteConfServiceImpl extends ServiceImpl<SysRouteConfMapper, Sys
|
||||
baseMapper.insert(routeConf);
|
||||
this.applicationEventPublisher.publishEvent(new DynamicRouteInitEvent(this));
|
||||
return routeConf;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -56,10 +56,10 @@ public class SysScheduleServiceImpl extends ServiceImpl<SysScheduleMapper, SysSc
|
||||
public IPage<SysScheduleEntity> getScheduleByScope(Page page, SysScheduleEntity sysSchedule) {
|
||||
LambdaQueryWrapper<SysScheduleEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.like(StrUtil.isNotBlank(sysSchedule.getTitle()), SysScheduleEntity::getTitle, sysSchedule.getTitle());
|
||||
wrapper.like(StrUtil.isNotBlank(sysSchedule.getScheduleType()), SysScheduleEntity::getScheduleType,
|
||||
sysSchedule.getScheduleType());
|
||||
wrapper.eq(Objects.nonNull(sysSchedule.getScheduleDate()), SysScheduleEntity::getScheduleDate,
|
||||
sysSchedule.getScheduleDate());
|
||||
wrapper.like(StrUtil.isNotBlank(sysSchedule.getScheduleType()), SysScheduleEntity::getScheduleType,
|
||||
sysSchedule.getScheduleType());
|
||||
wrapper.eq(Objects.nonNull(sysSchedule.getScheduleDate()), SysScheduleEntity::getScheduleDate,
|
||||
sysSchedule.getScheduleDate());
|
||||
DataScope dataScope = new DataScope();
|
||||
dataScope.setUsername(SecurityUtils.getUser().getUsername());
|
||||
return baseMapper.selectPageByScope(page, wrapper, dataScope);
|
||||
|
||||
+3
-3
@@ -63,8 +63,8 @@ public class SysSensitiveWordServiceImpl extends ServiceImpl<SysSensitiveWordMap
|
||||
RedisUtils.execute((RedisCallback<Void>) connection -> {
|
||||
connection.publish(CacheConstants.SENSITIVE_REDIS_RELOAD_TOPIC.getBytes(), "刷新敏感词缓存".getBytes());
|
||||
return null;
|
||||
});
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
});
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysOauthClientDetails;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysPublicParam;
|
||||
import com.pig4cloud.pigx.admin.config.ClientDetailsInitRunner;
|
||||
import com.pig4cloud.pigx.admin.dto.SiteConfigDTO;
|
||||
import com.pig4cloud.pigx.admin.service.SysI18nService;
|
||||
import com.pig4cloud.pigx.admin.service.SysOauthClientDetailsService;
|
||||
import com.pig4cloud.pigx.admin.service.SysPublicParamService;
|
||||
import com.pig4cloud.pigx.admin.service.SysSiteConfigService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.DictTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.YesNoEnum;
|
||||
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 站点配置服务实现
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-27
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SysSiteConfigServiceImpl implements SysSiteConfigService {
|
||||
|
||||
/**
|
||||
* SITE_ 参数键与 DTO 字段名的映射
|
||||
*/
|
||||
private static final Map<String, String> KEY_FIELD_MAP = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* 布尔类型的字段集合
|
||||
*/
|
||||
private static final Set<String> BOOLEAN_FIELDS = Set.of("forceResetPwd",
|
||||
"forceLogout", "smsLoginEnable", "socialLoginEnable", "registerEnable", "resetPassword", "i18nEnable", "darkModeEnable",
|
||||
"antiDebugEnable", "syncDingtalkEnabled", "syncWechatEnabled");
|
||||
|
||||
static {
|
||||
KEY_FIELD_MAP.put("SITE_CLARITY_ID", "clarityId");
|
||||
KEY_FIELD_MAP.put("SITE_CAPTCHA_TYPE", "captchaType");
|
||||
KEY_FIELD_MAP.put("SITE_PASSWORD_RULE", "passwordRule");
|
||||
KEY_FIELD_MAP.put("SITE_FORCE_RESET_PWD", "forceResetPwd");
|
||||
KEY_FIELD_MAP.put("SITE_FORCE_LOGOUT", "forceLogout");
|
||||
KEY_FIELD_MAP.put("SITE_SMS_LOGIN_ENABLE", "smsLoginEnable");
|
||||
KEY_FIELD_MAP.put("SITE_SOCIAL_LOGIN_ENABLE", "socialLoginEnable");
|
||||
KEY_FIELD_MAP.put("SITE_REGISTER_ENABLE", "registerEnable");
|
||||
KEY_FIELD_MAP.put("SITE_RESET_PASSWORD", "resetPassword");
|
||||
KEY_FIELD_MAP.put("SITE_I18N_ENABLE", "i18nEnable");
|
||||
KEY_FIELD_MAP.put("SITE_DARK_MODE_ENABLE", "darkModeEnable");
|
||||
KEY_FIELD_MAP.put("SITE_ANTI_DEBUG_ENABLE", "antiDebugEnable");
|
||||
KEY_FIELD_MAP.put("SITE_ANTI_DEBUG_KEY", "antiDebugKey");
|
||||
KEY_FIELD_MAP.put("SITE_SYNC_DINGTALK_ENABLED", "syncDingtalkEnabled");
|
||||
KEY_FIELD_MAP.put("SITE_SYNC_WECHAT_ENABLED", "syncWechatEnabled");
|
||||
KEY_FIELD_MAP.put("SITE_TITLE", "title");
|
||||
KEY_FIELD_MAP.put("SITE_FOOTER", "footer");
|
||||
KEY_FIELD_MAP.put("SITE_PRIVACY_TIP", "privacyTip");
|
||||
KEY_FIELD_MAP.put("SITE_LOGO", "logo");
|
||||
}
|
||||
|
||||
private final SysPublicParamService sysPublicParamService;
|
||||
|
||||
private final SysI18nService sysI18nService;
|
||||
|
||||
private final SysOauthClientDetailsService sysOauthClientDetailsService;
|
||||
|
||||
@Override
|
||||
@Cacheable(value = CacheConstants.SITE_CONFIG_DETAILS)
|
||||
public Map<String, Object> getAggregatedConfig() {
|
||||
Map<String, Object> result = new HashMap<>(4);
|
||||
result.put("i18n", sysI18nService.listMap());
|
||||
result.put("site", getSiteConfig());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SiteConfigDTO getSiteConfig() {
|
||||
List<SysPublicParam> paramList = sysPublicParamService
|
||||
.list(Wrappers.<SysPublicParam>lambdaQuery().likeRight(SysPublicParam::getPublicKey, "SITE_"));
|
||||
|
||||
SiteConfigDTO dto = new SiteConfigDTO();
|
||||
for (SysPublicParam param : paramList) {
|
||||
String fieldName = KEY_FIELD_MAP.get(param.getPublicKey());
|
||||
if (fieldName == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object value;
|
||||
if (BOOLEAN_FIELDS.contains(fieldName)) {
|
||||
value = YesNoEnum.YES.getCode().equals(param.getPublicValue());
|
||||
} else {
|
||||
value = param.getPublicValue();
|
||||
}
|
||||
BeanUtil.setFieldValue(dto, fieldName, value);
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@CacheEvict(value = {CacheConstants.SITE_CONFIG_DETAILS, CacheConstants.PARAMS_DETAILS}, allEntries = true)
|
||||
public void updateSiteConfig(SiteConfigDTO dto) {
|
||||
Map<String, Object> fieldValues = BeanUtil.beanToMap(dto, false, true);
|
||||
|
||||
for (Map.Entry<String, String> entry : KEY_FIELD_MAP.entrySet()) {
|
||||
String paramKey = entry.getKey();
|
||||
String fieldName = entry.getValue();
|
||||
Object fieldValue = fieldValues.get(fieldName);
|
||||
if (fieldValue == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String publicValue;
|
||||
if (BOOLEAN_FIELDS.contains(fieldName)) {
|
||||
publicValue = YesNoEnum.getCode((Boolean) fieldValue);
|
||||
} else {
|
||||
publicValue = fieldValue.toString();
|
||||
}
|
||||
|
||||
SysPublicParam existing = sysPublicParamService
|
||||
.getOne(Wrappers.<SysPublicParam>lambdaQuery().eq(SysPublicParam::getPublicKey, paramKey));
|
||||
if (Objects.nonNull(existing)) {
|
||||
existing.setPublicValue(publicValue);
|
||||
sysPublicParamService.updateById(existing);
|
||||
} else {
|
||||
SysPublicParam newParam = new SysPublicParam();
|
||||
newParam.setPublicKey(paramKey);
|
||||
newParam.setPublicValue(publicValue);
|
||||
newParam.setPublicName(paramKey);
|
||||
newParam.setStatus(YesNoEnum.NO.getCode());
|
||||
newParam.setSystemFlag(DictTypeEnum.SYSTEM.getType());
|
||||
sysPublicParamService.save(newParam);
|
||||
}
|
||||
}
|
||||
|
||||
// 同步 captcha_flag 到 OAuth 客户端扩展信息
|
||||
if (dto.getCaptchaType() != null) {
|
||||
syncCaptchaFlagToClients(!"none".equals(dto.getCaptchaType()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新站点配置缓存
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(value = {CacheConstants.SITE_CONFIG_DETAILS, CacheConstants.PARAMS_DETAILS}, allEntries = true)
|
||||
public void refreshCache() {
|
||||
// 仅触发 @CacheEvict,无需额外逻辑
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步验证码开关到所有 OAuth 客户端的 additionalInformation
|
||||
*
|
||||
* @param captchaEnable 是否启用验证码
|
||||
*/
|
||||
private void syncCaptchaFlagToClients(Boolean captchaEnable) {
|
||||
String captchaFlag = YesNoEnum.getCode(captchaEnable);
|
||||
List<SysOauthClientDetails> clients = sysOauthClientDetailsService.list();
|
||||
|
||||
for (SysOauthClientDetails client : clients) {
|
||||
if (StrUtil.isBlank(client.getAdditionalInformation())) {
|
||||
continue;
|
||||
}
|
||||
JSONObject informationObj = JSONUtil.parseObj(client.getAdditionalInformation());
|
||||
informationObj.set(CommonConstants.CAPTCHA_FLAG, captchaFlag);
|
||||
client.setAdditionalInformation(informationObj.toString());
|
||||
sysOauthClientDetailsService.updateById(client);
|
||||
}
|
||||
|
||||
// 刷新 Redis 客户端缓存
|
||||
SpringContextHolder.publishEvent(new ClientDetailsInitRunner.ClientDetailsInitEvent(this));
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -113,9 +113,9 @@ public class SysSystemConfigServiceImpl extends ServiceImpl<SysSystemConfigMappe
|
||||
Field configsField = ReflectUtil.getField(OaFactory.class, "CONFIGS");
|
||||
configsField.setAccessible(true);
|
||||
Map<String, OaSender> configs = (Map<String, OaSender>) configsField.get(null);
|
||||
configs.clear();
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
configs.clear();
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysClarityData;
|
||||
import com.pig4cloud.pigx.admin.handler.ClarityFetchTaskHandler;
|
||||
import com.pig4cloud.pigx.admin.mapper.SysClarityDataMapper;
|
||||
import com.pig4cloud.pigx.admin.service.SysSystemInfoService;
|
||||
import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes;
|
||||
import com.pig4cloud.pigx.common.core.util.MsgUtils;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.data.cache.RedisUtils;
|
||||
import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.connection.RedisServerCommands;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 系统监控服务实现
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SysSystemInfoServiceImpl extends ServiceImpl<SysClarityDataMapper, SysClarityData>
|
||||
implements SysSystemInfoService {
|
||||
|
||||
private final ClarityFetchTaskHandler clarityFetchTask;
|
||||
|
||||
@Override
|
||||
public R<Map<String, Object>> getCacheInfo() {
|
||||
Properties info = RedisUtils.execute((RedisCallback<Properties>) RedisServerCommands::info);
|
||||
Properties commandStats = RedisUtils
|
||||
.execute((RedisCallback<Properties>) connection -> connection.serverCommands().info("commandstats"));
|
||||
Object dbSize = RedisUtils.execute((RedisCallback<Object>) RedisServerCommands::dbSize);
|
||||
|
||||
if (commandStats == null) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_SYSTEM_CACHE_FETCH_ERROR));
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>(3);
|
||||
result.put("info", info);
|
||||
result.put("dbSize", dbSize);
|
||||
|
||||
List<Map<String, String>> pieList = new ArrayList<>();
|
||||
commandStats.stringPropertyNames().forEach(key -> {
|
||||
Map<String, String> data = new HashMap<>(2);
|
||||
String property = commandStats.getProperty(key);
|
||||
data.put("name", StringUtils.removeStart(key, "cmdstat_"));
|
||||
data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
|
||||
pieList.add(data);
|
||||
});
|
||||
|
||||
result.put("commandStats", pieList);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> getClarityData(int numOfDays) {
|
||||
// 查询今日对应 numOfDays 的缓存数据
|
||||
SysClarityData today = baseMapper.selectOne(Wrappers.<SysClarityData>lambdaQuery()
|
||||
.eq(SysClarityData::getDataDate, LocalDate.now())
|
||||
.eq(SysClarityData::getNumOfDays, numOfDays));
|
||||
|
||||
if (today != null) {
|
||||
return R.ok(today);
|
||||
}
|
||||
|
||||
// 无数据:触发异步拉取,立即返回 loading 状态
|
||||
// 提前捕获租户 ID,避免 @Async 新线程丢失 ThreadLocal 上下文
|
||||
clarityFetchTask.fetchAndSave(TenantContextHolder.getTenantId(), numOfDays);
|
||||
|
||||
Map<String, Object> loading = new HashMap<>(2);
|
||||
loading.put("loading", true);
|
||||
loading.put("message", "数据获取中,请稍后手动刷新");
|
||||
return R.ok(loading);
|
||||
}
|
||||
|
||||
}
|
||||
+64
-9
@@ -246,24 +246,68 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteUserByIds(Long[] ids) {
|
||||
Long currentTenantId = SecurityUtils.getUser().getTenantId();
|
||||
List<Long> userIds = CollUtil.toList(ids);
|
||||
|
||||
// 删除 spring cache
|
||||
List<SysUser> userList = baseMapper.selectByIds(CollUtil.toList(ids));
|
||||
List<SysUser> userList = baseMapper.selectByIds(userIds);
|
||||
Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
|
||||
for (SysUser sysUser : userList) {
|
||||
cache.evict(sysUser.getUsername());
|
||||
}
|
||||
|
||||
// 删除用户角色
|
||||
sysUserRoleMapper.delete(Wrappers.<SysUserRole>lambdaQuery().in(SysUserRole::getUserId, CollUtil.toList(ids)));
|
||||
sysUserDeptMapper.delete(Wrappers.<SysUserDept>lambdaQuery().in(SysUserDept::getUserId, CollUtil.toList(ids)));
|
||||
sysUserPostMapper.delete(Wrappers.<SysUserPost>lambdaQuery().in(SysUserPost::getUserId, CollUtil.toList(ids)));
|
||||
sysUserRoleMapper.delete(Wrappers.<SysUserRole>lambdaQuery().in(SysUserRole::getUserId, userIds));
|
||||
sysUserDeptMapper.delete(Wrappers.<SysUserDept>lambdaQuery().in(SysUserDept::getUserId, userIds));
|
||||
sysUserPostMapper.delete(Wrappers.<SysUserPost>lambdaQuery().in(SysUserPost::getUserId, userIds));
|
||||
sysTenantUserMapper.delete(Wrappers.<SysTenantUser>lambdaQuery()
|
||||
.in(SysTenantUser::getUserId, CollUtil.toList(ids))
|
||||
.eq(SysTenantUser::getTenantId, SecurityUtils.getUser().getTenantId()));
|
||||
this.removeBatchByIds(CollUtil.toList(ids));
|
||||
.in(SysTenantUser::getUserId, userIds)
|
||||
.eq(SysTenantUser::getTenantId, currentTenantId));
|
||||
|
||||
// 用户降级处理,如果用户关联了其他租户,则不删除用户信息,仅删除当前租户关联关系并更新用户所属租户;如果没有关联其他租户,则删除用户信息
|
||||
List<Long> removableUserIds = findRemovableUserIds(userList, currentTenantId);
|
||||
if (CollUtil.isNotEmpty(removableUserIds)) {
|
||||
this.removeBatchByIds(removableUserIds);
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找可移除的用户ID列表
|
||||
*
|
||||
* @param userList 用户列表
|
||||
* @param currentTenantId 当前租户ID
|
||||
* @return 可移除的用户ID列表
|
||||
*/
|
||||
private List<Long> findRemovableUserIds(List<SysUser> userList, Long currentTenantId) {
|
||||
return TenantBroker.noneAs(() -> {
|
||||
List<Long> removableUserIds = new ArrayList<>(userList.size());
|
||||
for (SysUser sysUser : userList) {
|
||||
SysTenantUser otherTenantUser = sysTenantUserMapper.selectOne(Wrappers.<SysTenantUser>lambdaQuery()
|
||||
.eq(SysTenantUser::getUserId, sysUser.getUserId())
|
||||
.ne(SysTenantUser::getTenantId, currentTenantId), false);
|
||||
|
||||
if (Objects.isNull(otherTenantUser)) {
|
||||
removableUserIds.add(sysUser.getUserId());
|
||||
continue;
|
||||
}
|
||||
|
||||
UserDTO userDTO = new UserDTO();
|
||||
userDTO.setUserId(sysUser.getUserId());
|
||||
userDTO.setUsername(sysUser.getUsername());
|
||||
userDTO.setTenantId(otherTenantUser.getTenantId());
|
||||
sysTenantService.updateUserTenant(userDTO);
|
||||
}
|
||||
return removableUserIds;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* @param userDto 用户信息传输对象
|
||||
* @return 更新结果
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username")
|
||||
public R<Boolean> updateUserInfo(UserDTO userDto) {
|
||||
@@ -282,14 +326,25 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
if (!StrUtil.equals(userDto.getCode(), codeObj.toString())) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_SMS_ERROR));
|
||||
}
|
||||
sysUser.setPhone(userDto.getPhone());
|
||||
}
|
||||
|
||||
return R.ok(this.updateById(sysUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息。
|
||||
* <p>
|
||||
* 字段审计:根据 {@code userId} 在方法执行前后分别调用当前 Service 的 Mapper 查询用户快照,
|
||||
* 对比结果由 pigx-common-audit 写入 sys_audit_log。
|
||||
* </p>
|
||||
*
|
||||
* @param userDto 用户更新数据
|
||||
* @return 更新结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Audit(name = "用户更新", spel = "@sysUserMapper.selectById(#userDto.userId)")
|
||||
@Audit(name = "用户更新", id = "#userDto.userId")
|
||||
@CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username")
|
||||
public Boolean updateUser(UserDTO userDto) {
|
||||
// 更新用户表信息
|
||||
@@ -537,7 +592,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
public R<Boolean> registerUser(RegisterUserDTO userDto) {
|
||||
String feignHeader = WebUtils.getRequest().getHeader(SecurityConstants.FROM);
|
||||
// 外部注册校验短信验证码,对 AI 的内部注册跳过验证码处理
|
||||
if (!SecurityConstants.FROM_IN.equals(feignHeader)){
|
||||
if (!SecurityConstants.FROM_IN.equals(feignHeader)) {
|
||||
if (StrUtil.isBlank(userDto.getCode())) {
|
||||
return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_PARAM_ILLEGAL));
|
||||
}
|
||||
|
||||
@@ -12,10 +12,11 @@ spring:
|
||||
server-addr: ${NACOS_HOST:pigx-register}:${NACOS_PORT:8848}
|
||||
config:
|
||||
server-addr: ${spring.cloud.nacos.discovery.server-addr}
|
||||
gateway.server.webflux: # 关闭 Spring Cloud Gateway 的能力,避免与 Spring MVC 冲突。
|
||||
redis.enabled: false
|
||||
enabled: false
|
||||
config:
|
||||
import:
|
||||
- optional:nacos:application-@profiles.active@.yml
|
||||
- optional:nacos:${spring.application.name}-@profiles.active@.yml
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
@@ -48,7 +48,7 @@
|
||||
WHERE p.post_id = up.post_id
|
||||
AND p.del_flag = '0'
|
||||
and up.user_id = #{userId}
|
||||
and up.tenant_id = #{tenantId}
|
||||
and up.tenant_id = #{tenantId}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
@@ -49,6 +49,6 @@
|
||||
WHERE sys_role.role_id = sys_user_role.role_id
|
||||
AND sys_role.del_flag = '0'
|
||||
and sys_user_role.user_id = #{userId}
|
||||
and sys_user_role.tenant_id = #{tenantId}
|
||||
and sys_user_role.tenant_id = #{tenantId}
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.pig4cloud.pigx.admin.mapper.SysRoleWidgetMapper">
|
||||
</mapper>
|
||||
Reference in new issue
Block a user