feat: 接入旧站管理端并完整迁移后台能力

This commit is contained in:
yml
2026-11-12 22:03:01 +08:00
parent e5f923706b
commit a4441bba9a
509 changed files with 140714 additions and 1068 deletions
+3 -3
View File
@@ -41,11 +41,11 @@ require_pattern "backend/internal/modules/pickup/repository.go" "if pageSize > 1
# 智能意图路由与单表唯一索引直达防线(防止多表跨表 OR/LIKE 导致 MySQL 放弃索引而退化为全表扫描)
require_pattern "backend/internal/modules/order/queries.go" "o.order_no = ?" "订单列表订单号唯一索引直达"
require_pattern "backend/internal/modules/order/queries.go" "l.listing_no = ?" "订单列表商品号唯一索引直达"
require_pattern "backend/internal/modules/order/queries.go" "owner.phone = ? OR renter.phone = ?" "订单列表手机号索引直达"
require_pattern "backend/internal/modules/order/queries.go" "o.owner_id = (SELECT id FROM users WHERE phone = ?) OR o.renter_id = (SELECT id FROM users WHERE phone = ?)" "订单列表手机号索引直达"
require_pattern "backend/internal/modules/order/queries.go" "page == 1 && len(rows) < pageSize" "订单列表首页面单扫描短路"
require_pattern "backend/internal/modules/pickup/repository.go" "p.pickup_no = ?" "提号列表提号单号唯一索引直达"
require_pattern "backend/internal/modules/pickup/repository.go" "owner.phone = ?" "提号列表号主手机号索引直达"
require_pattern "backend/internal/modules/pickup/repository.go" "p.owner_id = (SELECT id FROM users WHERE phone = ?)" "提号列表号主手机号索引直达"
require_pattern "backend/internal/modules/pickup/repository.go" "page == 1 && len(rows) < pageSize" "提号列表首页面单扫描短路"
require_pattern "backend/internal/modules/dispute/query.go" "o.order_no = ?" "纠纷列表订单号唯一索引直达"
@@ -58,7 +58,7 @@ require_pattern "backend/internal/modules/listing/query.go" "l.listing_no = ?" "
require_pattern "backend/internal/modules/listing/query.go" "page == 1 && len(rows) < pageSize" "商品列表首页面单扫描短路"
require_pattern "backend/internal/modules/chat/support.go" "l.listing_no = ?" "客服会话商品号唯一索引直达"
require_pattern "backend/internal/modules/chat/support.go" "renter.phone = ? OR owner.phone = ?" "客服会话手机号唯一索引直达"
require_pattern "backend/internal/modules/chat/support.go" "o2.owner_id = (SELECT id FROM users WHERE phone = ?)" "客服会话手机号唯一索引直达"
# 禁止核心列表重新引入跨表 6~10 路全扫描反模式
forbid_pattern "backend/internal/modules/dispute/query.go" "o.order_no LIKE ? OR l.listing_no LIKE ? OR a.title LIKE ? OR owner.phone LIKE ? OR renter.phone LIKE ? OR d.id = ?" "纠纷列表禁止6表无意图全表扫描"
+16
View File
@@ -16,6 +16,7 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BACKEND_DIR="${ROOT_DIR}/backend"
FRONTEND_DIR="${ROOT_DIR}/frontend"
ADMIN_WEB_DIR="${ROOT_DIR}/admin-web"
RED='\033[0;31m'
GREEN='\033[0;32m'
@@ -127,6 +128,21 @@ if [[ "${SKIP_FRONTEND}" == "0" ]]; then
log_warn "跳过前端测试 (--skip-tests)"
fi
run_step "npm run build (vite 生产构建)" npm run build || FAILED=1
log "管理端检查目录: ${ADMIN_WEB_DIR}"
cd "${ADMIN_WEB_DIR}"
if [[ ! -d node_modules ]]; then
run_step "管理端 npm ci (首次安装依赖)" npm ci || FAILED=1
fi
run_step "管理端 npm run check (prettier + eslint + typecheck)" npm run check || FAILED=1
if [[ "${SKIP_TESTS}" == "0" ]]; then
run_step "管理端 npm run test (vitest)" npm run test || FAILED=1
else
log_warn "跳过后台管理端测试 (--skip-tests)"
fi
run_step "管理端 npm run build (vite 生产构建)" npm run build || FAILED=1
else
log_warn "跳过前端检查 (--skip-frontend)"
fi
+80 -8
View File
@@ -10,6 +10,10 @@ FRONTEND_PACKAGE_JSON="${FRONTEND_DIR}/package.json"
FRONTEND_PACKAGE_LOCK="${FRONTEND_DIR}/package-lock.json"
FRONTEND_NODE_MODULES="${FRONTEND_DIR}/node_modules"
FRONTEND_NODE_MODULES_LOCK="${FRONTEND_NODE_MODULES}/.package-lock.json"
ADMIN_WEB_DIR="${ROOT_DIR}/admin-web"
ADMIN_WEB_PACKAGE_LOCK="${ADMIN_WEB_DIR}/package-lock.json"
ADMIN_WEB_NODE_MODULES="${ADMIN_WEB_DIR}/node_modules"
ADMIN_WEB_NODE_MODULES_LOCK="${ADMIN_WEB_NODE_MODULES}/.package-lock.json"
DISTRIBUTION_DIR="${ROOT_DIR}/distribution-admin"
DISTRIBUTION_PACKAGE_JSON="${DISTRIBUTION_DIR}/package.json"
DISTRIBUTION_PACKAGE_LOCK="${DISTRIBUTION_DIR}/package-lock.json"
@@ -19,6 +23,7 @@ MIGRATIONS_DIR="${ROOT_DIR}/backend/migrations"
MYSQL_DATABASE="${DEV_MYSQL_DATABASE:-hfb_jysh}"
BACKEND_PORT="${DEV_BACKEND_PORT:-18089}"
FRONTEND_PORT="${DEV_FRONTEND_PORT:-5289}"
ADMIN_WEB_PORT="${DEV_ADMIN_WEB_PORT:-5292}"
DISTRIBUTION_PORT="${DEV_DISTRIBUTION_PORT:-5290}"
DISTRIBUTION_STOREFRONT_PORT="${DEV_DISTRIBUTION_STOREFRONT_PORT:-5291}"
DISTRIBUTION_STOREFRONT_SITE_CODE="${DEV_DISTRIBUTION_STOREFRONT_SITE_CODE:-test_1}"
@@ -28,6 +33,7 @@ MINIO_API_PORT="${DEV_MINIO_API_PORT:-29090}"
MINIO_CONSOLE_PORT="${DEV_MINIO_CONSOLE_PORT:-29091}"
BACKEND_HEALTH_URL="${DEV_BACKEND_HEALTH_URL:-http://127.0.0.1:${BACKEND_PORT}/health}"
FRONTEND_HEALTH_URL="${DEV_FRONTEND_HEALTH_URL:-http://127.0.0.1:${FRONTEND_PORT}/}"
ADMIN_WEB_HEALTH_URL="${DEV_ADMIN_WEB_HEALTH_URL:-http://127.0.0.1:${ADMIN_WEB_PORT}${VITE_ADMIN_BASE_PATH:-/console-jy8k3m}/login}"
DISTRIBUTION_HEALTH_URL="${DEV_DISTRIBUTION_HEALTH_URL:-http://127.0.0.1:${DISTRIBUTION_PORT}/}"
DISTRIBUTION_STOREFRONT_HEALTH_URL="${DEV_DISTRIBUTION_STOREFRONT_HEALTH_URL:-http://127.0.0.1:${DISTRIBUTION_STOREFRONT_PORT}/}"
HTTP_READY_TIMEOUT="${DEV_HTTP_READY_TIMEOUT:-120}"
@@ -35,9 +41,11 @@ AUTO_KILL_PORTS="${DEV_AUTO_KILL_PORTS:-1}"
FORCE_NPM_CI="${DEV_FORCE_NPM_CI:-0}"
GOOSE_VERSION="${GOOSE_VERSION:-v3.27.1}"
GOOSE_BIN="${GOOSE_BIN:-}"
GOOSE_GOPROXY="${GOOSE_GOPROXY:-https://goproxy.cn,direct}"
BACKEND_PID=""
FRONTEND_PID=""
ADMIN_WEB_PID=""
DISTRIBUTION_PID=""
DISTRIBUTION_STOREFRONT_PID=""
CLEANED_UP=0
@@ -80,6 +88,7 @@ show_help() {
--reset-db 重置数据库(删除并重建)
--no-migrate 不执行数据库迁移
--no-frontend 不启动主站前端
--no-admin 不启动独立管理端
--no-backend 不启动后端
--no-distribution 不启动分销后台
--no-distribution-storefront
@@ -94,6 +103,8 @@ show_help() {
后端启动端口
DEV_FRONTEND_PORT=5289
主站前端启动端口
DEV_ADMIN_WEB_PORT=5292
独立管理端启动端口
DEV_DISTRIBUTION_PORT=5290
分销后台启动端口
DEV_MYSQL_PORT=23306
@@ -108,6 +119,8 @@ show_help() {
后端健康检查地址
DEV_FRONTEND_HEALTH_URL=http://127.0.0.1:5289/
主站前端健康检查地址
DEV_ADMIN_WEB_HEALTH_URL=http://127.0.0.1:5292/console-jy8k3m/login
独立管理端健康检查地址
DEV_DISTRIBUTION_HEALTH_URL=http://127.0.0.1:5290/
分销后台健康检查地址
DEV_HTTP_READY_TIMEOUT=120
@@ -116,6 +129,8 @@ show_help() {
前后端端口被占用时自动停止占用的 Docker 容器或本机进程
DEV_FORCE_NPM_CI=0
强制重新执行前端 npm ci
GOOSE_GOPROXY=https://goproxy.cn,direct
Goose 安装失败时使用的 Go 模块代理
示例:
./scripts/dev.sh # 正常启动(后端 + 主前后台 + 分销前台)
@@ -128,6 +143,7 @@ EOF
RESET_DB="${DEV_RESET_DB:-0}"
NO_MIGRATE=0
NO_FRONTEND=0
NO_ADMIN_WEB=0
NO_BACKEND=0
NO_DISTRIBUTION=0
NO_DISTRIBUTION_STOREFRONT=0
@@ -146,6 +162,10 @@ while [[ $# -gt 0 ]]; do
NO_FRONTEND=1
shift
;;
--no-admin)
NO_ADMIN_WEB=1
shift
;;
--no-backend)
NO_BACKEND=1
shift
@@ -174,7 +194,7 @@ cleanup() {
fi
CLEANED_UP=1
if [[ -z "${FRONTEND_PID}" && -z "${BACKEND_PID}" && -z "${DISTRIBUTION_PID}" && -z "${DISTRIBUTION_STOREFRONT_PID}" ]]; then
if [[ -z "${FRONTEND_PID}" && -z "${ADMIN_WEB_PID}" && -z "${BACKEND_PID}" && -z "${DISTRIBUTION_PID}" && -z "${DISTRIBUTION_STOREFRONT_PID}" ]]; then
return
fi
@@ -185,6 +205,9 @@ cleanup() {
if [[ -n "${DISTRIBUTION_PID}" ]] && kill -0 "${DISTRIBUTION_PID}" >/dev/null 2>&1; then
kill "${DISTRIBUTION_PID}" >/dev/null 2>&1 || true
fi
if [[ -n "${ADMIN_WEB_PID}" ]] && kill -0 "${ADMIN_WEB_PID}" >/dev/null 2>&1; then
kill "${ADMIN_WEB_PID}" >/dev/null 2>&1 || true
fi
if [[ -n "${FRONTEND_PID}" ]] && kill -0 "${FRONTEND_PID}" >/dev/null 2>&1; then
kill "${FRONTEND_PID}" >/dev/null 2>&1 || true
fi
@@ -446,7 +469,10 @@ ensure_goose() {
if [[ -z "${GOOSE_BIN}" ]]; then
need_cmd go
log "未找到 goose,正在安装 ${GOOSE_VERSION}..."
(cd "${ROOT_DIR}/backend" && go install "github.com/pressly/goose/v3/cmd/goose@${GOOSE_VERSION}")
if ! (cd "${ROOT_DIR}/backend" && go install "github.com/pressly/goose/v3/cmd/goose@${GOOSE_VERSION}"); then
log_warn "默认 Go 模块代理不可用,改用 ${GOOSE_GOPROXY} 重试..."
(cd "${ROOT_DIR}/backend" && GOPROXY="${GOOSE_GOPROXY}" go install "github.com/pressly/goose/v3/cmd/goose@${GOOSE_VERSION}")
fi
GOOSE_BIN="$(go env GOPATH)/bin/goose"
fi
@@ -575,6 +601,40 @@ start_frontend() {
FRONTEND_PID="$!"
}
ensure_admin_web_dependencies() {
if [[ ! -f "${ADMIN_WEB_PACKAGE_LOCK}" ]]; then
log_error "缺少 admin-web/package-lock.json,无法执行 npm ci"
exit 1
fi
if [[ "${FORCE_NPM_CI}" == "1" || ! -d "${ADMIN_WEB_NODE_MODULES}" || ! -f "${ADMIN_WEB_NODE_MODULES_LOCK}" ]]; then
log "同步管理端依赖(npm ci)..."
(
cd "${ADMIN_WEB_DIR}"
npm ci
)
log_success "管理端依赖已同步"
else
log "管理端依赖已同步,跳过 npm ci"
fi
}
start_admin_web() {
ensure_admin_web_dependencies
log "启动独立管理端:http://localhost:${ADMIN_WEB_PORT}${VITE_ADMIN_BASE_PATH:-/console-jy8k3m}/login"
(
cd "${ADMIN_WEB_DIR}"
export PORT="${ADMIN_WEB_PORT}"
export DEV_FRONTEND_PORT="${ADMIN_WEB_PORT}"
export DEV_BACKEND_PORT="${BACKEND_PORT}"
export VITE_ADMIN_BASE_PATH="${VITE_ADMIN_BASE_PATH:-/console-jy8k3m}"
export VITE_API_PROXY_TARGET="${VITE_API_PROXY_TARGET:-http://127.0.0.1:${BACKEND_PORT}}"
npm run dev
) &
ADMIN_WEB_PID="$!"
}
distribution_dependencies_need_ci() {
if [[ "${FORCE_NPM_CI}" == "1" ]]; then
return 0
@@ -660,6 +720,11 @@ watch_processes() {
printf "主站前端进程已退出\n" >&2
exit 1
fi
if [[ -n "${ADMIN_WEB_PID}" ]] && ! kill -0 "${ADMIN_WEB_PID}" >/dev/null 2>&1; then
wait "${ADMIN_WEB_PID}" || true
printf "管理端进程已退出\n" >&2
exit 1
fi
if [[ -n "${DISTRIBUTION_PID}" ]] && ! kill -0 "${DISTRIBUTION_PID}" >/dev/null 2>&1; then
wait "${DISTRIBUTION_PID}" || true
printf "分销后台进程已退出\n" >&2
@@ -679,10 +744,10 @@ main() {
if [[ "${NO_BACKEND}" == "0" ]]; then
need_cmd go
fi
if [[ "${NO_FRONTEND}" == "0" || "${NO_DISTRIBUTION}" == "0" ]]; then
if [[ "${NO_FRONTEND}" == "0" || "${NO_ADMIN_WEB}" == "0" || "${NO_DISTRIBUTION}" == "0" ]]; then
need_cmd npm
fi
if [[ "${NO_BACKEND}" == "0" || "${NO_FRONTEND}" == "0" || "${NO_DISTRIBUTION}" == "0" ]]; then
if [[ "${NO_BACKEND}" == "0" || "${NO_FRONTEND}" == "0" || "${NO_ADMIN_WEB}" == "0" || "${NO_DISTRIBUTION}" == "0" ]]; then
need_cmd curl
fi
@@ -726,6 +791,12 @@ main() {
wait_http_ready "${FRONTEND_HEALTH_URL}" "主站前端" "${FRONTEND_PID}"
fi
if [[ "${NO_ADMIN_WEB}" == "0" && -d "${ADMIN_WEB_DIR}" ]]; then
ensure_port_free "${ADMIN_WEB_PORT}" "管理端"
start_admin_web
wait_http_ready "${ADMIN_WEB_HEALTH_URL}" "管理端" "${ADMIN_WEB_PID}"
fi
if [[ "${NO_DISTRIBUTION}" == "0" && -d "${DISTRIBUTION_DIR}" ]]; then
ensure_port_free "${DISTRIBUTION_PORT}" "分销后台"
start_distribution
@@ -738,16 +809,17 @@ main() {
wait_http_ready "${DISTRIBUTION_STOREFRONT_HEALTH_URL}" "分销前台" "${DISTRIBUTION_STOREFRONT_PID}"
fi
if [[ -z "${BACKEND_PID}" && -z "${FRONTEND_PID}" && -z "${DISTRIBUTION_PID}" && -z "${DISTRIBUTION_STOREFRONT_PID}" ]]; then
log "仅启动 Docker 开发依赖,前后端与分销端未启动"
if [[ -z "${BACKEND_PID}" && -z "${FRONTEND_PID}" && -z "${ADMIN_WEB_PID}" && -z "${DISTRIBUTION_PID}" && -z "${DISTRIBUTION_STOREFRONT_PID}" ]]; then
log "仅启动 Docker 开发依赖,前端、管理端、后端与分销端未启动"
return 0
fi
log_success "开发环境已就绪"
if [[ "${NO_FRONTEND}" == "0" ]]; then
local admin_base_path="${VITE_ADMIN_BASE_PATH:-/admin}"
log "主站前台:http://localhost:${FRONTEND_PORT}"
log "主站后台:http://localhost:${FRONTEND_PORT}${admin_base_path%/}/login"
fi
if [[ "${NO_ADMIN_WEB}" == "0" && -d "${ADMIN_WEB_DIR}" ]]; then
log "管理端:http://localhost:${ADMIN_WEB_PORT}${VITE_ADMIN_BASE_PATH:-/console-jy8k3m}/login"
fi
if [[ "${NO_DISTRIBUTION}" == "0" && -d "${DISTRIBUTION_DIR}" ]]; then
log "分销后台:http://localhost:${DISTRIBUTION_PORT}"