This commit is contained in:
2026-09-23 08:58:56 +08:00
parent 2555eda797
commit 5c01b34503
23 changed files with 790 additions and 54 deletions
@@ -0,0 +1,31 @@
name: firstleg-lingxing-${DEPLOY_ENV:-development}
services:
lingxing-browser:
build:
context: ./docker/lingxing-browser
image: ${LINGXING_BROWSER_IMAGE:-firstleg-lingxing-browser:dev}
container_name: firstleg-${DEPLOY_ENV:-development}-lingxing-browser
restart: unless-stopped
init: true
ports:
- "127.0.0.1:${LINGXING_NOVNC_PORT:-6080}:6080"
volumes:
- lingxing-browser-profile:/profile
networks:
- firstleg-network
healthcheck:
test: ["CMD-SHELL", "wget -q -O - http://127.0.0.1:9222/json/version >/dev/null || exit 1"]
interval: 15s
timeout: 5s
start_period: 30s
retries: 5
networks:
firstleg-network:
external: true
name: firstleg-${DEPLOY_ENV:-development}-network
volumes:
lingxing-browser-profile:
name: firstleg-${DEPLOY_ENV:-development}-lingxing-browser-profile
+20
View File
@@ -0,0 +1,20 @@
FROM alpine:3.21
ARG ALPINE_MIRROR=https://mirrors.aliyun.com/alpine
RUN sed -i "s#https://dl-cdn.alpinelinux.org/alpine#${ALPINE_MIRROR}#g" /etc/apk/repositories \
&& apk add --no-cache chromium font-noto-cjk novnc openbox socat su-exec tigervnc websockify xvfb \
&& adduser -D -u 1000 browser \
&& ln -sf /usr/share/novnc/vnc_lite.html /usr/share/novnc/index.html
COPY entrypoint.sh /usr/local/bin/lingxing-browser-entrypoint
RUN chmod 0755 /usr/local/bin/lingxing-browser-entrypoint
ENV DISPLAY=:99 \
LINGXING_LOGIN_URL=https://wms.xlwms.com/login \
PROFILE_DIR=/profile
VOLUME ["/profile"]
EXPOSE 6080 9222
ENTRYPOINT ["/usr/local/bin/lingxing-browser-entrypoint"]
@@ -0,0 +1,38 @@
#!/bin/sh
set -eu
mkdir -p "$PROFILE_DIR" /tmp/lingxing-runtime /tmp/.X11-unix
rm -f /tmp/.X99-lock /tmp/.X11-unix/X99
rm -f "$PROFILE_DIR/SingletonCookie" "$PROFILE_DIR/SingletonLock" "$PROFILE_DIR/SingletonSocket"
chmod 1777 /tmp/.X11-unix
chown -R browser:browser "$PROFILE_DIR" /tmp/lingxing-runtime
chmod 0700 "$PROFILE_DIR" /tmp/lingxing-runtime
cleanup() {
for pid in "${CHROMIUM_PID:-}" "${CDP_PROXY_PID:-}" "${NOVNC_PID:-}" "${VNC_PID:-}" "${OPENBOX_PID:-}" "${XVFB_PID:-}"; do
[ -n "$pid" ] && kill "$pid" 2>/dev/null || true
done
}
trap cleanup EXIT INT TERM
su-exec browser Xvfb "$DISPLAY" -screen 0 1440x900x24 -nolisten tcp & XVFB_PID=$!
sleep 1
su-exec browser env DISPLAY="$DISPLAY" XDG_RUNTIME_DIR=/tmp/lingxing-runtime openbox & OPENBOX_PID=$!
su-exec browser x0vncserver -display "$DISPLAY" -localhost -rfbport 5900 -SecurityTypes None -AlwaysShared & VNC_PID=$!
su-exec browser websockify --web=/usr/share/novnc 0.0.0.0:6080 127.0.0.1:5900 & NOVNC_PID=$!
su-exec browser socat TCP-LISTEN:9222,bind=0.0.0.0,reuseaddr,fork TCP:127.0.0.1:9223 & CDP_PROXY_PID=$!
su-exec browser env DISPLAY="$DISPLAY" XDG_RUNTIME_DIR=/tmp/lingxing-runtime chromium-browser \
--user-data-dir="$PROFILE_DIR" \
--remote-debugging-address=0.0.0.0 \
--remote-debugging-port=9223 \
--window-size=1440,900 \
--disable-gpu \
--use-gl=swiftshader \
--disable-dev-shm-usage \
--no-first-run \
--no-default-browser-check \
--no-sandbox \
"$LINGXING_LOGIN_URL" & CHROMIUM_PID=$!
wait "$CHROMIUM_PID"
+84
View File
@@ -0,0 +1,84 @@
# 开发环境独立领星浏览器
该浏览器只供开发环境 Java Firstleg 使用,使用独立 Docker volume,不复用其他 Node.js 账号的 profile。
## 启动
项目开发部署脚本使用 `DEPLOY_ENV=development`,因此先确保 Java 服务所在 Docker 网络已经存在:
```bash
docker network inspect firstleg-development-network >/dev/null
```
在仓库 `baiyue-firstleg` 根目录执行:
```bash
DEPLOY_ENV=development docker compose \
-f deploy/docker-compose.lingxing-browser.yml \
up -d --build
```
Java 容器加入同一个 `firstleg-development-network` 后,Nacos 使用:
```yaml
firstleg:
lingxing:
login-origin: https://wms.xlwms.com
browser-cdp-endpoint: http://lingxing-browser:9222
auto-sync-enabled: false
```
已有开发数据库需要单独执行一次 SKU 同步权限脚本(目标库为 `baiyue_iam`),否则页面菜单会因没有权限而隐藏、接口会返回 403:
```bash
mysql -h <开发数据库地址> -u <账号> -p baiyue_iam \
< iam/src/main/resources/db/mojia-sku-sync-permissions.sql
```
执行后让后台账号重新登录,使新的授权快照生效。
账号密码通过 Nacos 的加密配置保存:
```yaml
firstleg:
lingxing:
username: ${LINGXING_USERNAME}
password: ${LINGXING_PASSWORD}
```
不要把真实账号、密码、Cookie、Token 或签名密钥写入仓库文件。
## 首次登录
浏览器容器启动后,noVNC 只绑定宿主机回环地址:
```text
http://127.0.0.1:6080/?autoconnect=1&resize=scale
```
从个人电脑查看开发服务器上的 noVNC 时,先建立 SSH 隧道:
```bash
ssh -N -L 6080:127.0.0.1:6080 <开发服务器用户>@<开发服务器地址>
```
然后在个人电脑浏览器打开上面的地址。CDP 不需要暴露到宿主机,Java 通过 Docker 网络访问 `lingxing-browser:9222`。
正常情况下不需要先人工登录。点击后台测试连接或人工推送时,Java 会自动填写账号密码。遇到腾讯安全滑块时,通过 noVNC 人工完成滑块;遇到短信、邮箱或 TOTP 二次验证时,在后台输入验证码,Java 会自动继续原操作。
登录页已按领星实际页面校验以下控件:账号 `input[name="username"]`、密码 `input[name="password"]`、普通登录按钮 `.login-btn`;检测到二次验证页后,短信/邮箱立即自动点击 `.verify-code-login .code-btn` 获取验证码,后台输入验证码后只填写 `input[placeholder="输入验证码"]` 并点击 `.verify-code-login .login-btn`,不会重复发送。TOTP 由页面自动初始化,验证码输入框为 `input.verification-code-native`,后台提交方式相同。
## 检查
```bash
chmod +x deploy/scripts/check-lingxing-browser.sh
DEPLOY_ENV=development deploy/scripts/check-lingxing-browser.sh
DEPLOY_ENV=development docker compose \
-f deploy/docker-compose.lingxing-browser.yml ps
docker exec firstleg-dev-lingxing-browser \
wget -q -O - http://127.0.0.1:9222/json/version
```
不要把 6080 当成 CDP 地址,也不要使用容器内原生 9223;Java 使用同网络的 `http://lingxing-browser:9222`。
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
deployment_env="${DEPLOY_ENV:-development}"
compose_file="deploy/docker-compose.lingxing-browser.yml"
container="firstleg-${deployment_env}-lingxing-browser"
docker compose -f "$compose_file" ps --status running
docker exec "$container" wget -q -O - http://127.0.0.1:9222/json/version \
| sed -E 's/("webSocketDebuggerUrl"[[:space:]]*:[[:space:]]*")[^"]+(".*)/\1[redacted]\2/'
echo "CDP is reachable; credentials and browser storage were not printed."
@@ -6011,7 +6011,7 @@ INSERT INTO iam_membership_role (membership_id, role_id, tenant_id, created_at)
-- 系统基线 iam_number_sequence:1 行。
INSERT INTO iam_number_sequence (sequence_key, next_value, updated_at) VALUES ('CUSTOMER_CODE',1,CURRENT_TIMESTAMP(3));
-- 系统基线 iam_permission:364 行。
-- 系统基线 iam_permission:367 行。
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5100000,0,'PLATFORM','MENU',0,'platform:customer','客户管理','/customers',NULL,10,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5100010,0,'PLATFORM','MENU',5100000,'platform:customer:list','企业客户','/customers/list',NULL,10,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5100011,0,'PLATFORM','BUTTON',5100010,'platform:customer:create','新增企业客户',NULL,NULL,10,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
@@ -6125,6 +6125,9 @@ INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id,
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5102127,0,'PLATFORM','BUTTON',5102030,'platform:firstleg:inbound:delivery:complete','完成派送',NULL,NULL,200,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5102128,0,'PLATFORM','BUTTON',5102030,'platform:firstleg:inbound:picking:edit','执行拣货',NULL,NULL,210,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5102129,0,'PLATFORM','BUTTON',5102030,'platform:firstleg:inbound:picking:view','查看拣货任务',NULL,NULL,220,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5102131,0,'PLATFORM','MENU',5103000,'platform:mojia-sku-sync:list','一件代发SKU同步','/general/mojia-sku-sync','GET',130,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5102132,0,'PLATFORM','BUTTON',5102131,'platform:mojia-sku-sync:edit','编辑一件代发SKU测量数据',NULL,'PUT',10,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5102133,0,'PLATFORM','BUTTON',5102131,'platform:mojia-sku-sync:retry','重新推送一件代发SKU',NULL,'POST',20,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5102140,0,'PLATFORM','MENU',5102000,'platform:firstleg:ocean-booking:list','订舱管理','/first-leg/ocean-bookings','GET',80,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5102142,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:create','创建订舱',NULL,'POST',20,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
INSERT INTO iam_permission (id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, built_in, created_at, updated_at, deleted) VALUES (5102146,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:release','确认放舱',NULL,'POST',60,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0);
@@ -6390,7 +6393,7 @@ INSERT INTO iam_role (id, tenant_id, app_code, role_code, role_name, role_type,
INSERT INTO iam_role (id, tenant_id, app_code, role_code, role_name, role_type, status, description, created_by, created_at, updated_by, updated_at, deleted, version) VALUES (4000007,0,'PLATFORM','CN_FIRSTLEG_PDA_OPERATOR','国内头程 PDA 作业员','SYSTEM','ACTIVE','负责使用国内 PDA 执行柜次拣货、现场取证和装柜完成',NULL,CURRENT_TIMESTAMP(3),NULL,CURRENT_TIMESTAMP(3),0,0);
INSERT INTO iam_role (id, tenant_id, app_code, role_code, role_name, role_type, status, description, created_by, created_at, updated_by, updated_at, deleted, version) VALUES (4000100,0,'MERCHANT','MERCHANT_OWNER','企业管理员','SYSTEM','ACTIVE','一期企业全部菜单和操作按钮模板',1000001,CURRENT_TIMESTAMP(3),1000001,CURRENT_TIMESTAMP(3),0,0);
-- 系统基线 iam_role_permission:539 行。
-- 系统基线 iam_role_permission:545 行。
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5100000,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5100010,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5100011,0,CURRENT_TIMESTAMP(3));
@@ -6501,6 +6504,9 @@ INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at)
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5102127,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5102128,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5102129,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5102131,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5102132,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5102133,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5102140,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5102142,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000001,5102146,0,CURRENT_TIMESTAMP(3));
@@ -6765,6 +6771,9 @@ INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at)
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000003,5102127,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000003,5102128,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000003,5102129,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000003,5102131,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000003,5102132,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000003,5102133,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000003,5102140,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000003,5102142,0,CURRENT_TIMESTAMP(3));
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id, created_at) VALUES (4000003,5102146,0,CURRENT_TIMESTAMP(3));