10号功能修订

This commit is contained in:
chenxing
2026-09-10 09:47:46 +08:00
parent 59226875f3
commit 0e3bd97c55
12 changed files with 221 additions and 7984 deletions
-11
View File
@@ -1,11 +0,0 @@
node_modules
src
public
config
dist/base
dist/ld
dist/_verify
.git
.workbuddy
*.log
vite.config.ts.timestamp-*
-91
View File
@@ -1,91 +0,0 @@
name: build
# 前端应用 CI:构建 → 打包 zip → 推 Nexus raw → 通知 projman 建档
# 触发:push 到 main / 手动 Run workflow
# 链路:clone 源码 -> node 容器 npm ci + npm run build -> 打包 dist 为 zip -> 推 Nexus raw -> 通知 projman 建档
# 依赖仓库 Secrets:DEPLOY_TOKEN(webhook 鉴权)、NEXUS_USER / NEXUS_PASSWORD(上传 zip 到 Nexus raw)
# 注意:CI 构建后自动建档(status=pending),前端发布仍由用户在「前端发布」Tab 点部署触发
on:
push:
branches: [ main ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
container:
image: docker:24
options: --network infra-infra-net
steps:
- name: 安装 git / curl / zip(alpine)
run: apk add --no-cache git curl zip
# 内网 Gitea 无法访问 GitHub 拉 actions/checkout,手动 clone 本仓库
- name: 拉取源码(内网无 GitHub,手动 clone 本仓库)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git clone --depth 1 --branch main "http://x-access-token:${GITHUB_TOKEN}@gitea.yunhaixingtong.com/${{ github.repository }}.git" /tmp/src
cp -a /tmp/src/. .
rm -rf /tmp/src
# 取提交备注首行,脱去换行/双引号/反斜杠(避免破坏 webhook JSON),截断到 200 字符
COMMIT_MSG=$(git log -1 --pretty=%s | tr '\n' ' ' | sed 's/["\\]/ /g' | cut -c1-200)
echo "COMMIT_MSG=${COMMIT_MSG}" >> "$GITHUB_ENV"
# 前端构建:起独立 node 容器(通过 docker.sock 调宿主 dockerd)
# node:20 镜像预装 npm,npm ci 按 lockfile 安装依赖
- name: 构建前端(node 容器)
run: |
set -e
docker rm -f frontbuild 2>/dev/null || true
docker run -d --name frontbuild -w /build node:20 sleep 3600
docker cp "${PWD}/." frontbuild:/build/
docker exec frontbuild sh -c '
set -e
cd /build
if [ -f .npmrc ]; then sed -i "/SASS_BINARY_PATH/d" .npmrc; fi
npm ci
npm run build
'
echo "=== dist 产物 ==="
docker exec frontbuild sh -c 'ls -la /build/dist/ | head -20'
# 从容器 cp dist 到宿主工作目录
docker cp frontbuild:/build/dist ./dist
docker rm -f frontbuild
echo "BUILD CHECK OK"
# 打包 dist → zip(进入 dist 目录打包,zip 根目录直接是 html/css/js 文件)
- name: 打包产物
run: |
TAG=$(echo "$GITHUB_SHA" | cut -c1-8)
ZIP_FILE="userhome-front-${TAG}.zip"
cd dist && zip -r "../${ZIP_FILE}" . && cd ..
echo "ZIP_FILE=${ZIP_FILE}" >> "$GITHUB_ENV"
echo "TAG=${TAG}" >> "$GITHUB_ENV"
ls -la "${ZIP_FILE}"
# 上传到 Nexus raw 仓库
- name: 上传产物到 Nexus
env:
NEXUS_USER: ${{ secrets.NEXUS_USER }}
NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
run: |
set -e
NEXUS_URL="http://nexus.yunhaixingtong.com:8081/repository/front-hosted/${ZIP_FILE}"
# -f:HTTP >=400 时 curl 以非零退出,仓库不存在/认证失败必须让 job 变红,否则建档的是不存在的 URL
curl -sSf -X PUT \
-u "${NEXUS_USER}:${NEXUS_PASSWORD}" \
--upload-file "${ZIP_FILE}" \
"${NEXUS_URL}"
echo "NEXUS_URL=${NEXUS_URL}" >> "$GITHUB_ENV"
echo "uploaded to ${NEXUS_URL}"
# 通知 projman:webhook 收到后建档(type=front, status=pending),部署由用户在「前端发布」Tab 手动触发
- name: 通知 projman 建档
run: |
set -e
curl -sSf -X POST "http://192.168.10.160:8090/projman/api/deploy/webhook" \
-H "Content-Type: application/json" \
-H "X-Deploy-Token: ${{ secrets.DEPLOY_TOKEN }}" \
-d "{\"app\":\"userhome\",\"front\":\"front\",\"tag\":\"${TAG}\",\"sha\":\"${GITHUB_SHA}\",\"message\":\"${COMMIT_MSG}\",\"nexusUrl\":\"${NEXUS_URL}\"}"
-4
View File
@@ -1,6 +1,3 @@
# WorkBuddy local state
.workbuddy/
# Logs
logs
*.log
@@ -27,4 +24,3 @@ yarn.lock
*.njsproj
*.sln
*.sw?
*.timestamp-*.mjs
+3
View File
@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}
-26
View File
@@ -1,26 +0,0 @@
// 纠正当前 api:gen 产物中命名空间的大小写错误
// 映射依据:后端 service 下真实的 *Controller 类名 -> PascalCase 命名空间
const fs = require('fs');
const map = {
Approle: 'AppRole',
Appsettings: 'AppSettings',
Groupauthz: 'GroupAuthz',
Pushsubscription: 'PushSubscription',
Signkeyinternal: 'SignKeyInternal',
Sysgroup: 'SysGroup',
Sysuser: 'SysUser',
};
const targets = ['src/api/apiDefinitions.ts', 'src/api/globals.d.ts'];
for (const f of targets) {
let s = fs.readFileSync(f, 'utf8');
for (const [wrong, correct] of Object.entries(map)) {
// 带引号 key:'Sysgroup. -> 'SysGroup.
s = s.split(`'${wrong}.`).join(`'${correct}.`);
// 裸引用(globals.d.ts 里的 interface/属性声明):Sysgroup: -> SysGroup: / Sysgroup. -> SysGroup.
s = s.split(`${wrong}:`).join(`${correct}:`);
s = s.split(`${wrong}.`).join(`${correct}.`);
}
fs.writeFileSync(f, s);
console.log('fixed', f);
}
console.log('done');
-65
View File
@@ -1,65 +0,0 @@
import type { GeneratorConfig } from "@alova/wormhole";
export default {
generator: [
{
input: "http://localhost:8060/v3/api-docs",
// 文档平台类型
platform: "swagger",
// 生成代码的输出目录
output: "src/api",
// 响应数据的媒体类型
responseMediaType: "application/json",
// 请求体数据的媒体类型
bodyMediaType: "application/json",
// 生成的 API 版本
version: 1,
// 生成代码类型
type: "typescript",
// 全局 API 名称
global: "Apis",
splitImplementation: true,
implementation: "inline",
// API 处理函数
handleApi: apiDescriptor => {
// 过滤掉已废弃的 API
if (apiDescriptor.deprecated) {
return undefined;
}
// 优先使用后端显式声明的 operationId(稳定、可读、无重名后缀),
// 仅在 operationId 缺失时回退到 summary。切勿用 summary 覆盖 operationId,
// 否则中文/长文本 summary 会变成方法名导致运行时查不到。
const newOpId = apiDescriptor.operationId ?? apiDescriptor.summary?.trim();
const url = apiDescriptor.url.replace(/^\/{platform}\//, "/api/");
// springdoc 默认把 Controller 类名转为 kebab tag(如 base-controller),
// 而业务代码统一用 Pascal 无 Controller 的命名空间(如 Base)调用。
// 这里剥掉 -controller 后缀并转为 PascalCase,使 wormhole 生成的
// API 路径(Base.getUserInfo 等)与 Apis.Base.xxx 调用风格一致。
// 由于转换结果是合法 JS 标识符,wormhole 会原样保留、不做 camelCase 改写。
// springdoc v3 的 tag 有两种形态,都要能正确转为 PascalCase 且无 Controller 后缀:
// - Pascal+Controller:SysGroupController -> SysGroup
// - kebab+controller:sys-group-controller -> SysGroup
// 关键:剥掉尾部 Controller 后,要在 camelCase 边界补空格再逐段首字母大写,
// 否则 SysGroup -> Sysgroup(后面全被 toLowerCase 压成小写)会让运行时查不到。
const toNamespace = (tag: string): string => {
const stripped = tag.replace(/controller$/i, "");
const withSpaces = stripped.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
const parts = withSpaces.split(/[^a-zA-Z0-9]+/).filter(Boolean);
return parts
.map((p: string) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase())
.join("");
};
if (Array.isArray(apiDescriptor.tags)) {
apiDescriptor.tags = apiDescriptor.tags.map(toNamespace);
}
return {
...apiDescriptor,
operationId: newOpId, // 关键:改掉它
url,
};
},
},
],
} as GeneratorConfig;
-10
View File
@@ -1,10 +0,0 @@
# UserBase 前端静态镜像(基于已构建好的 dist/prod)
# 构建上下文 = front 目录: docker build -f docker/Dockerfile -t <img> .
# 说明:caddy 用 handle_path /userbase/* 剥掉前缀后转发进来,故 nginx 直接以 / 提供服务
FROM nginx:1.27-alpine
RUN rm -rf /usr/share/nginx/html/*
COPY dist/prod/ /usr/share/nginx/html/
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
-26
View File
@@ -1,26 +0,0 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_min_length 1k;
gzip_comp_level 5;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
# hash 路由,所有非静态资源请求回落到 index.html
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpe?g|gif|svg|woff2?|ttf|eot)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800";
}
location = /index.html {
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}
-7751
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -37,6 +37,8 @@
"@types/node": "^20.4.4",
"@vitejs/plugin-vue": "^5.0.4",
"fast-glob": "^3.3.2",
"node-sass": "^6.0.1",
"sass-loader": "^10.0.1",
"typescript": "^5.2.2",
"vite": "^5.1.4",
"vite-plugin-html": "^3.2.2",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long