feat: 完善知识库、策略预览与OnlyOffice集成,增强后端启动依赖检查

This commit is contained in:
caoxiaozhu
2026-05-09 05:59:46 +00:00
parent 1d3ac5c2e0
commit d9133193e8
31 changed files with 5534 additions and 5343 deletions

View File

@@ -1,99 +1,99 @@
import assert from 'node:assert/strict'
import { apiRequest } from '../src/services/api.js'
async function testUsesCustomContentTypeHeader() {
let capturedOptions = null
global.fetch = async (_url, options) => {
capturedOptions = options
return {
ok: true,
async json() {
return { ok: true }
}
}
}
await apiRequest('/knowledge/documents', {
method: 'POST',
body: 'payload',
contentType: 'application/octet-stream'
})
assert.equal(capturedOptions.headers['Content-Type'], 'application/octet-stream')
}
async function testSupportsBlobResponses() {
const blob = new Blob(['preview'])
global.fetch = async () => ({
ok: true,
async blob() {
return blob
},
async json() {
throw new Error('json parser should not be used for blob responses')
}
})
const payload = await apiRequest('/knowledge/documents/demo/content', {
responseType: 'blob',
contentType: null
})
assert.equal(payload, blob)
}
async function testInjectsAuthenticatedUserHeaders() {
const sessionStorage = new Map([
[
'x-financial-auth-user',
JSON.stringify({
username: 'admin',
name: '系统管理员',
roleCodes: ['manager'],
isAdmin: true
})
]
])
global.window = {
sessionStorage: {
getItem(key) {
return sessionStorage.get(key) ?? null
}
}
}
let capturedOptions = null
global.fetch = async (_url, options) => {
capturedOptions = options
return {
ok: true,
async json() {
return { ok: true }
}
}
}
await apiRequest('/knowledge/library')
assert.equal(capturedOptions.headers['x-auth-username'], 'admin')
assert.equal(capturedOptions.headers['x-auth-name'], '系统管理员')
assert.equal(capturedOptions.headers['x-auth-role-codes'], 'manager')
assert.equal(capturedOptions.headers['x-auth-is-admin'], 'true')
}
async function run() {
await testUsesCustomContentTypeHeader()
await testSupportsBlobResponses()
await testInjectsAuthenticatedUserHeaders()
console.log('api-request tests passed')
}
run().catch((error) => {
console.error(error)
process.exit(1)
})
import assert from 'node:assert/strict'
import { apiRequest } from '../src/services/api.js'
async function testUsesCustomContentTypeHeader() {
let capturedOptions = null
global.fetch = async (_url, options) => {
capturedOptions = options
return {
ok: true,
async json() {
return { ok: true }
}
}
}
await apiRequest('/knowledge/documents', {
method: 'POST',
body: 'payload',
contentType: 'application/octet-stream'
})
assert.equal(capturedOptions.headers['Content-Type'], 'application/octet-stream')
}
async function testSupportsBlobResponses() {
const blob = new Blob(['preview'])
global.fetch = async () => ({
ok: true,
async blob() {
return blob
},
async json() {
throw new Error('json parser should not be used for blob responses')
}
})
const payload = await apiRequest('/knowledge/documents/demo/content', {
responseType: 'blob',
contentType: null
})
assert.equal(payload, blob)
}
async function testInjectsAuthenticatedUserHeaders() {
const sessionStorage = new Map([
[
'x-financial-auth-user',
JSON.stringify({
username: 'admin',
name: '系统管理员',
roleCodes: ['manager'],
isAdmin: true
})
]
])
global.window = {
sessionStorage: {
getItem(key) {
return sessionStorage.get(key) ?? null
}
}
}
let capturedOptions = null
global.fetch = async (_url, options) => {
capturedOptions = options
return {
ok: true,
async json() {
return { ok: true }
}
}
}
await apiRequest('/knowledge/library')
assert.equal(capturedOptions.headers['x-auth-username'], 'admin')
assert.equal(capturedOptions.headers['x-auth-name'], '系统管理员')
assert.equal(capturedOptions.headers['x-auth-role-codes'], 'manager')
assert.equal(capturedOptions.headers['x-auth-is-admin'], 'true')
}
async function run() {
await testUsesCustomContentTypeHeader()
await testSupportsBlobResponses()
await testInjectsAuthenticatedUserHeaders()
console.log('api-request tests passed')
}
run().catch((error) => {
console.error(error)
process.exit(1)
})

View File

@@ -0,0 +1,39 @@
import assert from 'node:assert/strict'
import { resolveKnowledgePreviewMode } from '../src/views/scripts/knowledgePreviewMode.js'
function testPrefersOnlyOfficeForSupportedOfficeFileWhenAvailable() {
const document = {
extension: 'xlsx',
previewKind: 'table'
}
assert.equal(resolveKnowledgePreviewMode(document, { onlyOfficeAvailable: true }), 'onlyoffice')
}
function testFallsBackToStructuredPreviewForOfficeFileWhenOnlyOfficeUnavailable() {
const document = {
extension: 'xlsx',
previewKind: 'table'
}
assert.equal(resolveKnowledgePreviewMode(document, { onlyOfficeAvailable: false }), 'table')
}
function testUsesPreviewKindForNonOnlyOfficeFile() {
const document = {
extension: 'pdf',
previewKind: 'pdf'
}
assert.equal(resolveKnowledgePreviewMode(document, { onlyOfficeAvailable: false }), 'pdf')
}
function run() {
testPrefersOnlyOfficeForSupportedOfficeFileWhenAvailable()
testFallsBackToStructuredPreviewForOfficeFileWhenOnlyOfficeUnavailable()
testUsesPreviewKindForNonOnlyOfficeFile()
console.log('knowledge preview mode tests passed')
}
run()

View File

@@ -1,13 +1,13 @@
import assert from 'node:assert/strict'
import { buildOnlyOfficeScriptUrl } from '../src/services/onlyoffice.js'
function run() {
assert.equal(
buildOnlyOfficeScriptUrl('http://127.0.0.1:8082/'),
'http://127.0.0.1:8082/web-apps/apps/api/documents/api.js'
)
console.log('onlyoffice service tests passed')
}
run()
import assert from 'node:assert/strict'
import { buildOnlyOfficeScriptUrl } from '../src/services/onlyoffice.js'
function run() {
assert.equal(
buildOnlyOfficeScriptUrl('http://127.0.0.1:8082/'),
'http://127.0.0.1:8082/web-apps/apps/api/documents/api.js'
)
console.log('onlyoffice service tests passed')
}
run()

View File

@@ -1,69 +1,69 @@
import assert from 'node:assert/strict'
import {
buildExcelPreviewTable,
buildPreviewMetaLine,
buildPreviewSecondaryMetaLine
} from '../src/views/scripts/policiesPreviewFormatters.js'
function testBuildPreviewMetaLineUsesRealDocumentFields() {
const document = {
summary: '财务知识库 · XLSX · 10.9 KB',
time: '2026-05-09 12:30'
}
assert.deepEqual(buildPreviewMetaLine(document), ['财务知识库 · XLSX · 10.9 KB', '2026-05-09 12:30'])
}
function testBuildPreviewSecondaryMetaLineForExcelUsesSubtitleAndStats() {
const document = {
previewKind: 'table',
previewPages: [
{
subtitle: '表格内容预览',
stats: [
{ label: '工作表数量', value: '4' },
{ label: '预览行数', value: '7' },
{ label: '文件大小', value: '10.9 KB' }
]
},
{
subtitle: '第二页签预览',
stats: [
{ label: '工作表数量', value: '4' },
{ label: '预览行数', value: '3' }
]
}
]
}
assert.deepEqual(buildPreviewSecondaryMetaLine(document, document.previewPages[0]), ['表格内容预览', '工作表数量 4', '预览行数 7'])
assert.deepEqual(buildPreviewSecondaryMetaLine(document, document.previewPages[1]), ['第二页签预览', '工作表数量 4', '预览行数 3'])
}
function testBuildExcelPreviewTableParsesHeaderAndRows() {
const page = {
blocks: [
{ heading: '第 1 行', lines: ['日期 | 部门 | 金额 | 备注'] },
{ heading: '第 2 行', lines: ['2026-05-01 | 财务部 | 300 | 差旅'] },
{ heading: '第 3 行', lines: ['2026-05-02 | 行政部 | 120 | '] }
]
}
assert.deepEqual(buildExcelPreviewTable(page), {
headers: ['日期', '部门', '金额', '备注'],
rows: [
['2026-05-01', '财务部', '300', '差旅'],
['2026-05-02', '行政部', '120', '']
]
})
}
function run() {
testBuildPreviewMetaLineUsesRealDocumentFields()
testBuildPreviewSecondaryMetaLineForExcelUsesSubtitleAndStats()
testBuildExcelPreviewTableParsesHeaderAndRows()
console.log('policies preview formatter tests passed')
}
run()
import assert from 'node:assert/strict'
import {
buildExcelPreviewTable,
buildPreviewMetaLine,
buildPreviewSecondaryMetaLine
} from '../src/views/scripts/policiesPreviewFormatters.js'
function testBuildPreviewMetaLineUsesRealDocumentFields() {
const document = {
summary: '财务知识库 · XLSX · 10.9 KB',
time: '2026-05-09 12:30'
}
assert.deepEqual(buildPreviewMetaLine(document), ['财务知识库 · XLSX · 10.9 KB', '2026-05-09 12:30'])
}
function testBuildPreviewSecondaryMetaLineForExcelUsesSubtitleAndStats() {
const document = {
previewKind: 'table',
previewPages: [
{
subtitle: '表格内容预览',
stats: [
{ label: '工作表数量', value: '4' },
{ label: '预览行数', value: '7' },
{ label: '文件大小', value: '10.9 KB' }
]
},
{
subtitle: '第二页签预览',
stats: [
{ label: '工作表数量', value: '4' },
{ label: '预览行数', value: '3' }
]
}
]
}
assert.deepEqual(buildPreviewSecondaryMetaLine(document, document.previewPages[0]), ['表格内容预览', '工作表数量 4', '预览行数 7'])
assert.deepEqual(buildPreviewSecondaryMetaLine(document, document.previewPages[1]), ['第二页签预览', '工作表数量 4', '预览行数 3'])
}
function testBuildExcelPreviewTableParsesHeaderAndRows() {
const page = {
blocks: [
{ heading: '第 1 行', lines: ['日期 | 部门 | 金额 | 备注'] },
{ heading: '第 2 行', lines: ['2026-05-01 | 财务部 | 300 | 差旅'] },
{ heading: '第 3 行', lines: ['2026-05-02 | 行政部 | 120 | '] }
]
}
assert.deepEqual(buildExcelPreviewTable(page), {
headers: ['日期', '部门', '金额', '备注'],
rows: [
['2026-05-01', '财务部', '300', '差旅'],
['2026-05-02', '行政部', '120', '']
]
})
}
function run() {
testBuildPreviewMetaLineUsesRealDocumentFields()
testBuildPreviewSecondaryMetaLineForExcelUsesSubtitleAndStats()
testBuildExcelPreviewTableParsesHeaderAndRows()
console.log('policies preview formatter tests passed')
}
run()