liyong
昨天 88ae1e650fc2fc30928edfe8f3cc39108d8d1ccd
Merge remote-tracking branch 'origin/dev_New_pro' into dev_New_pro
已添加2个文件
已修改18个文件
534 ■■■■ 文件已修改
doc/20260506_add_ai_enabled_to_sys_user.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260506_用户AI功能字段前端联调说明.md 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/framework/security/LoginUser.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProductionOperationTaskVo.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderWorkOrderDetailVo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOperationTaskController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOrderController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionOperationTaskMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionOperationTaskService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionOrderService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/controller/SysLoginController.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/domain/SysUser.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionOperationTaskMapper.xml 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionOrderMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysUserMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260506_add_ai_enabled_to_sys_user.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2 @@
alter table sys_user
    add ai_enabled tinyint(1) not null default 0 comment '是否开通AI功能(0否 1是)';
doc/20260506_Óû§AI¹¦ÄÜ×Ö¶Îǰ¶ËÁªµ÷˵Ã÷.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
# ç”¨æˆ· AI åŠŸèƒ½å­—æ®µå‰ç«¯è”è°ƒè¯´æ˜Ž
## èƒŒæ™¯
后端已在用户表增加隐藏字段 `ai_enabled`,用于标识用户是否开通 AI åŠŸèƒ½ã€‚
该字段不开放给用户资料维护接口(不通过用户编辑页面下发/回写)。
## å­—段定义
| å­—段 | ç±»åž‹ | é»˜è®¤å€¼ | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ai_enabled | tinyint(1) | 0 | æ˜¯å¦å¼€é€š AI åŠŸèƒ½ï¼š`0`=未开通,`1`=已开通 |
## è”调接口
登录后调用:
```http
GET /getInfo
```
返回中新增顶层字段 `aiEnabled`:
```json
{
  "code": 200,
  "msg": "操作成功",
  "user": {
    "userId": 1,
    "userName": "admin"
  },
  "aiEnabled": 1,
  "roles": [
    "admin"
  ],
  "permissions": [
    "*:*:*"
  ]
}
```
## å‰ç«¯ä½¿ç”¨å»ºè®®
1. ç™»å½•成功后按现有流程调用 `/getInfo`。
2. ä»Žå“åº”顶层读取 `aiEnabled`。
3. åˆ¤å®šé€»è¾‘建议:
   - `aiEnabled === 1`:展示/放开 AI ç›¸å…³å…¥å£ã€‚
   - å…¶ä»–值(`0`、`null`、`undefined`):按未开通处理。
## ç¼“存说明
- `aiEnabled` å·²æ”¾å…¥ç™»å½•缓存对象(`LoginUser`)并随 token ç”Ÿå‘½å‘¨æœŸç®¡ç†ã€‚
- ç¼“å­˜ key å‰ç¼€ï¼š`login_tokens:`。
src/main/java/com/ruoyi/framework/security/LoginUser.java
@@ -76,10 +76,15 @@
     */
    private Long tenantId;
    /**
     * å½“前部门id
     */
    /**
     * å½“前部门id
     */
    private Long currentDeptId;
    /**
     * æ˜¯å¦å¼€é€šAI功能(0否 1是)
     */
    private Integer aiEnabled;
    private String dataScope;
@@ -87,38 +92,42 @@
    {
    }
    public LoginUser(SysUser user, Set<String> permissions)
    {
        this.user = user;
        this.permissions = permissions;
    }
    public LoginUser(SysUser user, Set<String> permissions)
    {
        this.user = user;
        this.permissions = permissions;
        this.aiEnabled = user == null ? null : user.getAiEnabled();
    }
    public LoginUser(Long userId, Long [] deptId, SysUser user, Set<String> permissions)
    {
        this.userId = userId;
        this.deptIds = deptId;
        this.user = user;
        this.permissions = permissions;
    }
    public LoginUser(Long userId, Long [] deptId, SysUser user, Set<String> permissions)
    {
        this.userId = userId;
        this.deptIds = deptId;
        this.user = user;
        this.permissions = permissions;
        this.aiEnabled = user == null ? null : user.getAiEnabled();
    }
    public LoginUser(Long userId, Long [] deptIds, SysUser user,Long tenantId, Set<String> permissions)
    {
        this.userId = userId;
        this.deptIds = deptIds;
        this.user = user;
        this.permissions = permissions;
        this.tenantId = tenantId;
    }
    public LoginUser(Long userId, Long [] deptIds, SysUser user,Long tenantId, Set<String> permissions)
    {
        this.userId = userId;
        this.deptIds = deptIds;
        this.user = user;
        this.permissions = permissions;
        this.tenantId = tenantId;
        this.aiEnabled = user == null ? null : user.getAiEnabled();
    }
    public LoginUser(Long userId, Long [] deptIds, SysUser user,Long tenantId,Long currentDeptId, Set<String> permissions)
    {
        this.userId = userId;
        this.deptIds = deptIds;
        this.user = user;
        this.permissions = permissions;
        this.tenantId = tenantId;
        this.currentDeptId = currentDeptId;
    }
    public LoginUser(Long userId, Long [] deptIds, SysUser user,Long tenantId,Long currentDeptId, Set<String> permissions)
    {
        this.userId = userId;
        this.deptIds = deptIds;
        this.user = user;
        this.permissions = permissions;
        this.tenantId = tenantId;
        this.currentDeptId = currentDeptId;
        this.aiEnabled = user == null ? null : user.getAiEnabled();
    }
    public Long getUserId()
    {
@@ -289,10 +298,11 @@
        return user;
    }
    public void setUser(SysUser user)
    {
        this.user = user;
    }
    public void setUser(SysUser user)
    {
        this.user = user;
        this.aiEnabled = user == null ? null : user.getAiEnabled();
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities()
@@ -320,6 +330,20 @@
        this.currentDeptId = currentDeptId;
    }
    public Integer getAiEnabled() {
        if (aiEnabled != null) {
            return aiEnabled;
        }
        if (user != null && user.getAiEnabled() != null) {
            return user.getAiEnabled();
        }
        return 0;
    }
    public void setAiEnabled(Integer aiEnabled) {
        this.aiEnabled = aiEnabled;
    }
    public String getDataScope() {
        return dataScope;
    }
src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java
@@ -4,8 +4,10 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDate;
@EqualsAndHashCode(callSuper = true)
@Data
@@ -31,4 +33,15 @@
    @Schema(description = "完成进度")
    private BigDecimal completionStatus;
    @Schema(description = "订单号")
    private String npsNo;
    @Schema(description = "开始日期")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate startDate;
    @Schema(description = "结束日期")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate endDate;
}
src/main/java/com/ruoyi/production/bean/vo/ProductionOperationTaskVo.java
@@ -29,6 +29,15 @@
    @Schema(description = "工单类型 æ­£å¸¸/返工返修")
    private String workOrderType;
    @Schema(description = "生产任务数")
    private Long productionTaskCount;
    @Schema(description = "良品数")
    private BigDecimal goodQuantity;
    @Schema(description = "报废数量")
    private BigDecimal scrapQty;
    @Schema(description = "完成进度")
    private BigDecimal completionStatus;
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java
@@ -36,4 +36,7 @@
    @Schema(description = "完成进度")
    private BigDecimal completionStatus;
    @Schema(description = "是否已退料")
    private Boolean returned;
}
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderWorkOrderDetailVo.java
@@ -1,6 +1,5 @@
package com.ruoyi.production.bean.vo;
import com.ruoyi.production.pojo.ProductionOperationTask;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam;
import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.production.pojo.ProductionProductOutput;
@@ -13,54 +12,63 @@
import java.util.List;
@Data
@Schema(name = "ProductionOrderWorkOrderDetailVo", description = "Production order work order/report/inspect detail")
@Schema(name = "ProductionOrderWorkOrderDetailVo", description = "生产追溯返回对象")
public class ProductionOrderWorkOrderDetailVo {
    @Schema(description = "Production order info")
    @Schema(description = "订单")
    private ProductionOrderVo productionOrder;
    @Schema(description = "Work order list")
    @Schema(description = "工单明细列表")
    private List<WorkOrderDetail> workOrderList;
    @Data
    @Schema(name = "WorkOrderDetail", description = "Work order detail")
    @Schema(name = "WorkOrderDetail", description = "工单明细")
    public static class WorkOrderDetail {
        @Schema(description = "Work order info")
        private ProductionOperationTask workOrder;
        @Schema(description = "工单信息")
        private ProductionOperationTaskVo workOrder;
        @Schema(description = "Report list under current work order")
        @Schema(description = "报工详情列表")
        private List<ReportDetail> reportList;
    }
    @Data
    @Schema(name = "ReportDetail", description = "Production report detail")
    public static class ReportDetail {
        @Schema(description = "Report main info")
        private ProductionProductMain reportMain;
        @Schema(description = "Report output list")
        private List<ProductionProductOutput> reportOutputList;
        @Schema(description = "Report process param list")
        private List<ProductionOrderRoutingOperationParam> reportParamList;
        @Schema(description = "Inspect list under current report")
        @Schema(description = "质检详情列表")
        private List<InspectDetail> inspectList;
    }
    @Data
    @Schema(name = "InspectDetail", description = "Quality inspect detail")
    @Schema(name = "ReportDetail", description = "报工详情")
    public static class ReportDetail {
        @Schema(description = "报工主信息")
        private ProductionProductMain reportMain;
        @Schema(description = "报工产出明细")
        private List<ProductionProductOutput> reportOutputList;
        @Schema(description = "报工工序参数")
        private List<ProductionOrderRoutingOperationParam> reportParamList;
    }
    @Data
    @Schema(name = "InspectDetail", description = "质检详情")
    public static class InspectDetail {
        @Schema(description = "Inspect main info")
        @Schema(description = "报工ID")
        private Long reportId;
        @Schema(description = "报工单号")
        private String reportNo;
        @Schema(description = "报工主信息")
        private ProductionProductMain reportMain;
        @Schema(description = "质检主信息")
        private QualityInspect inspect;
        @Schema(description = "Inspect param list")
        @Schema(description = "质检指标明细")
        private List<QualityInspectParam> inspectParamList;
        @Schema(description = "Inspect attachment list")
        @Schema(description = "质检附件列表")
        private List<QualityInspectFile> inspectFileList;
    }
}
src/main/java/com/ruoyi/production/controller/ProductionOperationTaskController.java
@@ -70,14 +70,16 @@
        return R.ok(productionOperationTaskService.assign(dto));
    }
    /**
     * å·¥å•流转卡下载
     * @param response
     * @param dto
     */
    @PostMapping("/down")
    @Operation(summary = "工单流转卡下载")
    public void down(HttpServletResponse response, @RequestBody ProductionOperationTaskDto dto) {
        productionOperationTaskService.down(response, dto);
    }
    @GetMapping("/getOperation")
    @Operation(summary = "工序详情查询")
    public R<List<ProductionOperationTaskVo>> getOperation(ProductionOperationTaskDto dto) {
        return R.ok(productionOperationTaskService.getOperation(dto));
    }
}
src/main/java/com/ruoyi/production/controller/ProductionOrderController.java
@@ -84,10 +84,10 @@
        return R.ok(productionOrderService.pick(productionOrderId));
    }
    @GetMapping("/workOrder/detail/{productionOrderId}")
    @Operation(summary = "Query work orders/reports/inspects by production order id")
    public R<ProductionOrderWorkOrderDetailVo> getWorkOrderReportInspectDetail(@PathVariable Long productionOrderId) {
        return R.ok(productionOrderService.getWorkOrderReportInspectDetail(productionOrderId));
    @GetMapping("/ordeDetail")
    @Operation(summary = "生产追溯")
    public R<ProductionOrderWorkOrderDetailVo> getWorkOrderReportInspectDetail(ProductionOrderDto productionOrderDto) {
        return R.ok(productionOrderService.getWorkOrderReportInspectDetail(productionOrderDto));
    }
    @Operation(summary = "更新订单状态")
src/main/java/com/ruoyi/production/mapper/ProductionOperationTaskMapper.java
@@ -41,4 +41,6 @@
                                                                           @Param("processIds") List<Long> processIds);
    ProductionOperationTaskDto getProductWorkOrderFlowCard(@Param("id") Long id);
    List<ProductionOperationTaskVo> getOperation(@Param("c") ProductionOperationTaskDto dto);
}
src/main/java/com/ruoyi/production/service/ProductionOperationTaskService.java
@@ -28,4 +28,6 @@
    boolean assign(ProductionOperationTaskDto dto);
    void down(HttpServletResponse response, ProductionOperationTaskDto dto);
    List<ProductionOperationTaskVo> getOperation(ProductionOperationTaskDto dto);
}
src/main/java/com/ruoyi/production/service/ProductionOrderService.java
@@ -32,7 +32,7 @@
    List<ProductionOrderPickVo> pick(Long productionOrderId);
    ProductionOrderWorkOrderDetailVo getWorkOrderReportInspectDetail(Long productionOrderId);
    ProductionOrderWorkOrderDetailVo getWorkOrderReportInspectDetail(ProductionOrderDto productionOrderDto);
    int updateOrder(ProductionOrderDto productionOrderDto);
}
src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java
@@ -347,4 +347,9 @@
                return null;
        }
    }
    @Override
    public List<ProductionOperationTaskVo> getOperation(ProductionOperationTaskDto dto) {
        return baseMapper.getOperation(dto);
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
@@ -673,8 +673,8 @@
        if (dto.getProductModelId() == null) {
            throw new ServiceException("第" + rowNo + "条产品规格ID不能为空");
        }
        if (dto.getPickQuantity() == null || dto.getPickQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("第" + rowNo + "条领料数量必须大于0");
        if (dto.getPickQuantity() == null || dto.getPickQuantity().compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("第" + rowNo + "条领料数量不能小于0");
        }
        if (dto.getPickType() != null && dto.getPickType() != PICK_TYPE_NORMAL && dto.getPickType() != PICK_TYPE_FEEDING) {
            throw new ServiceException("第" + rowNo + "条领料类型只能是1或2");
@@ -688,8 +688,8 @@
        if (dto.getId() == null) {
            throw new ServiceException("第" + rowNo + "条领料ID不能为空");
        }
        if (dto.getFeedingQuantity() == null || dto.getFeedingQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("第" + rowNo + "条本次补料数量必须大于0");
        if (dto.getFeedingQuantity() == null || dto.getFeedingQuantity().compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("第" + rowNo + "条本次补料数量不能小于0");
        }
        if (!isFeedingPick(dto)) {
            throw new ServiceException("第" + rowNo + "条补料类型必须为2");
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -14,8 +14,10 @@
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.constant.StorageAttachmentConstants;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.production.bean.dto.ProductionOperationTaskDto;
import com.ruoyi.production.bean.dto.ProductionOrderDto;
import com.ruoyi.production.bean.vo.ProductionBomStructureVo;
import com.ruoyi.production.bean.vo.ProductionOperationTaskVo;
import com.ruoyi.production.bean.vo.ProductionOrderPickVo;
import com.ruoyi.production.bean.vo.ProductionOrderVo;
import com.ruoyi.production.bean.vo.ProductionPlanVo;
@@ -681,29 +683,33 @@
    }
    @Override
    public ProductionOrderWorkOrderDetailVo getWorkOrderReportInspectDetail(Long productionOrderId) {
        if (productionOrderId == null) {
            throw new ServiceException("productionOrderId can not be null");
        }
    public ProductionOrderWorkOrderDetailVo getWorkOrderReportInspectDetail(ProductionOrderDto dto) {
        Long productionOrderId = resolveProductionOrderId(dto);
        ProductionOrderVo orderInfo = getProductionOrderInfo(productionOrderId);
        if (orderInfo == null) {
            throw new ServiceException("production order not found");
            throw new ServiceException("生产订单不存在");
        }
        ProductionOrderWorkOrderDetailVo detailVo = new ProductionOrderWorkOrderDetailVo();
        detailVo.setProductionOrder(orderInfo);
        List<ProductionOperationTask> workOrderList = productionOperationTaskMapper.selectList(
                Wrappers.<ProductionOperationTask>lambdaQuery()
                        .eq(ProductionOperationTask::getProductionOrderId, productionOrderId)
                        .orderByAsc(ProductionOperationTask::getId));
        ProductionOperationTaskDto taskQuery = new ProductionOperationTaskDto();
        taskQuery.setProductionOrderId(productionOrderId);
        IPage<ProductionOperationTaskVo> workOrderPage = productionOperationTaskMapper.pageProductionOperationTask(
                new Page<ProductionOperationTaskVo>(1, -1), taskQuery);
        List<ProductionOperationTaskVo> workOrderList = workOrderPage == null || workOrderPage.getRecords() == null
                ? Collections.emptyList()
                : workOrderPage.getRecords().stream()
                .filter(Objects::nonNull)
                .sorted(Comparator.comparing(ProductionOperationTaskVo::getId, Comparator.nullsLast(Comparator.naturalOrder())))
                .collect(Collectors.toList());
        if (workOrderList == null || workOrderList.isEmpty()) {
            detailVo.setWorkOrderList(Collections.emptyList());
            return detailVo;
        }
        List<Long> workOrderIdList = workOrderList.stream()
                .map(ProductionOperationTask::getId)
                .map(ProductionOperationTaskVo::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        List<ProductionProductMain> reportMainList = workOrderIdList.isEmpty()
@@ -712,12 +718,12 @@
                Wrappers.<ProductionProductMain>lambdaQuery()
                        .in(ProductionProductMain::getProductionOperationTaskId, workOrderIdList)
                        .orderByAsc(ProductionProductMain::getId));
        Map<Long, List<ProductionProductMain>> reportMainMap = new LinkedHashMap<>();
        Map<Long, List<ProductionProductMain>> reportMainByWorkOrderMap = new LinkedHashMap<>();
        for (ProductionProductMain reportMain : reportMainList) {
            if (reportMain == null || reportMain.getProductionOperationTaskId() == null) {
                continue;
            }
            reportMainMap.computeIfAbsent(reportMain.getProductionOperationTaskId(), k -> new ArrayList<>()).add(reportMain);
            reportMainByWorkOrderMap.computeIfAbsent(reportMain.getProductionOperationTaskId(), key -> new ArrayList<>()).add(reportMain);
        }
        List<Long> reportMainIdList = reportMainList.stream()
@@ -766,7 +772,7 @@
                if (inspect == null || inspect.getProductMainId() == null) {
                    continue;
                }
                inspectMap.computeIfAbsent(inspect.getProductMainId(), k -> new ArrayList<>()).add(inspect);
                inspectMap.computeIfAbsent(inspect.getProductMainId(), key -> new ArrayList<>()).add(inspect);
            }
            List<Long> inspectIdList = inspectList.stream()
@@ -799,42 +805,44 @@
        }
        List<ProductionOrderWorkOrderDetailVo.WorkOrderDetail> workOrderDetailList = new ArrayList<>();
        for (ProductionOperationTask workOrder : workOrderList) {
        for (ProductionOperationTaskVo workOrder : workOrderList) {
            ProductionOrderWorkOrderDetailVo.WorkOrderDetail workOrderDetail = new ProductionOrderWorkOrderDetailVo.WorkOrderDetail();
            workOrderDetail.setWorkOrder(workOrder);
            List<ProductionProductMain> workOrderReportMainList = reportMainMap.get(workOrder.getId());
            if (workOrderReportMainList == null || workOrderReportMainList.isEmpty()) {
            List<ProductionProductMain> workOrderReportMainList = reportMainByWorkOrderMap.getOrDefault(workOrder.getId(), Collections.emptyList());
            if (workOrderReportMainList.isEmpty()) {
                workOrderDetail.setReportList(Collections.emptyList());
                workOrderDetail.setInspectList(Collections.emptyList());
                workOrderDetailList.add(workOrderDetail);
                continue;
            }
            List<ProductionOrderWorkOrderDetailVo.ReportDetail> reportDetailList = new ArrayList<>();
            List<ProductionOrderWorkOrderDetailVo.InspectDetail> inspectDetailList = new ArrayList<>();
            for (ProductionProductMain reportMain : workOrderReportMainList) {
                Long reportMainId = reportMain.getId();
                ProductionOrderWorkOrderDetailVo.ReportDetail reportDetail = new ProductionOrderWorkOrderDetailVo.ReportDetail();
                reportDetail.setReportMain(reportMain);
                reportDetail.setReportOutputList(reportOutputMap.getOrDefault(reportMainId, Collections.emptyList()));
                reportDetail.setReportParamList(reportParamMap.getOrDefault(reportMainId, Collections.emptyList()));
                List<QualityInspect> reportInspectList = inspectMap.get(reportMainId);
                if (reportInspectList == null || reportInspectList.isEmpty()) {
                    reportDetail.setInspectList(Collections.emptyList());
                } else {
                    List<ProductionOrderWorkOrderDetailVo.InspectDetail> inspectDetailList = new ArrayList<>();
                    for (QualityInspect inspect : reportInspectList) {
                        ProductionOrderWorkOrderDetailVo.InspectDetail inspectDetail = new ProductionOrderWorkOrderDetailVo.InspectDetail();
                        inspectDetail.setInspect(inspect);
                        inspectDetail.setInspectParamList(inspectParamMap.getOrDefault(inspect.getId(), Collections.emptyList()));
                        inspectDetail.setInspectFileList(inspectFileMap.getOrDefault(inspect.getId(), Collections.emptyList()));
                        inspectDetailList.add(inspectDetail);
                    }
                    reportDetail.setInspectList(inspectDetailList);
                }
                reportDetailList.add(reportDetail);
                List<QualityInspect> reportInspectList = inspectMap.getOrDefault(reportMainId, Collections.emptyList());
                for (QualityInspect inspect : reportInspectList) {
                    ProductionOrderWorkOrderDetailVo.InspectDetail inspectDetail = new ProductionOrderWorkOrderDetailVo.InspectDetail();
                    inspectDetail.setReportId(reportMainId);
                    inspectDetail.setReportNo(reportMain.getProductNo());
                    inspectDetail.setReportMain(reportMain);
                    inspectDetail.setInspect(inspect);
                    inspectDetail.setInspectParamList(inspectParamMap.getOrDefault(inspect.getId(), Collections.emptyList()));
                    inspectDetail.setInspectFileList(inspectFileMap.getOrDefault(inspect.getId(), Collections.emptyList()));
                    inspectDetailList.add(inspectDetail);
                }
            }
            workOrderDetail.setReportList(reportDetailList);
            workOrderDetail.setInspectList(inspectDetailList);
            workOrderDetailList.add(workOrderDetail);
        }
@@ -842,6 +850,26 @@
        return detailVo;
    }
    private Long resolveProductionOrderId(ProductionOrderDto dto) {
        if (dto == null) {
            throw new ServiceException("请传入生产订单ID或生产订单号");
        }
        if (dto.getId() != null) {
            return dto.getId();
        }
        if (dto.getNpsNo() == null || dto.getNpsNo().trim().isEmpty()) {
            throw new ServiceException("请传入生产订单ID或生产订单号");
        }
        ProductionOrder productionOrder = baseMapper.selectOne(
                Wrappers.<ProductionOrder>lambdaQuery()
                        .eq(ProductionOrder::getNpsNo, dto.getNpsNo().trim())
                        .last("limit 1"));
        if (productionOrder == null || productionOrder.getId() == null) {
            throw new ServiceException("生产订单不存在");
        }
        return productionOrder.getId();
    }
    @Override
    public List<ProductionOrderPickVo> pick(Long productionOrderId) {
        if (productionOrderId == null) {
src/main/java/com/ruoyi/project/system/controller/SysLoginController.java
@@ -12,9 +12,10 @@
import com.ruoyi.project.system.domain.SysMenu;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.domain.vo.SysUserDeptVo;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.service.ISysMenuService;
import com.ruoyi.project.system.service.ISysUserDeptService;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.service.ISysMenuService;
import com.ruoyi.project.system.service.ISysUserDeptService;
import com.ruoyi.project.system.service.ISysUserService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
@@ -40,10 +41,11 @@
{
    private SysLoginService loginService;
    private ISysMenuService menuService;
    private SysPermissionService permissionService;
    private TokenService tokenService;
    private ISysUserDeptService userDeptService;
    private SysDeptMapper sysDeptMapper;
    private SysPermissionService permissionService;
    private TokenService tokenService;
    private ISysUserDeptService userDeptService;
    private ISysUserService userService;
    private SysDeptMapper sysDeptMapper;
    /**
     * ç™»å½•方法
@@ -71,7 +73,17 @@
    public AjaxResult getInfo()
    {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = loginUser.getUser();
        SysUser user = userService.selectUserById(loginUser.getUserId());
        if (user == null)
        {
            user = loginUser.getUser();
        }
        else
        {
            loginUser.setUser(user);
            loginUser.setAiEnabled(user.getAiEnabled());
            tokenService.setLoginUser(loginUser);
        }
        // èŽ·å–å½“å‰ç™»å½•å…¬å¸
        Long tenantId = loginUser.getTenantId();
        if(null != tenantId){
@@ -90,11 +102,12 @@
            loginUser.setPermissions(permissions);
            tokenService.refreshToken(loginUser);
        }
        AjaxResult ajax = AjaxResult.success();
        ajax.put("user", user);
        ajax.put("roles", roles);
        ajax.put("permissions", permissions);
        return ajax;
        AjaxResult ajax = AjaxResult.success();
        ajax.put("user", user);
        ajax.put("aiEnabled", loginUser.getAiEnabled());
        ajax.put("roles", roles);
        ajax.put("permissions", permissions);
        return ajax;
    }
    /**
src/main/java/com/ruoyi/project/system/domain/SysUser.java
@@ -1,7 +1,8 @@
package com.ruoyi.project.system.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.ruoyi.common.xss.Xss;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ruoyi.common.xss.Xss;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType;
import com.ruoyi.framework.aspectj.lang.annotation.Excel.Type;
@@ -59,9 +60,13 @@
    @Excel(name = "账号状态", readConverterExp = "0=正常,1=停用")
    private String status;
    /** åˆ é™¤æ ‡å¿—(0代表存在 2代表删除) */
    private String delFlag;
    /** åˆ é™¤æ ‡å¿—(0代表存在 2代表删除) */
    private String delFlag;
    /** æ˜¯å¦å¼€é€šAI功能(0否 1是) */
    @JsonIgnore
    private Integer aiEnabled;
    /** æœ€åŽç™»å½•IP */
    @Excel(name = "最后登录IP", type = Type.EXPORT)
    private String loginIp;
@@ -250,15 +255,25 @@
        this.status = status;
    }
    public String getDelFlag()
    {
        return delFlag;
    }
    public String getDelFlag()
    {
        return delFlag;
    }
    public void setDelFlag(String delFlag)
    {
        this.delFlag = delFlag;
    }
    public void setDelFlag(String delFlag)
    {
        this.delFlag = delFlag;
    }
    public Integer getAiEnabled()
    {
        return aiEnabled;
    }
    public void setAiEnabled(Integer aiEnabled)
    {
        this.aiEnabled = aiEnabled;
    }
    public String getLoginIp()
    {
@@ -373,9 +388,10 @@
            .append("sex", getSex())
            .append("avatar", getAvatar())
            .append("password", getPassword())
            .append("status", getStatus())
            .append("delFlag", getDelFlag())
            .append("loginIp", getLoginIp())
            .append("status", getStatus())
            .append("delFlag", getDelFlag())
            .append("aiEnabled", getAiEnabled())
            .append("loginIp", getLoginIp())
            .append("loginDate", getLoginDate())
            .append("createBy", getCreateBy())
            .append("createTime", getCreateTime())
src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
@@ -29,7 +29,8 @@
               pm.model as model,
               pm.unit as unit,
               poro.operation_name as operationName,
        ROUND(pot.complete_quantity / pot.plan_quantity * 100, 2) AS completionStatus,
               IFNULL(scrapStat.scrapQty, 0) AS scrapQty,
        ROUND(IFNULL(pot.complete_quantity, 0) / NULLIF(pot.plan_quantity, 0) * 100, 2) AS completionStatus,
        CASE
            WHEN pot.work_order_no LIKE 'FG%' THEN '返工返修'
            ELSE '正常'
@@ -39,9 +40,19 @@
                 left join production_order_routing_operation poro on pot.production_order_routing_operation_id = poro.id
                 left join product_model pm on pm.id = ifnull(poro.product_model_id, po.product_model_id)
                 left join product p on pm.product_id = p.id
                 left join (
            select ppm.production_operation_task_id as taskId,
                   sum(ifnull(ppo.scrap_qty, 0)) as scrapQty
            from production_product_main ppm
                     left join production_product_output ppo on ppo.production_product_main_id = ppm.id
            group by ppm.production_operation_task_id
        ) scrapStat on scrapStat.taskId = pot.id
        <where>
            <if test="c != null and c.id != null">
                and pot.id = #{c.id}
            </if>
            <if test="c != null and c.npsNo != null">
                and po.nps_no like concat('%', #{c.npsNo}, '%')
            </if>
            <if test="c != null and c.productionOrderId != null">
                and pot.production_order_id = #{c.productionOrderId}
@@ -145,4 +156,58 @@
        WHERE pot.id = #{id}
    </select>
    <select id="getOperation" resultType="com.ruoyi.production.bean.vo.ProductionOperationTaskVo">
        select poro.operation_name as operationName,
               count(pot.id) as productionTaskCount,
               sum(ifnull(pot.plan_quantity, 0)) as planQuantity,
               sum(ifnull(pot.complete_quantity, 0)) as completeQuantity,
               sum(ifnull(pot.complete_quantity, 0)) as goodQuantity,
               sum(ifnull(outputStat.scrapQty, 0)) as scrapQty,
               round(
                   case
                       when sum(ifnull(pot.plan_quantity, 0)) = 0 then 0
                       else (sum(ifnull(pot.complete_quantity, 0)) + sum(ifnull(outputStat.scrapQty, 0)))
                           / sum(ifnull(pot.plan_quantity, 0)) * 100
                       end,
                   2
               ) as completionStatus
        from production_operation_task pot
                 left join production_order_routing_operation poro on pot.production_order_routing_operation_id = poro.id
                 left join (
            select ppm.production_operation_task_id as taskId,
                   sum(ifnull(ppo.scrap_qty, 0)) as scrapQty
            from production_product_main ppm
                     left join production_product_output ppo on ppo.production_product_main_id = ppm.id
            group by ppm.production_operation_task_id
        ) outputStat on outputStat.taskId = pot.id
        <where>
            <if test="c != null and c.startDate != null">
                and date(pot.create_time) &gt;= #{c.startDate}
            </if>
            <if test="c != null and c.endDate != null">
                and date(pot.create_time) &lt;= #{c.endDate}
            </if>
            <if test="c != null and c.planStartTime != null">
                and pot.plan_start_time &gt;= #{c.planStartTime}
            </if>
            <if test="c != null and c.planEndTime != null">
                and pot.plan_end_time &lt;= #{c.planEndTime}
            </if>
            <if test="c != null and c.productionOrderId != null">
                and pot.production_order_id = #{c.productionOrderId}
            </if>
            <if test="c != null and c.productionOrderRoutingOperationId != null">
                and pot.production_order_routing_operation_id = #{c.productionOrderRoutingOperationId}
            </if>
            <if test="c != null and c.status != null">
                and pot.status = #{c.status}
            </if>
            <if test="c != null and c.processName != null and c.processName != ''">
                and poro.operation_name like concat('%', #{c.processName}, '%')
            </if>
        </where>
        group by poro.operation_name
        order by min(poro.drag_sort), poro.operation_name
    </select>
</mapper>
src/main/resources/mapper/production/ProductionOrderMapper.xml
@@ -27,6 +27,7 @@
        <result column="productName" property="productName" />
        <result column="model" property="model" />
        <result column="processRouteCode" property="processRouteCode" />
        <result column="returned" property="returned" />
    </resultMap>
    <sql id="ProductionOrderVoColumns">
@@ -52,7 +53,8 @@
        po.is_end_order as endOrder,
        tr.process_route_code as processRouteCode,
        ROUND(po.complete_quantity / po.quantity * 100, 2) AS completionStatus,
        tb.bom_no as bomNo
        tb.bom_no as bomNo,
        pop_return.returned as returned
    </sql>
    <sql id="ProductionOrderVoFrom">
@@ -73,6 +75,12 @@
                 left join product p on pm.product_id = p.id
                 left join technology_routing tr on po.technology_routing_id = tr.id
                 left join technology_bom tb on tb.id = tr.bom_id
                 left join (
            select production_order_id as productionOrderId,
                   if(max(case when ifnull(is_returned, 0) = 1 then 1 else 0 end) = 1, true, false) as returned
            from production_order_pick
            group by production_order_id
        ) pop_return on pop_return.productionOrderId = po.id
    </sql>
    <sql id="ProductionOrderWhere">
src/main/resources/mapper/system/SysUserMapper.xml
@@ -15,6 +15,7 @@
        <result property="password"     column="password"     />
        <result property="status"       column="status"       />
        <result property="delFlag"      column="del_flag"     />
        <result property="aiEnabled"    column="ai_enabled"   />
        <result property="loginIp"      column="login_ip"     />
        <result property="loginDate"    column="login_date"   />
        <result property="createBy"     column="create_by"    />
@@ -48,7 +49,7 @@
    </resultMap>
    <sql id="selectUserVo">
        select u.user_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
        select u.user_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.ai_enabled, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
        r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status,u.tenant_id
        from sys_user u
            left join sys_user_role ur on u.user_id = ur.user_id