文件上传下载修正
This commit is contained in:
@@ -23,7 +23,7 @@ CREATE TABLE file.tb_sys_file (
|
|||||||
type VARCHAR(50) DEFAULT NULL, -- 文件类型
|
type VARCHAR(50) DEFAULT NULL, -- 文件类型
|
||||||
storage_type VARCHAR(50) DEFAULT NULL, -- 存储类型
|
storage_type VARCHAR(50) DEFAULT NULL, -- 存储类型
|
||||||
mime_type VARCHAR(255) DEFAULT NULL, -- MIME 类型
|
mime_type VARCHAR(255) DEFAULT NULL, -- MIME 类型
|
||||||
url VARCHAR(500) DEFAULT NULL, -- 文件访问 URL
|
url VARCHAR(500) DEFAULT NULL, -- 后端下载接口路径(保留用于扩展,建议使用 /api/file/download/{fileId})
|
||||||
status VARCHAR(50) DEFAULT NULL, -- 文件状态
|
status VARCHAR(50) DEFAULT NULL, -- 文件状态
|
||||||
module VARCHAR(100) DEFAULT NULL, -- 所属模块
|
module VARCHAR(100) DEFAULT NULL, -- 所属模块
|
||||||
business_id VARCHAR(50) DEFAULT NULL, -- 业务ID
|
business_id VARCHAR(50) DEFAULT NULL, -- 业务ID
|
||||||
@@ -60,7 +60,7 @@ COMMENT ON COLUMN file.tb_sys_file.size IS '文件大小(字节)';
|
|||||||
COMMENT ON COLUMN file.tb_sys_file.type IS '文件类型';
|
COMMENT ON COLUMN file.tb_sys_file.type IS '文件类型';
|
||||||
COMMENT ON COLUMN file.tb_sys_file.storage_type IS '存储类型';
|
COMMENT ON COLUMN file.tb_sys_file.storage_type IS '存储类型';
|
||||||
COMMENT ON COLUMN file.tb_sys_file.mime_type IS 'MIME 类型';
|
COMMENT ON COLUMN file.tb_sys_file.mime_type IS 'MIME 类型';
|
||||||
COMMENT ON COLUMN file.tb_sys_file.url IS '文件访问 URL';
|
COMMENT ON COLUMN file.tb_sys_file.url IS '后端下载接口路径(保留用于扩展,建议使用 /api/file/download/{fileId})';
|
||||||
COMMENT ON COLUMN file.tb_sys_file.status IS '文件状态';
|
COMMENT ON COLUMN file.tb_sys_file.status IS '文件状态';
|
||||||
COMMENT ON COLUMN file.tb_sys_file.module IS '所属模块';
|
COMMENT ON COLUMN file.tb_sys_file.module IS '所属模块';
|
||||||
COMMENT ON COLUMN file.tb_sys_file.business_id IS '业务ID';
|
COMMENT ON COLUMN file.tb_sys_file.business_id IS '业务ID';
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.xyzh.api.ai.service.DifyProxyService;
|
|||||||
import org.xyzh.api.ai.service.KnowledgeService;
|
import org.xyzh.api.ai.service.KnowledgeService;
|
||||||
import org.xyzh.api.ai.dto.TbKnowledge;
|
import org.xyzh.api.ai.dto.TbKnowledge;
|
||||||
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
||||||
|
import org.xyzh.api.ai.vo.KnowledgeFileVO;
|
||||||
import org.xyzh.common.core.domain.ResultDomain;
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
import org.xyzh.common.core.page.PageRequest;
|
import org.xyzh.common.core.page.PageRequest;
|
||||||
import org.xyzh.common.utils.validation.ValidationResult;
|
import org.xyzh.common.utils.validation.ValidationResult;
|
||||||
@@ -161,19 +162,16 @@ public class KnowledgeController {
|
|||||||
// ====================== 文件管理 ======================
|
// ====================== 文件管理 ======================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 获取知识库文档列表
|
* @description 获取知识库文档列表(含文件详细信息)
|
||||||
* @param knowledgeId 知识库id
|
* @param pageRequest 分页请求
|
||||||
* @author yslg
|
* @author yslg
|
||||||
* @since 2025-12-18
|
* @since 2025-12-20
|
||||||
*/
|
*/
|
||||||
@PreAuthorize("hasAuthority('ai:knowledge:file:view')")
|
@PreAuthorize("hasAuthority('ai:knowledge:file:view')")
|
||||||
@GetMapping("/{knowledgeId}/documents")
|
@PostMapping("/{knowledgeId}/documents")
|
||||||
public ResultDomain<Map<String, Object>> getDocumentList(
|
public ResultDomain<KnowledgeFileVO> getDocumentList(@RequestBody PageRequest<TbKnowledgeFile> pageRequest) {
|
||||||
@PathVariable("knowledgeId") @NotBlank String knowledgeId,
|
logger.info("获取文档列表: knowledgeId={}", pageRequest.getFilter().getKnowledgeId());
|
||||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
return knowledgeService.getDocumentList(pageRequest);
|
||||||
@RequestParam(value = "limit", defaultValue = "20") Integer limit) {
|
|
||||||
logger.info("获取文档列表: knowledgeId={}", knowledgeId);
|
|
||||||
return knowledgeService.getDocumentList(knowledgeId, page, limit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.xyzh.ai.mapper;
|
|||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
||||||
|
import org.xyzh.api.ai.vo.KnowledgeFileVO;
|
||||||
import org.xyzh.common.core.page.PageParam;
|
import org.xyzh.common.core.page.PageParam;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -41,9 +42,9 @@ public interface TbKnowledgeFileMapper {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据知识库ID查询文件列表
|
* 根据知识库ID查询文件列表(关联文件详细信息)
|
||||||
*/
|
*/
|
||||||
List<TbKnowledgeFile> selectFilesByKnowledgeId(@Param("knowledgeId") String knowledgeId);
|
List<KnowledgeFileVO> selectFilesByKnowledgeId(@Param("knowledgeId") String knowledgeId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据文件根ID查询所有版本
|
* 根据文件根ID查询所有版本
|
||||||
@@ -51,9 +52,9 @@ public interface TbKnowledgeFileMapper {
|
|||||||
List<TbKnowledgeFile> selectFileVersions(@Param("fileRootId") String fileRootId);
|
List<TbKnowledgeFile> selectFileVersions(@Param("fileRootId") String fileRootId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询知识库文件
|
* 分页查询知识库文件(关联文件详细信息)
|
||||||
*/
|
*/
|
||||||
List<TbKnowledgeFile> selectFilePage(
|
List<KnowledgeFileVO> selectFilePage(
|
||||||
@Param("knowledgeId") String knowledgeId,
|
@Param("knowledgeId") String knowledgeId,
|
||||||
@Param("pageParam") PageParam pageParam
|
@Param("pageParam") PageParam pageParam
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,11 +18,14 @@ import org.xyzh.api.ai.dto.TbKnowledge;
|
|||||||
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
||||||
import org.xyzh.api.ai.service.AIFileUploadService;
|
import org.xyzh.api.ai.service.AIFileUploadService;
|
||||||
import org.xyzh.api.ai.service.KnowledgeService;
|
import org.xyzh.api.ai.service.KnowledgeService;
|
||||||
|
import org.xyzh.api.ai.vo.KnowledgeFileVO;
|
||||||
import org.xyzh.api.file.dto.TbSysFileDTO;
|
import org.xyzh.api.file.dto.TbSysFileDTO;
|
||||||
import org.xyzh.api.file.service.FileService;
|
import org.xyzh.api.file.service.FileService;
|
||||||
import org.xyzh.common.core.domain.LoginDomain;
|
import org.xyzh.common.core.domain.LoginDomain;
|
||||||
import org.xyzh.common.core.domain.ResultDomain;
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
|
import org.xyzh.common.core.page.PageDomain;
|
||||||
import org.xyzh.common.core.page.PageParam;
|
import org.xyzh.common.core.page.PageParam;
|
||||||
|
import org.xyzh.common.core.page.PageRequest;
|
||||||
import org.xyzh.common.utils.id.IdUtil;
|
import org.xyzh.common.utils.id.IdUtil;
|
||||||
import org.xyzh.common.auth.utils.LoginUtil;
|
import org.xyzh.common.auth.utils.LoginUtil;
|
||||||
|
|
||||||
@@ -405,32 +408,43 @@ public class KnowledgeServiceImpl implements KnowledgeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 获取知识库文档列表(从Dify获取)
|
* @description 获取知识库文档列表(查询本地数据库文件)
|
||||||
* @param knowledgeId 知识库ID
|
* @param pageRequest 分页请求,filter 中包含 knowledgeId
|
||||||
* @param page 页码(从1开始)
|
* @return ResultDomain<KnowledgeFileVO> 文档列表
|
||||||
* @param limit 每页数量
|
|
||||||
* @return ResultDomain<Map<String, Object>> 文档列表
|
|
||||||
* @author yslg
|
* @author yslg
|
||||||
* @since 2025-12-18
|
* @since 2025-12-20
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ResultDomain<Map<String, Object>> getDocumentList(String knowledgeId, Integer page, Integer limit) {
|
public ResultDomain<KnowledgeFileVO> getDocumentList(PageRequest<TbKnowledgeFile> pageRequest) {
|
||||||
TbKnowledge knowledge = knowledgeMapper.selectKnowledgeById(knowledgeId);
|
TbKnowledgeFile filter = pageRequest.getFilter();
|
||||||
if (knowledge == null || !StringUtils.hasText(knowledge.getDifyDatasetId())) {
|
if (filter == null || !StringUtils.hasText(filter.getKnowledgeId())) {
|
||||||
return ResultDomain.failure("知识库不存在或未关联Dify");
|
return ResultDomain.failure("知识库ID不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String knowledgeId = filter.getKnowledgeId();
|
||||||
|
|
||||||
|
// 验证知识库是否存在
|
||||||
|
TbKnowledge knowledge = knowledgeMapper.selectKnowledgeById(knowledgeId);
|
||||||
|
if (knowledge == null) {
|
||||||
|
return ResultDomain.failure("知识库不存在");
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DocumentListResponse response = difyApiClient.listDocuments(knowledge.getDifyDatasetId(), page, limit);
|
// 分页查询知识库文件(已通过 SQL JOIN 关联文件详细信息)
|
||||||
Map<String, Object> result = new HashMap<>();
|
PageParam pageParam = pageRequest.getPageParam();
|
||||||
result.put("data", response.getData());
|
List<KnowledgeFileVO> files = knowledgeFileMapper.selectFilePage(knowledgeId, pageParam);
|
||||||
result.put("total", response.getTotal());
|
long total = knowledgeFileMapper.countFiles(knowledgeId);
|
||||||
result.put("page", response.getPage());
|
|
||||||
result.put("limit", response.getLimit());
|
// 设置总数
|
||||||
return ResultDomain.success("查询成功", result);
|
pageParam.setTotal((int) total);
|
||||||
|
|
||||||
|
// 创建分页结果
|
||||||
|
PageDomain<KnowledgeFileVO> pageDomain = new PageDomain<>(pageParam, files);
|
||||||
|
|
||||||
|
return ResultDomain.success("查询成功", pageDomain);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("获取Dify文档列表失败: {}", e.getMessage(), e);
|
logger.error("获取知识库文件列表失败: knowledgeId={}, error={}", knowledgeId, e.getMessage(), e);
|
||||||
return ResultDomain.failure("获取文档列表失败: " + e.getMessage());
|
return ResultDomain.failure("获取文件列表失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,26 @@
|
|||||||
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
|
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
<resultMap id="KnowledgeFileVOResultMap" type="org.xyzh.api.ai.vo.KnowledgeFileVO">
|
||||||
|
<result column="optsn" property="optsn" jdbcType="VARCHAR"/>
|
||||||
|
<result column="knowledge_id" property="knowledgeId" jdbcType="VARCHAR"/>
|
||||||
|
<result column="file_root_id" property="fileRootId" jdbcType="VARCHAR"/>
|
||||||
|
<result column="file_id" property="fileId" jdbcType="VARCHAR"/>
|
||||||
|
<result column="dify_file_id" property="difyFileId" jdbcType="VARCHAR"/>
|
||||||
|
<result column="version" property="version" jdbcType="INTEGER"/>
|
||||||
|
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
|
||||||
|
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
|
||||||
|
<result column="delete_time" property="deleteTime" jdbcType="TIMESTAMP"/>
|
||||||
|
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
|
||||||
|
<result column="file_name" property="fileName" jdbcType="VARCHAR"/>
|
||||||
|
<result column="file_path" property="filePath" jdbcType="VARCHAR"/>
|
||||||
|
<result column="file_size" property="fileSize" jdbcType="BIGINT"/>
|
||||||
|
<result column="file_mime_type" property="fileMimeType" jdbcType="VARCHAR"/>
|
||||||
|
<result column="file_url" property="fileUrl" jdbcType="VARCHAR"/>
|
||||||
|
<result column="file_extension" property="fileExtension" jdbcType="VARCHAR"/>
|
||||||
|
<result column="file_md5_hash" property="fileMd5Hash" jdbcType="VARCHAR"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
optsn, knowledge_id, file_root_id, file_id, dify_file_id, version,
|
optsn, knowledge_id, file_root_id, file_id, dify_file_id, version,
|
||||||
create_time, update_time, delete_time, deleted
|
create_time, update_time, delete_time, deleted
|
||||||
@@ -53,11 +73,21 @@
|
|||||||
WHERE knowledge_id = #{knowledgeId} AND file_id = #{fileId} AND deleted = false
|
WHERE knowledge_id = #{knowledgeId} AND file_id = #{fileId} AND deleted = false
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectFilesByKnowledgeId" resultMap="BaseResultMap">
|
<select id="selectFilesByKnowledgeId" resultMap="KnowledgeFileVOResultMap">
|
||||||
SELECT <include refid="Base_Column_List"/>
|
SELECT
|
||||||
FROM ai.tb_knowledge_file
|
kf.optsn, kf.knowledge_id, kf.file_root_id, kf.file_id, kf.dify_file_id, kf.version,
|
||||||
WHERE knowledge_id = #{knowledgeId} AND deleted = false
|
kf.create_time, kf.update_time, kf.delete_time, kf.deleted,
|
||||||
ORDER BY create_time DESC
|
f.name as file_name,
|
||||||
|
f.path as file_path,
|
||||||
|
f.size as file_size,
|
||||||
|
f.mime_type as file_mime_type,
|
||||||
|
f.url as file_url,
|
||||||
|
f.extension as file_extension,
|
||||||
|
f.md5_hash as file_md5_hash
|
||||||
|
FROM ai.tb_knowledge_file kf
|
||||||
|
LEFT JOIN file.tb_sys_file f ON kf.file_id = f.file_id AND f.deleted = false
|
||||||
|
WHERE kf.knowledge_id = #{knowledgeId} AND kf.deleted = false
|
||||||
|
ORDER BY kf.create_time DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectFileVersions" resultMap="BaseResultMap">
|
<select id="selectFileVersions" resultMap="BaseResultMap">
|
||||||
@@ -67,11 +97,21 @@
|
|||||||
ORDER BY version DESC
|
ORDER BY version DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectFilePage" resultMap="BaseResultMap">
|
<select id="selectFilePage" resultMap="KnowledgeFileVOResultMap">
|
||||||
SELECT <include refid="Base_Column_List"/>
|
SELECT
|
||||||
FROM ai.tb_knowledge_file
|
kf.optsn, kf.knowledge_id, kf.file_root_id, kf.file_id, kf.dify_file_id, kf.version,
|
||||||
WHERE knowledge_id = #{knowledgeId} AND deleted = false
|
kf.create_time, kf.update_time, kf.delete_time, kf.deleted,
|
||||||
ORDER BY create_time DESC
|
f.name as file_name,
|
||||||
|
f.path as file_path,
|
||||||
|
f.size as file_size,
|
||||||
|
f.mime_type as file_mime_type,
|
||||||
|
f.url as file_url,
|
||||||
|
f.extension as file_extension,
|
||||||
|
f.md5_hash as file_md5_hash
|
||||||
|
FROM ai.tb_knowledge_file kf
|
||||||
|
LEFT JOIN file.tb_sys_file f ON kf.file_id = f.file_id AND f.deleted = false
|
||||||
|
WHERE kf.knowledge_id = #{knowledgeId} AND kf.deleted = false
|
||||||
|
ORDER BY kf.create_time DESC
|
||||||
LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset}
|
LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import java.util.Map;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.xyzh.api.ai.dto.TbKnowledge;
|
import org.xyzh.api.ai.dto.TbKnowledge;
|
||||||
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
||||||
|
import org.xyzh.api.ai.vo.KnowledgeFileVO;
|
||||||
import org.xyzh.common.core.domain.ResultDomain;
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
import org.xyzh.common.core.page.PageParam;
|
import org.xyzh.common.core.page.PageParam;
|
||||||
|
import org.xyzh.common.core.page.PageRequest;
|
||||||
|
|
||||||
public interface KnowledgeService {
|
public interface KnowledgeService {
|
||||||
|
|
||||||
@@ -107,15 +109,13 @@ public interface KnowledgeService {
|
|||||||
// ================================= 文件管理 =================================
|
// ================================= 文件管理 =================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 获取知识库文档列表(从Dify获取)
|
* @description 获取知识库文档列表(查询本地数据库,关联文件详细信息)
|
||||||
* @param knowledgeId 知识库ID
|
* @param pageRequest 分页请求,filter 中包含 knowledgeId
|
||||||
* @param page 页码(从1开始)
|
* @return ResultDomain<KnowledgeFileVO> 文档列表(含文件详细信息)
|
||||||
* @param limit 每页数量
|
|
||||||
* @return ResultDomain<Map<String, Object>> 文档列表
|
|
||||||
* @author yslg
|
* @author yslg
|
||||||
* @since 2025-12-18
|
* @since 2025-12-20
|
||||||
*/
|
*/
|
||||||
ResultDomain<Map<String, Object>> getDocumentList(String knowledgeId, Integer page, Integer limit);
|
ResultDomain<KnowledgeFileVO> getDocumentList(PageRequest<TbKnowledgeFile> pageRequest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 上传文件到知识库(完整流程:minio + Dify + 数据库)
|
* @description 上传文件到知识库(完整流程:minio + Dify + 数据库)
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package org.xyzh.api.ai.vo;
|
||||||
|
|
||||||
|
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
||||||
|
import org.xyzh.common.vo.BaseVO;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 知识库文件视图对象(关联文件信息)
|
||||||
|
* @filename KnowledgeFileVO.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-12-20
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Schema(description = "知识库文件视图对象")
|
||||||
|
public class KnowledgeFileVO extends BaseVO {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
// TbKnowledgeFile 的字段
|
||||||
|
@Schema(description = "知识库ID")
|
||||||
|
private String knowledgeId;
|
||||||
|
|
||||||
|
@Schema(description = "文件ID")
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
|
@Schema(description = "文件根ID")
|
||||||
|
private String fileRootId;
|
||||||
|
|
||||||
|
@Schema(description = "Dify文件ID")
|
||||||
|
private String difyFileId;
|
||||||
|
|
||||||
|
@Schema(description = "文件版本")
|
||||||
|
private Integer version;
|
||||||
|
|
||||||
|
// TbSysFile 的字段
|
||||||
|
@Schema(description = "文件名")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Schema(description = "文件路径")
|
||||||
|
private String filePath;
|
||||||
|
|
||||||
|
@Schema(description = "文件大小(字节)")
|
||||||
|
private Long fileSize;
|
||||||
|
|
||||||
|
@Schema(description = "文件MIME类型")
|
||||||
|
private String fileMimeType;
|
||||||
|
|
||||||
|
@Schema(description = "文件访问URL")
|
||||||
|
private String fileUrl;
|
||||||
|
|
||||||
|
@Schema(description = "文件扩展名")
|
||||||
|
private String fileExtension;
|
||||||
|
|
||||||
|
@Schema(description = "文件MD5值")
|
||||||
|
private String fileMd5Hash;
|
||||||
|
}
|
||||||
@@ -78,7 +78,7 @@ public class FileController {
|
|||||||
|
|
||||||
@Operation(summary = "获取文件信息")
|
@Operation(summary = "获取文件信息")
|
||||||
@GetMapping("/{fileId}")
|
@GetMapping("/{fileId}")
|
||||||
public ResultDomain<TbSysFileDTO> getFileById(@PathVariable String fileId) {
|
public ResultDomain<TbSysFileDTO> getFileById(@PathVariable("fileId") String fileId) {
|
||||||
return fileService.getFileById(fileId);
|
return fileService.getFileById(fileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ public class FileController {
|
|||||||
|
|
||||||
@Operation(summary = "下载文件")
|
@Operation(summary = "下载文件")
|
||||||
@GetMapping("/download/{fileId}")
|
@GetMapping("/download/{fileId}")
|
||||||
public ResponseEntity<byte[]> downloadFile(@PathVariable String fileId) {
|
public ResponseEntity<byte[]> downloadFile(@PathVariable("fileId") String fileId) {
|
||||||
ResultDomain<byte[]> result = fileService.downloadFile(fileId);
|
ResultDomain<byte[]> result = fileService.downloadFile(fileId);
|
||||||
if (!result.getSuccess() || result.getData() == null) {
|
if (!result.getSuccess() || result.getData() == null) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
@@ -105,7 +105,7 @@ public class FileController {
|
|||||||
|
|
||||||
@Operation(summary = "删除文件")
|
@Operation(summary = "删除文件")
|
||||||
@DeleteMapping("/{fileId}")
|
@DeleteMapping("/{fileId}")
|
||||||
public ResultDomain<Boolean> deleteFile(@PathVariable String fileId) {
|
public ResultDomain<Boolean> deleteFile(@PathVariable("fileId") String fileId) {
|
||||||
return fileService.deleteFile(fileId);
|
return fileService.deleteFile(fileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public interface FileMapper extends BaseMapper<TbSysFileDTO> {
|
|||||||
* @param fileId 文件ID
|
* @param fileId 文件ID
|
||||||
* @return 文件信息
|
* @return 文件信息
|
||||||
*/
|
*/
|
||||||
@Select("SELECT * FROM file.tb_sys_file WHERE file_id = #{fileId} AND deleted = 0")
|
@Select("SELECT * FROM file.tb_sys_file WHERE file_id = #{fileId} AND deleted = false")
|
||||||
TbSysFileDTO selectByFileId(@Param("fileId") String fileId);
|
TbSysFileDTO selectByFileId(@Param("fileId") String fileId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +48,7 @@ public interface FileMapper extends BaseMapper<TbSysFileDTO> {
|
|||||||
* @param updater 更新者
|
* @param updater 更新者
|
||||||
* @return 影响行数
|
* @return 影响行数
|
||||||
*/
|
*/
|
||||||
@Update("UPDATE file.tb_sys_file SET deleted = 1, delete_time = CURRENT_TIMESTAMP, updater = #{updater} WHERE file_id = #{fileId}")
|
@Update("UPDATE file.tb_sys_file SET deleted = true, delete_time = CURRENT_TIMESTAMP, updater = #{updater} WHERE file_id = #{fileId}")
|
||||||
int deleteByFileId(@Param("fileId") String fileId, @Param("updater") String updater);
|
int deleteByFileId(@Param("fileId") String fileId, @Param("updater") String updater);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +57,7 @@ public interface FileMapper extends BaseMapper<TbSysFileDTO> {
|
|||||||
* @param businessId 业务ID
|
* @param businessId 业务ID
|
||||||
* @return 文件列表
|
* @return 文件列表
|
||||||
*/
|
*/
|
||||||
@Select("SELECT * FROM file.tb_sys_file WHERE module = #{module} AND business_id = #{businessId} AND deleted = 0 ORDER BY create_time DESC")
|
@Select("SELECT * FROM file.tb_sys_file WHERE module = #{module} AND business_id = #{businessId} AND deleted = false ORDER BY create_time DESC")
|
||||||
List<TbSysFileDTO> selectByModuleAndBusinessId(@Param("module") String module, @Param("businessId") String businessId);
|
List<TbSysFileDTO> selectByModuleAndBusinessId(@Param("module") String module, @Param("businessId") String businessId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,7 +65,7 @@ public interface FileMapper extends BaseMapper<TbSysFileDTO> {
|
|||||||
* @param uploader 上传者用户ID
|
* @param uploader 上传者用户ID
|
||||||
* @return 文件列表
|
* @return 文件列表
|
||||||
*/
|
*/
|
||||||
@Select("SELECT * FROM file.tb_sys_file WHERE uploader = #{uploader} AND deleted = 0 ORDER BY create_time DESC")
|
@Select("SELECT * FROM file.tb_sys_file WHERE uploader = #{uploader} AND deleted = false ORDER BY create_time DESC")
|
||||||
List<TbSysFileDTO> selectByUploader(@Param("uploader") String uploader);
|
List<TbSysFileDTO> selectByUploader(@Param("uploader") String uploader);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,7 +73,7 @@ public interface FileMapper extends BaseMapper<TbSysFileDTO> {
|
|||||||
* @param md5Hash MD5哈希值
|
* @param md5Hash MD5哈希值
|
||||||
* @return 文件信息
|
* @return 文件信息
|
||||||
*/
|
*/
|
||||||
@Select("SELECT * FROM file.tb_sys_file WHERE md5_hash = #{md5Hash} AND deleted = 0 LIMIT 1")
|
@Select("SELECT * FROM file.tb_sys_file WHERE md5_hash = #{md5Hash} AND deleted = false LIMIT 1")
|
||||||
TbSysFileDTO selectByMd5Hash(@Param("md5Hash") String md5Hash);
|
TbSysFileDTO selectByMd5Hash(@Param("md5Hash") String md5Hash);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,7 +82,7 @@ public interface FileMapper extends BaseMapper<TbSysFileDTO> {
|
|||||||
* @param objectName 对象名称
|
* @param objectName 对象名称
|
||||||
* @return 文件信息
|
* @return 文件信息
|
||||||
*/
|
*/
|
||||||
@Select("SELECT * FROM file.tb_sys_file WHERE bucket_name = #{bucketName} AND object_name = #{objectName} AND deleted = 0")
|
@Select("SELECT * FROM file.tb_sys_file WHERE bucket_name = #{bucketName} AND object_name = #{objectName} AND deleted = false")
|
||||||
TbSysFileDTO selectByMinioObject(@Param("bucketName") String bucketName, @Param("objectName") String objectName);
|
TbSysFileDTO selectByMinioObject(@Param("bucketName") String bucketName, @Param("objectName") String objectName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,6 +92,6 @@ public interface FileMapper extends BaseMapper<TbSysFileDTO> {
|
|||||||
* @author yslg
|
* @author yslg
|
||||||
* @since 2025-12-18
|
* @since 2025-12-18
|
||||||
*/
|
*/
|
||||||
@Select("SELECT MAX(version) FROM file.tb_sys_file WHERE file_root_id = #{fileRootId} AND deleted = 0")
|
@Select("SELECT MAX(version) FROM file.tb_sys_file WHERE file_root_id = #{fileRootId} AND deleted = false")
|
||||||
Integer selectMaxVersionByFileRootId(@Param("fileRootId") String fileRootId);
|
Integer selectMaxVersionByFileRootId(@Param("fileRootId") String fileRootId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,20 +80,19 @@ public class FileServiceImpl implements FileService {
|
|||||||
return ResultDomain.failure("文件上传到MinIO失败");
|
return ResultDomain.failure("文件上传到MinIO失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建文件访问URL
|
|
||||||
String fileUrl = minioConfig.buildFileUrl(objectName);
|
|
||||||
|
|
||||||
// 保存到数据库
|
// 保存到数据库
|
||||||
TbSysFileDTO fileDTO = new TbSysFileDTO();
|
TbSysFileDTO fileDTO = new TbSysFileDTO();
|
||||||
|
String fileId = UUID.randomUUID().toString();
|
||||||
fileDTO.setOptsn(UUID.randomUUID().toString());
|
fileDTO.setOptsn(UUID.randomUUID().toString());
|
||||||
fileDTO.setFileId(UUID.randomUUID().toString());
|
fileDTO.setFileId(fileId);
|
||||||
fileDTO.setName(originalFilename);
|
fileDTO.setName(originalFilename);
|
||||||
fileDTO.setPath(objectName);
|
fileDTO.setPath(objectName);
|
||||||
fileDTO.setSize(size);
|
fileDTO.setSize(size);
|
||||||
fileDTO.setType(extension);
|
fileDTO.setType(extension);
|
||||||
fileDTO.setStorageType("MINIO");
|
fileDTO.setStorageType("MINIO");
|
||||||
fileDTO.setMimeType(contentType);
|
fileDTO.setMimeType(contentType);
|
||||||
fileDTO.setUrl(fileUrl);
|
// URL 设为 NULL,前端通过后端接口 /api/file/download/{fileId} 下载
|
||||||
|
fileDTO.setUrl(null);
|
||||||
fileDTO.setStatus("NORMAL");
|
fileDTO.setStatus("NORMAL");
|
||||||
fileDTO.setModule(module);
|
fileDTO.setModule(module);
|
||||||
fileDTO.setBusinessId(businessId);
|
fileDTO.setBusinessId(businessId);
|
||||||
@@ -407,24 +406,26 @@ public class FileServiceImpl implements FileService {
|
|||||||
return ResultDomain.failure("文件上传到MinIO失败");
|
return ResultDomain.failure("文件上传到MinIO失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成文件URL
|
|
||||||
String fileUrl = minioConfig.getEndpoint() + "/" + bucketName + "/" + objectName;
|
|
||||||
|
|
||||||
// 创建文件DTO
|
// 创建文件DTO
|
||||||
TbSysFileDTO fileDTO = new TbSysFileDTO();
|
TbSysFileDTO fileDTO = new TbSysFileDTO();
|
||||||
|
String fileId = UUID.randomUUID().toString().replace("-", "");
|
||||||
fileDTO.setOptsn(UUID.randomUUID().toString());
|
fileDTO.setOptsn(UUID.randomUUID().toString());
|
||||||
fileDTO.setFileId(UUID.randomUUID().toString().replace("-", ""));
|
fileDTO.setFileId(fileId);
|
||||||
fileDTO.setName(fileName);
|
fileDTO.setName(fileName);
|
||||||
fileDTO.setPath(objectName);
|
fileDTO.setPath(objectName);
|
||||||
fileDTO.setUrl(fileUrl);
|
// URL 设为 NULL,前端通过后端接口 /api/file/download/{fileId} 下载
|
||||||
|
fileDTO.setUrl(null);
|
||||||
fileDTO.setSize(size);
|
fileDTO.setSize(size);
|
||||||
fileDTO.setMimeType(contentType);
|
fileDTO.setMimeType(contentType);
|
||||||
fileDTO.setExtension(extension);
|
fileDTO.setExtension(extension);
|
||||||
fileDTO.setMd5Hash(md5Hash);
|
fileDTO.setMd5Hash(md5Hash);
|
||||||
fileDTO.setModule(module);
|
fileDTO.setModule(module);
|
||||||
fileDTO.setBusinessId(businessId);
|
fileDTO.setBusinessId(businessId);
|
||||||
|
fileDTO.setStorageType("MINIO");
|
||||||
|
fileDTO.setObjectName(objectName);
|
||||||
|
fileDTO.setBucketName(bucketName);
|
||||||
fileDTO.setVersion(1);
|
fileDTO.setVersion(1);
|
||||||
fileDTO.setFileRootId(fileDTO.getFileId());
|
fileDTO.setFileRootId(fileId);
|
||||||
fileDTO.setCreateTime(new java.util.Date());
|
fileDTO.setCreateTime(new java.util.Date());
|
||||||
|
|
||||||
// 保存到数据库
|
// 保存到数据库
|
||||||
|
|||||||
@@ -5,18 +5,19 @@ server:
|
|||||||
# context-path: /urban-lifeline/file # 微服务架构下,context-path由Gateway管理
|
# context-path: /urban-lifeline/file # 微服务架构下,context-path由Gateway管理
|
||||||
|
|
||||||
# ================== Auth ====================
|
# ================== Auth ====================
|
||||||
urban-lifeline:
|
auth:
|
||||||
auth:
|
enabled: true
|
||||||
enabled: true
|
gateway-mode: true
|
||||||
whitelist:
|
whitelist:
|
||||||
- /swagger-ui/**
|
- /swagger-ui/**
|
||||||
- /swagger-ui.html
|
- /swagger-ui.html
|
||||||
- /v3/api-docs/**
|
- /v3/api-docs/**
|
||||||
- /webjars/**
|
- /webjars/**
|
||||||
- /favicon.ico
|
- /favicon.ico
|
||||||
- /error
|
- /error
|
||||||
- /actuator/health
|
- /actuator/health
|
||||||
- /actuator/info
|
- /actuator/info
|
||||||
|
- /file/download/**
|
||||||
|
|
||||||
security:
|
security:
|
||||||
aes:
|
aes:
|
||||||
|
|||||||
@@ -172,6 +172,13 @@ auth:
|
|||||||
- /doc.html
|
- /doc.html
|
||||||
- /favicon.ico
|
- /favicon.ico
|
||||||
- /error
|
- /error
|
||||||
|
# 各服务的 Swagger 文档
|
||||||
|
- /urban-lifeline/*/v3/api-docs/**
|
||||||
|
- /urban-lifeline/*/swagger-ui/**
|
||||||
|
# file 服务白名单
|
||||||
|
- /urban-lifeline/file/download/**
|
||||||
|
# ai 服务白名单
|
||||||
|
- /urban-lifeline/ai/chat/**
|
||||||
security:
|
security:
|
||||||
aes:
|
aes:
|
||||||
secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= # Base64 编码,32字节(256位)
|
secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= # Base64 编码,32字节(256位)
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ window.APP_RUNTIME_CONFIG = {
|
|||||||
|
|
||||||
// 文件配置
|
// 文件配置
|
||||||
file: {
|
file: {
|
||||||
downloadUrl: '/api/file/download/',
|
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||||
uploadUrl: '/api/file/upload',
|
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||||
maxSize: {
|
maxSize: {
|
||||||
image: 5, // MB
|
image: 5, // MB
|
||||||
video: 100, // MB
|
video: 100, // MB
|
||||||
|
|||||||
253
urbanLifelineWeb/packages/bidding/src/config/index.ts
Normal file
253
urbanLifelineWeb/packages/bidding/src/config/index.ts
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
/**
|
||||||
|
* @description Bidding 应用运行时配置
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-12-06
|
||||||
|
*
|
||||||
|
* 配置加载策略:
|
||||||
|
* 1. 开发环境:使用下面定义的开发配置
|
||||||
|
* 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js)
|
||||||
|
* 3. Docker部署:替换 app-config.js 文件实现配置外挂
|
||||||
|
*
|
||||||
|
* 配置结构说明:
|
||||||
|
* 此文件的配置结构与 app-config.js 完全对应
|
||||||
|
* 修改 app-config.js 后,这里的配置会自动应用
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 类型定义
|
||||||
|
// ============================================
|
||||||
|
export interface AppRuntimeConfig {
|
||||||
|
env?: string;
|
||||||
|
api: {
|
||||||
|
baseUrl: string;
|
||||||
|
timeout: number;
|
||||||
|
};
|
||||||
|
baseUrl: string;
|
||||||
|
file: {
|
||||||
|
downloadUrl: string;
|
||||||
|
uploadUrl: string;
|
||||||
|
maxSize: {
|
||||||
|
image: number;
|
||||||
|
video: number;
|
||||||
|
document: number;
|
||||||
|
};
|
||||||
|
acceptTypes: {
|
||||||
|
image: string;
|
||||||
|
video: string;
|
||||||
|
document: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
token: {
|
||||||
|
key: string;
|
||||||
|
refreshThreshold: number;
|
||||||
|
};
|
||||||
|
publicImgPath: string;
|
||||||
|
publicWebPath: string;
|
||||||
|
// 单点登录配置
|
||||||
|
sso?: {
|
||||||
|
platformUrl: string; // platform 平台地址
|
||||||
|
workcaseUrl: string; // workcase 服务地址
|
||||||
|
biddingUrl: string; // bidding 服务地址
|
||||||
|
};
|
||||||
|
features?: {
|
||||||
|
enableDebug?: boolean;
|
||||||
|
enableMockData?: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 配置定义(与 app-config.js 结构一致)
|
||||||
|
// ============================================
|
||||||
|
const isDev = (import.meta as any).env?.DEV ?? false;
|
||||||
|
|
||||||
|
// 开发环境配置
|
||||||
|
const devConfig: AppRuntimeConfig = {
|
||||||
|
env: 'development',
|
||||||
|
|
||||||
|
api: {
|
||||||
|
// 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域
|
||||||
|
// 实际请求路径示例:/api/... → 由代理转发到实际后端
|
||||||
|
baseUrl: '/api',
|
||||||
|
timeout: 30000
|
||||||
|
},
|
||||||
|
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
|
file: {
|
||||||
|
// 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀
|
||||||
|
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||||
|
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||||
|
maxSize: {
|
||||||
|
image: 5,
|
||||||
|
video: 100,
|
||||||
|
document: 10
|
||||||
|
},
|
||||||
|
acceptTypes: {
|
||||||
|
image: 'image/*',
|
||||||
|
video: 'video/*',
|
||||||
|
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
token: {
|
||||||
|
key: 'token',
|
||||||
|
refreshThreshold: 300000
|
||||||
|
},
|
||||||
|
|
||||||
|
publicImgPath: 'http://localhost:7002/img',
|
||||||
|
publicWebPath: 'http://localhost:7002',
|
||||||
|
|
||||||
|
// 单点登录配置
|
||||||
|
sso: {
|
||||||
|
platformUrl: '/', // 通过nginx访问platform
|
||||||
|
workcaseUrl: '/workcase', // 通过nginx访问workcase
|
||||||
|
biddingUrl: '/bidding' // 通过nginx访问bidding
|
||||||
|
},
|
||||||
|
|
||||||
|
features: {
|
||||||
|
enableDebug: true,
|
||||||
|
enableMockData: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生产环境默认配置(兜底)
|
||||||
|
const prodDefaultConfig: AppRuntimeConfig = {
|
||||||
|
env: 'production',
|
||||||
|
|
||||||
|
api: {
|
||||||
|
baseUrl: '/api',
|
||||||
|
timeout: 30000
|
||||||
|
},
|
||||||
|
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
|
file: {
|
||||||
|
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||||
|
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||||
|
maxSize: {
|
||||||
|
image: 5,
|
||||||
|
video: 100,
|
||||||
|
document: 10
|
||||||
|
},
|
||||||
|
acceptTypes: {
|
||||||
|
image: 'image/*',
|
||||||
|
video: 'video/*',
|
||||||
|
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
token: {
|
||||||
|
key: 'token',
|
||||||
|
refreshThreshold: 300000
|
||||||
|
},
|
||||||
|
|
||||||
|
publicImgPath: '/img',
|
||||||
|
publicWebPath: '/',
|
||||||
|
|
||||||
|
// 单点登录配置(生产环境通过nginx代理)
|
||||||
|
sso: {
|
||||||
|
platformUrl: '/',
|
||||||
|
workcaseUrl: '/workcase',
|
||||||
|
biddingUrl: '/bidding'
|
||||||
|
},
|
||||||
|
|
||||||
|
features: {
|
||||||
|
enableDebug: false,
|
||||||
|
enableMockData: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 配置加载
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取运行时配置
|
||||||
|
* 生产环境优先从 window.APP_RUNTIME_CONFIG 读取(app-config.js 注入)
|
||||||
|
*/
|
||||||
|
const getRuntimeConfig = (): AppRuntimeConfig => {
|
||||||
|
if (isDev) {
|
||||||
|
console.log('[配置] 开发环境,使用内置配置');
|
||||||
|
return devConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生产环境:尝试读取外部配置
|
||||||
|
try {
|
||||||
|
const runtimeConfig = (window as any).APP_RUNTIME_CONFIG;
|
||||||
|
if (runtimeConfig && typeof runtimeConfig === 'object') {
|
||||||
|
console.log('[配置] 加载外部配置 app-config.js');
|
||||||
|
console.log('[配置] API地址:', runtimeConfig.api?.baseUrl);
|
||||||
|
console.log('[配置] 环境:', runtimeConfig.env || 'production');
|
||||||
|
return runtimeConfig as AppRuntimeConfig;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[配置] 无法读取外部配置,使用默认配置', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[配置] 使用默认生产配置');
|
||||||
|
return prodDefaultConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 当前应用配置
|
||||||
|
const config = getRuntimeConfig();
|
||||||
|
console.log('[配置] 当前配置', config);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 导出配置(向后兼容)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// 单独导出常用配置项
|
||||||
|
export const API_BASE_URL = config.api.baseUrl;
|
||||||
|
export const FILE_DOWNLOAD_URL = config.file.downloadUrl;
|
||||||
|
export const PUBLIC_IMG_PATH = config.publicImgPath;
|
||||||
|
export const PUBLIC_WEB_PATH = config.publicWebPath;
|
||||||
|
|
||||||
|
// 导出完整配置对象
|
||||||
|
export const APP_CONFIG = {
|
||||||
|
// 应用标题
|
||||||
|
title: '泰豪电源招投标系统',
|
||||||
|
|
||||||
|
// 环境标识
|
||||||
|
env: config.env || 'production',
|
||||||
|
|
||||||
|
// 应用基础路径
|
||||||
|
baseUrl: config.baseUrl,
|
||||||
|
|
||||||
|
// API 配置
|
||||||
|
api: {
|
||||||
|
baseUrl: config.api.baseUrl,
|
||||||
|
timeout: config.api.timeout
|
||||||
|
},
|
||||||
|
|
||||||
|
// 文件配置
|
||||||
|
file: {
|
||||||
|
downloadUrl: config.file.downloadUrl,
|
||||||
|
uploadUrl: config.file.uploadUrl,
|
||||||
|
maxSize: config.file.maxSize,
|
||||||
|
acceptTypes: config.file.acceptTypes
|
||||||
|
},
|
||||||
|
|
||||||
|
// Token 配置
|
||||||
|
token: {
|
||||||
|
key: config.token.key,
|
||||||
|
refreshThreshold: config.token.refreshThreshold
|
||||||
|
},
|
||||||
|
|
||||||
|
// 公共路径
|
||||||
|
publicImgPath: config.publicImgPath,
|
||||||
|
publicWebPath: config.publicWebPath,
|
||||||
|
|
||||||
|
// 单点登录配置
|
||||||
|
sso: config.sso || {
|
||||||
|
platformUrl: '/',
|
||||||
|
workcaseUrl: '/workcase',
|
||||||
|
biddingUrl: '/bidding'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 功能开关
|
||||||
|
features: config.features || {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 默认导出
|
||||||
|
export default APP_CONFIG;
|
||||||
@@ -27,8 +27,8 @@ window.APP_RUNTIME_CONFIG = {
|
|||||||
|
|
||||||
// 文件配置
|
// 文件配置
|
||||||
file: {
|
file: {
|
||||||
downloadUrl: '/api/file/download/',
|
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||||
uploadUrl: '/api/file/upload',
|
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||||
maxSize: {
|
maxSize: {
|
||||||
image: 5, // MB
|
image: 5, // MB
|
||||||
video: 100, // MB
|
video: 100, // MB
|
||||||
|
|||||||
@@ -1,7 +1,21 @@
|
|||||||
/**
|
/**
|
||||||
* Platform 应用配置
|
* @description Platform 应用运行时配置
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-12-06
|
||||||
|
*
|
||||||
|
* 配置加载策略:
|
||||||
|
* 1. 开发环境:使用下面定义的开发配置
|
||||||
|
* 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js)
|
||||||
|
* 3. Docker部署:替换 app-config.js 文件实现配置外挂
|
||||||
|
*
|
||||||
|
* 配置结构说明:
|
||||||
|
* 此文件的配置结构与 app-config.js 完全对应
|
||||||
|
* 修改 app-config.js 后,这里的配置会自动应用
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// AES 加密密钥
|
||||||
|
// ============================================
|
||||||
/**
|
/**
|
||||||
* AES 加密密钥(与后端保持一致)
|
* AES 加密密钥(与后端保持一致)
|
||||||
* 对应后端配置:security.aes.secret-key
|
* 对应后端配置:security.aes.secret-key
|
||||||
@@ -9,16 +23,244 @@
|
|||||||
*/
|
*/
|
||||||
export const AES_SECRET_KEY = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=' // Base64 编码,解码后是 "12345678901234567890123456789012" (32字节)
|
export const AES_SECRET_KEY = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=' // Base64 编码,解码后是 "12345678901234567890123456789012" (32字节)
|
||||||
|
|
||||||
/**
|
// ============================================
|
||||||
* API 基础地址
|
// 类型定义
|
||||||
*/
|
// ============================================
|
||||||
export const API_BASE_URL = (import.meta as any).env?.VITE_API_BASE_URL || 'http://localhost:8180'
|
export interface AppRuntimeConfig {
|
||||||
|
env?: string;
|
||||||
|
api: {
|
||||||
|
baseUrl: string;
|
||||||
|
timeout: number;
|
||||||
|
};
|
||||||
|
baseUrl: string;
|
||||||
|
file: {
|
||||||
|
downloadUrl: string;
|
||||||
|
uploadUrl: string;
|
||||||
|
maxSize: {
|
||||||
|
image: number;
|
||||||
|
video: number;
|
||||||
|
document: number;
|
||||||
|
};
|
||||||
|
acceptTypes: {
|
||||||
|
image: string;
|
||||||
|
video: string;
|
||||||
|
document: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
token: {
|
||||||
|
key: string;
|
||||||
|
refreshThreshold: number;
|
||||||
|
};
|
||||||
|
publicImgPath: string;
|
||||||
|
publicWebPath: string;
|
||||||
|
// 单点登录配置
|
||||||
|
sso?: {
|
||||||
|
platformUrl: string; // platform 平台地址
|
||||||
|
workcaseUrl: string; // workcase 服务地址
|
||||||
|
biddingUrl: string; // bidding 服务地址
|
||||||
|
};
|
||||||
|
features?: {
|
||||||
|
enableDebug?: boolean;
|
||||||
|
enableMockData?: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 配置定义(与 app-config.js 结构一致)
|
||||||
|
// ============================================
|
||||||
|
const isDev = (import.meta as any).env?.DEV ?? false;
|
||||||
|
|
||||||
|
// 开发环境配置
|
||||||
|
const devConfig: AppRuntimeConfig = {
|
||||||
|
env: 'development',
|
||||||
|
|
||||||
|
api: {
|
||||||
|
// 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域
|
||||||
|
// 实际请求路径示例:/api/... → 由代理转发到实际后端
|
||||||
|
baseUrl: '/api',
|
||||||
|
timeout: 30000
|
||||||
|
},
|
||||||
|
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
|
file: {
|
||||||
|
// 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀
|
||||||
|
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||||
|
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||||
|
maxSize: {
|
||||||
|
image: 5,
|
||||||
|
video: 100,
|
||||||
|
document: 10
|
||||||
|
},
|
||||||
|
acceptTypes: {
|
||||||
|
image: 'image/*',
|
||||||
|
video: 'video/*',
|
||||||
|
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
token: {
|
||||||
|
key: 'token',
|
||||||
|
refreshThreshold: 300000
|
||||||
|
},
|
||||||
|
|
||||||
|
publicImgPath: 'http://localhost:7001/img',
|
||||||
|
publicWebPath: 'http://localhost:7001',
|
||||||
|
|
||||||
|
// 单点登录配置
|
||||||
|
sso: {
|
||||||
|
platformUrl: '/', // 通过nginx访问platform
|
||||||
|
workcaseUrl: '/workcase', // 通过nginx访问workcase
|
||||||
|
biddingUrl: '/bidding' // 通过nginx访问bidding
|
||||||
|
},
|
||||||
|
|
||||||
|
features: {
|
||||||
|
enableDebug: true,
|
||||||
|
enableMockData: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生产环境默认配置(兜底)
|
||||||
|
const prodDefaultConfig: AppRuntimeConfig = {
|
||||||
|
env: 'production',
|
||||||
|
|
||||||
|
api: {
|
||||||
|
baseUrl: '/api',
|
||||||
|
timeout: 30000
|
||||||
|
},
|
||||||
|
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
|
file: {
|
||||||
|
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||||
|
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||||
|
maxSize: {
|
||||||
|
image: 5,
|
||||||
|
video: 100,
|
||||||
|
document: 10
|
||||||
|
},
|
||||||
|
acceptTypes: {
|
||||||
|
image: 'image/*',
|
||||||
|
video: 'video/*',
|
||||||
|
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
token: {
|
||||||
|
key: 'token',
|
||||||
|
refreshThreshold: 300000
|
||||||
|
},
|
||||||
|
|
||||||
|
publicImgPath: '/img',
|
||||||
|
publicWebPath: '/',
|
||||||
|
|
||||||
|
// 单点登录配置(生产环境通过nginx代理)
|
||||||
|
sso: {
|
||||||
|
platformUrl: '/',
|
||||||
|
workcaseUrl: '/workcase',
|
||||||
|
biddingUrl: '/bidding'
|
||||||
|
},
|
||||||
|
|
||||||
|
features: {
|
||||||
|
enableDebug: false,
|
||||||
|
enableMockData: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 配置加载
|
||||||
|
// ============================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用配置
|
* 获取运行时配置
|
||||||
|
* 生产环境优先从 window.APP_RUNTIME_CONFIG 读取(app-config.js 注入)
|
||||||
*/
|
*/
|
||||||
|
const getRuntimeConfig = (): AppRuntimeConfig => {
|
||||||
|
if (isDev) {
|
||||||
|
console.log('[配置] 开发环境,使用内置配置');
|
||||||
|
return devConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生产环境:尝试读取外部配置
|
||||||
|
try {
|
||||||
|
const runtimeConfig = (window as any).APP_RUNTIME_CONFIG;
|
||||||
|
if (runtimeConfig && typeof runtimeConfig === 'object') {
|
||||||
|
console.log('[配置] 加载外部配置 app-config.js');
|
||||||
|
console.log('[配置] API地址:', runtimeConfig.api?.baseUrl);
|
||||||
|
console.log('[配置] 环境:', runtimeConfig.env || 'production');
|
||||||
|
return runtimeConfig as AppRuntimeConfig;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[配置] 无法读取外部配置,使用默认配置', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[配置] 使用默认生产配置');
|
||||||
|
return prodDefaultConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 当前应用配置
|
||||||
|
const config = getRuntimeConfig();
|
||||||
|
console.log('[配置] 当前配置', config);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 导出配置(向后兼容)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// 单独导出常用配置项
|
||||||
|
export const API_BASE_URL = config.api.baseUrl;
|
||||||
|
export const FILE_DOWNLOAD_URL = config.file.downloadUrl;
|
||||||
|
export const PUBLIC_IMG_PATH = config.publicImgPath;
|
||||||
|
export const PUBLIC_WEB_PATH = config.publicWebPath;
|
||||||
|
|
||||||
|
// 导出完整配置对象
|
||||||
export const APP_CONFIG = {
|
export const APP_CONFIG = {
|
||||||
name: '泰豪电源 AI 数智化平台',
|
// 应用标题
|
||||||
version: '1.0.0',
|
title: '泰豪电源 AI 数智化平台',
|
||||||
copyright: '泰豪电源'
|
name: '泰豪电源 AI 数智化平台',
|
||||||
}
|
version: '1.0.0',
|
||||||
|
copyright: '泰豪电源',
|
||||||
|
|
||||||
|
// 环境标识
|
||||||
|
env: config.env || 'production',
|
||||||
|
|
||||||
|
// 应用基础路径
|
||||||
|
baseUrl: config.baseUrl,
|
||||||
|
|
||||||
|
// API 配置
|
||||||
|
api: {
|
||||||
|
baseUrl: config.api.baseUrl,
|
||||||
|
timeout: config.api.timeout
|
||||||
|
},
|
||||||
|
|
||||||
|
// 文件配置
|
||||||
|
file: {
|
||||||
|
downloadUrl: config.file.downloadUrl,
|
||||||
|
uploadUrl: config.file.uploadUrl,
|
||||||
|
maxSize: config.file.maxSize,
|
||||||
|
acceptTypes: config.file.acceptTypes
|
||||||
|
},
|
||||||
|
|
||||||
|
// Token 配置
|
||||||
|
token: {
|
||||||
|
key: config.token.key,
|
||||||
|
refreshThreshold: config.token.refreshThreshold
|
||||||
|
},
|
||||||
|
|
||||||
|
// 公共路径
|
||||||
|
publicImgPath: config.publicImgPath,
|
||||||
|
publicWebPath: config.publicWebPath,
|
||||||
|
|
||||||
|
// 单点登录配置
|
||||||
|
sso: config.sso || {
|
||||||
|
platformUrl: '/',
|
||||||
|
workcaseUrl: '/workcase',
|
||||||
|
biddingUrl: '/bidding'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 功能开关
|
||||||
|
features: config.features || {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 默认导出
|
||||||
|
export default APP_CONFIG;
|
||||||
|
|||||||
@@ -80,14 +80,19 @@ export const aiKnowledgeAPI = {
|
|||||||
// ====================== 文件管理 ======================
|
// ====================== 文件管理 ======================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取知识库文档列表
|
* 获取知识库文档列表(查询本地数据库文件)
|
||||||
* @param knowledgeId 知识库ID
|
* @param knowledgeId 知识库ID
|
||||||
* @param page 页码
|
* @param page 页码
|
||||||
* @param limit 每页条数
|
* @param pageSize 每页条数
|
||||||
*/
|
*/
|
||||||
async getDocumentList(knowledgeId: string, page = 1, limit = 20): Promise<ResultDomain<Record<string, any>>> {
|
async getDocumentList(knowledgeId: string, page = 1, pageSize = 20): Promise<ResultDomain<any>> {
|
||||||
const response = await api.get<Record<string, any>>(`${this.baseUrl}/${knowledgeId}/documents`, {
|
const response = await api.post<any>(`${this.baseUrl}/${knowledgeId}/documents`, {
|
||||||
params: { page, limit }
|
filter: { knowledgeId },
|
||||||
|
pageParam: {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return response.data
|
return response.data
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { api } from '@/api/index'
|
|||||||
import { BatchFileUploadParam, FileUploadParam, ResultDomain, TbSysFileDTO } from '@/types';
|
import { BatchFileUploadParam, FileUploadParam, ResultDomain, TbSysFileDTO } from '@/types';
|
||||||
|
|
||||||
export const fileAPI = {
|
export const fileAPI = {
|
||||||
baseUrl: "/file",
|
baseUrl: "/urban-lifeline/file",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传文件
|
* 上传文件
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import url("./DocumentDetail.scss");
|
||||||
|
</style>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {default as DocumentDetail} from './DocumentDetail/DocumentDetail.vue'
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
export * from './fileupload'
|
export * from './fileupload'
|
||||||
export * from './base'
|
export * from './base'
|
||||||
export * from './dynamicFormItem'
|
export * from './dynamicFormItem'
|
||||||
|
export * from './ai'
|
||||||
|
|
||||||
// 通用视图组件
|
// 通用视图组件
|
||||||
export { default as IframeView } from './iframe/IframeView.vue'
|
export { default as IframeView } from './iframe/IframeView.vue'
|
||||||
@@ -76,8 +76,8 @@ const devConfig: AppRuntimeConfig = {
|
|||||||
|
|
||||||
file: {
|
file: {
|
||||||
// 同样走代理,保持与 api.baseUrl 一致
|
// 同样走代理,保持与 api.baseUrl 一致
|
||||||
downloadUrl: '/api/file/download/',
|
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||||
uploadUrl: '/api/file/upload',
|
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||||
maxSize: {
|
maxSize: {
|
||||||
image: 5,
|
image: 5,
|
||||||
video: 100,
|
video: 100,
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ window.APP_RUNTIME_CONFIG = {
|
|||||||
|
|
||||||
// 文件配置
|
// 文件配置
|
||||||
file: {
|
file: {
|
||||||
downloadUrl: '/api/file/download/',
|
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||||
uploadUrl: '/api/file/upload',
|
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||||
maxSize: {
|
maxSize: {
|
||||||
image: 5, // MB
|
image: 5, // MB
|
||||||
video: 100, // MB
|
video: 100, // MB
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
/**
|
|
||||||
* Workcase 应用配置
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AES 加密密钥(与后端保持一致)
|
|
||||||
* 对应后端配置:security.aes.secret-key
|
|
||||||
* Base64 编码的 32 字节密钥(256 位)
|
|
||||||
*/
|
|
||||||
export const AES_SECRET_KEY = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=' // Base64 编码,解码后是 "12345678901234567890123456789012" (32字节)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* API 基础地址
|
|
||||||
* 注意:使用 shared 的 APP_CONFIG 统一管理,这里保留用于特殊场景
|
|
||||||
*/
|
|
||||||
export const API_BASE_URL = (import.meta as any).env?.VITE_API_BASE_URL || '/api'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Platform URL(单点登录入口)
|
|
||||||
* 开发环境:
|
|
||||||
* - 通过nginx访问时使用 '/'(推荐)
|
|
||||||
* - 直接访问各服务时使用 'http://localhost:7001'
|
|
||||||
* 生产环境:统一使用 '/'
|
|
||||||
*/
|
|
||||||
export const PLATFORM_URL = (import.meta as any).env?.VITE_PLATFORM_URL || '/'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 应用配置
|
|
||||||
*/
|
|
||||||
export const APP_CONFIG = {
|
|
||||||
name: '泰豪小电',
|
|
||||||
version: '1.0.0',
|
|
||||||
copyright: '泰豪电源'
|
|
||||||
}
|
|
||||||
265
urbanLifelineWeb/packages/workcase/src/config/index.ts
Normal file
265
urbanLifelineWeb/packages/workcase/src/config/index.ts
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
/**
|
||||||
|
* @description 应用运行时配置
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-12-06
|
||||||
|
*
|
||||||
|
* 配置加载策略:
|
||||||
|
* 1. 开发环境:使用下面定义的开发配置
|
||||||
|
* 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js)
|
||||||
|
* 3. Docker部署:替换 app-config.js 文件实现配置外挂
|
||||||
|
*
|
||||||
|
* 配置结构说明:
|
||||||
|
* 此文件的配置结构与 app-config.js 完全对应
|
||||||
|
* 修改 app-config.js 后,这里的配置会自动应用
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// AES 加密密钥
|
||||||
|
// ============================================
|
||||||
|
/**
|
||||||
|
* AES 加密密钥(与后端保持一致)
|
||||||
|
* 对应后端配置:security.aes.secret-key
|
||||||
|
* Base64 编码的 32 字节密钥(256 位)
|
||||||
|
*/
|
||||||
|
export const AES_SECRET_KEY = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=' // Base64 编码,解码后是 "12345678901234567890123456789012" (32字节)
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 类型定义
|
||||||
|
// ============================================
|
||||||
|
export interface AppRuntimeConfig {
|
||||||
|
env?: string;
|
||||||
|
api: {
|
||||||
|
baseUrl: string;
|
||||||
|
timeout: number;
|
||||||
|
};
|
||||||
|
baseUrl: string;
|
||||||
|
file: {
|
||||||
|
downloadUrl: string;
|
||||||
|
uploadUrl: string;
|
||||||
|
maxSize: {
|
||||||
|
image: number;
|
||||||
|
video: number;
|
||||||
|
document: number;
|
||||||
|
};
|
||||||
|
acceptTypes: {
|
||||||
|
image: string;
|
||||||
|
video: string;
|
||||||
|
document: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
token: {
|
||||||
|
key: string;
|
||||||
|
refreshThreshold: number;
|
||||||
|
};
|
||||||
|
publicImgPath: string;
|
||||||
|
publicWebPath: string;
|
||||||
|
// 单点登录配置
|
||||||
|
sso?: {
|
||||||
|
platformUrl: string; // platform 平台地址
|
||||||
|
workcaseUrl: string; // workcase 服务地址
|
||||||
|
biddingUrl: string; // bidding 服务地址
|
||||||
|
};
|
||||||
|
features?: {
|
||||||
|
enableDebug?: boolean;
|
||||||
|
enableMockData?: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 配置定义(与 app-config.js 结构一致)
|
||||||
|
// ============================================
|
||||||
|
const isDev = (import.meta as any).env?.DEV ?? false;
|
||||||
|
|
||||||
|
// 开发环境配置
|
||||||
|
const devConfig: AppRuntimeConfig = {
|
||||||
|
env: 'development',
|
||||||
|
|
||||||
|
api: {
|
||||||
|
// 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域
|
||||||
|
// 实际请求路径示例:/api/... → 由代理转发到实际后端
|
||||||
|
baseUrl: '/api',
|
||||||
|
timeout: 30000
|
||||||
|
},
|
||||||
|
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
|
file: {
|
||||||
|
// 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀
|
||||||
|
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||||
|
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||||
|
maxSize: {
|
||||||
|
image: 5,
|
||||||
|
video: 100,
|
||||||
|
document: 10
|
||||||
|
},
|
||||||
|
acceptTypes: {
|
||||||
|
image: 'image/*',
|
||||||
|
video: 'video/*',
|
||||||
|
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
token: {
|
||||||
|
key: 'token',
|
||||||
|
refreshThreshold: 300000
|
||||||
|
},
|
||||||
|
|
||||||
|
publicImgPath: 'http://localhost:5173/img',
|
||||||
|
publicWebPath: 'http://localhost:5173',
|
||||||
|
|
||||||
|
// 单点登录配置
|
||||||
|
// 推荐:开发环境也通过nginx访问(http://localhost)
|
||||||
|
// 备选:直接访问各服务端口(platformUrl: 'http://localhost:7001')
|
||||||
|
sso: {
|
||||||
|
platformUrl: '/', // 通过nginx访问platform
|
||||||
|
workcaseUrl: '/workcase', // 通过nginx访问workcase
|
||||||
|
biddingUrl: '/bidding' // 通过nginx访问bidding
|
||||||
|
},
|
||||||
|
|
||||||
|
features: {
|
||||||
|
enableDebug: true,
|
||||||
|
enableMockData: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生产环境默认配置(兜底)
|
||||||
|
const prodDefaultConfig: AppRuntimeConfig = {
|
||||||
|
env: 'production',
|
||||||
|
|
||||||
|
api: {
|
||||||
|
baseUrl: '/api',
|
||||||
|
timeout: 30000
|
||||||
|
},
|
||||||
|
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
|
file: {
|
||||||
|
downloadUrl: '/api/file/download/',
|
||||||
|
uploadUrl: '/api/file/upload',
|
||||||
|
maxSize: {
|
||||||
|
image: 5,
|
||||||
|
video: 100,
|
||||||
|
document: 10
|
||||||
|
},
|
||||||
|
acceptTypes: {
|
||||||
|
image: 'image/*',
|
||||||
|
video: 'video/*',
|
||||||
|
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
token: {
|
||||||
|
key: 'token',
|
||||||
|
refreshThreshold: 300000
|
||||||
|
},
|
||||||
|
|
||||||
|
publicImgPath: '/img',
|
||||||
|
publicWebPath: '/',
|
||||||
|
|
||||||
|
// 单点登录配置(生产环境通过nginx代理)
|
||||||
|
sso: {
|
||||||
|
platformUrl: '/',
|
||||||
|
workcaseUrl: '/workcase',
|
||||||
|
biddingUrl: '/bidding'
|
||||||
|
},
|
||||||
|
|
||||||
|
features: {
|
||||||
|
enableDebug: false,
|
||||||
|
enableMockData: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 配置加载
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取运行时配置
|
||||||
|
* 生产环境优先从 window.APP_RUNTIME_CONFIG 读取(app-config.js 注入)
|
||||||
|
*/
|
||||||
|
const getRuntimeConfig = (): AppRuntimeConfig => {
|
||||||
|
if (isDev) {
|
||||||
|
console.log('[配置] 开发环境,使用内置配置');
|
||||||
|
return devConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生产环境:尝试读取外部配置
|
||||||
|
try {
|
||||||
|
const runtimeConfig = (window as any).APP_RUNTIME_CONFIG;
|
||||||
|
if (runtimeConfig && typeof runtimeConfig === 'object') {
|
||||||
|
console.log('[配置] 加载外部配置 app-config.js');
|
||||||
|
console.log('[配置] API地址:', runtimeConfig.api?.baseUrl);
|
||||||
|
console.log('[配置] 环境:', runtimeConfig.env || 'production');
|
||||||
|
return runtimeConfig as AppRuntimeConfig;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[配置] 无法读取外部配置,使用默认配置', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[配置] 使用默认生产配置');
|
||||||
|
return prodDefaultConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 当前应用配置
|
||||||
|
const config = getRuntimeConfig();
|
||||||
|
console.log('[配置] 当前配置', config);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 导出配置(向后兼容)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// 单独导出常用配置项
|
||||||
|
export const API_BASE_URL = config.api.baseUrl;
|
||||||
|
export const FILE_DOWNLOAD_URL = config.file.downloadUrl;
|
||||||
|
export const PUBLIC_IMG_PATH = config.publicImgPath;
|
||||||
|
export const PUBLIC_WEB_PATH = config.publicWebPath;
|
||||||
|
|
||||||
|
// 导出完整配置对象
|
||||||
|
export const APP_CONFIG = {
|
||||||
|
// 应用标题
|
||||||
|
title: '泰豪电源 AI 数智化平台',
|
||||||
|
|
||||||
|
// 环境标识
|
||||||
|
env: config.env || 'production',
|
||||||
|
|
||||||
|
// 应用基础路径
|
||||||
|
baseUrl: config.baseUrl,
|
||||||
|
|
||||||
|
// API 配置
|
||||||
|
api: {
|
||||||
|
baseUrl: config.api.baseUrl,
|
||||||
|
timeout: config.api.timeout
|
||||||
|
},
|
||||||
|
|
||||||
|
// 文件配置
|
||||||
|
file: {
|
||||||
|
downloadUrl: config.file.downloadUrl,
|
||||||
|
uploadUrl: config.file.uploadUrl,
|
||||||
|
maxSize: config.file.maxSize,
|
||||||
|
acceptTypes: config.file.acceptTypes
|
||||||
|
},
|
||||||
|
|
||||||
|
// Token 配置
|
||||||
|
token: {
|
||||||
|
key: config.token.key,
|
||||||
|
refreshThreshold: config.token.refreshThreshold
|
||||||
|
},
|
||||||
|
|
||||||
|
// 公共路径
|
||||||
|
publicImgPath: config.publicImgPath,
|
||||||
|
publicWebPath: config.publicWebPath,
|
||||||
|
|
||||||
|
// 单点登录配置
|
||||||
|
sso: config.sso || {
|
||||||
|
platformUrl: '/',
|
||||||
|
workcaseUrl: '/workcase',
|
||||||
|
biddingUrl: '/bidding'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 功能开关
|
||||||
|
features: config.features || {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 默认导出
|
||||||
|
export default APP_CONFIG;
|
||||||
@@ -7,7 +7,7 @@ import './assets/css/common.scss'
|
|||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router/'
|
import router from './router/'
|
||||||
import { AES_SECRET_KEY } from './config'
|
import { AES_SECRET_KEY } from './config/index'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { initAesEncrypt } from 'shared/utils'
|
import { initAesEncrypt } from 'shared/utils'
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<AdminLayout title="知识库管理" info="管理外部和内部知识库文档">
|
<AdminLayout title="知识库管理" info="管理外部和内部知识库文档">
|
||||||
<template #action>
|
<template #action>
|
||||||
<el-button type="primary" @click="openUploadDialog">
|
<!-- 上传文档组件 -->
|
||||||
<el-icon><Upload /></el-icon>
|
<FileUpload
|
||||||
上传文档
|
ref="fileUploadRef"
|
||||||
</el-button>
|
mode="dialog"
|
||||||
|
:title="'上传文档到:' + currentKnowledgeName"
|
||||||
|
button-text="上传文档"
|
||||||
|
accept=".pdf,.doc,.docx,.txt,.md"
|
||||||
|
:max-size="50 * 1024 * 1024"
|
||||||
|
:max-count="10"
|
||||||
|
:custom-upload="customKnowledgeUpload"
|
||||||
|
@upload-error="handleUploadError"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="knowledge-container">
|
<div class="knowledge-container">
|
||||||
@@ -67,19 +75,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 上传文档组件 -->
|
|
||||||
<FileUpload
|
|
||||||
ref="fileUploadRef"
|
|
||||||
mode="dialog"
|
|
||||||
:title="'上传文档到:' + currentKnowledgeName"
|
|
||||||
button-text="上传文档"
|
|
||||||
accept=".pdf,.doc,.docx,.txt,.md"
|
|
||||||
:max-size="50 * 1024 * 1024"
|
|
||||||
:max-count="10"
|
|
||||||
:custom-upload="customKnowledgeUpload"
|
|
||||||
@upload-error="handleUploadError"
|
|
||||||
/>
|
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -90,6 +85,7 @@ import { Upload, Search, Document, View, Download, Delete } from '@element-plus/
|
|||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { aiKnowledgeAPI } from 'shared/api/ai'
|
import { aiKnowledgeAPI } from 'shared/api/ai'
|
||||||
import { FileUpload } from 'shared/components'
|
import { FileUpload } from 'shared/components'
|
||||||
|
import { FILE_DOWNLOAD_URL } from '@/config/index'
|
||||||
import type { TbKnowledge } from 'shared/types'
|
import type { TbKnowledge } from 'shared/types'
|
||||||
|
|
||||||
// Tab 配置
|
// Tab 配置
|
||||||
@@ -113,12 +109,11 @@ interface DocumentItem {
|
|||||||
name: string
|
name: string
|
||||||
uploader: string
|
uploader: string
|
||||||
uploadTime: string
|
uploadTime: string
|
||||||
position: number
|
fileId?: string
|
||||||
dataSourceType: string
|
fileRootId?: string
|
||||||
wordCount: number
|
knowledgeId?: string
|
||||||
hitCount: number
|
difyFileId?: string
|
||||||
indexingStatus: string
|
version?: number
|
||||||
enabled: boolean
|
|
||||||
}
|
}
|
||||||
const documents = ref<DocumentItem[]>([])
|
const documents = ref<DocumentItem[]>([])
|
||||||
|
|
||||||
@@ -185,18 +180,17 @@ const fetchDocuments = async (knowledgeId: string) => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const result = await aiKnowledgeAPI.getDocumentList(knowledgeId, 1, 100)
|
const result = await aiKnowledgeAPI.getDocumentList(knowledgeId, 1, 100)
|
||||||
if (result.success && result.data) {
|
if (result.success && result.pageDomain) {
|
||||||
documents.value = (result.data.data || []).map((doc: any) => ({
|
documents.value = (result.pageDomain.dataList || []).map((file: any) => ({
|
||||||
id: doc.id,
|
id: file.fileId,
|
||||||
name: doc.name,
|
name: file.fileName || '-',
|
||||||
uploader: doc.created_by || '-',
|
uploader: file.creator || '-',
|
||||||
uploadTime: doc.created_at ? new Date(doc.created_at * 1000).toLocaleString() : '-',
|
uploadTime: file.createTime ? new Date(file.createTime).toLocaleString() : '-',
|
||||||
position: doc.position,
|
fileId: file.fileId,
|
||||||
dataSourceType: doc.data_source_type,
|
fileRootId: file.fileRootId,
|
||||||
wordCount: doc.word_count || 0,
|
knowledgeId: file.knowledgeId,
|
||||||
hitCount: doc.hit_count || 0,
|
difyFileId: file.difyFileId,
|
||||||
indexingStatus: doc.indexing_status,
|
version: file.version
|
||||||
enabled: doc.enabled
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -226,12 +220,30 @@ watch(activeKnowledgeId, (newVal) => {
|
|||||||
if (newVal) fetchDocuments(newVal)
|
if (newVal) fetchDocuments(newVal)
|
||||||
})
|
})
|
||||||
|
|
||||||
const previewFile = (row: DocumentItem) => {
|
const previewFile = async (row: DocumentItem) => {
|
||||||
ElMessage.info(`预览文件: ${row.name}`)
|
if (!row.fileId) {
|
||||||
|
ElMessage.warning('文件信息不完整')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 使用 FILE_DOWNLOAD_URL 构建文件 URL 并在新窗口打开
|
||||||
|
const fileUrl = `${FILE_DOWNLOAD_URL}${row.fileId}`
|
||||||
|
window.open(fileUrl, '_blank')
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadFile = (row: DocumentItem) => {
|
const downloadFile = (row: DocumentItem) => {
|
||||||
ElMessage.success(`下载文件: ${row.name}`)
|
if (!row.fileId) {
|
||||||
|
ElMessage.warning('文件信息不完整')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 创建隐藏的下载链接并触发下载
|
||||||
|
const fileUrl = `${FILE_DOWNLOAD_URL}${row.fileId}`
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = fileUrl
|
||||||
|
link.download = row.name || 'file'
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
ElMessage.success('开始下载')
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteFile = async (row: DocumentItem) => {
|
const deleteFile = async (row: DocumentItem) => {
|
||||||
@@ -291,15 +303,6 @@ const handleUploadError = (error: string) => {
|
|||||||
ElMessage.error(error)
|
ElMessage.error(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开上传弹窗
|
|
||||||
const openUploadDialog = () => {
|
|
||||||
if (!activeKnowledgeId.value) {
|
|
||||||
ElMessage.warning('请先选择一个知识库')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fileUploadRef.value?.openDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchKnowledges()
|
fetchKnowledges()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user