diff --git a/build.gradle.kts b/build.gradle.kts index 02e0869..f386b03 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-websocket") 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") implementation("com.baomidou:mybatis-plus-jsqlparser:3.5.16") developmentOnly("org.springframework.boot:spring-boot-devtools") diff --git a/settings.gradle.kts b/settings.gradle.kts index ec8bd9d..1ee972e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1 @@ -rootProject.name = "erp" +rootProject.name = "erp-backend" diff --git a/src/main/java/com/niuan/erp/common/aspect/ControllerLogAspect.java b/src/main/java/com/niuan/erp/common/aspect/ControllerLogAspect.java new file mode 100644 index 0000000..99afbbb --- /dev/null +++ b/src/main/java/com/niuan/erp/common/aspect/ControllerLogAspect.java @@ -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 params = new HashMap<>(); + + // 获取 URL 参数 + Map parameterMap = request.getParameterMap(); + if (!parameterMap.isEmpty()) { + Map 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; + } + } +} diff --git a/src/main/java/com/niuan/erp/common/config/ValidationConfig.java b/src/main/java/com/niuan/erp/common/config/ValidationConfig.java index da7bf13..46c8ea3 100644 --- a/src/main/java/com/niuan/erp/common/config/ValidationConfig.java +++ b/src/main/java/com/niuan/erp/common/config/ValidationConfig.java @@ -4,12 +4,21 @@ import jakarta.validation.Validator; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; @Configuration public class ValidationConfig { + @Bean + public MessageSource messageSource() { + ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + messageSource.setBasename("classpath:i18n/messages"); + messageSource.setDefaultEncoding("UTF-8"); + return messageSource; + } + @Bean public LocalValidatorFactoryBean validator(MessageSource messageSource) { LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean(); @@ -23,5 +32,4 @@ public class ValidationConfig { processor.setValidator(validator); return processor; } - } diff --git a/src/main/java/com/niuan/erp/common/handler/CustomerTenantHandler.java b/src/main/java/com/niuan/erp/common/handler/CustomerTenantHandler.java index 0bb3b4a..8d344ef 100644 --- a/src/main/java/com/niuan/erp/common/handler/CustomerTenantHandler.java +++ b/src/main/java/com/niuan/erp/common/handler/CustomerTenantHandler.java @@ -2,6 +2,8 @@ package com.niuan.erp.common.handler; import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; 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.LongValue; @@ -39,6 +41,7 @@ public class CustomerTenantHandler implements TenantLineHandler { @Override public boolean ignoreTable(String tableName) { - return !customerTables.contains(tableName); + return !customerTables.contains(tableName) || + SecurityUtils.getLoginUser().getUser().getUserType().equals(UserType.SYSTEM_USER); } } diff --git a/src/main/java/com/niuan/erp/common/utils/SecurityUtils.java b/src/main/java/com/niuan/erp/common/utils/SecurityUtils.java index f78dbd8..2481722 100644 --- a/src/main/java/com/niuan/erp/common/utils/SecurityUtils.java +++ b/src/main/java/com/niuan/erp/common/utils/SecurityUtils.java @@ -14,7 +14,6 @@ public class SecurityUtils { throw new BusinessException("auth.unLogin"); } Object principal = authentication.getPrincipal(); - System.out.println(principal); if (principal instanceof LoginUser user) { return user; } diff --git a/src/main/java/com/niuan/erp/module/auth/controller/SystemAuthController.java b/src/main/java/com/niuan/erp/module/auth/controller/SystemAuthController.java index 1b8351b..9543bdc 100644 --- a/src/main/java/com/niuan/erp/module/auth/controller/SystemAuthController.java +++ b/src/main/java/com/niuan/erp/module/auth/controller/SystemAuthController.java @@ -1,10 +1,18 @@ package com.niuan.erp.module.auth.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 com.niuan.erp.common.utils.SecurityUtils; import com.niuan.erp.module.auth.service.SystemAuthService; 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; @@ -17,6 +25,12 @@ public class SystemAuthController { private final SystemAuthService systemAuthService; + private final JwtTokenProvider jwtTokenProvider; + + private final TokenService tokenService; + + private static final String TOKEN_PREFIX = "Bearer "; + /** * 后台权限接口 * @return @@ -27,4 +41,78 @@ public class SystemAuthController { return BaseResult.successWithData(systemAuthService.getRouterConfigRawList()); } + /** + * 检测用户登录状态 + * 支持两种检测方式: + * 1. Session方式(Web端):检查Spring Security上下文 + * 2. JWT方式(小程序):检查Authorization头中的Token + */ + @GetMapping("/check-login") + @Operation(summary = "检测登录状态", description = "检测当前是否已登录,支持Session和JWT两种方式") + public BaseResult 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) { + } + } diff --git a/src/main/java/com/niuan/erp/module/common/mapper/DocumentMaterialMapper.java b/src/main/java/com/niuan/erp/module/common/mapper/DocumentMaterialMapper.java index 2e2bdca..8220f9f 100644 --- a/src/main/java/com/niuan/erp/module/common/mapper/DocumentMaterialMapper.java +++ b/src/main/java/com/niuan/erp/module/common/mapper/DocumentMaterialMapper.java @@ -16,6 +16,6 @@ import java.util.List; */ public interface DocumentMaterialMapper extends BaseMapper { - List selectByPartNumber(List documentIds, String partNumber); + List selectListByPartNumber(List documentIds, String partNumber); } diff --git a/src/main/java/com/niuan/erp/module/open/controller/OpenController.java b/src/main/java/com/niuan/erp/module/open/controller/OpenController.java deleted file mode 100644 index f55a6b6..0000000 --- a/src/main/java/com/niuan/erp/module/open/controller/OpenController.java +++ /dev/null @@ -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 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) { - } -} diff --git a/src/main/java/com/niuan/erp/module/production/controller/BomController.java b/src/main/java/com/niuan/erp/module/production/controller/BomController.java index e00ff4d..76bb42d 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/BomController.java +++ b/src/main/java/com/niuan/erp/module/production/controller/BomController.java @@ -4,10 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.niuan.erp.common.annotation.ApiLog; import com.niuan.erp.common.annotation.ModuleLog; -import com.niuan.erp.common.base.BasePageReqParams; -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.common.base.*; 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.BomItemDto; @@ -77,4 +74,13 @@ public class BomController { bomService.addBom(dto); 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(); + } } diff --git a/src/main/java/com/niuan/erp/module/production/controller/ProductionReturnController.java b/src/main/java/com/niuan/erp/module/production/controller/ProductionReturnController.java index f8f7e89..c67d126 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/ProductionReturnController.java +++ b/src/main/java/com/niuan/erp/module/production/controller/ProductionReturnController.java @@ -45,6 +45,7 @@ public class ProductionReturnController { .or() .like(Document::getFormMark, searchCode)); } + wrapper.orderByDesc(Document::getCreateDate); return BaseResult.successWithData(productionReturnService.getProductionReturnPage(pageParams, wrapper)); } diff --git a/src/main/java/com/niuan/erp/module/production/service/impl/BomServiceImpl.java b/src/main/java/com/niuan/erp/module/production/service/impl/BomServiceImpl.java index a2bcf42..64e9364 100644 --- a/src/main/java/com/niuan/erp/module/production/service/impl/BomServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/production/service/impl/BomServiceImpl.java @@ -50,14 +50,14 @@ public class BomServiceImpl extends ServiceImpl implements BomSe } @Override - public IPage getBomPageWithItem(BasePageReqParams pageParams, String searchCode, String partNumber) { - IPage bomPage = this.baseMapper.selectPageByPartNumber(new Page<>(pageParams.page(), pageParams.pageSize()), - searchCode, partNumber); - List bomList = bomPage.getRecords(); - bomList.forEach(b -> { - b.setBomItems(bomItemMapper.getBomItemListByBomIdAndPartNumber(b.getId(), partNumber)); - }); - return bomPage.convert(bomConverter::toDto); + public IPage getBomPageWithItem(BasePageReqParams pageParams, String searchCode, String partNumber) { + IPage bomPage = this.baseMapper.selectPageByPartNumber(new Page<>(pageParams.page(), pageParams.pageSize()), + searchCode, partNumber); + List bomList = bomPage.getRecords(); + bomList.forEach(b -> { + b.setBomItems(bomItemMapper.getBomItemListByBomIdAndPartNumber(b.getId(), partNumber)); + }); + return bomPage.convert(bomConverter::toDto); } @Override diff --git a/src/main/java/com/niuan/erp/module/purchase/controller/PurchaseOrderController.java b/src/main/java/com/niuan/erp/module/purchase/controller/PurchaseOrderController.java index 1d7bd0a..2291d78 100644 --- a/src/main/java/com/niuan/erp/module/purchase/controller/PurchaseOrderController.java +++ b/src/main/java/com/niuan/erp/module/purchase/controller/PurchaseOrderController.java @@ -38,12 +38,9 @@ public class PurchaseOrderController { @PreAuthorize("hasAuthority('purchase_order:index')") public BaseResult> getPurchaseOrderPage( @Validated BasePageReqParams pageParams, - @Parameter(description = "单据编号/供应商名称") @RequestParam(required = false) String searchCode) { - var wrapper = new LambdaQueryWrapper(); - if (StringUtils.hasText(searchCode)) { - wrapper.like(Document::getFormCode, searchCode).or().like(Document::getVendorName, searchCode); - } - return BaseResult.successWithData(purchaseOrderService.getPurchaseOrderPage(pageParams, wrapper)); + @Parameter(description = "单据编号/供应商名称") @RequestParam(required = false) String searchCode, + @Parameter(description = "物料编号") @RequestParam(required = false) String partNumber) { + return BaseResult.successWithData(purchaseOrderService.getPurchaseOrderPage(pageParams, searchCode, partNumber)); } @Operation(summary = "查询采购订单明细数据", operationId = "getPurchaseOrderItems") @@ -67,7 +64,7 @@ public class PurchaseOrderController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条采购订单记录") @Operation(summary = "更新采购订单", operationId = "updatePurchaseOrder") @PostMapping("/updatePurchaseOrder") - @PreAuthorize("hasAuthority('purchase_order:update')") + @PreAuthorize("hasAuthority('purchase_order:edit')") public BaseResult updatePurchaseOrder(@Validated @RequestBody PurchaseOrderAddDto dto) { purchaseOrderService.updatePurchaseOrder(dto); return BaseResult.success(); diff --git a/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderConverter.java b/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderConverter.java index 39e23ca..2c48d5d 100644 --- a/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderConverter.java +++ b/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderConverter.java @@ -10,11 +10,13 @@ import java.util.List; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface PurchaseOrderConverter { - + Document toEntity(PurchaseOrderDto dto); - + @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); - + List toDtoList(List entities); } diff --git a/src/main/java/com/niuan/erp/module/purchase/mapper/PurchaseOrderItemMapper.java b/src/main/java/com/niuan/erp/module/purchase/mapper/PurchaseOrderItemMapper.java index 278387d..d41590c 100644 --- a/src/main/java/com/niuan/erp/module/purchase/mapper/PurchaseOrderItemMapper.java +++ b/src/main/java/com/niuan/erp/module/purchase/mapper/PurchaseOrderItemMapper.java @@ -1,16 +1,26 @@ package com.niuan.erp.module.purchase.mapper; 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 org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; /** *

* Mapper 接口 *

* - * @author + * @author * @since 2026-02-11 */ public interface PurchaseOrderItemMapper extends BaseMapper { + @Select("SELECT DISTINCT DocumentNo FROM purchasematerial WHERE PartNumber LIKE CONCAT('%', #{partNumber}, '%')") + List selectOrderIdsByPartNumber(@Param("partNumber") String partNumber); + + List selectListByOrderIdsAndPartNumber(@Param("orderIds") List orderIds, @Param("partNumber") String partNumber); + } diff --git a/src/main/java/com/niuan/erp/module/purchase/service/PurchaseOrderService.java b/src/main/java/com/niuan/erp/module/purchase/service/PurchaseOrderService.java index 415a84b..e0c0a86 100644 --- a/src/main/java/com/niuan/erp/module/purchase/service/PurchaseOrderService.java +++ b/src/main/java/com/niuan/erp/module/purchase/service/PurchaseOrderService.java @@ -10,7 +10,7 @@ import java.util.List; public interface PurchaseOrderService { - IPage getPurchaseOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper); + IPage getPurchaseOrderPage(BasePageReqParams pageParams, String searchCode, String partNumber); void addPurchaseOrder(PurchaseOrderAddDto dto); diff --git a/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchaseOrderServiceImpl.java b/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchaseOrderServiceImpl.java index c10ad79..3b2f93b 100644 --- a/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchaseOrderServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchaseOrderServiceImpl.java @@ -30,6 +30,7 @@ import com.niuan.erp.module.warehouse.mapper.WarehouseItemMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -52,10 +53,58 @@ public class PurchaseOrderServiceImpl extends ServiceImpl getPurchaseOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { + public IPage getPurchaseOrderPage(BasePageReqParams pageParams, String searchCode, String partNumber) { + var wrapper = new LambdaQueryWrapper(); 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 orderIds = purchaseOrderItemMapper.selectOrderIdsByPartNumber(partNumber); + if (!orderIds.isEmpty()) { + wrapper.in(Document::getId, orderIds); + } else { + wrapper.eq(Document::getId, -1); + } + } + wrapper.orderByDesc(Document::getCreateDate); IPage result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper); + + // 如果有过滤条件,查询并填充明细 + if (StringUtils.hasText(partNumber) && !result.getRecords().isEmpty()) { + List parentIds = result.getRecords().stream() + .map(Document::getId) + .toList(); + List allItems = purchaseOrderItemMapper.selectListByOrderIdsAndPartNumber(parentIds, partNumber); + + return result.convert(entity -> { + PurchaseOrderDto dto = purchaseOrderConverter.toDto(entity); + Long entityId = entity.getId(); + List 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); } diff --git a/src/main/java/com/niuan/erp/module/sale/controller/dto/DeviceDto.java b/src/main/java/com/niuan/erp/module/sale/controller/dto/DeviceDto.java index 4d37a88..1ff3cb8 100644 --- a/src/main/java/com/niuan/erp/module/sale/controller/dto/DeviceDto.java +++ b/src/main/java/com/niuan/erp/module/sale/controller/dto/DeviceDto.java @@ -18,6 +18,14 @@ public record DeviceDto( String softVersion, @Schema(description = "算法版本") String alVersion, + @Schema(description = "算法标志") + String alNum, + @Schema(description = "激活状态") + Boolean alStatus, + @Schema(description = "生产日期") + LocalDateTime createDate, + @Schema(description = "备注") + String mark, @Schema(description = "出库日期/出货日期") LocalDateTime outProductDate, @Schema(description = "返修记录拼接字符串") diff --git a/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderAddDto.java b/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderAddDto.java index 444f0d3..fcbc966 100644 --- a/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderAddDto.java +++ b/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderAddDto.java @@ -32,6 +32,13 @@ public record SaleOrderAddDto( @NotBlank(message = "sale.sale_order.validate.customer_name.not_null") String customerName, + @Schema(description = "出货仓库ID") + @NotNull(message = "sale.sale_order.validate.store_no.not_null") + Integer storeNo, + + @Schema(description = "出货仓库名称") + String storeName, + @Schema(description = "销售订单明细列表") @NotEmpty(message = "sale.sale_order.validate.sale_order_items.not_null") @Valid diff --git a/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderDto.java b/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderDto.java index 3fb6ebf..408f9c7 100644 --- a/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderDto.java +++ b/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderDto.java @@ -18,6 +18,8 @@ public record SaleOrderDto( Integer formStatus, Integer customerId, String customerName, + Integer storeNo, + String storeName, Double totalValue, String searchCode ) {} diff --git a/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderUpdateDto.java b/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderUpdateDto.java index b6dc712..d15bdd3 100644 --- a/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderUpdateDto.java +++ b/src/main/java/com/niuan/erp/module/sale/controller/dto/SaleOrderUpdateDto.java @@ -26,6 +26,11 @@ public record SaleOrderUpdateDto( @NotBlank(message = "sale.sale_order.validate.customer_name.not_null", groups = CommonValidateGroup.Update.class) 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) @Valid List saleOrderItems diff --git a/src/main/java/com/niuan/erp/module/sale/converter/SaleOrderConverter.java b/src/main/java/com/niuan/erp/module/sale/converter/SaleOrderConverter.java index 31247f7..5be42bb 100644 --- a/src/main/java/com/niuan/erp/module/sale/converter/SaleOrderConverter.java +++ b/src/main/java/com/niuan/erp/module/sale/converter/SaleOrderConverter.java @@ -22,6 +22,8 @@ public interface SaleOrderConverter { @Mapping(source = "formStatus.value", target = "formStatus") @Mapping(source = "customerId", target = "customerId") @Mapping(source = "storeName", target = "customerName") + @Mapping(source = "storeNo", target = "storeNo") + @Mapping(source = "outStoreName", target = "storeName") @Mapping(source = "totalValue", target = "totalValue") SaleOrderDto toDto(Document entity); diff --git a/src/main/java/com/niuan/erp/module/sale/service/impl/SaleOrderServiceImpl.java b/src/main/java/com/niuan/erp/module/sale/service/impl/SaleOrderServiceImpl.java index 9bdb407..3629b15 100644 --- a/src/main/java/com/niuan/erp/module/sale/service/impl/SaleOrderServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/sale/service/impl/SaleOrderServiceImpl.java @@ -77,6 +77,8 @@ public class SaleOrderServiceImpl extends ServiceImpl entity.setFormMark(dto.formMark()); entity.setCustomerId(dto.customerId()); entity.setStoreName(dto.customerName()); + entity.setStoreNo(dto.storeNo()); + entity.setOutStoreName(dto.storeName()); entity.setTotalValue(totalValue); entity.setFormType(DocumentType.SALES_ORDER); entity.setFormStatus(FormStatus.NO_APPROVE); @@ -129,6 +131,8 @@ public class SaleOrderServiceImpl extends ServiceImpl existingEntity.setFormMark(dto.formMark()); existingEntity.setCustomerId(dto.customerId()); existingEntity.setStoreName(dto.customerName()); + existingEntity.setStoreNo(dto.storeNo()); + existingEntity.setOutStoreName(dto.storeName()); existingEntity.setTotalValue(totalValue); existingEntity.setUpdateUserId(SecurityUtils.getUserId()); existingEntity.setUpdateUserName(SecurityUtils.getUserName()); @@ -247,11 +251,11 @@ public class SaleOrderServiceImpl extends ServiceImpl } 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()) { - throw new BusinessException("warehouse.stock_transfer_order.exception.insufficient_stock"); + throw new BusinessException("sale.sale_order.exception.insufficient_stock", item.getPartNumber()); } // 扣除库存 - 如果有多条记录,只更新第一条 diff --git a/src/main/java/com/niuan/erp/module/sys/converter/SysUserConverter.java b/src/main/java/com/niuan/erp/module/sys/converter/SysUserConverter.java index 7f032a8..6e65384 100644 --- a/src/main/java/com/niuan/erp/module/sys/converter/SysUserConverter.java +++ b/src/main/java/com/niuan/erp/module/sys/converter/SysUserConverter.java @@ -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.entity.SysUser; +import com.niuan.erp.module.sys.enums.UserType; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) @@ -12,4 +14,11 @@ public interface SysUserConverter { SysUser toEntity(SysUserDto dto); + default Integer mapUserTypeToInt(UserType userType) { + return userType.getValue(); + } + + default UserType mapUserTypeToUserType(Integer userType) { + return UserType.fromCode(userType); + } } diff --git a/src/main/java/com/niuan/erp/module/sys/entity/SysUser.java b/src/main/java/com/niuan/erp/module/sys/entity/SysUser.java index 9e78db9..a7bfce3 100644 --- a/src/main/java/com/niuan/erp/module/sys/entity/SysUser.java +++ b/src/main/java/com/niuan/erp/module/sys/entity/SysUser.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.niuan.erp.module.sys.enums.UserType; import lombok.Data; import java.time.LocalDateTime; @@ -37,7 +38,7 @@ public class SysUser { private String updateUserName; @TableField("UserType") - private Long userType; + private UserType userType; @TableField("LoginName") private String loginName; diff --git a/src/main/java/com/niuan/erp/module/sys/enums/UserType.java b/src/main/java/com/niuan/erp/module/sys/enums/UserType.java new file mode 100644 index 0000000..b1d42d5 --- /dev/null +++ b/src/main/java/com/niuan/erp/module/sys/enums/UserType.java @@ -0,0 +1,27 @@ +package com.niuan.erp.module.sys.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +public enum UserType implements IEnum { + 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; + } +} diff --git a/src/main/java/com/niuan/erp/module/sys/mapper/SysUserMapper.java b/src/main/java/com/niuan/erp/module/sys/mapper/SysUserMapper.java index 07e61dc..64977ba 100644 --- a/src/main/java/com/niuan/erp/module/sys/mapper/SysUserMapper.java +++ b/src/main/java/com/niuan/erp/module/sys/mapper/SysUserMapper.java @@ -14,7 +14,7 @@ public interface SysUserMapper extends BaseMapper { /** * 根据角色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 selectUserIdsByRoleId(@Param("roleId") Long roleId); /** @@ -23,8 +23,8 @@ public interface SysUserMapper extends BaseMapper { @Select(""" SELECT DISTINCT ur.UserId FROM yy_usersrolemapping ur - INNER JOIN yy_rolepermission rp ON ur.RoleId = rp.RoleId - WHERE rp.PermissionId = #{permissionId} + INNER JOIN role_permission rp ON ur.RoleId = rp.role_id + WHERE rp.permission_id = #{permissionId} """) List selectUserIdsByPermissionId(@Param("permissionId") Long permissionId); } \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/sys/service/impl/KeyAccountServiceImpl.java b/src/main/java/com/niuan/erp/module/sys/service/impl/KeyAccountServiceImpl.java index fcc31e8..fef102a 100644 --- a/src/main/java/com/niuan/erp/module/sys/service/impl/KeyAccountServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/sys/service/impl/KeyAccountServiceImpl.java @@ -45,15 +45,19 @@ public class KeyAccountServiceImpl extends ServiceImpl getKeyAccountTree() { - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(KeyAccount::getStatus, 0); - wrapper.orderByAsc(KeyAccount::getLevelPath); - List keyAccounts = this.baseMapper.selectList(wrapper); +// LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); +// wrapper.eq(KeyAccount::getStatus, 0); +// wrapper.orderByAsc(KeyAccount::getLevelPath); +// List keyAccounts = this.baseMapper.selectList(wrapper); - Map> parentMap = keyAccounts.stream() - .collect(Collectors.groupingBy(ka -> ka.getParentId() != null ? ka.getParentId() : 0L)); +// Map> parentMap = keyAccounts.stream() +// .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 buildTree(Map> parentMap, Long parentId) { diff --git a/src/main/java/com/niuan/erp/module/sys/service/impl/SysUserServiceImpl.java b/src/main/java/com/niuan/erp/module/sys/service/impl/SysUserServiceImpl.java index a7e98eb..9802b6e 100644 --- a/src/main/java/com/niuan/erp/module/sys/service/impl/SysUserServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/sys/service/impl/SysUserServiceImpl.java @@ -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.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.niuan.erp.common.base.BasePageReqParams; @@ -148,6 +149,21 @@ public class SysUserServiceImpl extends ServiceImpl impl // 密码加密 entity.setPassWord(passwordEncoder.encode(dto.passWord())); + // 得到 CustomerId + List customerIdList = this.baseMapper.selectObjs( + Wrappers.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); // 保存用户角色关联 diff --git a/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseReceiptController.java b/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseReceiptController.java index 6a4ca92..0402b38 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseReceiptController.java +++ b/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseReceiptController.java @@ -64,7 +64,7 @@ public class WarehouseReceiptController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条WarehouseReceipt记录") @Operation(summary = "更新WarehouseReceipt", operationId = "updateWarehouseReceipt") @PostMapping("/updateWarehouseReceipt") - @PreAuthorize("hasAuthority('warehouse_receipt:update')") + @PreAuthorize("hasAuthority('warehouse_receipt:edit')") public BaseResult updateWarehouseReceipt(@Validated(Update.class) @RequestBody WarehouseReceiptDto dto) { warehouseReceiptService.updateWarehouseReceipt(dto); return BaseResult.success(); diff --git a/src/main/java/com/niuan/erp/module/warehouse/service/impl/StockTransferOrderServiceImpl.java b/src/main/java/com/niuan/erp/module/warehouse/service/impl/StockTransferOrderServiceImpl.java index f52b377..efcb511 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/service/impl/StockTransferOrderServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/warehouse/service/impl/StockTransferOrderServiceImpl.java @@ -68,7 +68,7 @@ public class StockTransferOrderServiceImpl extends ServiceImpl new StockTransferOrderDto( order.getId(), @@ -419,7 +419,7 @@ public class StockTransferOrderServiceImpl extends ServiceImpl 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(); } diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index c82c2bb..15f1765 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -48,49 +48,36 @@ warehouse.inventory_count.exception.cannot_delete_approved_batch=选中的盘点 warehouse.inventory_count.exception.ids_empty=请选择要删除的记录 warehouse.inventory_count.exception.already_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.bom.validate.bom_no.not_null=BOM 编号不能为空 -production.bom.validate.bom_name.not_null=BOM 名称不能为空 -production.bom.validate.manufacturer.not_null=厂家 / 型号不能为空 -production.bom.validate.spec.not_null=封装规格不能为空 -production.bom.validate.brand_name.not_null=品牌不能为空 -production.bom.validate.part_number.not_null=物料编号不能为空 -production.bom.validate.manufacture_count.not_null=用量不能为空 -production.bom.validate.manufacture_count.min=用量最小为 1 -production.bom.validate.item_position.not_null=位号不能为空 -production.bom.exception.duplicate_bom_name=BOM 名字重复 -production.bom.exception.duplicate_bom_item=BOM 明细重复 -production.bom.exception.unpair_position_count=BOM 明细中存在位点和数量不一致 -production.bom.exception.unexists_bom_item=BOM 物料有不存在的物料 +production.finished_product_receipt.validate.form_code.not_null=成品入库单编号不能为空 +production.finished_product_receipt.validate.form_name.not_null=成品入库单名称不能为空 +production.finished_product_receipt.validate.store_no.not_null=仓库 ID 不能为空 +production.finished_product_receipt.validate.store_name.not_null=仓库名称不能为空 +production.finished_product_receipt.validate.module_sn_items.not_null=成品明细不能为空 +production.finished_product_receipt.exception.not_found=成品入库单不存在 +production.finished_product_receipt.exception.cannot_update_approved=已审核的成品入库单不能修改 +production.finished_product_receipt.exception.cannot_delete_approved=已审核的成品入库单不能删除 +production.finished_product_receipt.exception.cannot_delete_approved_batch=选中的数据中存在已审核的成品入库单,不能删除 +production.finished_product_receipt.exception.ids_empty=请选择要删除的成品入库单 +production.finished_product_receipt.exception.already_approved=成品入库单已经审核 +production.finished_product_receipt.exception.not_approved=成品入库单不是已审核状态,不能反审 +production.finished_product_receipt.exception.no_module_sn_items=成品入库单没有明细 +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.production_plan.validate.issue.not_null=发料单数据不能为空 -production.production_plan.validate.form_code.not_null=发料单编号不能为空 -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.form_code.not_null=成品出库单编号不能为空 +production.finished_product_shipment.validate.form_name.not_null=成品出库单名称不能为空 +production.finished_product_shipment.validate.store_no.not_null=仓库 ID 不能为空 production.finished_product_shipment.validate.store_name.not_null=仓库名称不能为空 production.finished_product_shipment.validate.out_stock_type.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.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=编号不存在 # ==========>> 采购管理 diff --git a/src/main/resources/i18n/messages_en_US.properties b/src/main/resources/i18n/messages_en_US.properties index ae325ab..80ff769 100644 --- a/src/main/resources/i18n/messages_en_US.properties +++ b/src/main/resources/i18n/messages_en_US.properties @@ -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.exception.not_found=Warehouse item not found 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 diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties index 5b0f257..4324316 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -164,6 +164,8 @@ 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} 库存不足 production.devicesn.validate.product_sn.not_null=SN号不能为空 production.devicesn.validate.mac.not_null=MAC地址不能为空 production.devicesn.exception.not_found=SN号不存在 diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..ee64127 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + UTF-8 + + + + + + ${LOG_PATH}/app-info.log + + INFO + + + ${LOG_PATTERN} + UTF-8 + + + + ${LOG_PATH}/app-info.%d{yyyy-MM-dd}.%i.log.gz + + 7 + + 50MB + + 1GB + + + + + + ${LOG_PATH}/app-error.log + + ERROR + ACCEPT + DENY + + + ${LOG_PATTERN} + UTF-8 + + + ${LOG_PATH}/app-error.%d{yyyy-MM-dd}.%i.log.gz + 7 + 50MB + 500MB + + + + + + + + 512 + + 0 + + false + + + + + + 512 + 0 + false + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/common/DocumentMaterialMapper.xml b/src/main/resources/mapper/common/DocumentMaterialMapper.xml index c602d4c..73f2700 100644 --- a/src/main/resources/mapper/common/DocumentMaterialMapper.xml +++ b/src/main/resources/mapper/common/DocumentMaterialMapper.xml @@ -24,7 +24,7 @@ - SELECT m.*, p.ProductSpecs FROM materialinout m LEFT JOIN product p ON p.PartNumber = m.PartNumber diff --git a/src/main/resources/mapper/purchase/PurchaseOrderItemMapper.xml b/src/main/resources/mapper/purchase/PurchaseOrderItemMapper.xml index a71d81d..9c2f4d8 100644 --- a/src/main/resources/mapper/purchase/PurchaseOrderItemMapper.xml +++ b/src/main/resources/mapper/purchase/PurchaseOrderItemMapper.xml @@ -26,4 +26,38 @@ + + + diff --git a/src/main/resources/sql/dev/permission-static-init-data.sql b/src/main/resources/sql/dev/permission-static-init-data.sql new file mode 100644 index 0000000..c016dd3 --- /dev/null +++ b/src/main/resources/sql/dev/permission-static-init-data.sql @@ -0,0 +1,2 @@ +INSERT INTO `sys_permission` VALUES (45,0,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 14:40:47',NULL,NULL,'系统设置','systemset',0,'/systemset',NULL,NULL,NULL,NULL,'ri:settings-3-line',111,0),(46,45,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 17:46:52',317,'na','栏目管理','syschannel',0,'/systemset/syschannel','/systemset/syschannel/SysChannelView','syschannel:index',NULL,NULL,NULL,10,1),(47,46,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'syschannel:add','add',NULL,NULL,1,0),(48,46,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'syschannel:edit','edit',NULL,NULL,2, 0),(49,46,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'启用','enable',3,NULL,NULL,'syschannel:enable','enable',NULL,NULL,3,0),(50,46,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'禁用','disable',3,NULL,NULL,'syschannel:disable','disable',NULL,NULL,4,0),(51,46,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'syschannel:remove','remove',NULL,NULL,5,0),(52,45,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 17:49:21',317,'na','日志管理','sysrecord',0,'/systemset/sysrecord','/systemset/sysrecord/SysRecordView','sysrecord:index',NULL,NULL,'ic:baseline-terminal',90,0),(60,45,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-13 10:27:37',NULL,NULL,'系统用户','sysuser',0,'/systemset/sysuser','/systemset/sysuser/SysUserView','sysuser:index',NULL,NULL,'material-symbols:user-attributes',20,0),(61,60,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-13 10:13:23',NULL,NULL,'新建','add',1,NULL,NULL,'sysuser:add','add',NULL,NULL,1,0),(62,60,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-13 10:13:23',NULL,NULL,'编辑','edit',2,NULL,NULL,'sysuser:edit','edit',NULL,NULL,2,0),(63,60,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-13 10:13:23',NULL,NULL,'启用','enable',3,NULL,NULL,'sysuser:enable','enable',NULL,NULL,3,0),(64,60,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-13 10:13:23',NULL,NULL,'禁用','disable',3,NULL,NULL,'sysuser:disable','disable',NULL,NULL,4,0),(65,60,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-13 10:13:23',NULL,NULL,'删除','remove',2,NULL,NULL,'sysuser:remove','remove',NULL,NULL,5,0),(66,45,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 17:46:19',317,'na','角色管理','sysrole',0,'/systemset/sysrole','/systemset/sysrole/SysRoleView','sysrole:index',NULL,NULL,'ic:baseline-badge',30,0),(67,66,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'sysrole:add','add',NULL,NULL,1,0),(68,66,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'sysrole:edit','edit',NULL,NULL,2,0),(69,66,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'启用','enable',3,NULL,NULL,'sysrole:enable','enable',NULL,NULL,3,0),(70,66,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'禁用','disable',3,NULL,NULL,'sysrole:disable','disable',NULL,NULL,4,0),(71,66,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'sysrole:remove','remove',NULL,NULL,5,0),(78,0,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 14:25:42',NULL,NULL,'仓库管理','warehouse',0,'/warehouse',NULL,NULL,NULL,NULL,'ri:home-gear-line',100,0),(79,78,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:04:44',NULL,NULL,'物料总表','warehouse_item',0,'/warehouse/warehouse-item','/warehouse/warehouse-item/WarehouseItemView','warehouse_item:index',NULL,NULL,'ic:baseline-format-list-bulleted',1,0),(80,79,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'warehouse_item:add','add',NULL,NULL,1,0),(81,79,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'warehouse_item:edit','edit',NULL,NULL,2,0),(82,79,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-04 09:41:19',NULL,NULL,'导入','import',1,NULL,NULL,'warehouse_item:import','import',NULL,NULL,3,1),(83,79,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-04 13:42:05',NULL,NULL,'供应商列表','editVendors',2,NULL,NULL,'warehouse_item:editVendors','editVendors',NULL,NULL,3,0),(84,79,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-04 13:24:48',NULL,NULL,'删除','remove',2,NULL,NULL,'warehouse_item:remove','remove',NULL,NULL,4,0),(85,78,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:04:44',NULL,NULL,'规格分类','stock_by_type',0,'/warehouse/stock-by-type','/warehouse/stock-by-type/StockByTypeView','stock_by_type:index',NULL,NULL,'ic:outline-checklist',2,0),(91,78,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:07:04',NULL,NULL,'品牌分类','stock_by_brand',0,'/warehouse/stock-by-brand','/warehouse/stock-by-brand/StockByBrandView','stock_by_brand:index',NULL,NULL,'ic:outline-local-offer',3,0),(121,78,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:07:04',NULL,NULL,'仓库分类','stock_by_warehouse',0,'/warehouse/stock-by-warehouse','/warehouse/stock-by-warehouse/StockByWarehouseView','stock_by_warehouse:index',NULL,NULL,'ic:round-warehouse',4,0),(152,78,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:10:47',NULL,NULL,'仓库调拨','stock_transfer_order',0,'/warehouse/stock-transfer-order','/warehouse/stock-transfer-order/StockTransferOrderView','stock_transfer_order:index',NULL,NULL,'ic:baseline-swap-horiz',5,0),(153,152,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'stock_transfer_order:add','add',NULL,NULL,1,0),(154,152,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'stock_transfer_order:edit','edit',NULL,NULL,2,0),(155,152,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-06 10:32:21',NULL,NULL,'审核','approve',2,NULL,NULL,'stock_transfer_order:approve','approve',NULL,NULL,3,0),(156,152,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-06 10:32:21',NULL,NULL,'反审','reject',2,NULL,NULL,'stock_transfer_order:reject','reject',NULL,NULL,4,0),(157,152,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-16 15:35:30',317,'na','删除','remove',2,NULL,NULL,'stock_transfer_order:delete','remove',NULL,NULL,5,0),(171,78,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:10:47',NULL,NULL,'库存盘点','inventory_count',0,'/warehouse/inventory-count','/warehouse/inventory-count/InventoryCountView','inventory_count:index',NULL,NULL,'ic:outline-inventory',7,0),(174,171,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'inventory_count:add','add',NULL,NULL,1,0),(176,171,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'inventory_count:remove','remove',NULL,NULL,2,0),(185,78,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:10:47',NULL,NULL,'入库单','warehouse_receipt',0,'/warehouse/warehouse-receipt','/warehouse/warehouse-receipt/WarehouseReceiptView','warehouse_receipt:index',NULL,NULL,'ic:baseline-move-to-inbox',6,0),(186,185,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'warehouse_receipt:add','add',NULL,NULL,1,0),(187,185,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'warehouse_receipt:edit','edit',NULL,NULL,2,0),(188,185,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-07 17:59:21',NULL,NULL,'审核','approve',2,NULL,NULL,'warehouse_receipt:approve','approve',NULL,NULL,3,0),(189,185,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-07 17:59:21',NULL,NULL,'反审','reject',2,NULL,NULL,'warehouse_receipt:reject','reject',NULL,NULL,4,0),(190,185,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'warehouse_receipt:remove','remove',NULL,NULL,5,0),(196,0,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 14:47:11',NULL,NULL,'生产管理','production',0,'/production',NULL,NULL,NULL,NULL,'ic:baseline-factory',101,0),(203,196,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:18:37',NULL,NULL,'生产计划','production_plan',0,'/production/production-plan','/production/production-plan/ProductionPlanView','production_plan:index',NULL,NULL,'ic:baseline-calendar-today',2,0),(204,203,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'production_plan:add','add',NULL,NULL,1,0),(205,203,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'production_plan:edit','edit',NULL,NULL,2,0),(206,203,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-26 14:49:18',NULL,NULL,'删除','remove',2,NULL,NULL,'production_plan:delete','remove',NULL,NULL,3,0),(207,203,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-26 09:26:21',NULL,NULL,'查看缺料清单','viewMaterialShortage',1,NULL,NULL,'production_plan:viewMaterialShortage','viewMaterialShortage',NULL,NULL,4,0),(208,203,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-28 16:28:08',NULL,NULL,'生成发料单','generateProductionIssue',1,NULL,NULL,'production_plan:generateProductionIssue','generateProductionIssue',NULL,NULL,5,0),(209,203,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-25 18:03:53',NULL,NULL,'查看损耗率','viewLossRate',2,NULL,NULL,'production_plan:viewLossRate','viewLossRate',NULL,NULL,7,1),(211,196,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:18:37',NULL,NULL,'生产发料单','production_issue',0,'/production/production-issue','/production/production-issue/ProductionIssueView','production_issue:index',NULL,NULL,'ic:baseline-outbox',3,0),(212,211,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-02 10:36:46',NULL,NULL,'新建','add',1,NULL,NULL,'production_issue:add','add',NULL,NULL,1,1),(213,211,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-02 10:36:46',NULL,NULL,'审核','approve',2,NULL,NULL,'production_issue:approve','approve',NULL,NULL,2,0),(214,211,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-02 10:36:46',NULL,NULL,'反审','reject',2,NULL,NULL,'production_issue:reject','reject',NULL,NULL,3,0),(215,211,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 10:19:57',NULL,NULL,'退料','production_return',2,NULL,NULL,'production_issue:productionReturn','productionReturn',NULL,NULL,4,0),(216,211,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-02 10:36:46',NULL,NULL,'删除','remove',2,NULL,NULL,'production_issue:remove','remove',NULL,NULL,5,1),(217,196,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:18:37',NULL,NULL,'生产退料单','production_return',0,'/production/production-return','/production/production-return/ProductionReturnView','production_return:index',NULL,NULL,'ic:baseline-repartition',4,0),(218,217,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 14:00:44',NULL,NULL,'审核','approve',2,NULL,NULL,'production_return:approve','approve',NULL,NULL,1,0),(219,217,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 10:42:44',NULL,NULL,'反审','reject',2,NULL,NULL,'production_return:reject','reject',NULL,NULL,2,0),(220,217,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 10:42:44',NULL,NULL,'启用','enable',3,NULL,NULL,'production_return:enable','enable',NULL,NULL,3,1),(221,217,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 10:42:44',NULL,NULL,'禁用','disable',3,NULL,NULL,'production_return:disable','disable',NULL,NULL,4,1),(222,217,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 10:42:44',NULL,NULL,'删除','remove',2,NULL,NULL,'production_return:remove','remove',NULL,NULL,5,1),(223,196,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:36:24',NULL,NULL,'成品出货单','finished_product_shipment',0,'/production/finished-product-shipment','/production/finished-product-shipment/FinishedProductShipmentView','finished_product_shipment:index',NULL,NULL,'ic:baseline-local-shipping',6,0),(224,223,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'finished_product_shipment:add','add',NULL,NULL,1,0),(225,223,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'finished_product_shipment:edit','edit',NULL,NULL,2,0),(226,223,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-11 14:01:34',NULL,NULL,'审核','approve',2,NULL,NULL,'finished_product_shipment:approve','approve',NULL,NULL,3,0),(227,223,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-11 14:01:34',NULL,NULL,'反审','reject',2,NULL,NULL,'finished_product_shipment:reject','reject',NULL,NULL,4,0),(228,223,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-11 14:01:34',NULL,NULL,'展示明细表','showItem',2,NULL,NULL,'finished_product_shipment:showItem','showItem',NULL,NULL,5,0),(229,0,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 14:38:37',NULL,NULL,'采购管理','purchase',0,'/purchase',NULL,NULL,NULL,NULL,'ri:shopping-cart-2-line',102,0),(230,229,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:22:48',NULL,NULL,'采购订单','purchase_order',0,'/purchase/purchase-order','/purchase/purchase-order/PurchaseOrderView','purchase_order:index',NULL,NULL,'ic:baseline-receipt-long',1,0),(231,230,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'purchase_order:add','add',NULL,NULL,1,0),(232,230,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'purchase_order:edit','edit',NULL,NULL,2,0),(233,230,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-07 15:35:11',NULL,NULL,'启用','enable',3,NULL,NULL,'purchase_order:enable','enable',NULL,NULL,3,1),(234,230,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-07 15:35:11',NULL,NULL,'禁用','disable',3,NULL,NULL,'purchase_order:disable','disable',NULL,NULL,4,1),(235,230,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'purchase_order:remove','remove',NULL,NULL,5,0),(236,0,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 14:37:15',NULL,NULL,'销售管理','sale',0,'/sale',NULL,NULL,NULL,NULL,'ri:store-2-line',103,0),(237,236,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:26:32',NULL,NULL,'销售订单','sale_order',0,'/sale/sale-order','/sale/sale-order/SaleOrderView','sale_order:index',NULL,NULL,'ic:baseline-fact-check',1,0),(238,237,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'sale_order:add','add',NULL,NULL,1,0),(239,237,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'sale_order:edit','edit',NULL,NULL,2,0),(240,237,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 14:33:35',NULL,NULL,'审核','approve',2,NULL,NULL,'sale_order:approve','approve',NULL,NULL,3,0),(241,237,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 14:33:35',NULL,NULL,'反审','reject',2,NULL,NULL,'sale_order:reject','reject',NULL,NULL,4,0),(242,237,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'sale_order:remove','remove',NULL,NULL,5,0),(312,0,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-13 10:39:05',NULL,NULL,'数据报表','report',0,'/report',NULL,NULL,NULL,NULL,NULL,104,0),(313,312,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 14:53:31',NULL,NULL,'返修报表','repair_record',0,'/sale/repair-record','/sale/repair-record/RepairRecordView','repair_record:index',NULL,NULL,'ic:outline-home-repair-service',1,0),(314,313,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'repair_record:add','add',NULL,NULL,1,0),(316,45,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:10:47',NULL,NULL,'仓库列表','warehouse',0,'/warehouse/warehouse','/warehouse/warehouse/WarehouseView','warehouse:index',NULL,NULL,'ic:outline-warehouse',92,0),(317,316,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'warehouse:add','add',NULL,NULL,1,0),(318,316,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'warehouse:edit','edit',NULL,NULL,2,0),(319,316,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'启用','enable',3,NULL,NULL,'warehouse:enable','enable',NULL,NULL,3,0),(320,316,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'禁用','disable',3,NULL,NULL,'warehouse:disable','disable',NULL,NULL,4,0),(321,316,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'warehouse:remove','remove',NULL,NULL,5,0),(322,45,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-13 10:38:29',NULL,NULL,'用户列表','customer',0,'/systemset/customer','/systemset/customer/CustomerView','customer:index',NULL,NULL,NULL,93,0),(323,322,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'customer:add','add',NULL,NULL,1,0),(324,322,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'customer:edit','edit',NULL,NULL,2,0),(325,322,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'customer:remove','remove',NULL,NULL,3,0),(326,45,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 17:50:51',317,'na','供应商列表','vendor',0,'/systemset/vendor','/systemset/vendor/VendorView','vendor:index',NULL,NULL,'ic:baseline-business',94,0),(327,326,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'vendor:add','add',NULL,NULL,1,0),(328,326,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'vendor:edit','edit',NULL,NULL,2,0),(329,326,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'vendor:remove','remove',NULL,NULL,3,0),(333,171,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-09 15:59:00',NULL,NULL,'编辑','edit',2,NULL,NULL,'inventory_count:edit','edit',NULL,NULL,3,1),(334,171,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-09 16:08:28',NULL,NULL,'展示明细表','showItem',2,NULL,NULL,'inventory_count:showItem','showItem',NULL,NULL,7,0),(335,171,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-09 15:59:00',NULL,NULL,'禁用','disable',3,NULL,NULL,'inventory_count:disable','disable',NULL,NULL,6,1),(337,211,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-02 10:36:46',NULL,NULL,'创建','create',2,NULL,NULL,'production_issue:create','create',NULL,NULL,6,1),(339,196,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:18:37',NULL,NULL,'Bom管理','bom',0,'/production/bom','/production/bom/BomView','bom:index',NULL,NULL,'ri:node-tree',1,0),(340,339,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'bom:add','add',NULL,NULL,1,0),(341,339,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-24 09:48:54',NULL,NULL,'编辑','editData',2,NULL,NULL,'bom:edit','edit',NULL,NULL,2,0),(342,339,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'bom:remove','remove',NULL,NULL,3,0),(343,203,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 14:51:01',NULL,NULL,'生成采购计划','generatePurchasePlan',4,NULL,NULL,'production_plan:generatePurchasePlan','generatePurchasePlan',NULL,NULL,8,0),(345,229,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:22:48',NULL,NULL,'采购计划','purchase_plan',0,'/purchase/purchase-plan','/purchase/purchase-plan/PurchasePlanView','purchase_plan:index',NULL,NULL,'ic:baseline-pending-actions',2,0),(346,345,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 14:31:42',NULL,NULL,'新建','add',1,NULL,NULL,'purchase_plan:add','add',NULL,NULL,1,0),(347,345,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 14:11:01',NULL,NULL,'编辑','editData',2,NULL,NULL,'purchase_plan:editData','editData',NULL,NULL,2,1),(348,345,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 14:11:01',NULL,NULL,'启用','enable',3,NULL,NULL,'purchase_plan:enable','enable',NULL,NULL,3,1),(349,345,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 14:11:01',NULL,NULL,'禁用','disable',3,NULL,NULL,'purchase_plan:disable','disable',NULL,NULL,4,1),(350,345,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'删除','remove',2,NULL,NULL,'purchase_plan:remove','remove',NULL,NULL,5,0),(351,0,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 16:53:26',NULL,NULL,'打印','printHtml',2,'printHtml','producedeliver/print','printHtml','printHtml',NULL,NULL,112,0),(352,211,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-02 10:36:46',NULL,NULL,'打印','printHtml',2,NULL,NULL,'production_issue:printHtml','printHtml',NULL,NULL,7,1),(353,171,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-09 15:59:00',NULL,NULL,'打印','printHtml',2,NULL,NULL,'inventory_count:printHtml','printHtml',NULL,NULL,8,1),(354,0,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-13 10:39:12',NULL,NULL,'出库管理','/',0,'/','/app',NULL,NULL,NULL,NULL,113,0),(355,354,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-13 10:38:29',NULL,NULL,'扫码出库','appOut',0,'/systemset/appOut','/systemset/appOut/appOutView','appOut:index',NULL,NULL,NULL,1,0),(356,236,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:26:32',NULL,NULL,'SN溯源','device_sn',0,'/sale/device-sn','/sale/device-sn/DeviceSnView','device_sn:index',NULL,NULL,'ic:outline-qr-code-scanner',2,0),(357,45,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 17:57:14',317,'na','客户列表','key_account',0,'/systemset/key-account','/systemset/key-account/KeyAccountView','key_account:index',NULL,NULL,'ic:outline-lan',95,0),(358,357,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'key_account:add','add',NULL,NULL,1,0),(359,357,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'编辑','edit',2,NULL,NULL,'key_account:edit','edit',NULL,NULL,2,0),(360,196,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 15:36:24',NULL,NULL,'成品入库单','finished_product_receipt',0,'/production/finished-product-receipt','/production/finished-product-receipt/FinishedProductReceiptView','finished_product_receipt:index',NULL,NULL,'ic:baseline-production-quantity-limits',5,0),(361,360,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'finished_product_receipt:add','add',NULL,NULL,1,0),(362,360,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-10 10:56:19',NULL,NULL,'删除','remove',2,NULL,NULL,'finished_product_receipt:remove','remove',NULL,NULL,3,0),(363,339,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-24 09:48:54',NULL,NULL,'BOM 明细','showItem',2,NULL,NULL,'bom:showItem','showItem',NULL,NULL,4,0),(364,203,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-28 10:43:34',NULL,NULL,'生成调拨单','generateTransferOrder',4,NULL,NULL,'production_plan:generateTransferOrder','generateTransferOrder',NULL,NULL,8,0),(365,345,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 14:51:10',NULL,NULL,'生成采购订单','generatePurchaseOrder',2,NULL,NULL,'purchase_plan:generatePurchaseOrder','generatePurchaseOrder',NULL,NULL,5,0),(366,79,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-04 13:42:05',NULL,NULL,'打印二维码','printQrCode',2,NULL,NULL,'warehouse_item:printQrCode','printQrCode',NULL,NULL,5,0),(367,230,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'采购入库','inbound',2,NULL,NULL,'purchase_order:inbound','inbound',NULL,NULL,5,0),(368,230,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'打印二维码','printQrCode',2,NULL,NULL,'purchase_order:printQrCode','printQrCode',NULL,NULL,5,0),(369,360,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-16 10:11:19',NULL,NULL,'展示明细','showItem',2,NULL,NULL,'finished_product_receipt:showItem','showItem',NULL,NULL,2,0),(370,360,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'出货','outstock',2,NULL,NULL,'finished_product_receipt:outstock','outstock',NULL,NULL,1,0),(371,237,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'展示明细','showItem',2,NULL,NULL,'sale_order:showItem','showItem',NULL,NULL,5,0),(372,45,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 17:43:31',317,'na','权限管理','syschannel',0,'/systemset/permission','/systemset/permission/PermissionView','sys_permission:index',NULL,NULL,'ic:baseline-key',10,0),(373,372,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'新建','add',1,NULL,NULL,'sys_permission:add','add',NULL,NULL,1,0),(374,372,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 17:42:02',317,'na','批量删除','deleteBatch',1,NULL,NULL,'sys_permission:deleteBatch','removeBatch',NULL,NULL,2,1),(375,372,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 17:03:11',NULL,NULL,'更新','update',2,NULL,NULL,'sys_permission:update','edit',NULL,NULL,1,0),(376,372,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-12 17:14:06',NULL,NULL,'删除','delete',2,NULL,NULL,'sys_permission:delete','remove',NULL,NULL,2,0),(377,152,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'展示明细','showItem',2,NULL,NULL,'stock_transfer_order:showItem','showItem',NULL,NULL,6,0),(378,185,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'展示明细','showItem',2,NULL,NULL,'warehouse_receipt:showItem','showItem',NULL,NULL,6,0),(379,211,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-02 10:36:46',NULL,NULL,'展示明细','showItem',2,NULL,NULL,'production_issue:showItem','showItem',NULL,NULL,6,0),(380,217,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-16 10:50:22',NULL,NULL,'展示明细','showItem',2,NULL,NULL,'production_return:showItem','showItem',NULL,NULL,6,0),(381,217,0,'2026-02-12 16:53:26',NULL,NULL,'2026-03-03 10:42:44',NULL,NULL,'展示明细','showItem',2,NULL,NULL,'production_return:showItem','remove',NULL,NULL,6,1),(382,230,0,'2026-02-12 16:53:26',NULL,NULL,'2026-02-12 17:45:50',NULL,NULL,'展示明细','showItem',2,NULL,NULL,'purchase_order:showItem','showItem',NULL,NULL,6,0),(383,345,0,'2026-03-16 11:22:49',317,'na','2026-03-17 14:57:11',317,'na','展示明细','showItem',2,NULL,NULL,'purchase_order:showItem','showItem',NULL,NULL,6,0); +INSERT INTO `role_permission` VALUES (1,45),(6,45),(8,45),(1,46),(6,46),(8,46),(1,47),(6,47),(8,47),(1,48),(6,48),(8,48),(1,49),(6,49),(8,49),(1,50),(6,50),(8,50),(1,51),(6,51),(8,51),(1,52),(6,52),(8,52),(1,60),(6,60),(8,60),(1,61),(6,61),(8,61),(1,62),(6,62),(8,62),(1,63),(6,63),(8,63),(1,64),(6,64),(8,64),(1,65),(6,65),(8,65),(1,66),(6,66),(8,66),(1,67),(6,67),(8,67),(1,68),(6,68),(8,68),(1,69),(6,69),(8,69),(1,70),(6,70),(8,70),(1,71),(6,71),(8,71),(1,78),(2,78),(6,78),(8,78),(1,79),(2,79),(6,79),(8,79),(10,79),(1,80),(2,80),(6,80),(8,80),(10,80),(11,80),(1,81),(2,81),(6,81),(8,81),(10,81),(11,81),(1,82),(2,82),(6,82),(8,82),(10,82),(11,82),(1,83),(2,83),(6,83),(8,83),(10,83),(11,83),(1,84),(2,84),(6,84),(8,84),(10,84),(1,85),(2,85),(6,85),(8,85),(10,85),(1,91),(2,91),(6,91),(8,91),(1,121),(2,121),(6,121),(8,121),(10,121),(1,152),(2,152),(6,152),(8,152),(10,152),(1,153),(2,153),(6,153),(8,153),(10,153),(1,154),(2,154),(6,154),(8,154),(10,154),(1,155),(2,155),(6,155),(8,155),(10,155),(1,156),(2,156),(6,156),(8,156),(10,156),(1,157),(2,157),(6,157),(8,157),(10,157),(1,171),(2,171),(6,171),(8,171),(1,174),(2,174),(6,174),(8,174),(1,176),(2,176),(6,176),(8,176),(1,185),(2,185),(6,185),(8,185),(1,186),(2,186),(6,186),(8,186),(1,187),(2,187),(6,187),(8,187),(1,188),(2,188),(6,188),(8,188),(1,189),(2,189),(6,189),(8,189),(1,190),(2,190),(6,190),(8,190),(1,196),(2,196),(6,196),(8,196),(1,203),(2,203),(6,203),(8,203),(1,204),(2,204),(6,204),(8,204),(1,205),(2,205),(6,205),(8,205),(1,206),(2,206),(6,206),(8,206),(1,207),(2,207),(6,207),(8,207),(1,208),(2,208),(6,208),(8,208),(1,209),(2,209),(6,209),(8,209),(1,211),(2,211),(6,211),(8,211),(1,212),(2,212),(6,212),(8,212),(1,213),(2,213),(6,213),(8,213),(1,214),(2,214),(6,214),(8,214),(1,215),(2,215),(6,215),(8,215),(1,216),(2,216),(6,216),(8,216),(1,217),(2,217),(6,217),(8,217),(2,218),(6,218),(8,218),(2,219),(6,219),(8,219),(2,220),(6,220),(8,220),(2,221),(6,221),(8,221),(2,222),(6,222),(8,222),(1,223),(2,223),(6,223),(8,223),(1,224),(2,224),(6,224),(8,224),(1,225),(2,225),(6,225),(8,225),(1,226),(2,226),(6,226),(8,226),(1,227),(2,227),(6,227),(8,227),(1,228),(2,228),(6,228),(8,228),(1,229),(2,229),(8,229),(1,230),(2,230),(6,230),(8,230),(1,231),(2,231),(6,231),(8,231),(1,232),(2,232),(6,232),(8,232),(2,233),(6,233),(8,233),(2,234),(6,234),(8,234),(2,235),(6,235),(8,235),(1,236),(2,236),(6,236),(8,236),(9,236),(1,237),(6,237),(8,237),(9,237),(1,238),(6,238),(8,238),(9,238),(1,239),(6,239),(8,239),(9,239),(1,240),(6,240),(8,240),(9,240),(1,241),(6,241),(8,241),(9,241),(1,242),(6,242),(8,242),(9,242),(1,312),(2,312),(6,312),(8,312),(1,313),(2,313),(6,313),(8,313),(1,314),(2,314),(6,314),(8,314),(1,316),(6,316),(8,316),(1,317),(6,317),(8,317),(9,317),(1,318),(6,318),(8,318),(9,318),(1,319),(6,319),(8,319),(1,320),(6,320),(8,320),(1,321),(6,321),(8,321),(1,322),(6,322),(8,322),(1,323),(6,323),(8,323),(1,324),(6,324),(8,324),(1,325),(6,325),(8,325),(1,326),(6,326),(8,326),(1,327),(6,327),(8,327),(1,328),(6,328),(8,328),(1,329),(6,329),(8,329),(1,333),(2,333),(6,333),(8,333),(1,334),(2,334),(6,334),(8,334),(1,335),(2,335),(6,335),(8,335),(1,337),(2,337),(6,337),(8,337),(1,339),(2,339),(6,339),(8,339),(1,340),(2,340),(6,340),(8,340),(1,341),(2,341),(6,341),(8,341),(1,342),(2,342),(6,342),(8,342),(1,343),(2,343),(6,343),(8,343),(2,345),(8,345),(2,346),(6,346),(8,346),(2,347),(6,347),(8,347),(1,348),(2,348),(6,348),(8,348),(1,349),(2,349),(6,349),(8,349),(1,350),(2,350),(6,350),(8,350),(1,351),(2,351),(8,351),(1,352),(6,352),(8,352),(6,353),(8,353),(1,356),(2,356),(6,356),(8,356),(9,356),(1,357),(6,357),(8,357),(1,358),(6,358),(8,358),(1,359),(6,359),(8,359),(1,360),(2,360),(6,360),(8,360),(1,361),(2,361),(6,361),(8,361),(1,362),(2,362),(6,362),(8,362),(6,363),(8,363),(6,364),(8,364),(6,365),(8,365),(6,366),(8,366),(6,367),(8,367),(6,368),(8,368),(6,369),(8,369),(6,370),(8,370),(6,371),(8,371),(9,371),(6,372),(8,372),(6,373),(8,373),(6,374),(8,374),(6,375),(8,375),(6,376),(8,376),(6,377),(8,377),(6,378),(8,378),(6,379),(8,379),(6,380),(8,380),(6,381),(8,381),(6,382),(8,382),(8,383);