Compare commits
15 Commits
829e4db1cb
...
v2026.05.1
| Author | SHA1 | Date | |
|---|---|---|---|
| fc88270737 | |||
| 63867b4677 | |||
| 663143a30a | |||
| f77cf67b76 | |||
| 1460b71915 | |||
| 37131e1123 | |||
| deb999ff4d | |||
| 2a2274716e | |||
| 92eef39c5a | |||
| 5bb693e8f6 | |||
| 8a21a46554 | |||
| 54e8f370c8 | |||
| 2c3ce10837 | |||
| 9c65575eb3 | |||
| dbf7c78405 |
2
.env
@@ -44,7 +44,7 @@ S3_ENDPOINT=
|
|||||||
S3_BUCKET=
|
S3_BUCKET=
|
||||||
S3_FORCE_PATH_STYLE=false
|
S3_FORCE_PATH_STYLE=false
|
||||||
REDIS_URL=
|
REDIS_URL=
|
||||||
ENCRYPTION_SECRET=
|
ENCRYPTION_SECRET=10ddcad9814eaa5fa5bef8ba85d58a8122f3ede148bad11bc340899b5014af45
|
||||||
CLOUDFLARE_ACCOUNT_ID=
|
CLOUDFLARE_ACCOUNT_ID=
|
||||||
CLOUDFLARE_API_TOKEN=
|
CLOUDFLARE_API_TOKEN=
|
||||||
FLAG_DISABLE_SIGNUPS=false
|
FLAG_DISABLE_SIGNUPS=false
|
||||||
|
|||||||
@@ -13,6 +13,14 @@
|
|||||||
- `生成简历/backups/`: 每次线上 `resume` 被替换前导出的历史版本备份。
|
- `生成简历/backups/`: 每次线上 `resume` 被替换前导出的历史版本备份。
|
||||||
- `生成简历/visual-assets/`: 图文并茂版简历使用的轻量展示图。
|
- `生成简历/visual-assets/`: 图文并茂版简历使用的轻量展示图。
|
||||||
- `reactive_resume_data`: Reactive Resume 本地上传文件持久化 Docker 命名卷。
|
- `reactive_resume_data`: Reactive Resume 本地上传文件持久化 Docker 命名卷。
|
||||||
|
- `packages/`: 三套可读可改的安装包目录。
|
||||||
|
- `dist/`: 三套安装包的 `.tar.gz` / `.zip` 发布归档和 `SHA256SUMS`。
|
||||||
|
|
||||||
|
## 安装包
|
||||||
|
|
||||||
|
- `reactive-resume-clean-install-20260519`: 纯净迁移模板,域名、端口、FRP、密钥均为待填写占位。
|
||||||
|
- `reactive-resume-personal-direct-20260519`: 服务器直接运行版,预置 `https://isiseg.huijutec.cn`、FRP `remotePort = 10004`,并内置当前简历与上传图片。
|
||||||
|
- `reactive-resume-personal-qnap-nas-20260519`: 威联通 QNAP NAS 直接部署版,预置 `/share/Container/Reactive_Resume_Personal`、本地端口 `3004`、FRP `10004`,并内置当前简历与上传图片。
|
||||||
|
|
||||||
## 启动
|
## 启动
|
||||||
|
|
||||||
|
|||||||
6
dist/SHA256SUMS
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
f9daa11eeb735e1920d822094a7caf3f7eebcccf8467755bb6f904c68a53bdbf reactive-resume-clean-install-20260519.tar.gz
|
||||||
|
18b247b33feaf9ccc4ce7516fb76ea3523c578e3099c77e7549bdfa2c68fd658 reactive-resume-personal-direct-20260519.tar.gz
|
||||||
|
d2718004a8a0592f38096f426ef307c9df99b2f090dac733fb6c0f412d2e4916 reactive-resume-personal-qnap-nas-20260519.tar.gz
|
||||||
|
54bf5114e8ca9f29ae1feb0510f738396f354174f691bf96e863b41058742a58 reactive-resume-clean-install-20260519.zip
|
||||||
|
b7cdb405f24ff7653aac9d21f3bd347884e377c8a993d8bb1b7d9d77295425d2 reactive-resume-personal-direct-20260519.zip
|
||||||
|
d847a76e3cac28f9e973e21ba7bcbac89ccc940345694b3a3483cde9eed01ee0 reactive-resume-personal-qnap-nas-20260519.zip
|
||||||
BIN
dist/reactive-resume-clean-install-20260519.tar.gz
vendored
Normal file
BIN
dist/reactive-resume-clean-install-20260519.zip
vendored
Normal file
BIN
dist/reactive-resume-personal-direct-20260519.tar.gz
vendored
Normal file
BIN
dist/reactive-resume-personal-direct-20260519.zip
vendored
Normal file
BIN
dist/reactive-resume-personal-qnap-nas-20260519.tar.gz
vendored
Normal file
BIN
dist/reactive-resume-personal-qnap-nas-20260519.zip
vendored
Normal file
59
packages/reactive-resume-clean-install/.env.example
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# ===== Reactive Resume clean install environment =====
|
||||||
|
# Copy this file to .env, then replace every CHANGE_ME / YOUR_* value.
|
||||||
|
|
||||||
|
TZ=Asia/Shanghai
|
||||||
|
|
||||||
|
# Public URL behind your reverse proxy.
|
||||||
|
APP_URL=https://YOUR_DOMAIN.example.com
|
||||||
|
|
||||||
|
# Local debug binding on this Docker host.
|
||||||
|
# Use 127.0.0.1 if only frpc / local reverse proxy should reach the app.
|
||||||
|
LOCAL_BIND_IP=127.0.0.1
|
||||||
|
LOCAL_APP_PORT=CHANGE_ME_LOCAL_PORT
|
||||||
|
|
||||||
|
# PostgreSQL.
|
||||||
|
POSTGRES_DB=reactive_resume
|
||||||
|
POSTGRES_USER=reactive_resume
|
||||||
|
POSTGRES_PASSWORD=CHANGE_ME_POSTGRES_PASSWORD
|
||||||
|
DATABASE_URL=postgresql://reactive_resume:CHANGE_ME_POSTGRES_PASSWORD@postgres:5432/reactive_resume
|
||||||
|
|
||||||
|
# Generate with: openssl rand -hex 32
|
||||||
|
AUTH_SECRET=CHANGE_ME_64_HEX_AUTH_SECRET
|
||||||
|
ENCRYPTION_SECRET=CHANGE_ME_64_HEX_ENCRYPTION_SECRET
|
||||||
|
|
||||||
|
# Optional email/auth/storage/AI settings can be enabled later.
|
||||||
|
BETTER_AUTH_API_KEY=
|
||||||
|
GOOGLE_CLIENT_ID=
|
||||||
|
GOOGLE_CLIENT_SECRET=
|
||||||
|
GITHUB_CLIENT_ID=
|
||||||
|
GITHUB_CLIENT_SECRET=
|
||||||
|
LINKEDIN_CLIENT_ID=
|
||||||
|
LINKEDIN_CLIENT_SECRET=
|
||||||
|
OAUTH_PROVIDER_NAME=
|
||||||
|
OAUTH_CLIENT_ID=
|
||||||
|
OAUTH_CLIENT_SECRET=
|
||||||
|
OAUTH_DISCOVERY_URL=
|
||||||
|
OAUTH_AUTHORIZATION_URL=
|
||||||
|
OAUTH_TOKEN_URL=
|
||||||
|
OAUTH_USER_INFO_URL=
|
||||||
|
OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS=
|
||||||
|
OAUTH_SCOPES=
|
||||||
|
SMTP_HOST=
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_USER=
|
||||||
|
SMTP_PASS=
|
||||||
|
SMTP_FROM=Reactive Resume <noreply@YOUR_DOMAIN.example.com>
|
||||||
|
SMTP_SECURE=false
|
||||||
|
S3_ACCESS_KEY_ID=
|
||||||
|
S3_SECRET_ACCESS_KEY=
|
||||||
|
S3_REGION=us-east-1
|
||||||
|
S3_ENDPOINT=
|
||||||
|
S3_BUCKET=
|
||||||
|
S3_FORCE_PATH_STYLE=false
|
||||||
|
REDIS_URL=
|
||||||
|
CLOUDFLARE_ACCOUNT_ID=
|
||||||
|
CLOUDFLARE_API_TOKEN=
|
||||||
|
FLAG_DISABLE_SIGNUPS=false
|
||||||
|
FLAG_DISABLE_EMAIL_AUTH=false
|
||||||
|
FLAG_DISABLE_IMAGE_PROCESSING=false
|
||||||
|
FLAG_ALLOW_UNSAFE_AI_BASE_URL=false
|
||||||
38
packages/reactive-resume-clean-install/README.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Reactive Resume 纯净迁移安装包
|
||||||
|
|
||||||
|
这是一套不绑定具体域名、端口、FRP 服务器或密码的纯净安装包,适合迁移到新服务器或新 NAS 前作为模板使用。
|
||||||
|
|
||||||
|
## 文件说明
|
||||||
|
|
||||||
|
- `compose.yml`:通用 Linux / Docker Compose 部署版
|
||||||
|
- `compose-Nas.yml`:威联通 QNAP QTS / Container Station 部署模板
|
||||||
|
- `frpc.yaml`:通用服务器版 frpc 配置模板
|
||||||
|
- `.env.example`:通用服务器版环境变量模板
|
||||||
|
|
||||||
|
## 通用服务器部署
|
||||||
|
|
||||||
|
1. 复制环境变量文件:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 编辑 `.env` 和 `frpc.yaml`,把所有 `CHANGE_ME` / `YOUR_*` 改成真实值。
|
||||||
|
|
||||||
|
3. 启动:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f compose.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## QNAP NAS 部署
|
||||||
|
|
||||||
|
1. 编辑 `compose-Nas.yml` 顶部注释中的路径、域名、端口、FRP 服务器等待填写项。
|
||||||
|
2. 在 QTS Container Station 中导入 `compose-Nas.yml`。
|
||||||
|
3. 启动后,确认 app 和 frpc 都为 healthy / running。
|
||||||
|
|
||||||
|
## 注意
|
||||||
|
|
||||||
|
- `AUTH_SECRET` 和 `ENCRYPTION_SECRET` 必须使用长期固定值,迁移后不要随意更换。
|
||||||
|
- `APP_URL` 必须与反向代理对外访问域名一致,例如 `https://resume.example.com`。
|
||||||
|
- FRP 的 `remotePort` 需要与公网服务器 Nginx Proxy Manager / 反向代理转发端口一致。
|
||||||
97
packages/reactive-resume-clean-install/compose-Nas.yml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Reactive Resume / QNAP QTS 纯净模板。
|
||||||
|
# 使用前请填写:
|
||||||
|
# - NAS 数据目录,例如 /share/Container/Reactive_Resume
|
||||||
|
# - APP_URL,例如 https://resume.example.com
|
||||||
|
# - 本地访问端口,例如 3004
|
||||||
|
# - PostgreSQL 密码、AUTH_SECRET、ENCRYPTION_SECRET
|
||||||
|
# - FRP serverAddr/serverPort/token/proxy name/remotePort
|
||||||
|
|
||||||
|
name: reactive-resume-nas
|
||||||
|
|
||||||
|
services:
|
||||||
|
reactive_resume_permissions:
|
||||||
|
image: alpine:3.20
|
||||||
|
restart: "no"
|
||||||
|
command: ["sh", "-c", "mkdir -p /app/data && chown -R 1000:1000 /app/data"]
|
||||||
|
volumes:
|
||||||
|
# 待填写:改成你的 NAS 数据目录。
|
||||||
|
- /share/Container/CHANGE_ME_REACTIVE_RESUME/data/uploads:/app/data
|
||||||
|
|
||||||
|
reactive_resume_db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: reactive_resume
|
||||||
|
POSTGRES_USER: reactive_resume
|
||||||
|
POSTGRES_PASSWORD: CHANGE_ME_POSTGRES_PASSWORD
|
||||||
|
volumes:
|
||||||
|
# 待填写:改成你的 NAS 数据目录。
|
||||||
|
- /share/Container/CHANGE_ME_REACTIVE_RESUME/data/postgres:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U reactive_resume -d reactive_resume"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
|
||||||
|
reactive_resume_app:
|
||||||
|
image: amruthpillai/reactive-resume:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
reactive_resume_permissions:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
reactive_resume_db:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
# 待填写:NAS 本地访问端口。
|
||||||
|
- "CHANGE_ME_LOCAL_APP_PORT:3000"
|
||||||
|
volumes:
|
||||||
|
# 待填写:改成你的 NAS 数据目录。
|
||||||
|
- /share/Container/CHANGE_ME_REACTIVE_RESUME/data/uploads:/app/data
|
||||||
|
environment:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
APP_URL: https://YOUR_DOMAIN.example.com
|
||||||
|
DATABASE_URL: postgresql://reactive_resume:CHANGE_ME_POSTGRES_PASSWORD@reactive_resume_db:5432/reactive_resume
|
||||||
|
AUTH_SECRET: CHANGE_ME_64_HEX_AUTH_SECRET
|
||||||
|
ENCRYPTION_SECRET: CHANGE_ME_64_HEX_ENCRYPTION_SECRET
|
||||||
|
SMTP_FROM: Reactive Resume <noreply@YOUR_DOMAIN.example.com>
|
||||||
|
SMTP_PORT: "587"
|
||||||
|
SMTP_SECURE: "false"
|
||||||
|
FLAG_DISABLE_SIGNUPS: "false"
|
||||||
|
FLAG_DISABLE_EMAIL_AUTH: "false"
|
||||||
|
FLAG_DISABLE_IMAGE_PROCESSING: "false"
|
||||||
|
FLAG_ALLOW_UNSAFE_AI_BASE_URL: "false"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:3000/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 8
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
reactive_resume_frpc:
|
||||||
|
image: snowdreamtech/frpc:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
entrypoint: ["/bin/sh"]
|
||||||
|
command:
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
cat > /tmp/frpc.toml <<'EOF'
|
||||||
|
serverAddr = "YOUR_FRP_SERVER_IP_OR_DOMAIN"
|
||||||
|
serverPort = CHANGE_ME_FRP_SERVER_PORT
|
||||||
|
|
||||||
|
auth.method = "token"
|
||||||
|
auth.token = "CHANGE_ME_FRP_TOKEN"
|
||||||
|
|
||||||
|
transport.poolCount = 5
|
||||||
|
transport.heartbeatTimeout = -1
|
||||||
|
|
||||||
|
[[proxies]]
|
||||||
|
name = "CHANGE_ME_PROXY_NAME"
|
||||||
|
type = "tcp"
|
||||||
|
localIP = "reactive_resume_app"
|
||||||
|
localPort = 3000
|
||||||
|
remotePort = CHANGE_ME_REMOTE_PORT
|
||||||
|
EOF
|
||||||
|
exec frpc -c /tmp/frpc.toml
|
||||||
|
depends_on:
|
||||||
|
reactive_resume_app:
|
||||||
|
condition: service_healthy
|
||||||
72
packages/reactive-resume-clean-install/compose.yml
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Reactive Resume 通用服务器纯净模板。
|
||||||
|
# 使用前请先复制 .env.example 为 .env,并填写域名、端口、数据库密码和密钥。
|
||||||
|
|
||||||
|
name: reactive-resume
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- resume_net
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
|
||||||
|
reactive-resume:
|
||||||
|
image: amruthpillai/reactive-resume:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
ports:
|
||||||
|
# 待填写:LOCAL_BIND_IP / LOCAL_APP_PORT 在 .env 中配置。
|
||||||
|
# 例:127.0.0.1:3004:3000,仅允许本机反代或 frpc 访问。
|
||||||
|
- "${LOCAL_BIND_IP}:${LOCAL_APP_PORT}:3000"
|
||||||
|
volumes:
|
||||||
|
- reactive_resume_data:/app/data
|
||||||
|
networks:
|
||||||
|
- resume_net
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"node",
|
||||||
|
"-e",
|
||||||
|
"fetch('http://127.0.0.1:3000/api/health').then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1));",
|
||||||
|
]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
frpc:
|
||||||
|
image: fatedier/frpc:v0.68.0
|
||||||
|
restart: unless-stopped
|
||||||
|
command: ["-c", "/etc/frp/frpc.yaml"]
|
||||||
|
volumes:
|
||||||
|
# 待填写:frpc.yaml 中配置 FRP 服务器、token、remotePort。
|
||||||
|
- ./frpc.yaml:/etc/frp/frpc.yaml:ro
|
||||||
|
networks:
|
||||||
|
- resume_net
|
||||||
|
depends_on:
|
||||||
|
reactive-resume:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
networks:
|
||||||
|
resume_net:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
reactive_resume_data:
|
||||||
22
packages/reactive-resume-clean-install/frpc.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Reactive Resume frpc 纯净模板。
|
||||||
|
# 使用前把所有 CHANGE_ME / YOUR_* 改成真实值。
|
||||||
|
|
||||||
|
serverAddr: "YOUR_FRP_SERVER_IP_OR_DOMAIN"
|
||||||
|
serverPort: CHANGE_ME_FRP_SERVER_PORT
|
||||||
|
|
||||||
|
auth:
|
||||||
|
method: "token"
|
||||||
|
token: "CHANGE_ME_FRP_TOKEN"
|
||||||
|
|
||||||
|
transport:
|
||||||
|
poolCount: 5
|
||||||
|
heartbeatTimeout: -1
|
||||||
|
|
||||||
|
proxies:
|
||||||
|
- name: "CHANGE_ME_PROXY_NAME"
|
||||||
|
type: "tcp"
|
||||||
|
# Docker Compose 内部服务名,通常不用改。
|
||||||
|
localIP: "reactive-resume"
|
||||||
|
localPort: 3000
|
||||||
|
# 待填写:公网 FRP 远程端口,需与反向代理 forward port 一致。
|
||||||
|
remotePort: CHANGE_ME_REMOTE_PORT
|
||||||
50
packages/reactive-resume-personal-direct/.env
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
TZ=Asia/Shanghai
|
||||||
|
APP_URL=https://isiseg.huijutec.cn
|
||||||
|
|
||||||
|
# Local debug access only: http://127.0.0.1:3004
|
||||||
|
LOCAL_BIND_IP=127.0.0.1
|
||||||
|
LOCAL_APP_PORT=3004
|
||||||
|
|
||||||
|
POSTGRES_DB=reactive_resume
|
||||||
|
POSTGRES_USER=reactive_resume
|
||||||
|
POSTGRES_PASSWORD=2ed1869944c609f070699bdf8c92194f
|
||||||
|
DATABASE_URL=postgresql://reactive_resume:2ed1869944c609f070699bdf8c92194f@postgres:5432/reactive_resume
|
||||||
|
|
||||||
|
AUTH_SECRET=9ef1720ee316f9316241bdc84f5dfad99b52f139b48880300942ee61d81b7cda
|
||||||
|
ENCRYPTION_SECRET=20851888c2a96b11f1f6fc21b4eeb70f1e8258f2d0d414be8bace65eaff289ae
|
||||||
|
|
||||||
|
BETTER_AUTH_API_KEY=
|
||||||
|
GOOGLE_CLIENT_ID=
|
||||||
|
GOOGLE_CLIENT_SECRET=
|
||||||
|
GITHUB_CLIENT_ID=
|
||||||
|
GITHUB_CLIENT_SECRET=
|
||||||
|
LINKEDIN_CLIENT_ID=
|
||||||
|
LINKEDIN_CLIENT_SECRET=
|
||||||
|
OAUTH_PROVIDER_NAME=
|
||||||
|
OAUTH_CLIENT_ID=
|
||||||
|
OAUTH_CLIENT_SECRET=
|
||||||
|
OAUTH_DISCOVERY_URL=
|
||||||
|
OAUTH_AUTHORIZATION_URL=
|
||||||
|
OAUTH_TOKEN_URL=
|
||||||
|
OAUTH_USER_INFO_URL=
|
||||||
|
OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS=
|
||||||
|
OAUTH_SCOPES=
|
||||||
|
SMTP_HOST=
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_USER=
|
||||||
|
SMTP_PASS=
|
||||||
|
SMTP_FROM=Reactive Resume <noreply@isiseg.huijutec.cn>
|
||||||
|
SMTP_SECURE=false
|
||||||
|
S3_ACCESS_KEY_ID=
|
||||||
|
S3_SECRET_ACCESS_KEY=
|
||||||
|
S3_REGION=us-east-1
|
||||||
|
S3_ENDPOINT=
|
||||||
|
S3_BUCKET=
|
||||||
|
S3_FORCE_PATH_STYLE=false
|
||||||
|
REDIS_URL=
|
||||||
|
CLOUDFLARE_ACCOUNT_ID=
|
||||||
|
CLOUDFLARE_API_TOKEN=
|
||||||
|
FLAG_DISABLE_SIGNUPS=false
|
||||||
|
FLAG_DISABLE_EMAIL_AUTH=false
|
||||||
|
FLAG_DISABLE_IMAGE_PROCESSING=false
|
||||||
|
FLAG_ALLOW_UNSAFE_AI_BASE_URL=false
|
||||||
36
packages/reactive-resume-personal-direct/README.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Reactive Resume 个人简历直接运行安装包
|
||||||
|
|
||||||
|
这套包已经按 `https://isiseg.huijutec.cn` 和 FRP `remotePort = 10004` 预置,可以在当前服务器上直接运行。包内已包含当前简历初始化数据、头像和作品集图片,首次启动后可直接访问公开简历。
|
||||||
|
|
||||||
|
## 启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f compose.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
启动后:
|
||||||
|
|
||||||
|
- 本机调试地址:`http://127.0.0.1:3004`
|
||||||
|
- 公网访问地址:`https://isiseg.huijutec.cn`
|
||||||
|
- 当前公开简历:`https://isiseg.huijutec.cn/audience/resume`
|
||||||
|
- FRP 映射:本地 `reactive-resume:3000` -> 公网服务器 `10004`
|
||||||
|
|
||||||
|
## 反向代理要求
|
||||||
|
|
||||||
|
公网服务器上的 Nginx Proxy Manager / 反向代理应配置:
|
||||||
|
|
||||||
|
- Domain Names:`isiseg.huijutec.cn`
|
||||||
|
- Scheme:`http`
|
||||||
|
- Forward Hostname / IP:`82.157.255.195`
|
||||||
|
- Forward Port:`10004`
|
||||||
|
- Websockets Support:开启
|
||||||
|
- SSL:按现有 huijutec.cn 域名策略配置
|
||||||
|
|
||||||
|
## 数据
|
||||||
|
|
||||||
|
Compose 会创建独立项目名 `reactive-resume-personal`,默认使用 Docker named volumes:
|
||||||
|
|
||||||
|
- `reactive-resume-personal_postgres_data`
|
||||||
|
- `reactive-resume-personal_reactive_resume_data`
|
||||||
|
|
||||||
|
`seed/` 目录会在首次启动时导入当前用户、公开简历和上传图片。后续如需迁移数据,请备份这些 volumes。
|
||||||
94
packages/reactive-resume-personal-direct/compose.yml
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
name: reactive-resume-personal
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- resume_net
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
|
||||||
|
reactive-resume:
|
||||||
|
image: amruthpillai/reactive-resume:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
ports:
|
||||||
|
- "${LOCAL_BIND_IP}:${LOCAL_APP_PORT}:3000"
|
||||||
|
volumes:
|
||||||
|
- reactive_resume_data:/app/data
|
||||||
|
networks:
|
||||||
|
- resume_net
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"node",
|
||||||
|
"-e",
|
||||||
|
"fetch('http://127.0.0.1:3000/api/health').then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1));",
|
||||||
|
]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
seed:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
restart: "no"
|
||||||
|
user: "0:0"
|
||||||
|
entrypoint: ["/bin/sh"]
|
||||||
|
command:
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -eu
|
||||||
|
mkdir -p /app/data/uploads
|
||||||
|
cp -a /seed/uploads/. /app/data/uploads/
|
||||||
|
chown -R 1000:1000 /app/data/uploads || true
|
||||||
|
psql -h postgres -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -v ON_ERROR_STOP=1 -f /seed/seed.sql
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
PGPASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- reactive_resume_data:/app/data
|
||||||
|
- ./seed:/seed:ro
|
||||||
|
networks:
|
||||||
|
- resume_net
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
reactive-resume:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
frpc:
|
||||||
|
image: fatedier/frpc:v0.68.0
|
||||||
|
restart: unless-stopped
|
||||||
|
command: ["-c", "/etc/frp/frpc.yaml"]
|
||||||
|
volumes:
|
||||||
|
- ./frpc.yaml:/etc/frp/frpc.yaml:ro
|
||||||
|
networks:
|
||||||
|
- resume_net
|
||||||
|
depends_on:
|
||||||
|
seed:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
|
||||||
|
networks:
|
||||||
|
resume_net:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
reactive_resume_data:
|
||||||
17
packages/reactive-resume-personal-direct/frpc.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
serverAddr: "82.157.255.195"
|
||||||
|
serverPort: 7000
|
||||||
|
|
||||||
|
auth:
|
||||||
|
method: "token"
|
||||||
|
token: "en.xjtu.edu.cn"
|
||||||
|
|
||||||
|
transport:
|
||||||
|
poolCount: 5
|
||||||
|
heartbeatTimeout: -1
|
||||||
|
|
||||||
|
proxies:
|
||||||
|
- name: "reactive-resume-personal"
|
||||||
|
type: "tcp"
|
||||||
|
localIP: "reactive-resume"
|
||||||
|
localPort: 3000
|
||||||
|
remotePort: 10004
|
||||||
1250
packages/reactive-resume-personal-direct/seed/resume-data.json
Normal file
1326
packages/reactive-resume-personal-direct/seed/seed.sql
Normal file
|
After Width: | Height: | Size: 6.4 MiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 329 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 191 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 812 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 558 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 754 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 311 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 97 KiB |
37
packages/reactive-resume-personal-qnap-nas/README.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Reactive Resume 个人简历 / QNAP NAS 安装包
|
||||||
|
|
||||||
|
这套包面向威联通 QNAP QTS / Container Station,已按以下参数预置:
|
||||||
|
|
||||||
|
- NAS 数据目录:`/share/Container/Reactive_Resume_Personal`
|
||||||
|
- 本地端口:`3004:3000`
|
||||||
|
- 公网域名:`https://isiseg.huijutec.cn`
|
||||||
|
- 当前公开简历:`https://isiseg.huijutec.cn/audience/resume`
|
||||||
|
- FRP 服务器:`82.157.255.195:7000`
|
||||||
|
- FRP remotePort:`10004`
|
||||||
|
|
||||||
|
包内已包含当前简历初始化数据、头像和作品集图片,首次启动后会由 `reactive_resume_seed` 自动导入。
|
||||||
|
|
||||||
|
## 部署
|
||||||
|
|
||||||
|
1. 将本安装包内容放到 `/share/Container/Reactive_Resume_Personal`
|
||||||
|
2. 打开 Container Station,导入 `/share/Container/Reactive_Resume_Personal/compose-Nas.yml`
|
||||||
|
3. 启动项目
|
||||||
|
4. 访问 `https://isiseg.huijutec.cn/audience/resume`
|
||||||
|
|
||||||
|
发布归档中的顶层目录已经命名为 `Reactive_Resume_Personal/`,正常解压到 `/share/Container/` 下即可匹配上述路径。
|
||||||
|
|
||||||
|
## 反向代理要求
|
||||||
|
|
||||||
|
公网服务器上的 Nginx Proxy Manager / 反向代理应配置:
|
||||||
|
|
||||||
|
- Domain Names:`isiseg.huijutec.cn`
|
||||||
|
- Scheme:`http`
|
||||||
|
- Forward Hostname / IP:`82.157.255.195`
|
||||||
|
- Forward Port:`10004`
|
||||||
|
- Websockets Support:开启
|
||||||
|
|
||||||
|
## 数据目录
|
||||||
|
|
||||||
|
- PostgreSQL:`/share/Container/Reactive_Resume_Personal/data/postgres`
|
||||||
|
- 上传与本地存储:`/share/Container/Reactive_Resume_Personal/data/uploads`
|
||||||
|
- 初始化种子:`/share/Container/Reactive_Resume_Personal/seed`
|
||||||
145
packages/reactive-resume-personal-qnap-nas/compose-Nas.yml
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# Reactive Resume 个人简历 / QNAP QTS 直接部署版。
|
||||||
|
# 本文件已按 /share/Container/Reactive_Resume_Personal、
|
||||||
|
# https://isiseg.huijutec.cn、192.168.31.5:3004 本地访问、
|
||||||
|
# frpc 公网映射 82.157.255.195:10004 预置。
|
||||||
|
|
||||||
|
name: reactive-resume-personal-nas
|
||||||
|
|
||||||
|
services:
|
||||||
|
reactive_resume_permissions:
|
||||||
|
image: alpine:3.20
|
||||||
|
restart: "no"
|
||||||
|
command: ["sh", "-c", "mkdir -p /app/data && chown -R 1000:1000 /app/data"]
|
||||||
|
volumes:
|
||||||
|
- /share/Container/Reactive_Resume_Personal/data/uploads:/app/data
|
||||||
|
|
||||||
|
reactive_resume_db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: reactive_resume
|
||||||
|
POSTGRES_USER: reactive_resume
|
||||||
|
POSTGRES_PASSWORD: 5b341c0ca29fefd6d648661150c00fa4
|
||||||
|
volumes:
|
||||||
|
- /share/Container/Reactive_Resume_Personal/data/postgres:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U reactive_resume -d reactive_resume"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
|
||||||
|
reactive_resume_app:
|
||||||
|
image: amruthpillai/reactive-resume:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
reactive_resume_permissions:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
reactive_resume_db:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- "3004:3000"
|
||||||
|
volumes:
|
||||||
|
- /share/Container/Reactive_Resume_Personal/data/uploads:/app/data
|
||||||
|
environment:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
APP_URL: https://isiseg.huijutec.cn
|
||||||
|
DATABASE_URL: postgresql://reactive_resume:5b341c0ca29fefd6d648661150c00fa4@reactive_resume_db:5432/reactive_resume
|
||||||
|
AUTH_SECRET: c76b0eaf79f731e9ee95918dc69d41696aec9d1deffeabc122944898037bfab1
|
||||||
|
ENCRYPTION_SECRET: df3a460fa2f92f6e8765927a169322980e18f63a88fbcfedb090819b5afb2408
|
||||||
|
|
||||||
|
BETTER_AUTH_API_KEY: ""
|
||||||
|
GOOGLE_CLIENT_ID: ""
|
||||||
|
GOOGLE_CLIENT_SECRET: ""
|
||||||
|
GITHUB_CLIENT_ID: ""
|
||||||
|
GITHUB_CLIENT_SECRET: ""
|
||||||
|
LINKEDIN_CLIENT_ID: ""
|
||||||
|
LINKEDIN_CLIENT_SECRET: ""
|
||||||
|
OAUTH_PROVIDER_NAME: ""
|
||||||
|
OAUTH_CLIENT_ID: ""
|
||||||
|
OAUTH_CLIENT_SECRET: ""
|
||||||
|
OAUTH_DISCOVERY_URL: ""
|
||||||
|
OAUTH_AUTHORIZATION_URL: ""
|
||||||
|
OAUTH_TOKEN_URL: ""
|
||||||
|
OAUTH_USER_INFO_URL: ""
|
||||||
|
OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS: ""
|
||||||
|
OAUTH_SCOPES: ""
|
||||||
|
SMTP_HOST: ""
|
||||||
|
SMTP_PORT: "587"
|
||||||
|
SMTP_USER: ""
|
||||||
|
SMTP_PASS: ""
|
||||||
|
SMTP_FROM: "Reactive Resume <noreply@isiseg.huijutec.cn>"
|
||||||
|
SMTP_SECURE: "false"
|
||||||
|
S3_ACCESS_KEY_ID: ""
|
||||||
|
S3_SECRET_ACCESS_KEY: ""
|
||||||
|
S3_REGION: us-east-1
|
||||||
|
S3_ENDPOINT: ""
|
||||||
|
S3_BUCKET: ""
|
||||||
|
S3_FORCE_PATH_STYLE: "false"
|
||||||
|
REDIS_URL: ""
|
||||||
|
CLOUDFLARE_ACCOUNT_ID: ""
|
||||||
|
CLOUDFLARE_API_TOKEN: ""
|
||||||
|
FLAG_DISABLE_SIGNUPS: "false"
|
||||||
|
FLAG_DISABLE_EMAIL_AUTH: "false"
|
||||||
|
FLAG_DISABLE_IMAGE_PROCESSING: "false"
|
||||||
|
FLAG_ALLOW_UNSAFE_AI_BASE_URL: "false"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:3000/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 8
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
reactive_resume_seed:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
restart: "no"
|
||||||
|
user: "0:0"
|
||||||
|
entrypoint: ["/bin/sh"]
|
||||||
|
command:
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -eu
|
||||||
|
mkdir -p /app/data/uploads
|
||||||
|
cp -a /seed/uploads/. /app/data/uploads/
|
||||||
|
chown -R 1000:1000 /app/data/uploads || true
|
||||||
|
psql -h reactive_resume_db -U reactive_resume -d reactive_resume -v ON_ERROR_STOP=1 -f /seed/seed.sql
|
||||||
|
environment:
|
||||||
|
PGPASSWORD: 5b341c0ca29fefd6d648661150c00fa4
|
||||||
|
volumes:
|
||||||
|
- /share/Container/Reactive_Resume_Personal/data/uploads:/app/data
|
||||||
|
- /share/Container/Reactive_Resume_Personal/seed:/seed:ro
|
||||||
|
depends_on:
|
||||||
|
reactive_resume_permissions:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
reactive_resume_db:
|
||||||
|
condition: service_healthy
|
||||||
|
reactive_resume_app:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
reactive_resume_frpc:
|
||||||
|
image: snowdreamtech/frpc:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
entrypoint: ["/bin/sh"]
|
||||||
|
command:
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
cat > /tmp/frpc.toml <<'EOF'
|
||||||
|
serverAddr = "82.157.255.195"
|
||||||
|
serverPort = 7000
|
||||||
|
|
||||||
|
auth.method = "token"
|
||||||
|
auth.token = "en.xjtu.edu.cn"
|
||||||
|
|
||||||
|
transport.poolCount = 5
|
||||||
|
transport.heartbeatTimeout = -1
|
||||||
|
|
||||||
|
[[proxies]]
|
||||||
|
name = "Reactive_Resume_Personal_NAS"
|
||||||
|
type = "tcp"
|
||||||
|
localIP = "reactive_resume_app"
|
||||||
|
localPort = 3000
|
||||||
|
remotePort = 10004
|
||||||
|
EOF
|
||||||
|
exec frpc -c /tmp/frpc.toml
|
||||||
|
depends_on:
|
||||||
|
reactive_resume_seed:
|
||||||
|
condition: service_completed_successfully
|
||||||
1250
packages/reactive-resume-personal-qnap-nas/seed/resume-data.json
Normal file
1326
packages/reactive-resume-personal-qnap-nas/seed/seed.sql
Normal file
|
After Width: | Height: | Size: 6.4 MiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 329 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 191 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 812 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 558 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 754 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 311 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 97 KiB |
34
scripts/patch-reactive-resume-filename.sh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
CONTAINER="${1:-reactive-resume-reactive-resume-1}"
|
||||||
|
|
||||||
|
docker exec -u root -i "$CONTAINER" sh <<'SH'
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
PUBLIC_FILE="/app/apps/web/.output/public/assets/file-D5WsIgJH.js"
|
||||||
|
SSR_FILE="/app/apps/web/.output/server/_ssr/pdf-document-COfeOLVC.mjs"
|
||||||
|
|
||||||
|
cp "$PUBLIC_FILE" "$PUBLIC_FILE.bak-filename" 2>/dev/null || true
|
||||||
|
cp "$SSR_FILE" "$SSR_FILE.bak-filename" 2>/dev/null || true
|
||||||
|
|
||||||
|
node - <<'NODE'
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const publicFile = '/app/apps/web/.output/public/assets/file-D5WsIgJH.js';
|
||||||
|
let publicJs = fs.readFileSync(publicFile, 'utf8');
|
||||||
|
publicJs = publicJs.replace(
|
||||||
|
/function t\(t,n\)\{return`\$\{e\(t\)\}\$\{n\?`\.\$\{n\}`:""\}`\}/,
|
||||||
|
'function t(e,t){let n=(e||"resume").toString().trim()||"resume";return`${n}${t?`.${t}`:""}`}'
|
||||||
|
);
|
||||||
|
fs.writeFileSync(publicFile, publicJs);
|
||||||
|
|
||||||
|
const ssrFile = '/app/apps/web/.output/server/_ssr/pdf-document-COfeOLVC.mjs';
|
||||||
|
let ssr = fs.readFileSync(ssrFile, 'utf8');
|
||||||
|
ssr = ssr.replace(
|
||||||
|
/function generateFilename\(prefix, extension\) \{\n\s*return `\$\{slugify\(prefix\)\}\$\{extension \? `\.\$\{extension\}` : ""\}`;\n\}/,
|
||||||
|
'function generateFilename(prefix, extension) {\n\tconst filename = (prefix || "resume").toString().trim() || "resume";\n\treturn `${filename}${extension ? `.${extension}` : ""}`;\n}'
|
||||||
|
);
|
||||||
|
fs.writeFileSync(ssrFile, ssr);
|
||||||
|
NODE
|
||||||
|
SH
|
||||||
209
scripts/patch-reactive-resume-glalie-layout.sh
Executable file
@@ -0,0 +1,209 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
CONTAINER="${1:-reactive-resume-reactive-resume-1}"
|
||||||
|
|
||||||
|
docker exec -u root -i "$CONTAINER" sh <<'SH'
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
SSR_FILE="/app/apps/web/.output/server/_ssr/pdf-document-COfeOLVC.mjs"
|
||||||
|
PUBLIC_FILE="/app/apps/web/.output/public/assets/pdf-document-BplbXx-0.js"
|
||||||
|
SERVER_INDEX_FILE="/app/apps/web/.output/server/index.mjs"
|
||||||
|
CACHE_BUST="rr-glalie-layout-20260518"
|
||||||
|
|
||||||
|
test -f "$SSR_FILE.bak-glalie-layout" || cp "$SSR_FILE" "$SSR_FILE.bak-glalie-layout" 2>/dev/null || true
|
||||||
|
test -f "$PUBLIC_FILE.bak-glalie-layout" || cp "$PUBLIC_FILE" "$PUBLIC_FILE.bak-glalie-layout" 2>/dev/null || true
|
||||||
|
test -f "$SERVER_INDEX_FILE.bak-glalie-layout" || cp "$SERVER_INDEX_FILE" "$SERVER_INDEX_FILE.bak-glalie-layout" 2>/dev/null || true
|
||||||
|
|
||||||
|
node - <<'NODE'
|
||||||
|
const fs = require("fs");
|
||||||
|
const crypto = require("crypto");
|
||||||
|
|
||||||
|
const ssrFile = "/app/apps/web/.output/server/_ssr/pdf-document-COfeOLVC.mjs";
|
||||||
|
const publicFile = "/app/apps/web/.output/public/assets/pdf-document-BplbXx-0.js";
|
||||||
|
const serverIndexFile = "/app/apps/web/.output/server/index.mjs";
|
||||||
|
const cacheBust = "rr-glalie-layout-20260518";
|
||||||
|
|
||||||
|
function replaceOnce(source, from, to, label) {
|
||||||
|
if (source.includes(to)) return source;
|
||||||
|
if (!source.includes(from)) throw new Error(`Patch marker not found: ${label}`);
|
||||||
|
return source.replace(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceRegexOnce(source, regex, to, label) {
|
||||||
|
if (source.includes(to)) return source;
|
||||||
|
const next = source.replace(regex, to);
|
||||||
|
if (next === source) throw new Error(`Patch marker not found: ${label}`);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchSsr(source) {
|
||||||
|
source = source
|
||||||
|
.replace(/const sideMargin = bodyLineHeight \* \.(?:2|08);/, "const sideMargin = bodyLineHeight * .08;")
|
||||||
|
.replace(/metrics\.gapY\(3\.5\)/g, "metrics.gapY(3.0)")
|
||||||
|
.replace(/metrics\.gapY\(2\.6\)/g, "metrics.gapY(3.0)")
|
||||||
|
.replace(/metrics\.gapY\(2\.2\)/g, "metrics.gapY(3.0)");
|
||||||
|
|
||||||
|
source = source
|
||||||
|
.replace(
|
||||||
|
/style: composeStyles\(styles\.sidebarContent, \{ rowGap: metrics\.sectionGap \}\),/g,
|
||||||
|
"style: composeStyles(styles.sidebarContent, { rowGap: metrics.gapY(2.2) }),",
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
/style: composeStyles\(styles\.mainContent, \{ rowGap: metrics\.sectionGap \}\),/g,
|
||||||
|
"style: composeStyles(styles.mainContent, { rowGap: metrics.gapY(2.2) }),",
|
||||||
|
);
|
||||||
|
source = source.replace(
|
||||||
|
/sectionHeading: \{\s*borderBottomWidth: 1,\s*borderBottomColor: primary(?:,\s*paddingBottom: 1(?:\.3)?)?\s*\},/,
|
||||||
|
`sectionHeading: {
|
||||||
|
\t\t\t\t\tborderBottomWidth: 1,
|
||||||
|
\t\t\t\t\tborderBottomColor: primary,
|
||||||
|
\t\t\t\t\tpaddingBottom: 1.3
|
||||||
|
\t\t\t\t},`,
|
||||||
|
);
|
||||||
|
source = replaceRegexOnce(
|
||||||
|
source,
|
||||||
|
/sectionHeading: \{\s*borderBottomWidth: 1,\s*borderBottomColor: primary,\s*paddingBottom: 1(?:\.3)?\s*\},\s*item: \{ rowGap: metrics\.gapY\(\.125\) \},/,
|
||||||
|
`sectionHeading: {
|
||||||
|
\t\t\t\t\tborderBottomWidth: 1,
|
||||||
|
\t\t\t\t\tborderBottomColor: primary,
|
||||||
|
\t\t\t\t\tpaddingBottom: 1.3
|
||||||
|
\t\t\t\t},
|
||||||
|
\t\t\t\tsectionItems: { paddingTop: metrics.gapY(.55) },
|
||||||
|
\t\t\t\titem: { rowGap: metrics.gapY(.2) },`,
|
||||||
|
"SSR Glalie section item spacing",
|
||||||
|
);
|
||||||
|
source = replaceRegexOnce(
|
||||||
|
source,
|
||||||
|
/sidebarColumn: \{\s*zIndex: 1,\s*backgroundColor: primaryTint,\s*paddingHorizontal: metrics\.page\.paddingHorizontal,\s*paddingTop: metrics\.page\.paddingVertical,\s*(?:paddingBottom: metrics\.page\.paddingVertical,\s*)?rowGap: (?:metrics\.sectionGap|metrics\.gapY\([^)]+\))\s*\},/,
|
||||||
|
`sidebarColumn: {
|
||||||
|
\t\t\t\t\tzIndex: 1,
|
||||||
|
\t\t\t\t\tbackgroundColor: primaryTint,
|
||||||
|
\t\t\t\t\tpaddingHorizontal: metrics.page.paddingHorizontal,
|
||||||
|
\t\t\t\t\tpaddingTop: metrics.page.paddingVertical,
|
||||||
|
\t\t\t\t\tpaddingBottom: metrics.page.paddingVertical,
|
||||||
|
\t\t\t\t\trowGap: metrics.gapY(3.0)
|
||||||
|
\t\t\t\t},`,
|
||||||
|
"SSR Glalie sidebar bottom padding",
|
||||||
|
);
|
||||||
|
source = replaceRegexOnce(
|
||||||
|
source,
|
||||||
|
/mainContent: \{\s*paddingHorizontal: metrics\.page\.paddingHorizontal,\s*paddingTop: metrics\.page\.paddingVertical,\s*(?:paddingBottom: metrics\.page\.paddingVertical\s*)?\},/,
|
||||||
|
`mainContent: {
|
||||||
|
\t\t\t\t\tpaddingHorizontal: metrics.page.paddingHorizontal,
|
||||||
|
\t\t\t\t\tpaddingTop: metrics.page.paddingVertical,
|
||||||
|
\t\t\t\t\tpaddingBottom: metrics.page.paddingVertical
|
||||||
|
\t\t\t\t},`,
|
||||||
|
"SSR Glalie main bottom padding",
|
||||||
|
);
|
||||||
|
return source
|
||||||
|
.replace(/const sideMargin = bodyLineHeight \* \.(?:2|08);/, "const sideMargin = bodyLineHeight * .08;")
|
||||||
|
.replace(/metrics\.gapY\(3\.5\)/g, "metrics.gapY(3.0)")
|
||||||
|
.replace(/metrics\.gapY\(2\.6\)/g, "metrics.gapY(3.0)")
|
||||||
|
.replace(/metrics\.gapY\(2\.2\)/g, "metrics.gapY(3.0)");
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchPublic(source) {
|
||||||
|
source = source
|
||||||
|
.replace(/([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*)\*\.(?:2|08);return\{paragraph:\{marginTop:\1,marginBottom:\1\},listItem:\{marginTop:\1,marginBottom:\1\}\}/,
|
||||||
|
"$1=$2*.08;return{paragraph:{marginTop:$1,marginBottom:$1},listItem:{marginTop:$1,marginBottom:$1}}")
|
||||||
|
.replace(/o\.gapY\(3\.5\)/g, "o.gapY(3.0)")
|
||||||
|
.replace(/c\.gapY\(3\.5\)/g, "c.gapY(3.0)")
|
||||||
|
.replace(/o\.gapY\(2\.6\)/g, "o.gapY(3.0)")
|
||||||
|
.replace(/c\.gapY\(2\.6\)/g, "c.gapY(3.0)")
|
||||||
|
.replace(/o\.gapY\(2\.2\)/g, "o.gapY(3.0)")
|
||||||
|
.replace(/c\.gapY\(2\.2\)/g, "c.gapY(3.0)");
|
||||||
|
|
||||||
|
source = source
|
||||||
|
.replace(/style:\$\(a\.sidebarContent,\{rowGap:o\.sectionGap\}\)/g, "style:$(a.sidebarContent,{rowGap:o.gapY(3.0)})")
|
||||||
|
.replace(/style:\$\(a\.mainContent,\{rowGap:o\.sectionGap\}\)/g, "style:$(a.mainContent,{rowGap:o.gapY(3.0)})");
|
||||||
|
source = source.replace(
|
||||||
|
/sectionHeading:\{borderBottomWidth:1,borderBottomColor:a(?:,paddingBottom:1(?:\.3)?)?\}/,
|
||||||
|
"sectionHeading:{borderBottomWidth:1,borderBottomColor:a,paddingBottom:1.3}",
|
||||||
|
);
|
||||||
|
source = source.replace(
|
||||||
|
/sectionHeading:\{borderBottomWidth:1,borderBottomColor:a,paddingBottom:1(?:\.3)?\},item:\{rowGap:([a-zA-Z_$][\w$]*)\.gapY\(\.125\)\}/,
|
||||||
|
"sectionHeading:{borderBottomWidth:1,borderBottomColor:a,paddingBottom:1.3},sectionItems:{paddingTop:$1.gapY(.55)},item:{rowGap:$1.gapY(.2)}",
|
||||||
|
);
|
||||||
|
source = replaceOnce(
|
||||||
|
source,
|
||||||
|
"sidebarColumn:{zIndex:1,backgroundColor:o,paddingHorizontal:c.page.paddingHorizontal,paddingTop:c.page.paddingVertical,rowGap:c.sectionGap}",
|
||||||
|
"sidebarColumn:{zIndex:1,backgroundColor:o,paddingHorizontal:c.page.paddingHorizontal,paddingTop:c.page.paddingVertical,paddingBottom:c.page.paddingVertical,rowGap:c.gapY(3.0)}",
|
||||||
|
"public Glalie sidebar bottom padding",
|
||||||
|
);
|
||||||
|
source = replaceOnce(
|
||||||
|
source,
|
||||||
|
"mainContent:{paddingHorizontal:c.page.paddingHorizontal,paddingTop:c.page.paddingVertical}",
|
||||||
|
"mainContent:{paddingHorizontal:c.page.paddingHorizontal,paddingTop:c.page.paddingVertical,paddingBottom:c.page.paddingVertical}",
|
||||||
|
"public Glalie main bottom padding",
|
||||||
|
);
|
||||||
|
return source
|
||||||
|
.replace(/([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*)\*\.(?:2|08);return\{paragraph:\{marginTop:\1,marginBottom:\1\},listItem:\{marginTop:\1,marginBottom:\1\}\}/,
|
||||||
|
"$1=$2*.08;return{paragraph:{marginTop:$1,marginBottom:$1},listItem:{marginTop:$1,marginBottom:$1}}")
|
||||||
|
.replace(/o\.gapY\(3\.5\)/g, "o.gapY(3.0)")
|
||||||
|
.replace(/c\.gapY\(3\.5\)/g, "c.gapY(3.0)")
|
||||||
|
.replace(/o\.gapY\(2\.6\)/g, "o.gapY(3.0)")
|
||||||
|
.replace(/c\.gapY\(2\.6\)/g, "c.gapY(3.0)")
|
||||||
|
.replace(/o\.gapY\(2\.2\)/g, "o.gapY(3.0)")
|
||||||
|
.replace(/c\.gapY\(2\.2\)/g, "c.gapY(3.0)");
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchImporters() {
|
||||||
|
const assetsDir = "/app/apps/web/.output/public/assets";
|
||||||
|
const files = fs
|
||||||
|
.readdirSync(assetsDir)
|
||||||
|
.filter((name) => name.endsWith(".js"))
|
||||||
|
.map((name) => `${assetsDir}/${name}`)
|
||||||
|
.filter((file) => fs.readFileSync(file, "utf8").includes("pdf-document-BplbXx-0.js"));
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
let source = fs.readFileSync(file, "utf8");
|
||||||
|
source = source.replace(/\.\/pdf-document-BplbXx-0\.js(?:\?v=rr-glalie-layout-20260518)?/g, `./pdf-document-BplbXx-0.js?v=${cacheBust}`);
|
||||||
|
fs.writeFileSync(file, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeEtag(buffer) {
|
||||||
|
const digest = crypto.createHash("sha1").update(buffer).digest("base64").replace(/=+$/g, "");
|
||||||
|
return `"${buffer.length.toString(16)}-${digest}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchStaticManifestEntry(source, urlPath, filePath) {
|
||||||
|
const buffer = fs.readFileSync(filePath);
|
||||||
|
const startMarker = `"${urlPath}": {`;
|
||||||
|
const start = source.indexOf(startMarker);
|
||||||
|
if (start === -1) throw new Error(`Static manifest entry not found for ${urlPath}`);
|
||||||
|
|
||||||
|
const commaEnd = source.indexOf("\n\t},", start);
|
||||||
|
const objectEnd = source.indexOf("\n\t}", start);
|
||||||
|
const end = commaEnd === -1 ? objectEnd : Math.min(commaEnd, objectEnd);
|
||||||
|
if (end === -1) throw new Error(`Static manifest entry end not found for ${urlPath}`);
|
||||||
|
|
||||||
|
let entry = source.slice(start, end);
|
||||||
|
entry = entry
|
||||||
|
.replace(/"etag": "(?:\\.|[^"\\])*"/, `"etag": ${JSON.stringify(makeEtag(buffer))}`)
|
||||||
|
.replace(/"mtime": "(?:\\.|[^"\\])*"/, `"mtime": ${JSON.stringify(new Date().toISOString())}`)
|
||||||
|
.replace(/"size": \d+/, `"size": ${buffer.length}`);
|
||||||
|
|
||||||
|
return source.slice(0, start) + entry + source.slice(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(ssrFile, patchSsr(fs.readFileSync(ssrFile, "utf8")));
|
||||||
|
fs.writeFileSync(publicFile, patchPublic(fs.readFileSync(publicFile, "utf8")));
|
||||||
|
|
||||||
|
const importers = patchImporters();
|
||||||
|
let serverIndex = fs.readFileSync(serverIndexFile, "utf8");
|
||||||
|
serverIndex = patchStaticManifestEntry(serverIndex, "/assets/pdf-document-BplbXx-0.js", publicFile);
|
||||||
|
for (const file of importers) {
|
||||||
|
const urlPath = `/assets/${file.split("/").pop()}`;
|
||||||
|
serverIndex = patchStaticManifestEntry(serverIndex, urlPath, file);
|
||||||
|
}
|
||||||
|
fs.writeFileSync(serverIndexFile, serverIndex);
|
||||||
|
NODE
|
||||||
|
|
||||||
|
node --check "$SSR_FILE" >/dev/null
|
||||||
|
node --check "$PUBLIC_FILE" >/dev/null
|
||||||
|
node --check "$SERVER_INDEX_FILE" >/dev/null
|
||||||
|
SH
|
||||||
161
scripts/patch-reactive-resume-service-worker-cache.sh
Executable file
@@ -0,0 +1,161 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
CONTAINER="${1:-reactive-resume-reactive-resume-1}"
|
||||||
|
|
||||||
|
docker exec -u root -i "$CONTAINER" sh <<'SH'
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
SSR_FILE="/app/apps/web/.output/server/_ssr/pdf-document-COfeOLVC.mjs"
|
||||||
|
SW_FILE="/app/apps/web/.output/public/sw.js"
|
||||||
|
SERVER_INDEX_FILE="/app/apps/web/.output/server/index.mjs"
|
||||||
|
SSR_RENDERER_FILE="/app/apps/web/.output/server/_chunks/ssr-renderer.mjs"
|
||||||
|
|
||||||
|
test -f "$SSR_FILE.bak-sw-cache" || cp "$SSR_FILE" "$SSR_FILE.bak-sw-cache" 2>/dev/null || true
|
||||||
|
test -f "$SW_FILE.bak-sw-cache" || cp "$SW_FILE" "$SW_FILE.bak-sw-cache" 2>/dev/null || true
|
||||||
|
test -f "$SERVER_INDEX_FILE.bak-sw-cache" || cp "$SERVER_INDEX_FILE" "$SERVER_INDEX_FILE.bak-sw-cache" 2>/dev/null || true
|
||||||
|
test -f "$SSR_RENDERER_FILE.bak-sw-cache" || cp "$SSR_RENDERER_FILE" "$SSR_RENDERER_FILE.bak-sw-cache" 2>/dev/null || true
|
||||||
|
|
||||||
|
node - <<'NODE'
|
||||||
|
const fs = require("fs");
|
||||||
|
const crypto = require("crypto");
|
||||||
|
|
||||||
|
const ssrFile = "/app/apps/web/.output/server/_ssr/pdf-document-COfeOLVC.mjs";
|
||||||
|
const swFile = "/app/apps/web/.output/public/sw.js";
|
||||||
|
const serverIndexFile = "/app/apps/web/.output/server/index.mjs";
|
||||||
|
const ssrRendererFile = "/app/apps/web/.output/server/_chunks/ssr-renderer.mjs";
|
||||||
|
|
||||||
|
const registrationScript = `
|
||||||
|
\t(() => {
|
||||||
|
\t\tif (!("serviceWorker" in navigator)) return;
|
||||||
|
|
||||||
|
\t\twindow.addEventListener("load", () => {
|
||||||
|
\t\t\tconst clearReactiveResumeCaches = async () => {
|
||||||
|
\t\t\t\tif ("caches" in window) {
|
||||||
|
\t\t\t\t\tconst keys = await caches.keys();
|
||||||
|
\t\t\t\t\tawait Promise.all(keys.map((key) => caches.delete(key)));
|
||||||
|
\t\t\t\t}
|
||||||
|
|
||||||
|
\t\t\t\tif (navigator.serviceWorker.getRegistrations) {
|
||||||
|
\t\t\t\t\tconst registrations = await navigator.serviceWorker.getRegistrations();
|
||||||
|
\t\t\t\t\tawait Promise.all(registrations.map((registration) => registration.unregister()));
|
||||||
|
\t\t\t\t}
|
||||||
|
\t\t\t};
|
||||||
|
|
||||||
|
\t\t\tclearReactiveResumeCaches().catch(console.error);
|
||||||
|
\t\t});
|
||||||
|
\t})();
|
||||||
|
`;
|
||||||
|
|
||||||
|
let ssr = fs.readFileSync(ssrFile, "utf8");
|
||||||
|
const start = "var pwaServiceWorkerRegistrationScript = `";
|
||||||
|
const end = "`;\nvar src_default =";
|
||||||
|
const startIndex = ssr.indexOf(start);
|
||||||
|
if (startIndex === -1) {
|
||||||
|
throw new Error("Service worker registration script start marker not found");
|
||||||
|
}
|
||||||
|
const endIndex = ssr.indexOf(end, startIndex + start.length);
|
||||||
|
if (endIndex === -1) {
|
||||||
|
throw new Error("Service worker registration script end marker not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
ssr =
|
||||||
|
ssr.slice(0, startIndex) +
|
||||||
|
start +
|
||||||
|
registrationScript +
|
||||||
|
ssr.slice(endIndex);
|
||||||
|
fs.writeFileSync(ssrFile, ssr);
|
||||||
|
|
||||||
|
const sw = `self.addEventListener("install", () => {
|
||||||
|
self.skipWaiting();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("activate", (event) => {
|
||||||
|
event.waitUntil((async () => {
|
||||||
|
const keys = await caches.keys();
|
||||||
|
await Promise.all(keys.map((key) => caches.delete(key)));
|
||||||
|
await self.registration.unregister();
|
||||||
|
await self.clients.claim();
|
||||||
|
|
||||||
|
const clients = await self.clients.matchAll({
|
||||||
|
type: "window",
|
||||||
|
includeUncontrolled: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const client of clients) {
|
||||||
|
client.postMessage({ type: "RR_SW_CACHE_CLEARED" });
|
||||||
|
}
|
||||||
|
})());
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("fetch", () => {});
|
||||||
|
`;
|
||||||
|
fs.writeFileSync(swFile, sw);
|
||||||
|
|
||||||
|
function makeEtag(buffer) {
|
||||||
|
const digest = crypto.createHash("sha1").update(buffer).digest("base64").replace(/=+$/g, "");
|
||||||
|
return `"${buffer.length.toString(16)}-${digest}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchStaticManifestEntry(source, urlPath, filePath) {
|
||||||
|
const buffer = fs.readFileSync(filePath);
|
||||||
|
const startMarker = `"${urlPath}": {`;
|
||||||
|
const start = source.indexOf(startMarker);
|
||||||
|
if (start === -1) {
|
||||||
|
throw new Error(`Static manifest entry not found for ${urlPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const end = source.indexOf("\n\t},", start);
|
||||||
|
if (end === -1) {
|
||||||
|
throw new Error(`Static manifest entry end not found for ${urlPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry = source.slice(start, end);
|
||||||
|
entry = entry
|
||||||
|
.replace(/"etag": "(?:\\.|[^"\\])*"/, `"etag": ${JSON.stringify(makeEtag(buffer))}`)
|
||||||
|
.replace(/"mtime": "(?:\\.|[^"\\])*"/, `"mtime": ${JSON.stringify(new Date().toISOString())}`)
|
||||||
|
.replace(/"size": \d+/, `"size": ${buffer.length}`);
|
||||||
|
|
||||||
|
return source.slice(0, start) + entry + source.slice(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
let serverIndex = fs.readFileSync(serverIndexFile, "utf8");
|
||||||
|
serverIndex = patchStaticManifestEntry(serverIndex, "/sw.js", swFile);
|
||||||
|
fs.writeFileSync(serverIndexFile, serverIndex);
|
||||||
|
|
||||||
|
let ssrRenderer = fs.readFileSync(ssrRendererFile, "utf8");
|
||||||
|
const ssrRendererOriginal = `function ssrRenderer({ req }) {
|
||||||
|
\treturn fetchViteEnv("ssr", req);
|
||||||
|
}`;
|
||||||
|
const ssrRendererPatched = `async function ssrRenderer(event) {
|
||||||
|
\tconst response = await fetchViteEnv("ssr", event.req);
|
||||||
|
\tconst headers = new Headers(response.headers);
|
||||||
|
\tconst accept = event.req.headers.get("accept") || "";
|
||||||
|
|
||||||
|
\tif (accept.includes("text/html")) {
|
||||||
|
\t\theaders.set("Cache-Control", "no-store, max-age=0");
|
||||||
|
\t\theaders.set("Pragma", "no-cache");
|
||||||
|
\t\theaders.set("Expires", "0");
|
||||||
|
\t}
|
||||||
|
|
||||||
|
\treturn new Response(response.body, {
|
||||||
|
\t\tstatus: response.status,
|
||||||
|
\t\tstatusText: response.statusText,
|
||||||
|
\t\theaders,
|
||||||
|
\t});
|
||||||
|
}`;
|
||||||
|
if (!ssrRenderer.includes(ssrRendererPatched)) {
|
||||||
|
if (!ssrRenderer.includes(ssrRendererOriginal)) {
|
||||||
|
throw new Error("SSR renderer marker not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
ssrRenderer = ssrRenderer.replace(ssrRendererOriginal, ssrRendererPatched);
|
||||||
|
fs.writeFileSync(ssrRendererFile, ssrRenderer);
|
||||||
|
}
|
||||||
|
NODE
|
||||||
|
|
||||||
|
node --check "$SSR_FILE" >/dev/null
|
||||||
|
node --check "$SW_FILE" >/dev/null
|
||||||
|
node --check "$SERVER_INDEX_FILE" >/dev/null
|
||||||
|
node --check "$SSR_RENDERER_FILE" >/dev/null
|
||||||
|
SH
|
||||||
1159
生成简历/backups/王志博-医工智能外科求职简历-备份-20260516T013051.json
Normal file
1
生成简历/backups/王志博-医工智能外科简历-关键词隐藏前备份-20260518T225621.json
Normal file
1173
生成简历/backups/王志博-医工智能外科简历-备份-20260517T011333.json
Normal file
1188
生成简历/backups/王志博-医工智能外科简历-备份-20260517T013114.json
Normal file
1209
生成简历/backups/王志博-医工智能外科简历-备份-20260517T094516.json
Normal file
1188
生成简历/backups/王志博-医工智能外科简历-备份-20260518T163821.json
Normal file
1188
生成简历/backups/王志博-医工智能外科简历-备份-20260518T175949.json
Normal file
1
生成简历/backups/王志博-医工智能外科简历-志愿活动调整前备份-20260518T205516.json
Normal file
1188
生成简历/backups/王志博-医工智能外科简历-排版优化前备份-20260518T194756.json
Normal file
1167
生成简历/backups/王志博-医工智能外科简历-线上调整备份-20260517T004222.json
Normal file
1
生成简历/backups/王志博-医工智能外科简历-行距侧栏调整前备份-20260518T213410.json
Normal file
@@ -4,8 +4,8 @@
|
|||||||
"email": "zub572701190@stu.xjtu.edu.cn",
|
"email": "zub572701190@stu.xjtu.edu.cn",
|
||||||
"phone": "+86 139-4611-2059",
|
"phone": "+86 139-4611-2059",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "https://me.huijutec.cn/audience/resume",
|
||||||
"label": ""
|
"label": "me.huijutec.cn/audience/resume"
|
||||||
},
|
},
|
||||||
"headline": "AI 医工交叉博士|智能外科与微创手术导航|多模态大模型与临床转化",
|
"headline": "AI 医工交叉博士|智能外科与微创手术导航|多模态大模型与临床转化",
|
||||||
"location": "陕西西安|西安交通大学",
|
"location": "陕西西安|西安交通大学",
|
||||||
@@ -46,19 +46,19 @@
|
|||||||
"title": "个人简介",
|
"title": "个人简介",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"columns": 1,
|
"columns": 1,
|
||||||
"content": "<p>西安交通大学未来技术学院医工学方向博士研究生,中共党员,少年班与人工智能试验班背景。长期面向微创外科、术中影像分析、智能导航和图文病历生成等真实临床场景,推进<strong>医学问题定义、AI 算法研发、系统工程实现、专利论文产出、竞赛验证与成果转化</strong>的闭环实践,形成兼具科研深度、工程落地和组织协同能力的医工交叉特色。</p>"
|
"content": "<p>西安交通大学未来技术学院医工学方向博士研究生,中共党员,少年班与人工智能试验班背景。长期面向微创外科、术中影像分析、智能导航和图文病历生成等真实临床场景,推进<strong>医学问题定义、AI 算法研发、系统工程实现、专利论文产出、竞赛验证与成果转化</strong>的闭环实践,形成兼具科研深度、工程落地和组织协同能力的医工交叉特色</p>"
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"page": {
|
"page": {
|
||||||
"gapX": 4.4,
|
"gapX": 3.8,
|
||||||
"gapY": 2.6,
|
"gapY": 0.56,
|
||||||
"format": "a4",
|
"format": "a4",
|
||||||
"locale": "zh-CN",
|
"locale": "zh-CN",
|
||||||
"marginX": 8.2,
|
"marginX": 8.4,
|
||||||
"marginY": 6.4,
|
"marginY": 8.8,
|
||||||
"hideIcons": false
|
"hideIcons": false
|
||||||
},
|
},
|
||||||
"notes": "Design direction: refined clinical-tech academic profile. Glalie two-column layout, wider A4 margins, CJK-first typography, navy-blue medical technology accent, and two-page hierarchy for personal achievements and translational impact.",
|
"notes": "Design direction: refined clinical-tech academic profile. Glalie two-column layout with CJK-safe line height, balanced list rhythm, moderate section spacing, a continued second-page sidebar, and split volunteer/social activity evidence.",
|
||||||
"design": {
|
"design": {
|
||||||
"level": {
|
"level": {
|
||||||
"icon": "star",
|
"icon": "star",
|
||||||
@@ -71,38 +71,46 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"layout": {
|
"layout": {
|
||||||
"sidebarWidth": 29.5,
|
|
||||||
"pages": [
|
"pages": [
|
||||||
{
|
{
|
||||||
"fullWidth": false,
|
|
||||||
"main": [
|
"main": [
|
||||||
"summary",
|
"summary",
|
||||||
"awards",
|
"awards",
|
||||||
"education",
|
"education",
|
||||||
"projects",
|
"projects",
|
||||||
"experience",
|
"1014b66d-3de1-4bf4-903c-93e7f07c8f81"
|
||||||
"1014b66d-3de1-4bf4-903c-93e7f07c8f81",
|
|
||||||
"54972f49-3c83-4912-b429-dc659a02eda9",
|
|
||||||
"36ba1eaf-0984-4863-9b4b-691374ee27aa",
|
|
||||||
"certifications",
|
|
||||||
"752ddba0-3400-4e33-8f54-49fc3a4b57b9",
|
|
||||||
"559335bd-99c4-44c2-97b6-420b011415f7"
|
|
||||||
],
|
],
|
||||||
"sidebar": [
|
"sidebar": [
|
||||||
|
"f4fa59b9-34cf-41b0-b324-f07f07934fc2",
|
||||||
"skills",
|
"skills",
|
||||||
"languages",
|
"languages",
|
||||||
"802bfd76-af47-4c5d-8b04-f89a38932dcd",
|
"802bfd76-af47-4c5d-8b04-f89a38932dcd",
|
||||||
"volunteer"
|
"volunteer",
|
||||||
]
|
"4a9c2385-2f79-4d50-99cf-6d2aaef0b6c6"
|
||||||
|
],
|
||||||
|
"fullWidth": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": [
|
||||||
|
"experience",
|
||||||
|
"54972f49-3c83-4912-b429-dc659a02eda9",
|
||||||
|
"752ddba0-3400-4e33-8f54-49fc3a4b57b9",
|
||||||
|
"certifications",
|
||||||
|
"36ba1eaf-0984-4863-9b4b-691374ee27aa",
|
||||||
|
"559335bd-99c4-44c2-97b6-420b011415f7"
|
||||||
|
],
|
||||||
|
"sidebar": [],
|
||||||
|
"fullWidth": false
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"sidebarWidth": 29.2
|
||||||
},
|
},
|
||||||
"template": "glalie",
|
"template": "glalie",
|
||||||
"typography": {
|
"typography": {
|
||||||
"body": {
|
"body": {
|
||||||
"fontSize": 6.95,
|
"fontSize": 6.9,
|
||||||
"fontFamily": "Noto Sans SC",
|
"fontFamily": "Noto Sans SC",
|
||||||
"lineHeight": 1.22,
|
"lineHeight": 1.34,
|
||||||
"fontWeights": [
|
"fontWeights": [
|
||||||
"400",
|
"400",
|
||||||
"500",
|
"500",
|
||||||
@@ -110,9 +118,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"heading": {
|
"heading": {
|
||||||
"fontSize": 9.25,
|
"fontSize": 9.15,
|
||||||
"fontFamily": "Noto Serif SC",
|
"fontFamily": "Noto Serif SC",
|
||||||
"lineHeight": 1.18,
|
"lineHeight": 1.28,
|
||||||
"fontWeights": [
|
"fontWeights": [
|
||||||
"600",
|
"600",
|
||||||
"700"
|
"700"
|
||||||
@@ -123,19 +131,6 @@
|
|||||||
"sections": {
|
"sections": {
|
||||||
"awards": {
|
"awards": {
|
||||||
"items": [
|
"items": [
|
||||||
{
|
|
||||||
"id": "d05f036d-f81e-4090-a6a1-0dd53eb2e6ba",
|
|
||||||
"date": "2023",
|
|
||||||
"title": "博士国家奖学金(证书号 BSY202303836)",
|
|
||||||
"hidden": false,
|
|
||||||
"awarder": "教育部",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "f25ead8c-57eb-4421-8d7c-494d0e7c0118",
|
"id": "f25ead8c-57eb-4421-8d7c-494d0e7c0118",
|
||||||
"date": "2024",
|
"date": "2024",
|
||||||
@@ -150,8 +145,47 @@
|
|||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3d2e410a-cc9a-46f9-98db-d8e3128ef07c",
|
"id": "d05f036d-f81e-4090-a6a1-0dd53eb2e6ba",
|
||||||
"date": "2023",
|
"date": "2023",
|
||||||
|
"title": "博士国家奖学金(证书号 BSY202303836)",
|
||||||
|
"hidden": false,
|
||||||
|
"awarder": "教育部",
|
||||||
|
"website": {
|
||||||
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c81e732c-8a95-406b-adcd-f8f56db00407",
|
||||||
|
"date": "2025",
|
||||||
|
"title": "陕西省研究生创新成果展 A 类成果",
|
||||||
|
"hidden": false,
|
||||||
|
"awarder": "陕西省教育厅",
|
||||||
|
"website": {
|
||||||
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "09cfe17f-a90d-42cd-8ca6-dbabc817782a",
|
||||||
|
"date": "2024",
|
||||||
|
"title": "陕西省研究生创新成果展 A 类成果",
|
||||||
|
"hidden": false,
|
||||||
|
"awarder": "陕西省教育厅",
|
||||||
|
"website": {
|
||||||
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4bb24894-f47a-4e97-a980-8b1171a3a148",
|
||||||
|
"date": "2025",
|
||||||
"title": "西安交通大学优秀研究生",
|
"title": "西安交通大学优秀研究生",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "西安交通大学",
|
"awarder": "西安交通大学",
|
||||||
@@ -176,8 +210,8 @@
|
|||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "4bb24894-f47a-4e97-a980-8b1171a3a148",
|
"id": "3d2e410a-cc9a-46f9-98db-d8e3128ef07c",
|
||||||
"date": "2025",
|
"date": "2023",
|
||||||
"title": "西安交通大学优秀研究生",
|
"title": "西安交通大学优秀研究生",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "西安交通大学",
|
"awarder": "西安交通大学",
|
||||||
@@ -189,24 +223,11 @@
|
|||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "09cfe17f-a90d-42cd-8ca6-dbabc817782a",
|
"id": "eb4b8f1f-65e2-4287-a114-be59b47406d8",
|
||||||
"date": "2024",
|
|
||||||
"title": "陕西省研究生创新成果展 A 类成果",
|
|
||||||
"hidden": false,
|
|
||||||
"awarder": "陕西省",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "c81e732c-8a95-406b-adcd-f8f56db00407",
|
|
||||||
"date": "2025",
|
"date": "2025",
|
||||||
"title": "陕西省研究生创新成果展 A 类成果",
|
"title": "未来技术太湖奖学金“创业实践一等奖”",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "陕西省",
|
"awarder": "西安交通大学",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -240,19 +261,6 @@
|
|||||||
},
|
},
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "eb4b8f1f-65e2-4287-a114-be59b47406d8",
|
|
||||||
"date": "2025",
|
|
||||||
"title": "未来技术太湖奖学金“创业实践一等奖”",
|
|
||||||
"hidden": false,
|
|
||||||
"awarder": "西安交通大学",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "80648972-0619-48d9-82a3-7011f9e37a3c",
|
"id": "80648972-0619-48d9-82a3-7011f9e37a3c",
|
||||||
"date": "2023",
|
"date": "2023",
|
||||||
@@ -361,31 +369,31 @@
|
|||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"description": "<ul><li>面向微创外科手术记录“纯文字、难回溯、医生负荷重”的痛点,研发基于术中视频和医学知识库的图文病历自动生成系统。</li><li>融合微调 LLM、多模态视觉语言模型、RAG 检索增强生成与 Mamba 智能体架构,构建“端-边-云”协同体系。</li><li>单份图文报告生成时间缩短至约 <strong>30 秒</strong>,关键步骤识别精度超过 <strong>90%</strong>;累计生成 <strong>3000 余份</strong>图文手术报告。</li><li>形成授权发明专利与软著,入选陕西首批“数据要素×”典型案例,获新华社、中国教育报等媒体报道。</li></ul>"
|
"description": "<ul><li>面向微创外科手术记录“纯文字、难回溯、医生负荷重”的痛点,研发基于术中视频和医学知识库的图文病历自动生成系统</li><li>融合微调 LLM、多模态视觉语言模型、RAG 检索增强生成与 Mamba 智能体架构,构建“端-边-云”协同体系</li><li>单份图文报告生成时间缩短至约 <strong>30 秒</strong>,关键步骤识别精度超过 <strong>90%</strong>;累计生成 <strong>3000 余份</strong>图文手术报告</li><li>形成授权发明专利与软著,入选陕西首批“数据要素×”典型案例,获新华社、中国教育报等媒体报道</li></ul>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "17beb594-5513-4788-9db8-77cde270a476",
|
"id": "17beb594-5513-4788-9db8-77cde270a476",
|
||||||
"name": "多设备兼容的术中影像记录分析系统",
|
"name": "多设备兼容的术中影像记录分析系统",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2025",
|
"period": "2025 - 至今",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"description": "<ul><li>入选陕西首批 30 个“数据要素×”典型案例医疗健康方向。</li><li>面向微创外科精准度与安全性提升,支持多设备兼容、术中影像记录、智能分析与质控。</li></ul>"
|
"description": "<ul><li>入选陕西首批 30 个“数据要素×”典型案例医疗健康方向</li><li>面向微创外科精准度与安全性提升,支持多设备兼容、术中影像记录、智能分析与质控</li></ul>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "64fe9ea6-7efc-47f8-bed0-2fa883211ef4",
|
"id": "64fe9ea6-7efc-47f8-bed0-2fa883211ef4",
|
||||||
"name": "微创化手术智能导航平台建设",
|
"name": "微创化手术智能导航平台建设",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2024 - 2026",
|
"period": "2024 - 至今",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"description": "<ul><li>西安交通大学第一附属医院医智慧研究院横向项目,工科负责人之一。</li><li>围绕 CT、超声、腔镜图像开展分割识别、手术去雾、低剂量 CT 重建、多模态配准和临床报告生成探索。</li><li>支撑中国国际大学生创新大赛、“华为杯”中国研究生人工智能创新大赛、“数据要素×”等项目。</li></ul>"
|
"description": "<ul><li>西安交通大学第一附属医院医智慧研究院横向项目,工科负责人之一</li><li>围绕 CT、超声、腔镜图像开展分割识别、手术去雾、低剂量 CT 重建、多模态配准和临床报告生成探索</li><li>支撑中国国际大学生创新大赛、“华为杯”中国研究生人工智能创新大赛、“数据要素×”等项目</li></ul>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "d6691fc3-c992-482d-8592-3904b9bb1af1",
|
"id": "d6691fc3-c992-482d-8592-3904b9bb1af1",
|
||||||
@@ -397,7 +405,7 @@
|
|||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"description": "<ul><li>西安交通大学基本科研业务费基础研究专项“自由探索学生类项目”,主持。</li><li>探索多模态磁导航、器官模型配准和术中导航融合方法,支撑相关授权发明专利。</li></ul>"
|
"description": "<ul><li>西安交通大学基本科研业务费基础研究专项“自由探索学生类项目”,主持,已结题</li><li>探索多模态磁导航、器官模型配准和术中导航融合方法,支撑相关授权发明专利</li></ul>"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "核心项目",
|
"title": "核心项目",
|
||||||
@@ -420,7 +428,7 @@
|
|||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "西安",
|
"location": "西安",
|
||||||
"description": "<ul><li>研究方向:医工交叉、AI 腔镜外科导航、术中影像记录分析、多模态大模型与图文报告生成。</li><li>现任医工学博士党支部宣传委员、未来技术学院 B2275 班学习委员。</li></ul>"
|
"description": "<ul><li><u><strong>课题组:</strong>西安交通大学外科梦工场吕毅教授课题组;<strong>导师:</strong>吴荣谦教授</u></li><li>研究方向:医工交叉、AI 腔镜外科导航、术中影像记录分析、多模态大模型与图文报告生成</li><li>现任医工学博士党支部宣传委员、未来技术学院 B2275 班学习委员</li></ul>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "17863b1a-cfbc-4990-b870-ff3485048b40",
|
"id": "17863b1a-cfbc-4990-b870-ff3485048b40",
|
||||||
@@ -428,7 +436,7 @@
|
|||||||
"grade": "",
|
"grade": "",
|
||||||
"degree": "本科",
|
"degree": "本科",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2018.08 - 2022.07",
|
"period": "2018.09 - 2022.07",
|
||||||
"school": "西安交通大学人工智能学院",
|
"school": "西安交通大学人工智能学院",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
@@ -436,7 +444,7 @@
|
|||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "西安",
|
"location": "西安",
|
||||||
"description": "<p>人工智能背景,支撑后续医学影像算法、临床大模型和智能外科工程研发。</p>"
|
"description": "<p>人工智能背景,支撑后续医学影像算法、临床大模型和智能外科工程研发</p>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "125d97f1-ba3f-4331-9998-005d534c5aeb",
|
"id": "125d97f1-ba3f-4331-9998-005d534c5aeb",
|
||||||
@@ -465,7 +473,7 @@
|
|||||||
"id": "469cc8d5-b041-4f02-9ce5-83961cc00727",
|
"id": "469cc8d5-b041-4f02-9ce5-83961cc00727",
|
||||||
"icon": "stethoscope",
|
"icon": "stethoscope",
|
||||||
"name": "医工交叉",
|
"name": "医工交叉",
|
||||||
"hidden": false,
|
"hidden": true,
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"AI + 临床",
|
"AI + 临床",
|
||||||
"智能外科"
|
"智能外科"
|
||||||
@@ -476,7 +484,7 @@
|
|||||||
"id": "43ab8051-a0e5-422e-b2b1-90c6a30311da",
|
"id": "43ab8051-a0e5-422e-b2b1-90c6a30311da",
|
||||||
"icon": "rocket",
|
"icon": "rocket",
|
||||||
"name": "成果转化",
|
"name": "成果转化",
|
||||||
"hidden": false,
|
"hidden": true,
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"创业资助",
|
"创业资助",
|
||||||
"专利转化",
|
"专利转化",
|
||||||
@@ -488,7 +496,7 @@
|
|||||||
"id": "4721af36-ae2a-47f4-9852-4d71e7f5e44a",
|
"id": "4721af36-ae2a-47f4-9852-4d71e7f5e44a",
|
||||||
"icon": "database",
|
"icon": "database",
|
||||||
"name": "数据要素",
|
"name": "数据要素",
|
||||||
"hidden": false,
|
"hidden": true,
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"术中影像",
|
"术中影像",
|
||||||
"质控",
|
"质控",
|
||||||
@@ -498,7 +506,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "关键词",
|
"title": "关键词",
|
||||||
"hidden": false,
|
"hidden": true,
|
||||||
"columns": 1
|
"columns": 1
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
@@ -520,54 +528,54 @@
|
|||||||
{
|
{
|
||||||
"id": "50c41f06-d048-48d6-a5b0-c67521104562",
|
"id": "50c41f06-d048-48d6-a5b0-c67521104562",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2022 - 至今",
|
"period": "",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "西安交通大学",
|
"location": "",
|
||||||
"description": "<p>宣传委员。</p>",
|
"description": "<p>2022 - 至今<br/>宣传委员</p>",
|
||||||
"organization": "未来技术学院医工学博士党支部"
|
"organization": "未来技术学院医工学博士党支部"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bbc0e308-86db-4894-a136-24b80555d25c",
|
"id": "bbc0e308-86db-4894-a136-24b80555d25c",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2024",
|
"period": "",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "西安交通大学",
|
"location": "",
|
||||||
"description": "<p>学习部负责人;第十五次研究生代表大会代表。</p>",
|
"description": "<p>2024<br/>学习部负责人;第十五次研究生代表大会代表</p>",
|
||||||
"organization": "未来技术学院学生会 / 研究生会"
|
"organization": "未来技术学院学生会 / 研究生会"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "72a41ed7-2ce6-4c36-a524-aac20b6a7bef",
|
"id": "72a41ed7-2ce6-4c36-a524-aac20b6a7bef",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2026",
|
"period": "",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "西安交通大学",
|
"location": "",
|
||||||
"description": "<p>第一次党员代表大会代表。</p>",
|
"description": "<p>2026<br/>第一次党员代表大会代表</p>",
|
||||||
"organization": "未来技术学院党员代表大会"
|
"organization": "未来技术学院党员代表大会"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "0e5bd75f-e635-44c7-8379-43fdc2dc45b8",
|
"id": "0e5bd75f-e635-44c7-8379-43fdc2dc45b8",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2022 - 至今",
|
"period": "",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "西安交通大学",
|
"location": "",
|
||||||
"description": "<p>学习委员。</p>",
|
"description": "<p>2022 - 至今<br/>学习委员</p>",
|
||||||
"organization": "未来技术学院 B2275 班"
|
"organization": "未来技术学院 B2275 班班委"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "校内任职",
|
"title": "校内任职",
|
||||||
@@ -589,7 +597,7 @@
|
|||||||
},
|
},
|
||||||
"location": "",
|
"location": "",
|
||||||
"position": "主要工科参与人",
|
"position": "主要工科参与人",
|
||||||
"description": "<p>基于多模态磁导航技术的困难气道插管系统的研究;49 万元,在研。</p>"
|
"description": "<p>基于多模态磁导航技术的困难气道插管系统的研究;49 万元;在研</p>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "0b95f627-8395-4242-9f60-8fa0f1bb491c",
|
"id": "0b95f627-8395-4242-9f60-8fa0f1bb491c",
|
||||||
@@ -603,8 +611,8 @@
|
|||||||
"inlineLink": true
|
"inlineLink": true
|
||||||
},
|
},
|
||||||
"location": "",
|
"location": "",
|
||||||
"position": "在研",
|
"position": "主要工科参与人(第三序位)",
|
||||||
"description": "<p>微创化手术智能导航平台建设。</p>"
|
"description": "<p>微创化手术智能导航平台建设;70万元;在研</p>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "47a5d869-535f-42e2-8ac7-bd927853b973",
|
"id": "47a5d869-535f-42e2-8ac7-bd927853b973",
|
||||||
@@ -619,7 +627,7 @@
|
|||||||
},
|
},
|
||||||
"location": "",
|
"location": "",
|
||||||
"position": "主持",
|
"position": "主持",
|
||||||
"description": "<p>磁定位辅助多模态融合微创手术组织自动配准系统;3 万元,在研。</p>"
|
"description": "<p>磁定位辅助多模态融合微创手术组织自动配准系统;3 万元;结题</p>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "affc3464-2cad-4ea4-8399-162bd2c2e2eb",
|
"id": "affc3464-2cad-4ea4-8399-162bd2c2e2eb",
|
||||||
@@ -633,8 +641,8 @@
|
|||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "",
|
"location": "",
|
||||||
"position": "结题",
|
"position": "主要工科参与人(第三序位)",
|
||||||
"description": "<p>微创手术导航训练虚拟平台建设研究。</p>"
|
"description": "<p>微创手术导航训练虚拟平台建设研究;20万元,结题</p>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "eb837f99-e47f-4ec9-9c8b-0fb879302889",
|
"id": "eb837f99-e47f-4ec9-9c8b-0fb879302889",
|
||||||
@@ -648,8 +656,8 @@
|
|||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "",
|
"location": "",
|
||||||
"position": "参与第二位,结题",
|
"position": "主要工科参与人(第三序位)",
|
||||||
"description": "<p>微创化手术智能导航平台建设;3 万元。</p>"
|
"description": "<p>微创化手术智能导航平台建设;3 万元;结题</p>"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "科研与获批项目",
|
"title": "科研与获批项目",
|
||||||
@@ -675,7 +683,7 @@
|
|||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"publisher": "Frontiers in Oncology",
|
"publisher": "Frontiers in Oncology",
|
||||||
"description": "<p>共同第一作者。</p>"
|
"description": "<p>共同第一作者</p>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "857e2a52-7818-404c-8350-2df1aed6cc4b",
|
"id": "857e2a52-7818-404c-8350-2df1aed6cc4b",
|
||||||
@@ -688,7 +696,7 @@
|
|||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"publisher": "中华肝脏外科手术学电子杂志",
|
"publisher": "中华肝脏外科手术学电子杂志",
|
||||||
"description": "<p>共同一作方向,腔镜术中图像处理。</p>"
|
"description": "<p>共同一作作者,腔镜术中图像处理</p>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "97e962cf-49ca-434c-92c2-b73e8fc64147",
|
"id": "97e962cf-49ca-434c-92c2-b73e8fc64147",
|
||||||
@@ -701,7 +709,7 @@
|
|||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"publisher": "中华肝脏外科手术学电子杂志",
|
"publisher": "中华肝脏外科手术学电子杂志",
|
||||||
"description": "<p>共同一作方向,腔镜肝脏外科与扩展现实综述。</p>"
|
"description": "<p>共同一作作者,腔镜肝脏外科与扩展现实综述</p>"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "论文与会议",
|
"title": "论文与会议",
|
||||||
@@ -715,7 +723,7 @@
|
|||||||
"date": "ZL 2022 1 1431692.6",
|
"date": "ZL 2022 1 1431692.6",
|
||||||
"title": "一种抗干扰单面导通微针电极及制备方法",
|
"title": "一种抗干扰单面导通微针电极及制备方法",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"issuer": "发明专利",
|
"issuer": "第一发明人 授权发明专利",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -728,7 +736,7 @@
|
|||||||
"date": "ZL 2022 1 0478532.0",
|
"date": "ZL 2022 1 0478532.0",
|
||||||
"title": "一种具有绝缘薄膜的微针阵列电极及其制备方法",
|
"title": "一种具有绝缘薄膜的微针阵列电极及其制备方法",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"issuer": "发明专利",
|
"issuer": "第二发明人 授权发明专利",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -741,7 +749,7 @@
|
|||||||
"date": "ZL 2024 1 1527344.8",
|
"date": "ZL 2024 1 1527344.8",
|
||||||
"title": "一种微创腔镜图文报告生成系统",
|
"title": "一种微创腔镜图文报告生成系统",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"issuer": "发明专利",
|
"issuer": "第二发明人 授权发明专利",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -754,7 +762,7 @@
|
|||||||
"date": "ZL 2025 1 0096041.3",
|
"date": "ZL 2025 1 0096041.3",
|
||||||
"title": "一种实时术中腔镜影像分析装置及方法",
|
"title": "一种实时术中腔镜影像分析装置及方法",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"issuer": "发明专利",
|
"issuer": "第二发明人 授权发明专利",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -767,7 +775,7 @@
|
|||||||
"date": "ZL 2025 1 0896530.7",
|
"date": "ZL 2025 1 0896530.7",
|
||||||
"title": "模型与器官配准以辅助导航的方法及系统",
|
"title": "模型与器官配准以辅助导航的方法及系统",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"issuer": "发明专利",
|
"issuer": "第二发明人 授权发明专利",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -780,7 +788,7 @@
|
|||||||
"date": "ZL 2025 1 0933524.4",
|
"date": "ZL 2025 1 0933524.4",
|
||||||
"title": "一种喉部的体外磁靶标定位导航方法及系统",
|
"title": "一种喉部的体外磁靶标定位导航方法及系统",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"issuer": "发明专利",
|
"issuer": "第二发明人 授权发明专利",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -793,7 +801,7 @@
|
|||||||
"date": "ZL 2025 1 0766744.2",
|
"date": "ZL 2025 1 0766744.2",
|
||||||
"title": "一种带有旋转永磁定位和选择性脑降温功能的气管导管装置及其温度控制方法",
|
"title": "一种带有旋转永磁定位和选择性脑降温功能的气管导管装置及其温度控制方法",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"issuer": "发明专利",
|
"issuer": "第二发明人 授权发明专利",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -806,7 +814,7 @@
|
|||||||
"date": "ZL 2023 1 0062566.6",
|
"date": "ZL 2023 1 0062566.6",
|
||||||
"title": "一种多模态脑信号的意识状态分类系统及方法",
|
"title": "一种多模态脑信号的意识状态分类系统及方法",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"issuer": "发明专利",
|
"issuer": "授权发明专利",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -819,7 +827,7 @@
|
|||||||
"date": "ZL 2022 1 1379675.2",
|
"date": "ZL 2022 1 1379675.2",
|
||||||
"title": "一种用于临床意识检测的多模态信号采集眼罩",
|
"title": "一种用于临床意识检测的多模态信号采集眼罩",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"issuer": "发明专利",
|
"issuer": "授权发明专利",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -867,9 +875,9 @@
|
|||||||
{
|
{
|
||||||
"id": "c16d28b2-3a20-4c5e-b1ef-419139406aad",
|
"id": "c16d28b2-3a20-4c5e-b1ef-419139406aad",
|
||||||
"date": "2025",
|
"date": "2025",
|
||||||
"title": "全国大学生电子商务“创新、创意及创业”挑战赛(三创赛)国赛一等奖",
|
"title": "国家级一等奖|数智生命",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "三创赛组委会",
|
"awarder": "全国大学生电子商务“创新、创意及创业”挑战赛(三创赛) / 三创赛竞赛组织委员会",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -880,35 +888,9 @@
|
|||||||
{
|
{
|
||||||
"id": "5c350966-0a9f-4c51-8bcc-77c16af29a32",
|
"id": "5c350966-0a9f-4c51-8bcc-77c16af29a32",
|
||||||
"date": "2025",
|
"date": "2025",
|
||||||
"title": "第七届智慧医疗创新大赛国赛三等奖",
|
"title": "国家级三等奖|智康智影——智能外科向临床盲点进军",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "智慧医疗创新大赛",
|
"awarder": "第七届智慧医疗创新大赛 / 智慧医疗创新大赛组委会",
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "f9966d23-051a-4b4b-91b5-37238f32194d",
|
|
||||||
"date": "2024",
|
|
||||||
"title": "中国国际大学生创新大赛陕西赛区一等奖/金奖",
|
|
||||||
"hidden": false,
|
|
||||||
"awarder": "中国国际大学生创新大赛",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "bef745cf-22a4-42b2-9ab7-677325da0b99",
|
|
||||||
"date": "2024",
|
|
||||||
"title": "“数据要素×”大赛陕西分赛医疗健康赛道一等奖",
|
|
||||||
"hidden": false,
|
|
||||||
"awarder": "陕西分赛组委会",
|
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -919,9 +901,9 @@
|
|||||||
{
|
{
|
||||||
"id": "de9d4e86-e8b1-4330-ba88-c3dc399a8f50",
|
"id": "de9d4e86-e8b1-4330-ba88-c3dc399a8f50",
|
||||||
"date": "2024",
|
"date": "2024",
|
||||||
"title": "“挑战杯”大学生创业计划竞赛国赛三等奖",
|
"title": "国家级三等奖|杏林创涂——新一代纳米抗菌涂层领跑者",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "挑战杯",
|
"awarder": "“挑战杯”中国大学生创业计划竞赛 / 共青团中央、教育部等",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -932,22 +914,9 @@
|
|||||||
{
|
{
|
||||||
"id": "f974f854-cf64-4559-8adb-7da582dd8848",
|
"id": "f974f854-cf64-4559-8adb-7da582dd8848",
|
||||||
"date": "2024",
|
"date": "2024",
|
||||||
"title": "中国医疗器械创新创业大赛国赛二等奖",
|
"title": "国家级二等奖|新一代纳米抗菌涂层领跑者",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "中国医疗器械创新创业大赛",
|
"awarder": "第七届中国医疗器械创新创业大赛 / 中国医疗器械创新创业大赛组委会",
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "36b7e7bc-1e0f-439e-8f3a-f72f80c7a3a4",
|
|
||||||
"date": "2024",
|
|
||||||
"title": "中美青年创客大赛省赛一等奖",
|
|
||||||
"hidden": false,
|
|
||||||
"awarder": "中美青年创客大赛",
|
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -958,9 +927,9 @@
|
|||||||
{
|
{
|
||||||
"id": "066baf54-be1a-4c56-8cb5-d02d33f62a2d",
|
"id": "066baf54-be1a-4c56-8cb5-d02d33f62a2d",
|
||||||
"date": "2024",
|
"date": "2024",
|
||||||
"title": "第六届智慧医疗创新大赛国赛二等奖",
|
"title": "国家级二等奖|云链智康——新一代智能外科机要助理",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "智慧医疗创新大赛",
|
"awarder": "第六届智慧医疗创新大赛 / 智慧医疗创新大赛组委会",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -971,9 +940,9 @@
|
|||||||
{
|
{
|
||||||
"id": "4b5cc7f2-f4af-484a-93ee-cc588cde720a",
|
"id": "4b5cc7f2-f4af-484a-93ee-cc588cde720a",
|
||||||
"date": "2023",
|
"date": "2023",
|
||||||
"title": "产业融合发展——新工科创新大赛国赛一等奖",
|
"title": "国家级一等奖|云链智康——AI 辅助智能外科助手",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "新工科创新大赛",
|
"awarder": "产业融合发展——新工科创新大赛 / 工业和信息化部工业文化发展中心",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -984,9 +953,9 @@
|
|||||||
{
|
{
|
||||||
"id": "6a635d4a-ac93-4093-8574-6deba541d5c7",
|
"id": "6a635d4a-ac93-4093-8574-6deba541d5c7",
|
||||||
"date": "2023",
|
"date": "2023",
|
||||||
"title": "“华为杯”中国研究生人工智能创新大赛国赛二等奖",
|
"title": "国家级二等奖|云链智康:数据驱动下的临床微创手术新模式",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "中国研究生人工智能创新大赛",
|
"awarder": "“华为杯”中国研究生人工智能创新大赛 / 中国学位与研究生教育学会、中国科协青少年科技中心",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -997,9 +966,9 @@
|
|||||||
{
|
{
|
||||||
"id": "1171ae8b-e753-48f2-9866-c0cc50dbdbe2",
|
"id": "1171ae8b-e753-48f2-9866-c0cc50dbdbe2",
|
||||||
"date": "2023",
|
"date": "2023",
|
||||||
"title": "第二届中国研究生“双碳”创新与创意大赛国赛二等奖",
|
"title": "国家级二等奖",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "中国研究生“双碳”创新与创意大赛",
|
"awarder": "第二届中国研究生“双碳”创新与创意大赛 / 中国学位与研究生教育学会、中国科协青少年科技中心",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -1010,9 +979,48 @@
|
|||||||
{
|
{
|
||||||
"id": "2ee9d130-c5e6-4ba3-a1b7-6864726d4cee",
|
"id": "2ee9d130-c5e6-4ba3-a1b7-6864726d4cee",
|
||||||
"date": "2023",
|
"date": "2023",
|
||||||
"title": "三创赛国赛二等奖",
|
"title": "国家级二等奖|术中宝——新一代术中医疗影像分析辅助装置",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "三创赛组委会",
|
"awarder": "全国大学生电子商务“创新、创意及创业”挑战赛(三创赛) / 三创赛竞赛组织委员会",
|
||||||
|
"website": {
|
||||||
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f9966d23-051a-4b4b-91b5-37238f32194d",
|
||||||
|
"date": "2024",
|
||||||
|
"title": "省部级一等奖|智康智影——数字化赋能外科全链条诊疗体系",
|
||||||
|
"hidden": false,
|
||||||
|
"awarder": "中国国际大学生创新大赛陕西赛区 / 陕西省教育厅",
|
||||||
|
"website": {
|
||||||
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bef745cf-22a4-42b2-9ab7-677325da0b99",
|
||||||
|
"date": "2024",
|
||||||
|
"title": "省部级一等奖|智康智影——数字化赋能新型临床体系构建",
|
||||||
|
"hidden": false,
|
||||||
|
"awarder": "“数据要素×”大赛陕西分赛医疗健康赛道 / 陕西省数据和政务服务局",
|
||||||
|
"website": {
|
||||||
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "36b7e7bc-1e0f-439e-8f3a-f72f80c7a3a4",
|
||||||
|
"date": "2024",
|
||||||
|
"title": "省部级一等奖|适用于肝胆外科手术的术中智能导航平台",
|
||||||
|
"hidden": false,
|
||||||
|
"awarder": "中美青年创客大赛陕西赛区 / 教育部",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -1023,9 +1031,9 @@
|
|||||||
{
|
{
|
||||||
"id": "f9b63a59-7e30-4df5-984b-d9288288f1e2",
|
"id": "f9b63a59-7e30-4df5-984b-d9288288f1e2",
|
||||||
"date": "2023",
|
"date": "2023",
|
||||||
"title": "中国国际“互联网+”大学生创新创业大赛省赛一等奖/金奖",
|
"title": "省部级一等奖|云链智康——新一代智能外科机要助理",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"awarder": "互联网+ 大赛",
|
"awarder": "中国国际“互联网+”大学生创新创业大赛陕西赛区 / 陕西省教育厅",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -1041,119 +1049,202 @@
|
|||||||
{
|
{
|
||||||
"id": "802bfd76-af47-4c5d-8b04-f89a38932dcd",
|
"id": "802bfd76-af47-4c5d-8b04-f89a38932dcd",
|
||||||
"type": "volunteer",
|
"type": "volunteer",
|
||||||
"title": "学术组织任职",
|
|
||||||
"hidden": false,
|
|
||||||
"columns": 1,
|
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"id": "76390bf9-49d5-4fbc-98ab-6691477e982d",
|
"id": "76390bf9-49d5-4fbc-98ab-6691477e982d",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2024 - 至今",
|
"period": "",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "",
|
"location": "",
|
||||||
"organization": "中医药信息学会中西医外科智能诊疗分会",
|
"description": "<p>2024 - 至今<br/>理事</p>",
|
||||||
"description": "<p>理事。</p>"
|
"organization": "中医药信息学会中西医外科智能诊疗分会"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c2291187-b7c3-48a2-820a-40e06836072b",
|
"id": "c2291187-b7c3-48a2-820a-40e06836072b",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2024 - 至今",
|
"period": "",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "",
|
"location": "",
|
||||||
"organization": "中国抗癌协会",
|
"description": "<p>2024 - 至今<br/>青年理事</p>",
|
||||||
"description": "<p>青年理事。</p>"
|
"organization": "中国抗癌协会"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1f873e55-fde9-4b85-ab6f-d151d5b15d53",
|
"id": "1f873e55-fde9-4b85-ab6f-d151d5b15d53",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2024 - 至今",
|
"period": "",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "",
|
"location": "",
|
||||||
"organization": "中国医药教育协会数字医疗专业委员会",
|
"description": "<p>2024 - 至今<br/>数字医疗专业委员会委员</p>",
|
||||||
"description": "<p>委员。</p>"
|
"organization": "中国医药教育协会"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "e1bf3d6a-6018-4c3e-9330-3e7c3a2d89a1",
|
"id": "e1bf3d6a-6018-4c3e-9330-3e7c3a2d89a1",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"period": "2024 - 至今",
|
"period": "",
|
||||||
"website": {
|
"website": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"label": "",
|
"label": "",
|
||||||
"inlineLink": false
|
"inlineLink": false
|
||||||
},
|
},
|
||||||
"location": "",
|
"location": "",
|
||||||
"organization": "中国生物医学工程学会",
|
"description": "<p>2024 - 至今<br/>学生会员</p>",
|
||||||
"description": "<p>学生会员。</p>"
|
"organization": "中国生物医学工程学会"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"title": "学术组织任职",
|
||||||
|
"hidden": false,
|
||||||
|
"columns": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1014b66d-3de1-4bf4-903c-93e7f07c8f81",
|
"id": "1014b66d-3de1-4bf4-903c-93e7f07c8f81",
|
||||||
"type": "summary",
|
"type": "summary",
|
||||||
"title": "论文与会议",
|
|
||||||
"hidden": false,
|
|
||||||
"columns": 1,
|
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"id": "c16accb2-c907-44bf-aa0c-89e0085c6a50",
|
"id": "c16accb2-c907-44bf-aa0c-89e0085c6a50",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"content": "<p><strong>期刊论文</strong></p><ul><li><strong>Frontiers in Oncology, 2025</strong> — Development of an AI-driven digital assistance system for real-time safety evaluation and quality control in laparoscopic liver surgery(共同第一作者)</li><li><strong>中华肝脏外科手术学电子杂志, 2023/2024</strong> — 智能化辅助图像实时去雾技术在腹腔镜胆囊切除术中的应用研究(共同一作方向)</li><li><strong>中华肝脏外科手术学电子杂志, 2023</strong> — 增强现实、虚拟现实与混合现实在腔镜肝脏外科中的应用进展(共同一作方向)</li></ul><p><strong>ACS Clinical Congress 2025</strong></p><ul><li><strong>Oral</strong> — Biomarker Risk Scoring Algorithm And Preoperative Stratification In Patients With Lung Cancer</li><li><strong>Oral</strong> — Differences In Perioperative Complications Of Laparoscopic Cholecystectomy Under Voice Risk Alert System (V-RAS)-assisted Monitoring: Results From 48 Hospitals In Western China</li><li><strong>Oral</strong> — Intraoperative Smart Molecular Imaging And Recognition To Enhance Surgeons' Ability To Identify Lymph Node Metastasis In Thyroid Surgery</li><li><strong>Oral</strong> — Comparison Of Robot-assisted And Conventional Laparoscopic Hepatectomy For Perioperative Outcomes: A Clinical Randomized Controlled Trial (RCT)</li><li><strong>ePoster</strong> — Cost-effectiveness Of Endoscopic Magnetic Traction Technology Compared To Laparoscopic Heller Myotomy For The Treatment Of Achalasia</li></ul><p><strong>ACS / CMAIC / FIS 2023</strong></p><ul><li><strong>ePoster</strong> — Application Of Computer Intelligent Surgical Confidential Assistant In Laparoscopic Liver Resection</li><li><strong>ePoster</strong> — Intelligent Surgical Confidential Assistant Helps Precise Magnetic Assisted Vascular Anastomosis</li><li><strong>Poster</strong> — Intelligent Surgery Enters the Blind Spot of Lumpectomy Liver Resection</li><li><strong>Poster</strong> — Intelligent digital fogging technology shows great potential in laparoscopic hepatectomy surgery</li><li><strong>Poster</strong> — Prospects for intelligent surgical machine assistants in precision liver segment resection</li><li><strong>Poster</strong> — Application of Orthogonal Decomposition in Surgical Image Segmentation - for Unsupervised Adaptability in Intraoperative Surgical Image Recognition Navigation</li><li><strong>Poster</strong> — Intraoperative Image Detection and Clearing System Based on Generative Adversarial Network</li></ul>"
|
"content": "<p><strong>期刊论文</strong></p><ul><li><p><strong>Frontiers in Oncology, 2025</strong> — Development of an AI-driven digital assistance system for real-time safety evaluation and quality control in laparoscopic liver surgery(共同第一作者)</p></li><li><p><strong>中华肝脏外科手术学电子杂志, 2023</strong> — 智能化辅助图像实时去雾技术在腹腔镜胆囊切除术中的应用研究(共同一作作者)</p></li><li><p><strong>中华肝脏外科手术学电子杂志, 2023</strong> — 增强现实、虚拟现实与混合现实在腔镜肝脏外科中的应用进展(共同一作作者)</p></li></ul><p><strong>ACS Clinical Congress 2025</strong></p><ul><li><p><strong>Oral</strong> — Biomarker Risk Scoring Algorithm And Preoperative Stratification In Patients With Lung Cancer</p></li><li><p><strong>Oral</strong> — Differences In Perioperative Complications Of Laparoscopic Cholecystectomy Under Voice Risk Alert System (V-RAS)-assisted Monitoring: Results From 48 Hospitals In Western China</p></li><li><p><strong>Oral</strong> — Intraoperative Smart Molecular Imaging And Recognition To Enhance Surgeons Ability To Identify Lymph Node Metastasis In Thyroid Surgery</p></li><li><p><strong>Oral</strong> — Comparison Of Robot-assisted And Conventional Laparoscopic Hepatectomy For Perioperative Outcomes: A Clinical Randomized Controlled Trial (RCT)</p></li><li><p><strong>ePoster</strong> — Cost-effectiveness Of Endoscopic Magnetic Traction Technology Compared To Laparoscopic Heller Myotomy For The Treatment Of Achalasia</p></li></ul><p></p><p><strong>ACS / CMAIC / FIS 2023</strong></p><ul><li><strong>ePoster</strong> — Application Of Computer Intelligent Surgical Confidential Assistant In Laparoscopic Liver Resection</li><li><strong>ePoster</strong> — Intelligent Surgical Confidential Assistant Helps Precise Magnetic Assisted Vascular Anastomosis</li><li><strong>Poster</strong> — Intelligent Surgery Enters the Blind Spot of Lumpectomy Liver Resection</li><li><strong>Poster</strong> — Intelligent digital fogging technology shows great potential in laparoscopic hepatectomy surgery</li><li><strong>Poster</strong> — Prospects for intelligent surgical machine assistants in precision liver segment resection</li><li><strong>Poster</strong> — Application of Orthogonal Decomposition in Surgical Image Segmentation - for Unsupervised Adaptability in Intraoperative Surgical Image Recognition Navigation</li><li><strong>Poster</strong> — Intraoperative Image Detection and Clearing System Based on Generative Adversarial Network</li></ul>"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"title": "论文与会议",
|
||||||
|
"hidden": false,
|
||||||
|
"columns": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "54972f49-3c83-4912-b429-dc659a02eda9",
|
"id": "54972f49-3c83-4912-b429-dc659a02eda9",
|
||||||
"type": "summary",
|
"type": "summary",
|
||||||
"title": "创新创业与成果转化",
|
|
||||||
"hidden": false,
|
|
||||||
"columns": 1,
|
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"id": "8ed9e356-fdbf-4c6b-bf15-4033f40b85f5",
|
"id": "8ed9e356-fdbf-4c6b-bf15-4033f40b85f5",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"content": "<ul><li><strong>专利转化</strong>:2026 年相关专利完成 <strong>50 万元</strong>技术许可 / 转化公示。</li><li><strong>创业实践</strong>:2023 年依托大学生创新创业实践创立公司,获 <strong>30 万元</strong>创业资助。</li><li><strong>典型案例</strong>:2025 年“多设备兼容的术中影像记录分析系统”入选陕西首批 <strong>30 个</strong>“数据要素×”典型案例。</li></ul>"
|
"content": "<ul><li><p><strong>专利转化</strong>:2026 年相关专利完成 <strong>50 万元</strong>技术许可 / 转化公示</p></li><li><p><strong>创业实践</strong>:2023 年依托大学生创新创业实践创立公司,获 <strong>30 万元</strong>创业资助</p></li><li><p><strong>典型案例</strong>:2025 年“多设备兼容的术中影像记录分析系统”入选陕西<strong>首批“数据要素×”典型案例</strong></p></li></ul><p></p>"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"title": "创新创业与成果转化",
|
||||||
|
"hidden": false,
|
||||||
|
"columns": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "559335bd-99c4-44c2-97b6-420b011415f7",
|
"id": "559335bd-99c4-44c2-97b6-420b011415f7",
|
||||||
"type": "summary",
|
"type": "summary",
|
||||||
"title": "媒体报道",
|
|
||||||
"hidden": false,
|
|
||||||
"columns": 1,
|
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"id": "83f3ac59-c07d-4270-b786-9234eaf1da92",
|
"id": "83f3ac59-c07d-4270-b786-9234eaf1da92",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"content": "<ul><li><strong>新华社</strong>《西安交通大学:扎根西部传薪火 服务国家育英才》(2026)报道个人与项目实践。</li><li><strong>中国教育报</strong>《产学研“抱团”闯出创新路》(2025)关注创新港产学研协同实践。</li><li><strong>陕西省科学技术厅</strong>转载陕西网《后端深度融合:科学研究突破“围墙之困”》(2025)。</li></ul>"
|
"content": "<ul><li><p><strong>新华社</strong>《西安交通大学:扎根西部传薪火 服务国家育英才》(2026)130周年校庆专栏,其中报道本人与项目实践</p></li><li><p><strong>中国教育报</strong>《产学研“抱团”闯出创新路》(2025)关注创新港产学研协同实践,其中报道本人与项目实践</p></li><li><p><strong>陕西省科学技术厅</strong>转载《后端深度融合:科学研究突破“围墙之困”》(2025)关注复合型创新创业人才培养</p></li></ul><p></p>"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"title": "社会影响",
|
||||||
|
"hidden": false,
|
||||||
|
"columns": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "752ddba0-3400-4e33-8f54-49fc3a4b57b9",
|
"id": "752ddba0-3400-4e33-8f54-49fc3a4b57b9",
|
||||||
"type": "summary",
|
"type": "certifications",
|
||||||
"title": "编写著作",
|
|
||||||
"hidden": false,
|
|
||||||
"columns": 1,
|
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"id": "b71f628c-04ea-4d2b-9452-35f4dc0e0054",
|
"id": "b71f628c-04ea-4d2b-9452-35f4dc0e0054",
|
||||||
|
"date": "2024",
|
||||||
|
"title": "参编《2024中国数字医疗创新发展蓝皮书》",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"content": "<ul><li>编委参编《2024中国数字医疗创新发展蓝皮书》。</li></ul>"
|
"issuer": "编委",
|
||||||
}
|
"website": {
|
||||||
]
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "编写著作",
|
||||||
|
"hidden": false,
|
||||||
|
"columns": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f4fa59b9-34cf-41b0-b324-f07f07934fc2",
|
||||||
|
"type": "summary",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "dff431c1-739e-48f9-a835-6c8b1965c5a3",
|
||||||
|
"hidden": false,
|
||||||
|
"content": "<p><strong>西安交通大学外科梦工场</strong><br/>吕毅教授课题组<br/><strong>导师:</strong>吴荣谦教授</p>"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "所在课题组",
|
||||||
|
"hidden": false,
|
||||||
|
"columns": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4a9c2385-2f79-4d50-99cf-6d2aaef0b6c6",
|
||||||
|
"type": "volunteer",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "fc2f36e5-6737-4c77-a6e7-9d9a4f9f7250",
|
||||||
|
"hidden": false,
|
||||||
|
"period": "",
|
||||||
|
"website": {
|
||||||
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"location": "",
|
||||||
|
"description": "<p>2024<br/>科技副校长聘任 / 中学宣讲与科普服务</p>",
|
||||||
|
"organization": "西安市高陵区第四中学教育集团"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ce1f0d77-245c-4145-9b30-0ff274b31834",
|
||||||
|
"hidden": false,
|
||||||
|
"period": "",
|
||||||
|
"website": {
|
||||||
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"location": "",
|
||||||
|
"description": "<p>2022.12.30<br/>全血 400ml</p>",
|
||||||
|
"organization": "无偿献血"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a8b80562-00fc-42bb-ad5a-d8a0cd10bbbf",
|
||||||
|
"hidden": false,
|
||||||
|
"period": "",
|
||||||
|
"website": {
|
||||||
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"location": "",
|
||||||
|
"description": "<p>2023.06.03<br/>成分血 2 治疗量</p>",
|
||||||
|
"organization": "无偿献血"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "215ce4ab-7703-4d6e-9396-513c64877ead",
|
||||||
|
"hidden": false,
|
||||||
|
"period": "",
|
||||||
|
"website": {
|
||||||
|
"url": "",
|
||||||
|
"label": "",
|
||||||
|
"inlineLink": false
|
||||||
|
},
|
||||||
|
"location": "",
|
||||||
|
"description": "<p>2024.10.12<br/>全血 200ml</p>",
|
||||||
|
"organization": "无偿献血"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "志愿及社会活动",
|
||||||
|
"hidden": false,
|
||||||
|
"columns": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||