feat: 会议材料上传/发票OCR识别/用户管理增强 + 日志降噪 + 薪亿通接口规范

- 会议/劳务材料支持直接上传与提交审核 (BizMeetingMaterial 等)
- 发票 OCR 识别增强 (InvoiceOcrService)
- 现场照片/证书批量导入覆盖式回填 (ExcelUtil/AttendeeImportVo/CameraQrUpload)
- 用户管理增强、超级管理员校验与权限拒绝处理 (SuperManagerChecker/AccessDeniedHandlerImpl)
- 日志级别调整为 info/warn (生产降噪)
- 新增薪亿通·个人票税接口规范文档 (自然人开票服务分册 + 轻量化对接分册)
- .gitignore 排除 key.txt/*.rdb/*.key/tmp/opt 等敏感与本地文件

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-11-12 01:13:17 +00:00
co-authored by Claude Code
parent 44d4920777
commit 3f587fbd9f
39 changed files with 1544 additions and 192 deletions
@@ -17,6 +17,7 @@ import org.springframework.web.filter.CorsFilter;
import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
import com.ruoyi.framework.security.filter.LeaderReadOnlyFilter;
import com.ruoyi.framework.security.handle.AccessDeniedHandlerImpl;
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
@@ -32,6 +33,8 @@ public class SecurityConfig
@Autowired
private AuthenticationEntryPointImpl unauthorizedHandler;
@Autowired
private AccessDeniedHandlerImpl accessDeniedHandler;
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;
@Autowired
private JwtAuthenticationTokenFilter authenticationTokenFilter;
@@ -54,7 +57,7 @@ public class SecurityConfig
.headers((headersCustomizer) -> {
headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());
})
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler).accessDeniedHandler(accessDeniedHandler))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll());
@@ -0,0 +1,33 @@
package com.ruoyi.framework.security.handle;
import java.io.IOException;
import java.io.Serializable;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.ServletUtils;
/**
* 授权失败处理类 返回 403
*
* @author ruoyi
*/
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler, Serializable
{
private static final long serialVersionUID = -8970718410437077606L;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
throws IOException
{
int code = HttpStatus.FORBIDDEN;
String msg = "没有权限,请联系管理员授权";
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
}
}
@@ -37,7 +37,7 @@ public class GlobalExceptionHandler
public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
log.warn("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权");
}