mirror of
https://gitee.com/ShopeX/ECShopX
synced 2026-08-08 04:55:31 +08:00
4.6.4
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -43,3 +43,5 @@ docker-dev/seed-*.sql
|
||||
docker-dev/patch-*.sql
|
||||
docker-dev/extract-weapp-staging.py
|
||||
docker-compose.override.yml
|
||||
|
||||
.codegraph/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "laravel/lumen",
|
||||
"version": "4.6.3",
|
||||
"version": "4.6.4",
|
||||
"description": "The Laravel Lumen Framework.",
|
||||
"keywords": [
|
||||
"framework",
|
||||
|
||||
@@ -108,6 +108,7 @@ return [
|
||||
'encrypt_sensitive_data' => env('ENCRYPT_SENSITIVE_DATA', false),
|
||||
|
||||
'sms_send_limit' => env("SMS_SEND_LIMIT", 5), // 一种短信验证码在一天里的发送上限
|
||||
'sms_debug_vcode' => env('SMS_DEBUG_VCODE', false), // 开发调试短信验证码:开启后不调用真实短信通道,直接返回验证码
|
||||
|
||||
'shop_admin_url' => env('SHOP_ADMIN_URL', ''),// 管理后台地址,结尾带斜杠
|
||||
'appcenter_base_url' => env('APPCENTER_BASE_URL', env('TEST_MODE', false) ? 'https://account.uc.ex-sandbox.com' : 'https://account.shopex.cn'),
|
||||
|
||||
464
dev-setup.sh
464
dev-setup.sh
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ECShopX 开发环境设置脚本(Docker 方式)
|
||||
# 使用单容器运行所有服务:PHP-FPM、Nginx、MySQL、Redis
|
||||
# 支持三个项目:ECShopX、ECShopX_admin-frontend、ECShopX_mobile-frontend
|
||||
# 主容器运行 PHP-FPM、Nginx、MySQL、Redis,Web 前端使用独立 Node 容器
|
||||
# 支持四个项目:ECShopX、ECShopX_admin-frontend、ECShopX_mobile-frontend、ECShopX_web-frontend
|
||||
|
||||
set -e
|
||||
|
||||
@@ -17,6 +17,7 @@ PARENT_DIR="$(cd "$PROJECT_ROOT/.." && pwd)"
|
||||
|
||||
# 容器配置
|
||||
CONTAINER_NAME="ecshopx-dev"
|
||||
WEB_CONTAINER_NAME="ecshopx-web-frontend"
|
||||
DOCKER_COMPOSE_FILE="$PROJECT_ROOT/docker-compose.dev.yml"
|
||||
|
||||
# 数据库配置
|
||||
@@ -197,6 +198,18 @@ is_container_running() {
|
||||
docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$" 2>/dev/null
|
||||
}
|
||||
|
||||
is_web_container_running() {
|
||||
docker ps --format '{{.Names}}' | grep -q "^${WEB_CONTAINER_NAME}$" 2>/dev/null
|
||||
}
|
||||
|
||||
ensure_web_container_running() {
|
||||
if ! is_web_container_running; then
|
||||
log_error "Web 前端容器 $WEB_CONTAINER_NAME 未运行,请先启动 Docker Compose"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===========================================
|
||||
# 检查容器是否存在(包括已停止的)
|
||||
# ===========================================
|
||||
@@ -344,6 +357,62 @@ check_docker() {
|
||||
check_and_clone_frontend() {
|
||||
log_step "检查前端项目目录..."
|
||||
|
||||
# 检查 ECShopX_web-frontend
|
||||
PC_DIR="$PARENT_DIR/ECShopX_web-frontend"
|
||||
PC_REPO="https://gitee.com/ShopeX/ECShopX_web-frontend.git"
|
||||
|
||||
if [ ! -d "$PC_DIR" ] || [ ! -f "$PC_DIR/package.json" ]; then
|
||||
if [ ! -d "$PC_DIR" ]; then
|
||||
log_warning "ECShopX_web-frontend 目录不存在"
|
||||
else
|
||||
log_warning "ECShopX_web-frontend 目录存在但缺少 package.json"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -n "是否从 Gitee 克隆PC商城(ECShopX_web-frontend)代码? [Y/n]: "
|
||||
read -r answer < /dev/tty
|
||||
|
||||
if [ -z "$answer" ] || [ "$answer" = "Y" ] || [ "$answer" = "y" ] || [ "$answer" = "yes" ] || [ "$answer" = "YES" ]; then
|
||||
if [ -d "$PC_DIR" ]; then
|
||||
log_info "清空现有目录内容..."
|
||||
find "$PC_DIR" -mindepth 1 -delete 2>/dev/null
|
||||
fi
|
||||
|
||||
log_info "正在从 Gitee 克隆PC商城(ECShopX_web-frontend)..."
|
||||
git clone "$PC_REPO" "$PC_DIR" > /tmp/git_clone_pc.log 2>&1 &
|
||||
local clone_pid=$!
|
||||
|
||||
# 显示进度动画
|
||||
local spinstr='|/-\'
|
||||
while kill -0 $clone_pid 2>/dev/null; do
|
||||
local temp=${spinstr#?}
|
||||
printf "\r${CYAN}[INFO]${NC} 克隆PC商城(ECShopX_web-frontend)中 ${spinstr:0:1}"
|
||||
spinstr=$temp${spinstr%"$temp"}
|
||||
sleep 0.2
|
||||
done
|
||||
wait $clone_pid
|
||||
local clone_exit=$?
|
||||
|
||||
if [ $clone_exit -eq 0 ]; then
|
||||
printf "\r${GREEN}[SUCCESS]${NC}PC商城(ECShopX_web-frontend)克隆成功"
|
||||
printf "%50s" ""
|
||||
echo ""
|
||||
INSTALLED_PC=true
|
||||
else
|
||||
printf "\r${RED}[ERROR]${NC}PC商城(ECShopX_web-frontend)克隆失败"
|
||||
printf "%50s" ""
|
||||
echo ""
|
||||
cat /tmp/git_clone_pc.log 2>/dev/null | tail -10
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_warning "跳过PC商城(ECShopX_web-frontend)克隆"
|
||||
fi
|
||||
else
|
||||
log_info "ECShopX_web-frontend 目录已存在且包含 package.json,跳过克隆"
|
||||
INSTALLED_PC=true
|
||||
fi
|
||||
|
||||
# 检查 ECShopX_admin-frontend
|
||||
ADMIN_DIR="$PARENT_DIR/ECShopX_admin-frontend"
|
||||
ADMIN_REPO="https://gitee.com/ShopeX/ECShopX_admin-frontend.git"
|
||||
@@ -460,62 +529,6 @@ check_and_clone_frontend() {
|
||||
INSTALLED_VSHOP=true
|
||||
fi
|
||||
|
||||
# 检查 ECShopX_desktop-frontend
|
||||
PC_DIR="$PARENT_DIR/ECShopX_desktop-frontend"
|
||||
PC_REPO="https://gitee.com/ShopeX/ECShopX_desktop-frontend.git"
|
||||
|
||||
if [ ! -d "$PC_DIR" ] || [ ! -f "$PC_DIR/package.json" ]; then
|
||||
if [ ! -d "$PC_DIR" ]; then
|
||||
log_warning "ECShopX_desktop-frontend 目录不存在"
|
||||
else
|
||||
log_warning "ECShopX_desktop-frontend 目录存在但缺少 package.json"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -n "是否从 Gitee 克隆PC商城(ECShopX_desktop-frontend)代码? [Y/n]: "
|
||||
read -r answer < /dev/tty
|
||||
|
||||
if [ -z "$answer" ] || [ "$answer" = "Y" ] || [ "$answer" = "y" ] || [ "$answer" = "yes" ] || [ "$answer" = "YES" ]; then
|
||||
if [ -d "$PC_DIR" ]; then
|
||||
log_info "清空现有目录内容..."
|
||||
find "$PC_DIR" -mindepth 1 -delete 2>/dev/null
|
||||
fi
|
||||
|
||||
log_info "正在从 Gitee 克隆PC商城(ECShopX_desktop-frontend)..."
|
||||
git clone "$PC_REPO" "$PC_DIR" > /tmp/git_clone_pc.log 2>&1 &
|
||||
local clone_pid=$!
|
||||
|
||||
# 显示进度动画
|
||||
local spinstr='|/-\'
|
||||
while kill -0 $clone_pid 2>/dev/null; do
|
||||
local temp=${spinstr#?}
|
||||
printf "\r${CYAN}[INFO]${NC} 克隆PC商城(ECShopX_desktop-frontend)中 ${spinstr:0:1}"
|
||||
spinstr=$temp${spinstr%"$temp"}
|
||||
sleep 0.2
|
||||
done
|
||||
wait $clone_pid
|
||||
local clone_exit=$?
|
||||
|
||||
if [ $clone_exit -eq 0 ]; then
|
||||
printf "\r${GREEN}[SUCCESS]${NC}PC商城(ECShopX_desktop-frontend)克隆成功"
|
||||
printf "%50s" ""
|
||||
echo ""
|
||||
INSTALLED_PC=true
|
||||
else
|
||||
printf "\r${RED}[ERROR]${NC}PC商城(ECShopX_desktop-frontend)克隆失败"
|
||||
printf "%50s" ""
|
||||
echo ""
|
||||
cat /tmp/git_clone_pc.log 2>/dev/null | tail -10
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_warning "跳过PC商城(ECShopX_desktop-frontend)克隆"
|
||||
fi
|
||||
else
|
||||
log_info "ECShopX_desktop-frontend 目录已存在且包含 package.json,跳过克隆"
|
||||
INSTALLED_PC=true
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
@@ -544,12 +557,16 @@ check_container_status() {
|
||||
;;
|
||||
2)
|
||||
log_info "使用现有容器..."
|
||||
log_info "确保 Docker Compose 中的所有服务已启动..."
|
||||
$DOCKER_COMPOSE_CMD -f "$DOCKER_COMPOSE_FILE" up -d
|
||||
# 如果容器未运行,先启动它
|
||||
if ! is_container_running; then
|
||||
log_info "启动现有容器..."
|
||||
$DOCKER_COMPOSE_CMD -f "$DOCKER_COMPOSE_FILE" up -d
|
||||
sleep 5
|
||||
wait_for_services
|
||||
elif ! is_web_container_running; then
|
||||
log_info "等待 Web 前端容器启动..."
|
||||
sleep 5
|
||||
fi
|
||||
return 1 # 跳过构建
|
||||
;;
|
||||
@@ -559,6 +576,8 @@ check_container_status() {
|
||||
;;
|
||||
*)
|
||||
log_info "使用现有容器..."
|
||||
log_info "确保 Docker Compose 中的所有服务已启动..."
|
||||
$DOCKER_COMPOSE_CMD -f "$DOCKER_COMPOSE_FILE" up -d
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
@@ -1048,54 +1067,50 @@ check_container_directory() {
|
||||
local project_name=$3
|
||||
local max_retries=2
|
||||
local retry_count=0
|
||||
local target_container="$CONTAINER_NAME"
|
||||
|
||||
if [ "$project_name" = "ECShopX_web-frontend" ]; then
|
||||
target_container="$WEB_CONTAINER_NAME"
|
||||
fi
|
||||
|
||||
# 首先检查容器是否运行
|
||||
if ! is_container_running; then
|
||||
log_warning "容器未运行,无法检查目录挂载状态"
|
||||
if ! docker ps --format '{{.Names}}' | grep -q "^${target_container}$" 2>/dev/null; then
|
||||
log_warning "容器 $target_container 未运行,无法检查目录挂载状态"
|
||||
log_info "目录挂载将在容器启动后自动生效"
|
||||
# 对于 ECShopX_desktop-frontend,如果容器未运行,也返回成功(允许继续)
|
||||
if [ "$project_name" = "ECShopX_desktop-frontend" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 0 # 容器未运行时,假设挂载会在启动后生效
|
||||
fi
|
||||
|
||||
while [ $retry_count -lt $max_retries ]; do
|
||||
# 检查目录是否存在
|
||||
if docker exec "$CONTAINER_NAME" sh -c "test -d $container_path" 2>/dev/null; then
|
||||
if docker exec "$target_container" sh -c "test -d $container_path" 2>/dev/null; then
|
||||
# 检查 package.json 是否存在
|
||||
if docker exec "$CONTAINER_NAME" sh -c "test -f $container_path/package.json" 2>/dev/null; then
|
||||
log_success "目录挂载检查通过: $container_path"
|
||||
if docker exec "$target_container" sh -c "test -f $container_path/package.json" 2>/dev/null; then
|
||||
log_success "目录挂载检查通过: $target_container:$container_path"
|
||||
return 0 # 目录和文件都存在
|
||||
else
|
||||
if [ $retry_count -eq 0 ]; then
|
||||
log_warning "容器内 $container_path/package.json 不存在,尝试重启容器以确保目录正确挂载..."
|
||||
log_warning "容器 $target_container 内 $container_path/package.json 不存在,尝试重启容器以确保目录正确挂载..."
|
||||
if ! restart_container_for_mount "$project_name"; then
|
||||
return 1
|
||||
fi
|
||||
retry_count=$((retry_count + 1))
|
||||
continue
|
||||
else
|
||||
# 对于 ECShopX_desktop-frontend,如果重启后仍然不存在,也允许继续(可能是配置问题)
|
||||
if [ "$project_name" = "ECShopX_desktop-frontend" ]; then
|
||||
log_warning "容器内 $container_path/package.json 仍然不存在,但继续执行..."
|
||||
return 0
|
||||
fi
|
||||
log_error "容器内 $container_path/package.json 仍然不存在"
|
||||
log_error "容器 $target_container 内 $container_path/package.json 仍然不存在"
|
||||
log_info "请检查主机目录 $host_path 是否存在且包含 package.json"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if [ $retry_count -eq 0 ]; then
|
||||
log_warning "容器内 $container_path 目录不存在,尝试重启容器以确保目录正确挂载..."
|
||||
log_warning "容器 $target_container 内 $container_path 目录不存在,尝试重启容器以确保目录正确挂载..."
|
||||
if ! restart_container_for_mount "$project_name"; then
|
||||
return 1
|
||||
fi
|
||||
retry_count=$((retry_count + 1))
|
||||
continue
|
||||
else
|
||||
log_error "容器内 $container_path 目录仍然不存在"
|
||||
log_error "容器 $target_container 内 $container_path 目录仍然不存在"
|
||||
log_info "请检查 docker-compose.dev.yml 中的卷挂载配置是否正确"
|
||||
log_info "主机目录路径: $host_path"
|
||||
return 1
|
||||
@@ -1112,11 +1127,17 @@ check_container_directory() {
|
||||
|
||||
restart_container_for_mount() {
|
||||
local project_name=$1
|
||||
local target_container="$CONTAINER_NAME"
|
||||
|
||||
if [ "$project_name" = "ECShopX_web-frontend" ]; then
|
||||
target_container="$WEB_CONTAINER_NAME"
|
||||
fi
|
||||
|
||||
log_warning "检测到 $project_name 目录未正确挂载,正在重启容器..."
|
||||
|
||||
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$" 2>/dev/null; then
|
||||
log_info "重启容器 $CONTAINER_NAME..."
|
||||
docker-compose -f "$DOCKER_COMPOSE_FILE" restart || {
|
||||
if docker ps --format '{{.Names}}' | grep -q "^${target_container}$" 2>/dev/null; then
|
||||
log_info "重启容器 $target_container..."
|
||||
$DOCKER_COMPOSE_CMD -f "$DOCKER_COMPOSE_FILE" restart "$target_container" || {
|
||||
log_error "容器重启失败"
|
||||
return 1
|
||||
}
|
||||
@@ -1124,8 +1145,10 @@ restart_container_for_mount() {
|
||||
log_info "等待容器启动..."
|
||||
sleep 5
|
||||
|
||||
# 等待服务就绪
|
||||
wait_for_services
|
||||
if [ "$target_container" = "$CONTAINER_NAME" ]; then
|
||||
# 等待主服务就绪
|
||||
wait_for_services
|
||||
fi
|
||||
|
||||
log_success "容器重启完成"
|
||||
return 0
|
||||
@@ -1145,6 +1168,7 @@ configure_frontend_env() {
|
||||
local api_base_url=${3:-"http://localhost:8080/api/"}
|
||||
local app_id=${4:-""}
|
||||
local default_lang=${5:-""}
|
||||
local target_container="$CONTAINER_NAME"
|
||||
|
||||
# 检查主机目录是否存在
|
||||
if [ ! -d "$project_dir" ]; then
|
||||
@@ -1158,16 +1182,17 @@ configure_frontend_env() {
|
||||
container_path="/data/httpd/ECShopX_admin-frontend"
|
||||
elif [ "$project_name" = "ECShopX_mobile-frontend" ]; then
|
||||
container_path="/data/httpd/ECShopX_mobile-frontend"
|
||||
elif [ "$project_name" = "ECShopX_desktop-frontend" ]; then
|
||||
container_path="/data/httpd/ECShopX_desktop-frontend"
|
||||
elif [ "$project_name" = "ECShopX_web-frontend" ]; then
|
||||
container_path="/data/httpd/ECShopX_web-frontend"
|
||||
target_container="$WEB_CONTAINER_NAME"
|
||||
else
|
||||
log_warning "未知的项目名称: $project_name"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 如果容器未运行,直接配置主机目录
|
||||
if ! is_container_running; then
|
||||
log_info "容器未运行,配置主机目录的 .env 文件..."
|
||||
if ! docker ps --format '{{.Names}}' | grep -q "^${target_container}$" 2>/dev/null; then
|
||||
log_info "容器 $target_container 未运行,配置主机目录的 .env 文件..."
|
||||
local env_file="$project_dir/.env"
|
||||
|
||||
# 如果 .env 不存在,尝试从 .env.example 复制
|
||||
@@ -1223,6 +1248,21 @@ configure_frontend_env() {
|
||||
echo "VUE_APP_QIANKUN_ENTRY=http://localhost:8080/newpc/" >> "$env_file"
|
||||
fi
|
||||
log_success "已配置 VUE_APP_QIANKUN_ENTRY=http://localhost:8080/newpc/"
|
||||
|
||||
# 配置 VUE_APP_WEBSITE(PC前端访问地址)
|
||||
if [ "$INSTALLED_PC" = true ]; then
|
||||
if grep -q "^VUE_APP_WEBSITE=" "$env_file" 2>/dev/null; then
|
||||
# macOS 和 Linux 兼容的 sed 命令
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "s|^VUE_APP_WEBSITE=.*|VUE_APP_WEBSITE=http://localhost:8082|" "$env_file"
|
||||
else
|
||||
sed -i "s|^VUE_APP_WEBSITE=.*|VUE_APP_WEBSITE=http://localhost:8082|" "$env_file"
|
||||
fi
|
||||
else
|
||||
echo "VUE_APP_WEBSITE=http://localhost:8082" >> "$env_file"
|
||||
fi
|
||||
log_success "已配置 VUE_APP_WEBSITE=http://localhost:8082"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 配置 ECShopX_mobile-frontend
|
||||
@@ -1271,59 +1311,59 @@ configure_frontend_env() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# 配置 ECShopX_desktop-frontend
|
||||
if [ "$project_name" = "ECShopX_desktop-frontend" ]; then
|
||||
# 使用 sed 修改或添加 VUE_APP_API_BASE_URL
|
||||
if grep -q "^VUE_APP_API_BASE_URL=" "$env_file" 2>/dev/null; then
|
||||
# 配置 ECShopX_web-frontend
|
||||
if [ "$project_name" = "ECShopX_web-frontend" ]; then
|
||||
# 使用 sed 修改或添加 NUXT_PUBLIC_API_BASE
|
||||
if grep -q "^NUXT_PUBLIC_API_BASE=" "$env_file" 2>/dev/null; then
|
||||
# macOS 和 Linux 兼容的 sed 命令
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "s|^VUE_APP_API_BASE_URL=.*|VUE_APP_API_BASE_URL=$api_base_url|" "$env_file"
|
||||
sed -i '' "s|^NUXT_PUBLIC_API_BASE=.*|NUXT_PUBLIC_API_BASE=$api_base_url/api/h5app|" "$env_file"
|
||||
else
|
||||
sed -i "s|^VUE_APP_API_BASE_URL=.*|VUE_APP_API_BASE_URL=$api_base_url|" "$env_file"
|
||||
sed -i "s|^NUXT_PUBLIC_API_BASE=.*|NUXT_PUBLIC_API_BASE=$api_base_url/api/h5app|" "$env_file"
|
||||
fi
|
||||
else
|
||||
echo "VUE_APP_API_BASE_URL=$api_base_url" >> "$env_file"
|
||||
echo "NUXT_PUBLIC_API_BASE=$api_base_url/api/h5app" >> "$env_file"
|
||||
fi
|
||||
log_success "已配置 VUE_APP_API_BASE_URL=$api_base_url"
|
||||
log_success "已配置 NUXT_PUBLIC_API_BASE=$api_base_url/api/h5app"
|
||||
|
||||
# 配置 VUE_APP_COMPANYID(默认值为1)
|
||||
if grep -q "^VUE_APP_COMPANYID=" "$env_file" 2>/dev/null; then
|
||||
# 配置 NUXT_PUBLIC_COMPANY_ID(默认值为1)
|
||||
if grep -q "^NUXT_PUBLIC_COMPANY_ID=" "$env_file" 2>/dev/null; then
|
||||
# macOS 和 Linux 兼容的 sed 命令
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "s|^VUE_APP_COMPANYID=.*|VUE_APP_COMPANYID=1|" "$env_file"
|
||||
sed -i '' "s|^NUXT_PUBLIC_COMPANY_ID=.*|NUXT_PUBLIC_COMPANY_ID=1|" "$env_file"
|
||||
else
|
||||
sed -i "s|^VUE_APP_COMPANYID=.*|VUE_APP_COMPANYID=1|" "$env_file"
|
||||
sed -i "s|^NUXT_PUBLIC_COMPANY_ID=.*|NUXT_PUBLIC_COMPANY_ID=1|" "$env_file"
|
||||
fi
|
||||
else
|
||||
echo "VUE_APP_COMPANYID=1" >> "$env_file"
|
||||
echo "NUXT_PUBLIC_COMPANY_ID=1" >> "$env_file"
|
||||
fi
|
||||
log_success "已配置 VUE_APP_COMPANYID=1"
|
||||
log_success "已配置 NUXT_PUBLIC_COMPANY_ID=1"
|
||||
|
||||
# 配置 VUE_APP_DEFAULT_LANG(如果提供)
|
||||
# 配置 NUXT_PUBLIC_DEFAULT_COUNTRY_CODE(如果提供)
|
||||
if [ -n "$default_lang" ]; then
|
||||
if grep -q "^VUE_APP_DEFAULT_LANG=" "$env_file" 2>/dev/null; then
|
||||
if grep -q "^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=" "$env_file" 2>/dev/null; then
|
||||
# macOS 和 Linux 兼容的 sed 命令
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "s|^VUE_APP_DEFAULT_LANG=.*|VUE_APP_DEFAULT_LANG=$default_lang|" "$env_file"
|
||||
sed -i '' "s|^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=.*|NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang|" "$env_file"
|
||||
else
|
||||
sed -i "s|^VUE_APP_DEFAULT_LANG=.*|VUE_APP_DEFAULT_LANG=$default_lang|" "$env_file"
|
||||
sed -i "s|^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=.*|NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang|" "$env_file"
|
||||
fi
|
||||
else
|
||||
echo "VUE_APP_DEFAULT_LANG=$default_lang" >> "$env_file"
|
||||
echo "NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang" >> "$env_file"
|
||||
fi
|
||||
log_success "已配置 VUE_APP_DEFAULT_LANG=$default_lang"
|
||||
log_success "已配置 NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# 容器运行中,配置容器内的 .env 文件
|
||||
# 检查容器内目录是否存在
|
||||
if ! docker exec "$CONTAINER_NAME" sh -c "test -d $container_path" 2>/dev/null; then
|
||||
log_warning "容器内 $container_path 目录不存在,跳过配置"
|
||||
if ! docker exec "$target_container" sh -c "test -d $container_path" 2>/dev/null; then
|
||||
log_warning "容器 $target_container 内 $container_path 目录不存在,跳过配置"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 在容器内配置 .env 文件
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if [ ! -f .env ]; then
|
||||
if [ -f .env.example ]; then
|
||||
@@ -1336,7 +1376,7 @@ configure_frontend_env() {
|
||||
|
||||
# 配置 ECShopX_admin-frontend
|
||||
if [ "$project_name" = "ECShopX_admin-frontend" ]; then
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^VUE_APP_BASE_API=' .env 2>/dev/null; then
|
||||
sed -i 's|^VUE_APP_BASE_API=.*|VUE_APP_BASE_API=$api_base_url|' .env
|
||||
@@ -1348,7 +1388,7 @@ configure_frontend_env() {
|
||||
|
||||
# 配置 VUE_APP_DEFAULT_LANG(如果提供)
|
||||
if [ -n "$default_lang" ]; then
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^VUE_APP_DEFAULT_LANG=' .env 2>/dev/null; then
|
||||
sed -i 's|^VUE_APP_DEFAULT_LANG=.*|VUE_APP_DEFAULT_LANG=$default_lang|' .env
|
||||
@@ -1360,7 +1400,7 @@ configure_frontend_env() {
|
||||
fi
|
||||
|
||||
# 配置 VUE_APP_QIANKUN_ENTRY
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^VUE_APP_QIANKUN_ENTRY=' .env 2>/dev/null; then
|
||||
sed -i 's|^VUE_APP_QIANKUN_ENTRY=.*|VUE_APP_QIANKUN_ENTRY=http://localhost:8080/newpc/|' .env
|
||||
@@ -1369,11 +1409,24 @@ configure_frontend_env() {
|
||||
fi
|
||||
" 2>/dev/null || true
|
||||
log_success "已配置容器内 VUE_APP_QIANKUN_ENTRY=http://localhost:8080/newpc/"
|
||||
|
||||
# 配置 VUE_APP_WEBSITE(PC前端访问地址)
|
||||
if [ "$INSTALLED_PC" = true ]; then
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^VUE_APP_WEBSITE=' .env 2>/dev/null; then
|
||||
sed -i 's|^VUE_APP_WEBSITE=.*|VUE_APP_WEBSITE=http://localhost:8082|' .env
|
||||
else
|
||||
echo 'VUE_APP_WEBSITE=http://localhost:8082' >> .env
|
||||
fi
|
||||
" 2>/dev/null || true
|
||||
log_success "已配置容器内 VUE_APP_WEBSITE=http://localhost:8082"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 配置 ECShopX_mobile-frontend
|
||||
if [ "$project_name" = "ECShopX_mobile-frontend" ]; then
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^APP_BASE_URL=' .env 2>/dev/null; then
|
||||
sed -i 's|^APP_BASE_URL=.*|APP_BASE_URL=$api_base_url|' .env
|
||||
@@ -1385,7 +1438,7 @@ configure_frontend_env() {
|
||||
|
||||
# 配置 APP_PLATFORM(如果提供)
|
||||
if [ -n "$app_id" ]; then
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^APP_PLATFORM=' .env 2>/dev/null; then
|
||||
sed -i 's|^APP_PLATFORM=.*|APP_PLATFORM=$app_id|' .env
|
||||
@@ -1398,7 +1451,7 @@ configure_frontend_env() {
|
||||
|
||||
# 配置 APP_I18N_ORIGIN_LANG(如果提供)
|
||||
if [ -n "$default_lang" ]; then
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^APP_I18N_ORIGIN_LANG=' .env 2>/dev/null; then
|
||||
sed -i 's|^APP_I18N_ORIGIN_LANG=.*|APP_I18N_ORIGIN_LANG=$default_lang|' .env
|
||||
@@ -1410,40 +1463,40 @@ configure_frontend_env() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# 配置 ECShopX_desktop-frontend
|
||||
if [ "$project_name" = "ECShopX_desktop-frontend" ]; then
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
# 配置 ECShopX_web-frontend
|
||||
if [ "$project_name" = "ECShopX_web-frontend" ]; then
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^VUE_APP_API_BASE_URL=' .env 2>/dev/null; then
|
||||
sed -i 's|^VUE_APP_API_BASE_URL=.*|VUE_APP_API_BASE_URL=$api_base_url|' .env
|
||||
if grep -q '^NUXT_PUBLIC_API_BASE=' .env 2>/dev/null; then
|
||||
sed -i 's|^NUXT_PUBLIC_API_BASE=.*|NUXT_PUBLIC_API_BASE=$api_base_url/api/h5app|' .env
|
||||
else
|
||||
echo 'VUE_APP_API_BASE_URL=$api_base_url' >> .env
|
||||
echo 'NUXT_PUBLIC_API_BASE=$api_base_url/api/h5app' >> .env
|
||||
fi
|
||||
" 2>/dev/null || true
|
||||
log_success "已配置容器内 VUE_APP_API_BASE_URL=$api_base_url"
|
||||
log_success "已配置容器内 NUXT_PUBLIC_API_BASE=$api_base_url/api/h5app"
|
||||
|
||||
# 配置 VUE_APP_COMPANYID(默认值为1)
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
# 配置 NUXT_PUBLIC_COMPANY_ID(默认值为1)
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^VUE_APP_COMPANYID=' .env 2>/dev/null; then
|
||||
sed -i 's|^VUE_APP_COMPANYID=.*|VUE_APP_COMPANYID=1|' .env
|
||||
if grep -q '^NUXT_PUBLIC_COMPANY_ID=' .env 2>/dev/null; then
|
||||
sed -i 's|^NUXT_PUBLIC_COMPANY_ID=.*|NUXT_PUBLIC_COMPANY_ID=1|' .env
|
||||
else
|
||||
echo 'VUE_APP_COMPANYID=1' >> .env
|
||||
echo 'NUXT_PUBLIC_COMPANY_ID=1' >> .env
|
||||
fi
|
||||
" 2>/dev/null || true
|
||||
log_success "已配置容器内 VUE_APP_COMPANYID=1"
|
||||
log_success "已配置容器内 NUXT_PUBLIC_COMPANY_ID=1"
|
||||
|
||||
# 配置 VUE_APP_DEFAULT_LANG(如果提供)
|
||||
# 配置 NUXT_PUBLIC_DEFAULT_COUNTRY_CODE(如果提供)
|
||||
if [ -n "$default_lang" ]; then
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^VUE_APP_DEFAULT_LANG=' .env 2>/dev/null; then
|
||||
sed -i 's|^VUE_APP_DEFAULT_LANG=.*|VUE_APP_DEFAULT_LANG=$default_lang|' .env
|
||||
if grep -q '^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=' .env 2>/dev/null; then
|
||||
sed -i 's|^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=.*|NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang|' .env
|
||||
else
|
||||
echo 'VUE_APP_DEFAULT_LANG=$default_lang' >> .env
|
||||
echo 'NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang' >> .env
|
||||
fi
|
||||
" 2>/dev/null || true
|
||||
log_success "已配置容器内 VUE_APP_DEFAULT_LANG=$default_lang"
|
||||
log_success "已配置容器内 NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -1610,37 +1663,40 @@ build_admin() {
|
||||
}
|
||||
|
||||
# ===========================================
|
||||
# 编译 ECShopX_desktop-frontend
|
||||
# 编译 ECShopX_web-frontend
|
||||
# ===========================================
|
||||
|
||||
build_pc() {
|
||||
if [ "$SKIP_PC" = true ]; then
|
||||
log_info "跳过 ECShopX_desktop-frontend 编译(--skip-pc)"
|
||||
log_info "跳过 ECShopX_web-frontend 编译(--skip-pc)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
PC_DIR="$PARENT_DIR/ECShopX_desktop-frontend"
|
||||
PC_DIR="$PARENT_DIR/ECShopX_web-frontend"
|
||||
|
||||
if [ ! -d "$PC_DIR" ]; then
|
||||
log_warning "ECShopX_desktop-frontend 目录不存在,跳过编译"
|
||||
log_warning "ECShopX_web-frontend 目录不存在,跳过编译"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "=========================================="
|
||||
log_info "开始编译 ECShopX_desktop-frontend(PC前端)..."
|
||||
log_info "开始编译 ECShopX_web-frontend(PC前端)..."
|
||||
log_info "=========================================="
|
||||
|
||||
if [ ! -f "$PC_DIR/package.json" ]; then
|
||||
log_error "ECShopX_desktop-frontend/package.json 不存在"
|
||||
log_error "ECShopX_web-frontend/package.json 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查是否已有编译产物(Nuxt项目编译后生成.nuxt或.output目录)
|
||||
if docker exec "$CONTAINER_NAME" sh -c "[ -d /data/httpd/ECShopX_desktop-frontend/.nuxt ]" 2>/dev/null || \
|
||||
docker exec "$CONTAINER_NAME" sh -c "[ -d /data/httpd/ECShopX_desktop-frontend/.output ]" 2>/dev/null || \
|
||||
[ -d "$PC_DIR/.nuxt" ] || [ -d "$PC_DIR/.output" ]; then
|
||||
log_info "检测到已有编译产物,跳过编译"
|
||||
log_info "如需重新编译,请删除 ECShopX_desktop-frontend/.nuxt 或 .output 目录"
|
||||
if ! ensure_web_container_running; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查是否已有可启动的 Nuxt 生产产物(.nuxt 可能由 pnpm install/postinstall 生成,不能代表已编译完成)
|
||||
if docker exec "$WEB_CONTAINER_NAME" sh -c "[ -f /data/httpd/ECShopX_web-frontend/.output/server/index.mjs ]" 2>/dev/null || \
|
||||
[ -f "$PC_DIR/.output/server/index.mjs" ]; then
|
||||
log_info "检测到已有 Nuxt 生产编译产物,跳过编译"
|
||||
log_info "如需重新编译,请删除 ECShopX_web-frontend/.output 目录"
|
||||
# 即使跳过编译,也需要启动Nuxt服务
|
||||
need_build=false
|
||||
else
|
||||
@@ -1649,7 +1705,7 @@ build_pc() {
|
||||
|
||||
# 检查容器内目录是否存在并确保正确挂载
|
||||
log_info "检查容器内目录挂载状态..."
|
||||
if ! check_container_directory "/data/httpd/ECShopX_desktop-frontend" "$PC_DIR" "ECShopX_desktop-frontend"; then
|
||||
if ! check_container_directory "/data/httpd/ECShopX_web-frontend" "$PC_DIR" "ECShopX_web-frontend"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -1659,21 +1715,14 @@ build_pc() {
|
||||
log_info "启动 Nuxt 服务(监听3000端口)..."
|
||||
|
||||
# 检查是否已有Nuxt进程在运行
|
||||
if docker exec "$CONTAINER_NAME" sh -c "pgrep -f 'nuxt.*3000\|node.*nuxt' > /dev/null" 2>/dev/null; then
|
||||
if docker exec "$WEB_CONTAINER_NAME" sh -c "pgrep -f 'nuxt.*3000|pnpm.*preview|pnpm.*dev|node .*\\.output/server/index\\.mjs' > /dev/null" 2>/dev/null; then
|
||||
log_info "Nuxt 服务已在运行"
|
||||
else
|
||||
# 启动Nuxt服务
|
||||
if docker exec "$CONTAINER_NAME" sh -c "cd /data/httpd/ECShopX_desktop-frontend && npm run | grep -q 'start'" 2>/dev/null; then
|
||||
docker exec -d "$CONTAINER_NAME" sh -c "
|
||||
cd /data/httpd/ECShopX_desktop-frontend && \
|
||||
PORT=3000 HOST=0.0.0.0 nohup npm run start > /var/log/nuxt.log 2>&1 &
|
||||
" 2>/dev/null || true
|
||||
else
|
||||
docker exec -d "$CONTAINER_NAME" sh -c "
|
||||
cd /data/httpd/ECShopX_desktop-frontend && \
|
||||
PORT=3000 HOST=0.0.0.0 nohup npm run dev > /var/log/nuxt.log 2>&1 &
|
||||
" 2>/dev/null || true
|
||||
fi
|
||||
# 启动已编译的 Nuxt 服务
|
||||
docker exec -d "$WEB_CONTAINER_NAME" sh -c "
|
||||
cd /data/httpd/ECShopX_web-frontend && \
|
||||
NITRO_HOST=0.0.0.0 NITRO_PORT=3000 HOST=0.0.0.0 PORT=3000 nohup node .output/server/index.mjs > /var/log/nuxt.log 2>&1 &
|
||||
" 2>/dev/null || true
|
||||
|
||||
# 等待Nuxt服务启动
|
||||
sleep 3
|
||||
@@ -1714,18 +1763,19 @@ build_pc() {
|
||||
fi
|
||||
|
||||
# 配置前端项目的 .env 文件
|
||||
log_info "配置 ECShopX_desktop-frontend 的 .env 文件..."
|
||||
configure_frontend_env "$PC_DIR" "ECShopX_desktop-frontend" "http://localhost:8080" "" "$SELECTED_LANG"
|
||||
log_info "配置 ECShopX_web-frontend 的 .env 文件..."
|
||||
configure_frontend_env "$PC_DIR" "ECShopX_web-frontend" "http://localhost:8080" "" "$SELECTED_LANG"
|
||||
|
||||
log_info "安装 npm 依赖..."
|
||||
log_info "启用 pnpm 并安装依赖..."
|
||||
# 使用绝对路径并先验证目录存在
|
||||
docker exec "$CONTAINER_NAME" sh -c "
|
||||
if [ ! -d /data/httpd/ECShopX_desktop-frontend ]; then
|
||||
docker exec "$WEB_CONTAINER_NAME" sh -c "
|
||||
if [ ! -d /data/httpd/ECShopX_web-frontend ]; then
|
||||
echo '错误: 目录不存在'
|
||||
exit 1
|
||||
fi
|
||||
cd /data/httpd/ECShopX_desktop-frontend || exit 1
|
||||
npm install --legacy-peer-deps
|
||||
cd /data/httpd/ECShopX_web-frontend || exit 1
|
||||
corepack enable
|
||||
PNPM_HOME=/tmp/pnpm pnpm install --store-dir /tmp/pnpm-store
|
||||
" > /tmp/npm_pc_output.log 2>&1 &
|
||||
local npm_pid=$!
|
||||
|
||||
@@ -1733,7 +1783,7 @@ build_pc() {
|
||||
local spinstr='|/-\'
|
||||
while kill -0 $npm_pid 2>/dev/null; do
|
||||
local temp=${spinstr#?}
|
||||
printf "\r${CYAN}[INFO]${NC} 安装 ECShopX_desktop-frontend npm 依赖中 ${spinstr:0:1}"
|
||||
printf "\r${CYAN}[INFO]${NC} 安装 ECShopX_web-frontend pnpm 依赖中 ${spinstr:0:1}"
|
||||
spinstr=$temp${spinstr%"$temp"}
|
||||
sleep 0.2
|
||||
done
|
||||
@@ -1741,27 +1791,27 @@ build_pc() {
|
||||
local npm_exit=$?
|
||||
|
||||
if [ $npm_exit -ne 0 ]; then
|
||||
printf "\r${RED}[ERROR]${NC} ECShopX_desktop-frontend npm install 失败"
|
||||
printf "\r${RED}[ERROR]${NC} ECShopX_web-frontend pnpm install 失败"
|
||||
printf "%50s" ""
|
||||
echo ""
|
||||
cat /tmp/npm_pc_output.log 2>/dev/null | tail -20
|
||||
log_info "请检查容器内目录状态: docker exec $CONTAINER_NAME ls -la /data/httpd/ECShopX_desktop-frontend"
|
||||
log_info "请检查容器内目录状态: docker exec $WEB_CONTAINER_NAME ls -la /data/httpd/ECShopX_web-frontend"
|
||||
return 1
|
||||
else
|
||||
printf "\r${GREEN}[SUCCESS]${NC} ECShopX_desktop-frontend npm 依赖安装完成"
|
||||
printf "\r${GREEN}[SUCCESS]${NC} ECShopX_web-frontend pnpm 依赖安装完成"
|
||||
printf "%50s" ""
|
||||
echo ""
|
||||
fi
|
||||
|
||||
log_info "执行编译(npm run build)..."
|
||||
docker exec "$CONTAINER_NAME" sh -c "cd /data/httpd/ECShopX_desktop-frontend && npm run build" > /tmp/build_pc_output.log 2>&1 &
|
||||
log_info "执行编译(pnpm build)..."
|
||||
docker exec "$WEB_CONTAINER_NAME" sh -c "cd /data/httpd/ECShopX_web-frontend && PNPM_HOME=/tmp/pnpm pnpm build" > /tmp/build_pc_output.log 2>&1 &
|
||||
local build_pid=$!
|
||||
|
||||
# 显示进度动画
|
||||
local spinstr='|/-\'
|
||||
while kill -0 $build_pid 2>/dev/null; do
|
||||
local temp=${spinstr#?}
|
||||
printf "\r${CYAN}[INFO]${NC} 编译 ECShopX_desktop-frontend 中 ${spinstr:0:1}"
|
||||
printf "\r${CYAN}[INFO]${NC} 编译 ECShopX_web-frontend 中 ${spinstr:0:1}"
|
||||
spinstr=$temp${spinstr%"$temp"}
|
||||
sleep 0.2
|
||||
done
|
||||
@@ -1769,67 +1819,53 @@ build_pc() {
|
||||
local build_exit=$?
|
||||
|
||||
if [ $build_exit -ne 0 ]; then
|
||||
printf "\r${RED}[ERROR]${NC} ECShopX_desktop-frontend 编译失败"
|
||||
printf "\r${RED}[ERROR]${NC} ECShopX_web-frontend 编译失败"
|
||||
printf "%50s" ""
|
||||
echo ""
|
||||
cat /tmp/build_pc_output.log 2>/dev/null | tail -20
|
||||
return 1
|
||||
else
|
||||
printf "\r${GREEN}[SUCCESS]${NC} ECShopX_desktop-frontend 编译完成"
|
||||
printf "\r${GREEN}[SUCCESS]${NC} ECShopX_web-frontend 编译完成"
|
||||
printf "%50s" ""
|
||||
echo ""
|
||||
fi
|
||||
# 验证编译产物
|
||||
if ! docker exec "$CONTAINER_NAME" sh -c "[ -d /data/httpd/ECShopX_desktop-frontend/.nuxt ] || [ -d /data/httpd/ECShopX_desktop-frontend/.output ]" 2>/dev/null; then
|
||||
log_error "ECShopX_desktop-frontend 编译输出不完整"
|
||||
if ! docker exec "$WEB_CONTAINER_NAME" sh -c "[ -f /data/httpd/ECShopX_web-frontend/.output/server/index.mjs ]" 2>/dev/null; then
|
||||
log_error "ECShopX_web-frontend 编译输出不完整"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 检查编译产物(Nuxt项目编译后生成.nuxt或.output目录)
|
||||
if docker exec "$CONTAINER_NAME" sh -c "[ -d /data/httpd/ECShopX_desktop-frontend/.nuxt ] || [ -d /data/httpd/ECShopX_desktop-frontend/.output ]" 2>/dev/null; then
|
||||
log_success "ECShopX_desktop-frontend 编译成功"
|
||||
# 检查可启动的 Nuxt 生产编译产物
|
||||
if docker exec "$WEB_CONTAINER_NAME" sh -c "[ -f /data/httpd/ECShopX_web-frontend/.output/server/index.mjs ]" 2>/dev/null; then
|
||||
log_success "ECShopX_web-frontend 编译成功"
|
||||
|
||||
# 启动 Nuxt 服务
|
||||
log_info "启动 Nuxt 服务(监听3000端口)..."
|
||||
|
||||
# 检查是否已有Nuxt进程在运行
|
||||
if docker exec "$CONTAINER_NAME" sh -c "pgrep -f 'nuxt.*3000\|node.*nuxt' > /dev/null" 2>/dev/null; then
|
||||
if docker exec "$WEB_CONTAINER_NAME" sh -c "pgrep -f 'nuxt.*3000|pnpm.*preview|pnpm.*dev|node .*\\.output/server/index\\.mjs' > /dev/null" 2>/dev/null; then
|
||||
log_info "Nuxt 服务已在运行,重启服务..."
|
||||
docker exec "$CONTAINER_NAME" sh -c "pkill -f 'nuxt.*3000\|node.*nuxt'" 2>/dev/null || true
|
||||
docker exec "$WEB_CONTAINER_NAME" sh -c "pkill -f 'nuxt.*3000|pnpm.*preview|pnpm.*dev|node .*\\.output/server/index\\.mjs'" 2>/dev/null || true
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# 在后台启动Nuxt服务
|
||||
# 优先使用生产模式(npm run start),如果不存在则使用开发模式(npm run dev)
|
||||
log_info "检查可用的启动命令..."
|
||||
if docker exec "$CONTAINER_NAME" sh -c "cd /data/httpd/ECShopX_desktop-frontend && npm run | grep -q 'start'" 2>/dev/null; then
|
||||
log_info "使用生产模式启动(npm run start)..."
|
||||
docker exec -d "$CONTAINER_NAME" sh -c "
|
||||
cd /data/httpd/ECShopX_desktop-frontend && \
|
||||
PORT=3000 HOST=0.0.0.0 nohup npm run start > /var/log/nuxt.log 2>&1 &
|
||||
" || {
|
||||
log_error "Nuxt 服务启动失败"
|
||||
log_info "请检查日志: docker exec $CONTAINER_NAME tail -f /var/log/nuxt.log"
|
||||
return 1
|
||||
}
|
||||
else
|
||||
log_info "使用开发模式启动(npm run dev)..."
|
||||
docker exec -d "$CONTAINER_NAME" sh -c "
|
||||
cd /data/httpd/ECShopX_desktop-frontend && \
|
||||
PORT=3000 HOST=0.0.0.0 nohup npm run dev > /var/log/nuxt.log 2>&1 &
|
||||
" || {
|
||||
log_error "Nuxt 服务启动失败"
|
||||
log_info "请检查日志: docker exec $CONTAINER_NAME tail -f /var/log/nuxt.log"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
log_info "使用生产模式启动(node .output/server/index.mjs)..."
|
||||
docker exec -d "$WEB_CONTAINER_NAME" sh -c "
|
||||
cd /data/httpd/ECShopX_web-frontend && \
|
||||
NITRO_HOST=0.0.0.0 NITRO_PORT=3000 HOST=0.0.0.0 PORT=3000 nohup node .output/server/index.mjs > /var/log/nuxt.log 2>&1 &
|
||||
" || {
|
||||
log_error "Nuxt 服务启动失败"
|
||||
log_info "请检查日志: docker exec $WEB_CONTAINER_NAME tail -f /var/log/nuxt.log"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 等待Nuxt服务启动
|
||||
log_info "等待 Nuxt 服务启动..."
|
||||
local nuxt_ready=false
|
||||
for i in {1..30}; do
|
||||
if docker exec "$CONTAINER_NAME" sh -c "curl -s http://127.0.0.1:3000 > /dev/null" 2>/dev/null; then
|
||||
if docker exec "$WEB_CONTAINER_NAME" sh -c "wget -q --spider http://127.0.0.1:3000 >/dev/null 2>&1 || wget -S -O /dev/null http://127.0.0.1:3000 2>&1 | grep -q 'HTTP/'" 2>/dev/null; then
|
||||
nuxt_ready=true
|
||||
break
|
||||
fi
|
||||
@@ -1853,7 +1889,7 @@ build_pc() {
|
||||
printf "\r${YELLOW}[WARNING]${NC} Nuxt 服务启动超时,但可能仍在启动中"
|
||||
printf "%50s" ""
|
||||
echo ""
|
||||
log_info "请检查日志: docker exec $CONTAINER_NAME tail -f /var/log/nuxt.log"
|
||||
log_info "请检查日志: docker exec $WEB_CONTAINER_NAME tail -f /var/log/nuxt.log"
|
||||
if [ "$INSTALLED_VSHOP" = true ]; then
|
||||
log_info "H5前端访问地址: http://localhost:8081"
|
||||
fi
|
||||
@@ -1868,7 +1904,7 @@ build_pc() {
|
||||
log_warning "Nginx 配置重载失败,可能需要重启容器"
|
||||
}
|
||||
else
|
||||
log_error "ECShopX_desktop-frontend 编译输出不完整"
|
||||
log_error "ECShopX_web-frontend 编译输出不完整"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@@ -2304,8 +2340,10 @@ show_success_info() {
|
||||
fi
|
||||
log_info "常用命令:"
|
||||
log_info " 查看日志: $DOCKER_COMPOSE_CMD -f $DOCKER_COMPOSE_FILE logs -f"
|
||||
log_info " 服务状态: docker exec $CONTAINER_NAME supervisorctl status"
|
||||
log_info " 进入容器: docker exec -it $CONTAINER_NAME sh"
|
||||
log_info " 主服务状态: docker exec $CONTAINER_NAME supervisorctl status"
|
||||
log_info " 进入主容器: docker exec -it $CONTAINER_NAME sh"
|
||||
log_info " 进入Web容器: docker exec -it $WEB_CONTAINER_NAME sh"
|
||||
log_info " Web前端日志: docker exec $WEB_CONTAINER_NAME tail -f /var/log/nuxt.log"
|
||||
log_info " 停止服务: $DOCKER_COMPOSE_CMD -f $DOCKER_COMPOSE_FILE down"
|
||||
log_info " 重启服务: $DOCKER_COMPOSE_CMD -f $DOCKER_COMPOSE_FILE restart"
|
||||
log_info " 重新构建: $0 --rebuild"
|
||||
@@ -2325,7 +2363,7 @@ main() {
|
||||
|
||||
echo "=========================================="
|
||||
echo " ECShopX 开发环境设置 (Docker)"
|
||||
echo " 支持: ECShopX / ECShopX_admin-frontend / ECShopX_mobile-frontend / ECShopX_desktop-frontend"
|
||||
echo " 支持: ECShopX / ECShopX_admin-frontend / ECShopX_mobile-frontend / ECShopX_web-frontend"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
@@ -2346,9 +2384,9 @@ main() {
|
||||
configure_cron_and_supervisor_queues
|
||||
|
||||
# 编译前端项目
|
||||
build_pc || log_warning "PC商城(ECShopX_web-frontend)编译未完成"
|
||||
build_admin || log_warning "管理后台(ECShopX_admin-frontend)编译未完成"
|
||||
build_vshop || log_warning "移动商城(ECShopX_mobile-frontend)编译未完成"
|
||||
build_pc || log_warning "PC商城(ECShopX_desktop-frontend)编译未完成"
|
||||
|
||||
# 导入 Demo 数据
|
||||
import_demo_data || log_warning "Demo 数据导入未完成"
|
||||
|
||||
@@ -50,6 +50,19 @@ services:
|
||||
networks:
|
||||
- ecshopx-dev-network
|
||||
|
||||
ecshopx-web-frontend:
|
||||
image: node:20.19.0-alpine
|
||||
container_name: ecshopx-web-frontend
|
||||
restart: unless-stopped
|
||||
working_dir: /data/httpd/ECShopX_web-frontend
|
||||
command: sh -c "tail -f /dev/null"
|
||||
volumes:
|
||||
- ..:/data/httpd
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
networks:
|
||||
- ecshopx-dev-network
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
name: ecshopx-dev-mysql
|
||||
|
||||
@@ -68,7 +68,7 @@ RUN mkdir -p /var/log/supervisor \
|
||||
&& mkdir -p /data/httpd/ECShopX \
|
||||
&& mkdir -p /data/httpd/ECShopX_admin-frontend \
|
||||
&& mkdir -p /data/httpd/ECShopX_mobile-frontend \
|
||||
&& mkdir -p /data/httpd/ECShopX_desktop-frontend \
|
||||
&& mkdir -p /data/httpd/ECShopX_web-frontend \
|
||||
&& (id -u redis >/dev/null 2>&1 || adduser -D -s /sbin/nologin redis) \
|
||||
&& (id -u www-data >/dev/null 2>&1 || (addgroup -g 82 www-data && adduser -D -s /sbin/nologin -u 82 -G www-data www-data)) \
|
||||
&& chown -R redis:redis /var/lib/redis /var/log/redis /var/run/redis \
|
||||
|
||||
@@ -170,9 +170,9 @@ http {
|
||||
listen 8082;
|
||||
server_name _;
|
||||
|
||||
# 代理到Nuxt服务(监听3000端口)
|
||||
# 代理到独立的 Web 前端 Nuxt 服务(监听3000端口)
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_pass http://ecshopx-web-frontend:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
|
||||
@@ -65,19 +65,6 @@ stderr_logfile=/var/log/supervisor/nginx.log
|
||||
user=root
|
||||
priority=400
|
||||
|
||||
[program:nuxt]
|
||||
command=/bin/sh -c "sleep 5; cd /data/httpd/ECShopX_desktop-frontend && if [ -d .nuxt ] || [ -d .output ]; then if npm run 2>/dev/null | grep -q 'start'; then PORT=3000 HOST=0.0.0.0 npm run start; else PORT=3000 HOST=0.0.0.0 npm run dev; fi; else exit 0; fi"
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startsecs=10
|
||||
startretries=3
|
||||
stdout_logfile=/var/log/supervisor/nuxt.log
|
||||
stderr_logfile=/var/log/supervisor/nuxt.log
|
||||
user=root
|
||||
priority=500
|
||||
directory=/data/httpd/ECShopX_desktop-frontend
|
||||
|
||||
|
||||
[program:crond]
|
||||
command=/usr/sbin/crond -f -l 8
|
||||
autostart=true
|
||||
|
||||
@@ -39,6 +39,7 @@ $api->version('v1', function($api) {
|
||||
$api->get('/member/sms/code', ['as' => 'member.sms.code', 'uses' => 'Members@getSmsCode']);
|
||||
// 获取图片验证码-已支持h5
|
||||
$api->get('/member/image/code', ['as' => 'member.image.code', 'uses' => 'Members@getImageVcode']);
|
||||
$api->get('/member/verify', ['name'=>'POS会员手机号验证码校验并查询会员信息','middleware'=>['activated'], 'as' => 'member.verify', 'uses' =>'Members@verifyMember']);
|
||||
$api->post('/member', ['name'=>'新增会员','middleware'=>['activated'], 'as' => 'member.create', 'uses' =>'Members@createMember']);
|
||||
$api->patch('/member', ['name'=>'更新会员信息','middleware'=>'activated', 'as' => 'member.update', 'uses' =>'Members@updateMemberInfo']);
|
||||
$api->put('/member', ['name'=>'更新会员手机信息','middleware'=>'activated', 'as' => 'member.upate.mobile', 'uses' =>'Members@updateMobileById']);
|
||||
|
||||
@@ -16,6 +16,8 @@ $api->version('v1', function ($api) {
|
||||
$api->group(['namespace' => 'GoodsBundle\Http\FrontApi\V1\Action', 'middleware' => 'frontnoauth:h5app', 'prefix' => 'h5app'], function ($api) {
|
||||
// 商品列表-已支持h5
|
||||
$api->get('/wxapp/goods/items', ['as' => 'goods.items.lists', 'uses' => 'Items@getItemsList']);
|
||||
// 批量获取商品基本信息(图片、ID、名称、价格)
|
||||
$api->get('/wxapp/goods/items/batch', ['as' => 'goods.items.batch', 'uses' => 'Items@getBatchItems']);
|
||||
// 获取商品筛选条件
|
||||
$api->get('/wxapp/goods/items/filter', ['as' => 'goods.items.filter', 'uses' => 'Items@getItemsFilter']);
|
||||
//获取小店商品列表
|
||||
|
||||
@@ -795,8 +795,6 @@ class Distributor extends BaseController
|
||||
|
||||
$filter = [];
|
||||
|
||||
// $filter['is_valid'] = 'true';
|
||||
|
||||
$type = $request->input('type', 0); // 过滤条件
|
||||
$noHaving = false; // 是否过滤离用户的经纬度比较远的店铺 【true 不过滤】【false 过滤】
|
||||
switch ($type) {
|
||||
@@ -927,11 +925,11 @@ class Distributor extends BaseController
|
||||
// 默认拿不是总店的店铺
|
||||
$filter['distributor_self'] = 0;
|
||||
|
||||
// $filter['is_valid'] = 'true';
|
||||
// 默认只返回启用的店铺,可通过 is_valid 参数显式筛选
|
||||
if ($request->input('is_valid')) {
|
||||
$filter['is_valid'] = $request->input('is_valid');
|
||||
} else {
|
||||
$filter['is_valid'] = ['true', 'false'];
|
||||
$filter['is_valid'] = 'true';
|
||||
}
|
||||
|
||||
if ($request->input('get_shop')) {
|
||||
|
||||
@@ -21,7 +21,7 @@ class StoreHomePage extends BaseController
|
||||
* path="/wxapp/employeepurchase/store-home-page/{id}",
|
||||
* summary="内购模版详情(含完整模板装修数据)",
|
||||
* tags={"内购"},
|
||||
* description="返回内购模版表字段、pages_template 列表完整行(pages_template_record)、以及与 pagestemplate/detail 同构的 page_template_detail(list/config/tab_bar 等)。当存在 weapp_customize_page_id 时,page_template_detail 优先从 wechat_weapp_setting 的 page_name=custom_{weapp_customize_page_id}、version=v1.0.1 读取(与后台 shopDecoration 保存一致);若无匹配行则回退为商城首页 index(v1.0.2)。resolved_pages_template_id 为装修 pages_template 主键,与自定义页 page_name 语义不同。需传 distributor_id。可选 e_activity_id 参与组件价活动上下文。",
|
||||
* description="返回内购模版表字段、pages_template 列表完整行(pages_template_record)、以及与 pagestemplate/detail 同构的 page_template_detail(list/config/tab_bar 等)。当存在 weapp_customize_page_id 时,page_template_detail 从 wechat_weapp_setting 的 page_name=custom_{weapp_customize_page_id} 读取,version 依次尝试 shop_{distributor_id} 与 v1.0.1。resolved_pages_template_id 为装修 pages_template 主键,与自定义页 page_name 语义不同。可选 distributor_id、e_activity_id。",
|
||||
* operationId="employeepurchaseStoreHomePageDetailFront",
|
||||
* @SWG\Parameter(name="Authorization", in="header", description="JWT验证token", required=true, type="string"),
|
||||
* @SWG\Parameter(name="id", in="path", description="内购模版主键 employee_purchase_store_home_page.id", required=true, type="integer"),
|
||||
|
||||
@@ -63,6 +63,66 @@ class StoreHomePageService
|
||||
return 'custom_'.$weappCustomizePageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义页装修 version 候选(按优先级)。
|
||||
* - decorate/index scene=1010:门店维度写入 shop_{distributor_id}
|
||||
* - 旧 shopDecoration page_template:固定 v1.0.1(与 distributor 无关)
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public static function customDecorationSettingVersionCandidates(int $rowDistributorId, int $authDistributorId): array
|
||||
{
|
||||
$distributorId = $rowDistributorId > 0 ? $rowDistributorId : $authDistributorId;
|
||||
$versions = [];
|
||||
if ($distributorId > 0) {
|
||||
$versions[] = 'shop_'.$distributorId;
|
||||
}
|
||||
$versions[] = self::CUSTOM_DECORATION_SETTING_VERSION;
|
||||
|
||||
return array_values(array_unique($versions));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取 wechat_weapp_setting 时尝试的 template_name(decorate/index scene=1010 曾硬编码 yykweishop)。
|
||||
*
|
||||
* @param array<string,mixed> $storeHomeRow
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public static function decorationTemplateNameCandidates(array $storeHomeRow): array
|
||||
{
|
||||
$names = [];
|
||||
$primary = (string) ($storeHomeRow['template_name'] ?? '');
|
||||
if ($primary !== '') {
|
||||
$names[] = $primary;
|
||||
}
|
||||
if ($primary !== 'yykweishop') {
|
||||
$names[] = 'yykweishop';
|
||||
}
|
||||
|
||||
return array_values(array_unique($names));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 pages_template 列表时的查询计划(按优先级)。
|
||||
*
|
||||
* @return list<array{distributor_id: int, weapp_pages: string}>
|
||||
*/
|
||||
public static function pagesTemplateListSearchPlans(int $rowDistributorId): array
|
||||
{
|
||||
if ($rowDistributorId > 0) {
|
||||
return [
|
||||
['distributor_id' => $rowDistributorId, 'weapp_pages' => 'distributor_index'],
|
||||
['distributor_id' => $rowDistributorId, 'weapp_pages' => 'index'],
|
||||
['distributor_id' => 0, 'weapp_pages' => 'index'],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
['distributor_id' => 0, 'weapp_pages' => 'index'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed>|null $detail PagesTemplateServices::content 返回值
|
||||
*/
|
||||
@@ -253,10 +313,11 @@ class StoreHomePageService
|
||||
'page_template_detail' => null,
|
||||
];
|
||||
|
||||
$templateName = (string) ($row['template_name'] ?? '');
|
||||
$pid = (int) ($resolved['resolved_pages_template_id'] ?? 0);
|
||||
if ($pid > 0) {
|
||||
$distId = (int) ($row['distributor_id'] ?? 0);
|
||||
$customizeId = isset($row['weapp_customize_page_id']) ? (int) $row['weapp_customize_page_id'] : 0;
|
||||
$distId = (int) ($row['distributor_id'] ?? 0);
|
||||
$customizeId = isset($row['weapp_customize_page_id']) ? (int) $row['weapp_customize_page_id'] : 0;
|
||||
if ($templateName !== '' && ($pid > 0 || $customizeId > 0)) {
|
||||
$pagesTemplateServices = new PagesTemplateServices();
|
||||
|
||||
$indexParams = [
|
||||
@@ -264,8 +325,8 @@ class StoreHomePageService
|
||||
'regionauth_id' => 0,
|
||||
'user_id' => $userId,
|
||||
'distributor_id' => $distId,
|
||||
'weapp_pages' => 'index',
|
||||
'template_name' => (string) ($row['template_name'] ?? ''),
|
||||
'weapp_pages' => $distId > 0 ? 'distributor_index' : 'index',
|
||||
'template_name' => $templateName,
|
||||
'version' => self::INDEX_DECORATION_SETTING_VERSION,
|
||||
'page' => '1',
|
||||
'page_size' => '50',
|
||||
@@ -277,26 +338,14 @@ class StoreHomePageService
|
||||
|
||||
$customPageName = self::weappSettingPageNameForCustomizePage($customizeId);
|
||||
if ($customPageName !== null) {
|
||||
$customParams = array_merge($indexParams, [
|
||||
'version' => self::CUSTOM_DECORATION_SETTING_VERSION,
|
||||
'weapp_setting_page_name' => $customPageName,
|
||||
]);
|
||||
$detail = $pagesTemplateServices->content($customParams);
|
||||
if (!self::pageTemplateDetailHasNonEmptyList($detail)) {
|
||||
$customParams['weapp_setting_pages_template_id'] = 0;
|
||||
$detail = $pagesTemplateServices->content($customParams);
|
||||
}
|
||||
if (!self::pageTemplateDetailHasNonEmptyList($detail)) {
|
||||
app('log')->warning('[StoreHomePageService] enterprise_store_home 自定义页装修无匹配 wechat_weapp_setting,回退 index', [
|
||||
'company_id' => $companyId,
|
||||
'store_home_page_id' => $id,
|
||||
'weapp_customize_page_id' => $customizeId,
|
||||
'weapp_setting_page_name' => $customPageName,
|
||||
]);
|
||||
$detail = $pagesTemplateServices->content($indexParams);
|
||||
}
|
||||
$base['page_template_detail'] = $detail;
|
||||
} else {
|
||||
$base['page_template_detail'] = $this->fetchCustomPageDecorationDetail(
|
||||
$companyId,
|
||||
$row,
|
||||
$customPageName,
|
||||
$distId,
|
||||
$authDistributorId
|
||||
);
|
||||
} elseif ($pid > 0) {
|
||||
$base['page_template_detail'] = $pagesTemplateServices->content($indexParams);
|
||||
}
|
||||
}
|
||||
@@ -325,19 +374,25 @@ class StoreHomePageService
|
||||
}
|
||||
|
||||
$distributorId = (int) ($storeHomeRow['distributor_id'] ?? 0);
|
||||
$weappPages = $distributorId > 0 ? 'distributor_index' : 'index';
|
||||
|
||||
$pagesTemplateServices = new PagesTemplateServices();
|
||||
$listResult = $pagesTemplateServices->lists([
|
||||
'company_id' => $companyId,
|
||||
'distributor_id' => $distributorId,
|
||||
'weapp_pages' => $weappPages,
|
||||
'page_no' => 1,
|
||||
'page_size' => 100,
|
||||
]);
|
||||
|
||||
$rows = $listResult['list'] ?? [];
|
||||
$picked = self::pickResolvedPagesTemplateRow(is_array($rows) ? $rows : [], $templateName);
|
||||
$picked = null;
|
||||
foreach (self::decorationTemplateNameCandidates($storeHomeRow) as $tryTemplateName) {
|
||||
foreach (self::pagesTemplateListSearchPlans($distributorId) as $plan) {
|
||||
$listResult = $pagesTemplateServices->lists([
|
||||
'company_id' => $companyId,
|
||||
'distributor_id' => $plan['distributor_id'],
|
||||
'weapp_pages' => $plan['weapp_pages'],
|
||||
'page_no' => 1,
|
||||
'page_size' => 100,
|
||||
]);
|
||||
$rows = $listResult['list'] ?? [];
|
||||
$picked = self::pickResolvedPagesTemplateRow(is_array($rows) ? $rows : [], $tryTemplateName);
|
||||
if ($picked !== null) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($picked === null) {
|
||||
return [
|
||||
@@ -361,6 +416,122 @@ class StoreHomePageService
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义页装修:与后台 getParamByTempName 一致(custom_{id} + shop_{distributor_id},pages_template_id=0)。
|
||||
*
|
||||
* @param array<string,mixed> $storeHomeRow
|
||||
*
|
||||
* @return array{list: array<int, array<string,mixed>>, config: array<int, array<string,mixed>>}
|
||||
*/
|
||||
private function fetchCustomPageDecorationDetail(
|
||||
int $companyId,
|
||||
array $storeHomeRow,
|
||||
string $customPageName,
|
||||
int $distId,
|
||||
int $authDistributorId
|
||||
): array {
|
||||
foreach (self::decorationTemplateNameCandidates($storeHomeRow) as $tryTemplateName) {
|
||||
foreach (self::customDecorationSettingVersionCandidates($distId, $authDistributorId) as $version) {
|
||||
$entities = $this->weappSettingRepository->getParamByTempName(
|
||||
$companyId,
|
||||
$tryTemplateName,
|
||||
$customPageName,
|
||||
null,
|
||||
$version,
|
||||
0
|
||||
);
|
||||
$list = self::buildTemplateConfListFromWeappSettingEntities($entities, $companyId, $tryTemplateName);
|
||||
if ($list !== []) {
|
||||
return self::buildPageTemplateDetailFromTemplateConfList($list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ['list' => [], 'config' => []];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function safeDecodeWeappSettingParams($raw): array
|
||||
{
|
||||
if (is_array($raw)) {
|
||||
return $raw;
|
||||
}
|
||||
if (!is_string($raw) || $raw === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$unserialized = @unserialize($raw);
|
||||
if (is_array($unserialized)) {
|
||||
return $unserialized;
|
||||
}
|
||||
|
||||
$decoded = json_decode($raw, true);
|
||||
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $entities
|
||||
*
|
||||
* @return array<int,array<string,mixed>>
|
||||
*/
|
||||
public static function buildTemplateConfListFromWeappSettingEntities($entities, int $companyId, string $fallbackTemplateName = ''): array
|
||||
{
|
||||
if ($entities instanceof \Traversable) {
|
||||
$entities = iterator_to_array($entities);
|
||||
}
|
||||
if (!is_array($entities)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ($entities as $row) {
|
||||
if (!is_object($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$pageName = method_exists($row, 'getPageName') ? (string) $row->getPageName() : '';
|
||||
$list[] = [
|
||||
'id' => method_exists($row, 'getId') ? $row->getId() : 0,
|
||||
'template_name' => method_exists($row, 'getTemplateName') ? $row->getTemplateName() : $fallbackTemplateName,
|
||||
'company_id' => method_exists($row, 'getCompanyId') ? $row->getCompanyId() : $companyId,
|
||||
'name' => method_exists($row, 'getName') ? (string) $row->getName() : '',
|
||||
'page_name' => $pageName !== '' ? $pageName : 'index',
|
||||
'params' => self::safeDecodeWeappSettingParams(method_exists($row, 'getParams') ? $row->getParams() : null),
|
||||
];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int,array<string,mixed>> $list
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function buildPageTemplateDetailFromTemplateConfList(array $list): array
|
||||
{
|
||||
$config = [];
|
||||
foreach ($list as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$params = $row['params'] ?? null;
|
||||
if (is_array($params) && isset($params['name']) && isset($params['base'])) {
|
||||
$config[] = $params;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'list' => $list,
|
||||
'config' => $config,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 pages_template 列表中选出与内购模版 template_name 一致且启用的记录;多条时取列表顺序第一条。
|
||||
*
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
namespace GoodsBundle\Http\FrontApi\V1\Action;
|
||||
|
||||
use App\Http\Controllers\Controller as BaseController;
|
||||
use CompanysBundle\Ego\CompanysActivationEgo;
|
||||
use DistributionBundle\Services\DistributorService;
|
||||
use GoodsBundle\Services\ItemsCategoryService;
|
||||
use GoodsBundle\Services\ItemsRelCatsService;
|
||||
@@ -138,8 +139,13 @@ class Category extends BaseController
|
||||
// 小程序端仅返回前台展示的分类
|
||||
$filter['is_show_front'] = 1;
|
||||
|
||||
if ($request->input('distributor_id')) {
|
||||
// $filter['distributor_id'] = $request->input('distributor_id');
|
||||
$distributorId = (int)$request->input('distributor_id', 0);
|
||||
$company = (new CompanysActivationEgo())->check($company_id);
|
||||
$productModel = $company['product_model'] ?? 'platform';
|
||||
$itemsCategoryService = new ItemsCategoryService();
|
||||
$categoryDistributorId = $itemsCategoryService->resolveCategoryDistributorIdForFront($productModel, $distributorId);
|
||||
if ($productModel !== 'standard' && $categoryDistributorId > 0) {
|
||||
$filter['distributor_id'] = $categoryDistributorId;
|
||||
}
|
||||
|
||||
$onlyTop = $request->input('only_top', false);
|
||||
@@ -148,7 +154,6 @@ class Category extends BaseController
|
||||
$filter['category_level'] = 1;
|
||||
}
|
||||
|
||||
$itemsCategoryService = new ItemsCategoryService();
|
||||
$result = $itemsCategoryService->getItemsCategory($filter, true, 1, -1, ['sort' => 'DESC', 'created' => 'ASC'], 'category_id,category_name,category_level,parent_id,image_url,customize_page_id');
|
||||
|
||||
// 分类获取不到获取商城主类目
|
||||
@@ -180,8 +185,9 @@ class Category extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
$distributorId = (int)$request->input('distributor_id', 0);
|
||||
if ($distributorId > 0 && $itemsCategoryService->isSaleableCategoryFilterEnabled($company_id)) {
|
||||
$applySaleableFilter = $itemsCategoryService->shouldApplySaleableFilterForFront($productModel, $distributorId)
|
||||
|| ($distributorId > 0 && $itemsCategoryService->isSaleableCategoryFilterEnabled($company_id));
|
||||
if ($distributorId > 0 && $applySaleableFilter) {
|
||||
$result = $itemsCategoryService->filterCategoryTreeBySaleableItems($company_id, $distributorId, $result);
|
||||
}
|
||||
|
||||
@@ -444,13 +450,26 @@ class Category extends BaseController
|
||||
|
||||
$filter['company_id'] = $company_id;
|
||||
|
||||
if ($request->input('distributor_id')) {
|
||||
$filter['distributor_id'] = $request->input('distributor_id');
|
||||
$distributorId = (int)$request->input('distributor_id', 0);
|
||||
$company = (new CompanysActivationEgo())->check($company_id);
|
||||
$productModel = $company['product_model'] ?? 'platform';
|
||||
$itemsCategoryService = new ItemsCategoryService();
|
||||
$categoryDistributorId = $itemsCategoryService->resolveCategoryDistributorIdForFront($productModel, $distributorId);
|
||||
if ($productModel !== 'standard' && $categoryDistributorId > 0) {
|
||||
$filter['distributor_id'] = $categoryDistributorId;
|
||||
}
|
||||
|
||||
$filter['is_main_category'] = $request->input('is_main_category', false);
|
||||
// $filter['category_level'] = $request->input('category_level');
|
||||
|
||||
if ($itemsCategoryService->shouldApplySaleableFilterForFront($productModel, $distributorId)) {
|
||||
$filter['category_id'] = $itemsCategoryService->getSaleableTopLevelCategoryIds($company_id, $distributorId);
|
||||
if (empty($filter['category_id'])) {
|
||||
return $this->response->array(['list' => [], 'total_count' => 0]);
|
||||
}
|
||||
$result = $itemsCategoryService->lists($filter);
|
||||
|
||||
return $this->response->array($result);
|
||||
}
|
||||
|
||||
$settingService = new SettingService();
|
||||
$config = $settingService->getConfig($company_id);
|
||||
@@ -463,14 +482,18 @@ class Category extends BaseController
|
||||
$itemFilter['item_type'] = 'normal';
|
||||
$itemFilter['is_default'] = true;
|
||||
|
||||
$distributorFilter = [
|
||||
'company_id' => $company_id,
|
||||
'is_valid' => 'true'
|
||||
];
|
||||
$distributorService = new DistributorService();
|
||||
$validDistributorList = $distributorService->getDistributorOriginalList($distributorFilter, 1, -1);
|
||||
$validDistributorIds = array_column($validDistributorList['list'], 'distributor_id');
|
||||
$itemFilter['distributor_id'] = array_merge(['0'], $validDistributorIds);
|
||||
if ($categoryDistributorId > 0) {
|
||||
$itemFilter['distributor_id'] = $categoryDistributorId;
|
||||
} else {
|
||||
$distributorFilter = [
|
||||
'company_id' => $company_id,
|
||||
'is_valid' => 'true'
|
||||
];
|
||||
$distributorService = new DistributorService();
|
||||
$validDistributorList = $distributorService->getDistributorOriginalList($distributorFilter, 1, -1);
|
||||
$validDistributorIds = array_column($validDistributorList['list'], 'distributor_id');
|
||||
$itemFilter['distributor_id'] = array_merge(['0'], $validDistributorIds);
|
||||
}
|
||||
|
||||
$itemsService = new ItemsService();
|
||||
$itemsList = $itemsService->itemsRepository->list($itemFilter, [], -1, 1, ['item_id']);
|
||||
@@ -480,7 +503,6 @@ class Category extends BaseController
|
||||
$itemsRelCatsService = new ItemsRelCatsService();
|
||||
$itemsRelCatsList = $itemsRelCatsService->lists($itemRelCatsParams);
|
||||
|
||||
$itemsCategoryService = new ItemsCategoryService();
|
||||
$filter['category_id'] = [];
|
||||
foreach ($itemsRelCatsList['list'] as $cat) {
|
||||
$category = $itemsCategoryService->getInfo(['company_id' => $company_id, 'category_id' => $cat['category_id']]);
|
||||
@@ -495,7 +517,6 @@ class Category extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
$itemsCategoryService = new ItemsCategoryService();
|
||||
$result = $itemsCategoryService->lists($filter);
|
||||
|
||||
return $this->response->array($result);
|
||||
|
||||
@@ -1498,4 +1498,54 @@ class Items extends BaseController
|
||||
unset($itemInfo['itemId'], $itemInfo['consumeType'], $itemInfo['itemName'], $itemInfo['itemBn'], $itemInfo['companyId'], $itemInfo['item_main_cat_id'], $itemInfo['nospec'], $itemInfo['pics_create_qrcode']);
|
||||
return $this->response->array($itemInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @SWG\Get(
|
||||
* path="/wxapp/goods/items/batch",
|
||||
* summary="批量获取商品基本信息",
|
||||
* tags={"商品"},
|
||||
* description="批量获取商品基本信息(图片、ID、名称、价格),供 ecshopx-web 等端使用",
|
||||
* operationId="getBatchItems",
|
||||
* @SWG\Parameter( name="Authorization", in="header", description="JWT验证token", type="string" ),
|
||||
* @SWG\Parameter( name="item_ids", in="query", description="商品ID列表,逗号分隔,如 1,2,3", required=true, type="string" ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="成功返回结构",
|
||||
* @SWG\Schema(
|
||||
* @SWG\Property(
|
||||
* property="data",
|
||||
* type="array",
|
||||
* @SWG\Items(
|
||||
* type="object",
|
||||
* @SWG\Property(property="item_id", type="integer", description="商品ID"),
|
||||
* @SWG\Property(property="item_name", type="string", description="商品名称"),
|
||||
* @SWG\Property(property="price", type="integer", description="销售价(单位:分)"),
|
||||
* @SWG\Property(property="pics", type="array", description="商品图片数组", @SWG\Items(type="string"))
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @SWG\Response( response="default", description="错误返回结构", @SWG\Schema( type="array", @SWG\Items(ref="#/definitions/GoodsErrorRespones") ) )
|
||||
* )
|
||||
*/
|
||||
public function getBatchItems(Request $request)
|
||||
{
|
||||
$authInfo = $request->get('auth');
|
||||
$itemIdsStr = $request->input('item_ids', '');
|
||||
|
||||
if (empty($itemIdsStr)) {
|
||||
return $this->response->array([]);
|
||||
}
|
||||
|
||||
$itemIds = array_filter(array_map('intval', explode(',', $itemIdsStr)));
|
||||
if (empty($itemIds)) {
|
||||
return $this->response->array([]);
|
||||
}
|
||||
|
||||
$itemsRepository = app('registry')->getManager('default')->getRepository(\GoodsBundle\Entities\Items::class);
|
||||
$cols = 'item_id,item_name,price,pics';
|
||||
$result = $itemsRepository->getLists(['item_id' => $itemIds], $cols, 1, -1, []);
|
||||
|
||||
return $this->response->array($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +144,66 @@ class ItemsCategoryService
|
||||
return 'goods:category:saleable_filter:' . $companyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* FrontApi 分类列表查询用的 distributor_id(items_category 维度)。
|
||||
* BBC(standard) 共用平台销售分类;platform 使用店铺独立分类。
|
||||
*/
|
||||
public function resolveCategoryDistributorIdForFront(string $productModel, int $requestDistributorId): int
|
||||
{
|
||||
if ($productModel === 'standard') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $requestDistributorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* FrontApi 是否按店铺可售商品过滤分类树(BBC 店铺主页)。
|
||||
*/
|
||||
public function shouldApplySaleableFilterForFront(string $productModel, int $requestDistributorId): bool
|
||||
{
|
||||
return $productModel === 'standard' && $requestDistributorId > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取店铺可售商品对应的一级销售分类 ID 列表(用于 categorylevel 等扁平接口)。
|
||||
*/
|
||||
public function getSaleableTopLevelCategoryIds(int $companyId, int $distributorId): array
|
||||
{
|
||||
if ($companyId <= 0 || $distributorId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$categoryIdMap = $this->getSaleableCategoryIdMap($companyId, $distributorId);
|
||||
if (empty($categoryIdMap)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$topLevelIds = [];
|
||||
foreach (array_keys($categoryIdMap) as $categoryId) {
|
||||
if (!is_numeric($categoryId)) {
|
||||
continue;
|
||||
}
|
||||
$category = $this->itemsCategoryRepository->getInfo([
|
||||
'company_id' => $companyId,
|
||||
'category_id' => (int)$categoryId,
|
||||
]);
|
||||
if (!$category) {
|
||||
continue;
|
||||
}
|
||||
if ((int)($category['parent_id'] ?? 0) === 0) {
|
||||
$topLevelIds[] = (int)$category['category_id'];
|
||||
} else {
|
||||
$path = explode(',', (string)($category['path'] ?? ''));
|
||||
if (!empty($path[0]) && is_numeric($path[0])) {
|
||||
$topLevelIds[] = (int)$path[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($topLevelIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 关于节点的回溯
|
||||
* @param $categories
|
||||
|
||||
@@ -29,6 +29,7 @@ use KaquanBundle\Services\DiscountNewGiftCardService;
|
||||
use KaquanBundle\Services\KaquanService;
|
||||
use KaquanBundle\Services\DiscountCardService;
|
||||
use KaquanBundle\Services\UserDiscountService;
|
||||
use KaquanBundle\Support\ShopDiscountCardListFilter;
|
||||
|
||||
class DiscountCard extends BaseController
|
||||
{
|
||||
@@ -656,15 +657,12 @@ class DiscountCard extends BaseController
|
||||
}
|
||||
|
||||
$store_self = $request->input('store_self');
|
||||
$sourceId = floatval($request->get('distributor_id', 0));//如果是平台,这里是0
|
||||
$sourceId = intval($request->get('distributor_id', 0));//如果是平台,这里是0
|
||||
if ($store_self == "true") {//平台版仅支持自营商品【总店】
|
||||
$filter['or']['distributor_id|like'] = ',0,';
|
||||
$filter['or']['distributor_id|like'] = '%,%';
|
||||
} else {
|
||||
if ($request->get('distributor_id')) {
|
||||
$filter['or']['distributor_id|like'] = ',' . $request->get('distributor_id') . ',';
|
||||
$filter['or']['distributor_id|like'] = '%,%';
|
||||
}
|
||||
} elseif ($sourceId > 0) {
|
||||
ShopDiscountCardListFilter::applyToFilter($filter, $sourceId);
|
||||
}
|
||||
|
||||
if ($request->input('receive')) {
|
||||
@@ -672,10 +670,6 @@ class DiscountCard extends BaseController
|
||||
}
|
||||
|
||||
if ($from == 'btn') {
|
||||
// 如果来源是按钮出发,平台显示所有的券,店铺显示自己的券
|
||||
if ($sourceId > 0) {
|
||||
$filter['source_id'] = $sourceId;
|
||||
}
|
||||
$filter['end_date'] = time();//排除已过期的优惠券
|
||||
}
|
||||
|
||||
|
||||
77
src/KaquanBundle/Support/ShopDiscountCardListFilter.php
Normal file
77
src/KaquanBundle/Support/ShopDiscountCardListFilter.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace KaquanBundle\Support;
|
||||
|
||||
/**
|
||||
* 店铺端优惠券列表可见性过滤(理解 B:本店创建 或 适用本店)。
|
||||
*/
|
||||
final class ShopDiscountCardListFilter
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $filter
|
||||
*/
|
||||
public static function applyToFilter(array &$filter, int $distributorId): bool
|
||||
{
|
||||
if ($distributorId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$filter['or'] = self::orConditionsForShop($distributorId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function orConditionsForShop(int $distributorId): array
|
||||
{
|
||||
return [
|
||||
'source_id' => $distributorId,
|
||||
'use_all_shops' => 1,
|
||||
// DiscountCardsRepository::__orFilter 的 like 不会自动包裹 %
|
||||
'distributor_id|like' => '%,' . $distributorId . ',%',
|
||||
'distributor_id' => ',',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $coupon
|
||||
*/
|
||||
public static function matchesCoupon(array $coupon, int $distributorId): bool
|
||||
{
|
||||
if ($distributorId <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((int) ($coupon['source_id'] ?? 0) === $distributorId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!empty($coupon['use_all_shops']) && (int) $coupon['use_all_shops'] === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$distributorIdField = (string) ($coupon['distributor_id'] ?? '');
|
||||
if ($distributorIdField === ',') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return str_contains($distributorIdField, ',' . $distributorId . ',');
|
||||
}
|
||||
}
|
||||
@@ -1515,9 +1515,63 @@ class Members extends Controller
|
||||
if (!$memberRegSettingService->checkImageVcode($token, $companyId, $yzmcode, $type)) {
|
||||
throw new ResourceException(trans('MembersBundle/Members.image_captcha_error'));
|
||||
}
|
||||
$memberRegSettingService->generateSmsVcode($mobile, $companyId, $type);
|
||||
$debugVcode = $memberRegSettingService->generateSmsVcode($mobile, $companyId, $type);
|
||||
|
||||
return $this->response->array(['status' => true]);
|
||||
$response = ['status' => true];
|
||||
if (is_string($debugVcode)) {
|
||||
$response['message'] = '短信调试模式已开启';
|
||||
$response['debug_vcode'] = $debugVcode;
|
||||
}
|
||||
return $this->response->array($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @SWG\Get(
|
||||
* path="/member/verify",
|
||||
* summary="POS 会员手机号验证码校验并查询会员信息",
|
||||
* tags={"会员"},
|
||||
* description="POS 场景通过手机号和短信验证码校验会员身份,并返回会员信息,不生成前台会员登录态",
|
||||
* operationId="verifyMemberByMobileCode",
|
||||
* @SWG\Parameter(name="mobile", in="query", description="手机号", required=true, type="string"),
|
||||
* @SWG\Parameter(name="vcode", in="query", description="短信验证码", required=true, type="string"),
|
||||
* @SWG\Response(response=200, description="成功返回结构"),
|
||||
* @SWG\Response(response="default", description="错误返回结构", @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/MembersErrorRespones")))
|
||||
* )
|
||||
*/
|
||||
public function verifyMember(Request $request)
|
||||
{
|
||||
$companyId = (int) app('auth')->user()->get('company_id');
|
||||
$mobile = trim((string) $request->input('mobile', ''));
|
||||
$vcode = trim((string) $request->input('vcode', ''));
|
||||
|
||||
if ($mobile === '' || !preg_match('/^1[3456789]\d{9}$/', $mobile)) {
|
||||
throw new ResourceException(trans('MembersBundle/Members.mobile_error'));
|
||||
}
|
||||
|
||||
if ($vcode === '') {
|
||||
throw new ResourceException(trans('MembersBundle/Members.verification_code_required'));
|
||||
}
|
||||
|
||||
if (!(new MemberRegSettingService())->checkSmsVcode($mobile, $companyId, $vcode, 'login')) {
|
||||
throw new ResourceException(trans('MembersBundle/Members.sms_code_error'));
|
||||
}
|
||||
|
||||
$memberInfo = $this->memberService->getInfoByMobile($companyId, $mobile);
|
||||
if (empty($memberInfo['user_id'])) {
|
||||
throw new ResourceException(trans('MembersBundle/Members.mobile_not_exists'));
|
||||
}
|
||||
|
||||
return $this->response->array([
|
||||
'data' => [
|
||||
'user_id' => $memberInfo['user_id'],
|
||||
'mobile' => $memberInfo['mobile'] ?? $mobile,
|
||||
'username' => $memberInfo['username'] ?? '',
|
||||
'user_card_code' => $memberInfo['user_card_code'] ?? '',
|
||||
'avatar' => $memberInfo['avatar'] ?? '',
|
||||
'member_name' => $memberInfo['username'] ?? '',
|
||||
'member_mobile' => $memberInfo['mobile'] ?? $mobile,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function createMember(Request $request)
|
||||
@@ -1531,13 +1585,13 @@ class Members extends Controller
|
||||
throw new ResourceException(trans('MembersBundle/Members.invalid_mobile'));
|
||||
}
|
||||
|
||||
/*if (!$postData['vcode']) {
|
||||
if (empty($postData['vcode'])) {
|
||||
throw new ResourceException(trans('MembersBundle/Members.verification_code_required'));
|
||||
}
|
||||
|
||||
if (!(new MemberRegSettingService())->checkSmsVcode($postData['mobile'], $companyId, $postData['vcode'], $postData['check_type'] ?? 'sign')) {
|
||||
throw new ResourceException(trans('MembersBundle/Members.sms_code_error'));
|
||||
}*/
|
||||
}
|
||||
|
||||
$memberInfo = $this->memberService->getInfoByMobile((int)$companyId, (string)$postData['mobile']);
|
||||
if ($memberInfo) {
|
||||
@@ -1559,7 +1613,7 @@ class Members extends Controller
|
||||
//新增-会员信息
|
||||
$memberInfo = [
|
||||
'company_id' => $companyId,
|
||||
'username' => randValue(8),
|
||||
'username' => $postData['name'] ?? randValue(8),
|
||||
'mobile' => $postData['mobile'],
|
||||
'grade_id' => $defaultGradeInfo['grade_id'],
|
||||
'password' => substr(str_shuffle('QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm'), 5, 10),
|
||||
|
||||
@@ -1247,8 +1247,13 @@ class Members extends Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
$memberRegSettingService->generateSmsVcode($phone, $companyId, $type);
|
||||
return $this->response->array(['message' => "短信发送成功"]);
|
||||
$debugVcode = $memberRegSettingService->generateSmsVcode($phone, $companyId, $type);
|
||||
$response = ['message' => "短信发送成功"];
|
||||
if (is_string($debugVcode)) {
|
||||
$response['message'] = '短信调试模式已开启';
|
||||
$response['debug_vcode'] = $debugVcode;
|
||||
}
|
||||
return $this->response->array($response);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -231,6 +231,10 @@ class MemberRegSettingService
|
||||
app('log')->info("code :" . json_encode(['phone' => $phone, 'company' => $companyId, 'vcode' => $vcode]));
|
||||
//保存验证码
|
||||
$this->saveSmsVcode($phone, $companyId, $vcode, $type);
|
||||
if (config("common.sms_debug_vcode")) {
|
||||
app('log')->info("member sms debug vcode enabled :" . json_encode(['phone' => $phone, 'company' => $companyId, 'type' => $type]));
|
||||
return $vcode;
|
||||
}
|
||||
//发送短信
|
||||
$this->sendSmsVcode($companyId, $phone, $vcode, $type);
|
||||
return true;
|
||||
|
||||
@@ -1875,7 +1875,7 @@ class Order extends Controller
|
||||
$setting['latest_aftersale_time'] = $input['latest_aftersale_time'] ?? 0; //默认确认收货后不可申请售后
|
||||
$setting['auto_refuse_time'] = $input['auto_refuse_time'] ?? 0; //默认确认收货后不可申请售后
|
||||
$setting['auto_aftersales'] = isset($input['auto_aftersales']) && $input['auto_aftersales'] && $input['auto_aftersales'] != 'false'; // 未发货售后自动同意
|
||||
$setting['offline_aftersales'] = isset($input['offline_aftersales']) && $input['offline_aftersales'] && $input['offline_aftersales'] != 'false'; // 到店退货
|
||||
$setting['offline_aftersales'] = isset($input['offline_aftersales']) && ($input['offline_aftersales'] === 'true' || $input['offline_aftersales'] === true); // 到店退货
|
||||
$setting['is_refund_freight'] = $input['is_refund_freight'] ?? 0; // 退货退款可退运费
|
||||
|
||||
if ($setting['is_refund_freight'] == 1) {
|
||||
|
||||
@@ -48,7 +48,7 @@ class DistributorCartObject implements CartInterface
|
||||
public function checkItemParams($params)
|
||||
{
|
||||
// 检查是否是有效的会员优先购
|
||||
$memberpreference = $this->checkCurrentMemberpreferenceByItemId($params['company_id'], $params['user_id'], $params['item_id'], $params['shop_id'], false, $msg);
|
||||
$memberpreference = $this->checkCurrentMemberpreferenceByItemId($params['company_id'], $params['user_id'], $params['item_id'], $msg, $params['shop_id'], false);
|
||||
if (!$memberpreference) {
|
||||
throw new ResourceException($msg);
|
||||
}
|
||||
|
||||
@@ -662,7 +662,7 @@ class CartService
|
||||
$memberpreference = true;
|
||||
$cartdata['shop_type'] = $cartdata['shop_type'] ?? '';
|
||||
if ($cartType != 'employee_purchase' && ($cartdata['shop_type'] != 'pointsmall' && $cartdata['shop_type'] != 'shop_offline')) {
|
||||
$memberpreference = $this->checkCurrentMemberpreferenceByItemId($companyId, $userId, $itemId, $cartdata['shop_id'], false, $msg);
|
||||
$memberpreference = $this->checkCurrentMemberpreferenceByItemId($companyId, $userId, $itemId, $msg, $cartdata['shop_id'], false);
|
||||
}
|
||||
if (!$memberpreference) {
|
||||
$invalidCart[] = $cartdata;
|
||||
|
||||
@@ -1370,7 +1370,7 @@ class OrderService
|
||||
$itemsCommissionService = new ItemsCommissionService();
|
||||
foreach ($this->orderItemList as $itemInfo) {
|
||||
if (!in_array($this->orderInterface->orderClass, ['pointsmall', 'employee_purchase'])) {
|
||||
$memberpreference = $this->checkCurrentMemberpreferenceByItemId($orderData['company_id'], $orderData['user_id'], $itemInfo['itemId'], $orderData['distributor_id'], false, $msg);
|
||||
$memberpreference = $this->checkCurrentMemberpreferenceByItemId($orderData['company_id'], $orderData['user_id'], $itemInfo['itemId'], $msg, $orderData['distributor_id'], false);
|
||||
if (!$memberpreference) {
|
||||
$orderData['extraTips'] = $msg;
|
||||
if ($isCheck) {
|
||||
|
||||
@@ -81,15 +81,30 @@ trait GetOrderIdTrait
|
||||
$identityOrderId = $promoterOrderId = [];
|
||||
$is_promoter_identity = $is_promoter_mobile = false;
|
||||
if (isset($filter['promoter_identity']) && $filter['promoter_identity']) {
|
||||
$sql = "select promoter.user_id from popularize_promoter promoter left join popularize_promoter_identity identity on promoter.identity_id=identity.id where promoter.company_id=".$filter['company_id']." and identity.name='".$filter['promoter_identity']."'";
|
||||
$lists = $conn->executeQuery($sql)->fetchAll();
|
||||
$qb = $conn->createQueryBuilder();
|
||||
$lists = $qb->select('promoter.user_id')
|
||||
->from('popularize_promoter', 'promoter')
|
||||
->leftJoin('promoter', 'popularize_promoter_identity', 'identity', 'promoter.identity_id = identity.id')
|
||||
->where($qb->expr()->eq('promoter.company_id', $qb->expr()->literal($filter['company_id'])))
|
||||
->andWhere($qb->expr()->eq('identity.name', $qb->expr()->literal($filter['promoter_identity'])))
|
||||
->execute()
|
||||
->fetchAll();
|
||||
$userIds = array_column($lists, 'user_id');
|
||||
$userIds = array_filter($userIds, function($value) {
|
||||
return $value !== null && $value !== false && $value !== "" && $value !== 0;
|
||||
});
|
||||
if ($userIds) {
|
||||
$sql = "select id,order_id,user_id,buy_user_id from popularize_brokerage where company_id=".$filter['company_id']." and brokerage_type='first_level' and user_id in (".implode($userIds, ',').")";
|
||||
$lists = $conn->executeQuery($sql)->fetchAll();
|
||||
$qb = $conn->createQueryBuilder();
|
||||
$userIdLiterals = array_map(function ($value) use ($qb) {
|
||||
return $qb->expr()->literal($value);
|
||||
}, $userIds);
|
||||
$lists = $qb->select('id', 'order_id', 'user_id', 'buy_user_id')
|
||||
->from('popularize_brokerage')
|
||||
->where($qb->expr()->eq('company_id', $qb->expr()->literal($filter['company_id'])))
|
||||
->andWhere($qb->expr()->eq('brokerage_type', $qb->expr()->literal('first_level')))
|
||||
->andWhere($qb->expr()->in('user_id', $userIdLiterals))
|
||||
->execute()
|
||||
->fetchAll();
|
||||
$identityOrderId = array_column($lists, 'order_id');
|
||||
$identityOrderId = array_filter($identityOrderId, function($value) {
|
||||
return $value !== null && $value !== false && $value !== "" && $value !== 0;
|
||||
@@ -102,8 +117,14 @@ trait GetOrderIdTrait
|
||||
$memberService = new MemberService();
|
||||
$userId = $memberService->getUserIdByMobile($filter['promoter_mobile'], $filter['company_id']);
|
||||
if ($userId) {
|
||||
$sql = "select brokerage.order_id from popularize_promoter promoter left join popularize_brokerage brokerage on promoter.user_id=brokerage.user_id where promoter.company_id=".$filter['company_id']." and promoter.user_id=".$userId;
|
||||
$lists = $conn->executeQuery($sql)->fetchAll();
|
||||
$qb = $conn->createQueryBuilder();
|
||||
$lists = $qb->select('brokerage.order_id')
|
||||
->from('popularize_promoter', 'promoter')
|
||||
->leftJoin('promoter', 'popularize_brokerage', 'brokerage', 'promoter.user_id = brokerage.user_id')
|
||||
->where($qb->expr()->eq('promoter.company_id', $qb->expr()->literal($filter['company_id'])))
|
||||
->andWhere($qb->expr()->eq('promoter.user_id', $qb->expr()->literal($userId)))
|
||||
->execute()
|
||||
->fetchAll();
|
||||
$promoterOrderId = array_column($lists, 'order_id');
|
||||
$promoterOrderId = array_filter($promoterOrderId, function($value) {
|
||||
return $value !== null && $value !== false && $value !== "" && $value !== 0;
|
||||
@@ -151,8 +172,18 @@ trait GetOrderIdTrait
|
||||
// p_promoter_mobile:上级推广员手机号
|
||||
// promoter_is_close:是否结算
|
||||
$orderIds = array_column($orderLists, 'order_id');
|
||||
$sql = "select order_id,user_id,is_close from popularize_brokerage where brokerage_type='first_level' and source='order' and order_id in (".implode($orderIds, ',').")";
|
||||
$userLists = $conn->executeQuery($sql)->fetchAll();
|
||||
$literalQb = $conn->createQueryBuilder();
|
||||
$orderIdLiterals = array_map(function ($value) use ($literalQb) {
|
||||
return $literalQb->expr()->literal($value);
|
||||
}, $orderIds);
|
||||
$qb = $conn->createQueryBuilder();
|
||||
$userLists = $qb->select('order_id', 'user_id', 'is_close')
|
||||
->from('popularize_brokerage')
|
||||
->where($qb->expr()->eq('brokerage_type', $qb->expr()->literal('first_level')))
|
||||
->andWhere($qb->expr()->eq('source', $qb->expr()->literal('order')))
|
||||
->andWhere($qb->expr()->in('order_id', $orderIdLiterals))
|
||||
->execute()
|
||||
->fetchAll();
|
||||
$userIds = array_column($userLists, 'user_id');
|
||||
$userIds = array_filter($userIds, function($value) {
|
||||
return $value !== null && $value !== false && $value !== "" && $value !== 0;
|
||||
@@ -162,8 +193,22 @@ trait GetOrderIdTrait
|
||||
}
|
||||
$result = [];
|
||||
// 查询推广员
|
||||
$sql = "select promoter.user_id,promoter.promoter_name,promoter.pname p_promoter_name,promoter.pmobile p_promoter_mobile,identity.name promoter_identity from popularize_promoter promoter left join popularize_promoter_identity identity on promoter.identity_id=identity.id where promoter.user_id in (".implode($userIds, ',').")";
|
||||
$promoterLists = $conn->executeQuery($sql)->fetchAll();
|
||||
$userIdLiterals = array_map(function ($value) use ($literalQb) {
|
||||
return $literalQb->expr()->literal($value);
|
||||
}, $userIds);
|
||||
$qb = $conn->createQueryBuilder();
|
||||
$promoterLists = $qb->select(
|
||||
'promoter.user_id',
|
||||
'promoter.promoter_name',
|
||||
'promoter.pname AS p_promoter_name',
|
||||
'promoter.pmobile AS p_promoter_mobile',
|
||||
'identity.name AS promoter_identity'
|
||||
)
|
||||
->from('popularize_promoter', 'promoter')
|
||||
->leftJoin('promoter', 'popularize_promoter_identity', 'identity', 'promoter.identity_id = identity.id')
|
||||
->where($qb->expr()->in('promoter.user_id', $userIdLiterals))
|
||||
->execute()
|
||||
->fetchAll();
|
||||
$promoterLists = array_column($promoterLists, null, 'user_id');
|
||||
|
||||
// 查询推广员手机号
|
||||
@@ -175,16 +220,35 @@ trait GetOrderIdTrait
|
||||
$promoterData[$value['order_id']]['promoter_is_close'] = $value['is_close'];
|
||||
}
|
||||
// 查询订单分佣金额
|
||||
$where = "company_id=".$companyId." and order_id in (".implode($orderIds, ',').") and source='order'";
|
||||
$sql = "select order_id,sum(rebate) order_total_rebate from popularize_brokerage where ".$where." group by order_id";
|
||||
$orderRebate = $conn->executeQuery($sql)->fetchAll();
|
||||
$buildBrokerageRebateQuery = function ($brokerageType = null) use ($conn, $companyId, $orderIdLiterals) {
|
||||
$qb = $conn->createQueryBuilder();
|
||||
$qb->from('popularize_brokerage')
|
||||
->where($qb->expr()->eq('company_id', $qb->expr()->literal($companyId)))
|
||||
->andWhere($qb->expr()->in('order_id', $orderIdLiterals))
|
||||
->andWhere($qb->expr()->eq('source', $qb->expr()->literal('order')));
|
||||
if ($brokerageType !== null) {
|
||||
$qb->andWhere($qb->expr()->eq('brokerage_type', $qb->expr()->literal($brokerageType)));
|
||||
}
|
||||
return $qb;
|
||||
};
|
||||
$orderRebate = $buildBrokerageRebateQuery()
|
||||
->select('order_id', 'SUM(rebate) AS order_total_rebate')
|
||||
->groupBy('order_id')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
$orderRebate = array_column($orderRebate, null, 'order_id');
|
||||
// 查询订单分佣金额
|
||||
$sql = "select order_id,sum(rebate) as rebate from popularize_brokerage where ".$where." and brokerage_type='first_level' group by order_id";
|
||||
$firstRebate = $conn->executeQuery($sql)->fetchAll();
|
||||
$firstRebate = $buildBrokerageRebateQuery('first_level')
|
||||
->select('order_id', 'SUM(rebate) AS rebate')
|
||||
->groupBy('order_id')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
$firstRebate = array_column($firstRebate, null, 'order_id');
|
||||
$sql = "select order_id,sum(rebate) as rebate from popularize_brokerage where ".$where." and brokerage_type='second_level' group by order_id";
|
||||
$secondRebate = $conn->executeQuery($sql)->fetchAll();
|
||||
$secondRebate = $buildBrokerageRebateQuery('second_level')
|
||||
->select('order_id', 'SUM(rebate) AS rebate')
|
||||
->groupBy('order_id')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
$secondRebate = array_column($secondRebate, null, 'order_id');
|
||||
foreach ($orderIds as $order_id) {
|
||||
if (isset($promoterData[$order_id])) {
|
||||
|
||||
@@ -246,7 +246,7 @@ trait CheckPromotionsValid
|
||||
* @param inteter $itemId 商品详情也的商品ID,也是默认商品ID
|
||||
* @param bool $isItemsAll 如果商品为多规格商品是否需要查询所有的SKU信息
|
||||
*/
|
||||
public function checkCurrentMemberpreferenceByItemId($companyId, $userId, $itemId, $distributorId = null, $isItemsAll = true, &$msg)
|
||||
public function checkCurrentMemberpreferenceByItemId($companyId, $userId, $itemId, &$msg, $distributorId = null, $isItemsAll = true)
|
||||
{
|
||||
$itemsService = new ItemsService();
|
||||
$itemInfo = $itemsService->getInfo(['item_id' => $itemId, 'company_id' => $companyId]);
|
||||
|
||||
@@ -60,17 +60,28 @@ class OpenScreenAd extends Controller
|
||||
*/
|
||||
public function getInfo(Request $request)
|
||||
{
|
||||
// CONST: 1E236443
|
||||
$params = $request->all('company_id');
|
||||
$auth_info = $request->get('auth');
|
||||
|
||||
$filter['company_id'] = $auth_info['company_id'];
|
||||
$filter['is_enable'] = 1;
|
||||
$filter['start_time|lte'] = time();
|
||||
$filter['end_time|gte'] = time();
|
||||
$OpenScreenAd = new OpenScreenAdServices();
|
||||
$data = $OpenScreenAd->lists($filter, '*', 1, 1);
|
||||
$result = !empty($data['list']) ? reset($data['list']) : [];
|
||||
|
||||
// 只有配置了开始/结束时间才校验;都是 0 表示不限期(管理端未传时间时的默认值)
|
||||
if (!empty($result)) {
|
||||
$now = time();
|
||||
$startTime = (int) ($result['start_time'] ?? 0);
|
||||
$endTime = (int) ($result['end_time'] ?? 0);
|
||||
if (!($startTime === 0 && $endTime === 0)) {
|
||||
if ($startTime > 0 && $startTime > $now) {
|
||||
$result = [];
|
||||
} elseif ($endTime > 0 && $endTime < $now) {
|
||||
$result = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$result['ad_url'] = json_decode($result['ad_url'], true);
|
||||
}
|
||||
|
||||
@@ -122,12 +122,6 @@ class PagesTemplate extends Controller
|
||||
if ($result['data']) {
|
||||
$result['data'] = $itemsService->applyMultiSpecTotalStoreForItemList($result['data']);
|
||||
}
|
||||
if ($eActivityId > 0 && $result['data']) {
|
||||
$activityItemsService = new ActivityItemsService();
|
||||
$wrapped = ['list' => $result['data']];
|
||||
$wrapped = $activityItemsService->getItemsListActityPrice($wrapped, $eActivityId, (int) $params['company_id']);
|
||||
$result['data'] = $wrapped['list'];
|
||||
}
|
||||
if ($result['data']) {
|
||||
$result['data'] = $itemsService->getItemsListMemberPrice($result['data'], $authInfo['user_id'], $params['company_id']);
|
||||
}
|
||||
@@ -139,6 +133,13 @@ class PagesTemplate extends Controller
|
||||
$promotionType = '';
|
||||
}
|
||||
$result['data'] = $itemsService->getItemsListActityTag($result['data'], $params['company_id'], $params['regionauth_id'], $params['user_id'], $promotionId, $promotionType, 'include_not_start');
|
||||
// 内购活动价最后覆盖,避免被营销标签 activity_price 覆盖
|
||||
if ($eActivityId > 0 && $result['data']) {
|
||||
$activityItemsService = new ActivityItemsService();
|
||||
$wrapped = ['list' => $result['data']];
|
||||
$wrapped = $activityItemsService->getItemsListActityPrice($wrapped, $eActivityId, (int) $params['company_id']);
|
||||
$result['data'] = $wrapped['list'];
|
||||
}
|
||||
}
|
||||
|
||||
//优惠券标签
|
||||
|
||||
@@ -73,8 +73,8 @@ class OpenScreenAdServices
|
||||
$saveAdd['waiting_time'] = $params['waiting_time'];
|
||||
$saveAdd['ad_url'] = $params['ad_url'];
|
||||
$saveAdd['app'] = $params['app'];
|
||||
$saveAdd['start_time'] = $params['start_time'];
|
||||
$saveAdd['end_time'] = $params['end_time'];
|
||||
$saveAdd['start_time'] = ($params['start_time'] !== null && $params['start_time'] !== '') ? (int) $params['start_time'] : 0;
|
||||
$saveAdd['end_time'] = ($params['end_time'] !== null && $params['end_time'] !== '') ? (int) $params['end_time'] : 0;
|
||||
return $this->saveAdd($saveAdd);
|
||||
} else {
|
||||
$saveUpdate['ad_material'] = $params['ad_material'];
|
||||
@@ -86,8 +86,13 @@ class OpenScreenAdServices
|
||||
$saveUpdate['waiting_time'] = $params['waiting_time'];
|
||||
$saveUpdate['ad_url'] = $params['ad_url'];
|
||||
$saveUpdate['app'] = $params['app'];
|
||||
$saveUpdate['start_time'] = $params['start_time'];
|
||||
$saveUpdate['end_time'] = $params['end_time'];
|
||||
// 没传 start_time / end_time 时不更新,避免每次保存都写成 0
|
||||
if ($params['start_time'] !== null && $params['start_time'] !== '') {
|
||||
$saveUpdate['start_time'] = (int) $params['start_time'];
|
||||
}
|
||||
if ($params['end_time'] !== null && $params['end_time'] !== '') {
|
||||
$saveUpdate['end_time'] = (int) $params['end_time'];
|
||||
}
|
||||
$saveUpdate['updated'] = time();
|
||||
|
||||
return $this->saveUpdate($company_id, $saveUpdate);
|
||||
|
||||
@@ -1401,7 +1401,7 @@ class PagesTemplateServices
|
||||
if (!$distributor_ids) {
|
||||
break;
|
||||
}
|
||||
$distributor_list = $distributorService->entityRepository->getLists(['distributor_id' => $distributor_ids], 'distributor_id, name, logo, first_letter, tag_name, tag_start_time, tag_end_time');
|
||||
$distributor_list = $distributorService->entityRepository->getLists(['distributor_id' => $distributor_ids], 'distributor_id, name, logo, first_letter');
|
||||
if (!$distributor_list) {
|
||||
break;
|
||||
}
|
||||
@@ -1421,10 +1421,6 @@ class PagesTemplateServices
|
||||
if (!$distributor_info) {
|
||||
continue;
|
||||
}
|
||||
//店铺标签不在有效期内
|
||||
if (intval($distributor_info['tag_start_time']) > time() or intval($distributor_info['tag_end_time']) < time()) {
|
||||
$distributor_info['tag_name'] = '';
|
||||
}
|
||||
$child_data_v = array_merge($child_data_v, $distributor_info);
|
||||
$child_v['data'][$child_data_k] = $child_data_v;
|
||||
}
|
||||
@@ -1445,7 +1441,7 @@ class PagesTemplateServices
|
||||
if (!$distributor_ids) {
|
||||
break;
|
||||
}
|
||||
$distributor_list = $distributorService->entityRepository->getLists(['distributor_id' => $distributor_ids], 'distributor_id, name, logo, first_letter, tag_name, tag_start_time, tag_end_time');
|
||||
$distributor_list = $distributorService->entityRepository->getLists(['distributor_id' => $distributor_ids], 'distributor_id, name, logo, first_letter');
|
||||
if (!$distributor_list) {
|
||||
break;
|
||||
}
|
||||
@@ -1456,10 +1452,6 @@ class PagesTemplateServices
|
||||
if (!$distributor_info) {
|
||||
continue;
|
||||
}
|
||||
//店铺标签不在有效期内
|
||||
if (intval($distributor_info['tag_start_time']) > time() or intval($distributor_info['tag_end_time']) < time()) {
|
||||
$distributor_info['tag_name'] = '';
|
||||
}
|
||||
$tmp_v = array_merge($tmp_v, $distributor_info);
|
||||
$params['data'][$tmp_k] = $tmp_v;
|
||||
}
|
||||
|
||||
@@ -761,7 +761,7 @@ class Wxa extends Controller
|
||||
|
||||
$list = $settingService->getTemplateConf($companyId, $templateName, $pageName, $name, $version);
|
||||
|
||||
if (!isset($list[0]['params']['is_open'])) {
|
||||
if (!empty($list) && !isset($list[0]['params']['is_open'])) {
|
||||
$list[0]['params']['is_open'] = true;
|
||||
}
|
||||
|
||||
@@ -1265,5 +1265,4 @@ class Wxa extends Controller
|
||||
return $this->response->array($response);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
83
tests/GoodsBatchItemsActionTest.php
Normal file
83
tests/GoodsBatchItemsActionTest.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
use GoodsBundle\Entities\Items as ItemsEntity;
|
||||
use GoodsBundle\Http\FrontApi\V1\Action\Items;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class GoodsBatchItemsActionTest extends TestCase
|
||||
{
|
||||
public function testGetBatchItemsQueriesLightFieldsByItemIds(): void
|
||||
{
|
||||
$expected = [
|
||||
[
|
||||
'item_id' => 1,
|
||||
'item_name' => '商品A',
|
||||
'price' => 1000,
|
||||
'pics' => ['/a.jpg'],
|
||||
],
|
||||
[
|
||||
'item_id' => 2,
|
||||
'item_name' => '商品B',
|
||||
'price' => 2000,
|
||||
'pics' => ['/b.jpg'],
|
||||
],
|
||||
];
|
||||
|
||||
$repository = $this->getMockBuilder(\stdClass::class)
|
||||
->addMethods(['getLists'])
|
||||
->getMock();
|
||||
$repository->expects($this->once())
|
||||
->method('getLists')
|
||||
->with(['item_id' => [1, 2, 3]], 'item_id,item_name,price,pics', 1, -1, [])
|
||||
->willReturn($expected);
|
||||
|
||||
$this->bindItemsRepository($repository);
|
||||
|
||||
$request = Request::create('/h5app/wxapp/goods/items/batch', 'GET', [
|
||||
'item_ids' => '1,2,3',
|
||||
]);
|
||||
$request->attributes->set('auth', ['company_id' => 1]);
|
||||
|
||||
$response = (new Items())->getBatchItems($request);
|
||||
|
||||
$this->assertSame($expected, $response->getOriginalContent());
|
||||
}
|
||||
|
||||
public function testGetBatchItemsReturnsEmptyArrayWhenItemIdsEmpty(): void
|
||||
{
|
||||
$repository = $this->getMockBuilder(\stdClass::class)
|
||||
->addMethods(['getLists'])
|
||||
->getMock();
|
||||
$repository->expects($this->never())->method('getLists');
|
||||
|
||||
$this->bindItemsRepository($repository);
|
||||
|
||||
$request = Request::create('/h5app/wxapp/goods/items/batch', 'GET', [
|
||||
'item_ids' => '',
|
||||
]);
|
||||
$request->attributes->set('auth', ['company_id' => 1]);
|
||||
|
||||
$response = (new Items())->getBatchItems($request);
|
||||
|
||||
$this->assertSame([], $response->getOriginalContent());
|
||||
}
|
||||
|
||||
private function bindItemsRepository($repository): void
|
||||
{
|
||||
$manager = $this->getMockBuilder(\stdClass::class)
|
||||
->addMethods(['getRepository'])
|
||||
->getMock();
|
||||
$manager->method('getRepository')
|
||||
->with(ItemsEntity::class)
|
||||
->willReturn($repository);
|
||||
|
||||
$registry = $this->getMockBuilder(\stdClass::class)
|
||||
->addMethods(['getManager'])
|
||||
->getMock();
|
||||
$registry->method('getManager')
|
||||
->with('default')
|
||||
->willReturn($manager);
|
||||
|
||||
$this->app->instance('registry', $registry);
|
||||
}
|
||||
}
|
||||
@@ -43,4 +43,106 @@ final class StoreHomePageServiceInternalNameTest extends TestCase
|
||||
$this->assertFalse(StoreHomePageService::pageTemplateDetailHasNonEmptyList(['list' => []]));
|
||||
$this->assertTrue(StoreHomePageService::pageTemplateDetailHasNonEmptyList(['list' => [['x' => 1]]]));
|
||||
}
|
||||
|
||||
public function testCustomDecorationSettingVersionCandidatesForShopIncludesV101Fallback(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
['shop_12', 'v1.0.1'],
|
||||
StoreHomePageService::customDecorationSettingVersionCandidates(12, 0)
|
||||
);
|
||||
}
|
||||
|
||||
public function testCustomDecorationSettingVersionCandidatesForHeadquarters(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
['v1.0.1'],
|
||||
StoreHomePageService::customDecorationSettingVersionCandidates(0, 0)
|
||||
);
|
||||
}
|
||||
|
||||
public function testDecorationTemplateNameCandidatesFallsBackToYykweishop(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
['my_theme', 'yykweishop'],
|
||||
StoreHomePageService::decorationTemplateNameCandidates(['template_name' => 'my_theme'])
|
||||
);
|
||||
}
|
||||
|
||||
public function testPagesTemplateListSearchPlansForShop(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
[
|
||||
['distributor_id' => 8, 'weapp_pages' => 'distributor_index'],
|
||||
['distributor_id' => 8, 'weapp_pages' => 'index'],
|
||||
['distributor_id' => 0, 'weapp_pages' => 'index'],
|
||||
],
|
||||
StoreHomePageService::pagesTemplateListSearchPlans(8)
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildPageTemplateDetailFromTemplateConfList(): void
|
||||
{
|
||||
$detail = StoreHomePageService::buildPageTemplateDetailFromTemplateConfList([
|
||||
['name' => 'slider', 'params' => ['name' => 'slider', 'base' => [], 'data' => []]],
|
||||
['name' => 'plain', 'params' => ['title' => 'x']],
|
||||
]);
|
||||
$this->assertCount(1, $detail['config']);
|
||||
$this->assertCount(2, $detail['list']);
|
||||
}
|
||||
|
||||
public function testSafeDecodeWeappSettingParamsSupportsSerializeAndJson(): void
|
||||
{
|
||||
$payload = ['name' => 'page', 'base' => []];
|
||||
$this->assertSame($payload, StoreHomePageService::safeDecodeWeappSettingParams(serialize($payload)));
|
||||
$this->assertSame($payload, StoreHomePageService::safeDecodeWeappSettingParams(json_encode($payload)));
|
||||
}
|
||||
|
||||
public function testBuildTemplateConfListFromWeappSettingEntities(): void
|
||||
{
|
||||
$payload = ['name' => 'page', 'base' => []];
|
||||
$entity = new class($payload) {
|
||||
private $params;
|
||||
|
||||
public function __construct(array $params)
|
||||
{
|
||||
$this->params = serialize($params);
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return 53453;
|
||||
}
|
||||
|
||||
public function getTemplateName()
|
||||
{
|
||||
return 'yykweishop';
|
||||
}
|
||||
|
||||
public function getCompanyId()
|
||||
{
|
||||
return 34;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'page';
|
||||
}
|
||||
|
||||
public function getPageName()
|
||||
{
|
||||
return 'custom_106';
|
||||
}
|
||||
|
||||
public function getParams()
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
};
|
||||
|
||||
$list = StoreHomePageService::buildTemplateConfListFromWeappSettingEntities([$entity], 34, 'yykweishop');
|
||||
$this->assertCount(1, $list);
|
||||
$this->assertSame('page', $list[0]['name']);
|
||||
$this->assertSame($payload, $list[0]['params']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user