调整lsfx-mock默认数据库配置并更新NAS部署环境

This commit is contained in:
wkc
2026-03-31 23:03:14 +08:00
parent 2fdf5f1546
commit 8798aa9230
11 changed files with 213 additions and 5 deletions

View File

@@ -104,6 +104,9 @@ copy_path "${REPO_ROOT}/ruoyi-ui/dist" "${STAGE_ROOT}/frontend/dist"
copy_path "${REPO_ROOT}/docker-compose.yml" "${STAGE_ROOT}/docker-compose.yml" copy_path "${REPO_ROOT}/docker-compose.yml" "${STAGE_ROOT}/docker-compose.yml"
copy_path "${REPO_ROOT}/.env.example" "${STAGE_ROOT}/.env.example" copy_path "${REPO_ROOT}/.env.example" "${STAGE_ROOT}/.env.example"
copy_path "${REPO_ROOT}/ruoyi-admin/target/ruoyi-admin.jar" "${STAGE_ROOT}/backend/ruoyi-admin.jar" copy_path "${REPO_ROOT}/ruoyi-admin/target/ruoyi-admin.jar" "${STAGE_ROOT}/backend/ruoyi-admin.jar"
python3 "${SCRIPT_DIR}/render_nas_env.py" \
--template "${REPO_ROOT}/.env.example" \
--output "${STAGE_ROOT}/.env"
echo "[5/5] 上传并远端部署" echo "[5/5] 上传并远端部署"
ensure_paramiko ensure_paramiko

View File

@@ -95,6 +95,12 @@ Copy-ItemSafe (Join-Path $repoRoot "ruoyi-ui\\dist") (Join-Path $stageRoot "fron
Copy-ItemSafe (Join-Path $repoRoot "docker-compose.yml") (Join-Path $stageRoot "docker-compose.yml") Copy-ItemSafe (Join-Path $repoRoot "docker-compose.yml") (Join-Path $stageRoot "docker-compose.yml")
Copy-ItemSafe (Join-Path $repoRoot ".env.example") (Join-Path $stageRoot ".env.example") Copy-ItemSafe (Join-Path $repoRoot ".env.example") (Join-Path $stageRoot ".env.example")
Copy-ItemSafe (Join-Path $repoRoot "ruoyi-admin\\target\\ruoyi-admin.jar") (Join-Path $stageRoot "backend\\ruoyi-admin.jar") Copy-ItemSafe (Join-Path $repoRoot "ruoyi-admin\\target\\ruoyi-admin.jar") (Join-Path $stageRoot "backend\\ruoyi-admin.jar")
python (Join-Path $scriptDir "render_nas_env.py") `
--template (Join-Path $repoRoot ".env.example") `
--output (Join-Path $stageRoot ".env")
if ($LASTEXITCODE -ne 0) {
throw "生成 NAS 部署 .env 失败"
}
Write-Host "[5/5] 上传并远端部署" Write-Host "[5/5] 上传并远端部署"
$paramikoCheck = @' $paramikoCheck = @'

47
deploy/render_nas_env.py Normal file
View File

@@ -0,0 +1,47 @@
import argparse
from pathlib import Path
NAS_ENV_OVERRIDES = {
"CCDI_DB_HOST": "192.168.0.111",
"CCDI_DB_PORT": "40628",
}
def parse_args():
parser = argparse.ArgumentParser(description="Render NAS deployment .env for CCDI docker compose.")
parser.add_argument("--template", required=True)
parser.add_argument("--output", required=True)
return parser.parse_args()
def render_env_text(template_text: str) -> str:
rendered_lines = []
replaced_keys = set()
for line in template_text.splitlines():
key, separator, value = line.partition("=")
if separator and key in NAS_ENV_OVERRIDES:
rendered_lines.append(f"{key}={NAS_ENV_OVERRIDES[key]}")
replaced_keys.add(key)
continue
rendered_lines.append(line)
for key, value in NAS_ENV_OVERRIDES.items():
if key not in replaced_keys:
rendered_lines.append(f"{key}={value}")
return "\n".join(rendered_lines) + "\n"
def main():
args = parse_args()
template_path = Path(args.template)
output_path = Path(args.output)
template_text = template_path.read_text(encoding="utf-8")
output_path.write_text(render_env_text(template_text), encoding="utf-8")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,23 @@
# LSFX Mock Server 默认数据库地址调整实施记录
**日期**: 2026-03-31
**范围**: `lsfx-mock-server` 配置
## 1. 调整内容
-`lsfx-mock-server/config/settings.py` 中显式固定默认数据库地址:
- `CCDI_DB_HOST = 116.62.17.81`
- `CCDI_DB_PORT = 3307`
- 保持数据库名、用户名、密码继续沿用主工程 `application-dev.yml` 中的默认值读取逻辑
## 2. 调整原因
此前 `lsfx-mock-server` 的数据库 host/port 默认值隐式跟随 `ruoyi-admin` 的开发配置。虽然当前主工程配置本身也是 `116.62.17.81:3307`,但这种依赖关系不够直接。
本次改动后,`lsfx-mock-server` 会在自身配置层明确默认连接到 `116.62.17.81:3307`,避免后续主工程开发配置变化时影响 Mock 服务默认库选择。
## 3. 验证范围
- `lsfx-mock-server/tests/test_settings_sync.py`
- 校验默认 host/port 固定为 `116.62.17.81:3307`
- 校验数据库名、用户名、密码仍沿用主工程开发配置默认值

View File

@@ -0,0 +1,41 @@
# LSFX Mock Server 异常账户基线审计字段纠正实施记录
**日期**: 2026-03-31
**范围**: `lsfx-mock-server` 异常账户基线同步链路
## 1. 问题说明
在前一轮排查中,基于 MCP 表结构结果将 `ccdi_account_info` 的审计列误判为 `created_by``updated_by`,并据此调整了异常账户基线 upsert SQL。
随后使用 `mysql` 直连 `116.62.17.81:3307/ccdi` 执行:
- `SHOW COLUMNS FROM ccdi_account_info LIKE 'create_by';`
- `SHOW COLUMNS FROM ccdi_account_info LIKE 'update_by';`
- `SHOW COLUMNS FROM ccdi_account_info;`
确认真实表结构使用的是 `create_by``update_by`
## 2. 本次纠正内容
- 修正 `lsfx-mock-server/services/abnormal_account_baseline_service.py`
- upsert 字段改回 `create_by``update_by`
- 更新分支改回 `update_by = VALUES(update_by)`
- 修正 `sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql`
- `ccdi_account_info` 建表字段改回 `create_by``update_by`
- 规则初始化 SQL 的审计字段改回 `create_by` / `update_by`
- 修正 `sql/migration/2026-03-31-add-abnormal-account-rule-test-data.sql`
- `ccdi_account_info` 测试数据插入字段改回 `create_by``update_by`
- 修正 `docs/design/2026-03-31-lsfx-mock-server-abnormal-account-baseline-sync-design.md`
- 将设计文档中的账户表审计字段名改回真实库定义
## 3. 测试调整
- 更新 `lsfx-mock-server/tests/test_abnormal_account_baseline_service.py`
- 锁定 insert SQL 必须包含 `create_by``update_by`
- 锁定 upsert update 分支必须写 `update_by = VALUES(update_by)`
## 4. 结果
- 异常账户基线同步 SQL 已与 `116.62.17.81:3307/ccdi` 的真实表结构重新对齐
- 运行时不会再向不存在的 `created_by``updated_by` 字段写值
- 服务代码、migration、测试数据脚本与设计文档已恢复一致

View File

@@ -0,0 +1,34 @@
# NAS 部署脚本 LSFX Mock 数据库地址调整实施记录
**日期**: 2026-03-31
**范围**: NAS 部署脚本、部署配置
## 1. 本次调整
- 新增 `deploy/render_nas_env.py`
- 基于根目录 `.env.example` 渲染 NAS 部署专用 `.env`
- 固定输出:
- `CCDI_DB_HOST=192.168.0.111`
- `CCDI_DB_PORT=40628`
- 调整 `deploy/deploy-to-nas.sh`
- 在组装部署目录阶段生成 `${STAGE_ROOT}/.env`
- 调整 `deploy/deploy.ps1`
- 与 Shell 部署入口保持一致,在组装部署目录阶段生成 `${stageRoot}\\.env`
## 2. 调整目的
确保 NAS 部署后的 `lsfx-mock-server` 读取部署包中的 `.env`,从而连接:
- Host: `192.168.0.111`
- Port: `40628`
同时保持本地 `docker-compose.yml` 默认值不变,不影响本地开发和手工启动。
## 3. 验证范围
- `tests/deploy/test_render_nas_env.py`
- 校验渲染后的 `.env` 包含 `CCDI_DB_HOST=192.168.0.111`
- 校验渲染后的 `.env` 包含 `CCDI_DB_PORT=40628`
- `tests/deploy/test_deploy_to_nas.py`
- 校验 `deploy-to-nas.sh` 已接入 `render_nas_env.py`
- 校验部署目录会生成 `${STAGE_ROOT}/.env`

View File

@@ -26,6 +26,8 @@ def _load_ruoyi_mysql_defaults() -> dict:
MYSQL_DEFAULTS = _load_ruoyi_mysql_defaults() MYSQL_DEFAULTS = _load_ruoyi_mysql_defaults()
LSFX_DEFAULT_CCDI_DB_HOST = "116.62.17.81"
LSFX_DEFAULT_CCDI_DB_PORT = 3307
class Settings(BaseSettings): class Settings(BaseSettings):
@@ -50,8 +52,8 @@ class Settings(BaseSettings):
INITIAL_LOG_ID: int = 10000 INITIAL_LOG_ID: int = 10000
# 员工库只读配置 # 员工库只读配置
CCDI_DB_HOST: str = MYSQL_DEFAULTS.get("host", "") CCDI_DB_HOST: str = LSFX_DEFAULT_CCDI_DB_HOST
CCDI_DB_PORT: int = int(MYSQL_DEFAULTS.get("port", 3306)) CCDI_DB_PORT: int = LSFX_DEFAULT_CCDI_DB_PORT
CCDI_DB_NAME: str = MYSQL_DEFAULTS.get("database", "") CCDI_DB_NAME: str = MYSQL_DEFAULTS.get("database", "")
CCDI_DB_USERNAME: str = MYSQL_DEFAULTS.get("username", "") CCDI_DB_USERNAME: str = MYSQL_DEFAULTS.get("username", "")
CCDI_DB_PASSWORD: str = MYSQL_DEFAULTS.get("password", "") CCDI_DB_PASSWORD: str = MYSQL_DEFAULTS.get("password", "")

View File

@@ -96,6 +96,10 @@ def test_apply_should_insert_new_account_fact_by_account_no():
assert len(fake_connection.executed_sql) == 1 assert len(fake_connection.executed_sql) == 1
executed = fake_connection.executed_sql[0] executed = fake_connection.executed_sql[0]
assert "INSERT INTO ccdi_account_info" in executed["sql"] assert "INSERT INTO ccdi_account_info" in executed["sql"]
assert "create_by" in executed["sql"]
assert "update_by" in executed["sql"]
assert "created_by" not in executed["sql"]
assert "updated_by" not in executed["sql"]
assert executed["params"] == ( assert executed["params"] == (
"6222000000000001", "6222000000000001",
"DEBIT", "DEBIT",
@@ -140,6 +144,7 @@ def test_apply_should_update_existing_account_fact_by_account_no():
assert len(fake_connection.executed_sql) == 1 assert len(fake_connection.executed_sql) == 1
executed = fake_connection.executed_sql[0] executed = fake_connection.executed_sql[0]
assert "ON DUPLICATE KEY UPDATE" in executed["sql"] assert "ON DUPLICATE KEY UPDATE" in executed["sql"]
assert "update_by = VALUES(update_by)" in executed["sql"]
assert executed["params"][0] == "6222000000000001" assert executed["params"][0] == "6222000000000001"
assert executed["params"][2] == "测试员工结算卡" assert executed["params"][2] == "测试员工结算卡"
assert executed["params"][10] == 1 assert executed["params"][10] == 1

View File

@@ -15,11 +15,14 @@ def test_ruoyi_mysql_defaults_should_follow_application_dev_config():
assert _load_ruoyi_mysql_defaults()["port"] == match.group("port") assert _load_ruoyi_mysql_defaults()["port"] == match.group("port")
def test_settings_should_use_ruoyi_mysql_defaults(): def test_settings_should_default_to_lsfx_target_mysql_host_and_port():
assert settings.CCDI_DB_HOST == "116.62.17.81"
assert settings.CCDI_DB_PORT == 3307
def test_settings_should_still_use_ruoyi_mysql_defaults_for_db_name_and_credentials():
defaults = _load_ruoyi_mysql_defaults() defaults = _load_ruoyi_mysql_defaults()
assert settings.CCDI_DB_HOST == defaults["host"]
assert settings.CCDI_DB_PORT == int(defaults["port"])
assert settings.CCDI_DB_NAME == defaults["database"] assert settings.CCDI_DB_NAME == defaults["database"]
assert settings.CCDI_DB_USERNAME == defaults["username"] assert settings.CCDI_DB_USERNAME == defaults["username"]
assert settings.CCDI_DB_PASSWORD == defaults["password"] assert settings.CCDI_DB_PASSWORD == defaults["password"]

View File

@@ -43,3 +43,10 @@ def test_sh_dry_run_accepts_override_arguments():
assert "Port: 2222" in result.stdout assert "Port: 2222" in result.stdout
assert "Username: deploy-user" in result.stdout assert "Username: deploy-user" in result.stdout
assert "RemoteRoot: /volume2/custom/app" in result.stdout assert "RemoteRoot: /volume2/custom/app" in result.stdout
def test_sh_script_should_render_nas_env_into_stage_package():
script_text = SCRIPT_PATH.read_text(encoding="utf-8")
assert 'render_nas_env.py' in script_text
assert '"${STAGE_ROOT}/.env"' in script_text

View File

@@ -0,0 +1,37 @@
from pathlib import Path
import subprocess
import tempfile
REPO_ROOT = Path(__file__).resolve().parents[2]
SCRIPT_PATH = REPO_ROOT / "deploy" / "render_nas_env.py"
ENV_TEMPLATE = REPO_ROOT / ".env.example"
def test_render_nas_env_should_generate_lsfx_mock_db_override_file():
with tempfile.TemporaryDirectory() as tmp_dir:
output_path = Path(tmp_dir) / ".env"
result = subprocess.run(
[
"python3",
str(SCRIPT_PATH),
"--template",
str(ENV_TEMPLATE),
"--output",
str(output_path),
],
cwd=REPO_ROOT,
capture_output=True,
text=True,
)
assert result.returncode == 0
assert output_path.exists()
env_text = output_path.read_text(encoding="utf-8")
assert "CCDI_DB_HOST=192.168.0.111" in env_text
assert "CCDI_DB_PORT=40628" in env_text
assert "CCDI_DB_NAME=ccdi" in env_text