<bean:write>不能显示Integer,BigDecimal等类型的属性问题的解决

本文介绍了使用Struts标签库中的bean:write标签显示Integer和BigDecimal类型数据时遇到的问题及解决方案。通过设置format属性,可以有效避免JspException错误,并且能够自定义数值和日期的显示格式。

在用struts标签 bean:write name=".." property=".."/ 显示Integer ,BigDecimal类型的属性时,会报

javax.servlet.jsp.JspException: Cannot find message resources under key org.apache.struts.action.MESSAGE
        at org.apache.struts.taglib.TagUtils.retrieveMessageResources(TagUtils.java:1252)
        at org.apache.struts.taglib.TagUtils.message(TagUtils.java:1101)
        at org.apache.struts.taglib.TagUtils.message(TagUtils.java:1076)
        at org.apache.struts.taglib.bean.WriteTag.retrieveFormatString(WriteTag.java:254)
        at org.apache.struts.taglib.bean.WriteTag.formatValue(WriteTag.java:317)
        at org.apache.struts.taglib.bean.WriteTag.doStartTag(WriteTag.java:232)
        at org.apache.jsp.client$jsp._jspService(client$jsp.java:379)
        at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:107)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
        at org.apache.jasper.servlet.JspServlet$JspServletWrapper.service(JspServlet.java:201)
        at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:381)
        at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:473)

这样的错误

解决的办法就是利用 bean:write 的format属性,加上一个format="#" 属性

bean:write name="testForm" property="testBig" format="#" /BigDecimal型数据处理

bean:write name="testForm" property="testInt" format="#" /Intger型数据处理

 

关于 format还有不少很好的妙用

比如你要显示的日期格式为 年-月-日 时:分:秒,则可以定义为 format="yyyy-MM-dd HH:mm:ss"

比如你要定义显示到小数点后几位,则可以定义为 format="000.00"

package com.example.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import java.math.BigDecimal; import java.io.Serializable; import com.baomidou.mybatisplus.annotation.TableName; @Data @TableName("goods") public class Goods implements Serializable { @TableId(type = IdType.AUTO) private Long id; @TableField(value="name") private String name; private String storage; private String goodsType; // 改为驼峰命名,与数据库goods_type对应 private Long count; private String remark; private BigDecimal price; } package com.example.model; import lombok.Data; import java.math.BigDecimal; import java.util.Date; import java.util.List; import java.io.Serializable; @Data public class Order implements Serializable{ private Long orderId; private Long userId; private Date createTime; private BigDecimal totalAmount; private List<OrderItem> orderItems; // 关联订单项,创建订单时可一起处理 private String status; } package com.example.model; import lombok.Data; import java.math.BigDecimal; import java.io.Serializable; @Data public class OrderItem implements Serializable{ private Long itemId; private Long orderId; private Long goodsId; private String goodsName; private BigDecimal price; private Integer quantity; } package com.example.model; import lombok.Data; import java.io.Serializable; @Data public class User implements Serializable { private Long userId; // 对应表中的 id private String no; // 对应表中的 no private String name; // 对应表中的 name private String password; // 对应表中的 password private Integer age; // 对应表中的 age(int 类型Java 中对应 Integer) private String sex; // 对应表中的 sex private String phone; // 对应表中的 phone private Long roleId; // 对应表中的 role_id(Java 中一般用驼峰命名法) private Boolean isValid; // 对应表中的 is_valid(tinyint(1) 在 Java 中一般用 Boolean 表示) public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } } package com.example.order.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } package com.example.order.controller; import com.example.model.Goods; import com.example.model.Order; import com.example.model.OrderItem; import com.example.model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; @RestController @RequestMapping("/orders") public class OrderController { private static final Logger logger = LoggerFactory.getLogger(OrderController.class); private final Map<Long, Order> orderMap = new HashMap<>(); private final AtomicLong idGenerator = new AtomicLong(1); @Autowired private LoadBalancerClient loadBalancerClient; @Autowired private RestTemplate restTemplate; // @PostMapping("/create") // public Order createOrder(OrderRequest request) { // try { // // 调用用户服务获取用户信息 // User user = getUser(request.getUserId()); // if (user == null) { // logger.error("未找到用户信息,用户 ID: {}", request.getUserId()); // return null; // } // // List<OrderItem> orderItems = new ArrayList<>(); // BigDecimal totalAmount = BigDecimal.ZERO; // // for (OrderItemRequest itemRequest : request.getOrderItems()) { // Goods goods = getGoods(itemRequest.getGoodsId()); // if (goods != null) { // OrderItem orderItem = new OrderItem(); // orderItem.setGoodsId(goods.getId()); // orderItem.setGoodsName(goods.getName()); // orderItem.setPrice(goods.getPrice()); // orderItem.setQuantity(itemRequest.getQuantity()); // orderItems.add(orderItem); // totalAmount = totalAmount.add(goods.getPrice().multiply(BigDecimal.valueOf(itemRequest.getQuantity()))); // } else { // logger.warn("未找到商品信息,商品 ID: {}", itemRequest.getGoodsId()); // } // } // // // 创建订单 // Order order = new Order(); // order.setOrderId(idGenerator.getAndIncrement()); // order.setUserId(request.getUserId()); // order.setCreateTime(new Date()); // order.setTotalAmount(totalAmount); // order.setOrderItems(orderItems); // order.setStatus("CREATED"); // // orderMap.put(order.getOrderId(), order); // return order; // } catch (Exception e) { // logger.error("创建订单时发生异常", e); // return null; // } // } // // private User getUser(Long userId) { // try { // ServiceInstance instance = loadBalancerClient.choose("user-service"); // if (instance == null) { // logger.error("未找到 user - service 的实例"); // return null; // } // String url = String.format("http://%s:%d/users/%d", instance.getHost(), instance.getPort(), userId); // return restTemplate.getForObject(url, User.class); // } catch (Exception e) { // logger.error("调用用户服务获取用户信息时发生异常,用户 ID: {}", userId, e); // return null; // } // } // // private Goods getGoods(Long goodsId) { // try { // ServiceInstance instance = loadBalancerClient.choose("goods-service"); // String url = String.format("http://%s:%d/goods/%d", instance.getHost(), instance.getPort(), goodsId); // return restTemplate.getForObject(url, Goods.class); // } catch (Exception e) { // logger.error("调用商品服务获取商品信息时发生异常,商品 ID: {}", goodsId, e); // return null; // } // } @PostMapping(value = "/create", consumes = { "application/x-www-form-urlencoded", "application/json" }) public Order createOrder( @RequestParam("userId") Long userId, @RequestParam("goodsId") List<Long> goodsIds, @RequestParam("quantity") List<Integer> quantities) { try { // 检查参数有效性 if (userId == null || goodsIds == null || quantities == null || goodsIds.size() != quantities.size()) { logger.error("创建订单失败:参数不完整或无效"); return null; } // 调用用户服务获取用户信息 User user = getUser(userId); if (user == null) { logger.error("未找到用户信息,用户 ID: {}", userId); return null; } // 构建订单项 List<OrderItem> orderItems = new ArrayList<>(); BigDecimal totalAmount = BigDecimal.ZERO; for (int i = 0; i < goodsIds.size(); i++) { Long goodsId = goodsIds.get(i); Integer quantity = quantities.get(i); Goods goods = getGoods(goodsId); if (goods != null) { OrderItem orderItem = new OrderItem(); orderItem.setGoodsId(goods.getId()); orderItem.setGoodsName(goods.getName()); orderItem.setPrice(goods.getPrice()); orderItem.setQuantity(quantity); orderItems.add(orderItem); totalAmount = totalAmount.add(goods.getPrice().multiply(BigDecimal.valueOf(quantity))); } else { logger.warn("未找到商品信息,商品 ID: {}", goodsId); } } // 创建订单 Order order = new Order(); order.setOrderId(idGenerator.getAndIncrement()); order.setUserId(userId); order.setCreateTime(new Date()); order.setTotalAmount(totalAmount); order.setOrderItems(orderItems); order.setStatus("CREATED"); orderMap.put(order.getOrderId(), order); return order; } catch (Exception e) { logger.error("创建订单时发生异常", e); return null; } } private User getUser(Long userId) { try { ServiceInstance instance = loadBalancerClient.choose("service-user"); if (instance == null) { logger.error("service-user的实例"); return null; } String url = String.format("http://%s:%d/users/%d", instance.getHost(), instance.getPort(), userId); return restTemplate.getForObject(url, User.class); } catch (Exception e) { logger.error("调用用户服务获取用户信息时发生异常,用户 ID: {}", userId, e); return null; } } private Goods getGoods(Long goodsId) { try { ServiceInstance instance = loadBalancerClient.choose("service-goods"); String url = String.format("http://%s:%d/goods/%d", instance.getHost(), instance.getPort(), goodsId); return restTemplate.getForObject(url, Goods.class); } catch (Exception e) { logger.error("调用商品服务获取商品信息时发生异常,商品 ID: {}", goodsId, e); return null; } } @GetMapping("/{orderId}") public Order getOrder(@PathVariable Long orderId) { return orderMap.getOrDefault(orderId, null); } // 公开内部请求类(用于 x - www - form - urlencoded) public static class OrderRequest { private Long userId; private List<OrderItemRequest> orderItems; public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public List<OrderItemRequest> getOrderItems() { return orderItems; } public void setOrderItems(List<OrderItemRequest> orderItems) { this.orderItems = orderItems; } } public static class OrderItemRequest { private Long goodsId; private Integer quantity; public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public Integer getQuantity() { return quantity; } public void setQuantity(Integer quantity) { this.quantity = quantity; } } } //package com.example.order.mapper; // //import com.example.model.Order; //import com.example.model.OrderItem; //import org.apache.ibatis.annotations.Mapper; //import org.apache.ibatis.annotations.Insert; //import org.apache.ibatis.annotations.Options; // //@Mapper //public interface OrderMapper<OrderItem, Order> { // // // 插入订单主表数据,同时获取自增的 order_id // @Insert("INSERT INTO `order` (user_id, create_time, total_amount, status) " + // "VALUES (#{userId}, #{createTime}, #{totalAmount}, #{status})") // @Options(useGeneratedKeys = true, keyProperty = "orderId", keyColumn = "order_id") // void insertOrder(Order order); // // // 插入订单项数据 // @Insert("INSERT INTO order_item (order_id, goods_id, goods_name, price, quantity) " + // "VALUES (#{orderId}, #{goodsId}, #{goodsName}, #{price}, #{quantity})") // void insertOrderItem(OrderItem orderItem); //} package com.example.order.mapper; import com.example.model.Order; import com.example.model.OrderItem; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Options; @Mapper // 必须添加该注解,让 MyBatis 扫描并创建 Mapper Bean public interface OrderMapper { @Insert("INSERT INTO `order` (user_id, create_time, total_amount, status) " + "VALUES (#{userId}, #{createTime}, #{totalAmount}, #{status})") @Options(useGeneratedKeys = true, keyProperty = "orderId", keyColumn = "order_id") void insertOrder(Order order); @Insert("INSERT INTO order_item (order_id, goods_id, goods_name, price, quantity) " + "VALUES (#{orderId}, #{goodsId}, #{goodsName}, #{price}, #{quantity})") void insertOrderItem(OrderItem orderItem); } package com.example.order.service; import com.example.order.mapper.OrderMapper; import com.example.model.Order; import com.example.model.OrderItem; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Date; import java.util.List; @Service public class OrderService { private final OrderMapper orderMapper; public OrderService(OrderMapper orderMapper) { this.orderMapper = orderMapper; } @Transactional public Order createOrder(Order order) { // 设置订单创建时间 order.setCreateTime(new Date()); // 先插入订单主表,获取自增的 orderId orderMapper.insertOrder(order); Long orderId = order.getOrderId(); // 遍历订单项,设置 orderId 后插入 List<OrderItem> orderItems = order.getOrderItems(); if (orderItems != null && !orderItems.isEmpty()) { for (OrderItem item : orderItems) { item.setOrderId(orderId); orderMapper.insertOrderItem(item); } } return order; } } package com.example.order; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient @MapperScan("com.example.order.mapper") public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.mapper.OrderMapper"> <resultMap id="OrderResultMap" type="com.example.model.Order"> <id column="order_id" property="orderId"/> <result column="order_user_id" property="orderUserId"/> <result column="order_create_time" property="orderCreateTime"/> <result column="order_total_amount" property="orderTotalAmount"/> <result column="order_status" property="orderStatus"/> </resultMap> <select id="selectOrderById" parameterType="java.lang.Long" resultMap="OrderResultMap"> SELECT order_id, user_id, create_time, total_amount, status FROM `order` WHERE order_id = #{orderId} </select> <insert id="insertOrder" parameterType="com.example.model.Order" useGeneratedKeys="true" keyProperty="orderId"> INSERT INTO `order` (user_id, create_time, total_amount, status) VALUES (#{orderUserId}, #{orderCreateTime}, #{orderTotalAmount}, #{orderStatus}) </insert> <update id="updateOrder" parameterType="com.example.model.Order"> UPDATE `order` SET user_id = #{orderUserId}, create_time = #{orderCreateTime}, total_amount = #{orderTotalAmount}, status = #{orderStatus} WHERE order_id = #{orderId} </update> <delete id="deleteOrder" parameterType="java.lang.Long"> DELETE FROM `order` WHERE order_id = #{orderId} </delete> </mapper> server: port: 8000 spring: main: allow-bean-definition-overriding: true datasource: url: jdbc:mysql://localhost:3306/hdms?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 application: name: service-order cloud: nacos: discovery: server-addr: 127.0.0.1:8848 jackson: # 添加Jackson配置确保BigDecimal序列化正常 default-property-inclusion: non_null serialization: write-dates-as-timestamps: false deserialization: fail-on-unknown-properties: false mybatis-plus: configuration: map-underscore-to-camel-case: true # 开启驼峰命名 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 启用SQL日志 mapper-locations: classpath*:/mapper/**/*.xml type-aliases-package: com.example.model mapper-scan: com.example.order.mapper package com.example.user.controller; import com.example.model.User; import com.example.user.service.UserService; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/users") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping("/{id}") public User getUserById(@PathVariable Long id) { return userService.getById(id); // 调用 UserService 的查询方法 } @PostMapping("/create") public User save(User user) { return userService.save(user); } } package com.example.user.mapper; import com.example.model.User; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface UserMapper<User> { // 添加selectById方法声明 User selectById(Long id); // 保持原有方法 User selectUserById(Long id); // 新增基础CRUD方法 List<User> selectAll(); int insertUser(User user); int updateUser(User user); int deleteUser(Long id); } package com.example.user.service; import com.example.model.Goods; import com.example.user.mapper.UserMapper; import com.example.model.User; import org.springframework.stereotype.Service; @Service public class UserService { private final UserMapper userMapper; public UserService(UserMapper userMapper) { this.userMapper = userMapper; } public User save(User user) { // 设置 is_valid 的值 user.setIsValid(true); // 设置 role_id 的值(假设之前已经处理过 role_id 的问题) user.setRoleId(1L); userMapper.insertUser(user); return user; } public User getById(Long id) { return (User) userMapper.selectById(id); // MyBatis-Plus 内置的根据id查询方法 } } package com.example.user; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient @MapperScan("com.example.user.mapper") public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.user.mapper.UserMapper"> <!-- 使用正确的实体类路径 --> <resultMap id="UserResultMap" type="com.example.user.entity.User"> <id column="id" property="id"/> <result column="no" property="no"/> <result column="name" property="name"/> <result column="password" property="password"/> <result column="age" property="age"/> <result column="sex" property="sex"/> <result column="phone" property="phone"/> <result column="role_id" property="roleId"/> <result column="is_valid" property="isValid"/> </resultMap> <!-- 添加selectById方法 --> <select id="selectById" resultMap="UserResultMap"> SELECT id, no, name, password, age, sex, phone, role_id, is_valid FROM user WHERE id = #{id} </select> <!-- 简化参数类型声明 --> <select id="selectUserById" resultMap="UserResultMap"> SELECT id, no, name, password, age, sex, phone, role_id, is_valid FROM user WHERE id = #{id} </select> <!-- 添加基础CRUD操作 --> <select id="selectAll" resultMap="UserResultMap"> SELECT * FROM user </select> <insert id="insertUser" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user (no, name, password, age, sex, phone, role_id, is_valid) VALUES (#{no}, #{name}, #{password}, #{age}, #{sex}, #{phone}, #{roleId}, #{isValid}) </insert> <update id="updateUser"> UPDATE user SET no = #{no}, name = #{name}, password = #{password}, age = #{age}, sex = #{sex}, phone = #{phone}, role_id = #{roleId}, is_valid = #{isValid} WHERE id = #{id} </update> <delete id="deleteUser"> DELETE FROM user WHERE id = #{id} </delete> </mapper> server: port: 9000 spring: main: allow-bean-definition-overriding: true datasource: url: jdbc:mysql://localhost:3306/hdms?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 application: name: service-user cloud: nacos: discovery: server-addr: 127.0.0.1:8848 jackson: # 添加Jackson配置确保BigDecimal序列化正常 default-property-inclusion: non_null serialization: write-dates-as-timestamps: false deserialization: fail-on-unknown-properties: false mybatis-plus: configuration: map-underscore-to-camel-case: true # 开启驼峰命名 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 启用SQL日志 mapper-locations: classpath*:/mapper/**/*.xml type-aliases-package: com.example.model logging: level: com.example.user.mapper: TRACE 出现问题:2025-06-17T15:19:50.565+08:00 ERROR 21148 --- [service-order] [nio-8000-exec-2] c.e.order.controller.OrderController : service-user的实例 2025-06-17T15:19:50.565+08:00 ERROR 21148 --- [service-order] [nio-8000-exec-2] c.e.order.controller.OrderController : 未找到用户信息,用户 ID: 2
06-18
package cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain; import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApiImpl; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.marketing.controller.admin.aftersalesmanagement.vo.AftersalesManagementSaveReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainPageReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainRespVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainSaveReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainStartFlowReqVO; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.iomanagementmain.IoManagementMainDO; import cn.iocoder.yudao.module.yavwarehouse.service.iomanagementdetail.IoManagementDetailService; import cn.iocoder.yudao.module.yavwarehouse.service.iomanagementmain.IoManagementMainService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Tag(name = "管理后台 - 出入库管理") @RestController @RequestMapping("/yavwarehouse/io-management-main") @Validated public class IoManagementMainController { @Resource private IoManagementMainService ioManagementMainService; @Resource private IoManagementDetailService ioManagementDetailService; @Resource private BpmProcessInstanceApiImpl bpmProcessInstanceApi; // 变更说明: // 1) 参数校验、鉴权保持不变;业务逻辑仍在 Service 层,控制器保持薄。 @PostMapping("/create") @Operation(summary = "创建出入库主") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:create')") public CommonResult<Long> createIoManagementMain(@Valid @RequestBody IoManagementMainSaveReqVO createReqVO) { return success(ioManagementMainService.createIoManagementMain(createReqVO)); } @PutMapping("/update") @Operation(summary = "更新出入库主") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:update')") public CommonResult<Boolean> updateIoManagementMain(@Valid @RequestBody IoManagementMainSaveReqVO updateReqVO) { ioManagementMainService.updateIoManagementMain(updateReqVO); return success(true); } @PostMapping("/approval") @Operation(summary = "审批出入库主") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:approval')") public CommonResult<Boolean> approvalIoManagementMain(@Valid @RequestBody IoManagementMainSaveReqVO updateReqVO) { ioManagementMainService.approvalIoManagementMain(updateReqVO); return success(true); } @DeleteMapping("/delete") @Operation(summary = "删除出入库主") @Parameter(name = "id", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:delete')") public CommonResult<Boolean> deleteIoManagementMain(@RequestParam("id") Long id) { ioManagementMainService.deleteIoManagementMain(id); return success(true); } @DeleteMapping("/delete-list") @Parameter(name = "ids", description = "编号", required = true) @Operation(summary = "批量删除出入库主") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:delete')") public CommonResult<Boolean> deleteIoManagementMainList(@RequestParam("ids") List<Long> ids) { ioManagementMainService.deleteIoManagementMainListByIds(ids); return success(true); } @GetMapping("/get") @Operation(summary = "获得出入库主") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:query')") public CommonResult<IoManagementMainRespVO> getIoManagementMain(@RequestParam("id") Long id, @RequestParam(value = "withItems", required = false, defaultValue = "false") boolean withItems) { // 变更:将“查明细与组装”下沉到 Service;Controller 只透传参数 return success(ioManagementMainService.getIoManagementMainWithItems(id, withItems)); } @GetMapping("/page") @Operation(summary = "获得出入库主分页") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:query')") public CommonResult<PageResult<IoManagementMainRespVO>> getIoManagementMainPage(@Valid IoManagementMainPageReqVO pageReqVO) { return success(ioManagementMainService.getIoManagementMainPages(pageReqVO)); } @GetMapping("/export-excel") @Operation(summary = "导出出入库主 Excel") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:export')") @ApiAccessLog(operateType = EXPORT) public void exportIoManagementMainExcel(@Valid IoManagementMainPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List<IoManagementMainDO> list = ioManagementMainService.getIoManagementMainPage(pageReqVO).getList(); // 导出 Excel ExcelUtils.write(response, "出入库主.xls", "数据", IoManagementMainRespVO.class, BeanUtils.toBean(list, IoManagementMainRespVO.class)); } @GetMapping("/getOrderPage") @Operation(summary = "根据业务类型查询单据") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:query')") public CommonResult getOrderPage(@RequestParam String businessType, @RequestParam String businessTypeValue, @RequestParam String applyType, PageParam pageParam) { return success(ioManagementMainService.getOrderPage(businessType, businessTypeValue, applyType, pageParam)); } @PutMapping("/startFlow") @Operation(summary = "发起出入库流程") @PreAuthorize("@ss.hasPermission('yavwarehouse:io-management-main:update')") public CommonResult<Boolean> startFlow(@Valid @RequestBody IoManagementMainStartFlowReqVO reqVO) { Map<String, Object> variables = new HashMap<>(); variables.put("ioId", reqVO.getId()); String processInstanceId = bpmProcessInstanceApi.createProcessInstance(getLoginUserId(), new BpmProcessInstanceCreateReqDTO() .setProcessDefinitionKey("inbound") .setVariables(variables) .setBusinessKey(String.valueOf(reqVO.getId())) .setStartUserSelectAssignees(reqVO.getNextAssignees())); ioManagementMainService.updateProcessInfo(reqVO.getId(), processInstanceId, "RUNNING"); return success(true); } } package cn.iocoder.yudao.module.yavwarehouse.service.iomanagementmain; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.enums.AuditStateEnum; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.marketing.controller.admin.profilesalesorder.vo.ProfileSalesOrderPageReqVO; import cn.iocoder.yudao.module.marketing.controller.admin.projectsalesorder.vo.ProjectSalesOrderPageReqVO; import cn.iocoder.yudao.module.marketing.controller.admin.salesorder.vo.KreSalesOrderPageReqVO; import cn.iocoder.yudao.module.marketing.dal.dataobject.profilesalesorder.ProfileSalesOrderDO; import cn.iocoder.yudao.module.marketing.dal.dataobject.projectsalesorder.ProjectSalesOrderDO; import cn.iocoder.yudao.module.marketing.dal.dataobject.salesorder.KreSalesOrderDO; import cn.iocoder.yudao.module.marketing.dal.mysql.profilesalesorder.ProfileSalesOrderMapper; import cn.iocoder.yudao.module.marketing.dal.mysql.projectsalesorder.ProjectSalesOrderMapper; import cn.iocoder.yudao.module.marketing.dal.mysql.salesorder.KreSalesOrderMapper; import cn.iocoder.yudao.module.plan.controller.admin.factorymaterials.vo.FactoryMaterialsPageReqVO; import cn.iocoder.yudao.module.plan.controller.admin.fenestrationmanagement.vo.FenestrationManagementPageReqVO; import cn.iocoder.yudao.module.plan.controller.admin.profilecheck.vo.ProfileCheckPageReqVO; import cn.iocoder.yudao.module.plan.dal.dataobject.factorymaterials.FactoryMaterialsDO; import cn.iocoder.yudao.module.plan.dal.dataobject.fenestrationmanagement.FenestrationManagementDO; import cn.iocoder.yudao.module.plan.dal.dataobject.profilecheck.ProfileCheckDO; import cn.iocoder.yudao.module.plan.dal.mysql.factorymaterials.FactoryMaterialsMapper; import cn.iocoder.yudao.module.plan.dal.mysql.fenestrationmanagement.FenestrationManagementMapper; import cn.iocoder.yudao.module.plan.dal.mysql.profilecheck.ProfileCheckMapper; import cn.iocoder.yudao.module.purchase.controller.admin.receipt.vo.ReceiptPageReqVO; import cn.iocoder.yudao.module.purchase.dal.dataobject.material.MaterialDO; import cn.iocoder.yudao.module.purchase.dal.dataobject.materialinventory.MaterialInventoryDO; import cn.iocoder.yudao.module.purchase.dal.dataobject.receipt.ReceiptDO; import cn.iocoder.yudao.module.purchase.dal.mysql.material.MaterialMapper; import cn.iocoder.yudao.module.purchase.dal.mysql.materialinventory.MaterialInventoryMapper; import cn.iocoder.yudao.module.purchase.dal.mysql.receipt.ReceiptMapper; import cn.iocoder.yudao.module.purchase.utils.OrderNoGeneratorUtil; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.inventorydetails.vo.InventoryDetailsSaveReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementdetail.vo.IoManagementDetailRespVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementdetail.vo.IoManagementDetailSaveReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainPageReqVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainRespVO; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.IoManagementMainSaveReqVO; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.inventorydetails.InventoryDetailsDO; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.iomanagementdetail.IoManagementDetailDO; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.iomanagementmain.IoManagementMainDO; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.yavwarehouse.YavWarehouseDO; import cn.iocoder.yudao.module.yavwarehouse.dal.mysql.iomanagementdetail.IoManagementDetailMapper; import cn.iocoder.yudao.module.yavwarehouse.dal.mysql.iomanagementmain.IoManagementMainMapper; import cn.iocoder.yudao.module.yavwarehouse.dal.mysql.yavwarehouse.YavWarehouseMapper; import cn.iocoder.yudao.module.yavwarehouse.enums.BILL_TYPE; import cn.iocoder.yudao.module.yavwarehouse.enums.CostStrategyType; import cn.iocoder.yudao.module.yavwarehouse.enums.YavConstants; import cn.iocoder.yudao.module.yavwarehouse.service.inventorydetails.InventoryDetailsService; import cn.iocoder.yudao.module.yavwarehouse.service.iomanagementdetail.IoManagementDetailService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.datical.liquibase.ext.checks.config.IFileAccessor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.yavwarehouse.enums.ErrorCodeConstants.*; /** * 出入库主 Service 实现类 */ @Service @Validated @Slf4j public class IoManagementMainServiceImpl implements IoManagementMainService { @Resource private IoManagementMainMapper ioManagementMainMapper; @Resource private IoManagementDetailService ioManagementDetailService; @Resource private InventoryDetailsService inventoryDetailsService; @Resource private IoManagementDetailMapper ioManagementDetailMapper; @Resource private ProfileCheckMapper profileCheckMapper; @Resource private FenestrationManagementMapper fenestrationManagementMapper; @Resource private FactoryMaterialsMapper factoryMaterialsMapper; @Resource private ReceiptMapper receiptMapper; @Resource private KreSalesOrderMapper kreSalesOrderMapper; @Resource private ProjectSalesOrderMapper projectSalesOrderMapper; @Resource private ProfileSalesOrderMapper profileSalesOrderMapper; @Resource private MaterialInventoryMapper materialInventoryMapper; @Resource private MaterialMapper materialMapper; @Resource private YavWarehouseMapper warehouseMapper; @Override @Transactional(rollbackFor = Exception.class) public Long createIoManagementMain(IoManagementMainSaveReqVO createReqVO) { // 插入主表记录 if (StringUtils.isBlank(createReqVO.getIoBillNo())) { String next = OrderNoGeneratorUtil.next(); if (createReqVO.getIoBillType().equals(BILL_TYPE.OUTBOUND)) { createReqVO.setIoBillNo(BILL_TYPE.CK + "_" + next); } else { createReqVO.setIoBillNo(BILL_TYPE.RK + "_" + next); } } IoManagementMainDO ioManagementMain = BeanUtils.toBean(createReqVO, IoManagementMainDO.class); if (ObjectUtil.equals(createReqVO.getAuditState(), AuditStateEnum.NO.getStatus())) { checkSave(createReqVO); ioManagementMain.setAuditState(AuditStateEnum.NO.getStatus()); } if (ObjectUtil.equals(createReqVO.getAuditState(), AuditStateEnum.NOT_PASS.getStatus())) { checkAuditState(createReqVO); ioManagementMain.setAuditState(AuditStateEnum.NOT_PASS.getStatus()); } ioManagementMainMapper.insert(ioManagementMain); // 根据 ioBillType 判断入库/出库,影响库存增减方向与校验 boolean isInbound = BILL_TYPE.INBOUND.equals(createReqVO.getIoBillType()); // 默认策略:FIFO(保持现有行为不变)。如需切换策略,仅需改此处。 final CostStrategyType costStrategy = CostStrategyType.FIFO; // 明细持久化:先统一补主表关联字段,再调用明细创建(可后续优化为批量) List<IoManagementDetailDO> ioManagementDetailDOS = BeanUtils.toBean(createReqVO.getItems(), IoManagementDetailDO.class); ioManagementDetailDOS.forEach(item -> { item.setMainId(ioManagementMain.getId()); item.setIoBillNo(ioManagementMain.getIoBillNo()); if (!isInbound) item.setIoMainUnitQty(item.getIoMainUnitQty().negate()); Long detailId = ioManagementDetailService.createIoManagementDetail(item); Long warehouseId = item.getIoWarehouseId(); if (!isInbound) { if (costStrategy == CostStrategyType.FIFO) { // FIFO:按入库明细顺序分摊并写回成本 updateInboundDetail(warehouseId, item.getIoMaterialId(), detailId, item.getIoMainUnitQty().negate()); } else { // 其他策略:计算并写回成本(不改变入库明细的已出库数量) computeAndWriteOutboundCostByStrategy(warehouseId, item.getIoMaterialId(), detailId, item.getIoMainUnitQty().negate(), costStrategy); } } }); if (CollectionUtils.isNotEmpty(ioManagementDetailDOS)) { ioManagementDetailDOS.stream().map(p -> p.getIoWarehouseId()).distinct().forEach(warehouseId -> { // 合并明细:按物料ID汇总数量;使用已带方向(出库为负、入库为正)的明细集合 Map<Long, BigDecimal> materialIdToDelta = ioManagementDetailDOS.stream() .filter(p -> Objects.equals(p.getIoWarehouseId(), warehouseId)) .collect(Collectors.toMap( IoManagementDetailDO::getIoMaterialId, IoManagementDetailDO::getIoMainUnitQty, BigDecimal::add)); // 一次性查询所有涉及物料的现存库存,返回以 materialId 为 key 的 Map Map<Long, InventoryDetailsDO> materialIdToInv = inventoryDetailsService .listByWarehouseIdAndMaterialIds(warehouseId, materialIdToDelta.keySet()); // 分流创建/更新:出库校验库存存在与不为负 for (Map.Entry<Long, BigDecimal> entry : materialIdToDelta.entrySet()) { Long materialId = entry.getKey(); BigDecimal delta = entry.getValue(); // 已包含方向(入库+,出库-) InventoryDetailsDO inv = materialIdToInv.get(materialId); if (inv == null) { if (!isInbound) { // 出库:库存不存在,直接拒绝 throw exception(INVENTORY_REFUSE_OUTBOUND); } // 入库:不存在则创建 YavWarehouseDO warehouseDO = warehouseMapper.selectById(warehouseId); List<MaterialInventoryDO> materialInventoryDOS = materialInventoryMapper.selectList(new LambdaQueryWrapper<MaterialInventoryDO>().eq(Objects.nonNull(warehouseDO) && StringUtils.isNotBlank(warehouseDO.getWhName()), MaterialInventoryDO::getMWarehouse, warehouseDO.getWhName())); String position = ""; String unit = ""; String ioCustomOrder = ""; if (CollectionUtils.isNotEmpty(materialInventoryDOS)) { MaterialInventoryDO materialInventoryDO = materialInventoryDOS.get(0); position = materialInventoryDO.getMWarehouseShelf() + "-" + materialInventoryDO.getMWarehouseColumn() + "-" + materialInventoryDO.getMWarehouseNum(); } MaterialDO materialDO = materialMapper.selectById(materialId); List<IoManagementDetailDO> detailDOS = ioManagementDetailDOS.stream() .filter(p -> Objects.equals(p.getIoWarehouseId(), warehouseId) && Objects.equals(p.getIoMaterialCode(), Objects.nonNull(materialDO) ? materialDO.getMCode() : null)) .collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(detailDOS)) { position = detailDOS.get(0).getIoWarehouseLocation(); unit = detailDOS.get(0).getIoMainUnit(); ioCustomOrder = detailDOS.get(0).getIoCustomOrder(); } inventoryDetailsService.createInventoryDetails(InventoryDetailsSaveReqVO.builder() .idWhId(warehouseId) .idMaterialId(materialId) .idMaterialName(materialDO.getMName()) .idMaterialCode(materialDO.getMCode()) .idAvailableQuantity(delta) .idStockQuantity(delta) .idInventoryUnit(unit) .idCustomOrder(StringUtils.isNotBlank(ioCustomOrder) ? Integer.valueOf(ioCustomOrder) : null) .idMaterialPosition(position) .idWhName(Objects.nonNull(warehouseDO) ? warehouseDO.getWhName() : "") .idWhCode(Objects.nonNull(warehouseDO) ? warehouseDO.getWhNumber() : "") .build()); } else { BigDecimal newAvailable = inv.getIdAvailableQuantity().add(delta); BigDecimal newStock = inv.getIdStockQuantity().add(delta); if (!isInbound && newStock.compareTo(BigDecimal.ZERO) < 0) { // 出库:扣减后为负,拒绝 throw exception(BOUND_RUN_OUT); } YavWarehouseDO warehouseDO = warehouseMapper.selectById(warehouseId); List<MaterialInventoryDO> materialInventoryDOS = materialInventoryMapper.selectList(new LambdaQueryWrapper<MaterialInventoryDO>().eq(Objects.nonNull(warehouseDO) && StringUtils.isNotBlank(warehouseDO.getWhName()), MaterialInventoryDO::getMWarehouse, warehouseDO.getWhName())); String position = ""; String unit = ""; String ioCustomOrder = ""; if (CollectionUtils.isNotEmpty(materialInventoryDOS)) { MaterialInventoryDO materialInventoryDO = materialInventoryDOS.get(0); position = materialInventoryDO.getMWarehouseShelf() + "-" + materialInventoryDO.getMWarehouseColumn() + "-" + materialInventoryDO.getMWarehouseNum(); } MaterialDO materialDO = materialMapper.selectById(materialId); List<IoManagementDetailDO> detailDOS = ioManagementDetailDOS.stream() .filter(p -> Objects.equals(p.getIoWarehouseId(), warehouseId) && Objects.equals(p.getIoMaterialCode(), Objects.nonNull(materialDO) ? materialDO.getMCode() : null)) .collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(detailDOS)) { position = detailDOS.get(0).getIoWarehouseLocation(); unit = detailDOS.get(0).getIoMainUnit(); ioCustomOrder = detailDOS.get(0).getIoCustomOrder(); } inventoryDetailsService.updateInventoryDetails(InventoryDetailsSaveReqVO.builder() .id(inv.getId()) .idWhId(warehouseId) .idMaterialId(materialId) .idMaterialName(Objects.nonNull(materialDO) ? materialDO.getMName() : "") .idMaterialCode(Objects.nonNull(materialDO) ? materialDO.getMCode() : "") .idAvailableQuantity(newAvailable) .idStockQuantity(newStock) .idInventoryUnit(unit) .idCustomOrder(StringUtils.isNotBlank(ioCustomOrder) ? Integer.valueOf(ioCustomOrder) : null) .idMaterialPosition(position) .idMaterialPosition(position) .idWhName(Objects.nonNull(warehouseDO) ? warehouseDO.getWhName() : "") .idWhCode(Objects.nonNull(warehouseDO) ? warehouseDO.getWhNumber() : "") .build()); } } }); } //更改对应单据来源表出入库状态 if (Objects.nonNull(createReqVO.getIoRelateNum())) { updateSourceBillStatus(createReqVO, createReqVO.getIoBillType()); } return ioManagementMain.getId(); } private void checkSave(IoManagementMainSaveReqVO createReqVO) { } private void checkAuditState(IoManagementMainSaveReqVO createReqVO) { //入库类型、入库时间、应用类型、入库总成本、关联单号、入库用途、选择物料、仓库、仓库类型、入库数量、单位 if (StringUtils.isBlank(createReqVO.getIoBillType())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "入库/出库类型不能为空"); } String name = createReqVO.getIoBillType().equals(BILL_TYPE.INBOUND) ? "入库" : "出库"; if (ObjectUtil.isEmpty(createReqVO.getIoBillDate())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), name + "时间不能为空"); } if (StringUtils.isBlank(createReqVO.getIoApplyType())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "应用类型不能为空"); } if (ObjectUtil.isEmpty(createReqVO.getIoTotalCost())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), name + "总成本不能为空"); } if (ObjectUtil.isEmpty(createReqVO.getIoRelateNum())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "关联单号不能为空"); } if (StringUtils.isBlank(createReqVO.getIoPurpose())) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), name + "用途不能为空"); } List<IoManagementDetailSaveReqVO> items = createReqVO.getItems(); if (CollectionUtils.isEmpty(items)) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "选择物料不能为空"); } if (items.stream().anyMatch(item -> StringUtils.isBlank(item.getIoWarehouse()))) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "仓库名字不能为空"); } if (items.stream().anyMatch(item -> StringUtils.isBlank(item.getIoWarehouseType()))) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "仓库类型不能为空"); } if (items.stream().anyMatch(item -> ObjectUtil.isEmpty(item.getIoMainUnitQty()))) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), name + "数量不能为空"); } if (items.stream().anyMatch(item -> ObjectUtil.isEmpty(item.getIoMainUnit()))) { throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST_PARAM.getCode(), "主单位不能为空"); } } /** * 更新源单据状态 */ private void updateSourceBillStatus(IoManagementMainSaveReqVO createReqVO, String ioBillType) { Long ioRelateNum = createReqVO.getIoRelateNum(); if (ioRelateNum == null) { log.warn("关联单据ID为空,无法更新源单据状态"); return; } String ioApplyType = createReqVO.getIoApplyType(); String ioBusinessType = createReqVO.getIoBusinessType(); try { if (BILL_TYPE.INBOUND.equals(ioBillType)) { updateInboundSourceStatus(ioBusinessType, ioRelateNum); } else { updateOutboundSourceStatus(ioBusinessType, ioApplyType, ioRelateNum); } } catch (Exception e) { log.error("更新源单据状态失败: billType={}, businessType={}, applyType={}, relateNum={}", ioBillType, ioBusinessType, ioApplyType, ioRelateNum, e); } } /** * 更新入库源单据状态 */ private void updateInboundSourceStatus(String ioBusinessType, Long ioRelateNum) { if (YavConstants.ONE_STR.equals(ioBusinessType)) { // 采购入库(收料通知单表) receiptMapper.update(new LambdaUpdateWrapper<ReceiptDO>() .eq(ReceiptDO::getId, ioRelateNum) .set(ReceiptDO::getRWareStatus, YavConstants.ONE_STR)); } // 可以在这里添加其他入库类型的处理 } /** * 更新出库源单据状态 */ private void updateOutboundSourceStatus(String ioBusinessType, String ioApplyType, Long ioRelateNum) { if (YavConstants.ONE_STR.equals(ioBusinessType)) { switch (ioApplyType) { case YavConstants.ONE_STR: // 型材配置 profileCheckMapper.update(new LambdaUpdateWrapper<ProfileCheckDO>() .eq(ProfileCheckDO::getId, ioRelateNum) .set(ProfileCheckDO::getRWareStatus, YavConstants.ONE_STR)); break; case YavConstants.TWO_STR: // 门窗原料 fenestrationManagementMapper.update(new LambdaUpdateWrapper<FenestrationManagementDO>() .eq(FenestrationManagementDO::getId, ioRelateNum) .set(FenestrationManagementDO::getRWareStatus, YavConstants.ONE_STR)); break; case YavConstants.THREE_STR: // 门窗工厂 factoryMaterialsMapper.update(new LambdaUpdateWrapper<FactoryMaterialsDO>() .eq(FactoryMaterialsDO::getId, ioRelateNum) .set(FactoryMaterialsDO::getRWareStatus, YavConstants.ONE_STR)); break; default: log.warn("未知的出库应用类型: {}", ioApplyType); } } else { //出库-销售订单 switch (ioApplyType) { case YavConstants.ONE_STR: // 型材配置 profileSalesOrderMapper.update(new LambdaUpdateWrapper<ProfileSalesOrderDO>() .eq(ProfileSalesOrderDO::getId, ioRelateNum) .set(ProfileSalesOrderDO::getPsShipper, YavConstants.ONE)); break; case YavConstants.TWO_STR: // 家装销售订单 kreSalesOrderMapper.update(new LambdaUpdateWrapper<KreSalesOrderDO>() .eq(KreSalesOrderDO::getId, ioRelateNum) .set(KreSalesOrderDO::getSoShipper, YavConstants.ONE)); break; case YavConstants.THREE_STR: // 工程销售订单 projectSalesOrderMapper.update(new LambdaUpdateWrapper<ProjectSalesOrderDO>() .eq(ProjectSalesOrderDO::getId, ioRelateNum) .set(ProjectSalesOrderDO::getEsShipper, YavConstants.ONE)); break; default: log.warn("未知的出库应用类型: {}", ioApplyType); } return; } } // 计算除 FIFO 以外策略下的出库成本并写回当前出库明细(不改变入库明细) private void computeAndWriteOutboundCostByStrategy(Long warehouseId, Long ioMaterialId, Long outboundDetailId, BigDecimal outboundQty, CostStrategyType strategy) { if (outboundQty == null || outboundQty.compareTo(BigDecimal.ZERO) <= 0) { return; } BigDecimal unitCost; switch (strategy) { case AVERAGE: unitCost = computeAverageUnitCost(warehouseId, ioMaterialId); break; case WEIGHTED: unitCost = computeWeightedUnitCost(warehouseId, ioMaterialId); break; case MOVING_AVERAGE: unitCost = computeMovingAverageUnitCost(warehouseId, ioMaterialId); break; default: unitCost = BigDecimal.ZERO; } writeOutboundCost(outboundDetailId, unitCost, outboundQty); } // 简单平均:历史“可消耗入库明细”的单价算术平均(不考虑数量) private BigDecimal computeAverageUnitCost(Long warehouseId, Long materialId) { List<IoManagementDetailDO> inboundList = ioManagementDetailMapper.selectConsumableInbound(warehouseId, materialId); if (inboundList == null || inboundList.isEmpty()) return BigDecimal.ZERO; BigDecimal sum = BigDecimal.ZERO; int count = 0; for (IoManagementDetailDO d : inboundList) { if (d.getIoCostPrice() != null) { sum = sum.add(d.getIoCostPrice()); count++; } } if (count == 0) return BigDecimal.ZERO; return sum.divide(new BigDecimal(count), 6, RoundingMode.HALF_UP); } // 加权平均:按“剩余可用数量”加权 private BigDecimal computeWeightedUnitCost(Long warehouseId, Long materialId) { List<IoManagementDetailDO> inboundList = ioManagementDetailMapper.selectConsumableInbound(warehouseId, materialId); if (inboundList == null || inboundList.isEmpty()) return BigDecimal.ZERO; BigDecimal weightedSum = BigDecimal.ZERO; BigDecimal totalQty = BigDecimal.ZERO; for (IoManagementDetailDO d : inboundList) { BigDecimal inQty = d.getIoMainUnitQty(); BigDecimal used = d.getIoOutboundQuantity() == null ? BigDecimal.ZERO : d.getIoOutboundQuantity(); BigDecimal remain = inQty.subtract(used); if (remain.compareTo(BigDecimal.ZERO) <= 0) continue; BigDecimal unit = d.getIoCostPrice() == null ? BigDecimal.ZERO : d.getIoCostPrice(); weightedSum = weightedSum.add(unit.multiply(remain)); totalQty = totalQty.add(remain); } if (totalQty.compareTo(BigDecimal.ZERO) == 0) return BigDecimal.ZERO; return weightedSum.divide(totalQty, 6, RoundingMode.HALF_UP); } // 移动加权平均:优先使用主表平均成本;没有则回退为当前加权平均 private BigDecimal computeMovingAverageUnitCost(Long warehouseId, Long materialId) { // 主表暂未保存 avgCost 字段,先回退到加权平均 return computeWeightedUnitCost(warehouseId, materialId); } private void writeOutboundCost(Long outboundDetailId, BigDecimal unitCost, BigDecimal qty) { if (outboundDetailId == null || unitCost == null || qty == null) return; BigDecimal line = unitCost.multiply(qty); IoManagementDetailDO update = new IoManagementDetailDO(); update.setId(outboundDetailId); update.setIoCostPrice(unitCost); update.setIoLineCost(line); ioManagementDetailMapper.updateById(update); } // FIFO:按时间顺序(先进先出)累计入库明细的已出库数量,并计算本次出库成本 private void updateInboundDetail(Long warehouseId, Long ioMaterialId, Long outboundDetailId, BigDecimal outboundQty) { if (outboundQty == null || outboundQty.compareTo(BigDecimal.ZERO) <= 0) { return; } BigDecimal need = outboundQty; BigDecimal costSum = BigDecimal.ZERO; List<IoManagementDetailDO> inboundList = ioManagementDetailMapper.selectConsumableInbound(warehouseId, ioMaterialId); for (IoManagementDetailDO inbound : inboundList) { if (need.compareTo(BigDecimal.ZERO) <= 0) { break; } BigDecimal inQty = inbound.getIoMainUnitQty(); BigDecimal used = inbound.getIoOutboundQuantity() == null ? BigDecimal.ZERO : inbound.getIoOutboundQuantity(); BigDecimal remain = inQty.subtract(used); if (remain.compareTo(BigDecimal.ZERO) <= 0) { continue; } BigDecimal alloc = need.min(remain); int affected = ioManagementDetailMapper.increaseOutboundQuantity(inbound.getId(), alloc); if (affected != 1) { inboundList = ioManagementDetailMapper.selectConsumableInbound(warehouseId, ioMaterialId); continue; } BigDecimal unitCost = inbound.getIoCostPrice() == null ? BigDecimal.ZERO : inbound.getIoCostPrice(); costSum = costSum.add(unitCost.multiply(alloc)); need = need.subtract(alloc); if (need.compareTo(BigDecimal.ZERO) > 0) { throw exception(BOUND_RUN_OUT); } } if (outboundDetailId != null) { IoManagementDetailDO update = new IoManagementDetailDO(); update.setId(outboundDetailId); update.setIoLineCost(costSum); BigDecimal unit = costSum.divide(outboundQty, 6, RoundingMode.HALF_UP); update.setIoCostPrice(unit); ioManagementDetailMapper.updateById(update); } } @Override @Transactional(readOnly = true) public IoManagementMainRespVO getIoManagementMainWithItems(Long id, boolean withItems) { IoManagementMainDO main = ioManagementMainMapper.selectById(id); if (main == null) { throw exception(IO_MANAGEMENT_MAIN_NOT_EXISTS); } IoManagementMainRespVO vo = BeanUtils.toBean(main, IoManagementMainRespVO.class); List<IoManagementDetailDO> details = ioManagementDetailService.listByMainId(id); vo.setItems(BeanUtils.toBean(details, IoManagementDetailRespVO.class)); vo.setIoApplyType(CollectionUtils.isNotEmpty(details) ? details.get(0).getIoApplyType() : ""); return vo; } @Transactional(rollbackFor = Exception.class) @Override public void updateIoManagementMain(IoManagementMainSaveReqVO updateReqVO) { // 校验存在 validateIoManagementMainExists(updateReqVO.getId()); // 更新 IoManagementMainDO updateObj = BeanUtils.toBean(updateReqVO, IoManagementMainDO.class); if (ObjectUtil.equals(updateReqVO.getAuditState(), AuditStateEnum.NO.getStatus())) { checkSave(updateReqVO); updateObj.setAuditState(AuditStateEnum.NO.getStatus()); } if (ObjectUtil.equals(updateReqVO.getAuditState(), AuditStateEnum.NOT_PASS.getStatus())) { checkAuditState(updateReqVO); updateObj.setAuditState(AuditStateEnum.NOT_PASS.getStatus()); //更新明细 if (CollectionUtils.isNotEmpty(updateReqVO.getItems())) { updateReqVO.getItems().forEach(updateItem -> { IoManagementDetailDO newUpdateItem = BeanUtils.toBean(updateItem, IoManagementDetailDO.class); ioManagementDetailMapper.insertOrUpdate(newUpdateItem); }); } } ioManagementMainMapper.updateById(updateObj); } @Override public void approvalIoManagementMain(IoManagementMainSaveReqVO updateReqVO) { Long id = updateReqVO.getId(); IoManagementMainDO ioManagementMainDO = ioManagementMainMapper.selectById(id); // 状态校验 审批不通过,数据状态变为保存 if (ObjectUtil.equals(updateReqVO.getAuditState(), AuditStateEnum.NO.getStatus())) { ioManagementMainDO.setAuditState(AuditStateEnum.NO.getStatus()); } ioManagementMainDO.setAuditState(updateReqVO.getAuditState()); ioManagementMainMapper.updateById(ioManagementMainDO); } @Override public void deleteIoManagementMain(Long id) { // 校验存在 validateIoManagementMainExists(id); // 删除 ioManagementMainMapper.deleteById(id); } @Transactional(rollbackFor = Exception.class) @Override public void deleteIoManagementMainListByIds(List<Long> ids) { // 删除 ioManagementMainMapper.deleteByIds(ids); //删除出入库明细 ioManagementDetailMapper.delete(new LambdaUpdateWrapper<IoManagementDetailDO>().in(CollectionUtils.isNotEmpty(ids), IoManagementDetailDO::getMainId, ids)); } private void validateIoManagementMainExists(Long id) { if (ioManagementMainMapper.selectById(id) == null) { throw exception(IO_MANAGEMENT_MAIN_NOT_EXISTS); } } @Override public IoManagementMainDO getIoManagementMain(Long id) { return ioManagementMainMapper.selectById(id); } @Override public PageResult<IoManagementMainDO> getIoManagementMainPage(IoManagementMainPageReqVO pageReqVO) { return ioManagementMainMapper.selectPage(pageReqVO); } //@Override //public PageResult<IoManagementMainRespVO> getIoManagementMainPages(IoManagementMainPageReqVO pageReqVO) { // PageResult<IoManagementMainDO> pageResult = ioManagementMainMapper.selectPage(pageReqVO); // PageResult<IoManagementMainRespVO> result = BeanUtils.toBean(pageResult, IoManagementMainRespVO.class); // if (CollectionUtils.isEmpty(result.getList())) { // return result; // } // List<Long> ids = result.getList().stream().map(p -> p.getId()).collect(Collectors.toList()); // List<IoManagementDetailDO> detailDOS = ioManagementDetailMapper.selectList(new LambdaQueryWrapper<IoManagementDetailDO>().in(IoManagementDetailDO::getMainId, ids)); // // 5. 将明细数据按mainId分组 // Map<Long, List<IoManagementDetailDO>> detailMap = detailDOS.stream() // .collect(Collectors.groupingBy(IoManagementDetailDO::getMainId)); // result.getList().forEach(p -> { // List<IoManagementDetailDO> details = detailMap.get(p.getId()); // p.setIoApplyType(details.get(0).getIoApplyType()); // }); // return result; //} @Override public PageResult<IoManagementMainRespVO> getIoManagementMainPages(IoManagementMainPageReqVO pageReqVO) { PageResult<IoManagementMainDO> pageResult = ioManagementMainMapper.selectPage(pageReqVO); PageResult<IoManagementMainRespVO> result = BeanUtils.toBean(pageResult, IoManagementMainRespVO.class); if (CollectionUtils.isEmpty(result.getList())) { return result; } List<Long> ids = result.getList().stream().map(p -> p.getId()).collect(Collectors.toList()); List<IoManagementDetailDO> detailDOS = ioManagementDetailMapper.selectList( new LambdaQueryWrapper<IoManagementDetailDO>().in(IoManagementDetailDO::getMainId, ids) ); Map<Long, List<IoManagementDetailDO>> detailMap = detailDOS.stream() .collect(Collectors.groupingBy(IoManagementDetailDO::getMainId)); // 修复:增加非空判断,避免空指针 result.getList().forEach(p -> { List<IoManagementDetailDO> details = detailMap.get(p.getId()); if (CollectionUtils.isNotEmpty(details)) { // 只有明细数据存在时才赋值 p.setIoApplyType(details.get(0).getIoApplyType()); } else { p.setIoApplyType(null); // 无明细时设为null或默认值 } }); return result; } /** * @Description:根据业务类型查询单据列表 * @Author: hjs * @Date 2025/9/3 **/ @Override public PageResult<?> getOrderPage(String businessType, String businessTypeValue, String applyType, PageParam pageParam) { log.info("根据业务类型查询单据列表入参,businessType={},businessTypeValue={},applyType={}", businessType, businessTypeValue, applyType); if (BILL_TYPE.CK_BUSINESS_TYPE.equals(businessType)) { if (businessTypeValue.equals(YavConstants.ONE_STR)) { //出库-生产领料(字典出库明细应用类型 ck_apply_type) if (applyType.equals(YavConstants.ONE_STR)) { //型材配置 ProfileCheckPageReqVO pageReqVO = new ProfileCheckPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setRWareStatus(YavConstants.TWO_STR); return profileCheckMapper.selectPage(pageReqVO); } else if (applyType.equals(YavConstants.TWO_STR)) { //门窗原料-家装 FenestrationManagementPageReqVO pageReqVO = new FenestrationManagementPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setRWareStatus(YavConstants.TWO_STR); return fenestrationManagementMapper.selectPage(pageReqVO); } else if (applyType.equals(YavConstants.THREE_STR)) { //门窗工厂 FactoryMaterialsPageReqVO pageReqVO = new FactoryMaterialsPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setRWareStatus(YavConstants.TWO_STR); return factoryMaterialsMapper.selectPage(pageReqVO); } } else if (businessTypeValue.equals(YavConstants.TWO_STR)) { //出库-销售出库(字典出库明细应用类型 xs_ck_apply_type) if (applyType.equals(YavConstants.ONE_STR)) { //型材销售订单 ProfileSalesOrderPageReqVO pageReqVO = new ProfileSalesOrderPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setPsShipper(YavConstants.ZERO); return profileSalesOrderMapper.selectPage(pageReqVO); } else if (applyType.equals(YavConstants.TWO_STR)) { //家装销售订单 KreSalesOrderPageReqVO pageReqVO = new KreSalesOrderPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setSoShipper(YavConstants.ZERO); return kreSalesOrderMapper.selectPage(pageReqVO); } else if (applyType.equals(YavConstants.THREE_STR)) { //工程销售订单 ProjectSalesOrderPageReqVO pageReqVO = new ProjectSalesOrderPageReqVO(); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setEsShipper(YavConstants.ZERO); return projectSalesOrderMapper.selectPage(pageReqVO); } } } else { if (businessTypeValue.equals(YavConstants.ONE_STR)) { //入库-采购入库(收料通知单表) ReceiptPageReqVO pageReqVO = new ReceiptPageReqVO(); pageReqVO.setPageNo(pageParam.getPageNo()); pageReqVO.setPageSize(pageParam.getPageSize()); pageReqVO.setRWareStatus(YavConstants.TWO_STR); return receiptMapper.selectPage(pageReqVO); } else if (businessTypeValue.equals(YavConstants.TWO_STR)) { //入库-生产入库(来源生产订单,目前还没有) return null; } } return null; } @Override public void updateProcessInfo(Long id, String processInstanceId, String status) { validateIoManagementMainExists(id); IoManagementMainDO updateObj = new IoManagementMainDO(); updateObj.setId(id); updateObj.setProcessInstanceId(processInstanceId); updateObj.setStatus(status); ioManagementMainMapper.updateById(updateObj); } @Override public void updateProcessStatus(Long id, String status) { validateIoManagementMainExists(id); IoManagementMainDO updateObj = new IoManagementMainDO(); updateObj.setId(id); updateObj.setStatus(status); ioManagementMainMapper.updateById(updateObj); } } package cn.iocoder.yudao.module.yavwarehouse.dal.mysql.iomanagementmain; import java.util.*; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.module.yavwarehouse.dal.dataobject.iomanagementmain.IoManagementMainDO; import org.apache.ibatis.annotations.Mapper; import cn.iocoder.yudao.module.yavwarehouse.controller.admin.iomanagementmain.vo.*; /** * 出入库主 Mapper * * @author YAVII */ @Mapper public interface IoManagementMainMapper extends BaseMapperX<IoManagementMainDO> { default PageResult<IoManagementMainDO> selectPage(IoManagementMainPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX<IoManagementMainDO>() .eqIfPresent(IoManagementMainDO::getIoBillNo, reqVO.getIoBillNo()) .eqIfPresent(IoManagementMainDO::getIoBillType, reqVO.getIoBillType()) .eqIfPresent(IoManagementMainDO::getIoBusinessType, reqVO.getIoBusinessType()) .betweenIfPresent(IoManagementMainDO::getIoBillDate, reqVO.getIoBillDate()) .eqIfPresent(IoManagementMainDO::getIoWarehouseId, reqVO.getIoWarehouseId()) .eqIfPresent(IoManagementMainDO::getIoWarehouse, reqVO.getIoWarehouse()) .eqIfPresent(IoManagementMainDO::getIoWarehouseCode, reqVO.getIoWarehouseCode()) .eqIfPresent(IoManagementMainDO::getIoTotalCost, reqVO.getIoTotalCost()) .eqIfPresent(IoManagementMainDO::getIoReferenceTotalCost, reqVO.getIoReferenceTotalCost()) .eqIfPresent(IoManagementMainDO::getIoRemark, reqVO.getIoRemark()) .eqIfPresent(IoManagementMainDO::getReserve1, reqVO.getReserve1()) .eqIfPresent(IoManagementMainDO::getReserve2, reqVO.getReserve2()) .eqIfPresent(IoManagementMainDO::getReserve3, reqVO.getReserve3()) .eqIfPresent(IoManagementMainDO::getReserve4, reqVO.getReserve4()) .eqIfPresent(IoManagementMainDO::getReserve5, reqVO.getReserve5()) .eqIfPresent(IoManagementMainDO::getState, reqVO.getState()) .eqIfPresent(IoManagementMainDO::getIoRelateNum, reqVO.getIoRelateNum()) .eqIfPresent(IoManagementMainDO::getIoOwnership, reqVO.getIoOwnership()) .eqIfPresent(IoManagementMainDO::getIoPurpose, reqVO.getIoPurpose()) .eqIfPresent(IoManagementMainDO::getStatus, reqVO.getStatus()) .eqIfPresent(IoManagementMainDO::getAuditState, reqVO.getAuditState()) .betweenIfPresent(IoManagementMainDO::getCreateTime, reqVO.getCreateTime()) .orderByDesc(IoManagementMainDO::getId)); } }分页查询不显示
最新发布
10-01
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值