feat: expand vehicle data platform capabilities
This commit is contained in:
121
vehicle-data-platform/deploy/install-open-platform-release.sh
Executable file
121
vehicle-data-platform/deploy/install-open-platform-release.sh
Executable file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RELEASE_ID=${1:?release id is required}
|
||||
WEB_ARCHIVE=${2:?open portal web archive is required}
|
||||
API_BINARY=${3:?open platform API binary is required}
|
||||
ROOT=${OPEN_PLATFORM_ROOT:-/opt/lingniu-vehicle-open-platform}
|
||||
BASE_URL=${OPEN_PLATFORM_BASE_URL:-http://127.0.0.1:20310}
|
||||
SERVICE=${OPEN_PLATFORM_SERVICE:-lingniu-vehicle-open-platform}
|
||||
SYSTEMCTL_BIN=${SYSTEMCTL_BIN:-systemctl}
|
||||
CURL_BIN=${CURL_BIN:-curl}
|
||||
RELEASE_HISTORY_LIMIT=${RELEASE_HISTORY_LIMIT:-12}
|
||||
|
||||
case "$RELEASE_ID" in
|
||||
''|*[!A-Za-z0-9._-]*) printf 'invalid release id: %s\n' "$RELEASE_ID" >&2; exit 1 ;;
|
||||
esac
|
||||
test -f "$WEB_ARCHIVE" || { printf 'open portal archive is missing\n' >&2; exit 1; }
|
||||
test -f "$API_BINARY" || { printf 'open platform API binary is missing\n' >&2; exit 1; }
|
||||
|
||||
old=
|
||||
if test -L "$ROOT/current"; then
|
||||
old=$(readlink -f "$ROOT/current")
|
||||
fi
|
||||
next="$ROOT/releases/$RELEASE_ID"
|
||||
env_file="$ROOT/env/open-platform.env"
|
||||
switched=false
|
||||
|
||||
set_release_env() {
|
||||
python3 - "$env_file" "$1" <<'PY'
|
||||
import pathlib
|
||||
import sys
|
||||
path = pathlib.Path(sys.argv[1])
|
||||
release = sys.argv[2]
|
||||
lines = path.read_text(encoding="utf-8").splitlines() if path.exists() else []
|
||||
updated = []
|
||||
found = False
|
||||
for line in lines:
|
||||
if line.startswith("OPEN_PLATFORM_RELEASE="):
|
||||
updated.append("OPEN_PLATFORM_RELEASE=" + release)
|
||||
found = True
|
||||
else:
|
||||
updated.append(line)
|
||||
if not found:
|
||||
updated.append("OPEN_PLATFORM_RELEASE=" + release)
|
||||
path.write_text("\n".join(updated) + "\n", encoding="utf-8")
|
||||
PY
|
||||
chmod 0600 "$env_file"
|
||||
}
|
||||
|
||||
rollback() {
|
||||
status=$?
|
||||
trap - ERR
|
||||
set +e
|
||||
if test "$switched" = true && test -n "$old"; then
|
||||
ln -s "$old" "$ROOT/current.rollback"
|
||||
python3 -c 'import os,sys; os.replace(sys.argv[1],sys.argv[2])' "$ROOT/current.rollback" "$ROOT/current"
|
||||
set_release_env "$(basename "$old")"
|
||||
"$SYSTEMCTL_BIN" restart "$SERVICE"
|
||||
elif test "$switched" = true; then
|
||||
"$SYSTEMCTL_BIN" stop "$SERVICE"
|
||||
rm -f "$ROOT/current"
|
||||
set_release_env ""
|
||||
fi
|
||||
printf 'open platform release failed; current restored to %s\n' "${old:-none}" >&2
|
||||
exit "$status"
|
||||
}
|
||||
trap rollback ERR
|
||||
|
||||
test ! -e "$next" || { printf 'release already exists: %s\n' "$next" >&2; exit 1; }
|
||||
mkdir -p "$next/web" "$ROOT/env"
|
||||
cp "$API_BINARY" "$next/open-platform-api"
|
||||
chmod 0755 "$next/open-platform-api"
|
||||
|
||||
while IFS= read -r member; do
|
||||
case "$member" in
|
||||
/*|../*|*/../*|*/..) printf 'web archive contains unsafe path: %s\n' "$member" >&2; exit 1 ;;
|
||||
esac
|
||||
done < <(tar -tzf "$WEB_ARCHIVE")
|
||||
tar --no-same-owner -xzf "$WEB_ARCHIVE" -C "$next/web"
|
||||
test -f "$next/web/index.html" || { printf 'open portal index is missing\n' >&2; exit 1; }
|
||||
|
||||
ln -s "$next" "$ROOT/current.next"
|
||||
python3 -c 'import os,sys; os.replace(sys.argv[1],sys.argv[2])' "$ROOT/current.next" "$ROOT/current"
|
||||
switched=true
|
||||
set_release_env "$RELEASE_ID"
|
||||
"$SYSTEMCTL_BIN" restart "$SERVICE"
|
||||
|
||||
ready=false
|
||||
for _ in $(seq 1 20); do
|
||||
if "$SYSTEMCTL_BIN" is-active --quiet "$SERVICE" && "$CURL_BIN" -fsS "$BASE_URL/healthz" >/dev/null; then
|
||||
ready=true
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
test "$ready" = true || { printf 'open platform service did not become ready\n' >&2; exit 1; }
|
||||
"$CURL_BIN" -fsS "$BASE_URL/" | grep -q '<div id="root"></div>'
|
||||
"$CURL_BIN" -fsS "$BASE_URL/open-api/openapi.yaml" | grep -q '^openapi: 3.0.3'
|
||||
"$CURL_BIN" -fsS "$BASE_URL/portal-api/catalog" | grep -q 'daily_hydrogen'
|
||||
|
||||
python3 - "$ROOT" "$RELEASE_HISTORY_LIMIT" "$next" "$old" <<'PY'
|
||||
import pathlib
|
||||
import shutil
|
||||
import sys
|
||||
root = pathlib.Path(sys.argv[1])
|
||||
limit = int(sys.argv[2])
|
||||
protected = {pathlib.Path(value).resolve() for value in sys.argv[3:] if value}
|
||||
releases = sorted(
|
||||
(p for p in (root / "releases").iterdir() if p.is_dir()),
|
||||
key=lambda p: p.stat().st_mtime,
|
||||
reverse=True,
|
||||
)
|
||||
for path in releases[limit:]:
|
||||
if path.resolve() not in protected:
|
||||
shutil.rmtree(path)
|
||||
PY
|
||||
|
||||
switched=false
|
||||
trap - ERR
|
||||
printf 'open_platform_release_install=ok release=%s previous=%s\n' "$RELEASE_ID" "${old##*/}"
|
||||
48
vehicle-data-platform/deploy/install-open-platform-release.test.sh
Executable file
48
vehicle-data-platform/deploy/install-open-platform-release.test.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
FIXTURE=$(mktemp -d)
|
||||
trap 'rm -rf "$FIXTURE"' EXIT
|
||||
|
||||
ROOT="$FIXTURE/root"
|
||||
mkdir -p "$FIXTURE/web" "$FIXTURE/bin" "$ROOT/env"
|
||||
printf '<!doctype html><div id="root"></div>\n' > "$FIXTURE/web/index.html"
|
||||
printf 'binary\n' > "$FIXTURE/open-platform-api"
|
||||
tar -C "$FIXTURE/web" -czf "$FIXTURE/web.tar.gz" .
|
||||
cat > "$ROOT/env/open-platform.env" <<'EOF'
|
||||
OPEN_PLATFORM_HTTP_ADDR=:20310
|
||||
EOF
|
||||
|
||||
cat > "$FIXTURE/bin/systemctl" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
case "$1" in
|
||||
restart|stop) exit 0 ;;
|
||||
is-active) exit 0 ;;
|
||||
esac
|
||||
EOF
|
||||
cat > "$FIXTURE/bin/curl" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
url=${@: -1}
|
||||
case "$url" in
|
||||
*/healthz) printf '{"status":"ok"}\n' ;;
|
||||
*/open-api/openapi.yaml) printf 'openapi: 3.0.3\n' ;;
|
||||
*/portal-api/catalog) printf '{"data":[{"code":"daily_hydrogen"}]}\n' ;;
|
||||
*) printf '<div id="root"></div>\n' ;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$FIXTURE/bin/systemctl" "$FIXTURE/bin/curl"
|
||||
|
||||
OPEN_PLATFORM_ROOT="$ROOT" \
|
||||
SYSTEMCTL_BIN="$FIXTURE/bin/systemctl" \
|
||||
CURL_BIN="$FIXTURE/bin/curl" \
|
||||
"$SCRIPT_DIR/install-open-platform-release.sh" release-1 "$FIXTURE/web.tar.gz" "$FIXTURE/open-platform-api"
|
||||
|
||||
test "$(readlink "$ROOT/current")" = "$ROOT/releases/release-1"
|
||||
test -x "$ROOT/current/open-platform-api"
|
||||
test -f "$ROOT/current/web/index.html"
|
||||
grep -q '^OPEN_PLATFORM_RELEASE=release-1$' "$ROOT/env/open-platform.env"
|
||||
printf 'install_open_platform_release_test=ok\n'
|
||||
@@ -7,9 +7,16 @@ ARCHIVE=${2:?web archive is required}
|
||||
API_BINARY=${3:-}
|
||||
ONEOS_SCOPE_BINARY=${4:-}
|
||||
RECONCILIATION_BINARY=${5:-}
|
||||
OPEN_STAT_BINARY=${6:-}
|
||||
ALERT_EVALUATOR_BINARY=${7:-}
|
||||
ALERT_STREAM_EVALUATOR_BINARY=${8:-}
|
||||
ALERT_NOTIFICATION_DISPATCHER_BINARY=${9:-}
|
||||
ROOT=${PLATFORM_ROOT:-/opt/lingniu-vehicle-platform}
|
||||
BASE_URL=${PLATFORM_BASE_URL:-http://127.0.0.1:20300}
|
||||
SERVICE=${PLATFORM_SERVICE:-lingniu-vehicle-platform}
|
||||
ALERT_EVALUATOR_SERVICE=${ALERT_EVALUATOR_SERVICE:-lingniu-vehicle-alert-evaluator}
|
||||
ALERT_STREAM_EVALUATOR_SERVICE=${ALERT_STREAM_EVALUATOR_SERVICE:-lingniu-vehicle-alert-stream-evaluator}
|
||||
ALERT_NOTIFICATION_DISPATCHER_SERVICE=${ALERT_NOTIFICATION_DISPATCHER_SERVICE:-lingniu-vehicle-alert-notification-dispatcher}
|
||||
COMPATIBILITY_GENERATIONS=${COMPATIBILITY_GENERATIONS:-3}
|
||||
RELEASE_HISTORY_LIMIT=${RELEASE_HISTORY_LIMIT:-20}
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
@@ -29,6 +36,18 @@ fi
|
||||
if test -n "$RECONCILIATION_BINARY"; then
|
||||
test -f "$RECONCILIATION_BINARY" || { printf 'reconciliation evaluator binary is missing: %s\n' "$RECONCILIATION_BINARY" >&2; exit 1; }
|
||||
fi
|
||||
if test -n "$OPEN_STAT_BINARY"; then
|
||||
test -f "$OPEN_STAT_BINARY" || { printf 'open platform statistics binary is missing: %s\n' "$OPEN_STAT_BINARY" >&2; exit 1; }
|
||||
fi
|
||||
if test -n "$ALERT_EVALUATOR_BINARY"; then
|
||||
test -f "$ALERT_EVALUATOR_BINARY" || { printf 'alert evaluator binary is missing: %s\n' "$ALERT_EVALUATOR_BINARY" >&2; exit 1; }
|
||||
fi
|
||||
if test -n "$ALERT_STREAM_EVALUATOR_BINARY"; then
|
||||
test -f "$ALERT_STREAM_EVALUATOR_BINARY" || { printf 'alert stream evaluator binary is missing: %s\n' "$ALERT_STREAM_EVALUATOR_BINARY" >&2; exit 1; }
|
||||
fi
|
||||
if test -n "$ALERT_NOTIFICATION_DISPATCHER_BINARY"; then
|
||||
test -f "$ALERT_NOTIFICATION_DISPATCHER_BINARY" || { printf 'alert notification dispatcher binary is missing: %s\n' "$ALERT_NOTIFICATION_DISPATCHER_BINARY" >&2; exit 1; }
|
||||
fi
|
||||
test -x "$SCRIPT_DIR/prepare-web-release-tree.sh" || { printf 'release tree helper is missing\n' >&2; exit 1; }
|
||||
test -f "$SCRIPT_DIR/prune-release-history.py" || { printf 'release pruning helper is missing\n' >&2; exit 1; }
|
||||
test -f "$SCRIPT_DIR/verify-customer-demo.py" || { printf 'customer demo gate is missing\n' >&2; exit 1; }
|
||||
@@ -73,6 +92,9 @@ rollback() {
|
||||
python3 -c 'import os,sys; os.replace(sys.argv[1], sys.argv[2])' "$ROOT/current.rollback" "$ROOT/current"
|
||||
set_release_env "$old_release"
|
||||
"$SYSTEMCTL_BIN" restart "$SERVICE"
|
||||
test -z "$ALERT_EVALUATOR_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_EVALUATOR_SERVICE"
|
||||
test -z "$ALERT_STREAM_EVALUATOR_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_STREAM_EVALUATOR_SERVICE"
|
||||
test -z "$ALERT_NOTIFICATION_DISPATCHER_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_NOTIFICATION_DISPATCHER_SERVICE"
|
||||
fi
|
||||
printf 'web release install failed; current restored to %s\n' "$old" >&2
|
||||
exit "$status"
|
||||
@@ -81,7 +103,7 @@ trap rollback ERR
|
||||
|
||||
test ! -e "$next" || { printf 'release already exists: %s\n' "$next" >&2; exit 1; }
|
||||
mkdir -p "$next/web"
|
||||
for runtime_file in platform-api alert-evaluator alert-stream-evaluator platform-migrate oneos-scope-sync reconciliation-evaluator alert-benchmark; do
|
||||
for runtime_file in platform-api alert-evaluator alert-stream-evaluator alert-notification-dispatcher platform-migrate oneos-scope-sync reconciliation-evaluator alert-benchmark open-platform-stat; do
|
||||
if test -f "$old/$runtime_file"; then
|
||||
cp "$old/$runtime_file" "$next/$runtime_file"
|
||||
fi
|
||||
@@ -96,6 +118,18 @@ fi
|
||||
if test -n "$RECONCILIATION_BINARY"; then
|
||||
cp "$RECONCILIATION_BINARY" "$next/reconciliation-evaluator"
|
||||
fi
|
||||
if test -n "$OPEN_STAT_BINARY"; then
|
||||
cp "$OPEN_STAT_BINARY" "$next/open-platform-stat"
|
||||
fi
|
||||
if test -n "$ALERT_EVALUATOR_BINARY"; then
|
||||
cp "$ALERT_EVALUATOR_BINARY" "$next/alert-evaluator"
|
||||
fi
|
||||
if test -n "$ALERT_STREAM_EVALUATOR_BINARY"; then
|
||||
cp "$ALERT_STREAM_EVALUATOR_BINARY" "$next/alert-stream-evaluator"
|
||||
fi
|
||||
if test -n "$ALERT_NOTIFICATION_DISPATCHER_BINARY"; then
|
||||
cp "$ALERT_NOTIFICATION_DISPATCHER_BINARY" "$next/alert-notification-dispatcher"
|
||||
fi
|
||||
if test -f "$old/lingniu-vehicle-platform.service"; then
|
||||
cp "$old/lingniu-vehicle-platform.service" "$next/lingniu-vehicle-platform.service"
|
||||
fi
|
||||
@@ -114,7 +148,7 @@ cp "$SCRIPT_DIR/verify-customer-demo.py" "$next/deploy/verify-customer-demo.py"
|
||||
cp "$SCRIPT_DIR/import-source-providers.py" "$next/deploy/import-source-providers.py"
|
||||
cp "$0" "$next/deploy/install-web-release.sh"
|
||||
chmod +x "$next/platform-api" "$next/deploy/"*.sh "$next/deploy/prune-release-history.py" "$next/deploy/verify-customer-demo.py" "$next/deploy/import-source-providers.py"
|
||||
for runtime_file in alert-evaluator alert-stream-evaluator platform-migrate oneos-scope-sync reconciliation-evaluator alert-benchmark; do
|
||||
for runtime_file in alert-evaluator alert-stream-evaluator alert-notification-dispatcher platform-migrate oneos-scope-sync reconciliation-evaluator alert-benchmark open-platform-stat; do
|
||||
test ! -f "$next/$runtime_file" || chmod +x "$next/$runtime_file"
|
||||
done
|
||||
|
||||
@@ -132,6 +166,9 @@ python3 -c 'import os,sys; os.replace(sys.argv[1], sys.argv[2])' "$ROOT/current.
|
||||
switched=true
|
||||
set_release_env "$RELEASE_ID"
|
||||
"$SYSTEMCTL_BIN" restart "$SERVICE"
|
||||
test -z "$ALERT_EVALUATOR_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_EVALUATOR_SERVICE"
|
||||
test -z "$ALERT_STREAM_EVALUATOR_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_STREAM_EVALUATOR_SERVICE"
|
||||
test -z "$ALERT_NOTIFICATION_DISPATCHER_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_NOTIFICATION_DISPATCHER_SERVICE"
|
||||
|
||||
ready=false
|
||||
for _ in $(seq 1 15); do
|
||||
|
||||
@@ -29,25 +29,36 @@ tar -C "$new_web" -czf "$fixture/new.tar.gz" .
|
||||
printf 'new platform API\n' > "$fixture/new-platform-api"
|
||||
printf 'new OneOS scope sync\n' > "$fixture/new-oneos-scope-sync"
|
||||
printf 'new reconciliation evaluator\n' > "$fixture/new-reconciliation-evaluator"
|
||||
printf 'new open platform statistics\n' > "$fixture/new-open-platform-stat"
|
||||
printf 'new alert evaluator\n' > "$fixture/new-alert-evaluator"
|
||||
printf 'new alert stream evaluator\n' > "$fixture/new-alert-stream-evaluator"
|
||||
printf 'new alert notification dispatcher\n' > "$fixture/new-alert-notification-dispatcher"
|
||||
|
||||
printf '#!/usr/bin/env bash\nexit 0\n' > "$fixture/bin/systemctl"
|
||||
printf '#!/usr/bin/env bash\nexit 0\n' > "$fixture/bin/curl"
|
||||
printf '#!/usr/bin/env bash\nprintf "mock_verify=ok\\n"\n' > "$fixture/bin/verify"
|
||||
chmod +x "$fixture/bin/"*
|
||||
|
||||
PLATFORM_ROOT="$root" SYSTEMCTL_BIN="$fixture/bin/systemctl" CURL_BIN="$fixture/bin/curl" VERIFY_WEB_RELEASE_BIN="$fixture/bin/verify" RELEASE_HISTORY_LIMIT=3 "$SCRIPT_DIR/install-web-release.sh" new-release "$fixture/new.tar.gz" "$fixture/new-platform-api" "$fixture/new-oneos-scope-sync" "$fixture/new-reconciliation-evaluator" > "$fixture/install.out"
|
||||
PLATFORM_ROOT="$root" SYSTEMCTL_BIN="$fixture/bin/systemctl" CURL_BIN="$fixture/bin/curl" VERIFY_WEB_RELEASE_BIN="$fixture/bin/verify" RELEASE_HISTORY_LIMIT=3 "$SCRIPT_DIR/install-web-release.sh" new-release "$fixture/new.tar.gz" "$fixture/new-platform-api" "$fixture/new-oneos-scope-sync" "$fixture/new-reconciliation-evaluator" "$fixture/new-open-platform-stat" "$fixture/new-alert-evaluator" "$fixture/new-alert-stream-evaluator" "$fixture/new-alert-notification-dispatcher" > "$fixture/install.out"
|
||||
test "$(readlink -f "$root/current")" = "$(cd "$root/releases/new-release" && pwd -P)"
|
||||
grep -q '^PLATFORM_RELEASE=new-release$' "$root/env/platform.env"
|
||||
test "$(cat "$root/current/platform-api")" = 'new platform API'
|
||||
test "$(cat "$root/current/oneos-scope-sync")" = 'new OneOS scope sync'
|
||||
test "$(cat "$root/current/reconciliation-evaluator")" = 'new reconciliation evaluator'
|
||||
test "$(cat "$root/current/alert-evaluator")" = 'old evaluator'
|
||||
test "$(cat "$root/current/open-platform-stat")" = 'new open platform statistics'
|
||||
test -x "$root/current/open-platform-stat"
|
||||
test "$(cat "$root/current/alert-evaluator")" = 'new alert evaluator'
|
||||
test "$(cat "$root/current/alert-stream-evaluator")" = 'new alert stream evaluator'
|
||||
test "$(cat "$root/current/alert-notification-dispatcher")" = 'new alert notification dispatcher'
|
||||
test -x "$root/current/alert-notification-dispatcher"
|
||||
test -f "$root/current/web/assets/new.js"
|
||||
test -f "$root/current/web/assets/old.js"
|
||||
test -f "$root/current/web/.compatibility-manifests/1.assets"
|
||||
test -x "$root/current/deploy/verify-customer-demo.py"
|
||||
test -x "$root/current/deploy/import-source-providers.py"
|
||||
test -f "$root/current/deploy/migrations/017_reconciliation_center.sql"
|
||||
test -f "$root/current/deploy/migrations/036_alert_rule_archive.sql"
|
||||
test -f "$root/current/deploy/migrations/038_daily_pure_hydrogen_mileage.sql"
|
||||
test -f "$root/current/deploy/systemd/lingniu-vehicle-reconciliation-evaluator.timer"
|
||||
test ! -e "$root/current/lingniu-vehicle-platform.service"
|
||||
test "$(find "$root/releases" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')" = 3
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_app (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(96) NOT NULL,
|
||||
app_key_hash BINARY(32) NOT NULL,
|
||||
app_key_prefix CHAR(8) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'enabled',
|
||||
valid_from DATETIME(3) NOT NULL,
|
||||
valid_to DATETIME(3) NULL,
|
||||
created_by VARCHAR(96) NOT NULL,
|
||||
updated_by VARCHAR(96) NOT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
UNIQUE KEY uk_vehicle_open_app_key_hash (app_key_hash),
|
||||
INDEX idx_vehicle_open_app_validity (status, valid_from, valid_to),
|
||||
CONSTRAINT chk_vehicle_open_app_status CHECK (status IN ('enabled', 'disabled')),
|
||||
CONSTRAINT chk_vehicle_open_app_validity CHECK (valid_to IS NULL OR valid_to > valid_from)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_app_vehicle (
|
||||
app_id BIGINT UNSIGNED NOT NULL,
|
||||
vin VARCHAR(64) NOT NULL,
|
||||
valid_from DATETIME(3) NOT NULL,
|
||||
valid_to DATETIME(3) NULL,
|
||||
granted_by VARCHAR(96) NOT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (app_id, vin),
|
||||
INDEX idx_vehicle_open_grant_vin_time (vin, valid_from, valid_to),
|
||||
INDEX idx_vehicle_open_grant_app_time (app_id, valid_from, valid_to, vin),
|
||||
CONSTRAINT fk_vehicle_open_grant_app FOREIGN KEY (app_id) REFERENCES vehicle_open_app(id) ON DELETE CASCADE,
|
||||
CONSTRAINT chk_vehicle_open_grant_validity CHECK (valid_to IS NULL OR valid_to > valid_from)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_daily_energy (
|
||||
vin VARCHAR(64) NOT NULL,
|
||||
stat_date DATE NOT NULL,
|
||||
energy_type VARCHAR(24) NOT NULL,
|
||||
source_endpoint VARCHAR(128) NOT NULL DEFAULT '',
|
||||
consumption_kg DECIMAL(18,3) NOT NULL DEFAULT 0,
|
||||
unit VARCHAR(16) NOT NULL,
|
||||
first_mass_kg DECIMAL(18,3) NULL,
|
||||
last_mass_kg DECIMAL(18,3) NULL,
|
||||
sample_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
refuel_count INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
quality_status VARCHAR(24) NOT NULL,
|
||||
quality_reason VARCHAR(255) NOT NULL DEFAULT '',
|
||||
calculated_at DATETIME(3) NOT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (vin, stat_date, energy_type),
|
||||
INDEX idx_vehicle_open_energy_date_type (stat_date, energy_type, quality_status, vin),
|
||||
CONSTRAINT chk_vehicle_open_energy_type CHECK (energy_type IN ('HYDROGEN')),
|
||||
CONSTRAINT chk_vehicle_open_energy_quality CHECK (quality_status IN ('OK', 'NO_DATA', 'SUSPECT'))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_api_audit (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
trace_id VARCHAR(64) NOT NULL,
|
||||
app_id BIGINT UNSIGNED NULL,
|
||||
endpoint VARCHAR(64) NOT NULL,
|
||||
result VARCHAR(24) NOT NULL,
|
||||
requested_vehicle_count INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
detail VARCHAR(512) NOT NULL DEFAULT '',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
INDEX idx_vehicle_open_api_audit_app_time (app_id, created_at),
|
||||
INDEX idx_vehicle_open_api_audit_trace (trace_id),
|
||||
INDEX idx_vehicle_open_api_audit_result_time (result, created_at),
|
||||
CONSTRAINT fk_vehicle_open_api_audit_app FOREIGN KEY (app_id) REFERENCES vehicle_open_app(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_admin_audit (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
app_id BIGINT UNSIGNED NULL,
|
||||
actor VARCHAR(96) NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
detail_json JSON NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
INDEX idx_vehicle_open_admin_audit_app_time (app_id, created_at),
|
||||
INDEX idx_vehicle_open_admin_audit_actor_time (actor, created_at),
|
||||
CONSTRAINT fk_vehicle_open_admin_audit_app FOREIGN KEY (app_id) REFERENCES vehicle_open_app(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,65 @@
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_user (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
display_name VARCHAR(96) NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'enabled',
|
||||
valid_from DATETIME(3) NOT NULL,
|
||||
valid_to DATETIME(3) NULL,
|
||||
failed_login_count INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
locked_until DATETIME(3) NULL,
|
||||
password_changed_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
last_login_at DATETIME(3) NULL,
|
||||
created_by VARCHAR(96) NOT NULL,
|
||||
updated_by VARCHAR(96) NOT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
UNIQUE KEY uk_vehicle_open_user_username (username),
|
||||
INDEX idx_vehicle_open_user_status_validity (status, valid_from, valid_to),
|
||||
CONSTRAINT chk_vehicle_open_user_status CHECK (status IN ('enabled', 'disabled')),
|
||||
CONSTRAINT chk_vehicle_open_user_validity CHECK (valid_to IS NULL OR valid_to > valid_from)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_user_app (
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
app_id BIGINT UNSIGNED NOT NULL,
|
||||
role VARCHAR(16) NOT NULL,
|
||||
granted_by VARCHAR(96) NOT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (user_id, app_id),
|
||||
INDEX idx_vehicle_open_user_app_app (app_id, role, user_id),
|
||||
CONSTRAINT fk_vehicle_open_user_app_user FOREIGN KEY (user_id) REFERENCES vehicle_open_user(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_vehicle_open_user_app_app FOREIGN KEY (app_id) REFERENCES vehicle_open_app(id) ON DELETE CASCADE,
|
||||
CONSTRAINT chk_vehicle_open_user_app_role CHECK (role IN ('owner', 'developer', 'viewer'))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_user_session (
|
||||
id CHAR(32) NOT NULL PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
token_hash BINARY(32) NOT NULL,
|
||||
issued_at DATETIME(3) NOT NULL,
|
||||
expires_at DATETIME(3) NOT NULL,
|
||||
last_seen_at DATETIME(3) NOT NULL,
|
||||
revoked_at DATETIME(3) NULL,
|
||||
remote_addr VARCHAR(96) NOT NULL DEFAULT '',
|
||||
user_agent VARCHAR(255) NOT NULL DEFAULT '',
|
||||
UNIQUE KEY uk_vehicle_open_user_session_token (token_hash),
|
||||
INDEX idx_vehicle_open_user_session_user (user_id, expires_at),
|
||||
INDEX idx_vehicle_open_user_session_expiry (expires_at, revoked_at),
|
||||
CONSTRAINT fk_vehicle_open_user_session_user FOREIGN KEY (user_id) REFERENCES vehicle_open_user(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_user_audit (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NULL,
|
||||
actor VARCHAR(96) NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
result VARCHAR(24) NOT NULL,
|
||||
detail_json JSON NULL,
|
||||
remote_addr VARCHAR(96) NOT NULL DEFAULT '',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
INDEX idx_vehicle_open_user_audit_user_time (user_id, created_at),
|
||||
INDEX idx_vehicle_open_user_audit_action_time (action, created_at),
|
||||
CONSTRAINT fk_vehicle_open_user_audit_user FOREIGN KEY (user_id) REFERENCES vehicle_open_user(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,14 @@
|
||||
ALTER TABLE vehicle_open_user_session
|
||||
DROP FOREIGN KEY fk_vehicle_open_user_session_user;
|
||||
|
||||
ALTER TABLE vehicle_open_user_session
|
||||
MODIFY COLUMN user_id BIGINT UNSIGNED NULL,
|
||||
ADD COLUMN platform_user_id BIGINT UNSIGNED NULL AFTER user_id,
|
||||
ADD INDEX idx_vehicle_open_user_session_platform_user (platform_user_id, expires_at),
|
||||
ADD CONSTRAINT fk_vehicle_open_user_session_user
|
||||
FOREIGN KEY (user_id) REFERENCES vehicle_open_user(id) ON DELETE CASCADE,
|
||||
ADD CONSTRAINT fk_vehicle_open_user_session_platform_user
|
||||
FOREIGN KEY (platform_user_id) REFERENCES platform_user(id) ON DELETE CASCADE,
|
||||
ADD CONSTRAINT chk_vehicle_open_user_session_identity
|
||||
CHECK ((user_id IS NOT NULL AND platform_user_id IS NULL)
|
||||
OR (user_id IS NULL AND platform_user_id IS NOT NULL));
|
||||
@@ -0,0 +1,21 @@
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_hydrogen_stream_state (
|
||||
vin VARCHAR(64) NOT NULL,
|
||||
stat_date DATE NOT NULL,
|
||||
source_endpoint VARCHAR(128) NOT NULL DEFAULT '',
|
||||
first_mass_kg DECIMAL(18,3) NOT NULL,
|
||||
last_mass_kg DECIMAL(18,3) NOT NULL,
|
||||
consumption_kg DECIMAL(18,3) NOT NULL DEFAULT 0,
|
||||
sample_count BIGINT UNSIGNED NOT NULL DEFAULT 1,
|
||||
refuel_count INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
abnormal_drop_count INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
first_event_time DATETIME(3) NOT NULL,
|
||||
last_event_time DATETIME(3) NOT NULL,
|
||||
last_event_id VARCHAR(128) NOT NULL DEFAULT '',
|
||||
quality_status VARCHAR(24) NOT NULL DEFAULT 'NO_DATA',
|
||||
quality_reason VARCHAR(255) NOT NULL DEFAULT '有效车载氢量样本不足2条',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (vin, stat_date, source_endpoint),
|
||||
INDEX idx_hydrogen_stream_date_quality (stat_date, quality_status, vin),
|
||||
CONSTRAINT chk_hydrogen_stream_quality CHECK (quality_status IN ('OK', 'NO_DATA', 'SUSPECT'))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_hydrogen_rate_stream_state (
|
||||
vin VARCHAR(64) NOT NULL,
|
||||
stat_date DATE NOT NULL,
|
||||
source_endpoint VARCHAR(128) NOT NULL DEFAULT '',
|
||||
first_rate_kg_per_100km DECIMAL(18,3) NOT NULL,
|
||||
last_rate_kg_per_100km DECIMAL(18,3) NOT NULL,
|
||||
sample_count BIGINT UNSIGNED NOT NULL DEFAULT 1,
|
||||
first_event_time DATETIME(3) NOT NULL,
|
||||
last_event_time DATETIME(3) NOT NULL,
|
||||
last_event_id VARCHAR(128) NOT NULL DEFAULT '',
|
||||
quality_status VARCHAR(24) NOT NULL DEFAULT 'NO_DATA',
|
||||
quality_reason VARCHAR(255) NOT NULL DEFAULT '有效百公里氢耗样本不足2条',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (vin, stat_date, source_endpoint),
|
||||
INDEX idx_hydrogen_rate_stream_date_quality (stat_date, quality_status, vin),
|
||||
CONSTRAINT chk_hydrogen_rate_stream_quality CHECK (quality_status IN ('OK', 'NO_DATA'))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,26 @@
|
||||
CREATE TABLE IF NOT EXISTS vehicle_hydrogen_tank_capacity (
|
||||
vin VARCHAR(64) NOT NULL,
|
||||
source_vehicle_id BIGINT NOT NULL,
|
||||
source_model_id BIGINT NULL,
|
||||
plate_number VARCHAR(50) NOT NULL DEFAULT '',
|
||||
brand_name VARCHAR(500) NOT NULL DEFAULT '',
|
||||
model_name VARCHAR(500) NOT NULL DEFAULT '',
|
||||
tank_capacity_l DECIMAL(12,2) NOT NULL,
|
||||
active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
source_updated_at DATETIME NULL,
|
||||
synced_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (vin),
|
||||
INDEX idx_hydrogen_capacity_model (source_model_id),
|
||||
INDEX idx_hydrogen_capacity_active (active,vin)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE vehicle_open_hydrogen_stream_state
|
||||
ADD COLUMN tank_capacity_l DECIMAL(12,2) NULL AFTER abnormal_drop_count,
|
||||
ADD COLUMN first_pressure_mpa DECIMAL(10,3) NULL AFTER tank_capacity_l,
|
||||
ADD COLUMN last_pressure_mpa DECIMAL(10,3) NULL AFTER first_pressure_mpa,
|
||||
ADD COLUMN first_temperature_c DECIMAL(10,3) NULL AFTER last_pressure_mpa,
|
||||
ADD COLUMN last_temperature_c DECIMAL(10,3) NULL AFTER first_temperature_c,
|
||||
ADD COLUMN calculation_method VARCHAR(32) NOT NULL DEFAULT 'PRESSURE_NIST' AFTER last_temperature_c;
|
||||
|
||||
CREATE INDEX idx_hydrogen_capacity_sync
|
||||
ON vehicle_hydrogen_tank_capacity (synced_at,active);
|
||||
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE vehicle_open_hydrogen_stream_state
|
||||
ADD COLUMN cycle_min_mass_kg DECIMAL(18,3) NULL AFTER last_mass_kg;
|
||||
|
||||
UPDATE vehicle_open_hydrogen_stream_state
|
||||
SET cycle_min_mass_kg=last_mass_kg
|
||||
WHERE cycle_min_mass_kg IS NULL;
|
||||
@@ -0,0 +1,22 @@
|
||||
-- Add first-class automation trigger semantics without breaking existing metric rules.
|
||||
ALTER TABLE vehicle_alert_rule
|
||||
ADD COLUMN trigger_type VARCHAR(24) NOT NULL DEFAULT 'metric' AFTER description,
|
||||
ADD COLUMN fence_name VARCHAR(80) NOT NULL DEFAULT '' AFTER trigger_type,
|
||||
ADD COLUMN fence_longitude DOUBLE NOT NULL DEFAULT 0 AFTER fence_name,
|
||||
ADD COLUMN fence_latitude DOUBLE NOT NULL DEFAULT 0 AFTER fence_longitude,
|
||||
ADD COLUMN fence_radius_m DOUBLE NOT NULL DEFAULT 0 AFTER fence_latitude;
|
||||
|
||||
ALTER TABLE vehicle_alert_event
|
||||
ADD COLUMN trigger_type VARCHAR(24) NOT NULL DEFAULT 'metric' AFTER severity;
|
||||
|
||||
CREATE INDEX idx_alert_rule_trigger_type ON vehicle_alert_rule(trigger_type, enabled);
|
||||
CREATE INDEX idx_alert_event_trigger_type ON vehicle_alert_event(trigger_type, triggered_at);
|
||||
|
||||
-- Preserve the business meaning of rules that existed before trigger_type.
|
||||
UPDATE vehicle_alert_rule
|
||||
SET trigger_type = CASE
|
||||
WHEN metric = 'freshness_sec' THEN 'offline'
|
||||
WHEN metric = 'speed_kmh' AND operator IN ('lt','lte') AND duration_sec >= 300 THEN 'stationary'
|
||||
ELSE 'metric'
|
||||
END
|
||||
WHERE trigger_type = 'metric';
|
||||
@@ -0,0 +1,7 @@
|
||||
ALTER TABLE vehicle_reconciliation_issue
|
||||
ADD COLUMN assignee VARCHAR(128) NOT NULL DEFAULT '' AFTER resolved_by,
|
||||
ADD COLUMN assigned_by VARCHAR(128) NOT NULL DEFAULT '' AFTER assignee,
|
||||
ADD COLUMN assigned_at DATETIME(3) NULL AFTER assigned_by,
|
||||
ADD COLUMN due_at DATETIME(3) NULL AFTER assigned_at,
|
||||
ADD INDEX idx_reconciliation_owner (assignee, status, due_at),
|
||||
ADD INDEX idx_reconciliation_due (due_at, status);
|
||||
@@ -0,0 +1,29 @@
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_mileage_snapshot (
|
||||
snapshot_id CHAR(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL PRIMARY KEY,
|
||||
app_id BIGINT UNSIGNED NOT NULL,
|
||||
request_hash BINARY(32) NOT NULL,
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
vehicle_count INT UNSIGNED NOT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
expires_at DATETIME(3) NOT NULL,
|
||||
INDEX idx_open_mileage_snapshot_expiry (expires_at),
|
||||
INDEX idx_open_mileage_snapshot_app (app_id, created_at),
|
||||
CONSTRAINT fk_open_mileage_snapshot_app
|
||||
FOREIGN KEY (app_id) REFERENCES vehicle_open_app(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vehicle_open_mileage_snapshot_vehicle (
|
||||
snapshot_id CHAR(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
ordinal INT UNSIGNED NOT NULL,
|
||||
vin VARCHAR(64) NOT NULL,
|
||||
plate VARCHAR(64) NOT NULL,
|
||||
PRIMARY KEY (snapshot_id, ordinal),
|
||||
UNIQUE KEY uk_open_mileage_snapshot_plate (snapshot_id, plate),
|
||||
INDEX idx_open_mileage_snapshot_vin (snapshot_id, vin),
|
||||
CONSTRAINT fk_open_mileage_snapshot_vehicle
|
||||
FOREIGN KEY (snapshot_id) REFERENCES vehicle_open_mileage_snapshot(snapshot_id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE INDEX idx_daily_mileage_source_open_selected
|
||||
ON vehicle_daily_mileage_source(vin, stat_date, protocol, is_selected, latest_event_time);
|
||||
@@ -0,0 +1,28 @@
|
||||
ALTER TABLE vehicle_alert_notification
|
||||
ADD COLUMN recipient VARCHAR(160) NOT NULL DEFAULT '',
|
||||
ADD COLUMN attempt_count INT NOT NULL DEFAULT 1,
|
||||
ADD COLUMN provider_message_id VARCHAR(160) NOT NULL DEFAULT '',
|
||||
ADD COLUMN delivered_at DATETIME(3) NULL,
|
||||
ADD COLUMN last_error VARCHAR(500) NOT NULL DEFAULT '',
|
||||
ADD COLUMN last_attempt_at DATETIME(3) NULL,
|
||||
ADD COLUMN retry_requested_by VARCHAR(128) NOT NULL DEFAULT '',
|
||||
ADD COLUMN retry_requested_at DATETIME(3) NULL,
|
||||
ADD KEY idx_alert_notification_delivery (delivery_status, created_at),
|
||||
ADD KEY idx_alert_notification_retry (retry_requested_at, id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vehicle_alert_notification_retry_audit (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
notification_id BIGINT NOT NULL,
|
||||
idempotency_key VARCHAR(96) NOT NULL,
|
||||
actor VARCHAR(128) NOT NULL,
|
||||
reason VARCHAR(200) NOT NULL,
|
||||
previous_status VARCHAR(32) NOT NULL,
|
||||
next_status VARCHAR(32) NOT NULL,
|
||||
attempt_count INT NOT NULL,
|
||||
requested_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
UNIQUE KEY uk_alert_notification_retry_key (idempotency_key),
|
||||
KEY idx_alert_notification_retry_notification (notification_id, requested_at),
|
||||
CONSTRAINT fk_alert_notification_retry_notification
|
||||
FOREIGN KEY (notification_id) REFERENCES vehicle_alert_notification(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
@@ -0,0 +1,19 @@
|
||||
ALTER TABLE vehicle_alert_rule
|
||||
ADD COLUMN notification_targets_json TEXT NULL AFTER notification_channels_json;
|
||||
|
||||
UPDATE vehicle_alert_rule
|
||||
SET notification_targets_json = CASE
|
||||
WHEN JSON_CONTAINS(notification_channels_json, '"in_app"') THEN
|
||||
'[{"channel":"in_app","recipientId":"platform-operators","label":"平台值班组"}]'
|
||||
ELSE '[]'
|
||||
END
|
||||
WHERE notification_targets_json IS NULL;
|
||||
|
||||
ALTER TABLE vehicle_alert_rule
|
||||
MODIFY COLUMN notification_targets_json TEXT NOT NULL;
|
||||
|
||||
ALTER TABLE vehicle_alert_notification
|
||||
ADD COLUMN recipient_ref VARCHAR(96) NOT NULL DEFAULT '' AFTER recipient,
|
||||
ADD COLUMN lease_token VARCHAR(96) NOT NULL DEFAULT '',
|
||||
ADD COLUMN lease_expires_at DATETIME(3) NULL,
|
||||
ADD KEY idx_alert_notification_dispatch (channel, delivery_status, lease_expires_at, id);
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Reversible lifecycle for large automation-rule libraries.
|
||||
ALTER TABLE vehicle_alert_rule
|
||||
ADD COLUMN archived_by VARCHAR(128) NOT NULL DEFAULT '' AFTER updated_by,
|
||||
ADD COLUMN archived_at DATETIME(3) NULL AFTER archived_by,
|
||||
ADD COLUMN archive_reason VARCHAR(200) NOT NULL DEFAULT '' AFTER archived_at,
|
||||
ADD KEY idx_alert_rule_lifecycle (archived_at, enabled, updated_at);
|
||||
|
||||
ALTER TABLE vehicle_alert_rule_audit
|
||||
ADD COLUMN reason VARCHAR(200) NOT NULL DEFAULT '' AFTER action;
|
||||
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE vehicle_reconciliation_issue
|
||||
ADD COLUMN archived_at DATETIME(3) NULL AFTER due_at,
|
||||
ADD COLUMN archived_by VARCHAR(128) NOT NULL DEFAULT '' AFTER archived_at,
|
||||
ADD COLUMN archive_reason VARCHAR(500) NOT NULL DEFAULT '' AFTER archived_by,
|
||||
ADD INDEX idx_reconciliation_archive (archived_at, severity, last_seen_at),
|
||||
ADD INDEX idx_reconciliation_archive_actor (archived_by, archived_at);
|
||||
@@ -0,0 +1,8 @@
|
||||
ALTER TABLE vehicle_daily_mileage
|
||||
ADD COLUMN pure_hydrogen_mileage_km DECIMAL(18,3) NOT NULL DEFAULT 0 AFTER daily_mileage_km;
|
||||
|
||||
ALTER TABLE vehicle_daily_mileage_source
|
||||
ADD COLUMN pure_hydrogen_mileage_km DECIMAL(18,3) NOT NULL DEFAULT 0 AFTER daily_mileage_km,
|
||||
ADD COLUMN pure_hydrogen_sample_count BIGINT NOT NULL DEFAULT 0 AFTER sample_count,
|
||||
ADD COLUMN latest_pure_hydrogen_active TINYINT(1) NOT NULL DEFAULT 0 AFTER latest_event_time,
|
||||
ADD COLUMN latest_pure_hydrogen_mode_known TINYINT(1) NOT NULL DEFAULT 0 AFTER latest_pure_hydrogen_active;
|
||||
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE IF NOT EXISTS platform_user_business_scope (
|
||||
user_id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
|
||||
scope_level VARCHAR(24) NOT NULL,
|
||||
department_ids VARCHAR(1024) NOT NULL DEFAULT '',
|
||||
responsible_user_id VARCHAR(96) NOT NULL DEFAULT '',
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
source_system VARCHAR(32) NOT NULL DEFAULT 'oneos',
|
||||
source_updated_at DATETIME(3) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
CONSTRAINT fk_platform_user_business_scope_user FOREIGN KEY (user_id) REFERENCES platform_user(id) ON DELETE CASCADE,
|
||||
CONSTRAINT chk_platform_user_business_scope_level CHECK (scope_level IN ('department', 'responsible')),
|
||||
INDEX idx_platform_user_business_scope_level (scope_level, enabled),
|
||||
INDEX idx_platform_user_business_scope_responsible (responsible_user_id, enabled)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX idx_daily_mileage_open_latest
|
||||
ON vehicle_daily_mileage(vin, protocol, stat_date);
|
||||
@@ -0,0 +1,23 @@
|
||||
[Unit]
|
||||
Description=Lingniu Vehicle Alert Notification Dispatcher
|
||||
After=network-online.target lingniu-vehicle-platform.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/lingniu-vehicle-platform/current
|
||||
EnvironmentFile=/opt/lingniu-go-native/env/base.env
|
||||
EnvironmentFile=/opt/lingniu-vehicle-platform/env/platform.env
|
||||
ExecStart=/opt/lingniu-vehicle-platform/current/alert-notification-dispatcher
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
TimeoutStopSec=30
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
ProtectHome=true
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,24 @@
|
||||
[Unit]
|
||||
Description=Lingniu Vehicle Data Open Platform
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/lingniu-vehicle-open-platform/current
|
||||
EnvironmentFile=/opt/lingniu-go-native/env/base.env
|
||||
EnvironmentFile=/opt/lingniu-vehicle-open-platform/env/open-platform.env
|
||||
ExecStart=/opt/lingniu-vehicle-open-platform/current/open-platform-api
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
LimitNOFILE=1048576
|
||||
KillSignal=SIGTERM
|
||||
TimeoutStopSec=30
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
ProtectHome=true
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,24 @@
|
||||
[Unit]
|
||||
Description=Lingniu Vehicle Open Platform Daily Statistics
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
WorkingDirectory=/opt/lingniu-vehicle-platform/current
|
||||
EnvironmentFile=/opt/lingniu-go-native/env/base.env
|
||||
EnvironmentFile=/opt/lingniu-vehicle-platform/env/platform.env
|
||||
Environment=OPEN_STAT_LOOKBACK_DAYS=2
|
||||
ExecStart=/opt/lingniu-vehicle-platform/current/open-platform-stat
|
||||
Nice=10
|
||||
IOSchedulingClass=best-effort
|
||||
IOSchedulingPriority=6
|
||||
TimeoutStartSec=1800
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
ProtectHome=true
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Calculate vehicle open-platform daily statistics
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 01:15:00
|
||||
RandomizedDelaySec=10m
|
||||
AccuracySec=1m
|
||||
Persistent=true
|
||||
Unit=lingniu-vehicle-open-stat.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
Reference in New Issue
Block a user