Ver Fonte

工单下达

Pengxy há 2 meses atrás
pai
commit
6003ec8f16

+ 5 - 0
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java

@@ -20,6 +20,11 @@ public class LoginUser {
 
     public static final String INFO_KEY_NICKNAME = "nickname";
     public static final String INFO_KEY_DEPT_ID = "deptId";
+    public static final String INFO_KEY_USERNAME = "username";
+    public static final String INFO_KEY_MOBILE = "mobile";
+    public static final String INFO_KEY_EMAIL = "email";
+    public static final String INFO_KEY_SEX = "sex";
+    public static final String INFO_KEY_AVATAR = "avatar";
 
     /**
      * 用户编号

+ 56 - 0
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java

@@ -103,6 +103,62 @@ public class SecurityFrameworkUtils {
     }
 
     /**
+     * 获得当前用户的用户名(登录账号),从上下文中
+     *
+     * @return 用户名
+     */
+    @Nullable
+    public static String getLoginUsername() {
+        LoginUser loginUser = getLoginUser();
+        return loginUser != null ? MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_USERNAME) : null;
+    }
+
+    /**
+     * 获得当前用户的手机号,从上下文中
+     *
+     * @return 手机号
+     */
+    @Nullable
+    public static String getLoginUserMobile() {
+        LoginUser loginUser = getLoginUser();
+        return loginUser != null ? MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_MOBILE) : null;
+    }
+
+    /**
+     * 获得当前用户的邮箱,从上下文中
+     *
+     * @return 邮箱
+     */
+    @Nullable
+    public static String getLoginUserEmail() {
+        LoginUser loginUser = getLoginUser();
+        return loginUser != null ? MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_EMAIL) : null;
+    }
+
+    /**
+     * 获得当前用户的性别,从上下文中
+     *
+     * @return 性别
+     */
+    @Nullable
+    public static Integer getLoginUserSex() {
+        LoginUser loginUser = getLoginUser();
+        String sex = loginUser != null ? MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_SEX) : null;
+        return sex != null ? Integer.parseInt(sex) : null;
+    }
+
+    /**
+     * 获得当前用户的头像,从上下文中
+     *
+     * @return 头像
+     */
+    @Nullable
+    public static String getLoginUserAvatar() {
+        LoginUser loginUser = getLoginUser();
+        return loginUser != null ? MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_AVATAR) : null;
+    }
+
+    /**
      * 获得当前用户的部门编号,从上下文中
      *
      * @return 部门编号

+ 7 - 1
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java

@@ -218,7 +218,13 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
         if (userType.equals(UserTypeEnum.ADMIN.getValue())) {
             AdminUserDO user = adminUserService.getUser(userId);
             return MapUtil.builder(LoginUser.INFO_KEY_NICKNAME, user.getNickname())
-                    .put(LoginUser.INFO_KEY_DEPT_ID, StrUtil.toStringOrNull(user.getDeptId())).build();
+                    .put(LoginUser.INFO_KEY_DEPT_ID, StrUtil.toStringOrNull(user.getDeptId()))
+                    .put(LoginUser.INFO_KEY_USERNAME, user.getUsername())
+                    .put(LoginUser.INFO_KEY_MOBILE, user.getMobile())
+                    .put(LoginUser.INFO_KEY_EMAIL, user.getEmail())
+                    .put(LoginUser.INFO_KEY_SEX, StrUtil.toStringOrNull(user.getSex()))
+                    .put(LoginUser.INFO_KEY_AVATAR, user.getAvatar())
+                    .build();
         } else if (userType.equals(UserTypeEnum.MEMBER.getValue())) {
             // 注意:目前 Member 暂时不读取,可以按需实现
             return Collections.emptyMap();

+ 77 - 0
yudao-order-server/EXTERNAL_API_CONFIG.md

@@ -0,0 +1,77 @@
+# 外部接口配置说明
+
+## 工单下达接口用户账号配置
+
+### 问题背景
+工单下达时需要调用外部接口 `ProduceWorkOrdKittingCheck`,该接口需要传递 `userAccount` 参数。
+
+系统会按以下优先级获取用户账号:
+1. **配置的固定账号**(如果在系统参数中配置了 `order.external.api.userAccount`)
+2. **当前登录用户的用户名**(username,通常是英文账号,如 "admin")
+3. **当前登录用户的昵称**(nickname,可能是中文)
+4. **默认值 "system"**(如果以上都获取不到)
+
+如果用户昵称是中文,可能导致外部系统数据库保存失败,因此建议配置固定的英文账号。
+
+### 解决方案
+可以在系统参数配置中添加一个固定的英文用户账号,用于所有外部接口调用。
+
+### 配置步骤
+
+#### 方式一:通过管理后台配置(推荐)
+1. 登录管理后台
+2. 进入 **系统管理 > 参数配置**
+3. 点击 **新增参数**
+4. 填写以下信息:
+   - **参数名称**: 外部接口用户账号
+   - **参数键名**: `order.external.api.userAccount`
+   - **参数键值**: `admin` (或其他英文账号)
+   - **参数类型**: 系统参数
+   - **备注**: 用于调用外部接口时的用户账号,建议使用英文
+
+#### 方式二:直接在数据库中添加
+```sql
+INSERT INTO infra_config (category, name, `key`, value, type, visible, remark, creator, create_time, updater, update_time, deleted, tenant_id)
+VALUES ('order', '外部接口用户账号', 'order.external.api.userAccount', 'admin', 1, 1, '用于调用外部接口时的用户账号,建议使用英文', 'system', NOW(), 'system', NOW(), 0, 1);
+```
+
+### 配置说明
+- **如果配置了该参数**: 所有外部接口调用都会使用配置的固定账号(如 `admin`)
+- **如果未配置该参数**: 系统会优先使用当前登录用户的用户名(username),如果没有则使用昵称(nickname)
+
+### 新增功能说明
+系统已增强,现在可以从登录上下文中直接获取以下用户信息:
+- `username` - 登录用户名(通常是英文)
+- `nickname` - 用户昵称
+- `mobile` - 手机号
+- `email` - 邮箱
+- `sex` - 性别
+- `avatar` - 头像
+- `deptId` - 部门ID
+
+这些信息在用户登录时会自动加载到 Token 中,无需额外查询数据库。
+
+### 日志查看
+配置后,可以在日志中看到详细的调用信息:
+```
+========== 工单下达接口调用开始 ==========
+工单号: M500000005
+域: 8010
+当前用户昵称: 芋道源码
+配置的固定账号: admin
+最终使用账号: admin
+URL编码后: admin
+完整URL: http://123.60.180.165:8087/api/business/resource-examine/ProduceWorkOrdKittingCheck?workord=M500000005&domain=8010&userAccount=admin
+```
+
+### 测试建议
+1. **不配置固定账号**,使用 admin 账号登录,查看日志确认使用的是 "admin"(username)
+2. **不配置固定账号**,使用中文昵称账号登录,查看日志确认使用的是中文昵称
+3. **配置固定账号为 admin**,使用任意账号登录,查看日志确认使用的是 "admin"
+4. 确认外部接口返回 "ok"
+
+### 注意事项
+- 配置的账号必须是外部系统认可的有效账号
+- 建议使用英文账号,避免中文编码问题
+- 修改配置后无需重启服务,立即生效
+- 如果不配置固定账号,系统会智能选择用户名(优先)或昵称

+ 184 - 0
yudao-order-server/USER_INFO_ENHANCEMENT.md

@@ -0,0 +1,184 @@
+# 用户信息增强功能说明
+
+## 概述
+本次更新增强了系统的用户信息获取能力,现在可以从登录上下文中直接获取更多用户信息,无需额外查询数据库。
+
+## 修改内容
+
+### 1. LoginUser 类新增常量键
+**文件**: `yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java`
+
+新增以下常量:
+```java
+public static final String INFO_KEY_USERNAME = "username";  // 登录用户名
+public static final String INFO_KEY_MOBILE = "mobile";      // 手机号
+public static final String INFO_KEY_EMAIL = "email";        // 邮箱
+public static final String INFO_KEY_SEX = "sex";            // 性别
+public static final String INFO_KEY_AVATAR = "avatar";      // 头像
+```
+
+### 2. OAuth2TokenServiceImpl 增强用户信息构建
+**文件**: `yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java`
+
+在 `buildUserInfo()` 方法中,现在会从 `AdminUserDO` 读取并存储以下信息:
+- username(用户名)
+- mobile(手机号)
+- email(邮箱)
+- sex(性别)
+- avatar(头像)
+- nickname(昵称,原有)
+- deptId(部门ID,原有)
+
+### 3. SecurityFrameworkUtils 新增静态方法
+**文件**: `yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java`
+
+新增以下静态方法:
+```java
+// 获取登录用户名(账号)
+public static String getLoginUsername()
+
+// 获取手机号
+public static String getLoginUserMobile()
+
+// 获取邮箱
+public static String getLoginUserEmail()
+
+// 获取性别
+public static Integer getLoginUserSex()
+
+// 获取头像
+public static String getLoginUserAvatar()
+```
+
+原有方法:
+```java
+// 获取用户ID
+public static Long getLoginUserId()
+
+// 获取昵称
+public static String getLoginUserNickname()
+
+// 获取部门ID
+public static Long getLoginUserDeptId()
+```
+
+## 使用示例
+
+### 在 Service 中使用
+```java
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+
+@Service
+public class YourService {
+    
+    public void someMethod() {
+        // 获取当前登录用户的各种信息
+        Long userId = SecurityFrameworkUtils.getLoginUserId();
+        String username = SecurityFrameworkUtils.getLoginUsername();
+        String nickname = SecurityFrameworkUtils.getLoginUserNickname();
+        String mobile = SecurityFrameworkUtils.getLoginUserMobile();
+        String email = SecurityFrameworkUtils.getLoginUserEmail();
+        Integer sex = SecurityFrameworkUtils.getLoginUserSex();
+        String avatar = SecurityFrameworkUtils.getLoginUserAvatar();
+        Long deptId = SecurityFrameworkUtils.getLoginUserDeptId();
+        
+        // 使用这些信息...
+    }
+}
+```
+
+### 在 Controller 中使用
+```java
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+
+@RestController
+@RequestMapping("/api/example")
+public class ExampleController {
+    
+    @GetMapping("/current-user")
+    public Map<String, Object> getCurrentUser() {
+        Map<String, Object> userInfo = new HashMap<>();
+        userInfo.put("userId", SecurityFrameworkUtils.getLoginUserId());
+        userInfo.put("username", SecurityFrameworkUtils.getLoginUsername());
+        userInfo.put("nickname", SecurityFrameworkUtils.getLoginUserNickname());
+        userInfo.put("mobile", SecurityFrameworkUtils.getLoginUserMobile());
+        userInfo.put("email", SecurityFrameworkUtils.getLoginUserEmail());
+        return userInfo;
+    }
+}
+```
+
+## 优势
+
+### 1. 性能提升
+- **无需额外数据库查询**:用户信息在登录时已加载到 Token 中
+- **减少 API 调用**:不需要调用 `AdminUserApi.getUser()` 获取用户信息
+- **降低数据库压力**:减少对 `system_users` 表的查询
+
+### 2. 代码简化
+**之前的写法**:
+```java
+Long userId = SecurityFrameworkUtils.getLoginUserId();
+AdminUserRespDTO user = adminUserApi.getUser(userId);
+String username = user.getUsername();  // 可能为 null
+String mobile = user.getMobile();
+```
+
+**现在的写法**:
+```java
+String username = SecurityFrameworkUtils.getLoginUsername();
+String mobile = SecurityFrameworkUtils.getLoginUserMobile();
+```
+
+### 3. 一致性保证
+- 所有信息来自同一个 Token,保证数据一致性
+- 避免并发修改导致的数据不一致问题
+
+## 应用场景
+
+1. **日志记录**:记录操作用户的详细信息
+2. **权限控制**:基于用户属性进行权限判断
+3. **业务逻辑**:根据用户信息执行不同的业务逻辑
+4. **外部接口调用**:传递用户信息给外部系统(如工单下达接口)
+5. **审计追踪**:记录用户的完整信息用于审计
+
+## 注意事项
+
+1. **返回值可能为 null**:所有 `getXXX()` 方法都可能返回 null,使用前请做判空处理
+2. **用户信息更新**:用户信息在登录时加载,如果用户信息被修改,需要重新登录才能获取最新信息
+3. **性别字段**:`getLoginUserSex()` 返回 Integer 类型,对应 `SexEnum` 枚举值
+4. **线程安全**:这些方法从 `SecurityContextHolder` 获取信息,是线程安全的
+
+## 实际应用:工单下达接口
+
+在 `WorkOrderServiceImpl` 中的应用示例:
+```java
+private String getCurrentUserName() {
+    // 优先使用登录用户名(username),如果没有则使用昵称(nickname)
+    String username = SecurityFrameworkUtils.getLoginUsername();
+    if (StrUtil.isNotBlank(username)) {
+        return username;
+    }
+    
+    String nickname = SecurityFrameworkUtils.getLoginUserNickname();
+    if (StrUtil.isNotBlank(nickname)) {
+        return nickname;
+    }
+    
+    return "system";
+}
+```
+
+这样可以优先使用英文的 username,避免中文昵称导致的外部接口调用问题。
+
+## 兼容性
+
+- ✅ 完全向后兼容
+- ✅ 不影响现有代码
+- ✅ 可选择性使用新功能
+- ✅ 无需修改数据库结构
+- ✅ 无需修改配置文件
+
+## 总结
+
+本次增强为系统提供了更便捷、高效的用户信息获取方式,特别适合需要频繁获取用户信息的场景。建议在新代码中优先使用 `SecurityFrameworkUtils` 的静态方法,而不是通过 API 查询数据库。

+ 3 - 3
yudao-order-server/src/main/java/cn/iocoder/yudao/module/order/dal/mysql/WorkOrdMasterMapper.java

@@ -44,8 +44,8 @@ public interface WorkOrdMasterMapper extends BaseMapperX<WorkOrdMasterDO> {
     /**
      * 更新工单状态为下达
      */
-    @Update("UPDATE WorkOrdMaster SET Status = 'R', ReleaseDate = GETDATE(), " +
-            "LotSerial = #{lotSerial}, OrdDate = #{ordDate}, UpdateTime = GETDATE(), UpdateUser = #{updateUser} " +
+    @Update("UPDATE WorkOrdMaster SET Status = 'R', ReleaseDate = NOW(), " +
+            "LotSerial = #{lotSerial}, OrdDate = #{ordDate}, UpdateTime = NOW(), UpdateUser = #{updateUser} " +
             "WHERE RecID = #{recId}")
     int updateStatusToRelease(@Param("recId") Long recId, 
                               @Param("lotSerial") String lotSerial,
@@ -56,7 +56,7 @@ public interface WorkOrdMasterMapper extends BaseMapperX<WorkOrdMasterDO> {
      * 更新工单信息(开工日期、完工日期、生产批号)
      */
     @Update("UPDATE WorkOrdMaster SET LotSerial = #{lotSerial}, OrdDate = #{ordDate}, " +
-            "DueDate = #{dueDate}, UpdateTime = GETDATE(), UpdateUser = #{updateUser} " +
+            "DueDate = #{dueDate}, UpdateTime = NOW(), UpdateUser = #{updateUser} " +
             "WHERE WorkOrd = #{workOrd}")
     int updateWorkOrderDates(@Param("workOrd") String workOrd,
                              @Param("lotSerial") String lotSerial,

+ 4 - 1
yudao-order-server/src/main/java/cn/iocoder/yudao/module/order/service/impl/OrderServiceImpl.java

@@ -59,6 +59,7 @@ public class OrderServiceImpl implements OrderService {
         LocalDateTime now = LocalDateTime.now();
         order.setCreateBy(loginUserId);
         order.setCreateTime(now);
+        order.setDeleted(0); // 设置 IsDeleted 字段默认值为 0
         if (loginUserId != null) {
             AdminUserRespDTO user = adminUserApi.getUser(loginUserId);
             if (user != null) {
@@ -246,11 +247,12 @@ public class OrderServiceImpl implements OrderService {
             if (entry.getEntrySeq() == null) {
                 entry.setEntrySeq(i + 1);
             }
-            // 自动填入创建人、创建时间、progress默认为1
+            // 自动填入创建人、创建时间、progress默认为1、IsDeleted默认为0
             entry.setCreateBy(createBy);
             entry.setCreateByName(createByName);
             entry.setCreateTime(createTime);
             entry.setProgress(1);
+            entry.setDeleted(0);
         }
         if (CollUtil.isNotEmpty(entryList)) {
             orderEntryMapper.insertBatch(entryList);
@@ -279,6 +281,7 @@ public class OrderServiceImpl implements OrderService {
                 orderEntryMapper.updateById(entity);
             } else {
                 entity.setId(null);
+                entity.setDeleted(0); // 新增时设置 IsDeleted 默认值为 0
                 orderEntryMapper.insert(entity);
             }
         }

+ 16 - 32
yudao-order-server/src/main/java/cn/iocoder/yudao/module/order/service/impl/WorkOrderServiceImpl.java

@@ -2,14 +2,13 @@ package cn.iocoder.yudao.module.order.service.impl;
 
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
 import cn.iocoder.yudao.module.order.controller.admin.workorder.vo.*;
 import cn.iocoder.yudao.module.order.dal.dataobject.WorkOrdMasterDO;
 import cn.iocoder.yudao.module.order.dal.mysql.WorkOrdMasterMapper;
 import cn.iocoder.yudao.module.order.service.WorkOrderService;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.mzt.logapi.context.LogRecordContext;
 import com.mzt.logapi.starter.annotation.LogRecord;
@@ -28,7 +27,6 @@ import java.time.format.DateTimeFormatter;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 import static cn.iocoder.yudao.module.order.enums.ErrorCodeConstants.WORK_ORDER_NOT_EXISTS;
 import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
 
@@ -47,15 +45,11 @@ public class WorkOrderServiceImpl implements WorkOrderService {
     private ConfigApi configApi;
 
     @Resource
-    private AdminUserApi adminUserApi;
-
-    @Resource
     private RestTemplate restTemplate;
 
     // 外部接口地址
-    private static final String MATERIAL_CHECK_URL = "http://123.60.180.165:9898/api/business/resource-examine/producedayplankittingcheck";
-    private static final String MATERIAL_REQUIREMENT_URL = "http://123.60.180.165:9898/api/business/resource-examine/AutomaticPrAdjustDate";
-
+    private static final String MATERIAL_CHECK_URL = "http://123.60.180.165:8087/api/business/resource-examine/producedayplankittingcheck";
+    private static final String MATERIAL_REQUIREMENT_URL = "http://123.60.180.165:8087/api/business/resource-examine/AutomaticPrAdjustDate";
     @Override
     @TenantIgnore
     public PageResult<WorkOrderPoolRespVO> getWorkOrderPoolPage(WorkOrderPoolPageReqVO pageReqVO) {
@@ -139,7 +133,7 @@ public class WorkOrderServiceImpl implements WorkOrderService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     @TenantIgnore
-    @LogRecord(type = WORK_ORDER_TYPE, subType = WORK_ORDER_RELEASE_SUB_TYPE, bizNo = "{{#releaseReqVO.workOrderNo}}",
+    @LogRecord(type = WORK_ORDER_TYPE, subType = WORK_ORDER_RELEASE_SUB_TYPE, bizNo = "{{#workOrder.recId}}",
             success = WORK_ORDER_RELEASE_SUCCESS)
     public void releaseWorkOrder(WorkOrderReleaseReqVO releaseReqVO) {
         String domain = getDefaultDomain();
@@ -155,10 +149,10 @@ public class WorkOrderServiceImpl implements WorkOrderService {
             LocalDateTime newOrdDate = LocalDateTime.parse(releaseReqVO.getOrdDate() + " 00:00:00",
                     DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
             
-            // 2. 判断开工日期是否小于当前日期
+            // 2. 判断开工日期是否小于当前日期(允许等于当前日期)
             LocalDateTime now = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
             if (newOrdDate.isBefore(now)) {
-                throw new RuntimeException("工单开工日期需调整至今日之后");
+                throw new RuntimeException("工单开工日期不能早于今天");
             }
 
             // 3. 计算旧开工日期与新开工日期的天数差
@@ -197,20 +191,6 @@ public class WorkOrderServiceImpl implements WorkOrderService {
                     week
             );
 
-            // 8. 调用外部接口进行物料齐套检查
-            String userAccount = getCurrentUserName();
-            String url = "http://123.60.180.165:8087/api/business/resource-examine/ProduceWorkOrdKittingCheck" +
-                    "?workord=" + releaseReqVO.getWorkOrderNo() +
-                    "&domain=" + domain +
-                    "&userAccount=" + userAccount;
-
-            String response = restTemplate.getForObject(url, String.class);
-            
-            // 检查返回结果
-            if (!"ok".equalsIgnoreCase(response)) {
-                throw new RuntimeException("工单下达失败:" + response);
-            }
-
             log.info("工单下达成功,工单号:{},生产批号:{},开工日期:{},完工日期:{},周次:{}", 
                     releaseReqVO.getWorkOrderNo(), releaseReqVO.getLotSerial(), 
                     newOrdDate, newDueDate, week);
@@ -298,13 +278,17 @@ public class WorkOrderServiceImpl implements WorkOrderService {
     }
 
     private String getCurrentUserName() {
-        Long loginUserId = getLoginUserId();
-        if (loginUserId != null) {
-            AdminUserRespDTO user = adminUserApi.getUser(loginUserId);
-            if (user != null) {
-                return user.getNickname();
-            }
+        // 优先使用登录用户名(username),如果没有则使用昵称(nickname)
+        String username = SecurityFrameworkUtils.getLoginUsername();
+        if (StrUtil.isNotBlank(username)) {
+            return username;
         }
+        
+        String nickname = SecurityFrameworkUtils.getLoginUserNickname();
+        if (StrUtil.isNotBlank(nickname)) {
+            return nickname;
+        }
+        
         return "system";
     }
 }

+ 20 - 2
yudao-ui/yudao-ui-admin-vue3/src/api/jiaohuo/workorder.ts

@@ -6,9 +6,27 @@ export const getWorkOrderPoolList = (params: any) => {
   return request.get({ url: '/jiaohuo/workorder/pool/list', params })
 }
 
-// 下达工单
+// 下达工单 - 只更新数据库,不调用外部接口
 export const releaseWorkOrder = (data: any) => {
-  return request.post({ url: '/jiaohuo/workorder/release', data })
+  return request.post({ 
+    url: '/jiaohuo/workorder/release', 
+    data
+  })
+}
+
+// 调用外部工单下达接口
+export const callExternalReleaseApi = (params: { workord: string; domain: string; userAccount: string }) => {
+  return axios.get(
+    `http://123.60.180.165:8087/api/business/resource-examine/ProduceWorkOrdKittingCheck`,
+    {
+      params: {
+        workord: params.workord,
+        domain: params.domain,
+        userAccount: params.userAccount
+      },
+      timeout: 120000  // 120秒超时
+    }
+  )
 }
 
 // 检查物料齐套 - 直接调用外部接口

+ 11 - 9
yudao-ui/yudao-ui-admin-vue3/src/views/jiaohuo/components/SalesOrderForm.vue

@@ -89,16 +89,18 @@
           </el-form-item>
         </el-col>
         <el-col :span="8">
-          <el-form-item label="币种" prop="currency">
-            <el-select v-model="formData.currency" placeholder="请选择币种">
-              <el-option label="人民币" value="CNY" />
-              <el-option label="美元" value="USD" />
-            </el-select>
+          <el-form-item label="客户订单号" prop="billFrom">
+            <el-input v-model="formData.billFrom" placeholder="请输入客户订单号" />
           </el-form-item>
         </el-col>
         <el-col :span="8">
-          <el-form-item label="备注" prop="reason">
-            <el-input v-model="formData.reason" placeholder="请输入备注" />
+          <el-form-item label="国家" prop="country">
+            <el-select v-model="formData.country" placeholder="请选择国家">
+              <el-option label="中国" value="中国" />
+              <el-option label="欧洲" value="欧洲" />
+              <el-option label="拉美" value="拉美" />
+              <el-option label="其他" value="其他" />
+            </el-select>
           </el-form-item>
         </el-col>
       </el-row>
@@ -273,8 +275,8 @@ const getDefaultFormData = () => ({
   date: '' as string | number,
   rdate: '' as string | number,
   urgent: '0',
-  currency: 'CNY',
-  reason: '',
+  billFrom: '', // 客户订单号
+  country: '', // 国家
   factoryId: globalConfig.domain, // 使用全局配置
   companyId: globalConfig.companyId, // 使用全局配置
   closed: 0,

+ 63 - 12
yudao-ui/yudao-ui-admin-vue3/src/views/jiaohuo/components/WorkOrderReleaseForm.vue

@@ -60,7 +60,7 @@
 
 <script setup>
 import { ref, reactive } from 'vue'
-import { releaseWorkOrder, updateWorkOrder } from '@/api/jiaohuo/workorder'
+import { releaseWorkOrder, updateWorkOrder, callExternalReleaseApi } from '@/api/jiaohuo/workorder'
 import { ElMessage } from 'element-plus'
 import dayjs from 'dayjs'
 
@@ -101,7 +101,29 @@ const open = (row) => {
   formData.descr = row.descr || ''
   formData.qtyOrded = row.qtyOrded || ''
   formData.lotSerial = row.lotSerial || ''
-  formData.ordDate = row.ordDate ? row.ordDate.substring(0, 10) : dayjs().format('YYYY-MM-DD')
+  
+  // 处理开工日期 - 如果数据库日期小于当前日期则使用当前日期,否则使用数据库日期
+  try {
+    const today = dayjs().startOf('day') // 当前日期(去掉时分秒)
+    
+    if (row.ordDate) {
+      const dbDate = dayjs(row.ordDate).startOf('day') // 数据库日期(去掉时分秒)
+      
+      if (dbDate.isValid()) {
+        // 如果数据库日期小于当前日期,使用当前日期;否则使用数据库日期
+        formData.ordDate = dbDate.isBefore(today) 
+          ? today.format('YYYY-MM-DD') 
+          : dbDate.format('YYYY-MM-DD')
+      } else {
+        formData.ordDate = today.format('YYYY-MM-DD')
+      }
+    } else {
+      formData.ordDate = today.format('YYYY-MM-DD')
+    }
+  } catch (e) {
+    console.error('日期格式化错误:', e)
+    formData.ordDate = dayjs().format('YYYY-MM-DD')
+  }
 }
 
 // 提交
@@ -111,19 +133,48 @@ const handleSubmit = async () => {
 
     submitLoading.value = true
 
-    // 直接调用工单下达接口
-    await releaseWorkOrder({
-      workOrderNo: formData.workOrd,
-      lotSerial: formData.lotSerial,
-      ordDate: formData.ordDate
-    })
+    // 步骤1: 先调用外部接口进行工单下达
+    try {
+      const externalResponse = await callExternalReleaseApi({
+        workord: formData.workOrd,
+        domain: '8010',  // 默认域
+        userAccount: 'admin'  // 可以从用户信息中获取
+      })
+
+      // 检查外部接口返回
+      if (externalResponse.data !== 'ok') {
+        throw new Error('外部系统返回错误:' + externalResponse.data)
+      }
+
+      ElMessage.success('外部系统工单下达成功')
+    } catch (externalError) {
+      // 外部接口调用失败
+      const errorMsg = externalError.response?.data || externalError.message || '外部接口调用失败'
+      ElMessage.error('外部系统工单下达失败:' + errorMsg)
+      throw externalError
+    }
 
-    ElMessage.success('工单下达成功')
-    dialogVisible.value = false
-    emit('success')
+    // 步骤2: 外部接口成功后,调用后端接口更新数据库
+    try {
+      await releaseWorkOrder({
+        workOrderNo: formData.workOrd,
+        lotSerial: formData.lotSerial,
+        ordDate: formData.ordDate
+      })
+
+      ElMessage.success('工单下达完成')
+      dialogVisible.value = false
+      emit('success')
+    } catch (backendError) {
+      // 后端接口调用失败
+      ElMessage.warning('外部系统已下达,但本地数据更新失败:' + (backendError.message || '未知错误'))
+      // 即使后端失败,也关闭对话框并刷新列表
+      dialogVisible.value = false
+      emit('success')
+    }
   } catch (error) {
     if (error !== false) {
-      ElMessage.error('工单下达失败:' + (error.message || error || '未知错误'))
+      console.error('工单下达失败:', error)
     }
   } finally {
     submitLoading.value = false