feat(approval): 新增业务审批流程及配置管理

- 新增审批配置主子表(biz_approval_config / biz_approval_config_user),支持或签
- 5 个业务模块接入审批: 采购订单/客户报价/供应商报价/发货单/订单异议
- 统一审批动作接口(提交/通过/驳回),status=10 表示审批中
- 新增"待我审批"聚合页面,按业务类型筛选
- 修复 logback 写本地路径报错,去除文件 appender
- 修复 Redis SSL 配置在 Spring Boot 4 下需对象格式
- 补齐部分业务表缺失的 update_by/update_time 审计列

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 11:14:46 +08:00
parent 0180388a2f
commit 7ffc140cf8
69 changed files with 1563 additions and 446 deletions

View File

@@ -0,0 +1,46 @@
package com.ruoyi.web.controller.bid;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.mapper.bid.BizApprovalPendingMapper;
import com.ruoyi.system.service.bid.IBizApprovalActionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/bid/approval/action")
public class BizApprovalActionController extends BaseController {
@Autowired private IBizApprovalActionService service;
@Autowired private BizApprovalPendingMapper pendingMapper;
@GetMapping("/pending")
public AjaxResult pending() {
return success(pendingMapper.selectPendingForUser(SecurityUtils.getUserId()));
}
@Log(title = "提交审批", businessType = BusinessType.UPDATE)
@PostMapping("/submit/{bizType}/{id}")
public AjaxResult submit(@PathVariable String bizType, @PathVariable Long id) {
return toAjax(service.submit(bizType, id, getUsername()));
}
@Log(title = "审批通过", businessType = BusinessType.UPDATE)
@PostMapping("/approve/{bizType}/{id}")
public AjaxResult approve(@PathVariable String bizType, @PathVariable Long id) {
return toAjax(service.approve(bizType, id, SecurityUtils.getUserId(), getUsername()));
}
@Log(title = "审批驳回", businessType = BusinessType.UPDATE)
@PostMapping("/reject/{bizType}/{id}")
public AjaxResult reject(@PathVariable String bizType, @PathVariable Long id,
@RequestBody(required = false) Map<String, String> body) {
String reason = body != null ? body.get("reason") : null;
return toAjax(service.reject(bizType, id, SecurityUtils.getUserId(), getUsername(), reason));
}
}

View File

@@ -0,0 +1,54 @@
package com.ruoyi.web.controller.bid;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bid.BizApprovalConfig;
import com.ruoyi.system.service.bid.IBizApprovalConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/bid/approval")
public class BizApprovalConfigController extends BaseController {
@Autowired private IBizApprovalConfigService service;
@PreAuthorize("@ss.hasPermi('bid:approval:list')")
@GetMapping("/list")
public TableDataInfo list(BizApprovalConfig q) {
startPage();
return getDataTable(service.selectList(q));
}
@PreAuthorize("@ss.hasPermi('bid:approval:query')")
@GetMapping("/{id}")
public AjaxResult getInfo(@PathVariable Long id) {
return success(service.selectById(id));
}
@PreAuthorize("@ss.hasPermi('bid:approval:add')")
@Log(title = "审批配置", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BizApprovalConfig c) {
c.setCreateBy(getUsername());
return toAjax(service.insert(c));
}
@PreAuthorize("@ss.hasPermi('bid:approval:edit')")
@Log(title = "审批配置", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BizApprovalConfig c) {
c.setUpdateBy(getUsername());
return toAjax(service.update(c));
}
@PreAuthorize("@ss.hasPermi('bid:approval:remove')")
@Log(title = "审批配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(service.deleteByIds(ids));
}
}

View File

@@ -72,15 +72,18 @@ spring:
# redis 配置
redis:
# 地址
host: localhost
host: 49.232.154.205
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password:
password: WANGyu11!
# 连接超时时间
timeout: 10s
timeout: 30s
# 是否开启ssl
ssl:
enabled: false
lettuce:
pool:
# 连接池中的最小空闲连接
@@ -92,6 +95,28 @@ spring:
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
redisson:
# redis key前缀
keyPrefix:
# 线程池数量
threads: 16
# Netty线程池数量
nettyThreads: 32
# 单节点配置
singleServerConfig:
# 客户端名称
clientName: ${ruoyi.name}
# 最小空闲连接数
connectionMinimumIdleSize: 32
# 连接池大小
connectionPoolSize: 64
# 连接空闲超时,单位:毫秒
idleConnectionTimeout: 10000
# 命令等待超时,单位:毫秒
timeout: 3000
# 发布和订阅连接池大小
subscriptionConnectionPoolSize: 50
# token配置
token:
# 令牌自定义标识

View File

@@ -1,93 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="/home/ruoyi/logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.ruoyi" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<logger name="com.ruoyi" level="info" />
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<logger name="sys-user" level="info">
<appender-ref ref="console" />
</logger>
</configuration>