fix: 第一版普遍修复结束。
This commit is contained in:
@@ -32,6 +32,7 @@ dependencies {
|
|||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-websocket")
|
implementation("org.springframework.boot:spring-boot-starter-websocket")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-validation")
|
implementation("org.springframework.boot:spring-boot-starter-validation")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-aop")
|
||||||
compileOnly("com.baomidou:mybatis-plus-spring-boot3-starter:3.5.16")
|
compileOnly("com.baomidou:mybatis-plus-spring-boot3-starter:3.5.16")
|
||||||
implementation("com.baomidou:mybatis-plus-jsqlparser:3.5.16")
|
implementation("com.baomidou:mybatis-plus-jsqlparser:3.5.16")
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
rootProject.name = "erp"
|
rootProject.name = "erp-backend"
|
||||||
|
|||||||
@@ -0,0 +1,166 @@
|
|||||||
|
package com.niuan.erp.common.aspect;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Pointcut;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller 层日志切面
|
||||||
|
* 自动记录所有 Controller 接口的请求和响应信息
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ControllerLogAspect {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
// 响应体大小限制:2KB
|
||||||
|
private static final int MAX_RESPONSE_LENGTH = 2048;
|
||||||
|
|
||||||
|
// 忽略的静态资源后缀
|
||||||
|
private static final String[] IGNORE_SUFFIXES = {
|
||||||
|
".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".svg",
|
||||||
|
".woff", ".woff2", ".ttf", ".eot", ".pdf", ".html", ".htm"
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切点:拦截所有 @RestController 或 @Controller 下的方法
|
||||||
|
*/
|
||||||
|
@Pointcut("@within(org.springframework.web.bind.annotation.RestController) || " +
|
||||||
|
"@within(org.springframework.stereotype.Controller)")
|
||||||
|
public void controllerPointcut() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 环绕通知:记录请求和响应信息
|
||||||
|
*/
|
||||||
|
@Around("controllerPointcut()")
|
||||||
|
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
|
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
|
if (attributes == null) {
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpServletRequest request = attributes.getRequest();
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
|
||||||
|
// 忽略静态资源请求
|
||||||
|
if (isStaticResource(uri)) {
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
String method = request.getMethod();
|
||||||
|
String params = getRequestParams(joinPoint, request);
|
||||||
|
|
||||||
|
// 记录请求信息
|
||||||
|
log.info("[Request] {} {} | Params: {}", method, uri, params);
|
||||||
|
|
||||||
|
Object result = null;
|
||||||
|
int statusCode = 200;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = joinPoint.proceed();
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
statusCode = 500;
|
||||||
|
// 异常信息记录到 error 日志,包含完整堆栈
|
||||||
|
log.error("[Exception] {} {} | Params: {} | Error: {}", method, uri, params, e.getMessage(), e);
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
long costTime = System.currentTimeMillis() - startTime;
|
||||||
|
String responseStr = formatResponse(result);
|
||||||
|
log.info("[Response] {} {} | Status: {} | Cost: {}ms | Result: {}",
|
||||||
|
method, uri, statusCode, costTime, responseStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为静态资源请求
|
||||||
|
*/
|
||||||
|
private boolean isStaticResource(String uri) {
|
||||||
|
String lowerUri = uri.toLowerCase();
|
||||||
|
for (String suffix : IGNORE_SUFFIXES) {
|
||||||
|
if (lowerUri.endsWith(suffix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求参数
|
||||||
|
*/
|
||||||
|
private String getRequestParams(ProceedingJoinPoint joinPoint, HttpServletRequest request) {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
|
||||||
|
// 获取 URL 参数
|
||||||
|
Map<String, String[]> parameterMap = request.getParameterMap();
|
||||||
|
if (!parameterMap.isEmpty()) {
|
||||||
|
Map<String, String> queryParams = new HashMap<>();
|
||||||
|
parameterMap.forEach((k, v) -> queryParams.put(k, String.join(",", v)));
|
||||||
|
params.put("query", queryParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 Body 参数(排除 HttpServletRequest、MultipartFile 等类型)
|
||||||
|
Object[] args = joinPoint.getArgs();
|
||||||
|
if (args != null && args.length > 0) {
|
||||||
|
Object bodyParam = Arrays.stream(args)
|
||||||
|
.filter(arg -> arg != null
|
||||||
|
&& !(arg instanceof HttpServletRequest)
|
||||||
|
&& !(arg instanceof MultipartFile)
|
||||||
|
&& !(arg instanceof MultipartFile[]))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
if (bodyParam != null) {
|
||||||
|
params.put("body", bodyParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return objectMapper.writeValueAsString(params);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return params.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化响应内容,限制大小
|
||||||
|
*/
|
||||||
|
private String formatResponse(Object result) {
|
||||||
|
if (result == null) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String json = objectMapper.writeValueAsString(result);
|
||||||
|
if (json.length() > MAX_RESPONSE_LENGTH) {
|
||||||
|
return json.substring(0, MAX_RESPONSE_LENGTH) + "... [Response too large, total: " + json.length() + " chars]";
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
} catch (Exception e) {
|
||||||
|
String str = result.toString();
|
||||||
|
if (str.length() > MAX_RESPONSE_LENGTH) {
|
||||||
|
return str.substring(0, MAX_RESPONSE_LENGTH) + "... [Response too large, total: " + str.length() + " chars]";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,21 @@ import jakarta.validation.Validator;
|
|||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
|
||||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
|
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class ValidationConfig {
|
public class ValidationConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MessageSource messageSource() {
|
||||||
|
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
|
||||||
|
messageSource.setBasename("classpath:i18n/messages");
|
||||||
|
messageSource.setDefaultEncoding("UTF-8");
|
||||||
|
return messageSource;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public LocalValidatorFactoryBean validator(MessageSource messageSource) {
|
public LocalValidatorFactoryBean validator(MessageSource messageSource) {
|
||||||
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
|
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
|
||||||
@@ -23,5 +32,4 @@ public class ValidationConfig {
|
|||||||
processor.setValidator(validator);
|
processor.setValidator(validator);
|
||||||
return processor;
|
return processor;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.niuan.erp.common.handler;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||||
import com.niuan.erp.common.utils.SecurityUtils;
|
import com.niuan.erp.common.utils.SecurityUtils;
|
||||||
|
import com.niuan.erp.module.sys.entity.SysUser;
|
||||||
|
import com.niuan.erp.module.sys.enums.UserType;
|
||||||
import net.sf.jsqlparser.expression.Expression;
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
import net.sf.jsqlparser.expression.LongValue;
|
import net.sf.jsqlparser.expression.LongValue;
|
||||||
|
|
||||||
@@ -39,6 +41,7 @@ public class CustomerTenantHandler implements TenantLineHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean ignoreTable(String tableName) {
|
public boolean ignoreTable(String tableName) {
|
||||||
return !customerTables.contains(tableName);
|
return !customerTables.contains(tableName) ||
|
||||||
|
SecurityUtils.getLoginUser().getUser().getUserType().equals(UserType.SYSTEM_USER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ public class SecurityUtils {
|
|||||||
throw new BusinessException("auth.unLogin");
|
throw new BusinessException("auth.unLogin");
|
||||||
}
|
}
|
||||||
Object principal = authentication.getPrincipal();
|
Object principal = authentication.getPrincipal();
|
||||||
System.out.println(principal);
|
|
||||||
if (principal instanceof LoginUser user) {
|
if (principal instanceof LoginUser user) {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
package com.niuan.erp.module.auth.controller;
|
package com.niuan.erp.module.auth.controller;
|
||||||
|
|
||||||
import com.niuan.erp.common.base.BaseResult;
|
import com.niuan.erp.common.base.BaseResult;
|
||||||
|
import com.niuan.erp.common.base.LoginUser;
|
||||||
|
import com.niuan.erp.common.security.JwtTokenProvider;
|
||||||
|
import com.niuan.erp.common.security.TokenService;
|
||||||
|
import com.niuan.erp.common.utils.SecurityUtils;
|
||||||
import com.niuan.erp.module.auth.service.SystemAuthService;
|
import com.niuan.erp.module.auth.service.SystemAuthService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
@@ -17,6 +25,12 @@ public class SystemAuthController {
|
|||||||
|
|
||||||
private final SystemAuthService systemAuthService;
|
private final SystemAuthService systemAuthService;
|
||||||
|
|
||||||
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
|
|
||||||
|
private final TokenService tokenService;
|
||||||
|
|
||||||
|
private static final String TOKEN_PREFIX = "Bearer ";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 后台权限接口
|
* 后台权限接口
|
||||||
* @return
|
* @return
|
||||||
@@ -27,4 +41,78 @@ public class SystemAuthController {
|
|||||||
return BaseResult.successWithData(systemAuthService.getRouterConfigRawList());
|
return BaseResult.successWithData(systemAuthService.getRouterConfigRawList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测用户登录状态
|
||||||
|
* 支持两种检测方式:
|
||||||
|
* 1. Session方式(Web端):检查Spring Security上下文
|
||||||
|
* 2. JWT方式(小程序):检查Authorization头中的Token
|
||||||
|
*/
|
||||||
|
@GetMapping("/check-login")
|
||||||
|
@Operation(summary = "检测登录状态", description = "检测当前是否已登录,支持Session和JWT两种方式")
|
||||||
|
public BaseResult<LoginStatusResponse> checkLogin(HttpServletRequest request) {
|
||||||
|
// 方式1:检查Session(Web端)
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authentication != null && authentication.isAuthenticated()
|
||||||
|
&& authentication.getPrincipal() instanceof LoginUser) {
|
||||||
|
LoginUser user = (LoginUser) authentication.getPrincipal();
|
||||||
|
LoginStatusResponse response = new LoginStatusResponse(
|
||||||
|
true,
|
||||||
|
new UserInfo(user.getUser().getId(), user.getUser().getLoginName(), user.getUser().getUserName()),
|
||||||
|
"登录状态有效(Session)"
|
||||||
|
);
|
||||||
|
return BaseResult.successWithData(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式2:检查JWT(小程序端)
|
||||||
|
String token = extractToken(request);
|
||||||
|
if (StringUtils.hasText(token)) {
|
||||||
|
// 检查token是否在黑名单中
|
||||||
|
if (tokenService.isTokenBlacklisted(token)) {
|
||||||
|
return BaseResult.successWithData(new LoginStatusResponse(false, null, "Token已失效"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证token有效性
|
||||||
|
if (jwtTokenProvider.validateToken(token)) {
|
||||||
|
// 获取用户信息
|
||||||
|
Long userId = jwtTokenProvider.getUserIdFromToken(token);
|
||||||
|
String loginName = jwtTokenProvider.getLoginNameFromToken(token);
|
||||||
|
|
||||||
|
LoginStatusResponse response = new LoginStatusResponse(
|
||||||
|
true,
|
||||||
|
new UserInfo(userId, loginName, null),
|
||||||
|
"登录状态有效(JWT)"
|
||||||
|
);
|
||||||
|
return BaseResult.successWithData(response);
|
||||||
|
} else {
|
||||||
|
return BaseResult.successWithData(new LoginStatusResponse(false, null, "Token无效或已过期"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未登录
|
||||||
|
return BaseResult.successWithData(new LoginStatusResponse(false, null, "未登录"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从请求中提取token
|
||||||
|
*/
|
||||||
|
private String extractToken(HttpServletRequest request) {
|
||||||
|
String bearerToken = request.getHeader("Authorization");
|
||||||
|
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
|
||||||
|
return bearerToken.substring(TOKEN_PREFIX.length());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录状态响应
|
||||||
|
*/
|
||||||
|
public record LoginStatusResponse(boolean isLoggedIn, UserInfo userInfo, String message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户信息
|
||||||
|
*/
|
||||||
|
public record UserInfo(Long userId, String loginName, String username) {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface DocumentMaterialMapper extends BaseMapper<DocumentMaterial> {
|
public interface DocumentMaterialMapper extends BaseMapper<DocumentMaterial> {
|
||||||
|
|
||||||
List<DocumentMaterialWithInfo> selectByPartNumber(List<Long> documentIds, String partNumber);
|
List<DocumentMaterialWithInfo> selectListByPartNumber(List<Long> documentIds, String partNumber);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
package com.niuan.erp.module.open.controller;
|
|
||||||
|
|
||||||
import com.niuan.erp.common.base.BaseResult;
|
|
||||||
import com.niuan.erp.common.base.LoginUser;
|
|
||||||
import com.niuan.erp.common.security.JwtTokenProvider;
|
|
||||||
import com.niuan.erp.common.security.TokenService;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 公开接口控制器
|
|
||||||
* 用于前端检测登录状态等不需要认证的接口
|
|
||||||
* 同时支持Session和JWT两种认证方式
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/open")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Tag(name = "公开接口", description = "不需要认证的公开接口")
|
|
||||||
public class OpenController {
|
|
||||||
|
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
|
||||||
|
|
||||||
private final TokenService tokenService;
|
|
||||||
|
|
||||||
private static final String TOKEN_PREFIX = "Bearer ";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检测用户登录状态
|
|
||||||
* 支持两种检测方式:
|
|
||||||
* 1. Session方式(Web端):检查Spring Security上下文
|
|
||||||
* 2. JWT方式(小程序):检查Authorization头中的Token
|
|
||||||
*/
|
|
||||||
@GetMapping("/check-login")
|
|
||||||
@Operation(summary = "检测登录状态", description = "检测当前是否已登录,支持Session和JWT两种方式")
|
|
||||||
public BaseResult<LoginStatusResponse> checkLogin(HttpServletRequest request) {
|
|
||||||
// 方式1:检查Session(Web端)
|
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
|
||||||
if (authentication != null && authentication.isAuthenticated()
|
|
||||||
&& authentication.getPrincipal() instanceof LoginUser) {
|
|
||||||
LoginUser user = (LoginUser) authentication.getPrincipal();
|
|
||||||
LoginStatusResponse response = new LoginStatusResponse(
|
|
||||||
true,
|
|
||||||
new UserInfo(user.getUser().getId(), user.getUser().getLoginName(), user.getUser().getUserName()),
|
|
||||||
"登录状态有效(Session)"
|
|
||||||
);
|
|
||||||
return BaseResult.successWithData(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 方式2:检查JWT(小程序端)
|
|
||||||
String token = extractToken(request);
|
|
||||||
if (StringUtils.hasText(token)) {
|
|
||||||
// 检查token是否在黑名单中
|
|
||||||
if (tokenService.isTokenBlacklisted(token)) {
|
|
||||||
return BaseResult.successWithData(new LoginStatusResponse(false, null, "Token已失效"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证token有效性
|
|
||||||
if (jwtTokenProvider.validateToken(token)) {
|
|
||||||
// 获取用户信息
|
|
||||||
Long userId = jwtTokenProvider.getUserIdFromToken(token);
|
|
||||||
String loginName = jwtTokenProvider.getLoginNameFromToken(token);
|
|
||||||
|
|
||||||
LoginStatusResponse response = new LoginStatusResponse(
|
|
||||||
true,
|
|
||||||
new UserInfo(userId, loginName, null),
|
|
||||||
"登录状态有效(JWT)"
|
|
||||||
);
|
|
||||||
return BaseResult.successWithData(response);
|
|
||||||
} else {
|
|
||||||
return BaseResult.successWithData(new LoginStatusResponse(false, null, "Token无效或已过期"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 未登录
|
|
||||||
return BaseResult.successWithData(new LoginStatusResponse(false, null, "未登录"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从请求中提取token
|
|
||||||
*/
|
|
||||||
private String extractToken(HttpServletRequest request) {
|
|
||||||
String bearerToken = request.getHeader("Authorization");
|
|
||||||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
|
|
||||||
return bearerToken.substring(TOKEN_PREFIX.length());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 登录状态响应
|
|
||||||
*/
|
|
||||||
public record LoginStatusResponse(boolean isLoggedIn, UserInfo userInfo, String message) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户信息
|
|
||||||
*/
|
|
||||||
public record UserInfo(Long userId, String loginName, String userName) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,10 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.niuan.erp.common.annotation.ApiLog;
|
import com.niuan.erp.common.annotation.ApiLog;
|
||||||
import com.niuan.erp.common.annotation.ModuleLog;
|
import com.niuan.erp.common.annotation.ModuleLog;
|
||||||
import com.niuan.erp.common.base.BasePageReqParams;
|
import com.niuan.erp.common.base.*;
|
||||||
import com.niuan.erp.common.base.BaseResult;
|
|
||||||
import com.niuan.erp.common.base.BaseSelectDto;
|
|
||||||
import com.niuan.erp.common.base.OperationType;
|
|
||||||
import com.niuan.erp.module.production.controller.dto.BomAddDto;
|
import com.niuan.erp.module.production.controller.dto.BomAddDto;
|
||||||
import com.niuan.erp.module.production.controller.dto.BomDto;
|
import com.niuan.erp.module.production.controller.dto.BomDto;
|
||||||
import com.niuan.erp.module.production.controller.dto.BomItemDto;
|
import com.niuan.erp.module.production.controller.dto.BomItemDto;
|
||||||
@@ -77,4 +74,13 @@ public class BomController {
|
|||||||
bomService.addBom(dto);
|
bomService.addBom(dto);
|
||||||
return BaseResult.success();
|
return BaseResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiLog(type = OperationType.DELETE, remark = "删除一条Bom记录")
|
||||||
|
@Operation(summary = "删除Bom", operationId = "deleteBom")
|
||||||
|
@PostMapping("/deleteBom")
|
||||||
|
@PreAuthorize("hasAuthority('bom:delete')")
|
||||||
|
public BaseResult<?> deleteBom(@Validated(CommonValidateGroup.DeleteOne.class) @RequestBody BaseDeleteBody req) {
|
||||||
|
bomService.deleteBom(req.id());
|
||||||
|
return BaseResult.success();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public class ProductionReturnController {
|
|||||||
.or()
|
.or()
|
||||||
.like(Document::getFormMark, searchCode));
|
.like(Document::getFormMark, searchCode));
|
||||||
}
|
}
|
||||||
|
wrapper.orderByDesc(Document::getCreateDate);
|
||||||
return BaseResult.successWithData(productionReturnService.getProductionReturnPage(pageParams, wrapper));
|
return BaseResult.successWithData(productionReturnService.getProductionReturnPage(pageParams, wrapper));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,14 +50,14 @@ public class BomServiceImpl extends ServiceImpl<BomMapper, Bom> implements BomSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<BomDto> getBomPageWithItem(BasePageReqParams pageParams, String searchCode, String partNumber) {
|
public IPage<BomDto> getBomPageWithItem(BasePageReqParams pageParams, String searchCode, String partNumber) {
|
||||||
IPage<Bom> bomPage = this.baseMapper.selectPageByPartNumber(new Page<>(pageParams.page(), pageParams.pageSize()),
|
IPage<Bom> bomPage = this.baseMapper.selectPageByPartNumber(new Page<>(pageParams.page(), pageParams.pageSize()),
|
||||||
searchCode, partNumber);
|
searchCode, partNumber);
|
||||||
List<Bom> bomList = bomPage.getRecords();
|
List<Bom> bomList = bomPage.getRecords();
|
||||||
bomList.forEach(b -> {
|
bomList.forEach(b -> {
|
||||||
b.setBomItems(bomItemMapper.getBomItemListByBomIdAndPartNumber(b.getId(), partNumber));
|
b.setBomItems(bomItemMapper.getBomItemListByBomIdAndPartNumber(b.getId(), partNumber));
|
||||||
});
|
});
|
||||||
return bomPage.convert(bomConverter::toDto);
|
return bomPage.convert(bomConverter::toDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -38,12 +38,9 @@ public class PurchaseOrderController {
|
|||||||
@PreAuthorize("hasAuthority('purchase_order:index')")
|
@PreAuthorize("hasAuthority('purchase_order:index')")
|
||||||
public BaseResult<IPage<PurchaseOrderDto>> getPurchaseOrderPage(
|
public BaseResult<IPage<PurchaseOrderDto>> getPurchaseOrderPage(
|
||||||
@Validated BasePageReqParams pageParams,
|
@Validated BasePageReqParams pageParams,
|
||||||
@Parameter(description = "单据编号/供应商名称") @RequestParam(required = false) String searchCode) {
|
@Parameter(description = "单据编号/供应商名称") @RequestParam(required = false) String searchCode,
|
||||||
var wrapper = new LambdaQueryWrapper<Document>();
|
@Parameter(description = "物料编号") @RequestParam(required = false) String partNumber) {
|
||||||
if (StringUtils.hasText(searchCode)) {
|
return BaseResult.successWithData(purchaseOrderService.getPurchaseOrderPage(pageParams, searchCode, partNumber));
|
||||||
wrapper.like(Document::getFormCode, searchCode).or().like(Document::getVendorName, searchCode);
|
|
||||||
}
|
|
||||||
return BaseResult.successWithData(purchaseOrderService.getPurchaseOrderPage(pageParams, wrapper));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "查询采购订单明细数据", operationId = "getPurchaseOrderItems")
|
@Operation(summary = "查询采购订单明细数据", operationId = "getPurchaseOrderItems")
|
||||||
@@ -67,7 +64,7 @@ public class PurchaseOrderController {
|
|||||||
@ApiLog(type = OperationType.UPDATE, remark = "更新一条采购订单记录")
|
@ApiLog(type = OperationType.UPDATE, remark = "更新一条采购订单记录")
|
||||||
@Operation(summary = "更新采购订单", operationId = "updatePurchaseOrder")
|
@Operation(summary = "更新采购订单", operationId = "updatePurchaseOrder")
|
||||||
@PostMapping("/updatePurchaseOrder")
|
@PostMapping("/updatePurchaseOrder")
|
||||||
@PreAuthorize("hasAuthority('purchase_order:update')")
|
@PreAuthorize("hasAuthority('purchase_order:edit')")
|
||||||
public BaseResult<?> updatePurchaseOrder(@Validated @RequestBody PurchaseOrderAddDto dto) {
|
public BaseResult<?> updatePurchaseOrder(@Validated @RequestBody PurchaseOrderAddDto dto) {
|
||||||
purchaseOrderService.updatePurchaseOrder(dto);
|
purchaseOrderService.updatePurchaseOrder(dto);
|
||||||
return BaseResult.success();
|
return BaseResult.success();
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ public interface PurchaseOrderConverter {
|
|||||||
Document toEntity(PurchaseOrderDto dto);
|
Document toEntity(PurchaseOrderDto dto);
|
||||||
|
|
||||||
@Mapping(target = "planId", expression = "java(entity.getGroupId() != null ? entity.getGroupId().longValue() : null)")
|
@Mapping(target = "planId", expression = "java(entity.getGroupId() != null ? entity.getGroupId().longValue() : null)")
|
||||||
|
@Mapping(target = "vendorNo", source = "vendorNo")
|
||||||
|
@Mapping(target = "vendorName", source = "vendorName")
|
||||||
PurchaseOrderDto toDto(Document entity);
|
PurchaseOrderDto toDto(Document entity);
|
||||||
|
|
||||||
List<PurchaseOrderDto> toDtoList(List<Document> entities);
|
List<PurchaseOrderDto> toDtoList(List<Document> entities);
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
package com.niuan.erp.module.purchase.mapper;
|
package com.niuan.erp.module.purchase.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderItemDto;
|
||||||
import com.niuan.erp.module.purchase.entity.PurchaseOrderItem;
|
import com.niuan.erp.module.purchase.entity.PurchaseOrderItem;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -13,4 +18,9 @@ import com.niuan.erp.module.purchase.entity.PurchaseOrderItem;
|
|||||||
*/
|
*/
|
||||||
public interface PurchaseOrderItemMapper extends BaseMapper<PurchaseOrderItem> {
|
public interface PurchaseOrderItemMapper extends BaseMapper<PurchaseOrderItem> {
|
||||||
|
|
||||||
|
@Select("SELECT DISTINCT DocumentNo FROM purchasematerial WHERE PartNumber LIKE CONCAT('%', #{partNumber}, '%')")
|
||||||
|
List<Integer> selectOrderIdsByPartNumber(@Param("partNumber") String partNumber);
|
||||||
|
|
||||||
|
List<PurchaseOrderItemDto> selectListByOrderIdsAndPartNumber(@Param("orderIds") List<Long> orderIds, @Param("partNumber") String partNumber);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import java.util.List;
|
|||||||
|
|
||||||
public interface PurchaseOrderService {
|
public interface PurchaseOrderService {
|
||||||
|
|
||||||
IPage<PurchaseOrderDto> getPurchaseOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper<Document> wrapper);
|
IPage<PurchaseOrderDto> getPurchaseOrderPage(BasePageReqParams pageParams, String searchCode, String partNumber);
|
||||||
|
|
||||||
void addPurchaseOrder(PurchaseOrderAddDto dto);
|
void addPurchaseOrder(PurchaseOrderAddDto dto);
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import com.niuan.erp.module.warehouse.mapper.WarehouseItemMapper;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -52,10 +53,58 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<DocumentMapper, Docume
|
|||||||
private final DocumentMaterialMapper documentMaterialMapper;
|
private final DocumentMaterialMapper documentMaterialMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<PurchaseOrderDto> getPurchaseOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper<Document> wrapper) {
|
public IPage<PurchaseOrderDto> getPurchaseOrderPage(BasePageReqParams pageParams, String searchCode, String partNumber) {
|
||||||
|
var wrapper = new LambdaQueryWrapper<Document>();
|
||||||
wrapper.eq(Document::getFormType, DocumentType.PURCHASE_ORDER);
|
wrapper.eq(Document::getFormType, DocumentType.PURCHASE_ORDER);
|
||||||
|
|
||||||
|
if (StringUtils.hasText(searchCode)) {
|
||||||
|
wrapper.like(Document::getFormCode, searchCode).or().like(Document::getVendorName, searchCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(partNumber)) {
|
||||||
|
List<Integer> orderIds = purchaseOrderItemMapper.selectOrderIdsByPartNumber(partNumber);
|
||||||
|
if (!orderIds.isEmpty()) {
|
||||||
|
wrapper.in(Document::getId, orderIds);
|
||||||
|
} else {
|
||||||
|
wrapper.eq(Document::getId, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wrapper.orderByDesc(Document::getCreateDate);
|
wrapper.orderByDesc(Document::getCreateDate);
|
||||||
IPage<Document> result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper);
|
IPage<Document> result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper);
|
||||||
|
|
||||||
|
// 如果有过滤条件,查询并填充明细
|
||||||
|
if (StringUtils.hasText(partNumber) && !result.getRecords().isEmpty()) {
|
||||||
|
List<Long> parentIds = result.getRecords().stream()
|
||||||
|
.map(Document::getId)
|
||||||
|
.toList();
|
||||||
|
List<PurchaseOrderItemDto> allItems = purchaseOrderItemMapper.selectListByOrderIdsAndPartNumber(parentIds, partNumber);
|
||||||
|
|
||||||
|
return result.convert(entity -> {
|
||||||
|
PurchaseOrderDto dto = purchaseOrderConverter.toDto(entity);
|
||||||
|
Long entityId = entity.getId();
|
||||||
|
List<PurchaseOrderItemDto> items = allItems.stream()
|
||||||
|
.filter(item -> item.documentNo() != null && item.documentNo().longValue() == entityId.longValue())
|
||||||
|
.toList();
|
||||||
|
return new PurchaseOrderDto(
|
||||||
|
dto.id(),
|
||||||
|
dto.formCode(),
|
||||||
|
dto.formName(),
|
||||||
|
dto.vendorName(),
|
||||||
|
dto.vendorNo(),
|
||||||
|
dto.totalValue(),
|
||||||
|
dto.formMark(),
|
||||||
|
dto.formStatus(),
|
||||||
|
dto.createDate(),
|
||||||
|
dto.createUserName(),
|
||||||
|
dto.storeNo(),
|
||||||
|
dto.storeName(),
|
||||||
|
dto.planId(),
|
||||||
|
items
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return result.convert(purchaseOrderConverter::toDto);
|
return result.convert(purchaseOrderConverter::toDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ public record DeviceDto(
|
|||||||
String softVersion,
|
String softVersion,
|
||||||
@Schema(description = "算法版本")
|
@Schema(description = "算法版本")
|
||||||
String alVersion,
|
String alVersion,
|
||||||
|
@Schema(description = "算法标志")
|
||||||
|
String alNum,
|
||||||
|
@Schema(description = "激活状态")
|
||||||
|
Boolean alStatus,
|
||||||
|
@Schema(description = "生产日期")
|
||||||
|
LocalDateTime createDate,
|
||||||
|
@Schema(description = "备注")
|
||||||
|
String mark,
|
||||||
@Schema(description = "出库日期/出货日期")
|
@Schema(description = "出库日期/出货日期")
|
||||||
LocalDateTime outProductDate,
|
LocalDateTime outProductDate,
|
||||||
@Schema(description = "返修记录拼接字符串")
|
@Schema(description = "返修记录拼接字符串")
|
||||||
|
|||||||
@@ -32,6 +32,13 @@ public record SaleOrderAddDto(
|
|||||||
@NotBlank(message = "sale.sale_order.validate.customer_name.not_null")
|
@NotBlank(message = "sale.sale_order.validate.customer_name.not_null")
|
||||||
String customerName,
|
String customerName,
|
||||||
|
|
||||||
|
@Schema(description = "出货仓库ID")
|
||||||
|
@NotNull(message = "sale.sale_order.validate.store_no.not_null")
|
||||||
|
Integer storeNo,
|
||||||
|
|
||||||
|
@Schema(description = "出货仓库名称")
|
||||||
|
String storeName,
|
||||||
|
|
||||||
@Schema(description = "销售订单明细列表")
|
@Schema(description = "销售订单明细列表")
|
||||||
@NotEmpty(message = "sale.sale_order.validate.sale_order_items.not_null")
|
@NotEmpty(message = "sale.sale_order.validate.sale_order_items.not_null")
|
||||||
@Valid
|
@Valid
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ public record SaleOrderDto(
|
|||||||
Integer formStatus,
|
Integer formStatus,
|
||||||
Integer customerId,
|
Integer customerId,
|
||||||
String customerName,
|
String customerName,
|
||||||
|
Integer storeNo,
|
||||||
|
String storeName,
|
||||||
Double totalValue,
|
Double totalValue,
|
||||||
String searchCode
|
String searchCode
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ public record SaleOrderUpdateDto(
|
|||||||
@NotBlank(message = "sale.sale_order.validate.customer_name.not_null", groups = CommonValidateGroup.Update.class)
|
@NotBlank(message = "sale.sale_order.validate.customer_name.not_null", groups = CommonValidateGroup.Update.class)
|
||||||
String customerName,
|
String customerName,
|
||||||
|
|
||||||
|
@NotNull(message = "sale.sale_order.validate.store_no.not_null", groups = CommonValidateGroup.Update.class)
|
||||||
|
Integer storeNo,
|
||||||
|
|
||||||
|
String storeName,
|
||||||
|
|
||||||
@NotEmpty(message = "sale.sale_order.validate.sale_order_items.not_null", groups = CommonValidateGroup.Update.class)
|
@NotEmpty(message = "sale.sale_order.validate.sale_order_items.not_null", groups = CommonValidateGroup.Update.class)
|
||||||
@Valid
|
@Valid
|
||||||
List<SaleOrderItemUpdateDto> saleOrderItems
|
List<SaleOrderItemUpdateDto> saleOrderItems
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ public interface SaleOrderConverter {
|
|||||||
@Mapping(source = "formStatus.value", target = "formStatus")
|
@Mapping(source = "formStatus.value", target = "formStatus")
|
||||||
@Mapping(source = "customerId", target = "customerId")
|
@Mapping(source = "customerId", target = "customerId")
|
||||||
@Mapping(source = "storeName", target = "customerName")
|
@Mapping(source = "storeName", target = "customerName")
|
||||||
|
@Mapping(source = "storeNo", target = "storeNo")
|
||||||
|
@Mapping(source = "outStoreName", target = "storeName")
|
||||||
@Mapping(source = "totalValue", target = "totalValue")
|
@Mapping(source = "totalValue", target = "totalValue")
|
||||||
SaleOrderDto toDto(Document entity);
|
SaleOrderDto toDto(Document entity);
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
|
|||||||
entity.setFormMark(dto.formMark());
|
entity.setFormMark(dto.formMark());
|
||||||
entity.setCustomerId(dto.customerId());
|
entity.setCustomerId(dto.customerId());
|
||||||
entity.setStoreName(dto.customerName());
|
entity.setStoreName(dto.customerName());
|
||||||
|
entity.setStoreNo(dto.storeNo());
|
||||||
|
entity.setOutStoreName(dto.storeName());
|
||||||
entity.setTotalValue(totalValue);
|
entity.setTotalValue(totalValue);
|
||||||
entity.setFormType(DocumentType.SALES_ORDER);
|
entity.setFormType(DocumentType.SALES_ORDER);
|
||||||
entity.setFormStatus(FormStatus.NO_APPROVE);
|
entity.setFormStatus(FormStatus.NO_APPROVE);
|
||||||
@@ -129,6 +131,8 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
|
|||||||
existingEntity.setFormMark(dto.formMark());
|
existingEntity.setFormMark(dto.formMark());
|
||||||
existingEntity.setCustomerId(dto.customerId());
|
existingEntity.setCustomerId(dto.customerId());
|
||||||
existingEntity.setStoreName(dto.customerName());
|
existingEntity.setStoreName(dto.customerName());
|
||||||
|
existingEntity.setStoreNo(dto.storeNo());
|
||||||
|
existingEntity.setOutStoreName(dto.storeName());
|
||||||
existingEntity.setTotalValue(totalValue);
|
existingEntity.setTotalValue(totalValue);
|
||||||
existingEntity.setUpdateUserId(SecurityUtils.getUserId());
|
existingEntity.setUpdateUserId(SecurityUtils.getUserId());
|
||||||
existingEntity.setUpdateUserName(SecurityUtils.getUserName());
|
existingEntity.setUpdateUserName(SecurityUtils.getUserName());
|
||||||
@@ -247,11 +251,11 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stock == null) {
|
if (stock == null) {
|
||||||
throw new BusinessException("warehouse.stock_transfer_order.exception.stock_not_found");
|
throw new BusinessException("sale.sale_order.exception.stock_not_found", item.getPartNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stock.getProductCount() < item.getSaleCount()) {
|
if (stock.getProductCount() < item.getSaleCount()) {
|
||||||
throw new BusinessException("warehouse.stock_transfer_order.exception.insufficient_stock");
|
throw new BusinessException("sale.sale_order.exception.insufficient_stock", item.getPartNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 扣除库存 - 如果有多条记录,只更新第一条
|
// 扣除库存 - 如果有多条记录,只更新第一条
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package com.niuan.erp.module.sys.converter;
|
|||||||
|
|
||||||
import com.niuan.erp.module.sys.controller.dto.SysUserDto;
|
import com.niuan.erp.module.sys.controller.dto.SysUserDto;
|
||||||
import com.niuan.erp.module.sys.entity.SysUser;
|
import com.niuan.erp.module.sys.entity.SysUser;
|
||||||
|
import com.niuan.erp.module.sys.enums.UserType;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.ReportingPolicy;
|
import org.mapstruct.ReportingPolicy;
|
||||||
|
|
||||||
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||||
@@ -12,4 +14,11 @@ public interface SysUserConverter {
|
|||||||
|
|
||||||
SysUser toEntity(SysUserDto dto);
|
SysUser toEntity(SysUserDto dto);
|
||||||
|
|
||||||
|
default Integer mapUserTypeToInt(UserType userType) {
|
||||||
|
return userType.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
default UserType mapUserTypeToUserType(Integer userType) {
|
||||||
|
return UserType.fromCode(userType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
|
|||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.niuan.erp.module.sys.enums.UserType;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -37,7 +38,7 @@ public class SysUser {
|
|||||||
private String updateUserName;
|
private String updateUserName;
|
||||||
|
|
||||||
@TableField("UserType")
|
@TableField("UserType")
|
||||||
private Long userType;
|
private UserType userType;
|
||||||
|
|
||||||
@TableField("LoginName")
|
@TableField("LoginName")
|
||||||
private String loginName;
|
private String loginName;
|
||||||
|
|||||||
27
src/main/java/com/niuan/erp/module/sys/enums/UserType.java
Normal file
27
src/main/java/com/niuan/erp/module/sys/enums/UserType.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package com.niuan.erp.module.sys.enums;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IEnum;
|
||||||
|
|
||||||
|
public enum UserType implements IEnum<Integer> {
|
||||||
|
SYSTEM_USER(5, "系统管理员"),
|
||||||
|
COMMON_USER(0, "普通用户");
|
||||||
|
final int code;
|
||||||
|
final String description;
|
||||||
|
UserType(int code, String description) {
|
||||||
|
this.code = code;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserType fromCode(int code) {
|
||||||
|
for (UserType userType : UserType.values()) {
|
||||||
|
if (userType.code == code) return userType;
|
||||||
|
}
|
||||||
|
// TODO 异常
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getValue() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
|
|||||||
/**
|
/**
|
||||||
* 根据角色ID查询用户ID列表
|
* 根据角色ID查询用户ID列表
|
||||||
*/
|
*/
|
||||||
@Select("SELECT DISTINCT ur.UserId FROM yy_usersrolemapping ur WHERE ur.RoleId = #{roleId}")
|
@Select("SELECT DISTINCT ur.UserId FROM role_permission ur WHERE ur.role_id = #{roleId}")
|
||||||
List<Long> selectUserIdsByRoleId(@Param("roleId") Long roleId);
|
List<Long> selectUserIdsByRoleId(@Param("roleId") Long roleId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,8 +23,8 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
|
|||||||
@Select("""
|
@Select("""
|
||||||
SELECT DISTINCT ur.UserId
|
SELECT DISTINCT ur.UserId
|
||||||
FROM yy_usersrolemapping ur
|
FROM yy_usersrolemapping ur
|
||||||
INNER JOIN yy_rolepermission rp ON ur.RoleId = rp.RoleId
|
INNER JOIN role_permission rp ON ur.RoleId = rp.role_id
|
||||||
WHERE rp.PermissionId = #{permissionId}
|
WHERE rp.permission_id = #{permissionId}
|
||||||
""")
|
""")
|
||||||
List<Long> selectUserIdsByPermissionId(@Param("permissionId") Long permissionId);
|
List<Long> selectUserIdsByPermissionId(@Param("permissionId") Long permissionId);
|
||||||
}
|
}
|
||||||
@@ -45,15 +45,19 @@ public class KeyAccountServiceImpl extends ServiceImpl<KeyAccountMapper, KeyAcco
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BaseTree> getKeyAccountTree() {
|
public List<BaseTree> getKeyAccountTree() {
|
||||||
LambdaQueryWrapper<KeyAccount> wrapper = new LambdaQueryWrapper<>();
|
// LambdaQueryWrapper<KeyAccount> wrapper = new LambdaQueryWrapper<>();
|
||||||
wrapper.eq(KeyAccount::getStatus, 0);
|
// wrapper.eq(KeyAccount::getStatus, 0);
|
||||||
wrapper.orderByAsc(KeyAccount::getLevelPath);
|
// wrapper.orderByAsc(KeyAccount::getLevelPath);
|
||||||
List<KeyAccount> keyAccounts = this.baseMapper.selectList(wrapper);
|
// List<KeyAccount> keyAccounts = this.baseMapper.selectList(wrapper);
|
||||||
|
|
||||||
Map<Long, List<KeyAccount>> parentMap = keyAccounts.stream()
|
// Map<Long, List<KeyAccount>> parentMap = keyAccounts.stream()
|
||||||
.collect(Collectors.groupingBy(ka -> ka.getParentId() != null ? ka.getParentId() : 0L));
|
// .collect(Collectors.groupingBy(ka -> ka.getParentId() != null ? ka.getParentId() : 0L));
|
||||||
|
|
||||||
return buildTree(parentMap, 0L);
|
return this.baseMapper.getKeyAccountSelectList()
|
||||||
|
.stream()
|
||||||
|
.map(ka -> new BaseTree(ka.value(), ka.label(), null))
|
||||||
|
.toList();
|
||||||
|
// return buildTree(parentMap, 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BaseTree> buildTree(Map<Long, List<KeyAccount>> parentMap, Long parentId) {
|
private List<BaseTree> buildTree(Map<Long, List<KeyAccount>> parentMap, Long parentId) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.niuan.erp.module.sys.service.impl;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.niuan.erp.common.base.BasePageReqParams;
|
import com.niuan.erp.common.base.BasePageReqParams;
|
||||||
@@ -148,6 +149,21 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
// 密码加密
|
// 密码加密
|
||||||
entity.setPassWord(passwordEncoder.encode(dto.passWord()));
|
entity.setPassWord(passwordEncoder.encode(dto.passWord()));
|
||||||
|
|
||||||
|
// 得到 CustomerId
|
||||||
|
List<Integer> customerIdList = this.baseMapper.selectObjs(
|
||||||
|
Wrappers.<SysUser>lambdaQuery()
|
||||||
|
.select(SysUser::getCustomerId)
|
||||||
|
.orderByDesc(SysUser::getCustomerId)
|
||||||
|
.last("LIMIT 1")
|
||||||
|
);
|
||||||
|
|
||||||
|
entity.setIsCustomer(1);
|
||||||
|
if (customerIdList == null || customerIdList.isEmpty()) {
|
||||||
|
entity.setCustomerId(0);
|
||||||
|
} else {
|
||||||
|
entity.setCustomerId(customerIdList.getFirst() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
this.baseMapper.insert(entity);
|
this.baseMapper.insert(entity);
|
||||||
|
|
||||||
// 保存用户角色关联
|
// 保存用户角色关联
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class WarehouseReceiptController {
|
|||||||
@ApiLog(type = OperationType.UPDATE, remark = "更新一条WarehouseReceipt记录")
|
@ApiLog(type = OperationType.UPDATE, remark = "更新一条WarehouseReceipt记录")
|
||||||
@Operation(summary = "更新WarehouseReceipt", operationId = "updateWarehouseReceipt")
|
@Operation(summary = "更新WarehouseReceipt", operationId = "updateWarehouseReceipt")
|
||||||
@PostMapping("/updateWarehouseReceipt")
|
@PostMapping("/updateWarehouseReceipt")
|
||||||
@PreAuthorize("hasAuthority('warehouse_receipt:update')")
|
@PreAuthorize("hasAuthority('warehouse_receipt:edit')")
|
||||||
public BaseResult<?> updateWarehouseReceipt(@Validated(Update.class) @RequestBody WarehouseReceiptDto dto) {
|
public BaseResult<?> updateWarehouseReceipt(@Validated(Update.class) @RequestBody WarehouseReceiptDto dto) {
|
||||||
warehouseReceiptService.updateWarehouseReceipt(dto);
|
warehouseReceiptService.updateWarehouseReceipt(dto);
|
||||||
return BaseResult.success();
|
return BaseResult.success();
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class StockTransferOrderServiceImpl extends ServiceImpl<DocumentMapper, D
|
|||||||
searchParams.searchCode(),
|
searchParams.searchCode(),
|
||||||
searchParams.partNumber(),
|
searchParams.partNumber(),
|
||||||
DocumentType.STOCK_TRANSFER_ORDER);
|
DocumentType.STOCK_TRANSFER_ORDER);
|
||||||
var items = documentMaterialMapper.selectByPartNumber(
|
var items = documentMaterialMapper.selectListByPartNumber(
|
||||||
orderPage.getRecords().stream().map(Document::getId).toList(), searchParams.partNumber());
|
orderPage.getRecords().stream().map(Document::getId).toList(), searchParams.partNumber());
|
||||||
return orderPage.convert(order -> new StockTransferOrderDto(
|
return orderPage.convert(order -> new StockTransferOrderDto(
|
||||||
order.getId(),
|
order.getId(),
|
||||||
@@ -419,7 +419,7 @@ public class StockTransferOrderServiceImpl extends ServiceImpl<DocumentMapper, D
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<StockTransferOrderItemDto> getItem(long id) {
|
public List<StockTransferOrderItemDto> getItem(long id) {
|
||||||
var items = documentMaterialMapper.selectByPartNumber(List.of(id), null);
|
var items = documentMaterialMapper.selectListByPartNumber(List.of(id), null);
|
||||||
return items.stream().map(stockTransferOrderConverter::toItemDto).toList();
|
return items.stream().map(stockTransferOrderConverter::toItemDto).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,49 +48,36 @@ warehouse.inventory_count.exception.cannot_delete_approved_batch=选中的盘点
|
|||||||
warehouse.inventory_count.exception.ids_empty=请选择要删除的记录
|
warehouse.inventory_count.exception.ids_empty=请选择要删除的记录
|
||||||
warehouse.inventory_count.exception.already_approved=盘点单已经审核
|
warehouse.inventory_count.exception.already_approved=盘点单已经审核
|
||||||
warehouse.inventory_count.exception.not_approved=盘点单不是已审核状态,不能反审
|
warehouse.inventory_count.exception.not_approved=盘点单不是已审核状态,不能反审
|
||||||
warehouse.inventory_count.exception.no_items=盘点单没有明细
|
|
||||||
warehouse.inventory_count.exception.file_empty=文件不能为空
|
|
||||||
warehouse.inventory_count.exception.import_failed=导入失败
|
|
||||||
warehouse.inventory_count.exception.stock_not_found_for_unapprove=反审核时找不到对应库存记录
|
|
||||||
warehouse.stock_check.validate.store_no_not_null=仓库不能为空
|
|
||||||
warehouse.stock_check.validate.items_not_empty=物料明细不能为空
|
|
||||||
warehouse.stock_check.exception.insufficient_stock=物料 {0} 库存不足
|
|
||||||
|
|
||||||
|
|
||||||
# ==========>> 生产管理
|
# ==========>> 生产管理
|
||||||
|
|
||||||
production.bom.validate.bom_id.not_null=BOM Id 不能为空
|
production.finished_product_receipt.validate.form_code.not_null=成品入库单编号不能为空
|
||||||
production.bom.validate.bom_no.not_null=BOM 编号不能为空
|
production.finished_product_receipt.validate.form_name.not_null=成品入库单名称不能为空
|
||||||
production.bom.validate.bom_name.not_null=BOM 名称不能为空
|
production.finished_product_receipt.validate.store_no.not_null=仓库 ID 不能为空
|
||||||
production.bom.validate.manufacturer.not_null=厂家 / 型号不能为空
|
production.finished_product_receipt.validate.store_name.not_null=仓库名称不能为空
|
||||||
production.bom.validate.spec.not_null=封装规格不能为空
|
production.finished_product_receipt.validate.module_sn_items.not_null=成品明细不能为空
|
||||||
production.bom.validate.brand_name.not_null=品牌不能为空
|
production.finished_product_receipt.exception.not_found=成品入库单不存在
|
||||||
production.bom.validate.part_number.not_null=物料编号不能为空
|
production.finished_product_receipt.exception.cannot_update_approved=已审核的成品入库单不能修改
|
||||||
production.bom.validate.manufacture_count.not_null=用量不能为空
|
production.finished_product_receipt.exception.cannot_delete_approved=已审核的成品入库单不能删除
|
||||||
production.bom.validate.manufacture_count.min=用量最小为 1
|
production.finished_product_receipt.exception.cannot_delete_approved_batch=选中的数据中存在已审核的成品入库单,不能删除
|
||||||
production.bom.validate.item_position.not_null=位号不能为空
|
production.finished_product_receipt.exception.ids_empty=请选择要删除的成品入库单
|
||||||
production.bom.exception.duplicate_bom_name=BOM 名字重复
|
production.finished_product_receipt.exception.already_approved=成品入库单已经审核
|
||||||
production.bom.exception.duplicate_bom_item=BOM 明细重复
|
production.finished_product_receipt.exception.not_approved=成品入库单不是已审核状态,不能反审
|
||||||
production.bom.exception.unpair_position_count=BOM 明细中存在位点和数量不一致
|
production.finished_product_receipt.exception.no_module_sn_items=成品入库单没有明细
|
||||||
production.bom.exception.unexists_bom_item=BOM 物料有不存在的物料
|
production.finished_product_receipt.exception.duplicate_sn_in_request=导入数据中存在重复SN号
|
||||||
|
production.finished_product_receipt.exception.duplicate_mac_in_request=导入数据中存在重复MAC地址
|
||||||
|
production.finished_product_receipt.exception.duplicate_serial_num_in_request=导入数据中存在重复序列号
|
||||||
|
production.finished_product_receipt.exception.invalid_activation_status=存在未激活的SN号
|
||||||
|
production.finished_product_receipt.exception.sn_already_exists=系统中已存在的SN号
|
||||||
|
production.finished_product_receipt.exception.mac_already_exists=系统中已存在的MAC地址
|
||||||
|
production.finished_product_receipt.exception.serial_num_already_exists=系统中已存在的序列号
|
||||||
|
production.finished_product_receipt.exception.cannot_unapprove_with_shipped=成品入库单已出货,不能反审
|
||||||
|
production.finished_product_receipt.exception.file_empty=请选择要上传的文件
|
||||||
|
production.finished_product_receipt.exception.import_failed=导入文件失败
|
||||||
|
|
||||||
production.production_plan.validate.ids.not_null=生产计划 ID 不能为空
|
production.finished_product_shipment.validate.form_code.not_null=成品出库单编号不能为空
|
||||||
production.production_plan.validate.issue.not_null=发料单数据不能为空
|
production.finished_product_shipment.validate.form_name.not_null=成品出库单名称不能为空
|
||||||
production.production_plan.validate.form_code.not_null=发料单编号不能为空
|
production.finished_product_shipment.validate.store_no.not_null=仓库 ID 不能为空
|
||||||
production.production_plan.validate.store_no.not_null=发料仓库 ID 不能为空
|
|
||||||
production.production_plan.validate.store_name.not_null=发料仓库名称不能为空
|
|
||||||
production.production_plan.validate.items.not_null=发料单明细不能为空
|
|
||||||
production.production_plan.validate.part_number.not_null=物料编号不能为空
|
|
||||||
production.production_plan.validate.product_count.not_null=实发数量不能为空
|
|
||||||
production.production_plan.validate.product_count.min=实发数量不能小于 1
|
|
||||||
production.production_plan.validate.demand_count.not_null=需求数量不能为空
|
|
||||||
production.production_plan.validate.demand_count.min=需求数量不能小于 1
|
|
||||||
production.production_plan.exception.must_no_complete=选中的行其中有不是未完成的状态
|
|
||||||
production.production_plan.exception.more_than_one_warehouse=选中的行不能有多个仓库
|
|
||||||
|
|
||||||
production.finished_product_shipment.validate.form_code.not_null=单据编号不能为空
|
|
||||||
production.finished_product_shipment.validate.form_name.not_null=单据名称不能为空
|
|
||||||
production.finished_product_shipment.validate.store_no.not_null=仓库不能为空
|
|
||||||
production.finished_product_shipment.validate.store_name.not_null=仓库名称不能为空
|
production.finished_product_shipment.validate.store_name.not_null=仓库名称不能为空
|
||||||
production.finished_product_shipment.validate.out_stock_type.not_null=出库类型不能为空
|
production.finished_product_shipment.validate.out_stock_type.not_null=出库类型不能为空
|
||||||
production.finished_product_shipment.validate.shipment_items.not_null=出库明细不能为空
|
production.finished_product_shipment.validate.shipment_items.not_null=出库明细不能为空
|
||||||
@@ -110,6 +97,31 @@ production.finished_product_shipment.exception.insufficient_stock=库存不足
|
|||||||
production.finished_product_shipment.exception.file_empty=请选择要上传的文件
|
production.finished_product_shipment.exception.file_empty=请选择要上传的文件
|
||||||
production.finished_product_shipment.exception.import_failed=导入文件失败
|
production.finished_product_shipment.exception.import_failed=导入文件失败
|
||||||
|
|
||||||
|
# ==========>> 销售管理
|
||||||
|
|
||||||
|
sale.sale_order.validate.form_code.not_null=单据编号不能为空
|
||||||
|
sale.sale_order.validate.form_name.not_null=单据名称不能为空
|
||||||
|
sale.sale_order.validate.customer_id.not_null=客户不能为空
|
||||||
|
sale.sale_order.validate.customer_name.not_null=客户名称不能为空
|
||||||
|
sale.sale_order.validate.store_no.not_null=出货仓库不能为空
|
||||||
|
sale.sale_order.validate.sale_order_items.not_null=销售明细不能为空
|
||||||
|
sale.sale_order.validate.part_number.not_null=物料编号不能为空
|
||||||
|
sale.sale_order.validate.sale_count.not_null=销售数量不能为空
|
||||||
|
sale.sale_order.validate.price.not_null=单价不能为空
|
||||||
|
sale.sale_order.exception.not_found=销售订单不存在
|
||||||
|
sale.sale_order.exception.cannot_update_approved=已审核的销售订单不能修改
|
||||||
|
sale.sale_order.exception.cannot_delete_approved=已审核的销售订单不能删除
|
||||||
|
sale.sale_order.exception.cannot_delete_approved_batch=选中的数据中存在已审核的销售订单,不能删除
|
||||||
|
sale.sale_order.exception.ids_empty=请选择要删除的销售订单
|
||||||
|
sale.sale_order.exception.already_approved=销售订单已经审核
|
||||||
|
sale.sale_order.exception.not_approved=销售订单不是已审核状态,不能反审
|
||||||
|
sale.sale_order.exception.no_sale_order_items=销售订单没有明细
|
||||||
|
sale.sale_order.exception.cannot_unapprove_with_shipped=销售订单已出货,不能反审
|
||||||
|
sale.sale_order.exception.file_empty=请选择要上传的文件
|
||||||
|
sale.sale_order.exception.import_failed=导入文件失败
|
||||||
|
sale.sale_order.exception.stock_not_found=物料 {0} 在仓库中不存在
|
||||||
|
sale.sale_order.exception.insufficient_stock=物料 {0} 库存不足
|
||||||
|
|
||||||
sys.operationType.codeNotExists=编号不存在
|
sys.operationType.codeNotExists=编号不存在
|
||||||
|
|
||||||
# ==========>> 采购管理
|
# ==========>> 采购管理
|
||||||
|
|||||||
@@ -26,3 +26,28 @@ warehouse.warehouse_item.validate.product_type.not_null=Product type cannot be e
|
|||||||
warehouse.warehouse_item.validate.product_specs.not_null=Product specs cannot be empty
|
warehouse.warehouse_item.validate.product_specs.not_null=Product specs cannot be empty
|
||||||
warehouse.warehouse_item.exception.not_found=Warehouse item not found
|
warehouse.warehouse_item.exception.not_found=Warehouse item not found
|
||||||
warehouse.warehouse_item.exception.vendor_duplicate=Vendor cannot be duplicated
|
warehouse.warehouse_item.exception.vendor_duplicate=Vendor cannot be duplicated
|
||||||
|
|
||||||
|
# ==========>> Sale Management
|
||||||
|
|
||||||
|
sale.sale_order.validate.form_code.not_null=Form code cannot be empty
|
||||||
|
sale.sale_order.validate.form_name.not_null=Form name cannot be empty
|
||||||
|
sale.sale_order.validate.customer_id.not_null=Customer cannot be empty
|
||||||
|
sale.sale_order.validate.customer_name.not_null=Customer name cannot be empty
|
||||||
|
sale.sale_order.validate.store_no.not_null=Warehouse cannot be empty
|
||||||
|
sale.sale_order.validate.sale_order_items.not_null=Sale order items cannot be empty
|
||||||
|
sale.sale_order.validate.part_number.not_null=Part number cannot be empty
|
||||||
|
sale.sale_order.validate.sale_count.not_null=Sale count cannot be empty
|
||||||
|
sale.sale_order.validate.price.not_null=Price cannot be empty
|
||||||
|
sale.sale_order.exception.not_found=Sale order not found
|
||||||
|
sale.sale_order.exception.cannot_update_approved=Approved sale order cannot be updated
|
||||||
|
sale.sale_order.exception.cannot_delete_approved=Approved sale order cannot be deleted
|
||||||
|
sale.sale_order.exception.cannot_delete_approved_batch=Selected data contains approved sale orders, cannot delete
|
||||||
|
sale.sale_order.exception.ids_empty=Please select sale orders to delete
|
||||||
|
sale.sale_order.exception.already_approved=Sale order has been approved
|
||||||
|
sale.sale_order.exception.not_approved=Sale order is not approved, cannot reject
|
||||||
|
sale.sale_order.exception.no_sale_order_items=Sale order has no items
|
||||||
|
sale.sale_order.exception.cannot_unapprove_with_shipped=Sale order has been shipped, cannot reject
|
||||||
|
sale.sale_order.exception.file_empty=Please select a file to upload
|
||||||
|
sale.sale_order.exception.import_failed=Import failed
|
||||||
|
sale.sale_order.exception.stock_not_found=Part number {0} does not exist in warehouse
|
||||||
|
sale.sale_order.exception.insufficient_stock=Part number {0} has insufficient stock
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ sale.sale_order.exception.no_sale_order_items=销售订单没有明细
|
|||||||
sale.sale_order.exception.cannot_unapprove_with_shipped=销售订单已出货,不能反审
|
sale.sale_order.exception.cannot_unapprove_with_shipped=销售订单已出货,不能反审
|
||||||
sale.sale_order.exception.file_empty=请选择要上传的文件
|
sale.sale_order.exception.file_empty=请选择要上传的文件
|
||||||
sale.sale_order.exception.import_failed=导入文件失败
|
sale.sale_order.exception.import_failed=导入文件失败
|
||||||
|
sale.sale_order.exception.stock_not_found=物料 {0} 在仓库中不存在
|
||||||
|
sale.sale_order.exception.insufficient_stock=物料 {0} 库存不足
|
||||||
production.devicesn.validate.product_sn.not_null=SN号不能为空
|
production.devicesn.validate.product_sn.not_null=SN号不能为空
|
||||||
production.devicesn.validate.mac.not_null=MAC地址不能为空
|
production.devicesn.validate.mac.not_null=MAC地址不能为空
|
||||||
production.devicesn.exception.not_found=SN号不存在
|
production.devicesn.exception.not_found=SN号不存在
|
||||||
|
|||||||
98
src/main/resources/logback-spring.xml
Normal file
98
src/main/resources/logback-spring.xml
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<!-- 日志文件存放路径 -->
|
||||||
|
<property name="LOG_PATH" value="logs"/>
|
||||||
|
<!-- 日志输出格式 -->
|
||||||
|
<property name="LOG_PATTERN"
|
||||||
|
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
|
||||||
|
|
||||||
|
<!-- 彩色日志格式 -->
|
||||||
|
<property name="CONSOLE_LOG_PATTERN"
|
||||||
|
value="%yellow(%d{yyyy-MM-dd HH:mm:ss.SSS}) %green([%thread]) %highlight(%-5level) %cyan(%logger{36}) - %msg%n"/>
|
||||||
|
|
||||||
|
<!-- 控制台输出 -->
|
||||||
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||||
|
<charset>UTF-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- INFO 及以上级别日志文件 -->
|
||||||
|
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${LOG_PATH}/app-info.log</file>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||||
|
<level>INFO</level>
|
||||||
|
</filter>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${LOG_PATTERN}</pattern>
|
||||||
|
<charset>UTF-8</charset>
|
||||||
|
</encoder>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<!-- 按天滚动,自动压缩为 .gz -->
|
||||||
|
<fileNamePattern>${LOG_PATH}/app-info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||||
|
<!-- 保留最近 7 天 -->
|
||||||
|
<maxHistory>7</maxHistory>
|
||||||
|
<!-- 单个文件最大 50MB -->
|
||||||
|
<maxFileSize>50MB</maxFileSize>
|
||||||
|
<!-- 总日志大小限制 -->
|
||||||
|
<totalSizeCap>1GB</totalSizeCap>
|
||||||
|
</rollingPolicy>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- ERROR 级别日志文件(专门用于查错) -->
|
||||||
|
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${LOG_PATH}/app-error.log</file>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>ERROR</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${LOG_PATTERN}</pattern>
|
||||||
|
<charset>UTF-8</charset>
|
||||||
|
</encoder>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${LOG_PATH}/app-error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||||
|
<maxHistory>7</maxHistory>
|
||||||
|
<maxFileSize>50MB</maxFileSize>
|
||||||
|
<totalSizeCap>500MB</totalSizeCap>
|
||||||
|
</rollingPolicy>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 异步包装:INFO 日志 -->
|
||||||
|
<appender name="ASYNC_INFO" class="ch.qos.logback.classic.AsyncAppender">
|
||||||
|
<appender-ref ref="FILE_INFO"/>
|
||||||
|
<!-- 队列大小,默认 256 -->
|
||||||
|
<queueSize>512</queueSize>
|
||||||
|
<!-- 当队列剩余容量低于此阈值时,丢弃 TRACE/DEBUG/INFO 级别日志,默认为 20 -->
|
||||||
|
<discardingThreshold>0</discardingThreshold>
|
||||||
|
<!-- 不丢失日志 -->
|
||||||
|
<neverBlock>false</neverBlock>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 异步包装:ERROR 日志 -->
|
||||||
|
<appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
|
||||||
|
<appender-ref ref="FILE_ERROR"/>
|
||||||
|
<queueSize>512</queueSize>
|
||||||
|
<discardingThreshold>0</discardingThreshold>
|
||||||
|
<neverBlock>false</neverBlock>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 根日志配置 -->
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="ASYNC_INFO"/>
|
||||||
|
<appender-ref ref="ASYNC_ERROR"/>
|
||||||
|
</root>
|
||||||
|
|
||||||
|
<!-- 业务包日志级别 -->
|
||||||
|
<logger name="com.niuan.erp" level="INFO"/>
|
||||||
|
|
||||||
|
<!-- 减少 Spring 框架日志输出 -->
|
||||||
|
<logger name="org.springframework" level="WARN"/>
|
||||||
|
<logger name="org.springframework.web" level="INFO"/>
|
||||||
|
<logger name="org.hibernate" level="WARN"/>
|
||||||
|
<logger name="com.baomidou.mybatisplus" level="WARN"/>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<result column="PartId" property="partId" />
|
<result column="PartId" property="partId" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<select id="selectByPartNumber" resultType="DocumentMaterialWithInfo">
|
<select id="selectListByPartNumber" resultType="DocumentMaterialWithInfo">
|
||||||
SELECT m.*, p.ProductSpecs
|
SELECT m.*, p.ProductSpecs
|
||||||
FROM materialinout m
|
FROM materialinout m
|
||||||
LEFT JOIN product p ON p.PartNumber = m.PartNumber
|
LEFT JOIN product p ON p.PartNumber = m.PartNumber
|
||||||
|
|||||||
@@ -26,4 +26,38 @@
|
|||||||
<result column="PartId" property="partId" />
|
<result column="PartId" property="partId" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 根据订单ID和物料编号查询明细 -->
|
||||||
|
<select id="selectListByOrderIdsAndPartNumber" resultType="com.niuan.erp.module.purchase.controller.dto.PurchaseOrderItemDto">
|
||||||
|
SELECT
|
||||||
|
pm.Id as id,
|
||||||
|
pm.DocumentNo as documentNo,
|
||||||
|
pm.DocumentCode as documentCode,
|
||||||
|
pm.ProjectName as projectName,
|
||||||
|
pm.PartNumber as partNumber,
|
||||||
|
wi.ProductSpecs as productSpecs,
|
||||||
|
pm.PurchaseCount as purchaseCount,
|
||||||
|
pm.ReceiptCount as receiptCount,
|
||||||
|
(pm.PurchaseCount - IFNULL(pm.ReceiptCount, 0)) as remainingCount,
|
||||||
|
pm.Price as price,
|
||||||
|
pm.TotalPrice as totalPrice,
|
||||||
|
pm.PurchaseMark as purchaseMark,
|
||||||
|
pm.PartId as partId,
|
||||||
|
pm.CreateDate as createDate,
|
||||||
|
pm.CreateUserName as createUserName
|
||||||
|
FROM purchasematerial pm
|
||||||
|
LEFT JOIN product wi ON wi.PartNumber = pm.PartNumber
|
||||||
|
<where>
|
||||||
|
<if test="orderIds != null and !orderIds.isEmpty()">
|
||||||
|
AND pm.DocumentNo IN
|
||||||
|
<foreach collection="orderIds" open="(" separator="," close=")" item="id">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="partNumber != null and partNumber != ''">
|
||||||
|
AND pm.PartNumber LIKE CONCAT('%', #{partNumber}, '%')
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
ORDER BY pm.CreateDate DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user