修改权限控制
This commit is contained in:
@@ -12,7 +12,7 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 功能权限校验切面
|
* 功能权限校验切面
|
||||||
* <p>拦截 @FuncPermission 注解,校验权限并扣减库存后放行</p>
|
* <p>拦截 @FuncPermission 注解,校验权限并扣减库存后放行,业务异常时自动回退</p>
|
||||||
*
|
*
|
||||||
* @author zk
|
* @author zk
|
||||||
*/
|
*/
|
||||||
@@ -32,14 +32,15 @@ public class FuncPermissionAspect {
|
|||||||
|
|
||||||
log.info("功能权限校验 userId:{} funcCode:{}", userId, funcCode);
|
log.info("功能权限校验 userId:{} funcCode:{}", userId, funcCode);
|
||||||
|
|
||||||
// 校验权限 + 扣减库存
|
// 校验权限 + 扣减库存,返回使用记录ID
|
||||||
funcPermissionServer.checkAndDeduct(userId, funcCode);
|
Long logId = funcPermissionServer.checkAndDeduct(userId, funcCode);
|
||||||
|
|
||||||
// 放行,业务异常时回退库存
|
// 放行,业务异常时回退使用记录和库存
|
||||||
try {
|
try {
|
||||||
return joinPoint.proceed();
|
return joinPoint.proceed();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
funcPermissionServer.rollbackCount(userId, funcCode);
|
log.warn("业务异常,回退使用记录 logId:{} userId:{} funcCode:{}", logId, userId, funcCode);
|
||||||
|
funcPermissionServer.rollbackUsage(logId, userId, funcCode);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,21 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jiayunet.exception.BusinessException;
|
import org.jiayunet.exception.BusinessException;
|
||||||
import org.jiayunet.exception.BusinessExpCodeEnum;
|
import org.jiayunet.exception.BusinessExpCodeEnum;
|
||||||
|
import org.jiayunet.mapper.FuncPermissionMapper;
|
||||||
import org.jiayunet.mapper.UserFuncPermissionStockMapper;
|
import org.jiayunet.mapper.UserFuncPermissionStockMapper;
|
||||||
|
import org.jiayunet.mapper.UserFuncUsageLogMapper;
|
||||||
|
import org.jiayunet.pojo.po.FuncPermission;
|
||||||
import org.jiayunet.pojo.po.UserFuncPermissionStock;
|
import org.jiayunet.pojo.po.UserFuncPermissionStock;
|
||||||
|
import org.jiayunet.pojo.po.UserFuncUsageLog;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 功能权限服务(校验、扣减、查询、添加库存)
|
* 功能权限服务(校验、扣减、查询、添加库存、回退)
|
||||||
*
|
*
|
||||||
* @author zk
|
* @author zk
|
||||||
*/
|
*/
|
||||||
@@ -21,39 +27,72 @@ import java.time.Instant;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class FuncPermissionServer {
|
public class FuncPermissionServer {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FuncPermissionMapper funcPermissionMapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserFuncPermissionStockMapper userFuncPermissionStockMapper;
|
private UserFuncPermissionStockMapper userFuncPermissionStockMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserFuncUsageLogMapper userFuncUsageLogMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验用户功能权限并扣减库存
|
* 校验用户功能权限并扣减库存
|
||||||
|
* <p>优先使用每日免费额度,免费额度用完后走付费库存</p>
|
||||||
*
|
*
|
||||||
* @param userId 用户ID
|
* @param userId 用户ID
|
||||||
* @param funcCode 功能权限编码
|
* @param funcCode 功能权限编码
|
||||||
|
* @return 使用记录ID(用于异常回退)
|
||||||
*/
|
*/
|
||||||
public void checkAndDeduct(Long userId, String funcCode) {
|
public Long checkAndDeduct(Long userId, String funcCode) {
|
||||||
// 查询用户功能权限库存
|
// 1. 查功能权限定义
|
||||||
|
FuncPermission funcPermission = funcPermissionMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<FuncPermission>()
|
||||||
|
.eq(FuncPermission::getFuncCode, funcCode)
|
||||||
|
.eq(FuncPermission::getStatus, 1)
|
||||||
|
);
|
||||||
|
if (funcPermission == null) {
|
||||||
|
throw new BusinessException(BusinessExpCodeEnum.PERMISSION_DENIED, "功能不存在或未启用");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 判断每日免费额度
|
||||||
|
int dailyFreeCount = funcPermission.getDailyFreeCount() == null ? 0 : funcPermission.getDailyFreeCount();
|
||||||
|
if (dailyFreeCount > 0) {
|
||||||
|
Instant todayStart = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
|
Long todayUsed = userFuncUsageLogMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<UserFuncUsageLog>()
|
||||||
|
.eq(UserFuncUsageLog::getUserId, userId)
|
||||||
|
.eq(UserFuncUsageLog::getFuncCode, funcCode)
|
||||||
|
.ge(UserFuncUsageLog::getCreateTime, todayStart)
|
||||||
|
);
|
||||||
|
if (todayUsed < dailyFreeCount) {
|
||||||
|
// 免费额度未用完,插入使用记录,直接放行
|
||||||
|
return insertUsageLog(userId, funcCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 免费额度用完或无免费额度,查付费库存
|
||||||
UserFuncPermissionStock stock = userFuncPermissionStockMapper.selectOne(
|
UserFuncPermissionStock stock = userFuncPermissionStockMapper.selectOne(
|
||||||
new LambdaQueryWrapper<UserFuncPermissionStock>()
|
new LambdaQueryWrapper<UserFuncPermissionStock>()
|
||||||
.eq(UserFuncPermissionStock::getUserId, userId)
|
.eq(UserFuncPermissionStock::getUserId, userId)
|
||||||
.eq(UserFuncPermissionStock::getFuncCode, funcCode)
|
.eq(UserFuncPermissionStock::getFuncCode, funcCode)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 无记录,无权限
|
|
||||||
if (stock == null) {
|
if (stock == null) {
|
||||||
throw new BusinessException(BusinessExpCodeEnum.PERMISSION_DENIED, "无该功能权限");
|
throw new BusinessException(BusinessExpCodeEnum.PERMISSION_DENIED, "无该功能权限");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 时间维度校验
|
// 4. 时间维度校验
|
||||||
if (stock.getTimeLimit() == 1 && stock.getExpireTime() != null && stock.getExpireTime().isBefore(Instant.now())) {
|
if (stock.getTimeLimit() == 1 && stock.getExpireTime() != null && stock.getExpireTime().isBefore(Instant.now())) {
|
||||||
throw new BusinessException(BusinessExpCodeEnum.PERMISSION_DENIED, "功能权限已过期");
|
throw new BusinessException(BusinessExpCodeEnum.PERMISSION_DENIED, "功能权限已过期");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不限次,直接放行
|
// 5. 次数维度校验
|
||||||
if (stock.getCountLimit() == 0) {
|
if (stock.getCountLimit() == 0) {
|
||||||
return;
|
// 不限次,直接放行
|
||||||
|
return insertUsageLog(userId, funcCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 限次,扣减库存(乐观锁,SQL原子扣减)
|
// 限次,SQL原子扣减
|
||||||
int rows = userFuncPermissionStockMapper.update(null,
|
int rows = userFuncPermissionStockMapper.update(null,
|
||||||
new LambdaUpdateWrapper<UserFuncPermissionStock>()
|
new LambdaUpdateWrapper<UserFuncPermissionStock>()
|
||||||
.setSql("remain_count = remain_count - 1")
|
.setSql("remain_count = remain_count - 1")
|
||||||
@@ -61,10 +100,48 @@ public class FuncPermissionServer {
|
|||||||
.eq(UserFuncPermissionStock::getFuncCode, funcCode)
|
.eq(UserFuncPermissionStock::getFuncCode, funcCode)
|
||||||
.gt(UserFuncPermissionStock::getRemainCount, 0)
|
.gt(UserFuncPermissionStock::getRemainCount, 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (rows == 0) {
|
if (rows == 0) {
|
||||||
throw new BusinessException(BusinessExpCodeEnum.PERMISSION_DENIED, "功能使用次数已用完");
|
throw new BusinessException(BusinessExpCodeEnum.PERMISSION_DENIED, "功能使用次数已用完");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return insertUsageLog(userId, funcCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入使用记录
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param funcCode 功能编码
|
||||||
|
* @return 记录ID
|
||||||
|
*/
|
||||||
|
private Long insertUsageLog(Long userId, String funcCode) {
|
||||||
|
UserFuncUsageLog usageLog = new UserFuncUsageLog();
|
||||||
|
usageLog.setUserId(userId);
|
||||||
|
usageLog.setFuncCode(funcCode);
|
||||||
|
userFuncUsageLogMapper.insert(usageLog);
|
||||||
|
return usageLog.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回退使用记录(业务异常时调用)
|
||||||
|
* <p>删除使用记录,并尝试回退付费库存次数(SQL条件自动过滤)</p>
|
||||||
|
*
|
||||||
|
* @param logId 使用记录ID
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param funcCode 功能编码
|
||||||
|
*/
|
||||||
|
public void rollbackUsage(Long logId, Long userId, String funcCode) {
|
||||||
|
// 删除使用记录
|
||||||
|
userFuncUsageLogMapper.deleteById(logId);
|
||||||
|
|
||||||
|
// 尝试回退库存次数(count_limit=1才会匹配,其他情况update 0行无影响)
|
||||||
|
userFuncPermissionStockMapper.update(null,
|
||||||
|
new LambdaUpdateWrapper<UserFuncPermissionStock>()
|
||||||
|
.setSql("remain_count = remain_count + 1")
|
||||||
|
.eq(UserFuncPermissionStock::getUserId, userId)
|
||||||
|
.eq(UserFuncPermissionStock::getFuncCode, funcCode)
|
||||||
|
.eq(UserFuncPermissionStock::getCountLimit, 1)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,7 +171,6 @@ public class FuncPermissionServer {
|
|||||||
UserFuncPermissionStock existing = query(userId, funcCode);
|
UserFuncPermissionStock existing = query(userId, funcCode);
|
||||||
|
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
// 无记录,新增
|
|
||||||
UserFuncPermissionStock stock = new UserFuncPermissionStock();
|
UserFuncPermissionStock stock = new UserFuncPermissionStock();
|
||||||
stock.setUserId(userId);
|
stock.setUserId(userId);
|
||||||
stock.setFuncCode(funcCode);
|
stock.setFuncCode(funcCode);
|
||||||
@@ -138,7 +214,6 @@ public class FuncPermissionServer {
|
|||||||
UserFuncPermissionStock existing = query(userId, funcCode);
|
UserFuncPermissionStock existing = query(userId, funcCode);
|
||||||
|
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
// 无记录,新增
|
|
||||||
UserFuncPermissionStock stock = new UserFuncPermissionStock();
|
UserFuncPermissionStock stock = new UserFuncPermissionStock();
|
||||||
stock.setUserId(userId);
|
stock.setUserId(userId);
|
||||||
stock.setFuncCode(funcCode);
|
stock.setFuncCode(funcCode);
|
||||||
@@ -168,21 +243,4 @@ public class FuncPermissionServer {
|
|||||||
|
|
||||||
userFuncPermissionStockMapper.update(null, wrapper);
|
userFuncPermissionStockMapper.update(null, wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 回退次数库存(业务异常时调用,仅限次维度有效)
|
|
||||||
*
|
|
||||||
* @param userId 用户ID
|
|
||||||
* @param funcCode 功能权限编码
|
|
||||||
*/
|
|
||||||
public void rollbackCount(Long userId, String funcCode) {
|
|
||||||
userFuncPermissionStockMapper.update(null,
|
|
||||||
new LambdaUpdateWrapper<UserFuncPermissionStock>()
|
|
||||||
.setSql("remain_count = remain_count + 1")
|
|
||||||
.eq(UserFuncPermissionStock::getUserId, userId)
|
|
||||||
.eq(UserFuncPermissionStock::getFuncCode, funcCode)
|
|
||||||
.eq(UserFuncPermissionStock::getCountLimit, 1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.jiayunet.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.jiayunet.pojo.po.UserFuncUsageLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户功能使用记录Mapper
|
||||||
|
*
|
||||||
|
* @author zk
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface UserFuncUsageLogMapper extends CommonMapper<UserFuncUsageLog> {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -30,6 +30,11 @@ public class FuncPermission {
|
|||||||
*/
|
*/
|
||||||
private String funcName;
|
private String funcName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每日免费使用次数,0表示无免费额度
|
||||||
|
*/
|
||||||
|
private Integer dailyFreeCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态 1=启用 0=禁用
|
* 状态 1=启用 0=禁用
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package org.jiayunet.pojo.po;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户功能使用记录表
|
||||||
|
*
|
||||||
|
* @author zk
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName(value = "bg_user_func_usage_log")
|
||||||
|
public class UserFuncUsageLog {
|
||||||
|
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 功能编码
|
||||||
|
*/
|
||||||
|
private String funcCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用时间
|
||||||
|
*/
|
||||||
|
private Instant createTime;
|
||||||
|
}
|
||||||
@@ -56,7 +56,8 @@ offerpie/back-end
|
|||||||
│ ├─ RouteMenuMapper.java # 路由菜单Mapper
|
│ ├─ RouteMenuMapper.java # 路由菜单Mapper
|
||||||
│ ├─ FuncPermissionMapper.java # 功能权限Mapper
|
│ ├─ FuncPermissionMapper.java # 功能权限Mapper
|
||||||
│ ├─ UserRouteMenuStockMapper.java # 用户路由菜单库存Mapper
|
│ ├─ UserRouteMenuStockMapper.java # 用户路由菜单库存Mapper
|
||||||
│ └─ UserFuncPermissionStockMapper.java # 用户功能权限库存Mapper
|
│ ├─ UserFuncPermissionStockMapper.java # 用户功能权限库存Mapper
|
||||||
|
│ └─ UserFuncUsageLogMapper.java # 用户功能使用记录Mapper
|
||||||
├─ pojo/
|
├─ pojo/
|
||||||
│ ├─ po/ # 持久化实体
|
│ ├─ po/ # 持久化实体
|
||||||
│ │ ├─ User.java
|
│ │ ├─ User.java
|
||||||
@@ -64,7 +65,8 @@ offerpie/back-end
|
|||||||
│ │ ├─ RouteMenu.java # 路由菜单表(bg_route_menu)
|
│ │ ├─ RouteMenu.java # 路由菜单表(bg_route_menu)
|
||||||
│ │ ├─ FuncPermission.java # 功能权限表(bg_func_permission)
|
│ │ ├─ FuncPermission.java # 功能权限表(bg_func_permission)
|
||||||
│ │ ├─ UserRouteMenuStock.java # 用户路由菜单库存表(bg_user_route_menu_stock)
|
│ │ ├─ UserRouteMenuStock.java # 用户路由菜单库存表(bg_user_route_menu_stock)
|
||||||
│ │ └─ UserFuncPermissionStock.java # 用户功能权限库存表(bg_user_func_permission_stock)
|
│ │ ├─ UserFuncPermissionStock.java # 用户功能权限库存表(bg_user_func_permission_stock)
|
||||||
|
│ │ └─ UserFuncUsageLog.java # 用户功能使用记录表(bg_user_func_usage_log)
|
||||||
│ └─ vo/ # ViewObject(OssUrlVo 等)
|
│ └─ vo/ # ViewObject(OssUrlVo 等)
|
||||||
└─ server/ # 业务 Service(OssServer、SmsServer 等)
|
└─ server/ # 业务 Service(OssServer、SmsServer 等)
|
||||||
```
|
```
|
||||||
@@ -75,7 +77,7 @@ offerpie/back-end
|
|||||||
|------|----------|-----------|
|
|------|----------|-----------|
|
||||||
| **client-api** | - 面向终端用户的 REST API <br> - 启动 Spring Boot 应用 <br> - 短信验证码登录(含自动注册) <br> - **功能权限校验**:注解 + 切面 + 权限服务(校验、扣减、回退) <br> - **路由菜单**:获取用户有效菜单树 | `ClientApplication`、`LoginController`、`RouteMenuController`、`FuncPermission`、`FuncPermissionAspect`、`FuncPermissionServer`、`RouteMenuServer`、`RouteMenuVo` |
|
| **client-api** | - 面向终端用户的 REST API <br> - 启动 Spring Boot 应用 <br> - 短信验证码登录(含自动注册) <br> - **功能权限校验**:注解 + 切面 + 权限服务(校验、扣减、回退) <br> - **路由菜单**:获取用户有效菜单树 | `ClientApplication`、`LoginController`、`RouteMenuController`、`FuncPermission`、`FuncPermissionAspect`、`FuncPermissionServer`、`RouteMenuServer`、`RouteMenuVo` |
|
||||||
| **common** | - **统一配置**:OSS、Redis、Security、WxPay、Sms 等 <br> - **跨层工具**:HTTP、IP、认证、验证码、Redis Server 等 <br> - **全局拦截/切面**:日志、TraceId、黑名单、SQL 打印 <br> - **统一异常/响应**:`GlobalExceptionAdvice`、`UnifiedResponse` <br> - **业务抽象**:邮件发送、微信支付(Native/JS/Transfer) <br> - **公共 POJO**:登录令牌、防重放信息等 | `config/`, `tool/`, `interceptor/`, `aop/`, `exception/`, `email/`, `wxPay/`, `pojo/` |
|
| **common** | - **统一配置**:OSS、Redis、Security、WxPay、Sms 等 <br> - **跨层工具**:HTTP、IP、认证、验证码、Redis Server 等 <br> - **全局拦截/切面**:日志、TraceId、黑名单、SQL 打印 <br> - **统一异常/响应**:`GlobalExceptionAdvice`、`UnifiedResponse` <br> - **业务抽象**:邮件发送、微信支付(Native/JS/Transfer) <br> - **公共 POJO**:登录令牌、防重放信息等 | `config/`, `tool/`, `interceptor/`, `aop/`, `exception/`, `email/`, `wxPay/`, `pojo/` |
|
||||||
| **manager** | - **业务实体**(`User`、`OssFile`、`RouteMenu`、`FuncPermission`、`UserRouteMenuStock`、`UserFuncPermissionStock`) <br> - **MyBatis Mapper**(`UserMapper`、`OssFileMapper`、`RouteMenuMapper`、`FuncPermissionMapper`、`UserRouteMenuStockMapper`、`UserFuncPermissionStockMapper`) <br> - **业务 API**:文件上传/下载、健康检查等 <br> - **业务逻辑**:服务层、工具类等 <br> - **既供 B 端 UI(待实现)使用,也供 C 端业务直接调用** | `controller/`, `mapper/`, `pojo/po/`, `pojo/vo/`, `server/`, `constant/` |
|
| **manager** | - **业务实体**(`User`、`OssFile`、`RouteMenu`、`FuncPermission`、`UserRouteMenuStock`、`UserFuncPermissionStock`、`UserFuncUsageLog`) <br> - **MyBatis Mapper**(`UserMapper`、`OssFileMapper`、`RouteMenuMapper`、`FuncPermissionMapper`、`UserRouteMenuStockMapper`、`UserFuncPermissionStockMapper`、`UserFuncUsageLogMapper`) <br> - **业务 API**:文件上传/下载、健康检查等 <br> - **业务逻辑**:服务层、工具类等 <br> - **既供 B 端 UI(待实现)使用,也供 C 端业务直接调用** | `controller/`, `mapper/`, `pojo/po/`, `pojo/vo/`, `server/`, `constant/` |
|
||||||
|
|
||||||
## 3️⃣ 关键业务实体
|
## 3️⃣ 关键业务实体
|
||||||
| 实体 | 所属模块 | 作用概述 |
|
| 实体 | 所属模块 | 作用概述 |
|
||||||
@@ -83,9 +85,10 @@ offerpie/back-end
|
|||||||
| `User` | manager | 记录用户基础信息(手机号、邮箱、密码、昵称、微信绑定等),配合 `UserMapper` 完成持久化。 |
|
| `User` | manager | 记录用户基础信息(手机号、邮箱、密码、昵称、微信绑定等),配合 `UserMapper` 完成持久化。 |
|
||||||
| `OssFile` | manager | 描述 OSS(对象存储)中文件的元数据(路径、大小、标签等),通过 `OssFileMapper` 进行增删改查。 |
|
| `OssFile` | manager | 描述 OSS(对象存储)中文件的元数据(路径、大小、标签等),通过 `OssFileMapper` 进行增删改查。 |
|
||||||
| `RouteMenu` | manager | 路由菜单表(bg_route_menu),支持多级树形结构,通过 rootId/parentId 表达层级关系,openAccess 标识是否公开免费。 |
|
| `RouteMenu` | manager | 路由菜单表(bg_route_menu),支持多级树形结构,通过 rootId/parentId 表达层级关系,openAccess 标识是否公开免费。 |
|
||||||
| `FuncPermission` | manager | 功能权限表(bg_func_permission),定义功能点编码(func_code,最长12字符,唯一约束)。 |
|
| `FuncPermission` | manager | 功能权限表(bg_func_permission),定义功能点编码(func_code,最长12字符,唯一约束),daily_free_count 配置每日免费次数。 |
|
||||||
| `UserRouteMenuStock` | manager | 用户路由菜单库存表(bg_user_route_menu_stock),记录用户拥有的菜单权限,支持时间维度。 |
|
| `UserRouteMenuStock` | manager | 用户路由菜单库存表(bg_user_route_menu_stock),记录用户拥有的菜单权限,支持时间维度。 |
|
||||||
| `UserFuncPermissionStock` | manager | 用户功能权限库存表(bg_user_func_permission_stock),记录用户拥有的功能权限,支持时间/次数/复合维度。 |
|
| `UserFuncPermissionStock` | manager | 用户功能权限库存表(bg_user_func_permission_stock),记录用户拥有的功能权限,支持时间/次数/复合维度。 |
|
||||||
|
| `UserFuncUsageLog` | manager | 用户功能使用记录表(bg_user_func_usage_log),记录每次功能使用,用于免费次数统计和异常回退。 |
|
||||||
| `RouteMenuVo` | client-api | 路由菜单树形VO,包含 children 子菜单列表,供前端渲染动态路由。 |
|
| `RouteMenuVo` | client-api | 路由菜单树形VO,包含 children 子菜单列表,供前端渲染动态路由。 |
|
||||||
| `LoginVo` | client-api | 登录成功后返回的用户信息(userId、nick)。 |
|
| `LoginVo` | client-api | 登录成功后返回的用户信息(userId、nick)。 |
|
||||||
| `SmsLoginDto` | client-api | 短信验证码登录的请求参数(mobileNumber、code)。 |
|
| `SmsLoginDto` | client-api | 短信验证码登录的请求参数(mobileNumber、code)。 |
|
||||||
@@ -96,13 +99,14 @@ offerpie/back-end
|
|||||||
- **后端**:AOP 切面拦截 `@FuncPermission` 注解,校验权限 + 扣减库存,业务异常自动回退次数
|
- **后端**:AOP 切面拦截 `@FuncPermission` 注解,校验权限 + 扣减库存,业务异常自动回退次数
|
||||||
- **权限来源**:商品模块下单成功后写入库存表,权限框架不关心来源
|
- **权限来源**:商品模块下单成功后写入库存表,权限框架不关心来源
|
||||||
|
|
||||||
### 数据库表(4张)
|
### 数据库表(5张)
|
||||||
| 表名 | 说明 |
|
| 表名 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `bg_route_menu` | 路由菜单定义(树形结构,open_access 标识公开免费菜单) |
|
| `bg_route_menu` | 路由菜单定义(树形结构,open_access 标识公开免费菜单) |
|
||||||
| `bg_user_route_menu_stock` | 用户路由菜单库存(时间维度) |
|
| `bg_user_route_menu_stock` | 用户路由菜单库存(时间维度) |
|
||||||
| `bg_func_permission` | 功能权限定义(func_code 唯一) |
|
| `bg_func_permission` | 功能权限定义(func_code 唯一,daily_free_count 配置每日免费次数) |
|
||||||
| `bg_user_func_permission_stock` | 用户功能权限库存(时间/次数/复合维度) |
|
| `bg_user_func_permission_stock` | 用户功能权限库存(时间/次数/复合维度) |
|
||||||
|
| `bg_user_func_usage_log` | 用户功能使用记录(每次使用插入,用于免费次数统计和异常回退) |
|
||||||
|
|
||||||
### 库存维度
|
### 库存维度
|
||||||
- `time_limit` + `expire_time`:时间维度,0=不限时,1=限时
|
- `time_limit` + `expire_time`:时间维度,0=不限时,1=限时
|
||||||
@@ -112,12 +116,14 @@ offerpie/back-end
|
|||||||
|
|
||||||
### 切面校验流程
|
### 切面校验流程
|
||||||
1. 拿注解上的 funcCode + 当前登录用户 userId
|
1. 拿注解上的 funcCode + 当前登录用户 userId
|
||||||
2. 查库存表(唯一索引 userId + funcCode)
|
2. 查 bg_func_permission(funcCode + status=1),拿到 dailyFreeCount
|
||||||
3. 无记录 → 无权限
|
3. dailyFreeCount > 0 → COUNT 今日使用记录,未超额 → 插入使用记录,放行
|
||||||
4. time_limit=1 且过期 → 已过期
|
4. 免费额度用完或无免费额度 → 查付费库存表(userId + funcCode)
|
||||||
5. count_limit=0 → 直接放行
|
5. 无记录 → 无权限
|
||||||
6. count_limit=1 → SQL 原子扣减 `remain_count = remain_count - 1`(WHERE remain_count > 0)
|
6. time_limit=1 且过期 → 已过期
|
||||||
7. 业务方法异常 → 自动回退次数
|
7. count_limit=0 → 插入使用记录,放行
|
||||||
|
8. count_limit=1 → SQL 原子扣减 `remain_count = remain_count - 1`(WHERE remain_count > 0)→ 插入使用记录,放行
|
||||||
|
9. 业务方法异常 → 删除使用记录 + 尝试回退库存次数(count_limit=1 时 remain_count + 1)
|
||||||
|
|
||||||
## 5️⃣ 共享技术栈(位于 `common`)
|
## 5️⃣ 共享技术栈(位于 `common`)
|
||||||
| 类别 | 关键实现 | 位置 |
|
| 类别 | 关键实现 | 位置 |
|
||||||
|
|||||||
Reference in New Issue
Block a user