Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f1408b9d0 | ||
|
|
d555aad9ac | ||
|
|
029eea70cc | ||
|
|
2f42204794 | ||
|
|
22b1e65385 | ||
|
|
72bdb8d49b | ||
|
|
5ec6bc472f | ||
|
|
81b7356d67 | ||
|
|
44af16f451 | ||
|
|
c6157db0de | ||
|
|
39ac73dc66 | ||
|
|
2170c48763 | ||
|
|
c2d1c48aae | ||
|
|
e14958c643 | ||
|
|
da288338cd | ||
|
|
ea430f2468 | ||
|
|
f76f79744a | ||
|
|
19daaff907 | ||
|
|
db94d8f932 | ||
|
|
53618fe362 | ||
|
|
523edffc4e | ||
|
|
c7ddb1a8b0 | ||
|
|
45c9fc814a | ||
|
|
407dc13ac4 | ||
|
|
0c06bf16c2 | ||
|
|
1db39e77d3 |
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<groupId>com.imyeyu.timiserverapi</groupId>
|
<groupId>com.imyeyu.timiserverapi</groupId>
|
||||||
<artifactId>TimiServerAPI</artifactId>
|
<artifactId>TimiServerAPI</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.11</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>TimiServerAPI</name>
|
<name>TimiServerAPI</name>
|
||||||
<description>imyeyu.com API</description>
|
<description>imyeyu.com API</description>
|
||||||
@@ -178,7 +178,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.imyeyu.network</groupId>
|
<groupId>com.imyeyu.network</groupId>
|
||||||
<artifactId>timi-network</artifactId>
|
<artifactId>timi-network</artifactId>
|
||||||
<version>0.0.13</version>
|
<version>0.0.14</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.imyeyu.lang</groupId>
|
<groupId>com.imyeyu.lang</groupId>
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
package com.imyeyu.api.annotation;
|
|
||||||
|
|
||||||
import com.imyeyu.api.bean.RoleMatchMode;
|
|
||||||
import com.imyeyu.api.bean.CoreRoleCode;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// 要求 CORE 模块角色
|
|
||||||
///
|
|
||||||
/// @author Codex
|
|
||||||
/// @since 2026-07-26
|
|
||||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface RequireCoreRole {
|
|
||||||
|
|
||||||
/// 所需角色
|
|
||||||
///
|
|
||||||
/// @return 角色列表
|
|
||||||
CoreRoleCode[] value();
|
|
||||||
|
|
||||||
/// 匹配模式
|
|
||||||
///
|
|
||||||
/// @return 匹配模式
|
|
||||||
RoleMatchMode mode() default RoleMatchMode.ALL;
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package com.imyeyu.api.annotation;
|
|
||||||
|
|
||||||
import com.imyeyu.api.bean.ModuleCode;
|
|
||||||
import com.imyeyu.api.bean.RoleMatchMode;
|
|
||||||
import com.imyeyu.api.bean.CoreRoleCode;
|
|
||||||
import com.imyeyu.api.modules.user.service.RoleChecker;
|
|
||||||
import com.imyeyu.java.TimiJava;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.springframework.lang.NonNull;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.method.HandlerMethod;
|
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// CORE 角色注解处理器
|
|
||||||
///
|
|
||||||
/// @author Codex
|
|
||||||
/// @since 2026-07-27
|
|
||||||
@Component
|
|
||||||
public class RequireCoreRoleInterceptor implements HandlerInterceptor {
|
|
||||||
|
|
||||||
private final RoleChecker roleChecker;
|
|
||||||
|
|
||||||
public RequireCoreRoleInterceptor(RoleChecker roleChecker) {
|
|
||||||
this.roleChecker = roleChecker;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean preHandle(@NonNull HttpServletRequest req, @NonNull HttpServletResponse resp, @NonNull Object handler) {
|
|
||||||
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
RequireCoreRole annotation = handlerMethod.getMethodAnnotation(RequireCoreRole.class);
|
|
||||||
if (annotation == null || TimiJava.isEmpty(annotation.value())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
String[] roleCodeList = java.util.Arrays.stream(annotation.value()).map(CoreRoleCode::name).toArray(String[]::new);
|
|
||||||
if (annotation.mode() == RoleMatchMode.ANY) {
|
|
||||||
roleChecker.checkAny(ModuleCode.CORE, roleCodeList);
|
|
||||||
} else {
|
|
||||||
roleChecker.checkAll(ModuleCode.CORE, roleCodeList);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,9 +25,9 @@ public enum CorePermissionCode implements BuiltinPermissionCode {
|
|||||||
ROLE_PERMISSION_READ("ROLE_PERMISSION:READ", "角色权限读取", false, true),
|
ROLE_PERMISSION_READ("ROLE_PERMISSION:READ", "角色权限读取", false, true),
|
||||||
ROLE_PERMISSION_DELETE("ROLE_PERMISSION:DELETE", "角色权限删除", false, true),
|
ROLE_PERMISSION_DELETE("ROLE_PERMISSION:DELETE", "角色权限删除", false, true),
|
||||||
|
|
||||||
ROLE_RELATION_CREATE("ROLE_RELATION:CREATE", "角色关联创建", false, true),
|
ROLE_PERMISSION_INHERIT_CREATE("ROLE_PERMISSION_INHERIT:CREATE", "角色权限继承创建", false, true),
|
||||||
ROLE_RELATION_READ("ROLE_RELATION:READ", "角色关联读取", false, true),
|
ROLE_PERMISSION_INHERIT_READ("ROLE_PERMISSION_INHERIT:READ", "角色权限继承读取", false, true),
|
||||||
ROLE_RELATION_DELETE("ROLE_RELATION:DELETE", "角色关联删除", false, true),
|
ROLE_PERMISSION_INHERIT_DELETE("ROLE_PERMISSION_INHERIT:DELETE", "角色权限继承删除", false, true),
|
||||||
|
|
||||||
SETTING_CREATE("SETTING:CREATE", "设置创建", false, true),
|
SETTING_CREATE("SETTING:CREATE", "设置创建", false, true),
|
||||||
SETTING_READ("SETTING:READ", "设置读取", false, true),
|
SETTING_READ("SETTING:READ", "设置读取", false, true),
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ public enum CoreRoleCode implements BuiltinRoleCode {
|
|||||||
|
|
||||||
SYSTEM("系统", true, null),
|
SYSTEM("系统", true, null),
|
||||||
|
|
||||||
ADMIN("管理员", false, SYSTEM.name()),
|
ADMIN("管理员", false, SYSTEM.name());
|
||||||
|
|
||||||
USER_ROLE_ADMIN("授权管理", false, ADMIN.name());
|
|
||||||
|
|
||||||
final String defaultName;
|
final String defaultName;
|
||||||
final boolean protectedRole;
|
final boolean protectedRole;
|
||||||
@@ -40,4 +38,5 @@ public enum CoreRoleCode implements BuiltinRoleCode {
|
|||||||
public String getParentCode() {
|
public String getParentCode() {
|
||||||
return parentCode;
|
return parentCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
|||||||
import com.imyeyu.api.annotation.EnableSettingInterceptor;
|
import com.imyeyu.api.annotation.EnableSettingInterceptor;
|
||||||
import com.imyeyu.api.annotation.RequestRateLimitInterceptor;
|
import com.imyeyu.api.annotation.RequestRateLimitInterceptor;
|
||||||
import com.imyeyu.api.annotation.RequireCorePermissionInterceptor;
|
import com.imyeyu.api.annotation.RequireCorePermissionInterceptor;
|
||||||
import com.imyeyu.api.annotation.RequireCoreRoleInterceptor;
|
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermissionInterceptor;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermissionInterceptor;
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoRoleInterceptor;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoRoleInterceptor;
|
||||||
import com.imyeyu.api.modules.journal.util.JournalAPIInterceptor;
|
import com.imyeyu.api.modules.journal.util.JournalAPIInterceptor;
|
||||||
@@ -44,7 +43,6 @@ public class WebConfig implements WebMvcConfigurer {
|
|||||||
private final JournalAPIInterceptor journalAPIInterceptor;
|
private final JournalAPIInterceptor journalAPIInterceptor;
|
||||||
private final EnableSettingInterceptor enableSettingInterceptor;
|
private final EnableSettingInterceptor enableSettingInterceptor;
|
||||||
private final RequireGaoRoleInterceptor requireGaoRoleInterceptor;
|
private final RequireGaoRoleInterceptor requireGaoRoleInterceptor;
|
||||||
private final RequireCoreRoleInterceptor requireCoreRoleInterceptor;
|
|
||||||
private final RequestRateLimitInterceptor requestRateLimitInterceptor;
|
private final RequestRateLimitInterceptor requestRateLimitInterceptor;
|
||||||
private final RequireGaoPermissionInterceptor requireGaoPermissionInterceptor;
|
private final RequireGaoPermissionInterceptor requireGaoPermissionInterceptor;
|
||||||
private final RequireCorePermissionInterceptor requireCorePermissionInterceptor;
|
private final RequireCorePermissionInterceptor requireCorePermissionInterceptor;
|
||||||
@@ -59,7 +57,6 @@ public class WebConfig implements WebMvcConfigurer {
|
|||||||
registry.addInterceptor(systemAPIInterceptor).addPathPatterns(SystemAPIInterceptor.PATH);
|
registry.addInterceptor(systemAPIInterceptor).addPathPatterns(SystemAPIInterceptor.PATH);
|
||||||
registry.addInterceptor(journalAPIInterceptor).addPathPatterns(JournalAPIInterceptor.PATH);
|
registry.addInterceptor(journalAPIInterceptor).addPathPatterns(JournalAPIInterceptor.PATH);
|
||||||
registry.addInterceptor(enableSettingInterceptor).addPathPatterns("/**");
|
registry.addInterceptor(enableSettingInterceptor).addPathPatterns("/**");
|
||||||
registry.addInterceptor(requireCoreRoleInterceptor).addPathPatterns("/**");
|
|
||||||
registry.addInterceptor(requireCorePermissionInterceptor).addPathPatterns("/**");
|
registry.addInterceptor(requireCorePermissionInterceptor).addPathPatterns("/**");
|
||||||
registry.addInterceptor(requireGaoRoleInterceptor).addPathPatterns("/**");
|
registry.addInterceptor(requireGaoRoleInterceptor).addPathPatterns("/**");
|
||||||
registry.addInterceptor(requireGaoPermissionInterceptor).addPathPatterns("/**");
|
registry.addInterceptor(requireGaoPermissionInterceptor).addPathPatterns("/**");
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package com.imyeyu.api.modules.common.controller;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonView;
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
import com.imyeyu.api.annotation.RequireCorePermission;
|
import com.imyeyu.api.annotation.RequireCorePermission;
|
||||||
import com.imyeyu.api.annotation.RequireCoreRole;
|
|
||||||
import com.imyeyu.api.bean.CorePermissionCode;
|
import com.imyeyu.api.bean.CorePermissionCode;
|
||||||
import com.imyeyu.api.bean.CoreRoleCode;
|
|
||||||
import com.imyeyu.api.modules.common.entity.Multilingual;
|
import com.imyeyu.api.modules.common.entity.Multilingual;
|
||||||
import com.imyeyu.api.modules.common.service.MultilingualService;
|
import com.imyeyu.api.modules.common.service.MultilingualService;
|
||||||
import com.imyeyu.spring.annotation.AOPLog;
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
@@ -40,7 +38,6 @@ public class MultilingualController {
|
|||||||
*/
|
*/
|
||||||
@JsonView(ResponseView.Admin.class)
|
@JsonView(ResponseView.Admin.class)
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.MULTILINGUAL_READ)
|
@RequireCorePermission(CorePermissionCode.MULTILINGUAL_READ)
|
||||||
@PostMapping("/detail")
|
@PostMapping("/detail")
|
||||||
public Multilingual detail(@RequestParam String id) {
|
public Multilingual detail(@RequestParam String id) {
|
||||||
@@ -56,7 +53,6 @@ public class MultilingualController {
|
|||||||
@AOPLog
|
@AOPLog
|
||||||
@JsonView(ResponseView.Admin.class)
|
@JsonView(ResponseView.Admin.class)
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.MULTILINGUAL_CREATE)
|
@RequireCorePermission(CorePermissionCode.MULTILINGUAL_CREATE)
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
public Multilingual create(@RequestBody Multilingual multilingual) {
|
public Multilingual create(@RequestBody Multilingual multilingual) {
|
||||||
@@ -71,7 +67,6 @@ public class MultilingualController {
|
|||||||
*/
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.MULTILINGUAL_UPDATE)
|
@RequireCorePermission(CorePermissionCode.MULTILINGUAL_UPDATE)
|
||||||
@PostMapping("/update")
|
@PostMapping("/update")
|
||||||
public void update(@RequestBody Multilingual multilingual) {
|
public void update(@RequestBody Multilingual multilingual) {
|
||||||
@@ -85,7 +80,6 @@ public class MultilingualController {
|
|||||||
*/
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.MULTILINGUAL_DELETE)
|
@RequireCorePermission(CorePermissionCode.MULTILINGUAL_DELETE)
|
||||||
@PostMapping("/delete")
|
@PostMapping("/delete")
|
||||||
public void delete(@RequestParam String id) {
|
public void delete(@RequestParam String id) {
|
||||||
|
|||||||
+3
@@ -41,6 +41,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -347,6 +348,8 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
|
|||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void updateByBizId(Attachment.BizType bizType, String bizId, List<Attachment> list) {
|
public void updateByBizId(Attachment.BizType bizType, String bizId, List<Attachment> list) {
|
||||||
|
list = TimiJava.defaultIfNull(list, new ArrayList<>());
|
||||||
|
|
||||||
// 数据库现有附件
|
// 数据库现有附件
|
||||||
List<Attachment> dbList = mapper.selectAllByBizId(bizType, bizId);
|
List<Attachment> dbList = mapper.selectAllByBizId(bizType, bizId);
|
||||||
Set<String> dbIds = dbList.stream().map(Attachment::getId).collect(Collectors.toSet());
|
Set<String> dbIds = dbList.stream().map(Attachment::getId).collect(Collectors.toSet());
|
||||||
|
|||||||
@@ -21,45 +21,61 @@ public enum GaoPermissionCode implements BuiltinPermissionCode {
|
|||||||
|
|
||||||
STORE_DELETE("STORE:DELETE", "门店删除", false, true),
|
STORE_DELETE("STORE:DELETE", "门店删除", false, true),
|
||||||
|
|
||||||
CUSTOMER_CREATE("CUSTOMER:CREATE", "客户创建"),
|
CUSTOMER_CREATE("CUSTOMER:CREATE", "客户创建", false, false),
|
||||||
|
|
||||||
CUSTOMER_READ("CUSTOMER:READ", "客户读取"),
|
CUSTOMER_READ("CUSTOMER:READ", "客户读取", false, false),
|
||||||
|
|
||||||
CUSTOMER_EXPORT("CUSTOMER:EXPORT", "客户导出", true),
|
CUSTOMER_EXPORT("CUSTOMER:EXPORT", "客户导出", true, false),
|
||||||
|
|
||||||
CUSTOMER_UPDATE("CUSTOMER:UPDATE", "客户修改"),
|
CUSTOMER_UPDATE("CUSTOMER:UPDATE", "客户修改", false, false),
|
||||||
|
|
||||||
CUSTOMER_DELETE("CUSTOMER:DELETE", "客户删除"),
|
CUSTOMER_DELETE("CUSTOMER:DELETE", "客户删除", false, false),
|
||||||
|
|
||||||
EVENT_CREATE("EVENT:CREATE", "登记事件创建"),
|
EVENT_CREATE("EVENT:CREATE", "登记事件创建", false, false),
|
||||||
|
|
||||||
EVENT_READ("EVENT:READ", "登记事件读取"),
|
EVENT_READ("EVENT:READ", "登记事件读取", false, false),
|
||||||
|
|
||||||
EVENT_UPDATE("EVENT:UPDATE", "登记事件修改"),
|
EVENT_UPDATE("EVENT:UPDATE", "登记事件修改", false, false),
|
||||||
|
|
||||||
EVENT_DELETE("EVENT:DELETE", "登记事件删除"),
|
EVENT_DELETE("EVENT:DELETE", "登记事件删除", false, false),
|
||||||
|
|
||||||
EVENT_RECORD_CREATE("EVENT_RECORD:CREATE", "登记事件记录创建"),
|
EVENT_RECORD_CREATE("EVENT_RECORD:CREATE", "登记事件记录创建", false, false),
|
||||||
|
|
||||||
EVENT_RECORD_READ("EVENT_RECORD:READ", "登记事件记录读取"),
|
EVENT_RECORD_READ("EVENT_RECORD:READ", "登记事件记录读取", false, false),
|
||||||
|
|
||||||
EVENT_RECORD_EXPORT("EVENT_RECORD:EXPORT", "登记事件记录导出", true),
|
EVENT_RECORD_EXPORT("EVENT_RECORD:EXPORT", "登记事件记录导出", true, false),
|
||||||
|
|
||||||
EVENT_RECORD_UPDATE("EVENT_RECORD:UPDATE", "登记事件记录修改"),
|
EVENT_RECORD_UPDATE("EVENT_RECORD:UPDATE", "登记事件记录修改", false, false),
|
||||||
|
|
||||||
EVENT_RECORD_DELETE("EVENT_RECORD:DELETE", "登记事件记录删除"),
|
EVENT_RECORD_DELETE("EVENT_RECORD:DELETE", "登记事件记录删除", false, false),
|
||||||
|
|
||||||
STAT_READ("STAT:READ", "统计读取"),
|
POINT_ACCOUNT_READ("POINT_ACCOUNT:READ", "客户积分账户读取", true, true),
|
||||||
|
|
||||||
QR_CODE_CREATE("QR_CODE:CREATE", "二维码创建"),
|
POINT_LEDGER_READ("POINT_LEDGER:READ", "客户积分流水读取", true, true),
|
||||||
|
|
||||||
USER_CREATE("USER:CREATE", "用户创建", true),
|
POINT_RULE_CREATE("POINT_RULE:CREATE", "客户积分规则创建", true, true),
|
||||||
|
|
||||||
USER_READ("USER:READ", "用户读取", true),
|
POINT_RULE_READ("POINT_RULE:READ", "客户积分规则读取", true, true),
|
||||||
|
|
||||||
USER_UPDATE("USER:UPDATE", "用户修改", true),
|
POINT_RULE_UPDATE("POINT_RULE:UPDATE", "客户积分规则修改", true, true),
|
||||||
|
|
||||||
USER_DELETE("USER:DELETE", "用户删除", true);
|
POINT_RULE_DELETE("POINT_RULE:DELETE", "客户积分规则删除", true, true),
|
||||||
|
|
||||||
|
POINT_ADJUST("POINT:ADJUST", "客户积分人工调整", true, true),
|
||||||
|
|
||||||
|
POINT_SOURCE_VALUE("POINT:SOURCE_VALUE", "登记提交自定义积分值", true, true),
|
||||||
|
|
||||||
|
STAT_READ("STAT:READ", "统计读取", false, false),
|
||||||
|
|
||||||
|
QR_CODE_CREATE("QR_CODE:CREATE", "二维码创建", false, false),
|
||||||
|
|
||||||
|
USER_CREATE("USER:CREATE", "用户创建", true, false),
|
||||||
|
|
||||||
|
USER_READ("USER:READ", "用户读取", true, false),
|
||||||
|
|
||||||
|
USER_UPDATE("USER:UPDATE", "用户修改", true, false),
|
||||||
|
|
||||||
|
USER_DELETE("USER:DELETE", "用户删除", true, false);
|
||||||
|
|
||||||
final String value;
|
final String value;
|
||||||
final String defaultName;
|
final String defaultName;
|
||||||
@@ -72,14 +88,6 @@ public enum GaoPermissionCode implements BuiltinPermissionCode {
|
|||||||
@Getter
|
@Getter
|
||||||
final boolean grantToGlobalManager;
|
final boolean grantToGlobalManager;
|
||||||
|
|
||||||
GaoPermissionCode(String value, String defaultName) {
|
|
||||||
this(value, defaultName, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
GaoPermissionCode(String value, String defaultName, boolean grantToStoreManager) {
|
|
||||||
this(value, defaultName, grantToStoreManager, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
GaoPermissionCode(String value, String defaultName, boolean grantToStoreManager, boolean grantToGlobalManager) {
|
GaoPermissionCode(String value, String defaultName, boolean grantToStoreManager, boolean grantToGlobalManager) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.defaultName = defaultName;
|
this.defaultName = defaultName;
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ public enum GaoRoleCode implements BuiltinRoleCode {
|
|||||||
GLOBAL_MANAGER("全局管理员", null),
|
GLOBAL_MANAGER("全局管理员", null),
|
||||||
|
|
||||||
/// 店长
|
/// 店长
|
||||||
STORE_MANAGER("店长", GLOBAL_MANAGER.name()),
|
STORE_MANAGER("店长", GLOBAL_MANAGER.name());
|
||||||
|
|
||||||
EMPLOYEE("员工", STORE_MANAGER.name());
|
|
||||||
|
|
||||||
final String defaultName;
|
final String defaultName;
|
||||||
final String parentCode;
|
final String parentCode;
|
||||||
@@ -42,4 +40,5 @@ public enum GaoRoleCode implements BuiltinRoleCode {
|
|||||||
public String getParentCode() {
|
public String getParentCode() {
|
||||||
return parentCode;
|
return parentCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoCustomerChartService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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 java.util.List;
|
||||||
|
|
||||||
|
/// GAO 客户报表接口
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/gao/customer/chart")
|
||||||
|
public class GaoCustomerChartController {
|
||||||
|
|
||||||
|
private final GaoCustomerChartService service;
|
||||||
|
|
||||||
|
/// 查询客户登记每日统计,可同时支撑登记趋势和登记事件结构图表
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/event/record/daily")
|
||||||
|
public List<GaoEventRecordDailyStatView> eventRecordDaily(@RequestBody GaoCustomerChartReq req) {
|
||||||
|
return service.eventRecordDailyStat(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询客户登记日历记录
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/event/record/calendar")
|
||||||
|
public List<GaoEventRecordCalendarView> eventRecordCalendar(@RequestBody GaoCustomerChartReq req) {
|
||||||
|
return service.eventRecordCalendar(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,9 @@ package com.imyeyu.api.modules.gao.controller;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonView;
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
import com.imyeyu.api.bean.ModuleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.api.bean.RoleMatchMode;
|
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoRole;
|
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||||
@@ -46,7 +44,6 @@ import java.util.Map;
|
|||||||
/// @author 夜雨
|
/// @author 夜雨
|
||||||
/// @since 2026-07-27 14:12
|
/// @since 2026-07-27 14:12
|
||||||
@RestController
|
@RestController
|
||||||
@RequireGaoRole(value = {GaoRoleCode.GLOBAL_MANAGER, GaoRoleCode.STORE_MANAGER, GaoRoleCode.EMPLOYEE}, mode = RoleMatchMode.ANY)
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RequestMapping("/gao/customer")
|
@RequestMapping("/gao/customer")
|
||||||
public class GaoCustomerController {
|
public class GaoCustomerController {
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.gao.controller;
|
package com.imyeyu.api.modules.gao.controller;
|
||||||
|
|
||||||
import com.imyeyu.api.bean.ModuleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.api.bean.RoleMatchMode;
|
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoRole;
|
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
||||||
@@ -30,7 +28,6 @@ import java.util.Map;
|
|||||||
/// @author 夜雨
|
/// @author 夜雨
|
||||||
/// @since 2026-07-27 14:13
|
/// @since 2026-07-27 14:13
|
||||||
@RestController
|
@RestController
|
||||||
@RequireGaoRole(value = {GaoRoleCode.GLOBAL_MANAGER, GaoRoleCode.STORE_MANAGER, GaoRoleCode.EMPLOYEE}, mode = RoleMatchMode.ANY)
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RequestMapping("/gao/event")
|
@RequestMapping("/gao/event")
|
||||||
public class GaoEventController {
|
public class GaoEventController {
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ package com.imyeyu.api.modules.gao.controller;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonView;
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
import com.imyeyu.api.bean.ModuleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.api.bean.RoleMatchMode;
|
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoRole;
|
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||||
@@ -48,7 +46,6 @@ import java.util.Map;
|
|||||||
/// @author 夜雨
|
/// @author 夜雨
|
||||||
/// @since 2026-07-27 14:11
|
/// @since 2026-07-27 14:11
|
||||||
@RestController
|
@RestController
|
||||||
@RequireGaoRole(value = {GaoRoleCode.GLOBAL_MANAGER, GaoRoleCode.STORE_MANAGER, GaoRoleCode.EMPLOYEE}, mode = RoleMatchMode.ANY)
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RequestMapping("/gao/event/record")
|
@RequestMapping("/gao/event/record")
|
||||||
public class GaoEventRecordController {
|
public class GaoEventRecordController {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointAccount;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointAccountService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||||
|
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||||
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/// GAO 客户积分账户接口
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/gao/customer/point")
|
||||||
|
public class GaoPointAccountController {
|
||||||
|
|
||||||
|
private final RoleChecker roleChecker;
|
||||||
|
private final GaoStoreService storeService;
|
||||||
|
private final GaoPointAccountService accountService;
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.POINT_ACCOUNT_READ)
|
||||||
|
@PostMapping("/account/detail")
|
||||||
|
public GaoPointAccount detail(@RequestParam String customerId, @RequestParam(required = false) String storeId) {
|
||||||
|
storeId = resolveStoreId(storeId);
|
||||||
|
return accountService.getByCustomerId(storeId, customerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveStoreId(String requestedStoreId) {
|
||||||
|
if (roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||||
|
return requestedStoreId;
|
||||||
|
}
|
||||||
|
return storeService.getBelongIdByRequiredLoginUserId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointLedger;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointLedgerService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointTriggerService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoPointAdjustRequest;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoPointLedgerPage;
|
||||||
|
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// GAO 客户积分流水接口
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/gao/customer/point")
|
||||||
|
public class GaoPointLedgerController {
|
||||||
|
|
||||||
|
private final RoleChecker roleChecker;
|
||||||
|
private final GaoStoreService storeService;
|
||||||
|
private final GaoPointLedgerService ledgerService;
|
||||||
|
private final GaoPointTriggerService triggerService;
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.POINT_LEDGER_READ)
|
||||||
|
@PostMapping("/ledger/list")
|
||||||
|
public PageResult<GaoPointLedger> list(@RequestBody GaoPointLedgerPage page) {
|
||||||
|
TimiException.required(page, "not found page");
|
||||||
|
page.setStoreId(resolveStoreId(page.getStoreId()));
|
||||||
|
return ledgerService.pageByQuery(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.POINT_ADJUST)
|
||||||
|
@PostMapping("/adjust")
|
||||||
|
public void adjust(@RequestBody GaoPointAdjustRequest request) {
|
||||||
|
request.setStoreId(resolveStoreId(request.getStoreId()));
|
||||||
|
triggerService.adjust(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveStoreId(String requestedStoreId) {
|
||||||
|
if (roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
|
||||||
|
return requestedStoreId;
|
||||||
|
}
|
||||||
|
return storeService.getBelongIdByRequiredLoginUserId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointRule;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointRuleService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||||
|
import com.imyeyu.api.modules.user.service.RoleChecker;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
|
import com.imyeyu.spring.bean.Page;
|
||||||
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/// GAO 客户积分规则接口
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/gao/customer/point")
|
||||||
|
public class GaoPointRuleController {
|
||||||
|
|
||||||
|
private final RoleChecker roleChecker;
|
||||||
|
private final GaoStoreService storeService;
|
||||||
|
private final GaoPointRuleService ruleService;
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.POINT_RULE_READ)
|
||||||
|
@PostMapping("/rule/list")
|
||||||
|
public PageResult<GaoPointRule> list(@RequestBody Page<GaoPointRule> page) {
|
||||||
|
TimiException.required(page, "not found page");
|
||||||
|
if (!isGlobalManager()) {
|
||||||
|
GaoPointRule example = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoPointRule());
|
||||||
|
example.setStoreId(resolveStoreId(null));
|
||||||
|
page.setEqualsExample(example);
|
||||||
|
}
|
||||||
|
return ruleService.page(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.POINT_RULE_READ)
|
||||||
|
@PostMapping("/rule/detail")
|
||||||
|
public GaoPointRule detail(@RequestParam String id) {
|
||||||
|
GaoPointRule rule = ruleService.get(id);
|
||||||
|
TimiException.required(rule, "not found point rule");
|
||||||
|
checkStore(rule.getStoreId());
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.POINT_RULE_CREATE)
|
||||||
|
@PostMapping("/rule/create")
|
||||||
|
public String create(@RequestBody GaoPointRule rule) {
|
||||||
|
rule.setStoreId(resolveStoreId(rule.getStoreId()));
|
||||||
|
ruleService.create(rule);
|
||||||
|
return rule.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.POINT_RULE_UPDATE)
|
||||||
|
@PostMapping("/rule/update")
|
||||||
|
public void update(@RequestBody GaoPointRule rule) {
|
||||||
|
rule.setStoreId(resolveStoreId(rule.getStoreId()));
|
||||||
|
ruleService.update(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.POINT_RULE_DELETE)
|
||||||
|
@PostMapping("/rule/delete")
|
||||||
|
public void delete(@RequestParam String id) {
|
||||||
|
GaoPointRule rule = ruleService.get(id);
|
||||||
|
TimiException.required(rule, "not found point rule");
|
||||||
|
checkStore(rule.getStoreId());
|
||||||
|
ruleService.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveStoreId(String requestedStoreId) {
|
||||||
|
if (isGlobalManager()) {
|
||||||
|
return requestedStoreId;
|
||||||
|
}
|
||||||
|
return storeService.getBelongIdByRequiredLoginUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkStore(String storeId) {
|
||||||
|
if (!isGlobalManager()) {
|
||||||
|
TimiException.requiredTrue(resolveStoreId(null).equals(storeId), "无权操作其他门店数据");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isGlobalManager() {
|
||||||
|
return roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@ import com.imyeyu.api.bean.ModuleCode;
|
|||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoRole;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoRole;
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||||
import com.imyeyu.api.modules.user.entity.Role;
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
import com.imyeyu.api.modules.user.service.RoleService;
|
import com.imyeyu.api.modules.user.service.AuthorizationScopeService;
|
||||||
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.spring.annotation.AOPLog;
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -24,12 +25,13 @@ import java.util.List;
|
|||||||
@RequestMapping("/gao/role")
|
@RequestMapping("/gao/role")
|
||||||
public class GaoRoleController {
|
public class GaoRoleController {
|
||||||
|
|
||||||
private final RoleService roleService;
|
private final UserLoginService userLoginService;
|
||||||
|
private final AuthorizationScopeService authorizationScopeService;
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@PostMapping("/available/list")
|
@PostMapping("/available/list")
|
||||||
public List<Role> availableList() {
|
public List<Role> availableList() {
|
||||||
return roleService.listByModuleCode(ModuleCode.GAO);
|
return authorizationScopeService.listGrantableRole(userLoginService.getRequireLoginUserId(), ModuleCode.GAO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -26,7 +26,6 @@ import java.util.List;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RequestMapping("/gao/store/business/day")
|
@RequestMapping("/gao/store/business/day")
|
||||||
@RequireGaoRole(GaoRoleCode.GLOBAL_MANAGER)
|
|
||||||
public class GaoStoreBusinessDayController {
|
public class GaoStoreBusinessDayController {
|
||||||
|
|
||||||
private final GaoStoreBusinessDayService service;
|
private final GaoStoreBusinessDayService service;
|
||||||
@@ -57,7 +56,7 @@ public class GaoStoreBusinessDayController {
|
|||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireGaoPermission(GaoPermissionCode.STORE_CREATE)
|
@RequireGaoPermission(GaoPermissionCode.STORE_UPDATE)
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
public String create(@RequestBody GaoStoreBusinessDay businessDay) {
|
public String create(@RequestBody GaoStoreBusinessDay businessDay) {
|
||||||
service.create(businessDay);
|
service.create(businessDay);
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoStoreChartService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq;
|
||||||
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 门店报表接口
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/gao/store/chart")
|
||||||
|
public class GaoStoreChartController {
|
||||||
|
|
||||||
|
private final GaoStoreChartService service;
|
||||||
|
|
||||||
|
/// 查询客户增长趋势
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/customer/daily")
|
||||||
|
public List<GaoCustomerDailyStateView> customerDaily(@RequestBody GaoStoreChartReq req) {
|
||||||
|
return service.customerDailyTrend(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询客户性别统计
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/customer/gender")
|
||||||
|
public List<GaoCustomerGenderStatView> customerGender(@RequestParam String storeId) {
|
||||||
|
return service.customerGender(storeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询登记事件每日统计,可同时支撑登记趋势和事件结构图表
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/event/record/daily")
|
||||||
|
public List<GaoEventRecordDailyStatView> eventRecordDaily(@RequestBody GaoStoreChartReq req) {
|
||||||
|
return service.eventRecordDailyStat(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询登记事件日历记录
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/event/record/calendar")
|
||||||
|
public List<GaoEventRecordCalendarView> eventRecordCalendar(@RequestBody GaoStoreChartReq req) {
|
||||||
|
return service.eventRecordCalendar(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.imyeyu.api.modules.gao.controller;
|
package com.imyeyu.api.modules.gao.controller;
|
||||||
|
|
||||||
import com.imyeyu.api.bean.RoleMatchMode;
|
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
import com.imyeyu.api.modules.gao.annotation.RequireGaoRole;
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoRole;
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
@@ -34,13 +33,13 @@ import java.util.List;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RequestMapping("/gao/store")
|
@RequestMapping("/gao/store")
|
||||||
@RequireGaoRole(GaoRoleCode.GLOBAL_MANAGER)
|
|
||||||
public class GaoStoreController {
|
public class GaoStoreController {
|
||||||
|
|
||||||
private final GaoStoreService service;
|
private final GaoStoreService service;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
|
@RequireGaoRole(GaoRoleCode.GLOBAL_MANAGER)
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireGaoPermission(GaoPermissionCode.STORE_READ)
|
@RequireGaoPermission(GaoPermissionCode.STORE_READ)
|
||||||
@PostMapping("/list")
|
@PostMapping("/list")
|
||||||
@@ -49,6 +48,7 @@ public class GaoStoreController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
|
@RequireGaoRole(GaoRoleCode.STORE_MANAGER)
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireGaoPermission(GaoPermissionCode.STORE_READ)
|
@RequireGaoPermission(GaoPermissionCode.STORE_READ)
|
||||||
@PostMapping("/detail")
|
@PostMapping("/detail")
|
||||||
@@ -66,6 +66,7 @@ public class GaoStoreController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
|
@RequireGaoRole(GaoRoleCode.STORE_MANAGER)
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireGaoPermission(GaoPermissionCode.STORE_UPDATE)
|
@RequireGaoPermission(GaoPermissionCode.STORE_UPDATE)
|
||||||
@PostMapping("/update")
|
@PostMapping("/update")
|
||||||
@@ -74,6 +75,7 @@ public class GaoStoreController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
|
@RequireGaoRole(GaoRoleCode.GLOBAL_MANAGER)
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireGaoPermission(GaoPermissionCode.STORE_DELETE)
|
@RequireGaoPermission(GaoPermissionCode.STORE_DELETE)
|
||||||
@PostMapping("/delete")
|
@PostMapping("/delete")
|
||||||
@@ -82,6 +84,7 @@ public class GaoStoreController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
|
@RequireGaoRole(GaoRoleCode.GLOBAL_MANAGER)
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireGaoPermission(GaoPermissionCode.STORE_UPDATE)
|
@RequireGaoPermission(GaoPermissionCode.STORE_UPDATE)
|
||||||
@PostMapping("/sort")
|
@PostMapping("/sort")
|
||||||
@@ -104,7 +107,6 @@ public class GaoStoreController {
|
|||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireGaoRole(value = {GaoRoleCode.GLOBAL_MANAGER, GaoRoleCode.STORE_MANAGER, GaoRoleCode.EMPLOYEE}, mode = RoleMatchMode.ANY)
|
|
||||||
@PostMapping("/user")
|
@PostMapping("/user")
|
||||||
public GaoStore getLoginStore() {
|
public GaoStore getLoginStore() {
|
||||||
return service.getByUserId(userLoginService.getRequireLoginUserId());
|
return service.getByUserId(userLoginService.getRequireLoginUserId());
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import java.util.Map;
|
|||||||
/// @author 夜雨
|
/// @author 夜雨
|
||||||
/// @since 2026-07-30 14:15
|
/// @since 2026-07-30 14:15
|
||||||
@RestController
|
@RestController
|
||||||
@RequireGaoRole(GaoRoleCode.STORE_MANAGER)
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RequestMapping("/gao/user")
|
@RequestMapping("/gao/user")
|
||||||
public class GaoUserController {
|
public class GaoUserController {
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ public class GaoEventRecord extends UUIDEntity {
|
|||||||
/// 营业日递增序号
|
/// 营业日递增序号
|
||||||
private Long businessDayNo;
|
private Long businessDayNo;
|
||||||
|
|
||||||
|
/// 本次登记提交的积分值,只参与积分触发,不落库
|
||||||
|
@Transient
|
||||||
|
private Long pointValue;
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private GaoCustomer customer;
|
private GaoCustomer customer;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/// GAO 客户积分账户
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class GaoPointAccount extends UUIDEntity {
|
||||||
|
|
||||||
|
/// 门店 ID
|
||||||
|
private String storeId;
|
||||||
|
|
||||||
|
/// 客户 ID
|
||||||
|
private String customerId;
|
||||||
|
|
||||||
|
/// 当前可用积分余额
|
||||||
|
private Long balance;
|
||||||
|
|
||||||
|
/// 累计获得积分
|
||||||
|
private Long totalEarned;
|
||||||
|
|
||||||
|
/// 累计撤销积分
|
||||||
|
private Long totalRevoked;
|
||||||
|
|
||||||
|
/// 累计人工调整积分,可正可负
|
||||||
|
private Long totalAdjusted;
|
||||||
|
|
||||||
|
/// 乐观锁版本
|
||||||
|
private Long version;
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/// GAO 客户积分流水
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class GaoPointLedger extends UUIDEntity {
|
||||||
|
|
||||||
|
/// 流水变化类型
|
||||||
|
public enum ChangeType {
|
||||||
|
|
||||||
|
/// 登记事件获得积分
|
||||||
|
EARN,
|
||||||
|
|
||||||
|
/// 删除登记记录撤销积分
|
||||||
|
REVOKE,
|
||||||
|
|
||||||
|
/// 人工调整积分
|
||||||
|
ADJUST
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 流水方向
|
||||||
|
public enum Direction {
|
||||||
|
|
||||||
|
/// 增加积分
|
||||||
|
INCREASE,
|
||||||
|
|
||||||
|
/// 减少积分
|
||||||
|
DECREASE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 客户积分账户 ID
|
||||||
|
private String accountId;
|
||||||
|
|
||||||
|
/// 门店 ID
|
||||||
|
private String storeId;
|
||||||
|
|
||||||
|
/// 客户 ID
|
||||||
|
private String customerId;
|
||||||
|
|
||||||
|
/// 变化类型
|
||||||
|
private ChangeType changeType;
|
||||||
|
|
||||||
|
/// 方向
|
||||||
|
private Direction direction;
|
||||||
|
|
||||||
|
/// 本次变化积分,增加为正,减少为负
|
||||||
|
private Long pointDelta;
|
||||||
|
|
||||||
|
/// 变化前余额
|
||||||
|
private Long balanceBefore;
|
||||||
|
|
||||||
|
/// 变化后余额
|
||||||
|
private Long balanceAfter;
|
||||||
|
|
||||||
|
/// 命中的积分规则 ID
|
||||||
|
private String ruleId;
|
||||||
|
|
||||||
|
/// 登记事件 ID
|
||||||
|
private String eventId;
|
||||||
|
|
||||||
|
/// 登记记录 ID
|
||||||
|
private String eventRecordId;
|
||||||
|
|
||||||
|
/// 来源快照
|
||||||
|
private JsonNode sourcePayload;
|
||||||
|
|
||||||
|
/// 幂等键
|
||||||
|
private String idempotencyKey;
|
||||||
|
|
||||||
|
/// 备注
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/// 操作用户 ID
|
||||||
|
private String operatorUserId;
|
||||||
|
|
||||||
|
/// 发生时间
|
||||||
|
private Long occurredAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/// GAO 客户积分规则
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class GaoPointRule extends UUIDEntity {
|
||||||
|
|
||||||
|
/// 规则状态
|
||||||
|
public enum Status {
|
||||||
|
|
||||||
|
/// 草稿,不参与触发
|
||||||
|
DRAFT,
|
||||||
|
|
||||||
|
/// 启用,参与触发
|
||||||
|
ACTIVE,
|
||||||
|
|
||||||
|
/// 停用,不参与触发
|
||||||
|
INACTIVE,
|
||||||
|
|
||||||
|
/// 已删除,不参与触发
|
||||||
|
DELETED
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 触发类型
|
||||||
|
public enum TriggerType {
|
||||||
|
|
||||||
|
/// 每登记一次触发一次
|
||||||
|
EVERY_TIME,
|
||||||
|
|
||||||
|
/// 客户首次登记该事件触发一次
|
||||||
|
FIRST_TIME,
|
||||||
|
|
||||||
|
/// 客户累计登记达到 N 次后每次触发
|
||||||
|
AFTER_COUNT_EVERY_TIME,
|
||||||
|
|
||||||
|
/// 使用登记接口提交的积分值触发
|
||||||
|
SOURCE_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 积分值模式
|
||||||
|
public enum PointMode {
|
||||||
|
|
||||||
|
/// 使用规则固定积分值
|
||||||
|
FIXED,
|
||||||
|
|
||||||
|
/// 使用登记接口提交的积分值
|
||||||
|
SOURCE_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 门店 ID
|
||||||
|
private String storeId;
|
||||||
|
|
||||||
|
/// 登记事件 ID
|
||||||
|
private String eventId;
|
||||||
|
|
||||||
|
/// 规则名称
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/// 规则状态
|
||||||
|
private Status status;
|
||||||
|
|
||||||
|
/// 触发类型
|
||||||
|
private TriggerType triggerType;
|
||||||
|
|
||||||
|
/// 积分值模式
|
||||||
|
private PointMode pointMode;
|
||||||
|
|
||||||
|
/// 固定积分值
|
||||||
|
private Long pointValue;
|
||||||
|
|
||||||
|
/// 条件配置
|
||||||
|
private JsonNode conditionJson;
|
||||||
|
|
||||||
|
/// 生效开始时间戳,毫秒,空为不限制
|
||||||
|
private Long beginAt;
|
||||||
|
|
||||||
|
/// 生效结束时间戳,毫秒,空为不限制
|
||||||
|
private Long endAt;
|
||||||
|
|
||||||
|
/// 优先级,数值小优先
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
|
/// true 为命中后停止继续匹配后续规则
|
||||||
|
private Boolean exclusive;
|
||||||
|
|
||||||
|
/// 备注
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
package com.imyeyu.api.modules.gao.mapper;
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage;
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
@@ -49,4 +53,28 @@ public interface GaoEventRecordMapper extends BaseMapper<GaoEventRecord, String>
|
|||||||
/// @param page 排行榜分页请求
|
/// @param page 排行榜分页请求
|
||||||
/// @return 客户登记排行榜
|
/// @return 客户登记排行榜
|
||||||
List<GaoCustomerEventRankView> selectRankByRange(GaoEventRecordRankPage page);
|
List<GaoCustomerEventRankView> selectRankByRange(GaoEventRecordRankPage page);
|
||||||
|
|
||||||
|
/// 按时间范围和事件批量统计每日登记数
|
||||||
|
///
|
||||||
|
/// @param req 门店报表查询请求
|
||||||
|
/// @return 每日登记数
|
||||||
|
List<GaoEventRecordDailyStatView> stateDailyByRange(GaoStoreChartReq req);
|
||||||
|
|
||||||
|
/// 按时间范围查询登记日历记录
|
||||||
|
///
|
||||||
|
/// @param req 门店报表查询请求
|
||||||
|
/// @return 日历记录列表
|
||||||
|
List<GaoEventRecordCalendarView> selectCalendarByRange(GaoStoreChartReq req);
|
||||||
|
|
||||||
|
/// 按客户、时间范围和事件批量统计每日登记数
|
||||||
|
///
|
||||||
|
/// @param req 客户报表查询请求
|
||||||
|
/// @return 每日登记数
|
||||||
|
List<GaoEventRecordDailyStatView> stateCustomerDailyByRange(GaoCustomerChartReq req);
|
||||||
|
|
||||||
|
/// 按客户和时间范围查询登记日历记录
|
||||||
|
///
|
||||||
|
/// @param req 客户报表查询请求
|
||||||
|
/// @return 日历记录列表
|
||||||
|
List<GaoEventRecordCalendarView> selectCustomerCalendarByRange(GaoCustomerChartReq req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointAccount;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/// GAO 客户积分账户 Mapper
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
public interface GaoPointAccountMapper extends BaseMapper<GaoPointAccount, String> {
|
||||||
|
|
||||||
|
/// 查询客户当前有效积分账户
|
||||||
|
///
|
||||||
|
/// @param storeId 门店 ID
|
||||||
|
/// @param customerId 客户 ID
|
||||||
|
/// @return 积分账户
|
||||||
|
GaoPointAccount selectByStoreIdAndCustomerId(@Param("storeId") String storeId, @Param("customerId") String customerId);
|
||||||
|
|
||||||
|
/// 创建客户积分账户,已存在时保持原账户不变
|
||||||
|
///
|
||||||
|
/// @param account 积分账户
|
||||||
|
void insertIfAbsent(@Param("account") GaoPointAccount account);
|
||||||
|
|
||||||
|
/// 锁定积分账户
|
||||||
|
///
|
||||||
|
/// @param id 账户 ID
|
||||||
|
/// @return 积分账户
|
||||||
|
GaoPointAccount selectForUpdate(@Param("id") String id);
|
||||||
|
|
||||||
|
/// 原子更新积分账户余额和统计值
|
||||||
|
///
|
||||||
|
/// @param id 账户 ID
|
||||||
|
/// @param delta 本次余额变化
|
||||||
|
/// @param totalEarnedDelta 累计获得积分变化
|
||||||
|
/// @param totalRevokedDelta 累计撤销积分变化
|
||||||
|
/// @param totalAdjustedDelta 累计人工调整积分变化
|
||||||
|
/// @param updatedAt 更新时间
|
||||||
|
/// @return 更新行数
|
||||||
|
int updateBalance(@Param("id") String id, @Param("delta") long delta, @Param("totalEarnedDelta") long totalEarnedDelta, @Param("totalRevokedDelta") long totalRevokedDelta, @Param("totalAdjustedDelta") long totalAdjustedDelta, @Param("updatedAt") long updatedAt);
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointLedger;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoPointLedgerPage;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 客户积分流水 Mapper
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
public interface GaoPointLedgerMapper extends BaseMapper<GaoPointLedger, String> {
|
||||||
|
|
||||||
|
/// 按幂等键查询流水
|
||||||
|
///
|
||||||
|
/// @param idempotencyKey 幂等键
|
||||||
|
/// @return 流水
|
||||||
|
GaoPointLedger selectByIdempotencyKey(@Param("idempotencyKey") String idempotencyKey);
|
||||||
|
|
||||||
|
/// 查询登记记录产生的原始获得积分流水
|
||||||
|
///
|
||||||
|
/// @param eventRecordId 登记记录 ID
|
||||||
|
/// @return 获得积分流水
|
||||||
|
List<GaoPointLedger> selectEarnByEventRecordId(@Param("eventRecordId") String eventRecordId);
|
||||||
|
|
||||||
|
/// 批量查询幂等键对应的流水
|
||||||
|
///
|
||||||
|
/// @param idempotencyKeyList 幂等键列表
|
||||||
|
/// @return 已存在流水
|
||||||
|
List<GaoPointLedger> selectByIdempotencyKeyList(@Param("idempotencyKeyList") List<String> idempotencyKeyList);
|
||||||
|
|
||||||
|
/// 查询积分流水总数
|
||||||
|
///
|
||||||
|
/// @param page 查询条件
|
||||||
|
/// @return 流水总数
|
||||||
|
long countByQuery(GaoPointLedgerPage page);
|
||||||
|
|
||||||
|
/// 分页查询积分流水
|
||||||
|
///
|
||||||
|
/// @param page 查询条件
|
||||||
|
/// @return 流水列表
|
||||||
|
List<GaoPointLedger> selectByQuery(GaoPointLedgerPage page);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointRule;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 客户积分规则 Mapper
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
public interface GaoPointRuleMapper extends BaseMapper<GaoPointRule, String> {
|
||||||
|
|
||||||
|
/// 查询当前时间可匹配的启用规则
|
||||||
|
///
|
||||||
|
/// @param storeId 门店 ID
|
||||||
|
/// @param eventId 登记事件 ID
|
||||||
|
/// @param now 当前时间戳
|
||||||
|
/// @return 按优先级排序的规则列表
|
||||||
|
List<GaoPointRule> selectActiveByStoreIdAndEventId(@Param("storeId") String storeId, @Param("eventId") String eventId, @Param("now") long now);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 客户报表服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
public interface GaoCustomerChartService {
|
||||||
|
|
||||||
|
/// 查询客户登记每日统计
|
||||||
|
List<GaoEventRecordDailyStatView> eventRecordDailyStat(GaoCustomerChartReq req);
|
||||||
|
|
||||||
|
/// 查询客户登记日历记录
|
||||||
|
List<GaoEventRecordCalendarView> eventRecordCalendar(GaoCustomerChartReq req);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointAccount;
|
||||||
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
|
/// GAO 客户积分账户服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
public interface GaoPointAccountService extends BaseService<GaoPointAccount, String> {
|
||||||
|
|
||||||
|
/// 查询客户当前有效积分账户
|
||||||
|
///
|
||||||
|
/// @param storeId 门店 ID
|
||||||
|
/// @param customerId 客户 ID
|
||||||
|
/// @return 积分账户,不存在时返回 null
|
||||||
|
GaoPointAccount getByCustomerId(String storeId, String customerId);
|
||||||
|
|
||||||
|
/// 获取并锁定客户积分账户,不存在时创建
|
||||||
|
///
|
||||||
|
/// @param storeId 门店 ID
|
||||||
|
/// @param customerId 客户 ID
|
||||||
|
/// @return 已锁定的积分账户
|
||||||
|
GaoPointAccount getOrCreateForUpdate(String storeId, String customerId);
|
||||||
|
|
||||||
|
/// 原子更新账户余额
|
||||||
|
///
|
||||||
|
/// @param accountId 账户 ID
|
||||||
|
/// @param delta 本次余额变化
|
||||||
|
/// @param totalEarnedDelta 累计获得积分变化
|
||||||
|
/// @param totalRevokedDelta 累计撤销积分变化
|
||||||
|
/// @param totalAdjustedDelta 累计人工调整积分变化
|
||||||
|
/// @return 更新行数
|
||||||
|
int updateBalance(String accountId, long delta, long totalEarnedDelta, long totalRevokedDelta, long totalAdjustedDelta);
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointLedger;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointRule;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoPointAdjustRequest;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoPointLedgerPage;
|
||||||
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
|
/// GAO 客户积分流水服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
public interface GaoPointLedgerService extends BaseService<GaoPointLedger, String> {
|
||||||
|
|
||||||
|
/// 写入登记获得积分流水
|
||||||
|
///
|
||||||
|
/// @param record 登记记录
|
||||||
|
/// @param rule 命中规则
|
||||||
|
/// @param pointDelta 积分变化
|
||||||
|
/// @param sourcePayload 来源快照
|
||||||
|
/// @return 积分流水
|
||||||
|
GaoPointLedger earn(GaoEventRecord record, GaoPointRule rule, long pointDelta, JsonNode sourcePayload);
|
||||||
|
|
||||||
|
/// 写入删除登记记录的撤销流水
|
||||||
|
///
|
||||||
|
/// @param originLedger 原始获得积分流水
|
||||||
|
/// @param record 登记记录
|
||||||
|
/// @return 撤销流水
|
||||||
|
GaoPointLedger revoke(GaoPointLedger originLedger, GaoEventRecord record);
|
||||||
|
|
||||||
|
/// 写入人工调整流水
|
||||||
|
///
|
||||||
|
/// @param request 人工调整请求
|
||||||
|
/// @param operatorUserId 操作用户 ID
|
||||||
|
/// @return 调整流水
|
||||||
|
GaoPointLedger adjust(GaoPointAdjustRequest request, String operatorUserId);
|
||||||
|
|
||||||
|
/// 分页查询积分流水
|
||||||
|
///
|
||||||
|
/// @param page 查询条件
|
||||||
|
/// @return 分页结果
|
||||||
|
PageResult<GaoPointLedger> pageByQuery(GaoPointLedgerPage page);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointRule;
|
||||||
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 客户积分规则服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
public interface GaoPointRuleService extends BaseService<GaoPointRule, String> {
|
||||||
|
|
||||||
|
/// 查询当前时间可匹配的启用规则
|
||||||
|
///
|
||||||
|
/// @param storeId 门店 ID
|
||||||
|
/// @param eventId 登记事件 ID
|
||||||
|
/// @param now 当前时间戳
|
||||||
|
/// @return 规则列表
|
||||||
|
List<GaoPointRule> listActive(String storeId, String eventId, long now);
|
||||||
|
|
||||||
|
/// 创建积分规则
|
||||||
|
///
|
||||||
|
/// @param rule 积分规则
|
||||||
|
void create(GaoPointRule rule);
|
||||||
|
|
||||||
|
/// 修改积分规则
|
||||||
|
///
|
||||||
|
/// @param rule 积分规则
|
||||||
|
void update(GaoPointRule rule);
|
||||||
|
|
||||||
|
/// 删除积分规则
|
||||||
|
///
|
||||||
|
/// @param id 规则 ID
|
||||||
|
void delete(String id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointLedger;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoPointAdjustRequest;
|
||||||
|
|
||||||
|
/// GAO 客户积分触发服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
public interface GaoPointTriggerService {
|
||||||
|
|
||||||
|
/// 在登记记录创建成功后触发积分
|
||||||
|
///
|
||||||
|
/// @param record 登记记录
|
||||||
|
void onEventRecordCreated(GaoEventRecord record);
|
||||||
|
|
||||||
|
/// 撤销登记记录产生的全部获得积分
|
||||||
|
///
|
||||||
|
/// @param record 登记记录
|
||||||
|
void revokeByEventRecord(GaoEventRecord record);
|
||||||
|
|
||||||
|
/// 执行人工调整
|
||||||
|
///
|
||||||
|
/// @param request 人工调整请求
|
||||||
|
/// @return 调整流水
|
||||||
|
GaoPointLedger adjust(GaoPointAdjustRequest request);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 门店报表服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
public interface GaoStoreChartService {
|
||||||
|
|
||||||
|
/// 查询客户每日趋势
|
||||||
|
List<GaoCustomerDailyStateView> customerDailyTrend(GaoStoreChartReq req);
|
||||||
|
|
||||||
|
/// 查询客户性别统计
|
||||||
|
List<GaoCustomerGenderStatView> customerGender(String storeId);
|
||||||
|
|
||||||
|
/// 查询登记事件每日统计
|
||||||
|
List<GaoEventRecordDailyStatView> eventRecordDailyStat(GaoStoreChartReq req);
|
||||||
|
|
||||||
|
/// 查询登记事件日历记录
|
||||||
|
List<GaoEventRecordCalendarView> eventRecordCalendar(GaoStoreChartReq req);
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoEventRecordMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoCustomerChartService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 客户报表服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GaoCustomerChartServiceImplement implements GaoCustomerChartService {
|
||||||
|
|
||||||
|
private final GaoEventRecordMapper eventRecordMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoEventRecordDailyStatView> eventRecordDailyStat(GaoCustomerChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
return eventRecordMapper.stateCustomerDailyByRange(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoEventRecordCalendarView> eventRecordCalendar(GaoCustomerChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
return eventRecordMapper.selectCustomerCalendarByRange(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkRangeReq(GaoCustomerChartReq req) {
|
||||||
|
TimiException.required(req, "not found req");
|
||||||
|
TimiException.required(req.getStoreId(), "not found req.storeId");
|
||||||
|
TimiException.required(req.getCustomerId(), "not found req.customerId");
|
||||||
|
TimiException.required(req.getBeginAt(), "not found req.beginAt");
|
||||||
|
TimiException.required(req.getEndAt(), "not found req.endAt");
|
||||||
|
TimiException.requiredTrue(req.getBeginAt() <= req.getEndAt(), "req.endAt lt req.beginAt");
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -65,6 +65,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
|||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void create(GaoCustomer customer) {
|
public void create(GaoCustomer customer) {
|
||||||
TimiException.required(customer.getName(), "not found customer.name");
|
TimiException.required(customer.getName(), "not found customer.name");
|
||||||
@@ -118,6 +119,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void update(GaoCustomer customer) {
|
public void update(GaoCustomer customer) {
|
||||||
TimiException.required(customer, "not found customer");
|
TimiException.required(customer, "not found customer");
|
||||||
@@ -196,6 +198,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void updateWithAttachment(GaoCustomer customer) {
|
public void updateWithAttachment(GaoCustomer customer) {
|
||||||
update(customer);
|
update(customer);
|
||||||
|
|||||||
+39
@@ -10,6 +10,7 @@ import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
|||||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||||
import com.imyeyu.api.modules.gao.mapper.GaoEventRecordMapper;
|
import com.imyeyu.api.modules.gao.mapper.GaoEventRecordMapper;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointTriggerService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
import com.imyeyu.api.modules.gao.service.GaoEventRecordService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoEventService;
|
import com.imyeyu.api.modules.gao.service.GaoEventService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreBusinessDayService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreBusinessDayService;
|
||||||
@@ -63,6 +64,7 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
|||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
private final GaoCustomerService customerService;
|
private final GaoCustomerService customerService;
|
||||||
private final GaoStoreBusinessDayService businessDayService;
|
private final GaoStoreBusinessDayService businessDayService;
|
||||||
|
private final GaoPointTriggerService pointTriggerService;
|
||||||
|
|
||||||
private final GaoEventRecordMapper mapper;
|
private final GaoEventRecordMapper mapper;
|
||||||
|
|
||||||
@@ -116,6 +118,7 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
|||||||
dbRecord.setRegisteredAt(registeredAt);
|
dbRecord.setRegisteredAt(registeredAt);
|
||||||
dbRecord.setRegisteredDateValue(registeredDateValue);
|
dbRecord.setRegisteredDateValue(registeredDateValue);
|
||||||
dbRecord.setBusinessDayNo(businessDayNo);
|
dbRecord.setBusinessDayNo(businessDayNo);
|
||||||
|
dbRecord.setPointValue(record.getPointValue());
|
||||||
super.create(dbRecord);
|
super.create(dbRecord);
|
||||||
// 附件
|
// 附件
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
@@ -131,6 +134,7 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
|||||||
attachmentService.update(clone);
|
attachmentService.update(clone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pointTriggerService.onEventRecordCreated(dbRecord);
|
||||||
logIdList.add(dbRecord.getId());
|
logIdList.add(dbRecord.getId());
|
||||||
}
|
}
|
||||||
logger.setLevel(Logger.Level.INFO);
|
logger.setLevel(Logger.Level.INFO);
|
||||||
@@ -149,6 +153,14 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void createBatch(List<GaoEventRecord> recordList) {
|
||||||
|
for (GaoEventRecord record : TimiJava.safeIterable(recordList)) {
|
||||||
|
create(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void update(GaoEventRecord record) {
|
public void update(GaoEventRecord record) {
|
||||||
@@ -193,6 +205,33 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService<GaoEve
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void delete(String id) {
|
||||||
|
TimiException.required(id, "not found record.id");
|
||||||
|
Logger logger = new Logger(Logger.Module.GAO, "GAO_EVENT_RECORD_DELETE");
|
||||||
|
try {
|
||||||
|
logger.setContent(id);
|
||||||
|
GaoEventRecord record = get(id);
|
||||||
|
TimiException.required(record, "not found event record");
|
||||||
|
pointTriggerService.revokeByEventRecord(record);
|
||||||
|
super.delete(id);
|
||||||
|
logger.setLevel(Logger.Level.INFO);
|
||||||
|
logger.setResult(id);
|
||||||
|
} catch (TimiException e) {
|
||||||
|
logger.setLevel(Logger.Level.WARN);
|
||||||
|
logger.setException(e.getMessage());
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.setLevel(Logger.Level.ERROR);
|
||||||
|
logger.setException(TimiJava.serializeThrowable(e));
|
||||||
|
log.error("删除 GAO 登记记录失败", e);
|
||||||
|
throw new TimiException(TimiCode.ERROR, "删除 GAO 登记记录失败", e);
|
||||||
|
} finally {
|
||||||
|
loggerService.create(logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void createWithCustomer(GaoCustomer customer) {
|
public void createWithCustomer(GaoCustomer customer) {
|
||||||
|
|||||||
+71
@@ -0,0 +1,71 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointAccount;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoPointAccountMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointAccountService;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import com.imyeyu.spring.service.AbstractEntityService;
|
||||||
|
import com.imyeyu.utils.Time;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/// GAO 客户积分账户服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GaoPointAccountServiceImplement extends AbstractEntityService<GaoPointAccount, String> implements GaoPointAccountService {
|
||||||
|
|
||||||
|
private final GaoPointAccountMapper mapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BaseMapper<GaoPointAccount, String> mapper() {
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GaoPointAccount getByCustomerId(String storeId, String customerId) {
|
||||||
|
TimiException.required(storeId, "not found storeId");
|
||||||
|
TimiException.required(customerId, "not found customerId");
|
||||||
|
return mapper.selectByStoreIdAndCustomerId(storeId, customerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public GaoPointAccount getOrCreateForUpdate(String storeId, String customerId) {
|
||||||
|
TimiException.required(storeId, "not found storeId");
|
||||||
|
TimiException.required(customerId, "not found customerId");
|
||||||
|
|
||||||
|
GaoPointAccount account = mapper.selectByStoreIdAndCustomerId(storeId, customerId);
|
||||||
|
if (account == null) {
|
||||||
|
account = new GaoPointAccount();
|
||||||
|
account.setId(UUID.randomUUID().toString());
|
||||||
|
account.setStoreId(storeId);
|
||||||
|
account.setCustomerId(customerId);
|
||||||
|
account.setBalance(0L);
|
||||||
|
account.setTotalEarned(0L);
|
||||||
|
account.setTotalRevoked(0L);
|
||||||
|
account.setTotalAdjusted(0L);
|
||||||
|
account.setVersion(0L);
|
||||||
|
account.setCreatedAt(Time.now());
|
||||||
|
mapper.insertIfAbsent(account);
|
||||||
|
account = mapper.selectByStoreIdAndCustomerId(storeId, customerId);
|
||||||
|
}
|
||||||
|
TimiException.required(account, "not found customer point account");
|
||||||
|
GaoPointAccount lockedAccount = mapper.selectForUpdate(account.getId());
|
||||||
|
TimiException.required(lockedAccount, "not found customer point account");
|
||||||
|
return lockedAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int updateBalance(String accountId, long delta, long totalEarnedDelta, long totalRevokedDelta, long totalAdjustedDelta) {
|
||||||
|
TimiException.required(accountId, "not found accountId");
|
||||||
|
return mapper.updateBalance(accountId, delta, totalEarnedDelta, totalRevokedDelta, totalAdjustedDelta, Time.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointAccount;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointLedger;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointRule;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoPointLedgerMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointAccountService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointLedgerService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoPointAdjustRequest;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoPointLedgerPage;
|
||||||
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import com.imyeyu.spring.service.AbstractEntityService;
|
||||||
|
import com.imyeyu.utils.Time;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/// GAO 客户积分流水服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GaoPointLedgerServiceImplement extends AbstractEntityService<GaoPointLedger, String> implements GaoPointLedgerService {
|
||||||
|
|
||||||
|
private final GaoPointLedgerMapper mapper;
|
||||||
|
private final GaoPointAccountService accountService;
|
||||||
|
private final UserLoginService userLoginService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BaseMapper<GaoPointLedger, String> mapper() {
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(GaoPointLedger ledger) {
|
||||||
|
throw new TimiException(TimiCode.ERROR_NOT_SUPPORT, "积分流水只允许追加,禁止修改");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String id) {
|
||||||
|
throw new TimiException(TimiCode.ERROR_NOT_SUPPORT, "积分流水只允许追加,禁止删除");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy(String id) {
|
||||||
|
throw new TimiException(TimiCode.ERROR_NOT_SUPPORT, "积分流水只允许追加,禁止物理删除");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public GaoPointLedger earn(GaoEventRecord record, GaoPointRule rule, long pointDelta, JsonNode sourcePayload) {
|
||||||
|
TimiException.required(record, "not found event record");
|
||||||
|
TimiException.required(rule, "not found point rule");
|
||||||
|
TimiException.requiredTrue(0 < pointDelta, "pointDelta invalid");
|
||||||
|
String idempotencyKey = "GAO_EVENT_RECORD:%s:RULE:%s:EARN".formatted(record.getId(), rule.getId());
|
||||||
|
return apply(record.getStoreId(), record.getCustomerId(), GaoPointLedger.ChangeType.EARN, pointDelta, rule.getId(), record.getEventId(), record.getId(), sourcePayload, idempotencyKey, rule.getRemark(), record.getOperatorUserId(), record.getRegisteredAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public GaoPointLedger revoke(GaoPointLedger originLedger, GaoEventRecord record) {
|
||||||
|
TimiException.required(originLedger, "not found origin ledger");
|
||||||
|
TimiException.required(record, "not found event record");
|
||||||
|
TimiException.requiredTrue(0 < originLedger.getPointDelta(), "origin ledger pointDelta invalid");
|
||||||
|
String idempotencyKey = "GAO_EVENT_RECORD:%s:LEDGER:%s:REVOKE".formatted(record.getId(), originLedger.getId());
|
||||||
|
return apply(originLedger.getStoreId(), originLedger.getCustomerId(), GaoPointLedger.ChangeType.REVOKE, -originLedger.getPointDelta(), originLedger.getRuleId(), originLedger.getEventId(), originLedger.getEventRecordId(), originLedger.getSourcePayload(), idempotencyKey, "撤销登记记录积分", userLoginService.getRequireLoginUserId(), Time.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public GaoPointLedger adjust(GaoPointAdjustRequest request, String operatorUserId) {
|
||||||
|
TimiException.required(request, "not found adjust request");
|
||||||
|
TimiException.requiredTrue(TimiJava.isNotEmpty(request.getAdjustRequestId()), "not found adjustRequestId");
|
||||||
|
TimiException.required(request.getStoreId(), "not found storeId");
|
||||||
|
TimiException.required(request.getCustomerId(), "not found customerId");
|
||||||
|
TimiException.required(request.getPointDelta(), "not found pointDelta");
|
||||||
|
TimiException.requiredTrue(request.getPointDelta() != 0, "pointDelta invalid");
|
||||||
|
TimiException.requiredTrue(TimiJava.isNotEmpty(request.getRemark()), "not found adjust remark");
|
||||||
|
TimiException.required(operatorUserId, "not found operatorUserId");
|
||||||
|
String idempotencyKey = "MANUAL_ADJUST:%s".formatted(request.getAdjustRequestId());
|
||||||
|
return apply(request.getStoreId(), request.getCustomerId(), GaoPointLedger.ChangeType.ADJUST, request.getPointDelta(), null, null, null, null, idempotencyKey, request.getRemark(), operatorUserId, Time.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<GaoPointLedger> pageByQuery(GaoPointLedgerPage page) {
|
||||||
|
TimiException.required(page, "not found page");
|
||||||
|
PageResult<GaoPointLedger> result = new PageResult<>();
|
||||||
|
result.setTotal(mapper.countByQuery(page));
|
||||||
|
result.setList(mapper.selectByQuery(page));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GaoPointLedger apply(String storeId, String customerId, GaoPointLedger.ChangeType changeType, long pointDelta, String ruleId, String eventId, String eventRecordId, JsonNode sourcePayload, String idempotencyKey, String remark, String operatorUserId, Long occurredAt) {
|
||||||
|
GaoPointLedger existing = mapper.selectByIdempotencyKey(idempotencyKey);
|
||||||
|
if (existing != null) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
GaoPointAccount account = accountService.getOrCreateForUpdate(storeId, customerId);
|
||||||
|
existing = mapper.selectByIdempotencyKey(idempotencyKey);
|
||||||
|
if (existing != null) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
long balanceBefore = account.getBalance();
|
||||||
|
long balanceAfter = Math.addExact(balanceBefore, pointDelta);
|
||||||
|
long totalEarnedDelta = changeType == GaoPointLedger.ChangeType.EARN ? pointDelta : 0L;
|
||||||
|
long totalRevokedDelta = changeType == GaoPointLedger.ChangeType.REVOKE ? -pointDelta : 0L;
|
||||||
|
long totalAdjustedDelta = changeType == GaoPointLedger.ChangeType.ADJUST ? pointDelta : 0L;
|
||||||
|
|
||||||
|
GaoPointLedger ledger = new GaoPointLedger();
|
||||||
|
ledger.setAccountId(account.getId());
|
||||||
|
ledger.setStoreId(storeId);
|
||||||
|
ledger.setCustomerId(customerId);
|
||||||
|
ledger.setChangeType(changeType);
|
||||||
|
ledger.setDirection(0 < pointDelta ? GaoPointLedger.Direction.INCREASE : GaoPointLedger.Direction.DECREASE);
|
||||||
|
ledger.setPointDelta(pointDelta);
|
||||||
|
ledger.setBalanceBefore(balanceBefore);
|
||||||
|
ledger.setBalanceAfter(balanceAfter);
|
||||||
|
ledger.setRuleId(ruleId);
|
||||||
|
ledger.setEventId(eventId);
|
||||||
|
ledger.setEventRecordId(eventRecordId);
|
||||||
|
ledger.setSourcePayload(sourcePayload);
|
||||||
|
ledger.setIdempotencyKey(idempotencyKey);
|
||||||
|
ledger.setRemark(remark);
|
||||||
|
ledger.setOperatorUserId(operatorUserId);
|
||||||
|
ledger.setOccurredAt(occurredAt);
|
||||||
|
create(ledger);
|
||||||
|
int updated = accountService.updateBalance(account.getId(), pointDelta, totalEarnedDelta, totalRevokedDelta, totalAdjustedDelta);
|
||||||
|
TimiException.requiredTrue(0 < updated, "积分余额不足");
|
||||||
|
return ledger;
|
||||||
|
}
|
||||||
|
}
|
||||||
+154
@@ -0,0 +1,154 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointRule;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoEvent;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoPointRuleMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointRuleService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoEventService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import com.imyeyu.spring.service.AbstractEntityService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 客户积分规则服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GaoPointRuleServiceImplement extends AbstractEntityService<GaoPointRule, String> implements GaoPointRuleService {
|
||||||
|
|
||||||
|
private final GaoPointRuleMapper mapper;
|
||||||
|
private final GaoEventService eventService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BaseMapper<GaoPointRule, String> mapper() {
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoPointRule> listActive(String storeId, String eventId, long now) {
|
||||||
|
TimiException.required(storeId, "not found storeId");
|
||||||
|
TimiException.required(eventId, "not found eventId");
|
||||||
|
return mapper.selectActiveByStoreIdAndEventId(storeId, eventId, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void create(GaoPointRule rule) {
|
||||||
|
TimiException.required(rule, "not found rule");
|
||||||
|
TimiException.required(rule.getStoreId(), "not found rule.storeId");
|
||||||
|
TimiException.required(rule.getEventId(), "not found rule.eventId");
|
||||||
|
TimiException.required(rule.getName(), "not found rule.name");
|
||||||
|
checkEvent(rule.getStoreId(), rule.getEventId());
|
||||||
|
rule.setStatus(TimiJava.defaultIfNull(rule.getStatus(), GaoPointRule.Status.DRAFT));
|
||||||
|
rule.setPriority(TimiJava.defaultIfNull(rule.getPriority(), 0));
|
||||||
|
rule.setExclusive(TimiJava.defaultIfNull(rule.getExclusive(), false));
|
||||||
|
verifyRule(rule);
|
||||||
|
super.create(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void update(GaoPointRule rule) {
|
||||||
|
TimiException.required(rule, "not found rule");
|
||||||
|
TimiException.required(rule.getId(), "not found rule.id");
|
||||||
|
TimiException.required(rule.getStoreId(), "not found rule.storeId");
|
||||||
|
TimiException.required(rule.getEventId(), "not found rule.eventId");
|
||||||
|
TimiException.required(rule.getName(), "not found rule.name");
|
||||||
|
GaoPointRule dbRule = get(rule.getId());
|
||||||
|
TimiException.required(dbRule, "not found rule");
|
||||||
|
TimiException.requiredTrue(dbRule.getStoreId().equals(rule.getStoreId()), "rule.storeId invalid");
|
||||||
|
checkEvent(rule.getStoreId(), rule.getEventId());
|
||||||
|
rule.setPriority(TimiJava.defaultIfNull(rule.getPriority(), 0));
|
||||||
|
rule.setExclusive(TimiJava.defaultIfNull(rule.getExclusive(), false));
|
||||||
|
verifyRule(rule);
|
||||||
|
dbRule.setEventId(rule.getEventId());
|
||||||
|
dbRule.setName(rule.getName());
|
||||||
|
dbRule.setStatus(rule.getStatus());
|
||||||
|
dbRule.setTriggerType(rule.getTriggerType());
|
||||||
|
dbRule.setPointMode(rule.getPointMode());
|
||||||
|
dbRule.setPointValue(rule.getPointValue());
|
||||||
|
dbRule.setConditionJson(rule.getConditionJson());
|
||||||
|
dbRule.setBeginAt(rule.getBeginAt());
|
||||||
|
dbRule.setEndAt(rule.getEndAt());
|
||||||
|
dbRule.setPriority(rule.getPriority());
|
||||||
|
dbRule.setExclusive(rule.getExclusive());
|
||||||
|
dbRule.setRemark(rule.getRemark());
|
||||||
|
mapper.update(dbRule);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void delete(String id) {
|
||||||
|
TimiException.required(id, "not found rule.id");
|
||||||
|
GaoPointRule rule = get(id);
|
||||||
|
TimiException.required(rule, "not found rule");
|
||||||
|
rule.setStatus(GaoPointRule.Status.DELETED);
|
||||||
|
super.update(rule);
|
||||||
|
super.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkEvent(String storeId, String eventId) {
|
||||||
|
GaoEvent event = eventService.get(eventId);
|
||||||
|
TimiException.required(event, "not found event");
|
||||||
|
TimiException.requiredTrue(storeId.equals(event.getStoreId()), "规则和登记事件不属于同一门店");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyRule(GaoPointRule rule) {
|
||||||
|
TimiException.required(rule.getStatus(), "not found rule.status");
|
||||||
|
TimiException.required(rule.getTriggerType(), "not found rule.triggerType");
|
||||||
|
TimiException.required(rule.getPointMode(), "not found rule.pointMode");
|
||||||
|
TimiException.requiredTrue(rule.getPriority() != null && 0 <= rule.getPriority(), "rule.priority invalid");
|
||||||
|
TimiException.requiredTrue(rule.getBeginAt() == null || rule.getEndAt() == null || rule.getBeginAt() <= rule.getEndAt(), "rule.beginAt or rule.endAt invalid");
|
||||||
|
|
||||||
|
if (rule.getTriggerType() == GaoPointRule.TriggerType.SOURCE_VALUE) {
|
||||||
|
TimiException.requiredTrue(rule.getPointMode() == GaoPointRule.PointMode.SOURCE_VALUE, "SOURCE_VALUE 规则必须使用 SOURCE_VALUE 积分值模式");
|
||||||
|
TimiException.requiredNull(rule.getPointValue(), "SOURCE_VALUE 规则不能填写固定积分值");
|
||||||
|
verifySourceCondition(rule.getConditionJson());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimiException.requiredTrue(rule.getPointMode() == GaoPointRule.PointMode.FIXED, "固定触发规则必须使用 FIXED 积分值模式");
|
||||||
|
TimiException.required(rule.getPointValue(), "not found rule.pointValue");
|
||||||
|
TimiException.requiredTrue(0 < rule.getPointValue(), "rule.pointValue invalid");
|
||||||
|
if (rule.getTriggerType() == GaoPointRule.TriggerType.AFTER_COUNT_EVERY_TIME) {
|
||||||
|
JsonNode condition = requireObjectCondition(rule.getConditionJson());
|
||||||
|
JsonNode threshold = condition.get("threshold");
|
||||||
|
TimiException.requiredTrue(threshold != null && threshold.isIntegralNumber() && 0 < threshold.longValue(), "AFTER_COUNT_EVERY_TIME.threshold invalid");
|
||||||
|
JsonNode includeCurrent = condition.get("includeCurrent");
|
||||||
|
TimiException.requiredTrue(includeCurrent == null || includeCurrent.isNull() || includeCurrent.isBoolean(), "AFTER_COUNT_EVERY_TIME.includeCurrent invalid");
|
||||||
|
verifyScope(condition);
|
||||||
|
} else if (rule.getTriggerType() == GaoPointRule.TriggerType.FIRST_TIME) {
|
||||||
|
verifyScope(requireObjectCondition(rule.getConditionJson()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifySourceCondition(JsonNode conditionJson) {
|
||||||
|
JsonNode condition = conditionJson == null ? null : requireObjectCondition(conditionJson);
|
||||||
|
Long min = condition == null || condition.get("min") == null || condition.get("min").isNull() ? null : condition.get("min").isIntegralNumber() ? condition.get("min").longValue() : null;
|
||||||
|
Long max = condition == null || condition.get("max") == null || condition.get("max").isNull() ? null : condition.get("max").isIntegralNumber() ? condition.get("max").longValue() : null;
|
||||||
|
TimiException.requiredTrue(condition == null || min != null || condition.get("min") == null || condition.get("min").isNull(), "SOURCE_VALUE.min invalid");
|
||||||
|
TimiException.requiredTrue(condition == null || max != null || condition.get("max") == null || condition.get("max").isNull(), "SOURCE_VALUE.max invalid");
|
||||||
|
TimiException.requiredTrue(min == null || 0 < min, "SOURCE_VALUE.min invalid");
|
||||||
|
TimiException.requiredTrue(max == null || 0 < max, "SOURCE_VALUE.max invalid");
|
||||||
|
TimiException.requiredTrue(min == null || max == null || min <= max, "SOURCE_VALUE.min or SOURCE_VALUE.max invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode requireObjectCondition(JsonNode conditionJson) {
|
||||||
|
TimiException.requiredTrue(conditionJson != null && conditionJson.isObject(), "rule.conditionJson must be object");
|
||||||
|
return conditionJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyScope(JsonNode condition) {
|
||||||
|
JsonNode scope = condition.get("scope");
|
||||||
|
TimiException.requiredTrue(scope == null || scope.isNull() || "CUSTOMER_EVENT".equals(scope.asText()), "rule.conditionJson.scope invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointLedger;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointRule;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoPointLedgerMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoEventRecordMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointLedgerService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointRuleService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoPointTriggerService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoPointAdjustRequest;
|
||||||
|
import com.imyeyu.api.modules.user.service.PermissionChecker;
|
||||||
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.utils.Time;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/// GAO 客户积分触发服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GaoPointTriggerServiceImplement implements GaoPointTriggerService {
|
||||||
|
|
||||||
|
private final ObjectMapper jackson;
|
||||||
|
private final PermissionChecker permissionChecker;
|
||||||
|
private final UserLoginService userLoginService;
|
||||||
|
private final GaoPointRuleService ruleService;
|
||||||
|
private final GaoPointLedgerService ledgerService;
|
||||||
|
private final GaoCustomerService customerService;
|
||||||
|
private final GaoPointLedgerMapper ledgerMapper;
|
||||||
|
private final GaoEventRecordMapper eventRecordMapper;
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void onEventRecordCreated(GaoEventRecord record) {
|
||||||
|
TimiException.required(record, "not found event record");
|
||||||
|
TimiException.required(record.getStoreId(), "not found record.storeId");
|
||||||
|
TimiException.required(record.getCustomerId(), "not found record.customerId");
|
||||||
|
TimiException.required(record.getEventId(), "not found record.eventId");
|
||||||
|
|
||||||
|
List<GaoPointRule> ruleList = ruleService.listActive(record.getStoreId(), record.getEventId(), Time.now());
|
||||||
|
Long count = null;
|
||||||
|
for (GaoPointRule rule : ruleList) {
|
||||||
|
Long pointDelta = resolvePointDelta(record, rule, count);
|
||||||
|
if (pointDelta == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (count == null) {
|
||||||
|
count = eventRecordMapper.count(record.getStoreId(), record.getCustomerId(), record.getEventId(), null, null);
|
||||||
|
}
|
||||||
|
JsonNode sourcePayload = sourcePayload(record, rule, pointDelta);
|
||||||
|
ledgerService.earn(record, rule, pointDelta, sourcePayload);
|
||||||
|
if (Boolean.TRUE.equals(rule.getExclusive())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void revokeByEventRecord(GaoEventRecord record) {
|
||||||
|
TimiException.required(record, "not found event record");
|
||||||
|
List<GaoPointLedger> originLedgerList = ledgerMapper.selectEarnByEventRecordId(record.getId());
|
||||||
|
if (originLedgerList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<String> revokedKeySet = new HashSet<>();
|
||||||
|
List<String> revokeKeyList = originLedgerList.stream().map(origin -> revokeKey(record, origin)).toList();
|
||||||
|
for (GaoPointLedger ledger : ledgerMapper.selectByIdempotencyKeyList(revokeKeyList)) {
|
||||||
|
revokedKeySet.add(ledger.getIdempotencyKey());
|
||||||
|
}
|
||||||
|
for (GaoPointLedger originLedger : originLedgerList) {
|
||||||
|
if (!revokedKeySet.contains(revokeKey(record, originLedger))) {
|
||||||
|
ledgerService.revoke(originLedger, record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GaoPointLedger adjust(GaoPointAdjustRequest request) {
|
||||||
|
TimiException.required(request, "not found adjust request");
|
||||||
|
TimiException.required(request.getStoreId(), "not found storeId");
|
||||||
|
TimiException.required(request.getCustomerId(), "not found customerId");
|
||||||
|
GaoCustomer customer = customerService.get(request.getCustomerId());
|
||||||
|
TimiException.required(customer, "not found customer");
|
||||||
|
TimiException.requiredTrue(request.getStoreId().equals(customer.getStoreId()), "客户和积分调整门店不一致");
|
||||||
|
return ledgerService.adjust(request, userLoginService.getRequireLoginUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long resolvePointDelta(GaoEventRecord record, GaoPointRule rule, Long count) {
|
||||||
|
return switch (rule.getTriggerType()) {
|
||||||
|
case EVERY_TIME -> rule.getPointValue();
|
||||||
|
case FIRST_TIME -> {
|
||||||
|
long currentCount = count == null ? count(record) : count;
|
||||||
|
yield currentCount == 1 ? rule.getPointValue() : null;
|
||||||
|
}
|
||||||
|
case AFTER_COUNT_EVERY_TIME -> {
|
||||||
|
JsonNode condition = rule.getConditionJson();
|
||||||
|
long threshold = condition.get("threshold").longValue();
|
||||||
|
boolean includeCurrent = condition.get("includeCurrent") == null || condition.get("includeCurrent").isNull() || condition.get("includeCurrent").asBoolean();
|
||||||
|
long currentCount = count == null ? count(record) : count;
|
||||||
|
yield includeCurrent ? threshold <= currentCount ? rule.getPointValue() : null : threshold < currentCount ? rule.getPointValue() : null;
|
||||||
|
}
|
||||||
|
case SOURCE_VALUE -> resolveSourceValue(record, rule);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long resolveSourceValue(GaoEventRecord record, GaoPointRule rule) {
|
||||||
|
if (record.getPointValue() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
permissionChecker.checkAny(ModuleCode.GAO, GaoPermissionCode.POINT_SOURCE_VALUE.getValue());
|
||||||
|
long pointValue = record.getPointValue();
|
||||||
|
TimiException.requiredTrue(0 < pointValue, "pointValue invalid");
|
||||||
|
JsonNode condition = rule.getConditionJson();
|
||||||
|
if (condition != null) {
|
||||||
|
JsonNode min = condition.get("min");
|
||||||
|
JsonNode max = condition.get("max");
|
||||||
|
TimiException.requiredTrue(min == null || min.isNull() || min.longValue() <= pointValue, "pointValue 小于规则最小值");
|
||||||
|
TimiException.requiredTrue(max == null || max.isNull() || pointValue <= max.longValue(), "pointValue 大于规则最大值");
|
||||||
|
}
|
||||||
|
return pointValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode sourcePayload(GaoEventRecord record, GaoPointRule rule, long pointDelta) {
|
||||||
|
if (rule.getTriggerType() != GaoPointRule.TriggerType.SOURCE_VALUE) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ObjectNode payload = jackson.createObjectNode();
|
||||||
|
payload.put("submittedPointValue", record.getPointValue());
|
||||||
|
payload.put("validatedPointValue", pointDelta);
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long count(GaoEventRecord record) {
|
||||||
|
return TimiJava.defaultIfNull(eventRecordMapper.count(record.getStoreId(), record.getCustomerId(), record.getEventId(), null, null), 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String revokeKey(GaoEventRecord record, GaoPointLedger originLedger) {
|
||||||
|
return "GAO_EVENT_RECORD:%s:LEDGER:%s:REVOKE".formatted(record.getId(), originLedger.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.bean.CoreRoleCode;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoStore;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||||
|
import com.imyeyu.api.modules.user.bean.AuthOwnerType;
|
||||||
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
|
import com.imyeyu.api.modules.user.entity.UserRole;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.UserRoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.service.ModuleResourceScopeResolver;
|
||||||
|
import com.imyeyu.api.modules.user.service.RoleHierarchyService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 资源范围解析器
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-07
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GaoResourceScopeResolver implements ModuleResourceScopeResolver {
|
||||||
|
|
||||||
|
private final RoleMapper roleMapper;
|
||||||
|
private final UserRoleMapper userRoleMapper;
|
||||||
|
private final GaoStoreService gaoStoreService;
|
||||||
|
private final RoleHierarchyService roleHierarchyService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModuleCode moduleCode() {
|
||||||
|
return ModuleCode.GAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canManageUser(String operatorUserId, String targetUserId) {
|
||||||
|
if (hasCoreRole(operatorUserId, CoreRoleCode.ADMIN)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasGaoRole(operatorUserId, GaoRoleCode.GLOBAL_MANAGER)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!hasGaoRole(operatorUserId, GaoRoleCode.STORE_MANAGER)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GaoStore operatorStore = gaoStoreService.getByUserId(operatorUserId);
|
||||||
|
GaoStore targetStore = gaoStoreService.getByUserId(targetUserId);
|
||||||
|
return operatorStore != null && targetStore != null && operatorStore.getId().equals(targetStore.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canManageRole(String operatorUserId, Role role) {
|
||||||
|
if (hasCoreRole(operatorUserId, CoreRoleCode.ADMIN)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasGaoRole(operatorUserId, GaoRoleCode.GLOBAL_MANAGER)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!hasGaoRole(operatorUserId, GaoRoleCode.STORE_MANAGER)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (canUseOwnerScope(operatorUserId, role.getOwnerType(), role.getOwnerId())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ModuleCode.GAO == role.getModuleCode()
|
||||||
|
&& role.getOwnerType() == AuthOwnerType.MODULE
|
||||||
|
&& (TimiJava.isEmpty(role.getOwnerId()) || ModuleCode.GAO.name().equals(role.getOwnerId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canUseOwnerScope(String operatorUserId, AuthOwnerType ownerType, String ownerId) {
|
||||||
|
if (hasCoreRole(operatorUserId, CoreRoleCode.ADMIN)) {
|
||||||
|
return ownerType == AuthOwnerType.MODULE && ModuleCode.GAO.name().equals(ownerId) || ownerType == AuthOwnerType.STORE;
|
||||||
|
}
|
||||||
|
if (hasGaoRole(operatorUserId, GaoRoleCode.GLOBAL_MANAGER)) {
|
||||||
|
return ownerType == AuthOwnerType.MODULE && ModuleCode.GAO.name().equals(ownerId) || ownerType == AuthOwnerType.STORE;
|
||||||
|
}
|
||||||
|
if (!hasGaoRole(operatorUserId, GaoRoleCode.STORE_MANAGER) || ownerType != AuthOwnerType.STORE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GaoStore store = gaoStoreService.getByUserId(operatorUserId);
|
||||||
|
return store != null && store.getId().equals(ownerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasGaoRole(String userId, GaoRoleCode roleCode) {
|
||||||
|
if (TimiJava.isEmpty(userId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<String> roleIdList = userRoleMapper.selectByUserIdList(List.of(userId)).stream().map(UserRole::getRoleId).toList();
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 身份角色只依赖系统、模块级继承,不能依赖当前正在计算的门店作用域
|
||||||
|
List<String> allRoleIdList = roleHierarchyService.listAllRoleIdIncludingChildren(roleIdList);
|
||||||
|
return roleMapper.selectByIdList(allRoleIdList).stream().anyMatch(role -> ModuleCode.GAO == role.getModuleCode() && roleCode.name().equals(role.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasCoreRole(String userId, CoreRoleCode roleCode) {
|
||||||
|
if (TimiJava.isEmpty(userId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<String> roleIdList = userRoleMapper.selectByUserIdList(List.of(userId)).stream().map(UserRole::getRoleId).toList();
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<String> allRoleIdList = roleHierarchyService.listAllRoleIdIncludingChildren(roleIdList);
|
||||||
|
return roleMapper.selectByIdList(allRoleIdList).stream().anyMatch(role -> ModuleCode.CORE == role.getModuleCode() && roleCode.name().equals(role.getCode()));
|
||||||
|
}
|
||||||
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoEventRecordMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoStoreChartService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStateReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 门店报表服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GaoStoreChartServiceImplement implements GaoStoreChartService {
|
||||||
|
|
||||||
|
private final GaoCustomerService customerService;
|
||||||
|
private final GaoEventRecordMapper eventRecordMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoCustomerDailyStateView> customerDailyTrend(GaoStoreChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
GaoCustomerTrendStateReq statReq = new GaoCustomerTrendStateReq();
|
||||||
|
statReq.setStoreId(req.getStoreId());
|
||||||
|
statReq.setBeginCreatedAt(req.getBeginAt());
|
||||||
|
statReq.setEndCreatedAt(req.getEndAt());
|
||||||
|
return customerService.stateDailyTrend(statReq);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoCustomerGenderStatView> customerGender(String storeId) {
|
||||||
|
TimiException.required(storeId, "not found storeId");
|
||||||
|
return customerService.stateGender(storeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoEventRecordDailyStatView> eventRecordDailyStat(GaoStoreChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
return eventRecordMapper.stateDailyByRange(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoEventRecordCalendarView> eventRecordCalendar(GaoStoreChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
return eventRecordMapper.selectCalendarByRange(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkRangeReq(GaoStoreChartReq req) {
|
||||||
|
TimiException.required(req, "not found req");
|
||||||
|
TimiException.required(req.getStoreId(), "not found req.storeId");
|
||||||
|
TimiException.required(req.getBeginAt(), "not found req.beginAt");
|
||||||
|
TimiException.required(req.getEndAt(), "not found req.endAt");
|
||||||
|
TimiException.requiredTrue(req.getBeginAt() <= req.getEndAt(), "req.endAt lt req.beginAt");
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -50,6 +50,7 @@ public class GaoStoreServiceImplement extends AbstractEntityService<GaoStore, St
|
|||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void create(GaoStore store) {
|
public void create(GaoStore store) {
|
||||||
TimiException.required(store, "not found store");
|
TimiException.required(store, "not found store");
|
||||||
@@ -79,6 +80,7 @@ public class GaoStoreServiceImplement extends AbstractEntityService<GaoStore, St
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void update(GaoStore store) {
|
public void update(GaoStore store) {
|
||||||
TimiException.required(store, "not found store");
|
TimiException.required(store, "not found store");
|
||||||
@@ -114,6 +116,7 @@ public class GaoStoreServiceImplement extends AbstractEntityService<GaoStore, St
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void delete(String id) {
|
public void delete(String id) {
|
||||||
Logger logger = new Logger(Logger.Module.GAO, "GAO_STORE_DELETE");
|
Logger logger = new Logger(Logger.Module.GAO, "GAO_STORE_DELETE");
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 客户报表查询请求
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Data
|
||||||
|
public class GaoCustomerChartReq {
|
||||||
|
|
||||||
|
/// 门店 ID
|
||||||
|
private String storeId;
|
||||||
|
|
||||||
|
/// 客户 ID
|
||||||
|
private String customerId;
|
||||||
|
|
||||||
|
/// 开始时间
|
||||||
|
private Long beginAt;
|
||||||
|
|
||||||
|
/// 结束时间
|
||||||
|
private Long endAt;
|
||||||
|
|
||||||
|
/// 登记事件 ID 列表
|
||||||
|
private List<String> eventIdList;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/// 登记事件日历记录视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Data
|
||||||
|
public class GaoEventRecordCalendarView {
|
||||||
|
|
||||||
|
/// 登记记录 ID
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/// 客户 ID
|
||||||
|
private String customerId;
|
||||||
|
|
||||||
|
/// 客户编码
|
||||||
|
private String customerCode;
|
||||||
|
|
||||||
|
/// 客户姓名
|
||||||
|
private String customerName;
|
||||||
|
|
||||||
|
/// 登记事件 ID
|
||||||
|
private String eventId;
|
||||||
|
|
||||||
|
/// 登记时间
|
||||||
|
private Long registeredAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/// 登记事件每日统计视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Data
|
||||||
|
public class GaoEventRecordDailyStatView {
|
||||||
|
|
||||||
|
/// 登记事件 ID
|
||||||
|
private String eventId;
|
||||||
|
|
||||||
|
/// 日期值,格式为 yyyyMMdd
|
||||||
|
private Integer dateValue;
|
||||||
|
|
||||||
|
/// 当日登记数
|
||||||
|
private Long dailyCount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/// GAO 客户积分人工调整请求
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Data
|
||||||
|
public class GaoPointAdjustRequest {
|
||||||
|
|
||||||
|
/// 人工调整请求 ID,用于生成幂等键
|
||||||
|
private String adjustRequestId;
|
||||||
|
|
||||||
|
/// 门店 ID
|
||||||
|
private String storeId;
|
||||||
|
|
||||||
|
/// 客户 ID
|
||||||
|
private String customerId;
|
||||||
|
|
||||||
|
/// 调整积分,正数增加,负数减少
|
||||||
|
private Long pointDelta;
|
||||||
|
|
||||||
|
/// 调整原因
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoPointLedger;
|
||||||
|
import com.imyeyu.java.bean.BasePage;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/// GAO 客户积分流水查询请求
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class GaoPointLedgerPage extends BasePage {
|
||||||
|
|
||||||
|
/// 门店 ID
|
||||||
|
private String storeId;
|
||||||
|
|
||||||
|
/// 客户 ID
|
||||||
|
private String customerId;
|
||||||
|
|
||||||
|
/// 客户积分账户 ID
|
||||||
|
private String accountId;
|
||||||
|
|
||||||
|
/// 流水变化类型
|
||||||
|
private GaoPointLedger.ChangeType changeType;
|
||||||
|
|
||||||
|
/// 开始发生时间
|
||||||
|
private Long beginAt;
|
||||||
|
|
||||||
|
/// 结束发生时间
|
||||||
|
private Long endAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 门店报表查询请求
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-11
|
||||||
|
@Data
|
||||||
|
public class GaoStoreChartReq {
|
||||||
|
|
||||||
|
/// 门店 ID
|
||||||
|
private String storeId;
|
||||||
|
|
||||||
|
/// 开始时间
|
||||||
|
private Long beginAt;
|
||||||
|
|
||||||
|
/// 结束时间
|
||||||
|
private Long endAt;
|
||||||
|
|
||||||
|
/// 登记事件 ID 列表
|
||||||
|
private List<String> eventIdList;
|
||||||
|
}
|
||||||
+21
-8
@@ -3,10 +3,14 @@ package com.imyeyu.api.modules.system.service.implement;
|
|||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.entity.Logger;
|
||||||
import com.imyeyu.api.modules.system.mapper.LoggerMapper;
|
import com.imyeyu.api.modules.system.mapper.LoggerMapper;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||||
|
import com.imyeyu.api.modules.user.entity.User;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.spring.TimiSpring;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
import com.imyeyu.spring.service.AbstractEntityService;
|
import com.imyeyu.spring.service.AbstractEntityService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,6 +19,7 @@ import org.springframework.stereotype.Service;
|
|||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2026-05-13 14:46
|
* @since 2026-05-13 14:46
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LoggerServiceImplement extends AbstractEntityService<Logger, String> implements LoggerService {
|
public class LoggerServiceImplement extends AbstractEntityService<Logger, String> implements LoggerService {
|
||||||
@@ -31,19 +36,13 @@ public class LoggerServiceImplement extends AbstractEntityService<Logger, String
|
|||||||
@Override
|
@Override
|
||||||
public void create(Logger logger) {
|
public void create(Logger logger) {
|
||||||
// 默认异步
|
// 默认异步
|
||||||
|
fillContext(logger);
|
||||||
Thread.startVirtualThread(() -> createSync(logger));
|
Thread.startVirtualThread(() -> createSync(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createSync(Logger logger) {
|
public void createSync(Logger logger) {
|
||||||
// TODO 异步请求上下文已丢失
|
fillContext(logger);
|
||||||
// if (TimiJava.isEmpty(logger.getUserId())) {
|
|
||||||
// User loginUser = userLoginService.getLoginUser();
|
|
||||||
// if (TimiJava.isNotEmpty(loginUser)) {
|
|
||||||
// logger.setUserId(loginUser.getId());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// logger.setIp(TimiJava.defaultIfEmpty(logger.getIp(), TimiSpring.getRequestIP()));
|
|
||||||
super.create(logger);
|
super.create(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,4 +52,18 @@ public class LoggerServiceImplement extends AbstractEntityService<Logger, String
|
|||||||
log.setResult(result);
|
log.setResult(result);
|
||||||
create(log);
|
create(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillContext(Logger logger) {
|
||||||
|
try {
|
||||||
|
if (TimiJava.isEmpty(logger.getUserId())) {
|
||||||
|
User loginUser = userLoginService.getLoginUser();
|
||||||
|
if (TimiJava.isNotEmpty(loginUser)) {
|
||||||
|
logger.setUserId(loginUser.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.setIp(TimiJava.defaultIfEmpty(logger.getIp(), TimiSpring.getRequestIP()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 无请求上下文的后台任务允许继续写日志
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.imyeyu.api.modules.user.bean;
|
||||||
|
|
||||||
|
/// 授权对象归属类型
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-07
|
||||||
|
public enum AuthOwnerType {
|
||||||
|
|
||||||
|
/// 系统级
|
||||||
|
SYSTEM,
|
||||||
|
|
||||||
|
/// 模块级
|
||||||
|
MODULE,
|
||||||
|
|
||||||
|
/// 租户级
|
||||||
|
TENANT,
|
||||||
|
|
||||||
|
/// 门店级
|
||||||
|
STORE,
|
||||||
|
|
||||||
|
/// 用户级
|
||||||
|
USER
|
||||||
|
}
|
||||||
@@ -33,4 +33,25 @@ public interface BuiltinPermissionCode {
|
|||||||
///
|
///
|
||||||
/// @return 是否授权给 ADMIN 角色
|
/// @return 是否授权给 ADMIN 角色
|
||||||
boolean isGrantToAdmin();
|
boolean isGrantToAdmin();
|
||||||
|
|
||||||
|
/// true 为受保护权限
|
||||||
|
///
|
||||||
|
/// @return 是否受保护
|
||||||
|
default boolean isProtected() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 归属类型
|
||||||
|
///
|
||||||
|
/// @return 归属类型
|
||||||
|
default AuthOwnerType getOwnerType() {
|
||||||
|
return AuthOwnerType.MODULE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 归属 ID
|
||||||
|
///
|
||||||
|
/// @return 归属 ID
|
||||||
|
default String getOwnerId() {
|
||||||
|
return getModuleCode().name();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,4 +28,18 @@ public interface BuiltinRoleCode {
|
|||||||
///
|
///
|
||||||
/// @return 父角色代码
|
/// @return 父角色代码
|
||||||
String getParentCode();
|
String getParentCode();
|
||||||
|
|
||||||
|
/// 归属类型
|
||||||
|
///
|
||||||
|
/// @return 归属类型
|
||||||
|
default AuthOwnerType getOwnerType() {
|
||||||
|
return AuthOwnerType.MODULE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 归属 ID
|
||||||
|
///
|
||||||
|
/// @return 归属 ID
|
||||||
|
default String getOwnerId() {
|
||||||
|
return getModuleCode().name();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.imyeyu.api.modules.user.controller;
|
package com.imyeyu.api.modules.user.controller;
|
||||||
|
|
||||||
import com.imyeyu.api.annotation.RequireCorePermission;
|
import com.imyeyu.api.annotation.RequireCorePermission;
|
||||||
import com.imyeyu.api.annotation.RequireCoreRole;
|
|
||||||
import com.imyeyu.api.bean.CorePermissionCode;
|
import com.imyeyu.api.bean.CorePermissionCode;
|
||||||
import com.imyeyu.api.bean.CoreRoleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.api.bean.RoleMatchMode;
|
|
||||||
import com.imyeyu.api.modules.user.entity.Permission;
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
|
import com.imyeyu.api.modules.user.service.AuthorizationScopeService;
|
||||||
import com.imyeyu.api.modules.user.service.PermissionService;
|
import com.imyeyu.api.modules.user.service.PermissionService;
|
||||||
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.api.modules.user.vo.permission.BatchCreateReq;
|
import com.imyeyu.api.modules.user.vo.permission.BatchCreateReq;
|
||||||
import com.imyeyu.spring.annotation.AOPLog;
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
import com.imyeyu.spring.bean.Page;
|
import com.imyeyu.spring.bean.Page;
|
||||||
@@ -19,6 +19,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 权限 SDK 控制器
|
* 权限 SDK 控制器
|
||||||
*
|
*
|
||||||
@@ -32,16 +34,22 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
public class PermissionController {
|
public class PermissionController {
|
||||||
|
|
||||||
private final PermissionService service;
|
private final PermissionService service;
|
||||||
|
private final UserLoginService userLoginService;
|
||||||
|
private final AuthorizationScopeService authorizationScopeService;
|
||||||
|
|
||||||
@RequireCoreRole(value = {CoreRoleCode.ADMIN, CoreRoleCode.USER_ROLE_ADMIN}, mode = RoleMatchMode.ANY)
|
|
||||||
@RequireCorePermission(CorePermissionCode.PERMISSION_READ)
|
@RequireCorePermission(CorePermissionCode.PERMISSION_READ)
|
||||||
@PostMapping("/list")
|
@PostMapping("/list")
|
||||||
public PageResult<Permission> list(@RequestBody Page<Permission> page) {
|
public PageResult<Permission> list(@RequestBody Page<Permission> page) {
|
||||||
return service.page(page);
|
return service.page(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequireCorePermission(CorePermissionCode.ROLE_PERMISSION_READ)
|
||||||
|
@PostMapping("/grantable/list")
|
||||||
|
public List<Permission> listGrantable(@RequestParam ModuleCode moduleCode) {
|
||||||
|
return authorizationScopeService.listGrantablePermission(userLoginService.getRequireLoginUserId(), moduleCode);
|
||||||
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.PERMISSION_READ)
|
@RequireCorePermission(CorePermissionCode.PERMISSION_READ)
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
public void create(@RequestBody Permission permission) {
|
public void create(@RequestBody Permission permission) {
|
||||||
@@ -49,7 +57,6 @@ public class PermissionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.PERMISSION_CREATE)
|
@RequireCorePermission(CorePermissionCode.PERMISSION_CREATE)
|
||||||
@PostMapping("/create/batch")
|
@PostMapping("/create/batch")
|
||||||
public void createBatch(@RequestBody BatchCreateReq req) {
|
public void createBatch(@RequestBody BatchCreateReq req) {
|
||||||
@@ -57,7 +64,6 @@ public class PermissionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.PERMISSION_UPDATE)
|
@RequireCorePermission(CorePermissionCode.PERMISSION_UPDATE)
|
||||||
@PostMapping("/update")
|
@PostMapping("/update")
|
||||||
public void update(@RequestBody Permission permission) {
|
public void update(@RequestBody Permission permission) {
|
||||||
@@ -65,7 +71,6 @@ public class PermissionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.PERMISSION_DELETE)
|
@RequireCorePermission(CorePermissionCode.PERMISSION_DELETE)
|
||||||
@PostMapping("/delete")
|
@PostMapping("/delete")
|
||||||
public void delete(@RequestParam("id") String id) {
|
public void delete(@RequestParam("id") String id) {
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
package com.imyeyu.api.modules.user.controller;
|
package com.imyeyu.api.modules.user.controller;
|
||||||
|
|
||||||
import com.imyeyu.api.annotation.RequireCorePermission;
|
import com.imyeyu.api.annotation.RequireCorePermission;
|
||||||
import com.imyeyu.api.annotation.RequireCoreRole;
|
|
||||||
import com.imyeyu.api.bean.CorePermissionCode;
|
import com.imyeyu.api.bean.CorePermissionCode;
|
||||||
import com.imyeyu.api.bean.CoreRoleCode;
|
|
||||||
import com.imyeyu.api.bean.ModuleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.api.bean.RoleMatchMode;
|
|
||||||
import com.imyeyu.api.modules.user.entity.Permission;
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
import com.imyeyu.api.modules.user.entity.Role;
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
import com.imyeyu.api.modules.user.service.PermissionService;
|
import com.imyeyu.api.modules.user.service.PermissionService;
|
||||||
import com.imyeyu.api.modules.user.service.RoleService;
|
import com.imyeyu.api.modules.user.service.RoleService;
|
||||||
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.api.modules.user.service.UserRoleService;
|
import com.imyeyu.api.modules.user.service.UserRoleService;
|
||||||
import com.imyeyu.api.modules.user.vo.role.UserRoleAuthorizeRequest;
|
import com.imyeyu.api.modules.user.vo.role.UserRoleAuthorizeRequest;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import com.imyeyu.spring.annotation.AOPLog;
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
|
import com.imyeyu.spring.annotation.RequiredToken;
|
||||||
import com.imyeyu.spring.bean.Page;
|
import com.imyeyu.spring.bean.Page;
|
||||||
import com.imyeyu.spring.bean.PageResult;
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -23,7 +24,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 角色 SDK 控制器
|
* 角色 SDK 控制器
|
||||||
@@ -39,30 +43,52 @@ public class RoleController {
|
|||||||
|
|
||||||
private final RoleService service;
|
private final RoleService service;
|
||||||
private final UserRoleService userRoleService;
|
private final UserRoleService userRoleService;
|
||||||
|
private final UserLoginService userLoginService;
|
||||||
private final PermissionService permissionService;
|
private final PermissionService permissionService;
|
||||||
|
|
||||||
@RequireCoreRole(value = {CoreRoleCode.ADMIN, CoreRoleCode.USER_ROLE_ADMIN}, mode = RoleMatchMode.ANY)
|
|
||||||
@RequireCorePermission(CorePermissionCode.ROLE_READ)
|
@RequireCorePermission(CorePermissionCode.ROLE_READ)
|
||||||
@PostMapping("/list")
|
@PostMapping("/list")
|
||||||
public PageResult<Role> list(@RequestBody Page<Role> page) {
|
public PageResult<Role> list(@RequestBody Page<Role> page) {
|
||||||
|
Set<String> manageableRoleIdSet = listManageableRoleId();
|
||||||
PageResult<Role> result = service.page(page);
|
PageResult<Role> result = service.page(page);
|
||||||
if (result.getList() != null) {
|
if (result.getList() != null) {
|
||||||
|
result.setList(result.getList().stream().filter(role -> manageableRoleIdSet.contains(role.getId())).toList());
|
||||||
result.getList().forEach(this::fillRoleDetail);
|
result.getList().forEach(this::fillRoleDetail);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequireCoreRole(value = {CoreRoleCode.ADMIN, CoreRoleCode.USER_ROLE_ADMIN}, mode = RoleMatchMode.ANY)
|
@RequireCorePermission(CorePermissionCode.ROLE_READ)
|
||||||
|
@PostMapping("/manageable/list")
|
||||||
|
public List<Role> listManageableRole(@RequestParam ModuleCode moduleCode) {
|
||||||
|
return service.listManageableRole(moduleCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequireCorePermission(CorePermissionCode.USER_ROLE_READ)
|
||||||
|
@PostMapping("/grantable/list")
|
||||||
|
public List<Role> listGrantableRole(@RequestParam ModuleCode moduleCode) {
|
||||||
|
return service.listGrantableRole(moduleCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredToken
|
||||||
|
@PostMapping("/current/detail/list")
|
||||||
|
public List<Role> listCurrentRoleDetail(@RequestParam(required = false) ModuleCode moduleCode) {
|
||||||
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
|
List<Role> roleList = moduleCode == null ? userRoleService.listRoleByUserId(loginUserId) : userRoleService.listRoleByUserId(moduleCode, loginUserId);
|
||||||
|
roleList.forEach(this::fillRoleDetail);
|
||||||
|
return roleList;
|
||||||
|
}
|
||||||
|
|
||||||
@RequireCorePermission(CorePermissionCode.ROLE_READ)
|
@RequireCorePermission(CorePermissionCode.ROLE_READ)
|
||||||
@PostMapping("/detail")
|
@PostMapping("/detail")
|
||||||
public Role detail(@RequestParam String id) {
|
public Role detail(@RequestParam String id) {
|
||||||
Role role = service.get(id);
|
Role role = service.get(id);
|
||||||
|
requireManageableRole(role.getId());
|
||||||
fillRoleDetail(role);
|
fillRoleDetail(role);
|
||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.ROLE_CREATE)
|
@RequireCorePermission(CorePermissionCode.ROLE_CREATE)
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
public void create(@RequestBody Role role) {
|
public void create(@RequestBody Role role) {
|
||||||
@@ -70,49 +96,110 @@ public class RoleController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.ROLE_UPDATE)
|
@RequireCorePermission(CorePermissionCode.ROLE_UPDATE)
|
||||||
@PostMapping("/update")
|
@PostMapping("/update")
|
||||||
public void update(@RequestBody Role role) {
|
public void update(@RequestBody Role role) {
|
||||||
service.update(role);
|
service.update(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequireCorePermission(CorePermissionCode.ROLE_PERMISSION_READ)
|
||||||
|
@PostMapping("/permission/grantable/list")
|
||||||
|
public List<Permission> listGrantableRolePermission(@RequestParam String roleId) {
|
||||||
|
requireManageableRole(roleId);
|
||||||
|
return service.listGrantableRolePermission(roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequireCorePermission(CorePermissionCode.ROLE_PERMISSION_READ)
|
||||||
|
@PostMapping("/delegation/list")
|
||||||
|
public List<Permission> listDelegationPermission(@RequestParam String roleId) {
|
||||||
|
requireManageableRole(roleId);
|
||||||
|
return service.listDelegationPermissionByRoleId(roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequireCorePermission(CorePermissionCode.ROLE_PERMISSION_READ)
|
||||||
|
@PostMapping("/delegation/grantable/list")
|
||||||
|
public List<Permission> listGrantableDelegationPermission(@RequestParam String roleId) {
|
||||||
|
requireManageableRole(roleId);
|
||||||
|
return service.listGrantableDelegationPermission(roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequireCorePermission(value = {CorePermissionCode.ROLE_PERMISSION_CREATE, CorePermissionCode.ROLE_PERMISSION_DELETE})
|
||||||
|
@PostMapping("/delegation/update")
|
||||||
|
public void updateRoleDelegation(@RequestBody Role role) {
|
||||||
|
service.updateRoleDelegation(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequireCorePermission(CorePermissionCode.ROLE_READ)
|
||||||
|
@PostMapping("/parent/grantable/list")
|
||||||
|
public List<Role> listGrantableParentRole(@RequestParam String roleId) {
|
||||||
|
requireManageableRole(roleId);
|
||||||
|
return service.listGrantableParentRole(roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequireCorePermission(CorePermissionCode.ROLE_UPDATE)
|
||||||
|
@PostMapping("/parent/update")
|
||||||
|
public void updateRoleParent(@RequestBody Role role) {
|
||||||
|
service.updateRoleParent(role);
|
||||||
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequireCorePermission(CorePermissionCode.ROLE_DELETE)
|
@RequireCorePermission(CorePermissionCode.ROLE_DELETE)
|
||||||
@PostMapping("/delete")
|
@PostMapping("/delete")
|
||||||
public void delete(@RequestParam String id) {
|
public void delete(@RequestParam String id) {
|
||||||
service.delete(id);
|
service.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequireCoreRole(value = {CoreRoleCode.ADMIN, CoreRoleCode.USER_ROLE_ADMIN}, mode = RoleMatchMode.ANY)
|
|
||||||
@RequireCorePermission(CorePermissionCode.USER_ROLE_READ)
|
@RequireCorePermission(CorePermissionCode.USER_ROLE_READ)
|
||||||
@PostMapping("/authorized/list")
|
@PostMapping("/authorized/list")
|
||||||
public List<Role> listUserRole(@RequestParam(required = false) ModuleCode moduleCode, @RequestParam String userId) {
|
public List<Role> listUserRole(@RequestParam(required = false) ModuleCode moduleCode, @RequestParam String userId) {
|
||||||
|
Set<String> manageableRoleIdSet = listManageableRoleId();
|
||||||
if (moduleCode != null) {
|
if (moduleCode != null) {
|
||||||
return userRoleService.listRoleByUserId(moduleCode, userId);
|
return userRoleService.listRoleByUserId(moduleCode, userId).stream().filter(role -> manageableRoleIdSet.contains(role.getId())).toList();
|
||||||
}
|
}
|
||||||
return userRoleService.listRoleByUserId(userId);
|
return userRoleService.listRoleByUserId(userId).stream().filter(role -> manageableRoleIdSet.contains(role.getId())).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(value = {CoreRoleCode.ADMIN, CoreRoleCode.USER_ROLE_ADMIN}, mode = RoleMatchMode.ANY)
|
|
||||||
@RequireCorePermission(value = {CorePermissionCode.USER_ROLE_CREATE, CorePermissionCode.USER_ROLE_DELETE})
|
@RequireCorePermission(value = {CorePermissionCode.USER_ROLE_CREATE, CorePermissionCode.USER_ROLE_DELETE})
|
||||||
@PostMapping("/authorized/create")
|
@PostMapping("/authorized/create")
|
||||||
public void authorizeUserRole(@RequestBody UserRoleAuthorizeRequest req) {
|
public void authorizeUserRole(@RequestBody UserRoleAuthorizeRequest req) {
|
||||||
userRoleService.authorizeUserRole(req);
|
userRoleService.authorizeUserRole(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequireCoreRole(value = {CoreRoleCode.ADMIN, CoreRoleCode.USER_ROLE_ADMIN}, mode = RoleMatchMode.ANY)
|
|
||||||
@RequireCorePermission(CorePermissionCode.USER_ROLE_READ)
|
@RequireCorePermission(CorePermissionCode.USER_ROLE_READ)
|
||||||
@PostMapping("/authorized/permission")
|
@PostMapping("/authorized/permission")
|
||||||
public List<Permission> listAuthorizedPermission(@RequestParam String userId) {
|
public List<Permission> listAuthorizedPermission(@RequestParam String userId) {
|
||||||
return userRoleService.listAllPermissionByUserId(userId);
|
Set<String> manageableRoleIdSet = listManageableRoleId();
|
||||||
|
List<String> roleIdList = userRoleService.listRoleByUserId(userId)
|
||||||
|
.stream()
|
||||||
|
.filter(role -> manageableRoleIdSet.contains(role.getId()))
|
||||||
|
.map(Role::getId)
|
||||||
|
.toList();
|
||||||
|
return roleIdList.isEmpty() ? List.of() : permissionService.listByRoleId(service.listAllRoleIdIncludingChildren(roleIdList));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillRoleDetail(Role role) {
|
private void fillRoleDetail(Role role) {
|
||||||
role.setPermissionList(permissionService.listByRoleId(role.getId()));
|
role.setPermissionList(permissionService.listByRoleId(role.getId()));
|
||||||
role.setAllPermissionList(service.listPermissionByRoleId(role.getId()));
|
role.setAllPermissionList(service.listPermissionByRoleId(role.getId()));
|
||||||
role.setChildRoleList(service.listChildByParentRoleId(role.getId()));
|
role.setChildRoleList(service.listChildByParentRoleId(role.getId()));
|
||||||
|
if (role.getParentRoleId() != null) {
|
||||||
|
role.setParentRole(service.get(role.getParentRoleId()));
|
||||||
|
}
|
||||||
|
role.setDelegationPermissionList(service.listDelegationPermissionByRoleId(role.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listManageableRoleId() {
|
||||||
|
return Arrays.stream(ModuleCode.values())
|
||||||
|
.flatMap(moduleCode -> service.listManageableRole(moduleCode).stream())
|
||||||
|
.map(Role::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requireManageableRole(String roleId) {
|
||||||
|
if (!listManageableRoleId().contains(roleId)) {
|
||||||
|
throw new TimiException(TimiCode.PERMISSION_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package com.imyeyu.api.modules.user.controller;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonView;
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
import com.imyeyu.api.annotation.RequireCorePermission;
|
import com.imyeyu.api.annotation.RequireCorePermission;
|
||||||
import com.imyeyu.api.annotation.RequireCoreRole;
|
|
||||||
import com.imyeyu.api.bean.CorePermissionCode;
|
import com.imyeyu.api.bean.CorePermissionCode;
|
||||||
import com.imyeyu.api.bean.CoreRoleCode;
|
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.user.entity.User;
|
import com.imyeyu.api.modules.user.entity.User;
|
||||||
@@ -56,7 +54,6 @@ public class UserController {
|
|||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
|
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireCorePermission(CorePermissionCode.USER_READ)
|
@RequireCorePermission(CorePermissionCode.USER_READ)
|
||||||
@PostMapping("/list")
|
@PostMapping("/list")
|
||||||
@@ -65,7 +62,6 @@ public class UserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireCorePermission(CorePermissionCode.USER_READ)
|
@RequireCorePermission(CorePermissionCode.USER_READ)
|
||||||
@RequestMapping("/detail")
|
@RequestMapping("/detail")
|
||||||
@@ -74,7 +70,6 @@ public class UserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireCorePermission(CorePermissionCode.USER_CREATE)
|
@RequireCorePermission(CorePermissionCode.USER_CREATE)
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
@@ -83,7 +78,6 @@ public class UserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireCorePermission(CorePermissionCode.USER_UPDATE)
|
@RequireCorePermission(CorePermissionCode.USER_UPDATE)
|
||||||
@PostMapping("/update")
|
@PostMapping("/update")
|
||||||
@@ -92,7 +86,6 @@ public class UserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequireCoreRole(CoreRoleCode.ADMIN)
|
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequestMapping("/delete")
|
@RequestMapping("/delete")
|
||||||
public void delete(@RequestParam String id) {
|
public void delete(@RequestParam String id) {
|
||||||
@@ -125,6 +118,7 @@ public class UserController {
|
|||||||
@AOPLog
|
@AOPLog
|
||||||
@JsonView(ResponseView.Public.class)
|
@JsonView(ResponseView.Public.class)
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
|
@CaptchaValid
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
public LoginResponse login(@RequestBody @Valid CaptchaData<LoginRequest> req) {
|
public LoginResponse login(@RequestBody @Valid CaptchaData<LoginRequest> req) {
|
||||||
return userLoginService.login(req.getData());
|
return userLoginService.login(req.getData());
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.user.entity;
|
|||||||
|
|
||||||
import com.imyeyu.api.annotation.MultilingualField;
|
import com.imyeyu.api.annotation.MultilingualField;
|
||||||
import com.imyeyu.api.bean.ModuleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.modules.user.bean.AuthOwnerType;
|
||||||
import com.imyeyu.spring.annotation.table.Transient;
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
import com.imyeyu.spring.entity.UUIDEntity;
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -29,6 +30,21 @@ public class Permission extends UUIDEntity {
|
|||||||
/** 说明 */
|
/** 说明 */
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
|
/// true 为系统内置权限
|
||||||
|
private Boolean builtin;
|
||||||
|
|
||||||
|
/// true 为受保护权限
|
||||||
|
private Boolean protectedPermission;
|
||||||
|
|
||||||
|
/// 归属类型
|
||||||
|
private AuthOwnerType ownerType;
|
||||||
|
|
||||||
|
/// 归属 ID
|
||||||
|
private String ownerId;
|
||||||
|
|
||||||
|
/// 创建人用户 ID
|
||||||
|
private String createdBy;
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
@MultilingualField("nameLangId")
|
@MultilingualField("nameLangId")
|
||||||
private String name;
|
private String name;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.user.entity;
|
|||||||
|
|
||||||
import com.imyeyu.api.annotation.MultilingualField;
|
import com.imyeyu.api.annotation.MultilingualField;
|
||||||
import com.imyeyu.api.bean.ModuleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.modules.user.bean.AuthOwnerType;
|
||||||
import com.imyeyu.spring.annotation.table.Transient;
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
import com.imyeyu.spring.entity.UUIDEntity;
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -32,6 +33,24 @@ public class Role extends UUIDEntity {
|
|||||||
/** 说明 */
|
/** 说明 */
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
|
/// true 为系统内置角色
|
||||||
|
private Boolean builtin;
|
||||||
|
|
||||||
|
/// true 为受保护角色
|
||||||
|
private Boolean protectedRole;
|
||||||
|
|
||||||
|
/// 管理父角色 ID
|
||||||
|
private String parentRoleId;
|
||||||
|
|
||||||
|
/// 归属类型
|
||||||
|
private AuthOwnerType ownerType;
|
||||||
|
|
||||||
|
/// 归属 ID
|
||||||
|
private String ownerId;
|
||||||
|
|
||||||
|
/// 创建人用户 ID
|
||||||
|
private String createdBy;
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
@MultilingualField("nameLangId")
|
@MultilingualField("nameLangId")
|
||||||
private String name;
|
private String name;
|
||||||
@@ -48,6 +67,14 @@ public class Role extends UUIDEntity {
|
|||||||
@Transient
|
@Transient
|
||||||
protected List<Role> childRoleList;
|
protected List<Role> childRoleList;
|
||||||
|
|
||||||
|
/// 管理父角色
|
||||||
|
@Transient
|
||||||
|
protected Role parentRole;
|
||||||
|
|
||||||
|
/// 角色允许继续转授的权限列表
|
||||||
|
@Transient
|
||||||
|
protected List<Permission> delegationPermissionList;
|
||||||
|
|
||||||
/** 权限 ID 列表 */
|
/** 权限 ID 列表 */
|
||||||
@Transient
|
@Transient
|
||||||
protected Set<String> permissionIdList;
|
protected Set<String> permissionIdList;
|
||||||
@@ -55,4 +82,8 @@ public class Role extends UUIDEntity {
|
|||||||
/** 子角色 ID 列表 */
|
/** 子角色 ID 列表 */
|
||||||
@Transient
|
@Transient
|
||||||
protected Set<String> childRoleIdList;
|
protected Set<String> childRoleIdList;
|
||||||
|
|
||||||
|
/// 转授权限 ID 列表
|
||||||
|
@Transient
|
||||||
|
protected Set<String> delegationPermissionIdList;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.imyeyu.api.modules.user.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.spring.annotation.table.AutoUUID;
|
||||||
|
import com.imyeyu.spring.annotation.table.DeleteColumn;
|
||||||
|
import com.imyeyu.spring.annotation.table.Id;
|
||||||
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
|
import com.imyeyu.spring.entity.Creatable;
|
||||||
|
import com.imyeyu.spring.entity.Deletable;
|
||||||
|
import com.imyeyu.spring.entity.IDEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/// 角色转授权限
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-08
|
||||||
|
@Data
|
||||||
|
public class RolePermissionDelegation implements IDEntity<String>, Creatable, Deletable {
|
||||||
|
|
||||||
|
/// 主键
|
||||||
|
@Id
|
||||||
|
@AutoUUID
|
||||||
|
protected String id;
|
||||||
|
|
||||||
|
/// 角色 ID
|
||||||
|
protected String roleId;
|
||||||
|
|
||||||
|
/// 权限 ID
|
||||||
|
protected String permissionId;
|
||||||
|
|
||||||
|
/// 创建人用户 ID
|
||||||
|
protected String createdBy;
|
||||||
|
|
||||||
|
/// 创建时间
|
||||||
|
protected Long createdAt;
|
||||||
|
|
||||||
|
/// 删除时间
|
||||||
|
@DeleteColumn
|
||||||
|
protected Long deletedAt;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
protected Role role;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
protected Permission permission;
|
||||||
|
}
|
||||||
+20
-12
@@ -1,5 +1,6 @@
|
|||||||
package com.imyeyu.api.modules.user.entity;
|
package com.imyeyu.api.modules.user.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.user.bean.AuthOwnerType;
|
||||||
import com.imyeyu.spring.annotation.table.AutoUUID;
|
import com.imyeyu.spring.annotation.table.AutoUUID;
|
||||||
import com.imyeyu.spring.annotation.table.DeleteColumn;
|
import com.imyeyu.spring.annotation.table.DeleteColumn;
|
||||||
import com.imyeyu.spring.annotation.table.Id;
|
import com.imyeyu.spring.annotation.table.Id;
|
||||||
@@ -8,30 +9,37 @@ import com.imyeyu.spring.entity.Deletable;
|
|||||||
import com.imyeyu.spring.entity.IDEntity;
|
import com.imyeyu.spring.entity.IDEntity;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/// 角色权限继承
|
||||||
* 角色关联
|
///
|
||||||
*
|
/// @author 夜雨
|
||||||
* @author 夜雨
|
/// @since 2026-05-13 14:40
|
||||||
* @since 2026-05-13 14:40
|
|
||||||
*/
|
|
||||||
@Data
|
@Data
|
||||||
public class RoleRelation implements IDEntity<String>, Creatable, Deletable {
|
public class RolePermissionInherit implements IDEntity<String>, Creatable, Deletable {
|
||||||
|
|
||||||
/** 主键 */
|
/// 主键
|
||||||
@Id
|
@Id
|
||||||
@AutoUUID
|
@AutoUUID
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
/** 上级角色 ID */
|
/// 继承方角色 ID
|
||||||
private String parentRoleId;
|
private String parentRoleId;
|
||||||
|
|
||||||
/** 子级角色 ID */
|
/// 被继承角色 ID
|
||||||
private String childRoleId;
|
private String childRoleId;
|
||||||
|
|
||||||
/** 创建时间 */
|
/// 归属类型
|
||||||
|
private AuthOwnerType ownerType;
|
||||||
|
|
||||||
|
/// 归属 ID
|
||||||
|
private String ownerId;
|
||||||
|
|
||||||
|
/// 创建人用户 ID
|
||||||
|
private String createdBy;
|
||||||
|
|
||||||
|
/// 创建时间
|
||||||
private Long createdAt;
|
private Long createdAt;
|
||||||
|
|
||||||
/** 删除时间 */
|
/// 删除时间
|
||||||
@DeleteColumn
|
@DeleteColumn
|
||||||
private Long deletedAt;
|
private Long deletedAt;
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,8 @@ package com.imyeyu.api.modules.user.mapper;
|
|||||||
|
|
||||||
import com.imyeyu.api.modules.user.entity.Permission;
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -13,5 +15,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface PermissionMapper extends BaseMapper<Permission, String> {
|
public interface PermissionMapper extends BaseMapper<Permission, String> {
|
||||||
|
|
||||||
List<Permission> selectByIdList(List<String> list);
|
List<Permission> selectByIdList(@Param("idList") List<String> idList);
|
||||||
|
|
||||||
|
List<Permission> selectByModuleCode(@Param("moduleCode") ModuleCode moduleCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.imyeyu.api.modules.user.mapper;
|
|||||||
import com.imyeyu.api.bean.ModuleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.api.modules.user.entity.Role;
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -15,7 +16,13 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface RoleMapper extends BaseMapper<Role, String> {
|
public interface RoleMapper extends BaseMapper<Role, String> {
|
||||||
|
|
||||||
List<Role> selectByIdList(Collection<String> idList);
|
Role selectById(String id);
|
||||||
|
|
||||||
List<Role> selectByModuleCode(ModuleCode moduleCode);
|
List<Role> selectByIdList(@Param("idList") Collection<String> idList);
|
||||||
|
|
||||||
|
List<Role> selectByModuleCode(@Param("moduleCode") ModuleCode moduleCode);
|
||||||
|
|
||||||
|
List<Role> selectByParentRoleId(@Param("parentRoleId") String parentRoleId);
|
||||||
|
|
||||||
|
List<Role> selectAll();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.imyeyu.api.modules.user.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
|
import com.imyeyu.api.modules.user.entity.RolePermissionDelegation;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 角色转授权限 Mapper
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-08
|
||||||
|
public interface RolePermissionDelegationMapper extends BaseMapper<RolePermissionDelegation, String> {
|
||||||
|
|
||||||
|
default List<RolePermissionDelegation> selectByRoleId(String roleId) {
|
||||||
|
return selectByRoleIdList(List.of(roleId));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RolePermissionDelegation> selectByRoleIdList(@Param("roleIdList") List<String> roleIdList);
|
||||||
|
|
||||||
|
List<Permission> selectPermissionByRoleIdList(@Param("roleIdList") List<String> roleIdList);
|
||||||
|
|
||||||
|
void createBatch(@Param("rolePermissionDelegationList") List<RolePermissionDelegation> rolePermissionDelegationList);
|
||||||
|
|
||||||
|
void deleteByPermissionIdList(@Param("roleId") String roleId, @Param("permissionIdList") List<String> permissionIdList);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.imyeyu.api.modules.user.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.user.bean.AuthOwnerType;
|
||||||
|
import com.imyeyu.api.modules.user.entity.RolePermissionInherit;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 角色权限继承 Mapper
|
||||||
|
///
|
||||||
|
/// @author 夜雨
|
||||||
|
/// @since 2026-05-13 14:46
|
||||||
|
public interface RolePermissionInheritMapper extends BaseMapper<RolePermissionInherit, String> {
|
||||||
|
|
||||||
|
List<RolePermissionInherit> selectByParentRoleId(@Param("parentRoleId") String parentRoleId);
|
||||||
|
|
||||||
|
List<String> selectChildRoleIdListByParentRoleId(@Param("parentRoleId") String parentRoleId);
|
||||||
|
|
||||||
|
List<RolePermissionInherit> selectByParentRoleIdList(@Param("parentRoleIdList") List<String> parentRoleIdList);
|
||||||
|
|
||||||
|
List<String> selectChildRoleIdListByParentRoleIdList(@Param("parentRoleIdList") List<String> parentRoleIdList);
|
||||||
|
|
||||||
|
void createBatch(@Param("rolePermissionInheritList") List<RolePermissionInherit> rolePermissionInheritList);
|
||||||
|
|
||||||
|
void deleteByChildRoleIdList(
|
||||||
|
@Param("parentRoleId") String parentRoleId,
|
||||||
|
@Param("childRoleIdList") List<String> childRoleIdList,
|
||||||
|
@Param("ownerType") AuthOwnerType ownerType,
|
||||||
|
@Param("ownerId") String ownerId
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.imyeyu.api.modules.user.mapper;
|
|||||||
import com.imyeyu.api.modules.user.entity.Permission;
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
import com.imyeyu.api.modules.user.entity.RolePermission;
|
import com.imyeyu.api.modules.user.entity.RolePermission;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -18,11 +19,11 @@ public interface RolePermissionMapper extends BaseMapper<RolePermission, String>
|
|||||||
return selectByRoleIdList(List.of(roleId));
|
return selectByRoleIdList(List.of(roleId));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<RolePermission> selectByRoleIdList(List<String> roleIdList);
|
List<RolePermission> selectByRoleIdList(@Param("roleIdList") List<String> roleIdList);
|
||||||
|
|
||||||
List<Permission> selectPermissionByRoleIdList(List<String> roleIdList);
|
List<Permission> selectPermissionByRoleIdList(@Param("roleIdList") List<String> roleIdList);
|
||||||
|
|
||||||
void createBatch(List<RolePermission> rolePermissionList);
|
void createBatch(@Param("rolePermissionList") List<RolePermission> rolePermissionList);
|
||||||
|
|
||||||
void deleteByPermissionIdList(String roleId, List<String> permissionIdList);
|
void deleteByPermissionIdList(@Param("roleId") String roleId, @Param("permissionIdList") List<String> permissionIdList);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.user.mapper;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.user.entity.RoleRelation;
|
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色关联 Mapper
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2026-05-13 14:46
|
|
||||||
*/
|
|
||||||
public interface RoleRelationMapper extends BaseMapper<RoleRelation, String> {
|
|
||||||
|
|
||||||
List<RoleRelation> selectByParentRoleId(String parentRoleId);
|
|
||||||
|
|
||||||
List<String> selectChildRoleIdListByParentRoleId(String parentRoleId);
|
|
||||||
|
|
||||||
List<String> selectChildRoleIdListByParentRoleIdList(List<String> parentRoleIdList);
|
|
||||||
|
|
||||||
void createBatch(List<RoleRelation> roleRelationList);
|
|
||||||
|
|
||||||
void deleteByChildRoleIdList(String parentRoleId, List<String> childRoleIdList);
|
|
||||||
}
|
|
||||||
@@ -19,5 +19,5 @@ public interface UserRoleMapper extends BaseMapper<UserRole, String> {
|
|||||||
|
|
||||||
List<UserRole> selectByUserIdList(@Param("userIdList") Collection<String> userIdList);
|
List<UserRole> selectByUserIdList(@Param("userIdList") Collection<String> userIdList);
|
||||||
|
|
||||||
void deleteByRoleIdList(String userId, List<String> roleIdList);
|
void deleteByRoleIdList(@Param("userId") String userId, @Param("roleIdList") List<String> roleIdList);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.imyeyu.api.modules.user.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/// 授权边界服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-07
|
||||||
|
public interface AuthorizationBoundaryService {
|
||||||
|
|
||||||
|
/// 校验是否可以授予用户角色
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param targetUserId 目标用户 ID
|
||||||
|
/// @param roleIdList 角色 ID 列表
|
||||||
|
void checkCanGrantRoles(String operatorUserId, String targetUserId, Collection<String> roleIdList);
|
||||||
|
|
||||||
|
/// 校验是否可以移除用户角色
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param targetUserId 目标用户 ID
|
||||||
|
/// @param roleIdList 角色 ID 列表
|
||||||
|
void checkCanRevokeRoles(String operatorUserId, String targetUserId, Collection<String> roleIdList);
|
||||||
|
|
||||||
|
/// 校验是否可以创建角色
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param role 角色
|
||||||
|
void checkCanCreateRole(String operatorUserId, Role role);
|
||||||
|
|
||||||
|
/// 校验是否可以修改角色基础信息
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param role 角色
|
||||||
|
void checkCanUpdateRole(String operatorUserId, Role role);
|
||||||
|
|
||||||
|
/// 校验是否可以删除角色
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param roleId 角色 ID
|
||||||
|
void checkCanDeleteRole(String operatorUserId, String roleId);
|
||||||
|
|
||||||
|
/// 校验是否可以授予角色权限
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param roleId 角色 ID
|
||||||
|
/// @param permissionIdList 权限 ID 列表
|
||||||
|
void checkCanGrantPermissions(String operatorUserId, String roleId, Collection<String> permissionIdList);
|
||||||
|
|
||||||
|
/// 校验是否可以移除角色权限
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param roleId 角色 ID
|
||||||
|
/// @param permissionIdList 权限 ID 列表
|
||||||
|
void checkCanRevokePermissions(String operatorUserId, String roleId, Collection<String> permissionIdList);
|
||||||
|
|
||||||
|
/// 校验是否可以修改角色转授权限
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param roleId 角色 ID
|
||||||
|
/// @param permissionIdList 转授权限 ID 列表
|
||||||
|
void checkCanUpdateRoleDelegation(String operatorUserId, String roleId, Collection<String> permissionIdList);
|
||||||
|
|
||||||
|
/// 校验是否可以更新角色管理父级
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param roleId 角色 ID
|
||||||
|
/// @param parentRoleId 管理父角色 ID
|
||||||
|
void checkCanUpdateRoleParent(String operatorUserId, String roleId, String parentRoleId);
|
||||||
|
|
||||||
|
/// 校验是否可以更新角色继承关系
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param parentRoleId 父角色 ID
|
||||||
|
/// @param childRoleIdList 子角色 ID 列表
|
||||||
|
void checkCanUpdateRolePermissionInherits(String operatorUserId, String parentRoleId, Collection<String> childRoleIdList);
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.imyeyu.api.modules.user.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 授权范围服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-07
|
||||||
|
public interface AuthorizationScopeService {
|
||||||
|
|
||||||
|
/// 查询可授权角色
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param moduleCode 模块代码
|
||||||
|
/// @return 角色列表
|
||||||
|
List<Role> listGrantableRole(String operatorUserId, ModuleCode moduleCode);
|
||||||
|
|
||||||
|
/// 查询可授权权限
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param moduleCode 模块代码
|
||||||
|
/// @return 权限列表
|
||||||
|
List<Permission> listGrantablePermission(String operatorUserId, ModuleCode moduleCode);
|
||||||
|
|
||||||
|
/// 查询可管理角色
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param moduleCode 模块代码
|
||||||
|
/// @return 角色列表
|
||||||
|
List<Role> listManageableRole(String operatorUserId, ModuleCode moduleCode);
|
||||||
|
|
||||||
|
/// 判断是否为系统管理员
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @return true 为系统管理员
|
||||||
|
boolean isSystemAdmin(String operatorUserId);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.imyeyu.api.modules.user.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.modules.user.bean.AuthOwnerType;
|
||||||
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
|
|
||||||
|
/// 模块资源范围解析器
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-07
|
||||||
|
public interface ModuleResourceScopeResolver {
|
||||||
|
|
||||||
|
/// 模块代码
|
||||||
|
///
|
||||||
|
/// @return 模块代码
|
||||||
|
ModuleCode moduleCode();
|
||||||
|
|
||||||
|
/// 判断是否可以管理目标用户
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param targetUserId 目标用户 ID
|
||||||
|
/// @return true 为可以管理
|
||||||
|
boolean canManageUser(String operatorUserId, String targetUserId);
|
||||||
|
|
||||||
|
/// 判断是否可以管理角色
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param role 角色
|
||||||
|
/// @return true 为可以管理
|
||||||
|
boolean canManageRole(String operatorUserId, Role role);
|
||||||
|
|
||||||
|
/// 判断是否可以使用指定归属范围
|
||||||
|
///
|
||||||
|
/// @param operatorUserId 操作者用户 ID
|
||||||
|
/// @param ownerType 归属类型
|
||||||
|
/// @param ownerId 归属 ID
|
||||||
|
/// @return true 为可以使用
|
||||||
|
boolean canUseOwnerScope(String operatorUserId, AuthOwnerType ownerType, String ownerId);
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.user.service;
|
|||||||
|
|
||||||
import com.imyeyu.api.modules.user.entity.Permission;
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
import com.imyeyu.api.modules.user.vo.permission.BatchCreateReq;
|
import com.imyeyu.api.modules.user.vo.permission.BatchCreateReq;
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.spring.service.BaseService;
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -30,4 +31,10 @@ public interface PermissionService extends BaseService<Permission, String> {
|
|||||||
/// @param roleIdList 角色 ID 列表
|
/// @param roleIdList 角色 ID 列表
|
||||||
/// @return 权限列表
|
/// @return 权限列表
|
||||||
List<Permission> listByRoleId(List<String> roleIdList);
|
List<Permission> listByRoleId(List<String> roleIdList);
|
||||||
|
|
||||||
|
/// 查询模块权限
|
||||||
|
///
|
||||||
|
/// @param moduleCode 模块代码
|
||||||
|
/// @return 权限列表
|
||||||
|
List<Permission> listByModuleCode(ModuleCode moduleCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.imyeyu.api.modules.user.service;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 角色继承服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-07
|
||||||
|
public interface RoleHierarchyService {
|
||||||
|
|
||||||
|
/// 递归查询角色及其系统、模块作用域内的所有子角色 ID
|
||||||
|
///
|
||||||
|
/// 此方法不依赖用户资源范围,不会展开门店、用户等需要操作者上下文的作用域关系
|
||||||
|
///
|
||||||
|
/// @param roleIdList 角色 ID 列表
|
||||||
|
/// @return 角色 ID 列表
|
||||||
|
List<String> listAllRoleIdIncludingChildren(Collection<String> roleIdList);
|
||||||
|
|
||||||
|
/// 递归查询用户作用域内的角色及其所有子角色 ID
|
||||||
|
///
|
||||||
|
/// @param userId 用户 ID
|
||||||
|
/// @param roleIdList 角色 ID 列表
|
||||||
|
/// @return 角色 ID 列表
|
||||||
|
List<String> listAllRoleIdIncludingChildren(String userId, Collection<String> roleIdList);
|
||||||
|
|
||||||
|
/// 校验角色继承关系不存在环
|
||||||
|
///
|
||||||
|
/// @param parentRoleId 父角色 ID
|
||||||
|
/// @param childRoleIdList 子角色 ID 列表
|
||||||
|
void checkNoCycle(String parentRoleId, Collection<String> childRoleIdList);
|
||||||
|
|
||||||
|
/// 判断目标角色是否为子孙角色
|
||||||
|
///
|
||||||
|
/// @param parentRoleId 父角色 ID
|
||||||
|
/// @param childRoleId 子角色 ID
|
||||||
|
/// @return true 为子孙角色
|
||||||
|
boolean isDescendant(String parentRoleId, String childRoleId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.imyeyu.api.modules.user.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.user.entity.RolePermissionInherit;
|
||||||
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 角色权限继承服务
|
||||||
|
///
|
||||||
|
/// @author 夜雨
|
||||||
|
/// @since 2026-05-13 14:46
|
||||||
|
public interface RolePermissionInheritService extends BaseService<RolePermissionInherit, String> {
|
||||||
|
|
||||||
|
/// 查询直属权限继承
|
||||||
|
///
|
||||||
|
/// @param parentRoleId 继承方角色 ID
|
||||||
|
/// @return 权限继承列表
|
||||||
|
List<RolePermissionInherit> listByParentRoleId(String parentRoleId);
|
||||||
|
|
||||||
|
/// 查询直属被继承角色 ID 列表
|
||||||
|
///
|
||||||
|
/// @param parentRoleId 继承方角色 ID
|
||||||
|
/// @return 被继承角色 ID 列表
|
||||||
|
List<String> listChildRoleIdByParentRoleId(String parentRoleId);
|
||||||
|
|
||||||
|
/// 递归展开全部被继承角色 ID
|
||||||
|
///
|
||||||
|
/// @param parentRoleIdList 继承方角色 ID 列表
|
||||||
|
/// @return 全部被继承角色 ID 列表
|
||||||
|
List<String> listAllChildRoleIdByParentRoleIdList(List<String> parentRoleIdList);
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.user.service;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.user.entity.RoleRelation;
|
|
||||||
import com.imyeyu.spring.service.BaseService;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/// 角色继承关系服务
|
|
||||||
///
|
|
||||||
/// @author 夜雨
|
|
||||||
/// @since 2026-05-13 14:46
|
|
||||||
public interface RoleRelationService extends BaseService<RoleRelation, String> {
|
|
||||||
|
|
||||||
/// 查询直属子角色关联
|
|
||||||
///
|
|
||||||
/// @param parentRoleId 父角色 ID
|
|
||||||
/// @return 角色关联列表
|
|
||||||
List<RoleRelation> listByParentRoleId(String parentRoleId);
|
|
||||||
|
|
||||||
/// 查询直属子角色 ID 列表
|
|
||||||
///
|
|
||||||
/// @param parentRoleId 父角色 ID
|
|
||||||
/// @return 子角色 ID 列表
|
|
||||||
List<String> listChildRoleIdByParentRoleId(String parentRoleId);
|
|
||||||
|
|
||||||
/// 递归展开全部子角色 ID
|
|
||||||
///
|
|
||||||
/// @param parentRoleIdList 父角色 ID 列表
|
|
||||||
/// @return 全部子角色 ID 列表
|
|
||||||
List<String> listAllChildRoleIdByParentRoleIdList(List<String> parentRoleIdList);
|
|
||||||
}
|
|
||||||
@@ -22,6 +22,18 @@ public interface RoleService extends BaseService<Role, String> {
|
|||||||
|
|
||||||
List<Role> listByIdList(Collection<String> idList);
|
List<Role> listByIdList(Collection<String> idList);
|
||||||
|
|
||||||
|
/// 查询当前用户可管理的模块角色
|
||||||
|
///
|
||||||
|
/// @param moduleCode 模块代码
|
||||||
|
/// @return 角色列表
|
||||||
|
List<Role> listManageableRole(ModuleCode moduleCode);
|
||||||
|
|
||||||
|
/// 查询当前用户可授权给账号的模块角色
|
||||||
|
///
|
||||||
|
/// @param moduleCode 模块代码
|
||||||
|
/// @return 角色列表
|
||||||
|
List<Role> listGrantableRole(ModuleCode moduleCode);
|
||||||
|
|
||||||
/// 查询直属子角色
|
/// 查询直属子角色
|
||||||
///
|
///
|
||||||
/// @param parentRoleId 父角色 ID
|
/// @param parentRoleId 父角色 ID
|
||||||
@@ -34,6 +46,40 @@ public interface RoleService extends BaseService<Role, String> {
|
|||||||
/// @return 权限列表
|
/// @return 权限列表
|
||||||
List<Permission> listPermissionByRoleId(String roleId);
|
List<Permission> listPermissionByRoleId(String roleId);
|
||||||
|
|
||||||
|
/// 查询可配置给目标角色的直接权限
|
||||||
|
///
|
||||||
|
/// @param roleId 角色 ID
|
||||||
|
/// @return 权限列表
|
||||||
|
List<Permission> listGrantableRolePermission(String roleId);
|
||||||
|
|
||||||
|
/// 查询角色转授权限
|
||||||
|
///
|
||||||
|
/// @param roleId 角色 ID
|
||||||
|
/// @return 权限列表
|
||||||
|
List<Permission> listDelegationPermissionByRoleId(String roleId);
|
||||||
|
|
||||||
|
/// 查询可配置给目标角色的转授权限
|
||||||
|
///
|
||||||
|
/// @param roleId 角色 ID
|
||||||
|
/// @return 权限列表
|
||||||
|
List<Permission> listGrantableDelegationPermission(String roleId);
|
||||||
|
|
||||||
|
/// 修改角色转授权限
|
||||||
|
///
|
||||||
|
/// @param role 角色
|
||||||
|
void updateRoleDelegation(Role role);
|
||||||
|
|
||||||
|
/// 查询可配置给目标角色的管理父级角色
|
||||||
|
///
|
||||||
|
/// @param roleId 角色 ID
|
||||||
|
/// @return 角色列表
|
||||||
|
List<Role> listGrantableParentRole(String roleId);
|
||||||
|
|
||||||
|
/// 修改角色管理父级
|
||||||
|
///
|
||||||
|
/// @param role 角色
|
||||||
|
void updateRoleParent(Role role);
|
||||||
|
|
||||||
/// 递归获取角色及其所有子角色的 ID 列表
|
/// 递归获取角色及其所有子角色的 ID 列表
|
||||||
///
|
///
|
||||||
/// @param idList 角色 ID 列表
|
/// @param idList 角色 ID 列表
|
||||||
|
|||||||
+376
@@ -0,0 +1,376 @@
|
|||||||
|
package com.imyeyu.api.modules.user.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.CorePermissionCode;
|
||||||
|
import com.imyeyu.api.bean.CoreRoleCode;
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.modules.user.bean.AuthOwnerType;
|
||||||
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
|
import com.imyeyu.api.modules.user.entity.UserRole;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.PermissionMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RolePermissionDelegationMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RolePermissionMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.UserRoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.service.AuthorizationBoundaryService;
|
||||||
|
import com.imyeyu.api.modules.user.service.AuthorizationScopeService;
|
||||||
|
import com.imyeyu.api.modules.user.service.ModuleResourceScopeResolver;
|
||||||
|
import com.imyeyu.api.modules.user.service.RoleHierarchyService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/// 授权边界服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-07
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthorizationBoundaryServiceImplement implements AuthorizationBoundaryService {
|
||||||
|
|
||||||
|
private final RoleMapper roleMapper;
|
||||||
|
private final UserRoleMapper userRoleMapper;
|
||||||
|
private final PermissionMapper permissionMapper;
|
||||||
|
private final RolePermissionMapper rolePermissionMapper;
|
||||||
|
private final RolePermissionDelegationMapper rolePermissionDelegationMapper;
|
||||||
|
private final AuthorizationScopeService authorizationScopeService;
|
||||||
|
private final RoleHierarchyService roleHierarchyService;
|
||||||
|
private final List<ModuleResourceScopeResolver> moduleResourceScopeResolverList;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanGrantRoles(String operatorUserId, String targetUserId, Collection<String> roleIdList) {
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkCorePermission(operatorUserId, CorePermissionCode.USER_ROLE_CREATE);
|
||||||
|
if (authorizationScopeService.isSystemAdmin(operatorUserId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Role> roleList = roleMapper.selectByIdList(roleIdList);
|
||||||
|
checkRoleIdListComplete(roleIdList, roleList);
|
||||||
|
for (Role role : roleList) {
|
||||||
|
checkCanManageUserInModule(operatorUserId, targetUserId, role.getModuleCode());
|
||||||
|
checkGrantableRole(operatorUserId, role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanRevokeRoles(String operatorUserId, String targetUserId, Collection<String> roleIdList) {
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkCorePermission(operatorUserId, CorePermissionCode.USER_ROLE_DELETE);
|
||||||
|
if (authorizationScopeService.isSystemAdmin(operatorUserId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Role> roleList = roleMapper.selectByIdList(roleIdList);
|
||||||
|
checkRoleIdListComplete(roleIdList, roleList);
|
||||||
|
for (Role role : roleList) {
|
||||||
|
checkCanManageUserInModule(operatorUserId, targetUserId, role.getModuleCode());
|
||||||
|
checkManageableRole(operatorUserId, role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanCreateRole(String operatorUserId, Role role) {
|
||||||
|
checkCorePermission(operatorUserId, CorePermissionCode.ROLE_CREATE);
|
||||||
|
if (authorizationScopeService.isSystemAdmin(operatorUserId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TimiException.required(role.getModuleCode(), "not found role.moduleCode");
|
||||||
|
TimiException.required(role.getOwnerType(), "not found role.ownerType");
|
||||||
|
TimiException.requiredTrue(!Boolean.TRUE.equals(role.getBuiltin()), "不能创建内置角色");
|
||||||
|
TimiException.requiredTrue(!Boolean.TRUE.equals(role.getProtectedRole()), "不能创建受保护角色");
|
||||||
|
checkOwnerScope(operatorUserId, role.getModuleCode(), role.getOwnerType(), role.getOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanUpdateRole(String operatorUserId, Role role) {
|
||||||
|
checkCorePermission(operatorUserId, CorePermissionCode.ROLE_UPDATE);
|
||||||
|
Role dbRole = roleMapper.selectById(role.getId());
|
||||||
|
TimiException.required(dbRole, "not found role");
|
||||||
|
if (authorizationScopeService.isSystemAdmin(operatorUserId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkManageableRole(operatorUserId, dbRole);
|
||||||
|
TimiException.requiredTrue(!Boolean.TRUE.equals(role.getBuiltin()), "不能设置内置角色");
|
||||||
|
TimiException.requiredTrue(!Boolean.TRUE.equals(role.getProtectedRole()), "不能设置受保护角色");
|
||||||
|
if (role.getModuleCode() != null) {
|
||||||
|
TimiException.requiredTrue(role.getModuleCode() == dbRole.getModuleCode(), "不能修改角色模块");
|
||||||
|
}
|
||||||
|
if (role.getOwnerType() != null) {
|
||||||
|
checkOwnerScope(operatorUserId, dbRole.getModuleCode(), role.getOwnerType(), role.getOwnerId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanDeleteRole(String operatorUserId, String roleId) {
|
||||||
|
checkCorePermission(operatorUserId, CorePermissionCode.ROLE_DELETE);
|
||||||
|
Role role = roleMapper.selectById(roleId);
|
||||||
|
TimiException.required(role, "not found role");
|
||||||
|
if (authorizationScopeService.isSystemAdmin(operatorUserId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkManageableRole(operatorUserId, role);
|
||||||
|
TimiException.requiredTrue(!Boolean.TRUE.equals(role.getBuiltin()), "不能删除内置角色");
|
||||||
|
TimiException.requiredTrue(!Boolean.TRUE.equals(role.getProtectedRole()), "不能删除受保护角色");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanGrantPermissions(String operatorUserId, String roleId, Collection<String> permissionIdList) {
|
||||||
|
if (TimiJava.isEmpty(permissionIdList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkCorePermission(operatorUserId, CorePermissionCode.ROLE_PERMISSION_CREATE);
|
||||||
|
Role role = roleMapper.selectById(roleId);
|
||||||
|
TimiException.required(role, "not found role");
|
||||||
|
boolean systemAdmin = authorizationScopeService.isSystemAdmin(operatorUserId);
|
||||||
|
if (!systemAdmin) {
|
||||||
|
checkManageableRole(operatorUserId, role);
|
||||||
|
}
|
||||||
|
List<Permission> permissionList = permissionMapper.selectByIdList(permissionIdList.stream().toList());
|
||||||
|
checkPermissionIdListComplete(permissionIdList, permissionList);
|
||||||
|
Set<String> grantablePermissionIdSet = listRoleDirectGrantablePermissionId(operatorUserId, role);
|
||||||
|
for (Permission permission : permissionList) {
|
||||||
|
TimiException.requiredTrue(grantablePermissionIdSet.contains(permission.getId()), "权限不在上级下放边界内:%s".formatted(permission.getCode()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanRevokePermissions(String operatorUserId, String roleId, Collection<String> permissionIdList) {
|
||||||
|
if (TimiJava.isEmpty(permissionIdList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkCorePermission(operatorUserId, CorePermissionCode.ROLE_PERMISSION_DELETE);
|
||||||
|
Role role = roleMapper.selectById(roleId);
|
||||||
|
TimiException.required(role, "not found role");
|
||||||
|
if (authorizationScopeService.isSystemAdmin(operatorUserId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkManageableRole(operatorUserId, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanUpdateRoleDelegation(String operatorUserId, String roleId, Collection<String> permissionIdList) {
|
||||||
|
checkCorePermission(operatorUserId, CorePermissionCode.ROLE_PERMISSION_CREATE);
|
||||||
|
Role role = roleMapper.selectById(roleId);
|
||||||
|
TimiException.required(role, "not found role");
|
||||||
|
boolean systemAdmin = authorizationScopeService.isSystemAdmin(operatorUserId);
|
||||||
|
if (!systemAdmin) {
|
||||||
|
checkManageableRole(operatorUserId, role);
|
||||||
|
}
|
||||||
|
if (TimiJava.isEmpty(permissionIdList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Permission> permissionList = permissionMapper.selectByIdList(permissionIdList.stream().toList());
|
||||||
|
checkPermissionIdListComplete(permissionIdList, permissionList);
|
||||||
|
boolean hasParentRole = TimiJava.isNotEmpty(role.getParentRoleId());
|
||||||
|
Set<String> parentDelegationPermissionIdSet = listParentDelegationPermissionId(roleId);
|
||||||
|
if (!isCoreDelegationRoot(role)) {
|
||||||
|
TimiException.requiredTrue(hasParentRole, "目标角色缺少管理父级");
|
||||||
|
}
|
||||||
|
Set<String> operatorGrantablePermissionIdSet = listGrantablePermissionId(operatorUserId, permissionList);
|
||||||
|
for (Permission permission : permissionList) {
|
||||||
|
TimiException.requiredTrue(role.getModuleCode() == permission.getModuleCode() || isCoreDelegationRoot(role) || isCoreAuthorizationPermission(permission), "不能配置其他模块转授权限");
|
||||||
|
if (hasParentRole) {
|
||||||
|
TimiException.requiredTrue(parentDelegationPermissionIdSet.contains(permission.getId()), "转授权限超过上级边界:%s".formatted(permission.getCode()));
|
||||||
|
}
|
||||||
|
if (!systemAdmin) {
|
||||||
|
TimiException.requiredTrue(operatorGrantablePermissionIdSet.contains(permission.getId()), "权限不在可转授范围内:%s".formatted(permission.getCode()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanUpdateRoleParent(String operatorUserId, String roleId, String parentRoleId) {
|
||||||
|
checkCorePermission(operatorUserId, CorePermissionCode.ROLE_UPDATE);
|
||||||
|
Role targetRole = roleMapper.selectById(roleId);
|
||||||
|
TimiException.required(targetRole, "not found role");
|
||||||
|
boolean systemAdmin = authorizationScopeService.isSystemAdmin(operatorUserId);
|
||||||
|
if (!systemAdmin) {
|
||||||
|
checkManageableRole(operatorUserId, targetRole);
|
||||||
|
}
|
||||||
|
if (TimiJava.isEmpty(parentRoleId)) {
|
||||||
|
TimiException.requiredTrue(isCoreDelegationRoot(targetRole), "非根角色必须设置管理父级");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Role parentRole = roleMapper.selectById(parentRoleId);
|
||||||
|
TimiException.required(parentRole, "not found parent role");
|
||||||
|
TimiException.requiredTrue(!roleId.equals(parentRoleId), "管理父级不能指向自身");
|
||||||
|
TimiException.requiredTrue(!isManagementDescendant(roleId, parentRoleId), "管理父级不能指向自己的下级");
|
||||||
|
if (!systemAdmin && !isOperatorRole(operatorUserId, parentRole)) {
|
||||||
|
checkManageableRole(operatorUserId, parentRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanUpdateRolePermissionInherits(String operatorUserId, String parentRoleId, Collection<String> childRoleIdList) {
|
||||||
|
if (TimiJava.isEmpty(childRoleIdList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkCorePermission(operatorUserId, CorePermissionCode.ROLE_PERMISSION_INHERIT_CREATE);
|
||||||
|
Role parentRole = roleMapper.selectById(parentRoleId);
|
||||||
|
TimiException.required(parentRole, "not found parent role");
|
||||||
|
roleHierarchyService.checkNoCycle(parentRoleId, childRoleIdList);
|
||||||
|
if (authorizationScopeService.isSystemAdmin(operatorUserId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isOperatorRole(operatorUserId, parentRole)) {
|
||||||
|
checkManageableRole(operatorUserId, parentRole);
|
||||||
|
}
|
||||||
|
List<Role> childRoleList = roleMapper.selectByIdList(childRoleIdList);
|
||||||
|
checkRoleIdListComplete(childRoleIdList, childRoleList);
|
||||||
|
for (Role childRole : childRoleList) {
|
||||||
|
TimiException.requiredTrue(parentRole.getModuleCode() == childRole.getModuleCode(), "不能继承其他模块角色");
|
||||||
|
checkManageableRole(operatorUserId, childRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkGrantableRole(String operatorUserId, Role role) {
|
||||||
|
Set<String> grantableRoleIdSet = authorizationScopeService.listGrantableRole(operatorUserId, role.getModuleCode())
|
||||||
|
.stream()
|
||||||
|
.map(Role::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
TimiException.requiredTrue(grantableRoleIdSet.contains(role.getId()), "角色不在可授权范围内:%s".formatted(role.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkManageableRole(String operatorUserId, Role role) {
|
||||||
|
Set<String> manageableRoleIdSet = authorizationScopeService.listManageableRole(operatorUserId, role.getModuleCode())
|
||||||
|
.stream()
|
||||||
|
.map(Role::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
TimiException.requiredTrue(manageableRoleIdSet.contains(role.getId()), "角色不在可管理范围内:%s".formatted(role.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listRoleAllPermissionId(String operatorUserId, String roleId) {
|
||||||
|
List<String> allRoleIdList = roleHierarchyService.listAllRoleIdIncludingChildren(operatorUserId, List.of(roleId));
|
||||||
|
return rolePermissionMapper.selectPermissionByRoleIdList(allRoleIdList)
|
||||||
|
.stream()
|
||||||
|
.map(Permission::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listGrantablePermissionId(String operatorUserId, List<Permission> permissionList) {
|
||||||
|
if (TimiJava.isEmpty(permissionList)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return permissionList.stream()
|
||||||
|
.map(Permission::getModuleCode)
|
||||||
|
.distinct()
|
||||||
|
.flatMap(moduleCode -> authorizationScopeService.listGrantablePermission(operatorUserId, moduleCode).stream())
|
||||||
|
.map(Permission::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listRoleDirectGrantablePermissionId(String operatorUserId, Role role) {
|
||||||
|
boolean systemAdmin = authorizationScopeService.isSystemAdmin(operatorUserId);
|
||||||
|
if (systemAdmin && isCoreDelegationRoot(role) && TimiJava.isEmpty(role.getParentRoleId())) {
|
||||||
|
return permissionMapper.selectAll().stream().map(Permission::getId).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
TimiException.requiredTrue(TimiJava.isNotEmpty(role.getParentRoleId()), "目标角色缺少管理父级");
|
||||||
|
Set<String> parentDelegationPermissionIdSet = listParentDelegationPermissionId(role.getId());
|
||||||
|
if (systemAdmin) {
|
||||||
|
return parentDelegationPermissionIdSet;
|
||||||
|
}
|
||||||
|
List<Permission> parentDelegationPermissionList = TimiJava.isEmpty(parentDelegationPermissionIdSet)
|
||||||
|
? List.of()
|
||||||
|
: permissionMapper.selectByIdList(parentDelegationPermissionIdSet.stream().toList());
|
||||||
|
Set<String> operatorGrantablePermissionIdSet = listGrantablePermissionId(operatorUserId, parentDelegationPermissionList);
|
||||||
|
return parentDelegationPermissionIdSet.stream()
|
||||||
|
.filter(operatorGrantablePermissionIdSet::contains)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listParentDelegationPermissionId(String roleId) {
|
||||||
|
Role role = roleMapper.selectById(roleId);
|
||||||
|
if (role == null || TimiJava.isEmpty(role.getParentRoleId())) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return rolePermissionDelegationMapper.selectPermissionByRoleIdList(List.of(role.getParentRoleId()))
|
||||||
|
.stream()
|
||||||
|
.map(Permission::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOperatorRole(String operatorUserId, Role role) {
|
||||||
|
List<String> roleIdList = userRoleMapper.selectByUserIdList(List.of(operatorUserId)).stream().map(UserRole::getRoleId).toList();
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return roleHierarchyService.listAllRoleIdIncludingChildren(operatorUserId, roleIdList).contains(role.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCoreDelegationRoot(Role role) {
|
||||||
|
return role != null
|
||||||
|
&& ModuleCode.CORE == role.getModuleCode()
|
||||||
|
&& (CoreRoleCode.SYSTEM.name().equals(role.getCode()) || CoreRoleCode.ADMIN.name().equals(role.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isManagementDescendant(String parentRoleId, String childRoleId) {
|
||||||
|
List<Role> childRoleList = roleMapper.selectByParentRoleId(parentRoleId);
|
||||||
|
for (Role childRole : childRoleList) {
|
||||||
|
if (childRoleId.equals(childRole.getId()) || isManagementDescendant(childRole.getId(), childRoleId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCoreAuthorizationPermission(Permission permission) {
|
||||||
|
return permission != null && ModuleCode.CORE == permission.getModuleCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkCanManageUserInModule(String operatorUserId, String targetUserId, ModuleCode moduleCode) {
|
||||||
|
ModuleResourceScopeResolver resolver = getResolver(moduleCode);
|
||||||
|
TimiException.requiredTrue(resolver == null || resolver.canManageUser(operatorUserId, targetUserId), "目标用户不在可管理范围内");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkOwnerScope(String operatorUserId, ModuleCode moduleCode, AuthOwnerType ownerType, String ownerId) {
|
||||||
|
ModuleResourceScopeResolver resolver = getResolver(moduleCode);
|
||||||
|
TimiException.requiredTrue(resolver == null || resolver.canUseOwnerScope(operatorUserId, ownerType, ownerId), "归属范围不在可管理范围内");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkCorePermission(String operatorUserId, CorePermissionCode permissionCode) {
|
||||||
|
TimiException.requiredTrue(hasPermission(operatorUserId, ModuleCode.CORE, permissionCode.getValue()), "权限不足:%s".formatted(permissionCode.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPermission(String userId, ModuleCode moduleCode, String permissionCode) {
|
||||||
|
List<String> roleIdList = userRoleMapper.selectByUserIdList(List.of(userId)).stream().map(UserRole::getRoleId).toList();
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<String> allRoleIdList = roleHierarchyService.listAllRoleIdIncludingChildren(userId, roleIdList);
|
||||||
|
return rolePermissionMapper.selectPermissionByRoleIdList(allRoleIdList)
|
||||||
|
.stream()
|
||||||
|
.anyMatch(permission -> moduleCode == permission.getModuleCode() && permissionCode.equals(permission.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModuleResourceScopeResolver getResolver(ModuleCode moduleCode) {
|
||||||
|
return moduleResourceScopeResolverList.stream()
|
||||||
|
.filter(resolver -> moduleCode == resolver.moduleCode())
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkRoleIdListComplete(Collection<String> idList, List<Role> roleList) {
|
||||||
|
Set<String> foundIdSet = roleList.stream().map(Role::getId).collect(Collectors.toSet());
|
||||||
|
Set<String> idSet = new HashSet<>(idList);
|
||||||
|
TimiException.requiredTrue(foundIdSet.containsAll(idSet), "存在无效角色 ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPermissionIdListComplete(Collection<String> idList, List<Permission> permissionList) {
|
||||||
|
Set<String> foundIdSet = permissionList.stream().map(Permission::getId).collect(Collectors.toSet());
|
||||||
|
Set<String> idSet = new HashSet<>(idList);
|
||||||
|
TimiException.requiredTrue(foundIdSet.containsAll(idSet), "存在无效权限 ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+178
@@ -0,0 +1,178 @@
|
|||||||
|
package com.imyeyu.api.modules.user.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.CoreRoleCode;
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
|
import com.imyeyu.api.modules.user.entity.UserRole;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.PermissionMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RolePermissionDelegationMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RolePermissionMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.UserRoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.service.AuthorizationScopeService;
|
||||||
|
import com.imyeyu.api.modules.user.service.ModuleResourceScopeResolver;
|
||||||
|
import com.imyeyu.api.modules.user.service.RoleHierarchyService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/// 授权范围服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-07
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthorizationScopeServiceImplement implements AuthorizationScopeService {
|
||||||
|
|
||||||
|
private final RoleMapper roleMapper;
|
||||||
|
private final UserRoleMapper userRoleMapper;
|
||||||
|
private final PermissionMapper permissionMapper;
|
||||||
|
private final RolePermissionMapper rolePermissionMapper;
|
||||||
|
private final RolePermissionDelegationMapper rolePermissionDelegationMapper;
|
||||||
|
private final RoleHierarchyService roleHierarchyService;
|
||||||
|
private final List<ModuleResourceScopeResolver> moduleResourceScopeResolverList;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Role> listGrantableRole(String operatorUserId, ModuleCode moduleCode) {
|
||||||
|
List<Role> roleList = listManageableRole(operatorUserId, moduleCode);
|
||||||
|
if (isSystemAdmin(operatorUserId)) {
|
||||||
|
return roleList;
|
||||||
|
}
|
||||||
|
Set<String> operatorDelegationPermissionIdSet = listOperatorDelegationPermissionId(operatorUserId);
|
||||||
|
return roleList.stream()
|
||||||
|
.filter(role -> operatorDelegationPermissionIdSet.containsAll(listRoleAllPermissionId(operatorUserId, role.getId())))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Permission> listGrantablePermission(String operatorUserId, ModuleCode moduleCode) {
|
||||||
|
List<Permission> permissionList = permissionMapper.selectByModuleCode(moduleCode);
|
||||||
|
if (isSystemAdmin(operatorUserId)) {
|
||||||
|
return permissionList;
|
||||||
|
}
|
||||||
|
Set<String> delegationPermissionIdSet = listOperatorDelegationPermissionId(operatorUserId, moduleCode);
|
||||||
|
return permissionList.stream()
|
||||||
|
.filter(permission -> !Boolean.TRUE.equals(permission.getProtectedPermission()))
|
||||||
|
.filter(permission -> delegationPermissionIdSet.contains(permission.getId()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Role> listManageableRole(String operatorUserId, ModuleCode moduleCode) {
|
||||||
|
List<Role> roleList = roleMapper.selectByModuleCode(moduleCode);
|
||||||
|
if (isSystemAdmin(operatorUserId)) {
|
||||||
|
return roleList;
|
||||||
|
}
|
||||||
|
Set<String> manageableRoleIdSet = listManagementDescendantRoleId(operatorUserId);
|
||||||
|
ModuleResourceScopeResolver resolver = getResolver(moduleCode);
|
||||||
|
return roleList.stream()
|
||||||
|
.filter(role -> manageableRoleIdSet.contains(role.getId()))
|
||||||
|
.filter(role -> !Boolean.TRUE.equals(role.getProtectedRole()))
|
||||||
|
.filter(role -> resolver == null || resolver.canManageRole(operatorUserId, role))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSystemAdmin(String operatorUserId) {
|
||||||
|
return listAllOperatorRole(operatorUserId).stream()
|
||||||
|
.anyMatch(role -> ModuleCode.CORE == role.getModuleCode()
|
||||||
|
&& CoreRoleCode.SYSTEM.name().equals(role.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listManagementDescendantRoleId(String operatorUserId) {
|
||||||
|
List<String> operatorRoleIdList = listOperatorRoleId(operatorUserId);
|
||||||
|
if (TimiJava.isEmpty(operatorRoleIdList)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
Map<String, List<Role>> childRoleMap = roleMapper.selectAll()
|
||||||
|
.stream()
|
||||||
|
.filter(role -> TimiJava.isNotEmpty(role.getParentRoleId()))
|
||||||
|
.collect(Collectors.groupingBy(Role::getParentRoleId));
|
||||||
|
Set<String> result = new HashSet<>();
|
||||||
|
ArrayDeque<String> queue = new ArrayDeque<>(operatorRoleIdList);
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
String roleId = queue.removeFirst();
|
||||||
|
for (Role childRole : childRoleMap.getOrDefault(roleId, List.of())) {
|
||||||
|
if (result.add(childRole.getId())) {
|
||||||
|
queue.addLast(childRole.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Role> listAllOperatorRole(String operatorUserId) {
|
||||||
|
List<String> allRoleIdList = listAllOperatorRoleId(operatorUserId);
|
||||||
|
if (TimiJava.isEmpty(allRoleIdList)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
Map<String, Role> roleMap = roleMapper.selectByIdList(allRoleIdList).stream().collect(Collectors.toMap(Role::getId, Function.identity()));
|
||||||
|
return allRoleIdList.stream().map(roleMap::get).filter(java.util.Objects::nonNull).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> listAllOperatorRoleId(String operatorUserId) {
|
||||||
|
List<String> roleIdList = listOperatorRoleId(operatorUserId);
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
return roleHierarchyService.listAllRoleIdIncludingChildren(operatorUserId, roleIdList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> listOperatorRoleId(String operatorUserId) {
|
||||||
|
if (TimiJava.isEmpty(operatorUserId)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
return userRoleMapper.selectByUserIdList(List.of(operatorUserId)).stream().map(UserRole::getRoleId).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listOperatorDelegationPermissionId(String operatorUserId, ModuleCode moduleCode) {
|
||||||
|
List<String> roleIdList = listOperatorRoleId(operatorUserId);
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return rolePermissionDelegationMapper.selectPermissionByRoleIdList(roleIdList)
|
||||||
|
.stream()
|
||||||
|
.filter(permission -> moduleCode == permission.getModuleCode())
|
||||||
|
.map(Permission::getId)
|
||||||
|
.collect(Collectors.toCollection(HashSet::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listOperatorDelegationPermissionId(String operatorUserId) {
|
||||||
|
List<String> roleIdList = listOperatorRoleId(operatorUserId);
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return rolePermissionDelegationMapper.selectPermissionByRoleIdList(roleIdList)
|
||||||
|
.stream()
|
||||||
|
.map(Permission::getId)
|
||||||
|
.collect(Collectors.toCollection(HashSet::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listRoleAllPermissionId(String operatorUserId, String roleId) {
|
||||||
|
List<String> allRoleIdList = roleHierarchyService.listAllRoleIdIncludingChildren(operatorUserId, List.of(roleId));
|
||||||
|
if (TimiJava.isEmpty(allRoleIdList)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return rolePermissionMapper.selectPermissionByRoleIdList(allRoleIdList)
|
||||||
|
.stream()
|
||||||
|
.map(Permission::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModuleResourceScopeResolver getResolver(ModuleCode moduleCode) {
|
||||||
|
return moduleResourceScopeResolverList.stream()
|
||||||
|
.filter(resolver -> moduleCode == resolver.moduleCode())
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -1,8 +1,10 @@
|
|||||||
package com.imyeyu.api.modules.user.service.implement;
|
package com.imyeyu.api.modules.user.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.api.modules.common.mapper.MultilingualMapper;
|
import com.imyeyu.api.modules.common.mapper.MultilingualMapper;
|
||||||
import com.imyeyu.api.modules.common.service.MultilingualService;
|
import com.imyeyu.api.modules.common.service.MultilingualService;
|
||||||
|
import com.imyeyu.api.modules.user.bean.AuthOwnerType;
|
||||||
import com.imyeyu.api.modules.user.entity.Permission;
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
import com.imyeyu.api.modules.user.mapper.PermissionMapper;
|
import com.imyeyu.api.modules.user.mapper.PermissionMapper;
|
||||||
import com.imyeyu.api.modules.user.mapper.RolePermissionMapper;
|
import com.imyeyu.api.modules.user.mapper.RolePermissionMapper;
|
||||||
@@ -45,6 +47,7 @@ public class PermissionServiceImplement extends AbstractEntityService<Permission
|
|||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void create(Permission permission) {
|
public void create(Permission permission) {
|
||||||
|
fillPermissionDefault(permission);
|
||||||
permission.setNameLangId(multilingualService.create(permission.getName()));
|
permission.setNameLangId(multilingualService.create(permission.getName()));
|
||||||
super.create(permission);
|
super.create(permission);
|
||||||
}
|
}
|
||||||
@@ -62,6 +65,7 @@ public class PermissionServiceImplement extends AbstractEntityService<Permission
|
|||||||
permission.setModuleCode(req.getModuleCode());
|
permission.setModuleCode(req.getModuleCode());
|
||||||
permission.setCode(req.getPrefixCode() + "_" + type.name());
|
permission.setCode(req.getPrefixCode() + "_" + type.name());
|
||||||
permission.setName(req.getPrefixName() + messageResolver.resolve(type.name().toLowerCase(), null, TimiSpring.getLanguage()));
|
permission.setName(req.getPrefixName() + messageResolver.resolve(type.name().toLowerCase(), null, TimiSpring.getLanguage()));
|
||||||
|
fillPermissionDefault(permission);
|
||||||
permission.setCreatedAt(Time.now());
|
permission.setCreatedAt(Time.now());
|
||||||
permissionList.add(permission);
|
permissionList.add(permission);
|
||||||
}
|
}
|
||||||
@@ -84,4 +88,16 @@ public class PermissionServiceImplement extends AbstractEntityService<Permission
|
|||||||
}
|
}
|
||||||
return rolePermissionMapper.selectPermissionByRoleIdList(roleIdList);
|
return rolePermissionMapper.selectPermissionByRoleIdList(roleIdList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Permission> listByModuleCode(ModuleCode moduleCode) {
|
||||||
|
return mapper.selectByModuleCode(moduleCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillPermissionDefault(Permission permission) {
|
||||||
|
permission.setBuiltin(TimiJava.defaultIfNull(permission.getBuiltin(), false));
|
||||||
|
permission.setProtectedPermission(TimiJava.defaultIfNull(permission.getProtectedPermission(), false));
|
||||||
|
permission.setOwnerType(TimiJava.defaultIfNull(permission.getOwnerType(), AuthOwnerType.MODULE));
|
||||||
|
permission.setOwnerId(TimiJava.defaultIfEmpty(permission.getOwnerId(), permission.getModuleCode() == null ? null : permission.getModuleCode().name()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
package com.imyeyu.api.modules.user.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.user.bean.AuthOwnerType;
|
||||||
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
|
import com.imyeyu.api.modules.user.entity.RolePermissionInherit;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RolePermissionInheritMapper;
|
||||||
|
import com.imyeyu.api.modules.user.service.ModuleResourceScopeResolver;
|
||||||
|
import com.imyeyu.api.modules.user.service.RoleHierarchyService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/// 角色继承服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-08-07
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RoleHierarchyServiceImplement implements RoleHierarchyService {
|
||||||
|
|
||||||
|
private final RolePermissionInheritMapper rolePermissionInheritMapper;
|
||||||
|
private final RoleMapper roleMapper;
|
||||||
|
private final ObjectProvider<ModuleResourceScopeResolver> moduleResourceScopeResolverProvider;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> listAllRoleIdIncludingChildren(Collection<String> roleIdList) {
|
||||||
|
return listAllRoleIdIncludingChildren(null, roleIdList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> listAllRoleIdIncludingChildren(String userId, Collection<String> roleIdList) {
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
Set<String> allRoleIdSet = new HashSet<>(roleIdList);
|
||||||
|
List<String> currentLevelRoleIdList = new ArrayList<>(roleIdList);
|
||||||
|
while (TimiJava.isNotEmpty(currentLevelRoleIdList)) {
|
||||||
|
List<RolePermissionInherit> relationList = rolePermissionInheritMapper.selectByParentRoleIdList(currentLevelRoleIdList);
|
||||||
|
List<String> parentAndChildRoleIdList = relationList.stream()
|
||||||
|
.flatMap(relation -> java.util.stream.Stream.of(relation.getParentRoleId(), relation.getChildRoleId()))
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
Map<String, Role> roleMap = roleMapper.selectByIdList(parentAndChildRoleIdList).stream().collect(Collectors.toMap(Role::getId, Function.identity()));
|
||||||
|
List<String> childRoleIdList = relationList.stream()
|
||||||
|
.filter(relation -> isVisible(userId, relation, roleMap.get(relation.getParentRoleId())))
|
||||||
|
.map(RolePermissionInherit::getChildRoleId)
|
||||||
|
.toList();
|
||||||
|
if (TimiJava.isEmpty(childRoleIdList)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<String> newChildRoleIdList = childRoleIdList.stream().filter(id -> !allRoleIdSet.contains(id)).toList();
|
||||||
|
if (TimiJava.isEmpty(newChildRoleIdList)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
allRoleIdSet.addAll(newChildRoleIdList);
|
||||||
|
currentLevelRoleIdList = newChildRoleIdList;
|
||||||
|
}
|
||||||
|
return new ArrayList<>(allRoleIdSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkNoCycle(String parentRoleId, Collection<String> childRoleIdList) {
|
||||||
|
if (TimiJava.isEmpty(childRoleIdList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (String childRoleId : childRoleIdList) {
|
||||||
|
if (parentRoleId.equals(childRoleId) || isDescendant(childRoleId, parentRoleId)) {
|
||||||
|
throw new TimiException(TimiCode.PERMISSION_ERROR, "角色继承关系不能形成循环");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDescendant(String parentRoleId, String childRoleId) {
|
||||||
|
return listAllRoleIdIncludingChildren(List.of(parentRoleId)).contains(childRoleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isVisible(String userId, RolePermissionInherit relation, Role parentRole) {
|
||||||
|
if (relation.getOwnerType() == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (relation.getOwnerType() == AuthOwnerType.MODULE && parentRole != null && Objects.equals(relation.getOwnerId(), parentRole.getModuleCode().name())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (TimiJava.isEmpty(userId) || parentRole == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ModuleResourceScopeResolver resolver = moduleResourceScopeResolverProvider.orderedStream()
|
||||||
|
.filter(item -> parentRole.getModuleCode() == item.moduleCode())
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
return resolver != null && resolver.canUseOwnerScope(userId, relation.getOwnerType(), relation.getOwnerId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+10
-10
@@ -1,8 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.user.service.implement;
|
package com.imyeyu.api.modules.user.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.user.entity.RoleRelation;
|
import com.imyeyu.api.modules.user.entity.RolePermissionInherit;
|
||||||
import com.imyeyu.api.modules.user.mapper.RoleRelationMapper;
|
import com.imyeyu.api.modules.user.mapper.RolePermissionInheritMapper;
|
||||||
import com.imyeyu.api.modules.user.service.RoleRelationService;
|
import com.imyeyu.api.modules.user.service.RolePermissionInheritService;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
import com.imyeyu.spring.service.AbstractEntityService;
|
import com.imyeyu.spring.service.AbstractEntityService;
|
||||||
@@ -14,34 +14,34 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/// 角色关联服务实现
|
/// 角色权限继承服务实现
|
||||||
///
|
///
|
||||||
/// @author 夜雨
|
/// @author 夜雨
|
||||||
/// @since 2026-05-13 14:46
|
/// @since 2026-05-13 14:46
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class RoleRelationServiceImplement extends AbstractEntityService<RoleRelation, String> implements RoleRelationService {
|
public class RolePermissionInheritServiceImplement extends AbstractEntityService<RolePermissionInherit, String> implements RolePermissionInheritService {
|
||||||
|
|
||||||
private final RoleRelationMapper mapper;
|
private final RolePermissionInheritMapper mapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseMapper<RoleRelation, String> mapper() {
|
protected BaseMapper<RolePermissionInherit, String> mapper() {
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<RoleRelation> listByParentRoleId(String parentRoleId) {
|
public List<RolePermissionInherit> listByParentRoleId(String parentRoleId) {
|
||||||
if (TimiJava.isEmpty(parentRoleId)) {
|
if (TimiJava.isEmpty(parentRoleId)) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
RoleRelation example = new RoleRelation();
|
RolePermissionInherit example = new RolePermissionInherit();
|
||||||
example.setParentRoleId(parentRoleId);
|
example.setParentRoleId(parentRoleId);
|
||||||
return mapper.selectAllByExample(example);
|
return mapper.selectAllByExample(example);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> listChildRoleIdByParentRoleId(String parentRoleId) {
|
public List<String> listChildRoleIdByParentRoleId(String parentRoleId) {
|
||||||
return listByParentRoleId(parentRoleId).stream().map(RoleRelation::getChildRoleId).distinct().toList();
|
return listByParentRoleId(parentRoleId).stream().map(RolePermissionInherit::getChildRoleId).distinct().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
+392
-49
@@ -3,15 +3,27 @@ package com.imyeyu.api.modules.user.service.implement;
|
|||||||
import com.imyeyu.api.bean.ModuleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.common.service.MultilingualService;
|
import com.imyeyu.api.modules.common.service.MultilingualService;
|
||||||
|
import com.imyeyu.api.modules.system.entity.Logger;
|
||||||
|
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||||
|
import com.imyeyu.api.modules.user.bean.AuthOwnerType;
|
||||||
import com.imyeyu.api.modules.user.entity.Permission;
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
import com.imyeyu.api.modules.user.entity.Role;
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
import com.imyeyu.api.modules.user.entity.RolePermission;
|
import com.imyeyu.api.modules.user.entity.RolePermission;
|
||||||
import com.imyeyu.api.modules.user.entity.RoleRelation;
|
import com.imyeyu.api.modules.user.entity.RolePermissionDelegation;
|
||||||
|
import com.imyeyu.api.modules.user.entity.RolePermissionInherit;
|
||||||
|
import com.imyeyu.api.modules.user.entity.UserRole;
|
||||||
import com.imyeyu.api.modules.user.mapper.RoleMapper;
|
import com.imyeyu.api.modules.user.mapper.RoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RolePermissionDelegationMapper;
|
||||||
import com.imyeyu.api.modules.user.mapper.RolePermissionMapper;
|
import com.imyeyu.api.modules.user.mapper.RolePermissionMapper;
|
||||||
import com.imyeyu.api.modules.user.mapper.RoleRelationMapper;
|
import com.imyeyu.api.modules.user.mapper.RolePermissionInheritMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.UserRoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.service.AuthorizationBoundaryService;
|
||||||
|
import com.imyeyu.api.modules.user.service.AuthorizationScopeService;
|
||||||
|
import com.imyeyu.api.modules.user.service.ModuleResourceScopeResolver;
|
||||||
import com.imyeyu.api.modules.user.service.PermissionService;
|
import com.imyeyu.api.modules.user.service.PermissionService;
|
||||||
|
import com.imyeyu.api.modules.user.service.RoleHierarchyService;
|
||||||
import com.imyeyu.api.modules.user.service.RoleService;
|
import com.imyeyu.api.modules.user.service.RoleService;
|
||||||
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
@@ -22,11 +34,16 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/// 角色服务实现
|
/// 角色服务实现
|
||||||
@@ -39,10 +56,18 @@ public class RoleServiceImplement extends AbstractEntityService<Role, String> im
|
|||||||
|
|
||||||
private final PermissionService permissionService;
|
private final PermissionService permissionService;
|
||||||
private final MultilingualService multilingualService;
|
private final MultilingualService multilingualService;
|
||||||
|
private final LoggerService loggerService;
|
||||||
|
private final UserLoginService userLoginService;
|
||||||
|
private final RoleHierarchyService roleHierarchyService;
|
||||||
|
private final AuthorizationBoundaryService authorizationBoundaryService;
|
||||||
|
private final AuthorizationScopeService authorizationScopeService;
|
||||||
|
private final List<ModuleResourceScopeResolver> moduleResourceScopeResolverList;
|
||||||
|
|
||||||
private final RoleMapper mapper;
|
private final RoleMapper mapper;
|
||||||
private final RoleRelationMapper roleRelationMapper;
|
private final RolePermissionInheritMapper rolePermissionInheritMapper;
|
||||||
private final RolePermissionMapper rolePermissionMapper;
|
private final RolePermissionMapper rolePermissionMapper;
|
||||||
|
private final RolePermissionDelegationMapper rolePermissionDelegationMapper;
|
||||||
|
private final UserRoleMapper userRoleMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseMapper<Role, String> mapper() {
|
protected BaseMapper<Role, String> mapper() {
|
||||||
@@ -52,8 +77,20 @@ public class RoleServiceImplement extends AbstractEntityService<Role, String> im
|
|||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void create(Role role) {
|
public void create(Role role) {
|
||||||
|
fillRoleDefault(role);
|
||||||
|
if (!Boolean.TRUE.equals(role.getBuiltin())) {
|
||||||
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
|
role.setCreatedBy(TimiJava.defaultIfEmpty(role.getCreatedBy(), loginUserId));
|
||||||
|
authorizationBoundaryService.checkCanCreateRole(loginUserId, role);
|
||||||
|
}
|
||||||
role.setNameLangId(multilingualService.create(role.getName()));
|
role.setNameLangId(multilingualService.create(role.getName()));
|
||||||
super.create(role);
|
super.create(role);
|
||||||
|
if (!Boolean.TRUE.equals(role.getBuiltin())) {
|
||||||
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
|
authorizationBoundaryService.checkCanUpdateRoleParent(loginUserId, role.getId(), role.getParentRoleId());
|
||||||
|
authorizationBoundaryService.checkCanGrantPermissions(loginUserId, role.getId(), role.getPermissionIdList());
|
||||||
|
authorizationBoundaryService.checkCanUpdateRolePermissionInherits(loginUserId, role.getId(), role.getChildRoleIdList());
|
||||||
|
}
|
||||||
|
|
||||||
if (TimiJava.isNotEmpty(role.getPermissionIdList())) {
|
if (TimiJava.isNotEmpty(role.getPermissionIdList())) {
|
||||||
List<RolePermission> rolePermissionList = new ArrayList<>();
|
List<RolePermission> rolePermissionList = new ArrayList<>();
|
||||||
@@ -70,34 +107,47 @@ public class RoleServiceImplement extends AbstractEntityService<Role, String> im
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (TimiJava.isNotEmpty(role.getChildRoleIdList())) {
|
if (TimiJava.isNotEmpty(role.getChildRoleIdList())) {
|
||||||
List<RoleRelation> roleRelationList = new ArrayList<>();
|
List<RolePermissionInherit> rolePermissionInheritList = new ArrayList<>();
|
||||||
long now = Time.now();
|
long now = Time.now();
|
||||||
|
Map<String, Role> childRoleMap = mapper.selectByIdList(role.getChildRoleIdList()).stream().collect(Collectors.toMap(Role::getId, Function.identity()));
|
||||||
for (String childRoleId : role.getChildRoleIdList()) {
|
for (String childRoleId : role.getChildRoleIdList()) {
|
||||||
RoleRelation roleRelation = new RoleRelation();
|
RolePermissionInherit rolePermissionInherit = new RolePermissionInherit();
|
||||||
roleRelation.setId(UUID.randomUUID().toString());
|
rolePermissionInherit.setId(UUID.randomUUID().toString());
|
||||||
roleRelation.setParentRoleId(role.getId());
|
rolePermissionInherit.setParentRoleId(role.getId());
|
||||||
roleRelation.setChildRoleId(childRoleId);
|
rolePermissionInherit.setChildRoleId(childRoleId);
|
||||||
roleRelation.setCreatedAt(now);
|
fillRelationScope(rolePermissionInherit, childRoleMap.get(childRoleId), role.getCreatedBy());
|
||||||
roleRelationList.add(roleRelation);
|
rolePermissionInherit.setCreatedAt(now);
|
||||||
|
rolePermissionInheritList.add(rolePermissionInherit);
|
||||||
}
|
}
|
||||||
roleRelationMapper.createBatch(roleRelationList);
|
rolePermissionInheritMapper.createBatch(rolePermissionInheritList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void update(Role role) {
|
public void update(Role role) {
|
||||||
role.setNameLangId(multilingualService.createIfDifferent(role.getNameLangId(), role.getName()));
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
mapper.updateSelective(role);
|
Role dbRole = mapper.selectById(role.getId());
|
||||||
{
|
if (dbRole != null && Boolean.TRUE.equals(dbRole.getBuiltin())) {
|
||||||
|
// 内建角色允许修改名称和说明,但角色代码必须保持不变。
|
||||||
|
role.setCode(null);
|
||||||
|
}
|
||||||
|
if (hasRoleBaseField(role)) {
|
||||||
|
authorizationBoundaryService.checkCanUpdateRole(loginUserId, role);
|
||||||
|
role.setNameLangId(multilingualService.createIfDifferent(role.getNameLangId(), role.getName()));
|
||||||
|
mapper.updateSelective(role);
|
||||||
|
}
|
||||||
|
if (role.getPermissionIdList() != null) {
|
||||||
Set<String> dbIdSet = rolePermissionMapper.selectByRoleId(role.getId()).stream().map(RolePermission::getPermissionId).collect(Collectors.toSet());
|
Set<String> dbIdSet = rolePermissionMapper.selectByRoleId(role.getId()).stream().map(RolePermission::getPermissionId).collect(Collectors.toSet());
|
||||||
Set<String> newIdSet = TimiJava.defaultIfEmpty(new HashSet<>(role.getPermissionIdList()), new HashSet<>());
|
Set<String> newIdSet = TimiJava.isEmpty(role.getPermissionIdList()) ? new HashSet<>() : new HashSet<>(role.getPermissionIdList());
|
||||||
|
|
||||||
Set<String> deleteIdList = new HashSet<>(dbIdSet);
|
Set<String> deleteIdList = new HashSet<>(dbIdSet);
|
||||||
deleteIdList.removeAll(newIdSet);
|
deleteIdList.removeAll(newIdSet);
|
||||||
|
|
||||||
Set<String> addIdList = new HashSet<>(newIdSet);
|
Set<String> addIdList = new HashSet<>(newIdSet);
|
||||||
addIdList.removeAll(dbIdSet);
|
addIdList.removeAll(dbIdSet);
|
||||||
|
authorizationBoundaryService.checkCanRevokePermissions(loginUserId, role.getId(), deleteIdList);
|
||||||
|
authorizationBoundaryService.checkCanGrantPermissions(loginUserId, role.getId(), addIdList);
|
||||||
|
|
||||||
if (TimiJava.isNotEmpty(deleteIdList)) {
|
if (TimiJava.isNotEmpty(deleteIdList)) {
|
||||||
rolePermissionMapper.deleteByPermissionIdList(role.getId(), new ArrayList<>(deleteIdList));
|
rolePermissionMapper.deleteByPermissionIdList(role.getId(), new ArrayList<>(deleteIdList));
|
||||||
@@ -115,37 +165,58 @@ public class RoleServiceImplement extends AbstractEntityService<Role, String> im
|
|||||||
}
|
}
|
||||||
rolePermissionMapper.createBatch(rolePermissionList);
|
rolePermissionMapper.createBatch(rolePermissionList);
|
||||||
}
|
}
|
||||||
|
logRoleAuthorizationChange("USER_ROLE_PERMISSION_UPDATE", loginUserId, role.getId(), addIdList, deleteIdList);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if (role.getChildRoleIdList() != null) {
|
||||||
Set<String> dbIdSet = roleRelationMapper.selectByParentRoleId(role.getId()).stream().map(RoleRelation::getChildRoleId).collect(Collectors.toSet());
|
Role parentRole = mapper.selectById(role.getId());
|
||||||
Set<String> newIdSet = TimiJava.defaultIfEmpty(new HashSet<>(role.getChildRoleIdList()), new HashSet<>());
|
List<RolePermissionInherit> dbRelationList = rolePermissionInheritMapper.selectByParentRoleId(role.getId());
|
||||||
|
List<RolePermissionInherit> touchableRelationList = dbRelationList.stream()
|
||||||
|
.filter(relation -> canTouchRelation(loginUserId, parentRole, relation))
|
||||||
|
.toList();
|
||||||
|
Set<String> newIdSet = TimiJava.isEmpty(role.getChildRoleIdList()) ? new HashSet<>() : new HashSet<>(role.getChildRoleIdList());
|
||||||
|
Map<String, Role> childRoleMap = mapper.selectByIdList(newIdSet).stream().collect(Collectors.toMap(Role::getId, Function.identity()));
|
||||||
|
Set<String> dbKeySet = dbRelationList.stream().map(this::relationKey).collect(Collectors.toSet());
|
||||||
|
Set<String> newKeySet = newIdSet.stream().map(childRoleId -> relationKey(childRoleId, childRoleMap.get(childRoleId))).collect(Collectors.toSet());
|
||||||
|
|
||||||
Set<String> deleteIdList = new HashSet<>(dbIdSet);
|
Set<String> deleteKeyList = new HashSet<>(dbKeySet);
|
||||||
deleteIdList.removeAll(newIdSet);
|
deleteKeyList.removeAll(newKeySet);
|
||||||
|
|
||||||
Set<String> addIdList = new HashSet<>(newIdSet);
|
Set<String> addKeyList = new HashSet<>(newKeySet);
|
||||||
addIdList.removeAll(dbIdSet);
|
addKeyList.removeAll(dbKeySet);
|
||||||
|
Set<String> addIdList = addKeyList.stream().map(key -> key.split("\\|", 2)[0]).collect(Collectors.toSet());
|
||||||
|
authorizationBoundaryService.checkCanUpdateRolePermissionInherits(loginUserId, role.getId(), addIdList);
|
||||||
|
|
||||||
if (TimiJava.isNotEmpty(deleteIdList)) {
|
if (TimiJava.isNotEmpty(deleteKeyList)) {
|
||||||
roleRelationMapper.deleteByChildRoleIdList(role.getId(), new ArrayList<>(deleteIdList));
|
for (RolePermissionInherit relation : touchableRelationList) {
|
||||||
|
if (deleteKeyList.contains(relationKey(relation))) {
|
||||||
|
rolePermissionInheritMapper.deleteByChildRoleIdList(role.getId(), List.of(relation.getChildRoleId()), relation.getOwnerType(), relation.getOwnerId());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (TimiJava.isNotEmpty(addIdList)) {
|
if (TimiJava.isNotEmpty(addIdList)) {
|
||||||
List<RoleRelation> roleRelationList = new ArrayList<>();
|
List<RolePermissionInherit> rolePermissionInheritList = new ArrayList<>();
|
||||||
long now = Time.now();
|
long now = Time.now();
|
||||||
for (String childRoleId : addIdList) {
|
for (String childRoleId : addIdList) {
|
||||||
RoleRelation roleRelation = new RoleRelation();
|
RolePermissionInherit rolePermissionInherit = new RolePermissionInherit();
|
||||||
roleRelation.setId(UUID.randomUUID().toString());
|
rolePermissionInherit.setId(UUID.randomUUID().toString());
|
||||||
roleRelation.setParentRoleId(role.getId());
|
rolePermissionInherit.setParentRoleId(role.getId());
|
||||||
roleRelation.setChildRoleId(childRoleId);
|
rolePermissionInherit.setChildRoleId(childRoleId);
|
||||||
roleRelation.setCreatedAt(now);
|
fillRelationScope(rolePermissionInherit, childRoleMap.get(childRoleId), loginUserId);
|
||||||
roleRelationList.add(roleRelation);
|
rolePermissionInherit.setCreatedAt(now);
|
||||||
|
rolePermissionInheritList.add(rolePermissionInherit);
|
||||||
}
|
}
|
||||||
roleRelationMapper.createBatch(roleRelationList);
|
rolePermissionInheritMapper.createBatch(rolePermissionInheritList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String id) {
|
||||||
|
authorizationBoundaryService.checkCanDeleteRole(userLoginService.getRequireLoginUserId(), id);
|
||||||
|
super.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Role> listByModuleCode(ModuleCode moduleCode) {
|
public List<Role> listByModuleCode(ModuleCode moduleCode) {
|
||||||
TimiException.required(moduleCode, "not found moduleCode");
|
TimiException.required(moduleCode, "not found moduleCode");
|
||||||
@@ -157,9 +228,25 @@ public class RoleServiceImplement extends AbstractEntityService<Role, String> im
|
|||||||
return mapper.selectByIdList(new HashSet<>(idList));
|
return mapper.selectByIdList(new HashSet<>(idList));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Role> listManageableRole(ModuleCode moduleCode) {
|
||||||
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
|
return authorizationScopeService.listManageableRole(loginUserId, moduleCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Role> listGrantableRole(ModuleCode moduleCode) {
|
||||||
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
|
return authorizationScopeService.listGrantableRole(loginUserId, moduleCode);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Role> listChildByParentRoleId(String parentRoleId) {
|
public List<Role> listChildByParentRoleId(String parentRoleId) {
|
||||||
List<String> childRoleIdList = roleRelationMapper.selectChildRoleIdListByParentRoleId(parentRoleId);
|
List<String> childRoleIdList = rolePermissionInheritMapper.selectByParentRoleId(parentRoleId)
|
||||||
|
.stream()
|
||||||
|
.map(RolePermissionInherit::getChildRoleId)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
if (TimiJava.isEmpty(childRoleIdList)) {
|
if (TimiJava.isEmpty(childRoleIdList)) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
@@ -168,26 +255,282 @@ public class RoleServiceImplement extends AbstractEntityService<Role, String> im
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Permission> listPermissionByRoleId(String roleId) {
|
public List<Permission> listPermissionByRoleId(String roleId) {
|
||||||
List<String> allRoleIdList = listAllRoleIdIncludingChildren(List.of(roleId));
|
List<String> allRoleIdList = roleHierarchyService.listAllRoleIdIncludingChildren(userLoginService.getRequireLoginUserId(), List.of(roleId));
|
||||||
return permissionService.listByRoleId(allRoleIdList);
|
return permissionService.listByRoleId(allRoleIdList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> listAllRoleIdIncludingChildren(Collection<String> idList) {
|
@Override
|
||||||
Set<String> allRoleIdSet = new HashSet<>(idList);
|
public List<Permission> listGrantableRolePermission(String roleId) {
|
||||||
List<String> currentLevelRoleIdList = new ArrayList<>(idList);
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
|
Role role = mapper.selectById(roleId);
|
||||||
|
TimiException.required(role, "not found role");
|
||||||
|
checkManageableRole(loginUserId, role);
|
||||||
|
Set<String> candidateIdSet = listRoleDirectGrantablePermissionId(loginUserId, role);
|
||||||
|
return listPermissionById(candidateIdSet).stream()
|
||||||
|
.collect(Collectors.toMap(Permission::getId, Function.identity(), (oldValue, newValue) -> oldValue, LinkedHashMap::new))
|
||||||
|
.values()
|
||||||
|
.stream()
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
while (TimiJava.isNotEmpty(currentLevelRoleIdList)) {
|
@Override
|
||||||
List<String> childRoleIdList = roleRelationMapper.selectChildRoleIdListByParentRoleIdList(currentLevelRoleIdList);
|
public List<Permission> listDelegationPermissionByRoleId(String roleId) {
|
||||||
if (TimiJava.isEmpty(childRoleIdList)) {
|
return rolePermissionDelegationMapper.selectPermissionByRoleIdList(List.of(roleId));
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
List<String> newChildRoleIdList = childRoleIdList.stream().filter(id -> !allRoleIdSet.contains(id)).toList();
|
@Override
|
||||||
if (TimiJava.isEmpty(newChildRoleIdList)) {
|
public List<Permission> listGrantableDelegationPermission(String roleId) {
|
||||||
break;
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
}
|
Role role = mapper.selectById(roleId);
|
||||||
allRoleIdSet.addAll(newChildRoleIdList);
|
TimiException.required(role, "not found role");
|
||||||
currentLevelRoleIdList = newChildRoleIdList;
|
boolean hasParentRole = TimiJava.isNotEmpty(role.getParentRoleId());
|
||||||
|
boolean systemAdmin = authorizationScopeService.isSystemAdmin(loginUserId);
|
||||||
|
Set<String> candidateIdSet = isCoreDelegationRoot(role) && systemAdmin && !hasParentRole
|
||||||
|
? Arrays.stream(ModuleCode.values())
|
||||||
|
.flatMap(moduleCode -> permissionService.listByModuleCode(moduleCode).stream())
|
||||||
|
.map(Permission::getId)
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
: listDelegationParentPermissionId(roleId);
|
||||||
|
List<Permission> candidateList = listPermissionById(candidateIdSet);
|
||||||
|
Set<String> operatorGrantableIdSet = listOperatorGrantablePermissionId(loginUserId, candidateList);
|
||||||
|
return candidateList.stream()
|
||||||
|
.filter(permission -> role.getModuleCode() == permission.getModuleCode() || isCoreDelegationRoot(role) || isCoreAuthorizationPermission(permission))
|
||||||
|
.filter(permission -> operatorGrantableIdSet.contains(permission.getId()) || systemAdmin)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void updateRoleDelegation(Role role) {
|
||||||
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
|
TimiException.required(role.getId(), "not found role.id");
|
||||||
|
Set<String> newIdSet = TimiJava.isEmpty(role.getDelegationPermissionIdList()) ? new HashSet<>() : new HashSet<>(role.getDelegationPermissionIdList());
|
||||||
|
authorizationBoundaryService.checkCanUpdateRoleDelegation(loginUserId, role.getId(), newIdSet);
|
||||||
|
Set<String> dbIdSet = rolePermissionDelegationMapper.selectByRoleId(role.getId()).stream().map(RolePermissionDelegation::getPermissionId).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Set<String> deleteIdList = new HashSet<>(dbIdSet);
|
||||||
|
deleteIdList.removeAll(newIdSet);
|
||||||
|
|
||||||
|
Set<String> addIdList = new HashSet<>(newIdSet);
|
||||||
|
addIdList.removeAll(dbIdSet);
|
||||||
|
if (TimiJava.isNotEmpty(deleteIdList)) {
|
||||||
|
rolePermissionDelegationMapper.deleteByPermissionIdList(role.getId(), new ArrayList<>(deleteIdList));
|
||||||
}
|
}
|
||||||
return new ArrayList<>(allRoleIdSet);
|
if (TimiJava.isNotEmpty(addIdList)) {
|
||||||
|
List<RolePermissionDelegation> delegationList = new ArrayList<>();
|
||||||
|
long now = Time.now();
|
||||||
|
for (String permissionId : addIdList) {
|
||||||
|
RolePermissionDelegation delegation = new RolePermissionDelegation();
|
||||||
|
delegation.setId(UUID.randomUUID().toString());
|
||||||
|
delegation.setRoleId(role.getId());
|
||||||
|
delegation.setPermissionId(permissionId);
|
||||||
|
delegation.setCreatedBy(loginUserId);
|
||||||
|
delegation.setCreatedAt(now);
|
||||||
|
delegationList.add(delegation);
|
||||||
|
}
|
||||||
|
rolePermissionDelegationMapper.createBatch(delegationList);
|
||||||
|
}
|
||||||
|
logRoleAuthorizationChange("USER_ROLE_DELEGATION_PERMISSION_UPDATE", loginUserId, role.getId(), addIdList, deleteIdList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Role> listGrantableParentRole(String roleId) {
|
||||||
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
|
Role targetRole = mapper.selectById(roleId);
|
||||||
|
TimiException.required(targetRole, "not found role");
|
||||||
|
boolean systemAdmin = authorizationScopeService.isSystemAdmin(loginUserId);
|
||||||
|
Set<String> manageableRoleIdSet = Arrays.stream(ModuleCode.values())
|
||||||
|
.flatMap(moduleCode -> authorizationScopeService.listManageableRole(loginUserId, moduleCode).stream())
|
||||||
|
.map(Role::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Set<String> operatorRoleIdSet = listOperatorRoleId(loginUserId);
|
||||||
|
return mapper.selectAll()
|
||||||
|
.stream()
|
||||||
|
.filter(role -> !Objects.equals(role.getId(), targetRole.getId()))
|
||||||
|
.filter(role -> !isManagementDescendant(targetRole.getId(), role.getId()))
|
||||||
|
.filter(role -> systemAdmin || operatorRoleIdSet.contains(role.getId()) || manageableRoleIdSet.contains(role.getId()))
|
||||||
|
.collect(Collectors.toMap(Role::getId, Function.identity(), (oldValue, newValue) -> oldValue, LinkedHashMap::new))
|
||||||
|
.values()
|
||||||
|
.stream()
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void updateRoleParent(Role role) {
|
||||||
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
|
TimiException.required(role.getId(), "not found role.id");
|
||||||
|
Role dbRole = mapper.selectById(role.getId());
|
||||||
|
TimiException.required(dbRole, "not found role");
|
||||||
|
authorizationBoundaryService.checkCanUpdateRoleParent(loginUserId, role.getId(), role.getParentRoleId());
|
||||||
|
mapper.updateSelective(role);
|
||||||
|
Logger logger = new Logger(Logger.Module.USER, Logger.Level.INFO, "USER_ROLE_PARENT_UPDATE");
|
||||||
|
logger.setUserId(loginUserId);
|
||||||
|
logger.setContent("roleId=%s;oldParentRoleId=%s;newParentRoleId=%s".formatted(role.getId(), dbRole.getParentRoleId(), role.getParentRoleId()));
|
||||||
|
logger.setResult(role.getId());
|
||||||
|
loggerService.createSync(logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> listAllRoleIdIncludingChildren(Collection<String> idList) {
|
||||||
|
return roleHierarchyService.listAllRoleIdIncludingChildren(idList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillRoleDefault(Role role) {
|
||||||
|
role.setBuiltin(TimiJava.defaultIfNull(role.getBuiltin(), false));
|
||||||
|
role.setProtectedRole(TimiJava.defaultIfNull(role.getProtectedRole(), false));
|
||||||
|
role.setOwnerType(TimiJava.defaultIfNull(role.getOwnerType(), AuthOwnerType.MODULE));
|
||||||
|
role.setOwnerId(TimiJava.defaultIfEmpty(role.getOwnerId(), role.getModuleCode() == null ? null : role.getModuleCode().name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillRelationScope(RolePermissionInherit relation, Role childRole, String createdBy) {
|
||||||
|
if (childRole == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
relation.setOwnerType(childRole.getOwnerType());
|
||||||
|
relation.setOwnerId(childRole.getOwnerId());
|
||||||
|
relation.setCreatedBy(createdBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canTouchRelation(String userId, Role parentRole, RolePermissionInherit relation) {
|
||||||
|
if (authorizationScopeService.isSystemAdmin(userId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (relation.getOwnerType() == AuthOwnerType.MODULE && parentRole != null && Objects.equals(relation.getOwnerId(), parentRole.getModuleCode().name())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ModuleResourceScopeResolver resolver = moduleResourceScopeResolverList.stream()
|
||||||
|
.filter(item -> parentRole != null && parentRole.getModuleCode() == item.moduleCode())
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
return resolver != null && resolver.canUseOwnerScope(userId, relation.getOwnerType(), relation.getOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCoreDelegationRoot(Role role) {
|
||||||
|
return role != null
|
||||||
|
&& ModuleCode.CORE == role.getModuleCode()
|
||||||
|
&& (com.imyeyu.api.bean.CoreRoleCode.SYSTEM.name().equals(role.getCode()) || com.imyeyu.api.bean.CoreRoleCode.ADMIN.name().equals(role.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCoreAuthorizationPermission(Permission permission) {
|
||||||
|
return permission != null && ModuleCode.CORE == permission.getModuleCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listOperatorGrantablePermissionId(String loginUserId, List<Permission> permissionList) {
|
||||||
|
if (TimiJava.isEmpty(permissionList)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return permissionList.stream()
|
||||||
|
.map(Permission::getModuleCode)
|
||||||
|
.distinct()
|
||||||
|
.flatMap(moduleCode -> authorizationScopeService.listGrantablePermission(loginUserId, moduleCode).stream())
|
||||||
|
.map(Permission::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listRoleDirectGrantablePermissionId(String loginUserId, Role role) {
|
||||||
|
boolean systemAdmin = authorizationScopeService.isSystemAdmin(loginUserId);
|
||||||
|
if (systemAdmin && isCoreDelegationRoot(role) && TimiJava.isEmpty(role.getParentRoleId())) {
|
||||||
|
return Arrays.stream(ModuleCode.values())
|
||||||
|
.flatMap(moduleCode -> permissionService.listByModuleCode(moduleCode).stream())
|
||||||
|
.map(Permission::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
TimiException.requiredTrue(TimiJava.isNotEmpty(role.getParentRoleId()), "目标角色缺少管理父级");
|
||||||
|
Set<String> parentDelegationPermissionIdSet = listDelegationParentPermissionId(role.getId());
|
||||||
|
if (systemAdmin) {
|
||||||
|
return parentDelegationPermissionIdSet;
|
||||||
|
}
|
||||||
|
List<Permission> parentDelegationPermissionList = listPermissionById(parentDelegationPermissionIdSet);
|
||||||
|
Set<String> operatorGrantablePermissionIdSet = listOperatorGrantablePermissionId(loginUserId, parentDelegationPermissionList);
|
||||||
|
return parentDelegationPermissionIdSet.stream()
|
||||||
|
.filter(operatorGrantablePermissionIdSet::contains)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Permission> listPermissionById(Set<String> permissionIdSet) {
|
||||||
|
if (TimiJava.isEmpty(permissionIdSet)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
return Arrays.stream(ModuleCode.values())
|
||||||
|
.flatMap(moduleCode -> permissionService.listByModuleCode(moduleCode).stream())
|
||||||
|
.filter(permission -> permissionIdSet.contains(permission.getId()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listDelegationParentPermissionId(String roleId) {
|
||||||
|
Role role = mapper.selectById(roleId);
|
||||||
|
if (role == null || TimiJava.isEmpty(role.getParentRoleId())) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return rolePermissionDelegationMapper.selectPermissionByRoleIdList(List.of(role.getParentRoleId()))
|
||||||
|
.stream()
|
||||||
|
.map(Permission::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listOperatorRoleId(String loginUserId) {
|
||||||
|
List<String> roleIdList = userRoleMapper.selectByUserIdList(List.of(loginUserId)).stream().map(UserRole::getRoleId).toList();
|
||||||
|
if (TimiJava.isEmpty(roleIdList)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return new HashSet<>(roleHierarchyService.listAllRoleIdIncludingChildren(loginUserId, roleIdList));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasRoleBaseField(Role role) {
|
||||||
|
return role.getModuleCode() != null
|
||||||
|
|| role.getCode() != null
|
||||||
|
|| role.getNameLangId() != null
|
||||||
|
|| role.getDescription() != null
|
||||||
|
|| role.getBuiltin() != null
|
||||||
|
|| role.getProtectedRole() != null
|
||||||
|
|| role.getParentRoleId() != null
|
||||||
|
|| role.getOwnerType() != null
|
||||||
|
|| role.getOwnerId() != null
|
||||||
|
|| role.getCreatedBy() != null
|
||||||
|
|| role.getName() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkManageableRole(String loginUserId, Role role) {
|
||||||
|
Set<String> manageableRoleIdSet = authorizationScopeService.listManageableRole(loginUserId, role.getModuleCode())
|
||||||
|
.stream()
|
||||||
|
.map(Role::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
TimiException.requiredTrue(manageableRoleIdSet.contains(role.getId()), "角色不在可管理范围内:%s".formatted(role.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isManagementDescendant(String parentRoleId, String childRoleId) {
|
||||||
|
List<Role> childRoleList = mapper.selectByParentRoleId(parentRoleId);
|
||||||
|
for (Role childRole : childRoleList) {
|
||||||
|
if (childRoleId.equals(childRole.getId()) || isManagementDescendant(childRole.getId(), childRoleId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logRoleAuthorizationChange(String operation, String loginUserId, String roleId, Collection<String> addIdList, Collection<String> deleteIdList) {
|
||||||
|
if (TimiJava.isEmpty(addIdList) && TimiJava.isEmpty(deleteIdList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Logger logger = new Logger(Logger.Module.USER, Logger.Level.INFO, operation);
|
||||||
|
logger.setUserId(loginUserId);
|
||||||
|
logger.setContent("roleId=%s;addCount=%d;deleteCount=%d;addIdList=%s;deleteIdList=%s".formatted(
|
||||||
|
roleId,
|
||||||
|
TimiJava.isEmpty(addIdList) ? 0 : addIdList.size(),
|
||||||
|
TimiJava.isEmpty(deleteIdList) ? 0 : deleteIdList.size(),
|
||||||
|
TimiJava.isEmpty(addIdList) ? "" : String.join(",", addIdList),
|
||||||
|
TimiJava.isEmpty(deleteIdList) ? "" : String.join(",", deleteIdList)
|
||||||
|
));
|
||||||
|
logger.setResult(roleId);
|
||||||
|
loggerService.createSync(logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String relationKey(RolePermissionInherit relation) {
|
||||||
|
return "%s|%s|%s".formatted(relation.getChildRoleId(), relation.getOwnerType(), relation.getOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String relationKey(String childRoleId, Role childRole) {
|
||||||
|
return "%s|%s|%s".formatted(childRoleId, childRole == null ? null : childRole.getOwnerType(), childRole == null ? null : childRole.getOwnerId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-2
@@ -2,12 +2,16 @@ package com.imyeyu.api.modules.user.service.implement;
|
|||||||
|
|
||||||
import com.imyeyu.api.bean.ModuleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.modules.user.bean.BootstrapData;
|
||||||
import com.imyeyu.api.modules.user.entity.Permission;
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
import com.imyeyu.api.modules.user.entity.Role;
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
import com.imyeyu.api.modules.user.entity.UserRole;
|
import com.imyeyu.api.modules.user.entity.UserRole;
|
||||||
import com.imyeyu.api.modules.user.mapper.UserRoleMapper;
|
import com.imyeyu.api.modules.user.mapper.UserRoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.service.AuthorizationBoundaryService;
|
||||||
import com.imyeyu.api.modules.user.service.PermissionService;
|
import com.imyeyu.api.modules.user.service.PermissionService;
|
||||||
|
import com.imyeyu.api.modules.user.service.RoleHierarchyService;
|
||||||
import com.imyeyu.api.modules.user.service.RoleService;
|
import com.imyeyu.api.modules.user.service.RoleService;
|
||||||
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.api.modules.user.service.UserRoleService;
|
import com.imyeyu.api.modules.user.service.UserRoleService;
|
||||||
import com.imyeyu.api.modules.user.vo.role.UserRoleAuthorizeRequest;
|
import com.imyeyu.api.modules.user.vo.role.UserRoleAuthorizeRequest;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
@@ -40,6 +44,9 @@ public class UserRoleServiceImplement extends AbstractEntityService<UserRole, St
|
|||||||
|
|
||||||
private final RoleService roleService;
|
private final RoleService roleService;
|
||||||
private final PermissionService permissionService;
|
private final PermissionService permissionService;
|
||||||
|
private final UserLoginService userLoginService;
|
||||||
|
private final AuthorizationBoundaryService authorizationBoundaryService;
|
||||||
|
private final RoleHierarchyService roleHierarchyService;
|
||||||
|
|
||||||
private final UserRoleMapper mapper;
|
private final UserRoleMapper mapper;
|
||||||
|
|
||||||
@@ -78,7 +85,17 @@ public class UserRoleServiceImplement extends AbstractEntityService<UserRole, St
|
|||||||
Set<String> addIdList = new HashSet<>(newIdSet);
|
Set<String> addIdList = new HashSet<>(newIdSet);
|
||||||
addIdList.removeAll(dbIdSet);
|
addIdList.removeAll(dbIdSet);
|
||||||
|
|
||||||
|
String loginUserId = userLoginService.getRequireLoginUserId();
|
||||||
|
authorizationBoundaryService.checkCanRevokeRoles(loginUserId, req.getUserId(), deleteIdList);
|
||||||
|
authorizationBoundaryService.checkCanGrantRoles(loginUserId, req.getUserId(), addIdList);
|
||||||
|
|
||||||
if (TimiJava.isNotEmpty(deleteIdList)) {
|
if (TimiJava.isNotEmpty(deleteIdList)) {
|
||||||
|
for (String roleId : deleteIdList) {
|
||||||
|
UserRole userRole = new UserRole();
|
||||||
|
userRole.setUserId(req.getUserId());
|
||||||
|
userRole.setRoleId(roleId);
|
||||||
|
BootstrapData.checkProtectedUserRole(userRole);
|
||||||
|
}
|
||||||
deleteByRoleIdList(req.getUserId(), new ArrayList<>(deleteIdList));
|
deleteByRoleIdList(req.getUserId(), new ArrayList<>(deleteIdList));
|
||||||
}
|
}
|
||||||
if (TimiJava.isNotEmpty(addIdList)) {
|
if (TimiJava.isNotEmpty(addIdList)) {
|
||||||
@@ -123,7 +140,7 @@ public class UserRoleServiceImplement extends AbstractEntityService<UserRole, St
|
|||||||
@Override
|
@Override
|
||||||
public List<String> listAllRoleCodeByUserId(String userId) {
|
public List<String> listAllRoleCodeByUserId(String userId) {
|
||||||
List<String> roleIdList = listRoleByUserId(userId).stream().map(Role::getId).toList();
|
List<String> roleIdList = listRoleByUserId(userId).stream().map(Role::getId).toList();
|
||||||
List<String> allRoleIdList = roleService.listAllRoleIdIncludingChildren(roleIdList);
|
List<String> allRoleIdList = roleHierarchyService.listAllRoleIdIncludingChildren(userId, roleIdList);
|
||||||
if (TimiJava.isEmpty(allRoleIdList)) {
|
if (TimiJava.isEmpty(allRoleIdList)) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
@@ -133,7 +150,7 @@ public class UserRoleServiceImplement extends AbstractEntityService<UserRole, St
|
|||||||
@Override
|
@Override
|
||||||
public List<Permission> listAllPermissionByUserId(String userId) {
|
public List<Permission> listAllPermissionByUserId(String userId) {
|
||||||
List<String> roleIdList = listRoleByUserId(userId).stream().map(Role::getId).toList();
|
List<String> roleIdList = listRoleByUserId(userId).stream().map(Role::getId).toList();
|
||||||
List<String> allRoleIdList = roleService.listAllRoleIdIncludingChildren(roleIdList);
|
List<String> allRoleIdList = roleHierarchyService.listAllRoleIdIncludingChildren(userId, roleIdList);
|
||||||
return permissionService.listByRoleId(allRoleIdList);
|
return permissionService.listByRoleId(allRoleIdList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.imyeyu.api.util;
|
package com.imyeyu.api.util;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.CorePermissionCode;
|
||||||
|
import com.imyeyu.api.bean.CoreRoleCode;
|
||||||
import com.imyeyu.api.bean.ModuleCode;
|
import com.imyeyu.api.bean.ModuleCode;
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.common.bean.ImageType;
|
import com.imyeyu.api.modules.common.bean.ImageType;
|
||||||
@@ -14,16 +16,18 @@ import com.imyeyu.api.modules.user.bean.BuiltinRoleCode;
|
|||||||
import com.imyeyu.api.modules.user.entity.Permission;
|
import com.imyeyu.api.modules.user.entity.Permission;
|
||||||
import com.imyeyu.api.modules.user.entity.Role;
|
import com.imyeyu.api.modules.user.entity.Role;
|
||||||
import com.imyeyu.api.modules.user.entity.RolePermission;
|
import com.imyeyu.api.modules.user.entity.RolePermission;
|
||||||
import com.imyeyu.api.modules.user.entity.RoleRelation;
|
import com.imyeyu.api.modules.user.entity.RolePermissionDelegation;
|
||||||
|
import com.imyeyu.api.modules.user.entity.RolePermissionInherit;
|
||||||
import com.imyeyu.api.modules.user.entity.User;
|
import com.imyeyu.api.modules.user.entity.User;
|
||||||
import com.imyeyu.api.modules.user.entity.UserRole;
|
import com.imyeyu.api.modules.user.entity.UserRole;
|
||||||
import com.imyeyu.api.modules.user.mapper.PermissionMapper;
|
import com.imyeyu.api.modules.user.mapper.PermissionMapper;
|
||||||
import com.imyeyu.api.modules.user.mapper.RoleMapper;
|
import com.imyeyu.api.modules.user.mapper.RoleMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RolePermissionDelegationMapper;
|
||||||
|
import com.imyeyu.api.modules.user.mapper.RolePermissionInheritMapper;
|
||||||
import com.imyeyu.api.modules.user.mapper.RolePermissionMapper;
|
import com.imyeyu.api.modules.user.mapper.RolePermissionMapper;
|
||||||
import com.imyeyu.api.modules.user.mapper.RoleRelationMapper;
|
|
||||||
import com.imyeyu.api.modules.user.mapper.UserRoleMapper;
|
import com.imyeyu.api.modules.user.mapper.UserRoleMapper;
|
||||||
import com.imyeyu.api.modules.user.service.PermissionService;
|
import com.imyeyu.api.modules.user.service.PermissionService;
|
||||||
import com.imyeyu.api.modules.user.service.RoleRelationService;
|
import com.imyeyu.api.modules.user.service.RolePermissionInheritService;
|
||||||
import com.imyeyu.api.modules.user.service.RoleService;
|
import com.imyeyu.api.modules.user.service.RoleService;
|
||||||
import com.imyeyu.api.modules.user.service.UserRoleService;
|
import com.imyeyu.api.modules.user.service.UserRoleService;
|
||||||
import com.imyeyu.api.modules.user.service.UserService;
|
import com.imyeyu.api.modules.user.service.UserService;
|
||||||
@@ -53,13 +57,14 @@ public class InitBuiltinUser implements ApplicationRunner {
|
|||||||
private final LoggerService loggerService;
|
private final LoggerService loggerService;
|
||||||
private final UserRoleService userRoleService;
|
private final UserRoleService userRoleService;
|
||||||
private final PermissionService permissionService;
|
private final PermissionService permissionService;
|
||||||
private final RoleRelationService roleRelationService;
|
private final RolePermissionInheritService rolePermissionInheritService;
|
||||||
|
|
||||||
private final RoleMapper roleMapper;
|
private final RoleMapper roleMapper;
|
||||||
private final UserRoleMapper userRoleMapper;
|
private final UserRoleMapper userRoleMapper;
|
||||||
private final PermissionMapper permissionMapper;
|
private final PermissionMapper permissionMapper;
|
||||||
private final RolePermissionMapper rolePermissionMapper;
|
private final RolePermissionMapper rolePermissionMapper;
|
||||||
private final RoleRelationMapper roleRelationMapper;
|
private final RolePermissionDelegationMapper rolePermissionDelegationMapper;
|
||||||
|
private final RolePermissionInheritMapper rolePermissionInheritMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化内建数据
|
* 初始化内建数据
|
||||||
@@ -73,8 +78,10 @@ public class InitBuiltinUser implements ApplicationRunner {
|
|||||||
// 先同步所有内建权限和角色,再补齐继承与默认授权关系
|
// 先同步所有内建权限和角色,再补齐继承与默认授权关系
|
||||||
Map<String, Permission> permissionMap = initBuiltinPermissionMap();
|
Map<String, Permission> permissionMap = initBuiltinPermissionMap();
|
||||||
Map<String, Role> roleMap = initBuiltinRoleMap();
|
Map<String, Role> roleMap = initBuiltinRoleMap();
|
||||||
bindBuiltinRoleRelation(roleMap);
|
bindBuiltinRolePermissionInherit(roleMap);
|
||||||
|
bindBuiltinRoleParent(roleMap);
|
||||||
bindBuiltinPermission(roleMap, permissionMap);
|
bindBuiltinPermission(roleMap, permissionMap);
|
||||||
|
bindBuiltinPermissionDelegation(roleMap, permissionMap);
|
||||||
|
|
||||||
Role systemRole = roleMap.get(BuiltinAuthRegistry.toRoleKey(BootstrapData.SYS_ROLE_MODULE_CODE, BootstrapData.SYS_ROLE_CODE));
|
Role systemRole = roleMap.get(BuiltinAuthRegistry.toRoleKey(BootstrapData.SYS_ROLE_MODULE_CODE, BootstrapData.SYS_ROLE_CODE));
|
||||||
// 初始化系统用户
|
// 初始化系统用户
|
||||||
@@ -94,9 +101,7 @@ public class InitBuiltinUser implements ApplicationRunner {
|
|||||||
|
|
||||||
String logContent = "初始化 %s 自举用户成功,请立即修改密码:%s".formatted(BootstrapData.SYS_USER_NAME, password);
|
String logContent = "初始化 %s 自举用户成功,请立即修改密码:%s".formatted(BootstrapData.SYS_USER_NAME, password);
|
||||||
|
|
||||||
Logger logger = new Logger();
|
Logger logger = Logger.info(Logger.Module.SYSTEM, "INITIALIZE_SYSTEM_USER");
|
||||||
logger.setModule(Logger.Module.SYSTEM);
|
|
||||||
logger.setOperation("INITIALIZE_SYSTEM_USER");
|
|
||||||
logger.setUserId(user.getId());
|
logger.setUserId(user.getId());
|
||||||
logger.setContent(logContent);
|
logger.setContent(logContent);
|
||||||
loggerService.create(logger);
|
loggerService.create(logger);
|
||||||
@@ -131,8 +136,12 @@ public class InitBuiltinUser implements ApplicationRunner {
|
|||||||
permission.setModuleCode(item.getModuleCode());
|
permission.setModuleCode(item.getModuleCode());
|
||||||
permission.setCode(item.getValue());
|
permission.setCode(item.getValue());
|
||||||
permission.setName(item.getDefaultName());
|
permission.setName(item.getDefaultName());
|
||||||
|
fillBuiltinPermission(permission, item);
|
||||||
permissionService.create(permission);
|
permissionService.create(permission);
|
||||||
permission = permissionMapper.selectByExample(example);
|
permission = permissionMapper.selectByExample(example);
|
||||||
|
} else {
|
||||||
|
fillBuiltinPermission(permission, item);
|
||||||
|
permissionMapper.updateSelective(permission);
|
||||||
}
|
}
|
||||||
permissionMap.put(BuiltinAuthRegistry.toPermissionKey(item.getModuleCode(), item.getValue()), permission);
|
permissionMap.put(BuiltinAuthRegistry.toPermissionKey(item.getModuleCode(), item.getValue()), permission);
|
||||||
}
|
}
|
||||||
@@ -155,8 +164,12 @@ public class InitBuiltinUser implements ApplicationRunner {
|
|||||||
role.setModuleCode(item.getModuleCode());
|
role.setModuleCode(item.getModuleCode());
|
||||||
role.setCode(code);
|
role.setCode(code);
|
||||||
role.setName(item.getDefaultName());
|
role.setName(item.getDefaultName());
|
||||||
|
fillBuiltinRole(role, item);
|
||||||
roleService.create(role);
|
roleService.create(role);
|
||||||
role = roleMapper.selectByExample(example);
|
role = roleMapper.selectByExample(example);
|
||||||
|
} else {
|
||||||
|
fillBuiltinRole(role, item);
|
||||||
|
roleMapper.updateSelective(role);
|
||||||
}
|
}
|
||||||
roleMap.put(BuiltinAuthRegistry.toRoleKey(item.getModuleCode(), code), role);
|
roleMap.put(BuiltinAuthRegistry.toRoleKey(item.getModuleCode(), code), role);
|
||||||
}
|
}
|
||||||
@@ -166,7 +179,7 @@ public class InitBuiltinUser implements ApplicationRunner {
|
|||||||
/// 按角色定义补齐父子角色关系
|
/// 按角色定义补齐父子角色关系
|
||||||
///
|
///
|
||||||
/// @param roleMap 已同步的角色映射
|
/// @param roleMap 已同步的角色映射
|
||||||
private void bindBuiltinRoleRelation(Map<String, Role> roleMap) {
|
private void bindBuiltinRolePermissionInherit(Map<String, Role> roleMap) {
|
||||||
for (BuiltinRoleCode item : BuiltinAuthRegistry.ROLE_CODE_LIST) {
|
for (BuiltinRoleCode item : BuiltinAuthRegistry.ROLE_CODE_LIST) {
|
||||||
if (TimiJava.isEmpty(item.getParentCode())) {
|
if (TimiJava.isEmpty(item.getParentCode())) {
|
||||||
continue;
|
continue;
|
||||||
@@ -176,15 +189,26 @@ public class InitBuiltinUser implements ApplicationRunner {
|
|||||||
if (parentRole == null || childRole == null) {
|
if (parentRole == null || childRole == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
RoleRelation relation = new RoleRelation();
|
RolePermissionInherit relation = new RolePermissionInherit();
|
||||||
relation.setParentRoleId(parentRole.getId());
|
relation.setParentRoleId(parentRole.getId());
|
||||||
relation.setChildRoleId(childRole.getId());
|
relation.setChildRoleId(childRole.getId());
|
||||||
if (roleRelationMapper.selectByExample(relation) == null) {
|
relation.setOwnerType(item.getOwnerType());
|
||||||
roleRelationService.create(relation);
|
relation.setOwnerId(item.getOwnerId());
|
||||||
|
if (rolePermissionInheritMapper.selectByExample(relation) == null) {
|
||||||
|
rolePermissionInheritService.create(relation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 补齐内置角色管理父级
|
||||||
|
///
|
||||||
|
/// @param roleMap 已同步的角色映射
|
||||||
|
private void bindBuiltinRoleParent(Map<String, Role> roleMap) {
|
||||||
|
setRoleParent(roleMap, ModuleCode.CORE, CoreRoleCode.ADMIN.name(), ModuleCode.CORE, CoreRoleCode.SYSTEM.name());
|
||||||
|
setRoleParent(roleMap, ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name(), ModuleCode.CORE, CoreRoleCode.ADMIN.name());
|
||||||
|
setRoleParent(roleMap, ModuleCode.GAO, GaoRoleCode.STORE_MANAGER.name(), ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name());
|
||||||
|
}
|
||||||
|
|
||||||
/// 按权限定义补齐 SYSTEM 和 ADMIN 的默认权限
|
/// 按权限定义补齐 SYSTEM 和 ADMIN 的默认权限
|
||||||
///
|
///
|
||||||
/// @param roleMap 已同步的角色映射
|
/// @param roleMap 已同步的角色映射
|
||||||
@@ -205,6 +229,12 @@ public class InitBuiltinUser implements ApplicationRunner {
|
|||||||
if (item.isGrantToAdmin() && adminRole != null) {
|
if (item.isGrantToAdmin() && adminRole != null) {
|
||||||
createRolePermissionIfAbsent(adminRole.getId(), permission.getId());
|
createRolePermissionIfAbsent(adminRole.getId(), permission.getId());
|
||||||
}
|
}
|
||||||
|
if (item instanceof CorePermissionCode corePermission && isGrantToModuleRoleManager(corePermission) && gaoGlobalManagerRole != null) {
|
||||||
|
createRolePermissionIfAbsent(gaoGlobalManagerRole.getId(), permission.getId());
|
||||||
|
}
|
||||||
|
if (item instanceof CorePermissionCode corePermission && isGrantToModuleRoleManager(corePermission) && gaoStoreManagerRole != null) {
|
||||||
|
createRolePermissionIfAbsent(gaoStoreManagerRole.getId(), permission.getId());
|
||||||
|
}
|
||||||
if (item instanceof GaoPermissionCode gaoPermission && gaoPermission.isGrantToGlobalManager() && gaoGlobalManagerRole != null) {
|
if (item instanceof GaoPermissionCode gaoPermission && gaoPermission.isGrantToGlobalManager() && gaoGlobalManagerRole != null) {
|
||||||
createRolePermissionIfAbsent(gaoGlobalManagerRole.getId(), permission.getId());
|
createRolePermissionIfAbsent(gaoGlobalManagerRole.getId(), permission.getId());
|
||||||
}
|
}
|
||||||
@@ -214,6 +244,35 @@ public class InitBuiltinUser implements ApplicationRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 按内置策略补齐角色转授权限
|
||||||
|
///
|
||||||
|
/// @param roleMap 已同步的角色映射
|
||||||
|
/// @param permissionMap 已同步的权限映射
|
||||||
|
private void bindBuiltinPermissionDelegation(Map<String, Role> roleMap, Map<String, Permission> permissionMap) {
|
||||||
|
Role systemRole = roleMap.get(BuiltinAuthRegistry.toRoleKey(BootstrapData.SYS_ROLE_MODULE_CODE, BootstrapData.SYS_ROLE_CODE));
|
||||||
|
Role adminRole = roleMap.get(BuiltinAuthRegistry.toRoleKey(BootstrapData.ADMIN_ROLE_MODULE_CODE, BootstrapData.ADMIN_ROLE_CODE));
|
||||||
|
Role gaoGlobalManagerRole = roleMap.get(BuiltinAuthRegistry.toRoleKey(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name()));
|
||||||
|
Role gaoStoreManagerRole = roleMap.get(BuiltinAuthRegistry.toRoleKey(ModuleCode.GAO, GaoRoleCode.STORE_MANAGER.name()));
|
||||||
|
for (BuiltinPermissionCode item : BuiltinAuthRegistry.PERMISSION_CODE_LIST) {
|
||||||
|
Permission permission = permissionMap.get(BuiltinAuthRegistry.toPermissionKey(item.getModuleCode(), item.getValue()));
|
||||||
|
if (permission == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (systemRole != null) {
|
||||||
|
createRolePermissionDelegationIfAbsent(systemRole.getId(), permission.getId());
|
||||||
|
}
|
||||||
|
if (adminRole != null) {
|
||||||
|
createRolePermissionDelegationIfAbsent(adminRole.getId(), permission.getId());
|
||||||
|
}
|
||||||
|
if (item instanceof GaoPermissionCode gaoPermission && gaoPermission.isGrantToStoreManager() && gaoGlobalManagerRole != null) {
|
||||||
|
createRolePermissionDelegationIfAbsent(gaoGlobalManagerRole.getId(), permission.getId());
|
||||||
|
}
|
||||||
|
if (item instanceof GaoPermissionCode gaoPermission && gaoPermission.isGrantToStoreManager() && gaoStoreManagerRole != null) {
|
||||||
|
createRolePermissionDelegationIfAbsent(gaoStoreManagerRole.getId(), permission.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 角色权限关系不存在时才创建
|
/// 角色权限关系不存在时才创建
|
||||||
///
|
///
|
||||||
/// @param roleId 角色 ID
|
/// @param roleId 角色 ID
|
||||||
@@ -230,4 +289,70 @@ public class InitBuiltinUser implements ApplicationRunner {
|
|||||||
rolePermission.setPermissionId(permissionId);
|
rolePermission.setPermissionId(permissionId);
|
||||||
rolePermissionMapper.insert(rolePermission);
|
rolePermissionMapper.insert(rolePermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 角色转授权限关系不存在时才创建
|
||||||
|
///
|
||||||
|
/// @param roleId 角色 ID
|
||||||
|
/// @param permissionId 权限 ID
|
||||||
|
private void createRolePermissionDelegationIfAbsent(String roleId, String permissionId) {
|
||||||
|
RolePermissionDelegation example = new RolePermissionDelegation();
|
||||||
|
example.setRoleId(roleId);
|
||||||
|
example.setPermissionId(permissionId);
|
||||||
|
if (rolePermissionDelegationMapper.selectByExample(example) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RolePermissionDelegation rolePermissionDelegation = new RolePermissionDelegation();
|
||||||
|
rolePermissionDelegation.setRoleId(roleId);
|
||||||
|
rolePermissionDelegation.setPermissionId(permissionId);
|
||||||
|
rolePermissionDelegationMapper.insert(rolePermissionDelegation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 填充内置权限元数据
|
||||||
|
///
|
||||||
|
/// @param permission 权限
|
||||||
|
/// @param item 内置权限定义
|
||||||
|
private void fillBuiltinPermission(Permission permission, BuiltinPermissionCode item) {
|
||||||
|
permission.setBuiltin(true);
|
||||||
|
permission.setProtectedPermission(item.isProtected());
|
||||||
|
permission.setOwnerType(item.getOwnerType());
|
||||||
|
permission.setOwnerId(item.getOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 填充内置角色元数据
|
||||||
|
///
|
||||||
|
/// @param role 角色
|
||||||
|
/// @param item 内置角色定义
|
||||||
|
private void fillBuiltinRole(Role role, BuiltinRoleCode item) {
|
||||||
|
role.setBuiltin(true);
|
||||||
|
role.setProtectedRole(item.isProtected());
|
||||||
|
role.setOwnerType(item.getOwnerType());
|
||||||
|
role.setOwnerId(item.getOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置角色管理父级
|
||||||
|
///
|
||||||
|
/// @param roleMap 已同步的角色映射
|
||||||
|
/// @param moduleCode 角色模块
|
||||||
|
/// @param roleCode 角色代码
|
||||||
|
/// @param parentModuleCode 父角色模块
|
||||||
|
/// @param parentRoleCode 父角色代码
|
||||||
|
private void setRoleParent(Map<String, Role> roleMap, ModuleCode moduleCode, String roleCode, ModuleCode parentModuleCode, String parentRoleCode) {
|
||||||
|
Role role = roleMap.get(BuiltinAuthRegistry.toRoleKey(moduleCode, roleCode));
|
||||||
|
Role parentRole = roleMap.get(BuiltinAuthRegistry.toRoleKey(parentModuleCode, parentRoleCode));
|
||||||
|
if (role == null || parentRole == null || parentRole.getId().equals(role.getParentRoleId())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
role.setParentRoleId(parentRole.getId());
|
||||||
|
roleMapper.updateSelective(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 判断权限是否默认授予模块角色管理员
|
||||||
|
///
|
||||||
|
/// @param permissionCode 核心权限
|
||||||
|
/// @return true 为授予
|
||||||
|
private boolean isGrantToModuleRoleManager(CorePermissionCode permissionCode) {
|
||||||
|
return permissionCode.name().startsWith("ROLE_")
|
||||||
|
|| permissionCode.name().startsWith("USER_ROLE_")
|
||||||
|
|| CorePermissionCode.PERMISSION_READ == permissionCode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
ALTER TABLE `role`
|
||||||
|
ADD COLUMN `builtin` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'true 为系统内置角色' AFTER `description`,
|
||||||
|
ADD COLUMN `protected_role` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'true 为受保护角色' AFTER `builtin`,
|
||||||
|
ADD COLUMN `parent_role_id` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '管理父角色 ID' AFTER `protected_role`,
|
||||||
|
ADD COLUMN `owner_type` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'MODULE' COMMENT '归属类型' AFTER `parent_role_id`,
|
||||||
|
ADD COLUMN `owner_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '归属 ID' AFTER `owner_type`,
|
||||||
|
ADD COLUMN `created_by` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '创建人用户 ID' AFTER `owner_id`,
|
||||||
|
ADD INDEX `idx_role_parent_role_id` (`parent_role_id`),
|
||||||
|
ADD INDEX `idx_role_owner` (`owner_type`, `owner_id`);
|
||||||
|
|
||||||
|
ALTER TABLE `permission`
|
||||||
|
ADD COLUMN `builtin` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'true 为系统内置权限' AFTER `description`,
|
||||||
|
ADD COLUMN `protected_permission` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'true 为受保护权限' AFTER `builtin`,
|
||||||
|
ADD COLUMN `owner_type` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'MODULE' COMMENT '归属类型' AFTER `protected_permission`,
|
||||||
|
ADD COLUMN `owner_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '归属 ID' AFTER `owner_type`,
|
||||||
|
ADD COLUMN `created_by` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '创建人用户 ID' AFTER `owner_id`,
|
||||||
|
ADD INDEX `idx_permission_owner` (`owner_type`, `owner_id`);
|
||||||
|
|
||||||
|
RENAME TABLE `role_relation` TO `role_permission_inherit`;
|
||||||
|
|
||||||
|
ALTER TABLE `role_permission_inherit`
|
||||||
|
DROP INDEX `idx_role_relation`,
|
||||||
|
DROP INDEX `idx_child_role_id`,
|
||||||
|
ADD INDEX `idx_role_permission_inherit` (`parent_role_id`, `child_role_id`),
|
||||||
|
ADD INDEX `idx_role_permission_inherit_child_role_id` (`child_role_id`);
|
||||||
|
|
||||||
|
ALTER TABLE `role_permission_inherit`
|
||||||
|
ADD COLUMN `owner_type` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'MODULE' COMMENT '归属类型' AFTER `child_role_id`,
|
||||||
|
ADD COLUMN `owner_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '归属 ID' AFTER `owner_type`,
|
||||||
|
ADD COLUMN `created_by` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '创建人用户 ID' AFTER `owner_id`,
|
||||||
|
ADD INDEX `idx_role_permission_inherit_owner` (`owner_type`, `owner_id`);
|
||||||
|
|
||||||
|
UPDATE `role_permission_inherit` inherit
|
||||||
|
INNER JOIN `role` child_role ON inherit.`child_role_id` = child_role.`id`
|
||||||
|
SET inherit.`owner_id` = child_role.`module_code`
|
||||||
|
WHERE
|
||||||
|
inherit.`owner_type` = 'MODULE'
|
||||||
|
AND inherit.`owner_id` IS NULL
|
||||||
|
AND inherit.`deleted_at` IS NULL;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
CREATE TABLE `role_permission_delegation` (
|
||||||
|
`id` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`role_id` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色 ID',
|
||||||
|
`permission_id` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '允许转授的权限 ID',
|
||||||
|
`created_by` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '创建人用户 ID',
|
||||||
|
`created_at` BIGINT(20) COMMENT '创建时间',
|
||||||
|
`deleted_at` BIGINT(20) COMMENT '删除时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
INDEX `idx_role_permission_delegation` (`role_id` ASC, `permission_id` ASC) USING BTREE,
|
||||||
|
INDEX `idx_permission_id` (`permission_id` ASC) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色转授权限' ROW_FORMAT = Dynamic;
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
UPDATE `role_permission_delegation` target
|
||||||
|
JOIN (
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY role_id, permission_id
|
||||||
|
ORDER BY created_at DESC, id DESC
|
||||||
|
) AS rn
|
||||||
|
FROM `role_permission_delegation`
|
||||||
|
WHERE deleted_at IS NULL
|
||||||
|
) duplicate ON duplicate.id = target.id
|
||||||
|
SET target.deleted_at = UNIX_TIMESTAMP() * 1000
|
||||||
|
WHERE duplicate.rn > 1;
|
||||||
|
|
||||||
|
ALTER TABLE `role_permission_delegation`
|
||||||
|
ADD COLUMN `active_key` TINYINT(1) GENERATED ALWAYS AS (IF(`deleted_at` IS NULL, 1, NULL)) STORED COMMENT '未删除唯一键';
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX `uk_role_permission_delegation`
|
||||||
|
ON `role_permission_delegation` (`role_id`, `permission_id`, `active_key`);
|
||||||
|
|
||||||
|
UPDATE `role_permission_inherit` target
|
||||||
|
JOIN (
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY parent_role_id, child_role_id, owner_type, IFNULL(owner_id, '')
|
||||||
|
ORDER BY created_at DESC, id DESC
|
||||||
|
) AS rn
|
||||||
|
FROM `role_permission_inherit`
|
||||||
|
WHERE deleted_at IS NULL
|
||||||
|
) duplicate ON duplicate.id = target.id
|
||||||
|
SET target.deleted_at = UNIX_TIMESTAMP() * 1000
|
||||||
|
WHERE duplicate.rn > 1;
|
||||||
|
|
||||||
|
ALTER TABLE `role_permission_inherit`
|
||||||
|
ADD COLUMN `active_key` TINYINT(1) GENERATED ALWAYS AS (IF(`deleted_at` IS NULL, 1, NULL)) STORED COMMENT '未删除唯一键',
|
||||||
|
ADD COLUMN `owner_key` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci GENERATED ALWAYS AS (IFNULL(`owner_id`, '')) STORED COMMENT '归属唯一键';
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX `uk_role_permission_inherit`
|
||||||
|
ON `role_permission_inherit` (`parent_role_id`, `child_role_id`, `owner_type`, `owner_key`, `active_key`);
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `gao_point_account` (
|
||||||
|
`id` VARCHAR(36) NOT NULL,
|
||||||
|
`store_id` VARCHAR(36) NOT NULL COMMENT '门店 ID',
|
||||||
|
`customer_id` VARCHAR(36) NOT NULL COMMENT '客户 ID',
|
||||||
|
`active_customer_id` VARCHAR(36) GENERATED ALWAYS AS (
|
||||||
|
CASE WHEN `deleted_at` IS NULL THEN `customer_id` END
|
||||||
|
) STORED COMMENT '未删除客户 ID' INVISIBLE,
|
||||||
|
`balance` BIGINT NOT NULL DEFAULT 0 COMMENT '当前可用积分余额',
|
||||||
|
`total_earned` BIGINT NOT NULL DEFAULT 0 COMMENT '累计获得积分',
|
||||||
|
`total_revoked` BIGINT NOT NULL DEFAULT 0 COMMENT '累计撤销积分',
|
||||||
|
`total_adjusted` BIGINT NOT NULL DEFAULT 0 COMMENT '累计人工调整积分',
|
||||||
|
`version` BIGINT NOT NULL DEFAULT 0 COMMENT '乐观锁版本',
|
||||||
|
`created_at` BIGINT NOT NULL,
|
||||||
|
`updated_at` BIGINT NULL,
|
||||||
|
`deleted_at` BIGINT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_gao_point_account_customer` (`store_id`, `active_customer_id`),
|
||||||
|
KEY `idx_gao_point_account_customer_id` (`customer_id`),
|
||||||
|
KEY `idx_gao_point_account_store_balance` (`store_id`, `deleted_at`, `balance`)
|
||||||
|
) COMMENT='GAO 客户积分账户';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `gao_point_rule` (
|
||||||
|
`id` VARCHAR(36) NOT NULL,
|
||||||
|
`store_id` VARCHAR(36) NOT NULL COMMENT '门店 ID',
|
||||||
|
`event_id` VARCHAR(36) NOT NULL COMMENT '登记事件 ID',
|
||||||
|
`name` VARCHAR(64) NOT NULL COMMENT '规则名称',
|
||||||
|
`status` VARCHAR(32) NOT NULL COMMENT '规则状态',
|
||||||
|
`trigger_type` VARCHAR(32) NOT NULL COMMENT '触发类型',
|
||||||
|
`point_mode` VARCHAR(32) NOT NULL COMMENT '积分值模式',
|
||||||
|
`point_value` BIGINT NULL COMMENT '固定积分值',
|
||||||
|
`condition_json` JSON NULL COMMENT '条件配置',
|
||||||
|
`begin_at` BIGINT NULL COMMENT '生效开始时间戳,毫秒',
|
||||||
|
`end_at` BIGINT NULL COMMENT '生效结束时间戳,毫秒',
|
||||||
|
`priority` INT NOT NULL DEFAULT 0 COMMENT '优先级,数值小优先',
|
||||||
|
`exclusive` BIT(1) NOT NULL DEFAULT b'0' COMMENT 'true 为命中后停止继续匹配后续规则',
|
||||||
|
`remark` TEXT NULL COMMENT '备注',
|
||||||
|
`created_at` BIGINT NOT NULL,
|
||||||
|
`updated_at` BIGINT NULL,
|
||||||
|
`deleted_at` BIGINT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_gao_point_rule_match` (`store_id`, `event_id`, `status`, `deleted_at`, `priority`),
|
||||||
|
KEY `idx_gao_point_rule_type` (`trigger_type`, `point_mode`)
|
||||||
|
) COMMENT='GAO 客户积分规则';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `gao_point_ledger` (
|
||||||
|
`id` VARCHAR(36) NOT NULL,
|
||||||
|
`account_id` VARCHAR(36) NOT NULL COMMENT '客户积分账户 ID',
|
||||||
|
`store_id` VARCHAR(36) NOT NULL COMMENT '门店 ID',
|
||||||
|
`customer_id` VARCHAR(36) NOT NULL COMMENT '客户 ID',
|
||||||
|
`change_type` VARCHAR(32) NOT NULL COMMENT '变化类型',
|
||||||
|
`direction` VARCHAR(16) NOT NULL COMMENT '方向',
|
||||||
|
`point_delta` BIGINT NOT NULL COMMENT '本次变化积分,增加为正,减少为负',
|
||||||
|
`balance_before` BIGINT NOT NULL COMMENT '变化前余额',
|
||||||
|
`balance_after` BIGINT NOT NULL COMMENT '变化后余额',
|
||||||
|
`rule_id` VARCHAR(36) NULL COMMENT '积分规则 ID',
|
||||||
|
`event_id` VARCHAR(36) NULL COMMENT '登记事件 ID',
|
||||||
|
`event_record_id` VARCHAR(36) NULL COMMENT '登记记录 ID',
|
||||||
|
`source_payload` JSON NULL COMMENT '来源快照',
|
||||||
|
`idempotency_key` VARCHAR(128) NOT NULL COMMENT '幂等键',
|
||||||
|
`remark` TEXT NULL COMMENT '备注',
|
||||||
|
`operator_user_id` VARCHAR(36) NULL COMMENT '操作用户 ID',
|
||||||
|
`occurred_at` BIGINT NOT NULL COMMENT '发生时间',
|
||||||
|
`created_at` BIGINT NOT NULL,
|
||||||
|
`updated_at` BIGINT NULL,
|
||||||
|
`deleted_at` BIGINT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_gao_point_ledger_idempotency` (`idempotency_key`),
|
||||||
|
KEY `idx_gao_point_ledger_account_time` (`account_id`, `deleted_at`, `occurred_at`),
|
||||||
|
KEY `idx_gao_point_ledger_customer_time` (`store_id`, `customer_id`, `deleted_at`, `occurred_at`),
|
||||||
|
KEY `idx_gao_point_ledger_event_record` (`event_record_id`, `change_type`),
|
||||||
|
KEY `idx_gao_point_ledger_rule_time` (`rule_id`, `occurred_at`)
|
||||||
|
) COMMENT='GAO 客户积分流水';
|
||||||
@@ -203,4 +203,93 @@
|
|||||||
ORDER BY `rank`
|
ORDER BY `rank`
|
||||||
LIMIT #{offset}, #{limit}
|
LIMIT #{offset}, #{limit}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<sql id="storeChartRangeWhere">
|
||||||
|
r.`store_id` = #{storeId}
|
||||||
|
AND r.`registered_at` >= #{beginAt}
|
||||||
|
AND r.`registered_at` <= #{endAt}
|
||||||
|
AND r.`deleted_at` IS NULL
|
||||||
|
<if test="eventIdList != null and eventIdList.size() > 0">
|
||||||
|
AND r.`event_id` IN
|
||||||
|
<foreach collection="eventIdList" item="eventId" open="(" separator="," close=")">
|
||||||
|
#{eventId}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
</sql>
|
||||||
|
<select id="stateDailyByRange" resultType="com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView">
|
||||||
|
SELECT
|
||||||
|
r.`event_id` AS `eventId`,
|
||||||
|
r.`registered_date_value` AS `dateValue`,
|
||||||
|
COUNT(1) AS `dailyCount`
|
||||||
|
FROM `gao_event_record` r
|
||||||
|
WHERE
|
||||||
|
<include refid="storeChartRangeWhere"/>
|
||||||
|
GROUP BY
|
||||||
|
r.`event_id`,
|
||||||
|
r.`registered_date_value`
|
||||||
|
ORDER BY
|
||||||
|
r.`registered_date_value` ASC,
|
||||||
|
r.`event_id` ASC
|
||||||
|
</select>
|
||||||
|
<select id="selectCalendarByRange" resultType="com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView">
|
||||||
|
SELECT
|
||||||
|
r.`id`,
|
||||||
|
r.`customer_id` AS `customerId`,
|
||||||
|
c.`code` AS `customerCode`,
|
||||||
|
c.`name` AS `customerName`,
|
||||||
|
r.`event_id` AS `eventId`,
|
||||||
|
r.`registered_at` AS `registeredAt`
|
||||||
|
FROM `gao_event_record` r
|
||||||
|
LEFT JOIN `gao_customer` c ON r.`customer_id` = c.`id` AND c.`deleted_at` IS NULL
|
||||||
|
WHERE
|
||||||
|
<include refid="storeChartRangeWhere"/>
|
||||||
|
ORDER BY
|
||||||
|
r.`registered_at` ASC,
|
||||||
|
r.`created_at` ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<sql id="customerChartRangeWhere">
|
||||||
|
r.`store_id` = #{storeId}
|
||||||
|
AND r.`customer_id` = #{customerId}
|
||||||
|
AND r.`registered_at` >= #{beginAt}
|
||||||
|
AND r.`registered_at` <= #{endAt}
|
||||||
|
AND r.`deleted_at` IS NULL
|
||||||
|
<if test="eventIdList != null and eventIdList.size() > 0">
|
||||||
|
AND r.`event_id` IN
|
||||||
|
<foreach collection="eventIdList" item="eventId" open="(" separator="," close=")">
|
||||||
|
#{eventId}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
</sql>
|
||||||
|
<select id="stateCustomerDailyByRange" resultType="com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView">
|
||||||
|
SELECT
|
||||||
|
r.`event_id` AS `eventId`,
|
||||||
|
r.`registered_date_value` AS `dateValue`,
|
||||||
|
COUNT(1) AS `dailyCount`
|
||||||
|
FROM `gao_event_record` r
|
||||||
|
WHERE
|
||||||
|
<include refid="customerChartRangeWhere"/>
|
||||||
|
GROUP BY
|
||||||
|
r.`event_id`,
|
||||||
|
r.`registered_date_value`
|
||||||
|
ORDER BY
|
||||||
|
r.`registered_date_value` ASC,
|
||||||
|
r.`event_id` ASC
|
||||||
|
</select>
|
||||||
|
<select id="selectCustomerCalendarByRange" resultType="com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView">
|
||||||
|
SELECT
|
||||||
|
r.`id`,
|
||||||
|
r.`customer_id` AS `customerId`,
|
||||||
|
c.`code` AS `customerCode`,
|
||||||
|
c.`name` AS `customerName`,
|
||||||
|
r.`event_id` AS `eventId`,
|
||||||
|
r.`registered_at` AS `registeredAt`
|
||||||
|
FROM `gao_event_record` r
|
||||||
|
LEFT JOIN `gao_customer` c ON r.`customer_id` = c.`id` AND c.`deleted_at` IS NULL
|
||||||
|
WHERE
|
||||||
|
<include refid="customerChartRangeWhere"/>
|
||||||
|
ORDER BY
|
||||||
|
r.`registered_at` DESC,
|
||||||
|
r.`created_at` DESC
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?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.imyeyu.api.modules.gao.mapper.GaoPointAccountMapper">
|
||||||
|
<select id="selectByStoreIdAndCustomerId" resultType="com.imyeyu.api.modules.gao.entity.GaoPointAccount">
|
||||||
|
SELECT *
|
||||||
|
FROM `gao_point_account`
|
||||||
|
WHERE `store_id` = #{storeId}
|
||||||
|
AND `customer_id` = #{customerId}
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertIfAbsent">
|
||||||
|
INSERT INTO `gao_point_account` (
|
||||||
|
`id`, `store_id`, `customer_id`, `balance`, `total_earned`, `total_revoked`, `total_adjusted`, `version`, `created_at`, `updated_at`, `deleted_at`
|
||||||
|
) VALUES (
|
||||||
|
#{account.id}, #{account.storeId}, #{account.customerId}, #{account.balance}, #{account.totalEarned}, #{account.totalRevoked}, #{account.totalAdjusted}, #{account.version}, #{account.createdAt}, #{account.updatedAt}, #{account.deletedAt}
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE `id` = `id`
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<select id="selectForUpdate" resultType="com.imyeyu.api.modules.gao.entity.GaoPointAccount">
|
||||||
|
SELECT *
|
||||||
|
FROM `gao_point_account`
|
||||||
|
WHERE `id` = #{id}
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
LIMIT 1
|
||||||
|
FOR UPDATE
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<update id="updateBalance">
|
||||||
|
UPDATE `gao_point_account`
|
||||||
|
SET
|
||||||
|
`balance` = `balance` + #{delta},
|
||||||
|
`total_earned` = `total_earned` + #{totalEarnedDelta},
|
||||||
|
`total_revoked` = `total_revoked` + #{totalRevokedDelta},
|
||||||
|
`total_adjusted` = `total_adjusted` + #{totalAdjustedDelta},
|
||||||
|
`version` = `version` + 1,
|
||||||
|
`updated_at` = #{updatedAt}
|
||||||
|
WHERE `id` = #{id}
|
||||||
|
AND `balance` + #{delta} >= 0
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
</update>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<?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.imyeyu.api.modules.gao.mapper.GaoPointLedgerMapper">
|
||||||
|
<select id="selectByIdempotencyKey" resultType="com.imyeyu.api.modules.gao.entity.GaoPointLedger">
|
||||||
|
SELECT *
|
||||||
|
FROM `gao_point_ledger`
|
||||||
|
WHERE `idempotency_key` = #{idempotencyKey}
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectEarnByEventRecordId" resultType="com.imyeyu.api.modules.gao.entity.GaoPointLedger">
|
||||||
|
SELECT *
|
||||||
|
FROM `gao_point_ledger`
|
||||||
|
WHERE `event_record_id` = #{eventRecordId}
|
||||||
|
AND `change_type` = 'EARN'
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
ORDER BY `created_at` ASC, `id` ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectByIdempotencyKeyList" resultType="com.imyeyu.api.modules.gao.entity.GaoPointLedger">
|
||||||
|
SELECT *
|
||||||
|
FROM `gao_point_ledger`
|
||||||
|
WHERE `deleted_at` IS NULL
|
||||||
|
AND `idempotency_key` IN
|
||||||
|
<foreach collection="idempotencyKeyList" item="idempotencyKey" open="(" separator="," close=")">
|
||||||
|
#{idempotencyKey}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<sql id="queryWhere">
|
||||||
|
`deleted_at` IS NULL
|
||||||
|
<if test="storeId != null and storeId != ''">
|
||||||
|
AND `store_id` = #{storeId}
|
||||||
|
</if>
|
||||||
|
<if test="customerId != null and customerId != ''">
|
||||||
|
AND `customer_id` = #{customerId}
|
||||||
|
</if>
|
||||||
|
<if test="accountId != null and accountId != ''">
|
||||||
|
AND `account_id` = #{accountId}
|
||||||
|
</if>
|
||||||
|
<if test="changeType != null">
|
||||||
|
AND `change_type` = #{changeType}
|
||||||
|
</if>
|
||||||
|
<if test="beginAt != null">
|
||||||
|
AND `occurred_at` >= #{beginAt}
|
||||||
|
</if>
|
||||||
|
<if test="endAt != null">
|
||||||
|
AND `occurred_at` <= #{endAt}
|
||||||
|
</if>
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="countByQuery" resultType="long">
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM `gao_point_ledger`
|
||||||
|
WHERE
|
||||||
|
<include refid="queryWhere"/>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectByQuery" resultType="com.imyeyu.api.modules.gao.entity.GaoPointLedger">
|
||||||
|
SELECT *
|
||||||
|
FROM `gao_point_ledger`
|
||||||
|
WHERE
|
||||||
|
<include refid="queryWhere"/>
|
||||||
|
ORDER BY `occurred_at` DESC, `created_at` DESC
|
||||||
|
LIMIT #{offset}, #{limit}
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?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.imyeyu.api.modules.gao.mapper.GaoPointRuleMapper">
|
||||||
|
<select id="selectActiveByStoreIdAndEventId" resultType="com.imyeyu.api.modules.gao.entity.GaoPointRule">
|
||||||
|
SELECT *
|
||||||
|
FROM `gao_point_rule`
|
||||||
|
WHERE `store_id` = #{storeId}
|
||||||
|
AND `event_id` = #{eventId}
|
||||||
|
AND `status` = 'ACTIVE'
|
||||||
|
AND (`begin_at` IS NULL OR `begin_at` <= #{now})
|
||||||
|
AND (`end_at` IS NULL OR #{now} <= `end_at`)
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
ORDER BY `priority` ASC, `created_at` ASC, `id` ASC
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?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.imyeyu.api.modules.user.mapper.PermissionMapper">
|
||||||
|
<select id="selectByIdList" resultType="com.imyeyu.api.modules.user.entity.Permission">
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM `permission`
|
||||||
|
WHERE
|
||||||
|
TRUE
|
||||||
|
<if test="idList != null and !idList.isEmpty()">
|
||||||
|
AND `id` IN
|
||||||
|
<foreach collection="idList" item="item" separator="," open="(" close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="idList == null or idList.isEmpty()">
|
||||||
|
AND FALSE
|
||||||
|
</if>
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
</select>
|
||||||
|
<select id="selectByModuleCode" resultType="com.imyeyu.api.modules.user.entity.Permission">
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM `permission`
|
||||||
|
WHERE
|
||||||
|
`module_code` = #{moduleCode}
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
ORDER BY `code`
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@@ -1,6 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?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" >
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||||
<mapper namespace="com.imyeyu.api.modules.user.mapper.RoleMapper">
|
<mapper namespace="com.imyeyu.api.modules.user.mapper.RoleMapper">
|
||||||
|
<select id="selectById" resultType="com.imyeyu.api.modules.user.entity.Role">
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM `role`
|
||||||
|
WHERE
|
||||||
|
`id` = #{id}
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
<select id="selectByIdList" resultType="com.imyeyu.api.modules.user.entity.Role">
|
<select id="selectByIdList" resultType="com.imyeyu.api.modules.user.entity.Role">
|
||||||
SELECT
|
SELECT
|
||||||
*
|
*
|
||||||
@@ -27,4 +36,26 @@
|
|||||||
AND `deleted_at` IS NULL
|
AND `deleted_at` IS NULL
|
||||||
ORDER BY `created_at`
|
ORDER BY `created_at`
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectByParentRoleId" resultType="com.imyeyu.api.modules.user.entity.Role">
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM `role`
|
||||||
|
WHERE
|
||||||
|
<if test="parentRoleId != null">
|
||||||
|
`parent_role_id` = #{parentRoleId}
|
||||||
|
</if>
|
||||||
|
<if test="parentRoleId == null">
|
||||||
|
`parent_role_id` IS NULL
|
||||||
|
</if>
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
ORDER BY `module_code`, `created_at`
|
||||||
|
</select>
|
||||||
|
<select id="selectAll" resultType="com.imyeyu.api.modules.user.entity.Role">
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM `role`
|
||||||
|
WHERE
|
||||||
|
`deleted_at` IS NULL
|
||||||
|
ORDER BY `module_code`, `created_at`
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<?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.imyeyu.api.modules.user.mapper.RolePermissionDelegationMapper">
|
||||||
|
<select id="selectByRoleIdList" resultType="com.imyeyu.api.modules.user.entity.RolePermissionDelegation">
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM `role_permission_delegation`
|
||||||
|
WHERE
|
||||||
|
`role_id` IN
|
||||||
|
<foreach collection="roleIdList" item="item" separator="," open="(" close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
</select>
|
||||||
|
<select id="selectPermissionByRoleIdList" resultType="com.imyeyu.api.modules.user.entity.Permission">
|
||||||
|
SELECT DISTINCT
|
||||||
|
permission.*
|
||||||
|
FROM `role_permission_delegation`
|
||||||
|
LEFT JOIN `permission` ON permission.id = role_permission_delegation.permission_id
|
||||||
|
WHERE
|
||||||
|
`role_permission_delegation`.`role_id` IN
|
||||||
|
<foreach collection="roleIdList" item="item" separator="," open="(" close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
AND `role_permission_delegation`.`deleted_at` IS NULL
|
||||||
|
AND `permission`.`deleted_at` IS NULL
|
||||||
|
ORDER BY
|
||||||
|
`permission`.code ASC
|
||||||
|
</select>
|
||||||
|
<insert id="createBatch">
|
||||||
|
INSERT INTO `role_permission_delegation` (
|
||||||
|
id,
|
||||||
|
role_id,
|
||||||
|
permission_id,
|
||||||
|
created_by,
|
||||||
|
created_at
|
||||||
|
) VALUES
|
||||||
|
<foreach collection="rolePermissionDelegationList" item="item" separator=",">
|
||||||
|
(#{item.id}, #{item.roleId}, #{item.permissionId}, #{item.createdBy}, #{item.createdAt})
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
<update id="deleteByPermissionIdList">
|
||||||
|
UPDATE `role_permission_delegation`
|
||||||
|
SET `deleted_at` = UNIX_TIMESTAMP()
|
||||||
|
WHERE
|
||||||
|
`role_id` = #{roleId}
|
||||||
|
AND `permission_id` IN
|
||||||
|
<foreach collection="permissionIdList" item="item" separator="," open="(" close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
</update>
|
||||||
|
</mapper>
|
||||||
+31
-10
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?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" >
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||||
<mapper namespace="com.imyeyu.api.modules.user.mapper.RoleRelationMapper">
|
<mapper namespace="com.imyeyu.api.modules.user.mapper.RolePermissionInheritMapper">
|
||||||
<select id="selectByParentRoleId" resultType="com.imyeyu.api.modules.user.entity.RoleRelation">
|
<select id="selectByParentRoleId" resultType="com.imyeyu.api.modules.user.entity.RolePermissionInherit">
|
||||||
SELECT
|
SELECT
|
||||||
*
|
*
|
||||||
FROM `role_relation`
|
FROM `role_permission_inherit`
|
||||||
WHERE
|
WHERE
|
||||||
`parent_role_id` = #{parentRoleId}
|
`parent_role_id` = #{parentRoleId}
|
||||||
AND `deleted_at` IS NULL
|
AND `deleted_at` IS NULL
|
||||||
@@ -12,15 +12,26 @@
|
|||||||
<select id="selectChildRoleIdListByParentRoleId" resultType="java.lang.String">
|
<select id="selectChildRoleIdListByParentRoleId" resultType="java.lang.String">
|
||||||
SELECT
|
SELECT
|
||||||
`child_role_id`
|
`child_role_id`
|
||||||
FROM `role_relation`
|
FROM `role_permission_inherit`
|
||||||
WHERE
|
WHERE
|
||||||
`parent_role_id` = #{parentRoleId}
|
`parent_role_id` = #{parentRoleId}
|
||||||
AND `deleted_at` IS NULL
|
AND `deleted_at` IS NULL
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectByParentRoleIdList" resultType="com.imyeyu.api.modules.user.entity.RolePermissionInherit">
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM `role_permission_inherit`
|
||||||
|
WHERE
|
||||||
|
`parent_role_id` IN
|
||||||
|
<foreach collection="parentRoleIdList" item="item" separator="," open="(" close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
AND `deleted_at` IS NULL
|
||||||
|
</select>
|
||||||
<select id="selectChildRoleIdListByParentRoleIdList" resultType="java.lang.String">
|
<select id="selectChildRoleIdListByParentRoleIdList" resultType="java.lang.String">
|
||||||
SELECT
|
SELECT
|
||||||
`child_role_id`
|
`child_role_id`
|
||||||
FROM `role_relation`
|
FROM `role_permission_inherit`
|
||||||
WHERE
|
WHERE
|
||||||
`parent_role_id` IN
|
`parent_role_id` IN
|
||||||
<foreach collection="parentRoleIdList" item="item" separator="," open="(" close=")">
|
<foreach collection="parentRoleIdList" item="item" separator="," open="(" close=")">
|
||||||
@@ -29,24 +40,34 @@
|
|||||||
AND `deleted_at` IS NULL
|
AND `deleted_at` IS NULL
|
||||||
</select>
|
</select>
|
||||||
<insert id="createBatch">
|
<insert id="createBatch">
|
||||||
INSERT INTO `role_relation` (
|
INSERT INTO `role_permission_inherit` (
|
||||||
id,
|
id,
|
||||||
parent_role_id,
|
parent_role_id,
|
||||||
child_role_id,
|
child_role_id,
|
||||||
|
owner_type,
|
||||||
|
owner_id,
|
||||||
|
created_by,
|
||||||
created_at
|
created_at
|
||||||
) VALUES
|
) VALUES
|
||||||
<foreach collection="roleRelationList" item="item" separator=",">
|
<foreach collection="rolePermissionInheritList" item="item" separator=",">
|
||||||
(#{item.id}, #{item.parentRoleId}, #{item.childRoleId}, #{item.createdAt})
|
(#{item.id}, #{item.parentRoleId}, #{item.childRoleId}, #{item.ownerType}, #{item.ownerId}, #{item.createdBy}, #{item.createdAt})
|
||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
<update id="deleteByChildRoleIdList">
|
<update id="deleteByChildRoleIdList">
|
||||||
UPDATE `role_relation`
|
UPDATE `role_permission_inherit`
|
||||||
SET `deleted_at` = UNIX_TIMESTAMP()
|
SET `deleted_at` = UNIX_TIMESTAMP() * 1000
|
||||||
WHERE
|
WHERE
|
||||||
`parent_role_id` = #{parentRoleId}
|
`parent_role_id` = #{parentRoleId}
|
||||||
AND `child_role_id` IN
|
AND `child_role_id` IN
|
||||||
<foreach collection="childRoleIdList" item="item" separator="," open="(" close=")">
|
<foreach collection="childRoleIdList" item="item" separator="," open="(" close=")">
|
||||||
#{item}
|
#{item}
|
||||||
</foreach>
|
</foreach>
|
||||||
|
AND `owner_type` = #{ownerType}
|
||||||
|
<if test="ownerId != null">
|
||||||
|
AND `owner_id` = #{ownerId}
|
||||||
|
</if>
|
||||||
|
<if test="ownerId == null">
|
||||||
|
AND `owner_id` IS NULL
|
||||||
|
</if>
|
||||||
</update>
|
</update>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
AND `role_permission`.`deleted_at` IS NULL
|
AND `role_permission`.`deleted_at` IS NULL
|
||||||
</select>
|
</select>
|
||||||
<select id="selectPermissionByRoleIdList" resultType="com.imyeyu.api.modules.user.entity.Permission">
|
<select id="selectPermissionByRoleIdList" resultType="com.imyeyu.api.modules.user.entity.Permission">
|
||||||
SELECT
|
SELECT DISTINCT
|
||||||
permission.*
|
permission.*
|
||||||
FROM `role_permission`
|
FROM `role_permission`
|
||||||
LEFT JOIN `permission` ON permission.id = role_permission.permission_id
|
LEFT JOIN `permission` ON permission.id = role_permission.permission_id
|
||||||
|
|||||||
Reference in New Issue
Block a user