feat: 添加构建时间信息和相关功能

This commit is contained in:
2026-03-20 22:13:06 +08:00
parent 72e974672e
commit d8d323e293
6 changed files with 95 additions and 7 deletions

View File

@@ -12,7 +12,7 @@ APP_BASE_URL=https://bai-api.blv-oa.com
# Database Configuration (Pocketbase) # Database Configuration (Pocketbase)
POCKETBASE_API_URL=https://bai-api.blv-oa.com/pb/ POCKETBASE_API_URL=https://bai-api.blv-oa.com/pb/
POCKETBASE_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJfcGJfdXNlcnNfYXV0aF8iLCJleHAiOjE3NzQzNjEzMzIsImlkIjoiazQ0aHI5MW90bnBydG10IiwicmVmcmVzaGFibGUiOmZhbHNlLCJ0eXBlIjoiYXV0aCJ9.qm4E6xYrDbEpAfdxZnHHRZs_EqiwHgDIIwSBz2k90Nk POCKETBASE_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJwYmNfMzE0MjYzNTgyMyIsImV4cCI6MTc3NDEwMTc4NiwiaWQiOiJmcnl2dG1qNnlwcHlmMXAiLCJyZWZyZXNoYWJsZSI6ZmFsc2UsInR5cGUiOiJhdXRoIn0.67DCKhqRQYF3ClPU_9mBgON_9ZDEy-NzqTeS50rGGZo
# WeChat Configuration # WeChat Configuration
WECHAT_APPID=wx3bd7a7b19679da7a WECHAT_APPID=wx3bd7a7b19679da7a

View File

@@ -5,6 +5,7 @@ const rootDir = path.resolve(__dirname, '..')
const distDir = path.join(rootDir, 'dist') const distDir = path.join(rootDir, 'dist')
const sourceDirs = ['src', 'spec'] const sourceDirs = ['src', 'spec']
const sourceFiles = ['package.json', 'package-lock.json', '.env', 'eslint.config.js'] const sourceFiles = ['package.json', 'package-lock.json', '.env', 'eslint.config.js']
const buildInfoPath = path.join(distDir, 'build-info.json')
function ensureCleanDir(dirPath) { function ensureCleanDir(dirPath) {
fs.rmSync(dirPath, { recursive: true, force: true }) fs.rmSync(dirPath, { recursive: true, force: true })
@@ -46,6 +47,17 @@ function build() {
} }
} }
fs.writeFileSync(
buildInfoPath,
JSON.stringify(
{
buildTime: new Date().toISOString(),
},
null,
2
)
)
console.log('Build completed. Deployable files generated in dist/.') console.log('Build completed. Deployable files generated in dist/.')
} }

View File

@@ -57,6 +57,11 @@ components:
status: status:
type: string type: string
example: success example: success
build_time:
type: string
nullable: true
description: 最近一次构建时间;开发模式下若未执行构建则可能为空
format: date-time
WechatProfileRequest: WechatProfileRequest:
type: object type: object
required: [users_name, users_phone_code, users_picture] required: [users_name, users_phone_code, users_picture]
@@ -197,7 +202,7 @@ paths:
post: post:
tags: [系统] tags: [系统]
summary: Test endpoint summary: Test endpoint
description: Returns a hello world message description: Returns a hello world message and build time
responses: responses:
'200': '200':
description: Successful response description: Successful response

View File

@@ -1,23 +1,29 @@
const express = require('express') const express = require('express')
const { success } = require('../utils/response') const { success } = require('../utils/response')
const { getBuildTimestamp } = require('../utils/buildInfo')
const wechatRoutes = require('./wechatRoutes') const wechatRoutes = require('./wechatRoutes')
const router = express.Router() const router = express.Router()
router.post('/test-helloworld', (req, res) => { function respondHelloWorld(req, res) {
return success(res, '请求成功', { return success(res, '请求成功', {
message: 'Hello, World!', message: 'Hello, World!',
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
status: 'success', status: 'success',
build_time: getBuildTimestamp(),
}) })
}) }
router.post('/health', (req, res) => { function respondHealth(req, res) {
return success(res, '服务运行正常', { return success(res, '服务运行正常', {
status: 'healthy', status: 'healthy',
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
}) })
}) }
router.post('/test-helloworld', respondHelloWorld)
router.post('/health', respondHealth)
router.use('/wechat', wechatRoutes) router.use('/wechat', wechatRoutes)

View File

@@ -0,0 +1,30 @@
const fs = require('fs')
const path = require('path')
function getBuildInfoFilePath() {
return path.resolve(__dirname, '..', '..', 'build-info.json')
}
function getBuildTimestamp() {
if (process.env.BUILD_TIME) {
return process.env.BUILD_TIME
}
const filePath = getBuildInfoFilePath()
if (!fs.existsSync(filePath)) {
return null
}
try {
const content = fs.readFileSync(filePath, 'utf8')
const payload = JSON.parse(content)
return payload.buildTime || null
} catch {
return null
}
}
module.exports = {
getBuildTimestamp,
}

View File

@@ -1,12 +1,16 @@
const test = require('node:test') const test = require('node:test')
const assert = require('node:assert/strict') const assert = require('node:assert/strict')
const request = require('supertest') const request = require('supertest')
const fs = require('fs')
const path = require('path')
const env = require('../../src/config/env') const env = require('../../src/config/env')
const { createApp } = require('../../src/index') const { createApp } = require('../../src/index')
const userService = require('../../src/services/userService') const userService = require('../../src/services/userService')
test('POST /api/health 返回统一结构', async () => { const buildInfoPath = path.resolve(__dirname, '..', '..', 'build-info.json')
test('POST /api/health 仍返回统一结构', async () => {
const app = createApp() const app = createApp()
const response = await request(app).post('/api/health').send({}) const response = await request(app).post('/api/health').send({})
@@ -16,6 +20,36 @@ test('POST /api/health 返回统一结构', async () => {
assert.equal(response.body.data.status, 'healthy') assert.equal(response.body.data.status, 'healthy')
}) })
test('POST /api/test-helloworld 返回统一结构和构建时间', async (t) => {
const app = createApp()
const buildTime = '2026-03-20T08:00:00.000Z'
fs.writeFileSync(buildInfoPath, JSON.stringify({ buildTime }, null, 2))
t.after(() => {
if (fs.existsSync(buildInfoPath)) {
fs.rmSync(buildInfoPath)
}
})
const response = await request(app).post('/api/test-helloworld').send({})
assert.equal(response.status, 200)
assert.equal(response.body.code, 200)
assert.equal(response.body.msg, '请求成功')
assert.equal(response.body.data.message, 'Hello, World!')
assert.equal(response.body.data.build_time, buildTime)
})
test('GET /api/test-helloworld 返回 404', async () => {
const app = createApp()
const response = await request(app).get('/api/test-helloworld')
assert.equal(response.status, 404)
assert.equal(response.body.code, 404)
assert.equal(response.body.msg, 'Route not found')
})
test('POST /api/test-helloworld 返回统一结构', async () => { test('POST /api/test-helloworld 返回统一结构', async () => {
const app = createApp() const app = createApp()
const response = await request(app).post('/api/test-helloworld').send({}) const response = await request(app).post('/api/test-helloworld').send({})
@@ -24,6 +58,7 @@ test('POST /api/test-helloworld 返回统一结构', async () => {
assert.equal(response.body.code, 200) assert.equal(response.body.code, 200)
assert.equal(response.body.msg, '请求成功') assert.equal(response.body.msg, '请求成功')
assert.equal(response.body.data.message, 'Hello, World!') assert.equal(response.body.data.message, 'Hello, World!')
assert.ok(Object.prototype.hasOwnProperty.call(response.body.data, 'build_time'))
}) })
test('未匹配路由返回统一 404', async () => { test('未匹配路由返回统一 404', async () => {