From 3056f44a6e204adbf6094ac290890d33136da774 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 5 Jan 2026 14:58:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AC=AC=E4=B8=89=E6=96=B9=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E9=85=8D=E7=BD=AE=20+=20=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 系统设置新增第三方数据库配置管理 - 支持SQL Server/MySQL/PostgreSQL - 支持连接测试、启用/禁用 2. 任务子类型新增「检查库存」选项 - 启用后发布任务需要输入商品条码 - 自动查询聚水潭库存信息 3. 发起任务页面集成库存检查 - 库存>1才允许发布任务 - 库存不足提示先申请样品 库存查询配置: - 数据表:jst_inventoryInfo - 仓库条件:wms_co_id='14138566' --- backend/pom.xml | 7 + .../controller/ThirdPartyDbController.java | 88 +++++ .../com/designhub/entity/TaskSubType.java | 14 + .../designhub/entity/ThirdPartyDbConfig.java | 85 +++++ .../mapper/ThirdPartyDbConfigMapper.java | 13 + .../service/ThirdPartyDbService.java | 245 +++++++++++++ database/create_third_party_db_config.sql | 24 ++ frontend/src/api/thirdPartyDb.ts | 94 +++++ frontend/src/views/CreateTask.vue | 141 ++++++++ frontend/src/views/Settings.vue | 330 +++++++++++++++++- 10 files changed, 1036 insertions(+), 5 deletions(-) create mode 100644 backend/src/main/java/com/designhub/controller/ThirdPartyDbController.java create mode 100644 backend/src/main/java/com/designhub/entity/ThirdPartyDbConfig.java create mode 100644 backend/src/main/java/com/designhub/mapper/ThirdPartyDbConfigMapper.java create mode 100644 backend/src/main/java/com/designhub/service/ThirdPartyDbService.java create mode 100644 database/create_third_party_db_config.sql create mode 100644 frontend/src/api/thirdPartyDb.ts diff --git a/backend/pom.xml b/backend/pom.xml index 231a3a8..24026b3 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -174,6 +174,13 @@ thumbnailator 0.4.20 + + + + com.microsoft.sqlserver + mssql-jdbc + 12.4.2.jre11 + diff --git a/backend/src/main/java/com/designhub/controller/ThirdPartyDbController.java b/backend/src/main/java/com/designhub/controller/ThirdPartyDbController.java new file mode 100644 index 0000000..534dada --- /dev/null +++ b/backend/src/main/java/com/designhub/controller/ThirdPartyDbController.java @@ -0,0 +1,88 @@ +package com.designhub.controller; + +import com.designhub.common.Result; +import com.designhub.entity.ThirdPartyDbConfig; +import com.designhub.service.ThirdPartyDbService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 第三方数据库配置控制器 + */ +@Slf4j +@RestController +@RequestMapping("/api/third-party-db") +@RequiredArgsConstructor +@Tag(name = "第三方数据库配置", description = "第三方数据库配置和库存查询相关接口") +public class ThirdPartyDbController { + + private final ThirdPartyDbService thirdPartyDbService; + + @Operation(summary = "获取所有配置") + @GetMapping("/configs") + public Result> getAllConfigs() { + return Result.success(thirdPartyDbService.getAllConfigs()); + } + + @Operation(summary = "获取配置详情") + @GetMapping("/configs/{id}") + public Result getConfigById(@PathVariable Long id) { + return Result.success(thirdPartyDbService.getConfigById(id)); + } + + @Operation(summary = "获取启用的配置") + @GetMapping("/configs/enabled") + public Result getEnabledConfig() { + return Result.success(thirdPartyDbService.getEnabledConfig()); + } + + @Operation(summary = "创建配置") + @PostMapping("/configs") + public Result createConfig(@RequestBody ThirdPartyDbConfig config) { + return Result.success(thirdPartyDbService.createConfig(config)); + } + + @Operation(summary = "更新配置") + @PutMapping("/configs/{id}") + public Result updateConfig(@PathVariable Long id, @RequestBody ThirdPartyDbConfig config) { + config.setId(id); + return Result.success(thirdPartyDbService.updateConfig(config)); + } + + @Operation(summary = "删除配置") + @DeleteMapping("/configs/{id}") + public Result deleteConfig(@PathVariable Long id) { + thirdPartyDbService.deleteConfig(id); + return Result.success(); + } + + @Operation(summary = "测试数据库连接") + @PostMapping("/configs/test") + public Result testConnection(@RequestBody ThirdPartyDbConfig config) { + boolean success = thirdPartyDbService.testConnection(config); + if (success) { + return Result.success(true); + } else { + return Result.error("数据库连接失败,请检查配置"); + } + } + + @Operation(summary = "查询商品库存") + @GetMapping("/inventory/{barcode}") + public Result> checkInventory(@PathVariable String barcode) { + return Result.success(thirdPartyDbService.checkInventory(barcode)); + } + + @Operation(summary = "检查库存是否满足发布任务条件") + @GetMapping("/inventory/check-for-task/{barcode}") + public Result> checkInventoryForTask(@PathVariable String barcode) { + return Result.success(thirdPartyDbService.checkInventoryForTask(barcode)); + } +} + diff --git a/backend/src/main/java/com/designhub/entity/TaskSubType.java b/backend/src/main/java/com/designhub/entity/TaskSubType.java index c90ecd9..c30bfe8 100644 --- a/backend/src/main/java/com/designhub/entity/TaskSubType.java +++ b/backend/src/main/java/com/designhub/entity/TaskSubType.java @@ -45,6 +45,12 @@ public class TaskSubType implements Serializable { */ private Integer isEnabled; + /** + * 是否检查库存:0-否,1-是 + * 如果为1,发布任务时需要输入商品条码并检查库存 + */ + private Integer checkInventory; + /** * 创建时间 */ @@ -106,6 +112,14 @@ public class TaskSubType implements Serializable { this.isEnabled = isEnabled; } + public Integer getCheckInventory() { + return checkInventory; + } + + public void setCheckInventory(Integer checkInventory) { + this.checkInventory = checkInventory; + } + public LocalDateTime getCreateTime() { return createTime; } diff --git a/backend/src/main/java/com/designhub/entity/ThirdPartyDbConfig.java b/backend/src/main/java/com/designhub/entity/ThirdPartyDbConfig.java new file mode 100644 index 0000000..369e890 --- /dev/null +++ b/backend/src/main/java/com/designhub/entity/ThirdPartyDbConfig.java @@ -0,0 +1,85 @@ +package com.designhub.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 第三方数据库配置实体类 + */ +@Data +@TableName("third_party_db_config") +public class ThirdPartyDbConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 配置名称 + */ + private String name; + + /** + * 数据库类型:sqlserver/mysql/postgresql等 + */ + private String dbType; + + /** + * 服务器地址 + */ + private String host; + + /** + * 端口号 + */ + private Integer port; + + /** + * 数据库名称 + */ + private String database; + + /** + * 用户名 + */ + private String username; + + /** + * 密码(加密存储) + */ + private String password; + + /** + * 是否启用:0-禁用 1-启用 + */ + private Integer enabled; + + /** + * 连接超时时间(秒) + */ + private Integer connectionTimeout; + + /** + * 备注 + */ + private String remark; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; +} + diff --git a/backend/src/main/java/com/designhub/mapper/ThirdPartyDbConfigMapper.java b/backend/src/main/java/com/designhub/mapper/ThirdPartyDbConfigMapper.java new file mode 100644 index 0000000..c382065 --- /dev/null +++ b/backend/src/main/java/com/designhub/mapper/ThirdPartyDbConfigMapper.java @@ -0,0 +1,13 @@ +package com.designhub.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.designhub.entity.ThirdPartyDbConfig; +import org.apache.ibatis.annotations.Mapper; + +/** + * 第三方数据库配置Mapper + */ +@Mapper +public interface ThirdPartyDbConfigMapper extends BaseMapper { +} + diff --git a/backend/src/main/java/com/designhub/service/ThirdPartyDbService.java b/backend/src/main/java/com/designhub/service/ThirdPartyDbService.java new file mode 100644 index 0000000..a117970 --- /dev/null +++ b/backend/src/main/java/com/designhub/service/ThirdPartyDbService.java @@ -0,0 +1,245 @@ +package com.designhub.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.designhub.entity.ThirdPartyDbConfig; +import com.designhub.exception.BusinessException; +import com.designhub.mapper.ThirdPartyDbConfigMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.sql.*; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 第三方数据库服务 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ThirdPartyDbService { + + private final ThirdPartyDbConfigMapper configMapper; + + /** + * 获取所有配置 + */ + public List getAllConfigs() { + return configMapper.selectList( + new LambdaQueryWrapper() + .orderByDesc(ThirdPartyDbConfig::getCreateTime) + ); + } + + /** + * 获取配置详情 + */ + public ThirdPartyDbConfig getConfigById(Long id) { + return configMapper.selectById(id); + } + + /** + * 获取启用的配置 + */ + public ThirdPartyDbConfig getEnabledConfig() { + return configMapper.selectOne( + new LambdaQueryWrapper() + .eq(ThirdPartyDbConfig::getEnabled, 1) + .last("LIMIT 1") + ); + } + + /** + * 创建配置 + */ + @Transactional(rollbackFor = Exception.class) + public ThirdPartyDbConfig createConfig(ThirdPartyDbConfig config) { + config.setCreateTime(LocalDateTime.now()); + config.setUpdateTime(LocalDateTime.now()); + configMapper.insert(config); + return config; + } + + /** + * 更新配置 + */ + @Transactional(rollbackFor = Exception.class) + public ThirdPartyDbConfig updateConfig(ThirdPartyDbConfig config) { + config.setUpdateTime(LocalDateTime.now()); + configMapper.updateById(config); + return config; + } + + /** + * 删除配置 + */ + @Transactional(rollbackFor = Exception.class) + public void deleteConfig(Long id) { + configMapper.deleteById(id); + } + + /** + * 测试数据库连接 + */ + public boolean testConnection(ThirdPartyDbConfig config) { + Connection conn = null; + try { + conn = getConnection(config); + return conn != null && !conn.isClosed(); + } catch (Exception e) { + log.error("数据库连接测试失败: {}", e.getMessage()); + return false; + } finally { + closeConnection(conn); + } + } + + /** + * 获取数据库连接 + */ + private Connection getConnection(ThirdPartyDbConfig config) throws SQLException { + String url; + String driverClass; + + switch (config.getDbType().toLowerCase()) { + case "sqlserver": + driverClass = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; + url = String.format("jdbc:sqlserver://%s:%d;databaseName=%s;encrypt=false;trustServerCertificate=true", + config.getHost(), config.getPort(), config.getDatabase()); + break; + case "mysql": + driverClass = "com.mysql.cj.jdbc.Driver"; + url = String.format("jdbc:mysql://%s:%d/%s?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai", + config.getHost(), config.getPort(), config.getDatabase()); + break; + case "postgresql": + driverClass = "org.postgresql.Driver"; + url = String.format("jdbc:postgresql://%s:%d/%s", + config.getHost(), config.getPort(), config.getDatabase()); + break; + default: + throw new BusinessException("不支持的数据库类型: " + config.getDbType()); + } + + try { + Class.forName(driverClass); + } catch (ClassNotFoundException e) { + throw new SQLException("数据库驱动未找到: " + driverClass); + } + + int timeout = config.getConnectionTimeout() != null ? config.getConnectionTimeout() : 10; + DriverManager.setLoginTimeout(timeout); + + return DriverManager.getConnection(url, config.getUsername(), config.getPassword()); + } + + /** + * 关闭连接 + */ + private void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + log.error("关闭数据库连接失败", e); + } + } + } + + /** + * 查询商品库存信息 + * @param barcode 商品条码 + * @return 库存数量,如果未找到返回0 + */ + public Map checkInventory(String barcode) { + ThirdPartyDbConfig config = getEnabledConfig(); + if (config == null) { + throw new BusinessException("未配置第三方数据库或配置未启用"); + } + + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + Map result = new HashMap<>(); + result.put("barcode", barcode); + result.put("quantity", 0); + result.put("found", false); + + try { + conn = getConnection(config); + + // 查询聚水潭库存信息表 + // 条件:wms_co_id='14138566',根据商品条码查询 + String sql = "SELECT sku_id, i_id, name, qty, lock_qty, qty - ISNULL(lock_qty, 0) AS available_qty " + + "FROM jst_inventoryInfo " + + "WHERE wms_co_id = '14138566' AND (sku_id = ? OR i_id = ?)"; + + pstmt = conn.prepareStatement(sql); + pstmt.setString(1, barcode); + pstmt.setString(2, barcode); + + rs = pstmt.executeQuery(); + + if (rs.next()) { + result.put("found", true); + result.put("skuId", rs.getString("sku_id")); + result.put("itemId", rs.getString("i_id")); + result.put("name", rs.getString("name")); + result.put("quantity", rs.getInt("qty")); + result.put("lockQty", rs.getInt("lock_qty")); + result.put("availableQty", rs.getInt("available_qty")); + + log.info("查询到商品库存: barcode={}, name={}, qty={}, available={}", + barcode, rs.getString("name"), rs.getInt("qty"), rs.getInt("available_qty")); + } else { + log.info("未找到商品库存信息: barcode={}", barcode); + } + + } catch (SQLException e) { + log.error("查询库存失败: barcode={}, error={}", barcode, e.getMessage()); + throw new BusinessException("查询库存失败: " + e.getMessage()); + } finally { + try { + if (rs != null) rs.close(); + if (pstmt != null) pstmt.close(); + } catch (SQLException e) { + log.error("关闭资源失败", e); + } + closeConnection(conn); + } + + return result; + } + + /** + * 检查库存是否足够(库存大于1才能发布任务) + * @param barcode 商品条码 + * @return 检查结果 + */ + public Map checkInventoryForTask(String barcode) { + Map inventoryResult = checkInventory(barcode); + Map result = new HashMap<>(inventoryResult); + + boolean found = (boolean) inventoryResult.get("found"); + int availableQty = found ? (int) inventoryResult.getOrDefault("availableQty", 0) : 0; + + // 库存大于1才允许发布 + boolean canPublish = found && availableQty > 1; + result.put("canPublish", canPublish); + + if (!found) { + result.put("message", "未找到该商品库存信息,请先申请样品"); + } else if (availableQty <= 1) { + result.put("message", "商品库存不足(可用库存: " + availableQty + "),请先申请样品"); + } else { + result.put("message", "库存充足(可用库存: " + availableQty + "),可以发布任务"); + } + + return result; + } +} + diff --git a/database/create_third_party_db_config.sql b/database/create_third_party_db_config.sql new file mode 100644 index 0000000..e397707 --- /dev/null +++ b/database/create_third_party_db_config.sql @@ -0,0 +1,24 @@ +-- 创建第三方数据库配置表 +CREATE TABLE IF NOT EXISTS third_party_db_config ( + id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', + name VARCHAR(100) NOT NULL COMMENT '配置名称', + db_type VARCHAR(50) NOT NULL DEFAULT 'sqlserver' COMMENT '数据库类型:sqlserver/mysql/postgresql', + host VARCHAR(255) NOT NULL COMMENT '服务器地址', + port INT NOT NULL DEFAULT 1433 COMMENT '端口号', + `database` VARCHAR(100) NOT NULL COMMENT '数据库名称', + username VARCHAR(100) NOT NULL COMMENT '用户名', + password VARCHAR(255) NOT NULL COMMENT '密码', + enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用:0-禁用 1-启用', + connection_timeout INT DEFAULT 10 COMMENT '连接超时时间(秒)', + remark VARCHAR(500) COMMENT '备注', + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='第三方数据库配置表'; + +-- 插入默认配置 +INSERT INTO third_party_db_config (name, db_type, host, port, `database`, username, password, enabled, connection_timeout, remark) +VALUES ('聚水潭数据中台', 'sqlserver', '218.93.191.16', 1433, 'JSDataMiddlePlatform', 'nanting', 'yq1_xL?:=E>G', 1, 10, '聚水潭商品库存查询'); + +-- 在任务子类型表中添加检查库存字段 +ALTER TABLE task_sub_type ADD COLUMN check_inventory TINYINT(1) DEFAULT 0 COMMENT '是否检查库存:0-否 1-是' AFTER enabled; + diff --git a/frontend/src/api/thirdPartyDb.ts b/frontend/src/api/thirdPartyDb.ts new file mode 100644 index 0000000..1e77c4a --- /dev/null +++ b/frontend/src/api/thirdPartyDb.ts @@ -0,0 +1,94 @@ +import request from '@/utils/request' + +export interface ThirdPartyDbConfig { + id?: number + name: string + dbType: string + host: string + port: number + database: string + username: string + password: string + enabled: number + connectionTimeout?: number + remark?: string + createTime?: string + updateTime?: string +} + +export interface InventoryResult { + barcode: string + found: boolean + quantity: number + availableQty?: number + skuId?: string + itemId?: string + name?: string + lockQty?: number + canPublish?: boolean + message?: string +} + +/** + * 获取所有第三方数据库配置 + */ +export const getAllConfigs = () => { + return request.get('/third-party-db/configs') +} + +/** + * 获取配置详情 + */ +export const getConfigById = (id: number) => { + return request.get(`/third-party-db/configs/${id}`) +} + +/** + * 获取启用的配置 + */ +export const getEnabledConfig = () => { + return request.get('/third-party-db/configs/enabled') +} + +/** + * 创建配置 + */ +export const createConfig = (config: ThirdPartyDbConfig) => { + return request.post('/third-party-db/configs', config) +} + +/** + * 更新配置 + */ +export const updateConfig = (id: number, config: ThirdPartyDbConfig) => { + return request.put(`/third-party-db/configs/${id}`, config) +} + +/** + * 删除配置 + */ +export const deleteConfig = (id: number) => { + return request.delete(`/third-party-db/configs/${id}`) +} + +/** + * 测试数据库连接 + */ +export const testConnection = (config: ThirdPartyDbConfig) => { + return request.post('/third-party-db/configs/test', config) +} + +/** + * 查询商品库存 + */ +export const checkInventory = (barcode: string) => { + return request.get(`/third-party-db/inventory/${barcode}`) +} + +/** + * 检查库存是否满足发布任务条件 + */ +export const checkInventoryForTask = (barcode: string) => { + return request.get(`/third-party-db/inventory/check-for-task/${barcode}`) +} + diff --git a/frontend/src/views/CreateTask.vue b/frontend/src/views/CreateTask.vue index 1cdf962..55e5fbb 100644 --- a/frontend/src/views/CreateTask.vue +++ b/frontend/src/views/CreateTask.vue @@ -202,6 +202,54 @@ + + + + + + 检查库存 + + + + +
+ +
+
+ 商品名称: + {{ inventoryCheckResult.name || '-' }} +
+
+ 可用库存: + + {{ inventoryCheckResult.availableQty }} + +
+
+
+
+ @@ -437,6 +485,7 @@ import { getSystemConfigsMapByGroup } from '@/api/system' import { uploadFilesToS3 } from '@/api/file' import type { FormInstance } from 'ant-design-vue' import type { DemandType, Brand, Platform, Category, TaskSubType } from '@/api/config' +import { checkInventoryForTask, type InventoryResult } from '@/api/thirdPartyDb' import { useAuthStore } from '@/stores/auth' import { useConfigStore } from '@/stores/config' @@ -518,10 +567,49 @@ const formData = reactive({ previewImage: '', // 预览图URL remark: '', // 备注信息 releaseType: 'default', + productBarcode: '', // 商品条码(库存检查用) // receiverId: undefined, // 已移除,改用子任务指定 isUrgent: false }) +// 库存检查相关 +const checkingInventory = ref(false) +const inventoryCheckResult = ref(null) + +// 判断当前选择的任务子类型是否需要检查库存 +const selectedSubTypeNeedsInventoryCheck = computed(() => { + if (!formData.taskSubType) return false + const selectedSubType = taskSubTypes.value.find(st => st.name === formData.taskSubType) + return selectedSubType?.checkInventory === 1 +}) + +// 检查库存 +const handleCheckInventory = async () => { + if (!formData.productBarcode) { + message.warning('请输入商品条码') + return + } + + checkingInventory.value = true + inventoryCheckResult.value = null + + try { + const response = await checkInventoryForTask(formData.productBarcode) + inventoryCheckResult.value = response.data + + if (response.data.canPublish) { + message.success('库存检查通过,可以发布任务') + } else { + message.error(response.data.message || '库存不足,请先申请样品') + } + } catch (error: any) { + console.error('检查库存失败:', error) + message.error(error.response?.data?.message || '检查库存失败') + } finally { + checkingInventory.value = false + } +} + // 表单验证规则 const formRules = { title: [ @@ -826,6 +914,22 @@ const handleSubmit = async () => { return } + // 检查库存(如果需要) + if (selectedSubTypeNeedsInventoryCheck.value) { + if (!formData.productBarcode) { + message.error('请输入商品条码') + return + } + if (!inventoryCheckResult.value) { + message.error('请先检查库存') + return + } + if (!inventoryCheckResult.value.canPublish) { + message.error('库存不足,请先申请样品后再发布任务') + return + } + } + // 检查积分余额(管理员跳过) if (!isAdmin.value && estimatedScore.value > teamScore.value) { message.error('团队积分余额不足') @@ -1917,5 +2021,42 @@ onUnmounted(() => { } } } + +// 库存检查结果样式 +.inventory-result { + margin-top: 12px; + + .inventory-details { + margin-top: 12px; + padding: 12px; + background: rgba(255, 255, 255, 0.02); + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.1); + + .detail-item { + display: flex; + margin-bottom: 8px; + + &:last-child { + margin-bottom: 0; + } + + .label { + color: $text-tertiary; + width: 100px; + flex-shrink: 0; + } + + .value { + color: $text-primary; + + &.low-stock { + color: #ff4d4f; + font-weight: 600; + } + } + } + } +} diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue index 0fb152a..99c29e0 100644 --- a/frontend/src/views/Settings.vue +++ b/frontend/src/views/Settings.vue @@ -1042,6 +1042,81 @@ + + + +
+
+

第三方数据库配置

+ + + 添加配置 + +
+ + + + + + +
+
@@ -1132,6 +1207,13 @@ placeholder="排序值越小越靠前" /> + + +
+ + 开启后,发布此类型任务时需要输入商品条码并检查库存,库存不足则无法发布任务 +
+
@@ -1478,6 +1560,88 @@
+ + + + + + + + + + + + SQL Server + MySQL + PostgreSQL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1535,6 +1699,14 @@ import { activateS3Config, type S3Config } from '@/api/s3' +import { + getAllConfigs as getAllThirdPartyDbConfigs, + createConfig as createThirdPartyDbConfig, + updateConfig as updateThirdPartyDbConfig, + deleteConfig as deleteThirdPartyDbConfig, + testConnection as testThirdPartyDbConnection, + type ThirdPartyDbConfig +} from '@/api/thirdPartyDb' import request from '@/utils/request' type LimitPeriod = 'DAILY' | 'WEEKLY' | 'MONTHLY' @@ -1597,7 +1769,8 @@ const modals = reactive({ category: false, department: false, scorePool: false, - s3Config: false + s3Config: false, + thirdPartyDb: false }) const demandTypes = ref([]) @@ -1651,6 +1824,33 @@ const s3ConfigColumns = [ { title: '操作', key: 'actions', width: 200 } ] +// 第三方数据库配置相关 +const thirdPartyDbConfigs = ref([]) +const loadingThirdPartyDb = ref(false) +const thirdPartyDbForm = reactive({ + name: '', + dbType: 'sqlserver', + host: '', + port: 1433, + database: '', + username: '', + password: '', + enabled: true, + connectionTimeout: 10, + remark: '' +}) + +const thirdPartyDbColumns = [ + { title: 'ID', dataIndex: 'id', key: 'id', width: 60 }, + { title: '配置名称', dataIndex: 'name', key: 'name' }, + { title: '数据库类型', dataIndex: 'dbType', key: 'dbType', width: 120 }, + { title: '连接信息', key: 'connection' }, + { title: '用户名', dataIndex: 'username', key: 'username', width: 100 }, + { title: '密码', key: 'password', width: 100 }, + { title: '状态', key: 'enabled', width: 100 }, + { title: '操作', key: 'actions', width: 260 } +] + const defaultDepartmentForm = (): DepartmentForm => ({ deptName: '', deptCode: '', @@ -2188,8 +2388,8 @@ const showDemandTypeModal = async (item?: DemandType) => { const showTaskSubTypeModal = (item?: TaskSubType) => { editingItem.value = item formData.taskSubType = item - ? { ...item } - : { sortOrder: 0, isEnabled: 1 } + ? { ...item, checkInventory: item.checkInventory === 1 } + : { sortOrder: 0, isEnabled: 1, checkInventory: false } modals.taskSubType = true } @@ -2292,12 +2492,13 @@ const saveDemandType = async () => { const saveTaskSubType = async () => { try { - const saveData: Partial = { + const saveData: Partial & { checkInventory?: number } = { name: formData.taskSubType.name, code: formData.taskSubType.code, description: formData.taskSubType.description, sortOrder: formData.taskSubType.sortOrder ?? 0, - isEnabled: formData.taskSubType.isEnabled ?? 1 + isEnabled: formData.taskSubType.isEnabled ?? 1, + checkInventory: (formData.taskSubType as any).checkInventory ? 1 : 0 } if (editingItem.value) { @@ -3174,6 +3375,121 @@ const handleActivateS3Config = async (id: number) => { } } +// 第三方数据库配置相关方法 +const loadThirdPartyDbConfigs = async () => { + loadingThirdPartyDb.value = true + try { + const response = await getAllThirdPartyDbConfigs() + thirdPartyDbConfigs.value = response.data || [] + } catch (error: any) { + console.error('加载第三方数据库配置失败:', error) + message.error(error.response?.data?.message || '加载第三方数据库配置失败') + } finally { + loadingThirdPartyDb.value = false + } +} + +const showThirdPartyDbModal = (config?: ThirdPartyDbConfig) => { + if (config) { + Object.assign(thirdPartyDbForm, { + ...config, + enabled: config.enabled === 1, + password: '' // 编辑时不显示密码 + }) + } else { + Object.assign(thirdPartyDbForm, { + id: undefined, + name: '', + dbType: 'sqlserver', + host: '', + port: 1433, + database: '', + username: '', + password: '', + enabled: true, + connectionTimeout: 10, + remark: '' + }) + } + modals.thirdPartyDb = true +} + +const handleSaveThirdPartyDb = async () => { + try { + const data = { + ...thirdPartyDbForm, + enabled: thirdPartyDbForm.enabled ? 1 : 0 + } + + if (thirdPartyDbForm.id) { + await updateThirdPartyDbConfig(thirdPartyDbForm.id, data) + message.success('更新数据库配置成功') + } else { + await createThirdPartyDbConfig(data) + message.success('创建数据库配置成功') + } + modals.thirdPartyDb = false + loadThirdPartyDbConfigs() + } catch (error: any) { + console.error('保存数据库配置失败:', error) + message.error(error.response?.data?.message || '保存数据库配置失败') + } +} + +const handleDeleteThirdPartyDb = async (id: number) => { + try { + await deleteThirdPartyDbConfig(id) + message.success('删除数据库配置成功') + loadThirdPartyDbConfigs() + } catch (error: any) { + console.error('删除数据库配置失败:', error) + message.error(error.response?.data?.message || '删除数据库配置失败') + } +} + +const handleTestThirdPartyDb = async (config: ThirdPartyDbConfig) => { + try { + message.loading({ content: '正在测试连接...', key: 'testConn' }) + const response = await testThirdPartyDbConnection(config) + if (response.data) { + message.success({ content: '数据库连接成功', key: 'testConn' }) + } else { + message.error({ content: '数据库连接失败', key: 'testConn' }) + } + } catch (error: any) { + console.error('测试连接失败:', error) + message.error({ content: error.response?.data?.message || '连接测试失败', key: 'testConn' }) + } +} + +const handleEnableThirdPartyDb = async (id: number) => { + try { + const config = thirdPartyDbConfigs.value.find(c => c.id === id) + if (config) { + await updateThirdPartyDbConfig(id, { ...config, enabled: 1 }) + message.success('已启用配置') + loadThirdPartyDbConfigs() + } + } catch (error: any) { + console.error('启用配置失败:', error) + message.error(error.response?.data?.message || '启用配置失败') + } +} + +const handleDisableThirdPartyDb = async (id: number) => { + try { + const config = thirdPartyDbConfigs.value.find(c => c.id === id) + if (config) { + await updateThirdPartyDbConfig(id, { ...config, enabled: 0 }) + message.success('已禁用配置') + loadThirdPartyDbConfigs() + } + } catch (error: any) { + console.error('禁用配置失败:', error) + message.error(error.response?.data?.message || '禁用配置失败') + } +} + watch(activeTab, (value) => { // 切换到用户与部门 tab 时,始终刷新数据以获取最新状态 if (value === 'users') { @@ -3195,6 +3511,10 @@ watch(activeTab, (value) => { if (value === 's3-config') { loadS3Configs() } + // 切换到第三方数据库配置 tab 时,加载配置 + if (value === 'third-party-db') { + loadThirdPartyDbConfigs() + } })