master
liweidong-hj 3 months ago
parent 014187cf99
commit dbf2112ca0
  1. 19
      pom.xml
  2. 42
      src/main/java/com/nov/KgLowDurable/config/FileUploadConfig.java
  3. 59
      src/main/java/com/nov/KgLowDurable/config/RedisConfig.java
  4. 15
      src/main/java/com/nov/KgLowDurable/constant/BatchConstant.java
  5. 38
      src/main/java/com/nov/KgLowDurable/controller/LdDurableFormController.java
  6. 4
      src/main/java/com/nov/KgLowDurable/controller/LdInventoryRecordController.java
  7. 24
      src/main/java/com/nov/KgLowDurable/controller/LdMaterialController.java
  8. 4
      src/main/java/com/nov/KgLowDurable/controller/LdTwoInventoryRecordController.java
  9. 2
      src/main/java/com/nov/KgLowDurable/mapper/LdDurableFormMapper.java
  10. 30
      src/main/java/com/nov/KgLowDurable/pojo/dto/ApproveDto.java
  11. 48
      src/main/java/com/nov/KgLowDurable/pojo/dto/LdDurableFormDto.java
  12. 5
      src/main/java/com/nov/KgLowDurable/pojo/entity/LdDurableForm.java
  13. 3
      src/main/java/com/nov/KgLowDurable/pojo/entity/LdInventoryRecord.java
  14. 3
      src/main/java/com/nov/KgLowDurable/pojo/entity/LdTwoOutStorageDetail.java
  15. 8
      src/main/java/com/nov/KgLowDurable/pojo/vo/UserInfoVO.java
  16. 15
      src/main/java/com/nov/KgLowDurable/service/ILdDurableFormService.java
  17. 5
      src/main/java/com/nov/KgLowDurable/service/ILdInventoryRecordService.java
  18. 5
      src/main/java/com/nov/KgLowDurable/service/ILdTwoInventoryRecordService.java
  19. 31
      src/main/java/com/nov/KgLowDurable/service/Impl/LdDurableFormServiceImpl.java
  20. 19
      src/main/java/com/nov/KgLowDurable/service/Impl/LdInventoryRecordServiceImpl.java
  21. 19
      src/main/java/com/nov/KgLowDurable/service/Impl/LdOneOutStorageServiceImpl.java
  22. 13
      src/main/java/com/nov/KgLowDurable/service/Impl/LdOnePutStorageServiceImpl.java
  23. 67
      src/main/java/com/nov/KgLowDurable/service/Impl/LdTwoInventoryRecordServiceImpl.java
  24. 8
      src/main/java/com/nov/KgLowDurable/service/Impl/LdTwoOutStorageServiceImpl.java
  25. 8
      src/main/java/com/nov/KgLowDurable/service/Impl/LdTwoPutStorageServiceImpl.java
  26. 49
      src/main/java/com/nov/KgLowDurable/service/Impl/UserServiceImpl.java
  27. 224
      src/main/java/com/nov/KgLowDurable/util/ImageUploadUtil.java
  28. 159
      src/main/java/com/nov/KgLowDurable/util/OrderNoGen.java
  29. 313
      src/main/java/com/nov/KgLowDurable/util/SerialNumberUtil.java
  30. 12
      src/main/resources/application-test.properties
  31. 4
      src/main/resources/mapper/LdDurableFormMapper.xml

@ -223,7 +223,7 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.36.Final</version>
<version>4.1.68.Final</version>
</dependency>
<dependency>
@ -262,19 +262,10 @@
<artifactId>gson</artifactId>
</dependency>
<!-- &lt;!&ndash; Redis相关依赖 &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-redis</artifactId>-->
<!-- <version>2.7.18</version>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; Redis连接池 &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>org.apache.commons</groupId>-->
<!-- <artifactId>commons-pool2</artifactId>-->
<!-- <version>2.11.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>

@ -0,0 +1,42 @@
package com.nov.KgLowDurable.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author: liweidong
* @create: 2025-12-31
*/
@Component
public class FileUploadConfig {
// 本地服务器地址
@Value("${file.upload.server:http://10.90.100.231:8132}")
private String serverUrl;
// 基础存储路径
@Value("${file.upload.base-path:/opt/SmartParkFilesTest}")
private String basePath;
// 允许的文件类型
@Value("${file.upload.allowed-types:jpg,jpeg,png,gif,bmp}")
private String allowedTypes;
// 最大文件大小(MB)
@Value("${file.upload.max-size:10}")
private long maxFileSize;
public String getServerUrl() {
return serverUrl;
}
public String getBasePath() {
return basePath;
}
public String[] getAllowedTypes() {
return allowedTypes.split(",");
}
public long getMaxFileSize() {
return maxFileSize * 1024 * 1024; // 转换为字节
}
}

@ -1,31 +1,28 @@
//package com.nov.KgLowDurable.config;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.data.redis.connection.RedisConnectionFactory;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
//import org.springframework.data.redis.serializer.StringRedisSerializer;
//
///**
// * @author: liweidong
// * @create: 2025-12-17
// */
//@Configuration
//public class RedisConfig {
// @Bean
// public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// RedisTemplate<String, Object> template = new RedisTemplate<>();
// template.setConnectionFactory(connectionFactory);
//
// // 使用 String 序列化器作为 key 的序列化器
// template.setKeySerializer(new StringRedisSerializer());
// template.setHashKeySerializer(new StringRedisSerializer());
//
// // 使用 JSON 序列化器作为 value 的序列化器(可以存储复杂对象)
// template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
//
// template.afterPropertiesSet();
// return template;
// }
//}
package com.nov.KgLowDurable.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author: liweidong
* @create: 2025-12-17
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 使用String序列化
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}

@ -82,6 +82,21 @@ public interface BatchConstant {
*/
String OUT_STORAGE_TYPE = "1";
/** 耐用品状态*/
/**
* 借出
*/
String LEND = "0";
/**
* 归还
*/
String RESTORE = "1";
/**
* 归还
*/
String SCRAP = "2";

@ -1,13 +1,14 @@
package com.nov.KgLowDurable.controller;
import com.nov.KgLowDurable.pojo.dto.LdDurableFormDto;
import com.nov.KgLowDurable.service.ILdDurableFormService;
import com.nov.KgLowDurable.util.Result;
import com.nov.KgLowDurable.util.SerialNumberUtil;
import io.swagger.annotations.*;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
@ -38,4 +39,33 @@ public class LdDurableFormController {
return Result.OK(durableFormService.getDurableFormList(departmentId,materialName,pageNum,pageSize));
}
@PostMapping("/restoreAndScrap")
@ApiOperationSort(2)
@ApiOperation(value = "二级耐用品归还报废", notes = "修改耐用品状态", httpMethod = "POST", response = Result.class)
public Result restoreAndScrap(@RequestBody LdDurableFormDto dto) {
return Result.OK(durableFormService.restoreAndScrap(dto));
}
@Autowired
SerialNumberUtil serialNumberUtil;
@PostMapping("/test")
@ApiOperationSort(3)
@ApiOperation(value = "二级耐用品归还报废", notes = "修改耐用品状态", httpMethod = "POST", response = Result.class)
public Result test() {
for (int i = 0; i < 10; i++) {
String fi = serialNumberUtil.generateSimple("FI");
String fo = serialNumberUtil.generateSimple("FO");
String si = serialNumberUtil.generateSimple("SI");
String fid = serialNumberUtil.generateSimple("FID");
System.out.println("第"+i+"次"+":"+fi+" ,"+fo+", "+si+", "+fid);
}
List<String> list = serialNumberUtil.generateBatchSimple("FID", 10);
System.out.println(list);
return Result.OK();
}
}

@ -37,9 +37,7 @@ public class LdInventoryRecordController {
@ApiOperationSort(2)
@ApiOperation(value = "审批通过", notes = "回调接口", httpMethod = "POST", response = Result.class)
public Result approve(@RequestBody ApproveDto approveDto) {
Long onePutStorageId = approveDto.getOnePutStorageId();
boolean approveResult = approveDto.getApproveResult();
return Result.OK(inventoryRecordService.approve(onePutStorageId,approveResult));
return Result.OK(inventoryRecordService.approve(approveDto));
}

@ -2,12 +2,15 @@ package com.nov.KgLowDurable.controller;
import com.nov.KgLowDurable.enums.MaterialCategoryEnum;
import com.nov.KgLowDurable.pojo.entity.LdMaterial;
import com.nov.KgLowDurable.service.ILdMaterialService;
import com.nov.KgLowDurable.util.ImageUploadUtil;
import com.nov.KgLowDurable.util.Result;
import com.nov.KgLowDurable.util.StringUtils;
import io.swagger.annotations.*;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
@ -84,4 +87,23 @@ public class LdMaterialController {
return Result.OK(MaterialCategoryEnum.getEnumList());
}
@Autowired
private ImageUploadUtil imageUploadUtil;
@PostMapping("/upload")
@ApiOperationSort(7)
public Result uploadImage(
@RequestParam("file") MultipartFile file) {
try {
String module = "material";
String fileUrl = imageUploadUtil.uploadImage(file, module);
return Result.ok(fileUrl);
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
} catch (IOException e) {
return Result.error("文件上传失败: " + e.getMessage());
}
}
}

@ -40,9 +40,7 @@ public class LdTwoInventoryRecordController {
@ApiOperationSort(2)
@ApiOperation(value = "审批通过", notes = "二级出库回调接口", httpMethod = "POST", response = Result.class)
public Result approve(@RequestBody ApproveDto approveDto) {
Long twoOutStorageId = approveDto.getTwoOutStorageId();
boolean approveResult = approveDto.getApproveResult();
return Result.OK(twoInventoryRecordService.approve(twoOutStorageId,approveResult));
return Result.OK(twoInventoryRecordService.approve(approveDto));
}

@ -13,4 +13,6 @@ public interface LdDurableFormMapper extends BaseMapper<LdDurableForm> {
List<LdDurableForm> selectDurableFormList(@Param("departmentId") String departmentId, @Param("materialName") String materialName);
LdDurableForm selectByMaterialId(@Param("materialId") Long materialId);
}

@ -1,7 +1,11 @@
package com.nov.KgLowDurable.pojo.dto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* @author: liweidong
* @create: 2025-12-24
@ -24,5 +28,31 @@ public class ApproveDto {
*/
private Boolean approveResult;
/**
* 操作人ID
*/
private String operatorId;
/**
* 操作人姓名
*/
private String operatorName;
/**
* 操作时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date operationTime;
/**
* 部门ID
*/
private String departmentId;
/**
* 部门名称
*/
private String departmentName;
}

@ -0,0 +1,48 @@
package com.nov.KgLowDurable.pojo.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 低值耐用品二级库库存表VO
* @author: liweidong
* @create: 2025-12-24
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class LdDurableFormDto implements Serializable {
/**
* 耐用品Id
*/
private Long durableFormId;
/**
* 数量
*/
private BigDecimal num;
/**
* 归还时间
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private Date returnTime;
/**
* 归还理由
*/
private String returnReason;
/**
* 1.归还 2.报废
*/
private String type;
}

@ -112,5 +112,10 @@ public class LdDurableForm implements Serializable {
@JsonFormat(pattern = "yyyy-MM-dd")
private Date returnTime;
/**
* 归还理由
*/
private String returnReason;
}

@ -6,6 +6,7 @@ import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 出入库记录
@ -58,7 +59,7 @@ public class LdInventoryRecord {
/**
* 操作时间
*/
private LocalDateTime operationTime;
private Date operationTime;
/**
* 部门ID

@ -4,6 +4,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
@ -80,7 +81,7 @@ public class LdTwoOutStorageDetail implements Serializable {
* 数量 出库数量
*/
@TableField("num")
private String num;
private BigDecimal num;
/**
* 物品详细描述

@ -3,6 +3,8 @@ package com.nov.KgLowDurable.pojo.vo;
import lombok.Data;
import java.util.List;
/**
* 视图实体类
*
@ -26,6 +28,7 @@ public class UserInfoVO {
*/
private String department;
/**
* 用户性别
*/
@ -36,6 +39,9 @@ public class UserInfoVO {
*/
private String phone;
private List<> rolelist;
/**
* 用户的权限名包含所有的权限
*/
@ -50,4 +56,6 @@ public class UserInfoVO {
* 用户主部门名称
*/
private String mainErDepartmentName;
}

@ -2,6 +2,7 @@ package com.nov.KgLowDurable.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.github.pagehelper.PageInfo;
import com.nov.KgLowDurable.pojo.dto.LdDurableFormDto;
import com.nov.KgLowDurable.pojo.entity.LdDurableForm;
@ -20,4 +21,18 @@ public interface ILdDurableFormService extends IService<LdDurableForm> {
* @return
*/
PageInfo<LdDurableForm> getDurableFormList(String departmentId, String materialName, Integer pageNum, Integer pageSize);
/**
* 归还 报废 接口
* @param dto
* @return
*/
boolean restoreAndScrap(LdDurableFormDto dto);
/**
* 根据物资ID 查询
* @param materialId
* @return
*/
LdDurableForm getByMaterialId(Long materialId);
}

@ -2,6 +2,7 @@ package com.nov.KgLowDurable.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.github.pagehelper.PageInfo;
import com.nov.KgLowDurable.pojo.dto.ApproveDto;
import com.nov.KgLowDurable.pojo.entity.LdInventoryRecord;
@ -25,9 +26,7 @@ public interface ILdInventoryRecordService extends IService<LdInventoryRecord> {
/**
* 审批通过 回调
* @param onePutStorageId
* @param approveResult
* @return
*/
boolean approve(Long onePutStorageId, boolean approveResult);
boolean approve(ApproveDto approveDto);
}

@ -1,6 +1,7 @@
package com.nov.KgLowDurable.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.github.pagehelper.PageInfo;
import com.nov.KgLowDurable.pojo.dto.ApproveDto;
import com.nov.KgLowDurable.pojo.entity.LdTwoInventoryRecord;
@ -23,9 +24,7 @@ public interface ILdTwoInventoryRecordService extends IService<LdTwoInventoryRec
/**
* 二级出库 审批回调接口
* @param twoOutStorageId
* @param approveResult
* @return
*/
boolean approve(Long twoOutStorageId, boolean approveResult);
boolean approve(ApproveDto approveDto);
}

@ -3,8 +3,10 @@ package com.nov.KgLowDurable.service.Impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.nov.KgLowDurable.exception.CustomerException;
import com.nov.KgLowDurable.mapper.LdApproveMapper;
import com.nov.KgLowDurable.mapper.LdDurableFormMapper;
import com.nov.KgLowDurable.pojo.dto.LdDurableFormDto;
import com.nov.KgLowDurable.pojo.entity.LdApprove;
import com.nov.KgLowDurable.pojo.entity.LdConsumerForm;
import com.nov.KgLowDurable.pojo.entity.LdDurableForm;
@ -14,6 +16,7 @@ import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
/**
@ -34,4 +37,32 @@ public class LdDurableFormServiceImpl extends ServiceImpl<LdDurableFormMapper, L
PageInfo<LdDurableForm> pageInfo = new PageInfo<>(ldDurableFormList);
return pageInfo;
}
@Override
public boolean restoreAndScrap(LdDurableFormDto dto) {
if(null == dto.getDurableFormId()){
throw new CustomerException("耐用品ID为空");
}
LdDurableForm ldDurableForm = durableFormMapper.selectById(dto.getDurableFormId());
if(null == ldDurableForm){
throw new CustomerException("耐用品不存在");
}
if(dto.getNum().compareTo(BigDecimal.ZERO) <= 0 || null == dto.getNum()){
throw new CustomerException("耐用品数量错误");
}
ldDurableForm.setNum(dto.getNum())
.setReturnTime(dto.getReturnTime())
.setReturnReason(dto.getReturnReason())
.setIsBorrow(dto.getType());
int updateReturn = durableFormMapper.updateById(ldDurableForm);
if(updateReturn <= 0){
throw new CustomerException("操作失败");
}
return true;
}
@Override
public LdDurableForm getByMaterialId(Long materialId) {
return durableFormMapper.selectByMaterialId(materialId);
}
}

@ -7,6 +7,7 @@ import com.nov.KgLowDurable.constant.BatchConstant;
import com.nov.KgLowDurable.exception.CustomerException;
import com.nov.KgLowDurable.mapper.LdInventoryRecordMapper;
import com.nov.KgLowDurable.mapper.LdMaterialMapper;
import com.nov.KgLowDurable.pojo.dto.ApproveDto;
import com.nov.KgLowDurable.pojo.entity.*;
import com.nov.KgLowDurable.pojo.vo.LdMaterialVO;
import com.nov.KgLowDurable.service.*;
@ -64,13 +65,13 @@ public class LdInventoryRecordServiceImpl extends ServiceImpl<LdInventoryRecordM
@Override
@Transactional(rollbackFor = Exception.class)
public boolean approve(Long onePutStorageId, boolean approveResult) {
LdOnePutStorage ldOnePutStorage = onePutStorageService.getById(onePutStorageId);
public boolean approve(ApproveDto approveDto) {
LdOnePutStorage ldOnePutStorage = onePutStorageService.getById(approveDto.getOnePutStorageId());
if(null == ldOnePutStorage){
throw new CustomerException("未查到一级入库单");
}
//更新审批状态
if(!approveResult){
if(!approveDto.getApproveResult()){
ldOnePutStorage.setStatus(BatchConstant.APPROVAL_FAILED);
onePutStorageService.updateById(ldOnePutStorage);
return true;
@ -81,7 +82,7 @@ public class LdInventoryRecordServiceImpl extends ServiceImpl<LdInventoryRecordM
//库存入库
List<LdOnePutStorageDetail> onePutStorageDetailList = onePutStorageDetailService.getByOnePutStorageId(onePutStorageId);
List<LdOnePutStorageDetail> onePutStorageDetailList = onePutStorageDetailService.getByOnePutStorageId(approveDto.getOnePutStorageId());
for (LdOnePutStorageDetail onePutStorageDetail : onePutStorageDetailList) {
//物资编码
String materialCode = onePutStorageDetail.getMaterialCode();
@ -132,11 +133,11 @@ public class LdInventoryRecordServiceImpl extends ServiceImpl<LdInventoryRecordM
ldInventoryRecord.setTransactionType(BatchConstant.PUT_WAREHOUSE);
ldInventoryRecord.setQuantity(onePutStorageDetail.getInboundQuantity());
ldInventoryRecord.setMoney(onePutStorageDetail.getUnitPrice());
ldInventoryRecord.setOperatorId(ldOnePutStorage.getInOperatorId());
ldInventoryRecord.setOperatorName(ldOnePutStorage.getInOperatorName());
ldInventoryRecord.setOperationTime(LocalDateTime.now());
ldInventoryRecord.setDepartmentId("部门ID");
ldInventoryRecord.setDepartmentName("部门名称");
ldInventoryRecord.setOperatorId(approveDto.getOperatorId());
ldInventoryRecord.setOperatorName(approveDto.getOperatorName());
ldInventoryRecord.setOperationTime(approveDto.getOperationTime());
ldInventoryRecord.setDepartmentId(approveDto.getDepartmentId());
ldInventoryRecord.setDepartmentName(approveDto.getDepartmentName());
inventoryRecordMapper.insert(ldInventoryRecord);
}
return true;

@ -5,16 +5,13 @@ import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.nov.KgLowDurable.constant.BatchConstant;
import com.nov.KgLowDurable.enums.DepartmentEnum;
import com.nov.KgLowDurable.enums.MaterialCategoryEnum;
import com.nov.KgLowDurable.exception.CustomerException;
import com.nov.KgLowDurable.mapper.LdOneOutStorageMapper;
import com.nov.KgLowDurable.mapper.LdOnePutStorageMapper;
import com.nov.KgLowDurable.pojo.dto.LdOneOutStorageDto;
import com.nov.KgLowDurable.pojo.dto.LdOnePutStorageDto;
import com.nov.KgLowDurable.pojo.entity.*;
import com.nov.KgLowDurable.pojo.vo.*;
import com.nov.KgLowDurable.service.*;
import com.nov.KgLowDurable.util.OrderNoGen;
import com.nov.KgLowDurable.util.SerialNumberUtil;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -24,8 +21,6 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@ -41,7 +36,7 @@ public class LdOneOutStorageServiceImpl extends ServiceImpl<LdOneOutStorageMappe
LdOneOutStorageMapper oneOutStorageMapper;
@Autowired
OrderNoGen orderNoGen;
SerialNumberUtil serialNumberUtil;
@Autowired
ILdOneOutStorageDetailService oneOutStorageDetailService;
@ -104,7 +99,7 @@ public class LdOneOutStorageServiceImpl extends ServiceImpl<LdOneOutStorageMappe
if(BatchConstant.SUBMIT.equals(dto.getStatus())){
LdTwoPutStorage ldTwoPutStorage = new LdTwoPutStorage()
.setDemandEndInfo(dto.getDemandEndInfo())
.setOrderNo("22222222")
.setOrderNo(serialNumberUtil.generateSimple("SI"))
.setInDate(new Date())
.setReason(dto.getReason())
.setMaterialType(dto.getMaterialType())
@ -157,7 +152,7 @@ public class LdOneOutStorageServiceImpl extends ServiceImpl<LdOneOutStorageMappe
.setTwoPutStorageId(ldTwoPutStorage.getId())
.setTwoPutStorageNo(ldTwoPutStorage.getOrderNo())
.setLdDemandEndId(ldOneOutStorageDetail.getLdDemandEndId())
.setInboundDetailCode("二级入库单号")
.setInboundDetailCode(serialNumberUtil.generateSimple("SID"))
.setMaterialId(ldOneOutStorageDetail.getMaterialId())
.setMaterialName(ldOneOutStorageDetail.getMaterialName())
.setMaterialCode(materialCode)
@ -215,7 +210,7 @@ public class LdOneOutStorageServiceImpl extends ServiceImpl<LdOneOutStorageMappe
UserInfoVO userInfo = dto.getUserInfoVO();
// 生成出库单号
String orderNo = orderNoGen.generate("FO");
String orderNo = serialNumberUtil.generateSimple("FO");
dto.setOrderNo(orderNo);
// 设置出库操作人
@ -294,7 +289,7 @@ public class LdOneOutStorageServiceImpl extends ServiceImpl<LdOneOutStorageMappe
detailEntity.setOutboundQuantity(detailVO.getTheOutboundQuantity());
detailEntity.setOutStorageTime(outStorageTime);
String outboundDetailCode = orderNoGen.generate("FID");
String outboundDetailCode = serialNumberUtil.generateSimple("FOD");
detailEntity.setOneOutDetailCode(outboundDetailCode);
detailEntity.setShipperStatus(
BatchConstant.TEMPORARY.equals(status) ?
@ -420,7 +415,7 @@ public class LdOneOutStorageServiceImpl extends ServiceImpl<LdOneOutStorageMappe
detailEntity.setOutStorageTime(new Date());
// 生成明细编号
String outboundDetailCode = orderNoGen.generate("FID");
String outboundDetailCode = serialNumberUtil.generateSimple("FID");
detailEntity.setOneOutDetailCode(outboundDetailCode);
return detailEntity;

@ -1,20 +1,17 @@
package com.nov.KgLowDurable.service.Impl;
import com.baomidou.mybatisplus.core.injector.methods.DeleteById;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.nov.KgLowDurable.constant.BatchConstant;
import com.nov.KgLowDurable.exception.CustomerException;
import com.nov.KgLowDurable.mapper.LdOneFormMapper;
import com.nov.KgLowDurable.mapper.LdOnePutStorageMapper;
import com.nov.KgLowDurable.pojo.dto.LdOnePutStorageDto;
import com.nov.KgLowDurable.pojo.entity.*;
import com.nov.KgLowDurable.pojo.vo.ApproverUser;
import com.nov.KgLowDurable.pojo.vo.LdOnePutStorageDetailVO;
import com.nov.KgLowDurable.pojo.vo.LdOnePutStorageInfoVO;
import com.nov.KgLowDurable.pojo.vo.UserInfoVO;
import com.nov.KgLowDurable.service.*;
import com.nov.KgLowDurable.util.OrderNoGen;
import com.nov.KgLowDurable.util.SerialNumberUtil;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -44,7 +41,7 @@ public class LdOnePutStorageServiceImpl extends ServiceImpl<LdOnePutStorageMappe
ILdOnePutStorageDetailService onePutStorageDetailService;
@Autowired
OrderNoGen orderNoGen;
SerialNumberUtil serialNumberUtil;
@Autowired
IUserService userService;
@ -183,7 +180,7 @@ public class LdOnePutStorageServiceImpl extends ServiceImpl<LdOnePutStorageMappe
UserInfoVO userInfo = dto.getUserInfoVO();
// 生成入库单号
String orderNo = orderNoGen.generate("FI");
String orderNo = serialNumberUtil.generateSimple("SI");
dto.setOrderNo(orderNo);
// 设置入库操作人
@ -300,7 +297,7 @@ public class LdOnePutStorageServiceImpl extends ServiceImpl<LdOnePutStorageMappe
}
// 生成明细编号
String inboundDetailCode = orderNoGen.generate("FID");
String inboundDetailCode = serialNumberUtil.generateSimple("FID");
detailEntity.setInboundDetailCode(inboundDetailCode);
//生成物资编码
@ -456,7 +453,7 @@ public class LdOnePutStorageServiceImpl extends ServiceImpl<LdOnePutStorageMappe
}
// 7. 生成明细编号
String inboundDetailCode = orderNoGen.generate("FID");
String inboundDetailCode = serialNumberUtil.generateSimple("FID");
detailEntity.setInboundDetailCode(inboundDetailCode);
// 8. 保存明细

@ -6,6 +6,7 @@ import com.nov.KgLowDurable.constant.BatchConstant;
import com.nov.KgLowDurable.exception.CustomerException;
import com.nov.KgLowDurable.mapper.LdInventoryRecordMapper;
import com.nov.KgLowDurable.mapper.LdTwoInventoryRecordMapper;
import com.nov.KgLowDurable.pojo.dto.ApproveDto;
import com.nov.KgLowDurable.pojo.entity.*;
import com.nov.KgLowDurable.service.*;
import com.nov.KgLowDurable.util.StringUtils;
@ -30,6 +31,18 @@ public class LdTwoInventoryRecordServiceImpl extends ServiceImpl<LdTwoInventoryR
@Autowired
LdTwoInventoryRecordMapper twoInventoryRecordMapper;
@Autowired
ILdTwoOutStorageService twoOutStorageService;
@Autowired
ILdTwoOutStorageDetailService twoOutStorageDetailService;
@Autowired
ILdConsumerFormService consumerFormService;
@Autowired
ILdDurableFormService durableFormService;
@Override
public PageInfo<LdTwoInventoryRecord> getTwoInventoryRecordList(Long consumerFormId, String transactionType, Integer pageNum, Integer pageSize) {
@ -42,7 +55,57 @@ public class LdTwoInventoryRecordServiceImpl extends ServiceImpl<LdTwoInventoryR
}
@Override
public boolean approve(Long twoOutStorageId, boolean approveResult) {
return false;
public boolean approve(ApproveDto approveDto) {
LdTwoOutStorage twoOutStorage = twoOutStorageService.getById(approveDto.getTwoOutStorageId());
if(null == twoOutStorage){
throw new CustomerException("未查到一级入库单");
}
//更新审批状态
if(!approveDto.getApproveResult()){
twoOutStorage.setStatus(BatchConstant.APPROVAL_FAILED);
twoOutStorageService.updateById(twoOutStorage);
return true;
}else {
twoOutStorage.setStatus(BatchConstant.APPROVE);
twoOutStorageService.updateById(twoOutStorage);
}
//库存入库
List<LdTwoOutStorageDetail> ldTwoOutStorageDetailList = twoOutStorageDetailService.selectByTwoOutStorageId(approveDto.getTwoOutStorageId());
for (LdTwoOutStorageDetail twoOutStorageDetail : ldTwoOutStorageDetailList) {
if(BatchConstant.CONSUMER.equals(twoOutStorageDetail.getType())){
LdConsumerForm consumerForm = consumerFormService.getByMaterialId(twoOutStorageDetail.getMaterialId());
//计算差值
BigDecimal difference = consumerForm.getNum().subtract(twoOutStorageDetail.getNum());
consumerForm.setNum(difference);
boolean updateConfumerForm = consumerFormService.updateById(consumerForm);
if(!updateConfumerForm){
throw new CustomerException("易耗品更新失败");
}
//出库记录
LdTwoInventoryRecord ldTwoInventoryRecord = new LdTwoInventoryRecord()
.setConsumerFormId(consumerForm.getId().longValue())
.setTransactionType(BatchConstant.OUT_WAREHOUSE)
.setQuantity(twoOutStorageDetail.getNum())
.setOperatorId(approveDto.getOperatorId())
.setOperatorName(approveDto.getOperatorName())
.setOperationTime(approveDto.getOperationTime())
.setDepartmentId(approveDto.getDepartmentId())
.setDepartmentName(approveDto.getDepartmentName());
int insertTwoInventoryRecord = twoInventoryRecordMapper.insert(ldTwoInventoryRecord);
if(insertTwoInventoryRecord <= 0){
throw new CustomerException("出库记录保存失败");
}
}else {
LdDurableForm durableForm = durableFormService.getByMaterialId(twoOutStorageDetail.getMaterialId());
durableForm.setIsBorrow(BatchConstant.LEND);
durableForm.setNum(new BigDecimal(0));
boolean updateDurableForm = durableFormService.updateById(durableForm);
if(!updateDurableForm){
throw new CustomerException("耐用品更新失败");
}
}
}
return true;
}
}

@ -15,7 +15,7 @@ import com.nov.KgLowDurable.service.ILdApproveService;
import com.nov.KgLowDurable.service.ILdTwoOutStorageDetailService;
import com.nov.KgLowDurable.service.ILdTwoOutStorageService;
import com.nov.KgLowDurable.service.IUserService;
import com.nov.KgLowDurable.util.OrderNoGen;
import com.nov.KgLowDurable.util.SerialNumberUtil;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -37,7 +37,7 @@ public class LdTwoOutStorageServiceImpl extends ServiceImpl<LdTwoOutStorageMappe
LdTwoOutStorageMapper twoOutStorageMapper;
@Autowired
OrderNoGen orderNoGen;
SerialNumberUtil serialNumberUtil;
@Autowired
ILdTwoOutStorageDetailService twoOutStorageDetailService;
@ -72,7 +72,7 @@ public class LdTwoOutStorageServiceImpl extends ServiceImpl<LdTwoOutStorageMappe
LdTwoOutStorage ldTwoOutStorage = dto.getLdTwoOutStorage();
// 生成出库单号
String orderNo = orderNoGen.generate("SO");
String orderNo = serialNumberUtil.generateSimple("SO");
ldTwoOutStorage.setOrderNo(orderNo);
// 设置出库操作人
@ -99,7 +99,7 @@ public class LdTwoOutStorageServiceImpl extends ServiceImpl<LdTwoOutStorageMappe
for (LdTwoOutStorageDetail ldTwoOutStorageDetail : ldTwoOutStorageDetailList) {
ldTwoOutStorageDetail.setTwoOutStorageId(outOneStorageId);
ldTwoOutStorageDetail.setTwoOutStorageNo(ldTwoOutStorage.getOrderNo());
ldTwoOutStorageDetail.setTwoOutDetailCode(orderNoGen.generate("SOD"));
ldTwoOutStorageDetail.setTwoOutDetailCode(serialNumberUtil.generateSimple("SOD"));
ldTwoOutStorageDetail.setShipperStatus(BatchConstant.NO_SHIPPED_OUT);
ldTwoOutStorageDetail.setOptTime(new Date());
list.add(ldTwoOutStorageDetail);

@ -4,28 +4,22 @@ import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.nov.KgLowDurable.constant.BatchConstant;
import com.nov.KgLowDurable.exception.CustomerException;
import com.nov.KgLowDurable.mapper.LdOnePutStorageMapper;
import com.nov.KgLowDurable.mapper.LdTwoPutStorageMapper;
import com.nov.KgLowDurable.pojo.dto.LdOnePutStorageDto;
import com.nov.KgLowDurable.pojo.dto.LdTwoPutStorageDto;
import com.nov.KgLowDurable.pojo.entity.*;
import com.nov.KgLowDurable.pojo.vo.*;
import com.nov.KgLowDurable.service.*;
import com.nov.KgLowDurable.util.OrderNoGen;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 服务实现类

@ -34,32 +34,33 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
@Override
public UserInfoVO getUserInfo() throws Exception {
Map<String, String> headers = new HashMap<String, String>() {{
put("Content-Type", "application/json");
}};
Map<String, String> querys = new HashMap<String, String>() {{
put("taskId", "1");
}};
String resp = EntityUtils.toString(
HttpUtils.doGet(
"http://192.168.198.1:8105",
"/dsTasking/processAuthorized",
"GET",
headers,
querys
).getEntity()
);
JSONObject obj = JSON.parseObject(resp);
if (!"200".equals(obj.getString("code"))) {
throw new RuntimeException("失败: " + obj.getString("message"));
}
// Map<String, String> headers = new HashMap<String, String>() {{
// put("Content-Type", "application/json");
// }};
//
// Map<String, String> querys = new HashMap<String, String>() {{
// put("taskId", "1");
// }};
//
// String resp = EntityUtils.toString(
// HttpUtils.doGet(
// "http://192.168.198.1:8105",
// "/dsTasking/processAuthorized",
// "GET",
// headers,
// querys
// ).getEntity()
// );
//
// JSONObject obj = JSON.parseObject(resp);
//
// if (!"200".equals(obj.getString("code"))) {
// throw new RuntimeException("失败: " + obj.getString("message"));
// }
UserInfoVO data = JSON.toJavaObject(obj.getJSONObject("data"), UserInfoVO.class);
// UserInfoVO data = JSON.toJavaObject(obj.getJSONObject("data"), UserInfoVO.class);
UserInfoVO data = new UserInfoVO();
data.setUserId("AnXinghe");
data.setName("安星河");
data.setDepartment("中国民用航空青岛空中交通管理站/实业公司/IT事业部");

@ -0,0 +1,224 @@
package com.nov.KgLowDurable.util;
import com.nov.KgLowDurable.config.FileUploadConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
* @author: liweidong
* @create: 2025-12-31
*/
@Component
public class ImageUploadUtil {
private static final Logger logger = LoggerFactory.getLogger(ImageUploadUtil.class);
@Autowired
private FileUploadConfig config;
/**
* 上传图片文件
* @param file 上传的文件
* @param module 业务模块名称
* @return 上传后的文件访问URL
*/
public String uploadImage(MultipartFile file, String module) throws IOException {
// 1. 参数校验
validateFile(file);
// 2. 验证文件类型
validateFileType(file);
// 3. 生成存储路径
String storagePath = generateStoragePath(module);
// 4. 生成文件名
String fileName = generateFileName(file.getOriginalFilename());
// 5. 创建目录(如果不存在)
createDirectoryIfNotExists(storagePath);
// 6. 保存文件
String fullPath = saveFile(file, storagePath, fileName);
// 7. 生成访问URL
return generateAccessUrl(module, fileName);
}
/**
* 批量上传图片
* @param files 文件数组
* @param module 业务模块
* @return 上传成功的文件URL数组
*/
public String[] uploadImages(MultipartFile[] files, String module) throws IOException {
if (files == null || files.length == 0) {
throw new IllegalArgumentException("上传文件不能为空");
}
String[] urls = new String[files.length];
for (int i = 0; i < files.length; i++) {
urls[i] = uploadImage(files[i], module);
}
return urls;
}
/**
* 删除文件
* @param fileUrl 文件URL
* @return 是否删除成功
*/
public boolean deleteFile(String fileUrl) {
try {
// 从URL中提取文件路径
String relativePath = extractRelativePathFromUrl(fileUrl);
String fullPath = config.getBasePath() + relativePath;
File file = new File(fullPath);
if (file.exists()) {
return file.delete();
}
return false;
} catch (Exception e) {
logger.error("删除文件失败: {}", fileUrl, e);
return false;
}
}
/**
* 获取文件的本地存储路径
* @param fileUrl 文件URL
* @return 本地文件路径
*/
public String getLocalFilePath(String fileUrl) {
String relativePath = extractRelativePathFromUrl(fileUrl);
return config.getBasePath() + relativePath;
}
private void validateFile(MultipartFile file) {
if (file == null || file.isEmpty()) {
throw new IllegalArgumentException("上传文件不能为空");
}
if (file.getSize() > config.getMaxFileSize()) {
throw new IllegalArgumentException("文件大小不能超过 " + (config.getMaxFileSize() / 1024 / 1024) + "MB");
}
}
private void validateFileType(MultipartFile file) throws IOException {
String originalFilename = file.getOriginalFilename();
if (originalFilename == null) {
throw new IllegalArgumentException("文件名不能为空");
}
String fileExtension = getFileExtension(originalFilename).toLowerCase();
boolean allowed = false;
for (String allowedType : config.getAllowedTypes()) {
if (allowedType.trim().toLowerCase().equals(fileExtension)) {
allowed = true;
break;
}
}
if (!allowed) {
throw new IllegalArgumentException("不支持的文件类型,仅支持: " +
String.join(", ", config.getAllowedTypes()));
}
// 通过MIME类型再次验证
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("image/")) {
throw new IllegalArgumentException("请上传图片文件");
}
}
private String generateStoragePath(String module) {
// 按天创建文件夹:yyyy-MM-dd
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String dateFolder = dateFormat.format(new Date());
// 生成完整路径:/opt/SmartParkFilesTest/模块名/yyyy-MM-dd/
return config.getBasePath() + File.separator +
module + File.separator +
dateFolder + File.separator;
}
private String generateFileName(String originalFilename) {
// 生成UUID作为文件名,避免重复
String uuid = UUID.randomUUID().toString().replace("-", "");
// 保留原文件扩展名
String extension = getFileExtension(originalFilename);
return uuid + "." + extension;
}
private void createDirectoryIfNotExists(String path) {
File directory = new File(path);
if (!directory.exists()) {
boolean created = directory.mkdirs();
if (!created) {
throw new RuntimeException("创建目录失败: " + path);
}
logger.info("创建目录: {}", path);
}
}
private String saveFile(MultipartFile file, String storagePath, String fileName) throws IOException {
String fullPath = storagePath + fileName;
Path path = Paths.get(fullPath);
// 保存文件
Files.copy(file.getInputStream(), path);
logger.info("文件保存成功: {}", fullPath);
return fullPath;
}
private String generateAccessUrl(String module, String fileName) {
// 按天创建文件夹:yyyy-MM-dd
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String dateFolder = dateFormat.format(new Date());
// 生成访问URL:http://10.90.100.231:8132/opt/SmartParkFilesTest/模块名/yyyy-MM-dd/文件名
return String.format("%s/%s/%s/%s",
config.getServerUrl().endsWith("/") ?
config.getServerUrl().substring(0, config.getServerUrl().length() - 1) :
config.getServerUrl(),
module,
dateFolder,
fileName);
}
private String getFileExtension(String filename) {
int lastDotIndex = filename.lastIndexOf(".");
if (lastDotIndex == -1) {
throw new IllegalArgumentException("文件缺少扩展名");
}
return filename.substring(lastDotIndex + 1);
}
private String extractRelativePathFromUrl(String fileUrl) {
// 从URL中提取相对路径
// 例如: http://10.90.100.231:8132/opt/SmartParkFilesTest/user/2025-12-31/uuid.jpg
// 提取为: /user/2023-10-25/uuid.jpg
String baseUrl = config.getServerUrl() + config.getBasePath();
if (!fileUrl.startsWith(baseUrl)) {
throw new IllegalArgumentException("无效的文件URL");
}
return fileUrl.substring(baseUrl.length());
}
}

@ -1,159 +0,0 @@
package com.nov.KgLowDurable.util;
import com.nov.KgLowDurable.mapper.LdOnePutStorageDetailMapper;
import com.nov.KgLowDurable.mapper.LdOnePutStorageMapper;
import com.nov.KgLowDurable.service.ILdOneFormService;
import com.nov.KgLowDurable.service.ILdOnePutStorageService;
import com.nov.KgLowDurable.service.Impl.LdOnePutStorageServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 单号生成方法
* 传入前缀FI, FO, SI, SO, FID
* 返回前缀 + yyyyMMdd + 3位序号
* @author liweidong
*/
@Component
public class OrderNoGen {
@Autowired
private LdOnePutStorageMapper onePutStorageMapper;
@Autowired
private LdOnePutStorageDetailMapper onePutStorageDetailMapper;
// 添加静态变量保存状态
private static String lastPrefix = null;
private static String lastDate = null;
private static int currentSeq = 0;
private static boolean initialized = false;
// 私有构造,防止直接new
private OrderNoGen() {}
/**
* 生成单号
*/
public synchronized String generate(String prefix) {
validatePrefix(prefix);
// 获取当天日期
String today = new SimpleDateFormat("yyyyMMdd").format(new Date());
// 如果是新的一天或新类型,需要重新初始化
if (!today.equals(lastDate) || !prefix.equals(lastPrefix)) {
// 重置状态
lastPrefix = prefix;
lastDate = today;
currentSeq = getTodayMaxSeqFromDB(prefix, today);
initialized = true;
} else if (!initialized) {
// 第一次调用,需要初始化
currentSeq = getTodayMaxSeqFromDB(prefix, today);
initialized = true;
}
// 递增序号
currentSeq++;
if (currentSeq > 999) {
throw new RuntimeException("今天" + prefix + "类型的单号已超过999个");
}
return String.format("%s%s%03d", prefix, today, currentSeq);
}
/**
* 重置状态可选比如在每天开始时调用
*/
public synchronized void reset() {
lastPrefix = null;
lastDate = null;
currentSeq = 0;
initialized = false;
}
/**
* 批量生成
*/
public synchronized List<String> generateBatch(String prefix, int count) {
validatePrefix(prefix);
String today = new SimpleDateFormat("yyyyMMdd").format(new Date());
// 获取当前最大序号
int maxSeq;
if (today.equals(lastDate) && prefix.equals(lastPrefix) && initialized) {
maxSeq = currentSeq;
} else {
maxSeq = getTodayMaxSeqFromDB(prefix, today);
lastPrefix = prefix;
lastDate = today;
currentSeq = maxSeq;
initialized = true;
}
// 检查是否超限
if (maxSeq + count > 999) {
throw new RuntimeException("今天" + prefix + "类型的单号已超过999个");
}
// 生成批量单号
List<String> orderNos = new ArrayList<>();
for (int i = 1; i <= count; i++) {
int seq = maxSeq + i;
orderNos.add(String.format("%s%s%03d", prefix, today, seq));
}
// 更新当前序号
currentSeq = maxSeq + count;
return orderNos;
}
/**
* 从数据库查询当天最大序号
*/
private int getTodayMaxSeqFromDB(String prefix, String date) {
try {
// 查询数据库中该类型和日期的最大单号
String maxOrderNo = null;
if("FI".equals(prefix)){
maxOrderNo = onePutStorageMapper.selectMaxOrderNo(prefix, date);
}
if("FID".equals(prefix)){
maxOrderNo = onePutStorageDetailMapper.selectMaxOrderNo(prefix, date);
}
if (maxOrderNo != null && maxOrderNo.length() == 13) {
String seqStr = maxOrderNo.substring(10);
return Integer.parseInt(seqStr);
}
return 0;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 校验前缀是否合法
*/
private void validatePrefix(String prefix) {
if (prefix == null) {
throw new IllegalArgumentException("前缀不能为null");
}
// 可随时增加
if (!prefix.matches("^(FI|FID|FO|SI|SO|SOD)$")) {
throw new IllegalArgumentException(
"前缀必须为以下值之一:FI(一级库入库),FID(一级入库明细), FO(一级库出库), SI(二级库入库), SO(二级库出库)"
);
}
}
}

@ -0,0 +1,313 @@
package com.nov.KgLowDurable.util;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 精简版单号生成工具类
* 功能前缀 + yyyyMMdd + 3位序号001-999
* 存储于Redis每天从001重新开始
*/
@Component
public class SerialNumberUtil {
@Autowired
private StringRedisTemplate redisTemplate;
// Redis键前缀
private static final String REDIS_KEY_PREFIX = "SN";
// 日期格式
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
// 最大序列号
private static final int MAX_SEQUENCE = 999;
// 默认前缀集合
private static final String[] DEFAULT_PREFIXES = {"FI","FID","FO","FOD","SI","SID", "SO", "SOD"};
/**
* 生成单个单号
* @param prefix 前缀FI, FO, SI, SO, FID
* @return 完整单号FI20251231001
*/
public String generateSimple(String prefix) {
// 验证前缀
validatePrefix(prefix);
// 获取当前日期
String dateStr = getCurrentDate();
// 构建Redis键
String redisKey = buildRedisKey(prefix, dateStr);
try {
// 使用Redis的原子递增操作
Long sequence = redisTemplate.opsForValue().increment(redisKey);
// 如果是第一次设置,设置过期时间(到明天凌晨)
if (sequence == 1) {
setExpireAtMidnight(redisKey);
}
// 检查是否超过最大值
if (sequence > MAX_SEQUENCE) {
// 递减回去
redisTemplate.opsForValue().decrement(redisKey);
throw new RuntimeException(String.format(
"序列号已用完,前缀:%s,日期:%s,最大:%d",
prefix, dateStr, MAX_SEQUENCE
));
}
// 生成并返回单号
String sequenceStr = String.format("%03d", sequence);
return prefix + dateStr + sequenceStr;
} catch (Exception e) {
throw new RuntimeException("生成序列号失败:" + e.getMessage(), e);
}
}
/**
* 批量生成单号
* @param prefix 前缀
* @param count 生成数量1-100
* @return 单号列表
*/
public List<String> generateBatchSimple(String prefix, int count) {
// 验证参数
validatePrefix(prefix);
if (count <= 0 || count > 100) {
throw new IllegalArgumentException("生成数量必须在1-100之间");
}
// 获取当前日期
String dateStr = getCurrentDate();
// 构建Redis键
String redisKey = buildRedisKey(prefix, dateStr);
try {
// 批量递增
Long sequence = redisTemplate.opsForValue().increment(redisKey, count);
// 如果是第一次设置,设置过期时间
if (sequence == count) {
setExpireAtMidnight(redisKey);
}
// 检查是否超过最大值
if (sequence > MAX_SEQUENCE) {
// 计算超出的数量并递减回去
long overCount = sequence - MAX_SEQUENCE;
redisTemplate.opsForValue().decrement(redisKey, overCount);
throw new RuntimeException(String.format(
"序列号已用完,前缀:%s,日期:%s,需要:%d,最大:%d",
prefix, dateStr, count, MAX_SEQUENCE
));
}
// 生成序列号列表
return generateNumbers(prefix, dateStr, sequence - count + 1, count);
} catch (Exception e) {
throw new RuntimeException("批量生成序列号失败:" + e.getMessage(), e);
}
}
/**
* 获取当前序列号不递增
* @param prefix 前缀
* @return 当前序列号0-999
*/
public int getCurrentSequence(String prefix) {
validatePrefix(prefix);
String dateStr = getCurrentDate();
String redisKey = buildRedisKey(prefix, dateStr);
String value = redisTemplate.opsForValue().get(redisKey);
return value == null ? 0 : Integer.parseInt(value);
}
/**
* 注册新前缀支持扩展
* @param prefix 新前缀
*/
public void registerPrefix(String prefix) {
validatePrefix(prefix);
String dateStr = getCurrentDate();
String redisKey = buildRedisKey(prefix, dateStr);
// 如果key不存在,初始化
Boolean exists = redisTemplate.hasKey(redisKey);
if (exists == null || !exists) {
redisTemplate.opsForValue().setIfAbsent(redisKey, "0", getExpireSeconds(), TimeUnit.SECONDS);
}
}
/**
* 预测下一个单号不实际生成
* @param prefix 前缀
* @param count 预测数量
* @return 预测的单号列表
*/
public List<String> predictNext(String prefix, int count) {
int current = getCurrentSequence(prefix);
if (current + count > MAX_SEQUENCE) {
throw new RuntimeException(String.format(
"预测数量将超过最大值,当前:%d,需要:%d,最大:%d",
current, count, MAX_SEQUENCE
));
}
String dateStr = getCurrentDate();
return generateNumbers(prefix, dateStr, current + 1, count);
}
/**
* 重置序列号主要用于测试
* @param prefix 前缀
* @param date 日期yyyyMMdd格式
* @param sequence 序列号0-999
*/
public void resetSequence(String prefix, String date, int sequence) {
validatePrefix(prefix);
if (sequence < 0 || sequence > MAX_SEQUENCE) {
throw new IllegalArgumentException("序列号必须在0-" + MAX_SEQUENCE + "之间");
}
String redisKey = buildRedisKey(prefix, date);
redisTemplate.opsForValue().set(
redisKey,
String.valueOf(sequence),
getExpireSeconds(date),
TimeUnit.SECONDS
);
}
// ============ 私有辅助方法 ============
/**
* 验证前缀格式
*/
private void validatePrefix(String prefix) {
if (prefix == null || prefix.trim().isEmpty()) {
throw new IllegalArgumentException("前缀不能为空");
}
// 前缀格式:字母数字,长度1-10
if (!prefix.matches("[A-Za-z0-9]{1,10}")) {
throw new IllegalArgumentException("前缀必须是1-10位字母数字");
}
}
/**
* 获取当前日期字符串
*/
private String getCurrentDate() {
return LocalDate.now().format(DATE_FORMATTER);
}
/**
* 构建Redis键
*/
private String buildRedisKey(String prefix, String date) {
return String.format("%s:%s:%s", REDIS_KEY_PREFIX, prefix, date);
}
/**
* 设置key在明天凌晨过期
*/
private void setExpireAtMidnight(String redisKey) {
long expireSeconds = getExpireSeconds();
if (expireSeconds > 0) {
redisTemplate.expire(redisKey, expireSeconds, TimeUnit.SECONDS);
}
}
/**
* 获取过期时间
*/
private long getExpireSeconds() {
return getExpireSeconds(getCurrentDate());
}
/**
* 获取指定日期的过期时间
*/
private long getExpireSeconds(String dateStr) {
try {
LocalDate date = LocalDate.parse(dateStr, DATE_FORMATTER);
LocalDate tomorrow = date.plusDays(1);
long expireAt = tomorrow.atStartOfDay()
.atZone(ZoneId.systemDefault())
.toEpochSecond();
long now = System.currentTimeMillis() / 1000;
// 返回距离明天凌晨的秒数,至少60秒
return Math.max(expireAt - now, 60);
} catch (Exception e) {
// 解析失败,默认24小时
return 24 * 60 * 60;
}
}
/**
* 生成单号列表
*/
private List<String> generateNumbers(String prefix, String date, long startSeq, int count) {
List<String> result = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
String sequenceStr = String.format("%03d", startSeq + i);
result.add(prefix + date + sequenceStr);
}
return result;
}
/**
* 初始化默认前缀可在应用启动时调用
*/
public void initDefaultPrefixes() {
for (String prefix : DEFAULT_PREFIXES) {
try {
registerPrefix(prefix);
} catch (Exception e) {
// 初始化失败,记录日志但不中断
System.err.println("初始化前缀失败:" + prefix + ", " + e.getMessage());
}
}
}
}

@ -68,10 +68,10 @@ spring.servlet.multipart.file-size-threshold=0
spring.mvc.async.request-timeout=100000
spring.servlet.multipart.max-file-size =500MB
spring.servlet.multipart.max-request-size =500MB
#spring.redis.host= localhost
#spring.redis.port= 6379
#spring.redis.password=
#spring.redis.database= 0
#spring.redis.timeout= 10s
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
spring.redis.timeout=3000ms
spring.redis.connect-timeout=2000ms

@ -21,6 +21,7 @@
<result column="remark" property="remark"/>
<result column="is_borrow" property="isBorrow"/>
<result column="return_time" property="returnTime"/>
<result column="return_reason" property="returnReason"/>
</resultMap>
<select id="selectDurableFormList" resultType="com.nov.KgLowDurable.pojo.entity.LdDurableForm">
select * from ld_durable_form where 1=1
@ -31,6 +32,9 @@
AND material_name LIKE CONCAT('%', #{materialName}, '%')
</if>
</select>
<select id="selectByMaterialId" resultType="com.nov.KgLowDurable.pojo.entity.LdDurableForm">
select * from ld_durable_form where material_id = #{materialId}
</select>
</mapper>

Loading…
Cancel
Save