diff --git a/back-end/.env b/back-end/.env index 75bdffd..8608def 100644 --- a/back-end/.env +++ b/back-end/.env @@ -12,7 +12,7 @@ APP_BASE_URL=https://bai-api.blv-oa.com # Database Configuration (Pocketbase) 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_APPID=wx3bd7a7b19679da7a diff --git a/back-end/scripts/build.js b/back-end/scripts/build.js index 1fb64e9..38d2a3b 100644 --- a/back-end/scripts/build.js +++ b/back-end/scripts/build.js @@ -5,6 +5,7 @@ const rootDir = path.resolve(__dirname, '..') const distDir = path.join(rootDir, 'dist') const sourceDirs = ['src', 'spec'] const sourceFiles = ['package.json', 'package-lock.json', '.env', 'eslint.config.js'] +const buildInfoPath = path.join(distDir, 'build-info.json') function ensureCleanDir(dirPath) { 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/.') } diff --git a/back-end/spec/openapi.yaml b/back-end/spec/openapi.yaml index c6647c3..55492fb 100644 --- a/back-end/spec/openapi.yaml +++ b/back-end/spec/openapi.yaml @@ -57,6 +57,11 @@ components: status: type: string example: success + build_time: + type: string + nullable: true + description: 最近一次构建时间;开发模式下若未执行构建则可能为空 + format: date-time WechatProfileRequest: type: object required: [users_name, users_phone_code, users_picture] @@ -197,7 +202,7 @@ paths: post: tags: [系统] summary: Test endpoint - description: Returns a hello world message + description: Returns a hello world message and build time responses: '200': description: Successful response diff --git a/back-end/src/routes/apiRoutes.js b/back-end/src/routes/apiRoutes.js index cf9e8b6..0514f4d 100644 --- a/back-end/src/routes/apiRoutes.js +++ b/back-end/src/routes/apiRoutes.js @@ -1,23 +1,29 @@ const express = require('express') const { success } = require('../utils/response') +const { getBuildTimestamp } = require('../utils/buildInfo') const wechatRoutes = require('./wechatRoutes') const router = express.Router() -router.post('/test-helloworld', (req, res) => { +function respondHelloWorld(req, res) { return success(res, '请求成功', { message: 'Hello, World!', timestamp: new Date().toISOString(), status: 'success', + build_time: getBuildTimestamp(), }) -}) +} -router.post('/health', (req, res) => { +function respondHealth(req, res) { return success(res, '服务运行正常', { status: 'healthy', timestamp: new Date().toISOString(), }) -}) +} + +router.post('/test-helloworld', respondHelloWorld) + +router.post('/health', respondHealth) router.use('/wechat', wechatRoutes) diff --git a/back-end/src/utils/buildInfo.js b/back-end/src/utils/buildInfo.js new file mode 100644 index 0000000..fecbbb0 --- /dev/null +++ b/back-end/src/utils/buildInfo.js @@ -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, +} \ No newline at end of file diff --git a/back-end/tests/integration/wechat.test.js b/back-end/tests/integration/wechat.test.js index 7b803f0..a4b794d 100644 --- a/back-end/tests/integration/wechat.test.js +++ b/back-end/tests/integration/wechat.test.js @@ -1,12 +1,16 @@ const test = require('node:test') const assert = require('node:assert/strict') const request = require('supertest') +const fs = require('fs') +const path = require('path') const env = require('../../src/config/env') const { createApp } = require('../../src/index') 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 response = await request(app).post('/api/health').send({}) @@ -16,6 +20,36 @@ test('POST /api/health 返回统一结构', async () => { 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 () => { const app = createApp() 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.msg, '请求成功') assert.equal(response.body.data.message, 'Hello, World!') + assert.ok(Object.prototype.hasOwnProperty.call(response.body.data, 'build_time')) }) test('未匹配路由返回统一 404', async () => {