Compare commits
46 Commits
v0.0.1-1
...
test/weixi
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a8e11414f | |||
| 46135002f9 | |||
| 72f6b341a1 | |||
| 0cb2dfa647 | |||
| 0443cc46ec | |||
| 85cf52b0e4 | |||
| c7aaa1bd16 | |||
| 0519879ebb | |||
| f461fe93f0 | |||
| 2eb98efe61 | |||
| 866ca8d938 | |||
| 267cfa12a2 | |||
| d7df9293bb | |||
| 604917b56c | |||
| 840ce860ea | |||
| 04e1735028 | |||
| ba829c4f53 | |||
| b266023afc | |||
| 23abd0496b | |||
| dffb2563be | |||
| 80998fba70 | |||
| 1f65bb61dd | |||
| 9e044672ee | |||
| 989494af6d | |||
| 5d8c3ba4ce | |||
| a973ce720e | |||
| 218d405f56 | |||
| 2d07082b81 | |||
| 15720c6a62 | |||
| 224c2a3117 | |||
| e2d6a02860 | |||
| 1f8504f002 | |||
| 9ff2492962 | |||
| 88355a3f48 | |||
| 3effc03a19 | |||
| be65996398 | |||
| c58bf929b1 | |||
| ec1b93e473 | |||
| 8a78cea36d | |||
| 96f0d9b602 | |||
| ee884a3737 | |||
| 326dbb1c3c | |||
| f03dbbfc60 | |||
| c1170aafa8 | |||
| 198871d3c9 | |||
| caaf85290f |
3
.env
@@ -7,8 +7,6 @@ APP_ENV=development
|
||||
# PHP/PHP-FPM 配置
|
||||
PHP_VERSION=7.4
|
||||
PHP_FPM_VERSION=7.4-fpm
|
||||
PHP_FPM_PORT=9100
|
||||
XDEBUG_POST=9103
|
||||
|
||||
# 数据库配置
|
||||
MYSQL_ROOT_HOST=%
|
||||
@@ -23,5 +21,4 @@ REDIS_PORT=6399
|
||||
|
||||
# Nginx 配置
|
||||
NGINX_PORT=8010
|
||||
NGINX_SSL_PORT=8012
|
||||
|
||||
|
||||
24
.env.development
Normal file
@@ -0,0 +1,24 @@
|
||||
# 项目配置, 请根据实际情况修改
|
||||
PROJECT_NAME=newshop
|
||||
|
||||
# ThinkPHP 6.x 配置, 请根据实际情况修改
|
||||
APP_ENV=development
|
||||
|
||||
# PHP/PHP-FPM 配置
|
||||
PHP_VERSION=7.4
|
||||
PHP_FPM_VERSION=7.4-fpm
|
||||
|
||||
# 数据库配置
|
||||
MYSQL_ROOT_HOST=%
|
||||
MYSQL_DATABASE=shop_mallnew
|
||||
MYSQL_USER=shop_mallnew
|
||||
MYSQL_PASSWORD=shop_mallnew
|
||||
MYSQL_PORT=3326
|
||||
|
||||
# Redis 绑定端口及密码
|
||||
REDIS_PASSWORD=luckyshop123!@#
|
||||
REDIS_PORT=6499
|
||||
|
||||
# Nginx 暴漏端口
|
||||
NGINX_PORT=8050
|
||||
|
||||
13
.env.example
@@ -1,24 +1,23 @@
|
||||
# 项目配置, 请根据实际情况修改
|
||||
PROJECT_NAME=newshop
|
||||
|
||||
# ThinkPHP 6.x 配置, 请根据实际情况修改
|
||||
APP_ENV=development
|
||||
|
||||
# PHP/PHP-FPM 配置
|
||||
PHP_VERSION=7.4
|
||||
PHP_FPM_VERSION=7.4-fpm
|
||||
PHP_FPM_PORT=9000
|
||||
XDEBUG_POST=9003
|
||||
|
||||
# 数据库配置
|
||||
MYSQL_ROOT_HOST=%
|
||||
MYSQL_DATABASE=shop_mallnew
|
||||
MYSQL_USER=shop_mallnew
|
||||
MYSQL_PASSWORD=shop_mallnew
|
||||
MYSQL_PORT=3306
|
||||
MYSQL_PORT=3316
|
||||
|
||||
# Redis 配置
|
||||
REDIS_PASSWORD=luckyshop123!@#
|
||||
REDIS_PORT=6379
|
||||
REDIS_PORT=6399
|
||||
|
||||
# Nginx 配置
|
||||
NGINX_PORT=80
|
||||
NGINX_SSL_PORT=443
|
||||
|
||||
NGINX_PORT=8010
|
||||
|
||||
24
.env.production
Normal file
@@ -0,0 +1,24 @@
|
||||
# 项目配置, 请根据实际情况修改
|
||||
PROJECT_NAME=newshop
|
||||
|
||||
# ThinkPHP 6.x 配置, 请根据实际情况修改
|
||||
APP_ENV=production
|
||||
|
||||
# PHP/PHP-FPM 配置
|
||||
PHP_VERSION=7.4
|
||||
PHP_FPM_VERSION=7.4-fpm
|
||||
|
||||
# 数据库配置
|
||||
MYSQL_ROOT_HOST=%
|
||||
MYSQL_DATABASE=shop_mallnew
|
||||
MYSQL_USER=shop_mallnew
|
||||
MYSQL_PASSWORD=shop_mallnew
|
||||
MYSQL_PORT=3926
|
||||
|
||||
# Redis 绑定端口及密码
|
||||
REDIS_PASSWORD=luckyshop123!@#
|
||||
REDIS_PORT=6829
|
||||
|
||||
# Nginx 暴漏端口
|
||||
NGINX_PORT=8858
|
||||
|
||||
24
.env.staging
Normal file
@@ -0,0 +1,24 @@
|
||||
# 项目配置, 请根据实际情况修改
|
||||
PROJECT_NAME=newshop
|
||||
|
||||
# ThinkPHP 6.x 配置, 请根据实际情况修改
|
||||
APP_ENV=staging
|
||||
|
||||
# PHP/PHP-FPM 配置
|
||||
PHP_VERSION=7.4
|
||||
PHP_FPM_VERSION=7.4-fpm
|
||||
|
||||
# 数据库配置
|
||||
MYSQL_ROOT_HOST=%
|
||||
MYSQL_DATABASE=shop_mallnew
|
||||
MYSQL_USER=shop_mallnew
|
||||
MYSQL_PASSWORD=shop_mallnew
|
||||
MYSQL_PORT=3826
|
||||
|
||||
# Redis 绑定端口及密码
|
||||
REDIS_PASSWORD=luckyshop123!@#
|
||||
REDIS_PORT=6809
|
||||
|
||||
# Nginx 暴漏端口
|
||||
NGINX_PORT=8854
|
||||
|
||||
24
.env.test
Normal file
@@ -0,0 +1,24 @@
|
||||
# 项目配置, 请根据实际情况修改
|
||||
PROJECT_NAME=newshop
|
||||
|
||||
# ThinkPHP 6.x 配置, 请根据实际情况修改
|
||||
APP_ENV=test
|
||||
|
||||
# PHP/PHP-FPM 配置
|
||||
PHP_VERSION=7.4
|
||||
PHP_FPM_VERSION=7.4-fpm
|
||||
|
||||
# 数据库配置
|
||||
MYSQL_ROOT_HOST=%
|
||||
MYSQL_DATABASE=shop_mallnew
|
||||
MYSQL_USER=shop_mallnew
|
||||
MYSQL_PASSWORD=shop_mallnew
|
||||
MYSQL_PORT=3346
|
||||
|
||||
# Redis 绑定端口及密码
|
||||
REDIS_PASSWORD=luckyshop123!@#
|
||||
REDIS_PORT=6799
|
||||
|
||||
# Nginx 暴漏端口
|
||||
NGINX_PORT=8360
|
||||
|
||||
3
.gitignore
vendored
@@ -18,6 +18,9 @@ __pycache__
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
# 环境变量
|
||||
.env
|
||||
|
||||
# 源码结构
|
||||
debug.txt
|
||||
.travis.yml
|
||||
|
||||
138
README.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# 在线商城PHP项目
|
||||
|
||||
## Git 分支策略
|
||||
|
||||
| 环境 | 推荐分支 | 备选分支 | 说明 |
|
||||
|------|----------|----------|------|
|
||||
| local | `dev` | `develop` | 本地开发环境 |
|
||||
| development | `dev` | `development` | 开发测试环境 |
|
||||
| test | `test` | `staging` | 测试环境 |
|
||||
| staging | `staging` | `pre-release` | 预发布环境 |
|
||||
| production | `master` | `main` | 生产环境 |
|
||||
|
||||
**部署建议**:
|
||||
- 每个环境部署前请先切换到对应的Git分支
|
||||
- 确保代码版本与目标环境匹配
|
||||
- 生产环境部署前建议先在staging环境验证
|
||||
|
||||
## Docker 部署
|
||||
|
||||
```bash
|
||||
cp .env.example .env.development
|
||||
```
|
||||
|
||||
**注意**
|
||||
|
||||
- 在同一目录下面,执行 `docker-compose` 命令时,需要指定项目名称。用来区分不同的环境。如 `shop_local`、`shop_dev` 等。
|
||||
- 本地部署时,需要将 `APP_ENV` 设置为 `local`。
|
||||
- 开发环境部署时,需要将 `APP_ENV` 设置为 `development`。
|
||||
|
||||
|
||||
|
||||
## 环境变量
|
||||
|
||||
- `APP_ENV`: 应用环境,默认值为 `development`。
|
||||
|
||||
## 开发环境-local 部署
|
||||
|
||||
**对应Git分支**: `main` 或 `develop`
|
||||
|
||||
```bash
|
||||
# 切换到本地开发分支
|
||||
git checkout main # 或 develop
|
||||
|
||||
# 本地部署时,需要将 APP_ENV 设置为 local, 并指定 docker-compose.local.yml 文件
|
||||
docker-compose --env-file .env.local -f docker-compose.local.yml up -d
|
||||
# docker-compose --project-name shop_local --env-file .env.local -f docker-compose.local.yml up -d
|
||||
|
||||
# docker-compose down 命令,用来停止并删除容器
|
||||
docker-compose -f docker-compose.local.yml down -v
|
||||
# docker-compose --project-name shop_local down -v
|
||||
```
|
||||
|
||||
|
||||
## 开发环境-development 部署
|
||||
|
||||
**对应Git分支**: `dev` 或 `development`
|
||||
|
||||
```bash
|
||||
# 切换到开发分支
|
||||
git checkout dev # 或 development
|
||||
|
||||
# 默认使用 docker-compose.yml 文件
|
||||
docker-compose --project-name shop_development --env-file .env.development up -d
|
||||
|
||||
# docker-compose down 命令,用来停止并删除容器
|
||||
docker-compose --project-name shop_development down -v
|
||||
```
|
||||
|
||||
## 开发环境-test 部署 (测试环境)
|
||||
|
||||
**对应Git分支**: `test` 或 `staging`
|
||||
|
||||
```bash
|
||||
# 切换到测试分支
|
||||
git checkout test # 或 staging
|
||||
|
||||
# 默认使用 docker-compose.yml 文件
|
||||
docker-compose --project-name shop_test --env-file .env.test up -d
|
||||
|
||||
# docker-compose down 命令,用来停止并删除容器
|
||||
docker-compose --project-name shop_test down -v
|
||||
```
|
||||
|
||||
## 开发环境-staging 部署(预发布环境)
|
||||
|
||||
**对应Git分支**: `staging` 或 `pre-release`
|
||||
|
||||
```bash
|
||||
# 切换到预发布分支
|
||||
git checkout staging # 或 pre-release
|
||||
|
||||
# 默认使用 docker-compose.yml 文件
|
||||
docker-compose --project-name shop_staging --env-file .env.staging up -d
|
||||
|
||||
# docker-compose down 命令,用来停止并删除容器
|
||||
docker-compose --project-name shop_staging down -v
|
||||
```
|
||||
|
||||
## 生产环境-production 部署
|
||||
|
||||
**对应Git分支**: `master` 或 `main` 或 `production`
|
||||
|
||||
```bash
|
||||
# 切换到生产分支
|
||||
git checkout main
|
||||
|
||||
# 确保代码是最新的生产版本
|
||||
git pull origin main
|
||||
|
||||
# 默认使用 docker-compose.yml 文件
|
||||
docker-compose --project-name shop_production --env-file .env.production up -d
|
||||
|
||||
# docker-compose down 命令,用来停止并删除容器
|
||||
docker-compose --project-name shop_production down -v
|
||||
```
|
||||
|
||||
## 便捷部署脚本
|
||||
|
||||
### 环境切换与部署脚本
|
||||
|
||||
- `deploy.sh` 脚本:
|
||||
|
||||
|
||||
### 使用方法
|
||||
|
||||
```bash
|
||||
# 赋予执行权限
|
||||
chmod +x deploy.sh
|
||||
|
||||
# 部署到开发环境
|
||||
./deploy.sh development
|
||||
|
||||
# 部署到测试环境
|
||||
./deploy.sh test
|
||||
|
||||
# 部署到生产环境
|
||||
./deploy.sh production
|
||||
```
|
||||
100
deploy.sh
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
# deploy.sh - 环境切换与部署脚本
|
||||
# 使用方法: ./deploy.sh <environment>
|
||||
# 示例: ./deploy.sh development
|
||||
|
||||
ENVIRONMENT=$1
|
||||
PROJECT_NAME="shop_${ENVIRONMENT}"
|
||||
BRANCH=""
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
ENV_FILE=".env.${ENVIRONMENT}"
|
||||
|
||||
# 现实运行中的服务器网站,数据库备份目录
|
||||
RUN_SERVER_WEB_ROOT="/data/wwwroot/shop-projects"
|
||||
RUN_SERVER_DB_BACKUP_DIR="/data/backup/shop-projects"
|
||||
|
||||
case $ENVIRONMENT in
|
||||
"local")
|
||||
BRANCH="dev"
|
||||
COMPOSE_FILE="docker-compose.local.yml"
|
||||
ENV_FILE=".env.local"
|
||||
;;
|
||||
"development")
|
||||
BRANCH="dev"
|
||||
;;
|
||||
"test")
|
||||
BRANCH="test"
|
||||
;;
|
||||
"staging")
|
||||
BRANCH="staging"
|
||||
;;
|
||||
"production")
|
||||
BRANCH="main"
|
||||
;;
|
||||
*)
|
||||
echo "错误: 不支持的环境 '$ENVIRONMENT'"
|
||||
echo "支持的环境: local, development, test, staging, production"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "=========================================="
|
||||
echo "部署环境: $ENVIRONMENT"
|
||||
echo "项目名称: $PROJECT_NAME"
|
||||
echo "Git分支: $BRANCH"
|
||||
echo "配置文件: $COMPOSE_FILE"
|
||||
echo "环境文件: $ENV_FILE"
|
||||
echo "=========================================="
|
||||
|
||||
# 切换分支
|
||||
echo "切换到Git分支: $BRANCH"
|
||||
git checkout $BRANCH
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "错误: 无法切换到分支 $BRANCH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 拉取最新代码
|
||||
echo "拉取最新代码..."
|
||||
git pull origin $BRANCH
|
||||
|
||||
# 根据不同的环境,执行不同的操作
|
||||
# 预发布环境、生产环境都需要使用运行服务上的用户文件,将用户文件复制到指定目录
|
||||
|
||||
|
||||
|
||||
# 根据不同的环境,执行不同的操作
|
||||
# 预发布环境、生产环境都需要还原数据库,使用数据库备份文件,并尝试使用数据库升级
|
||||
if [ "$ENVIRONMENT" = "local" ]; then
|
||||
echo "本地环境,跳过数据库还原"
|
||||
else
|
||||
echo "还原数据库..."
|
||||
# 还原数据库
|
||||
docker-compose --project-name $PROJECT_NAME exec -T db bash -c "mysql -uroot -p$DB_ROOT_PASSWORD shop < $DB_BACKUP_DIR/shop.sql"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "错误: 数据库还原失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 尝试使用数据库升级脚本
|
||||
echo "尝试使用数据库升级..."
|
||||
docker-compose --project-name $PROJECT_NAME exec -T db bash -c "php artisan migrate --force"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "数据库升级失败"
|
||||
else
|
||||
echo "数据库升级成功"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 构建并启动容器
|
||||
echo "构建并启动Docker容器..."
|
||||
if [ "$ENVIRONMENT" = "local" ]; then
|
||||
docker-compose --env-file $ENV_FILE -f $COMPOSE_FILE up -d
|
||||
else
|
||||
docker-compose --project-name $PROJECT_NAME --env-file $ENV_FILE up -d
|
||||
fi
|
||||
|
||||
echo "部署完成!"
|
||||
echo "查看容器状态: docker-compose --project-name $PROJECT_NAME ps"
|
||||
echo "查看日志: docker-compose --project-name $PROJECT_NAME logs -f"
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
build:
|
||||
context: ./docker/php
|
||||
dockerfile: Dockerfile
|
||||
container_name: ${PROJECT_NAME}_php
|
||||
container_name: ${PROJECT_NAME}_${APP_ENV}_php
|
||||
restart: always
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway" # 支持主机名解析
|
||||
@@ -25,23 +25,20 @@ services:
|
||||
# 不然,ThinkPHP 6.x 系列,会只加载 .env 文件,而不会加载 .env.local 文件,导致 .env.local 文件中的配置不会生效
|
||||
APP_ENV: ${APP_ENV:-development}
|
||||
APP_DEBUG: ${APP_DEBUG:-true}
|
||||
XDEBUG_CONFIG: ${XDEBUG_CONFIG:-client_host=host.docker.internal client_port=9003}
|
||||
PHP_IDE_CONFIG: serverName=docker-php
|
||||
ports:
|
||||
- "${PHP_FPM_PORT:-9000}:9000" # PHP-FPM
|
||||
- "${XDEBUG_POST:-9003}:9003" # Xdebug
|
||||
# PHP应用根目录(可选,默认 /var/www/html)
|
||||
PHP_APP_ROOT: ${PHP_APP_ROOT:-/var/www/html}
|
||||
# 用户ID映射(可选,用于解决挂载权限问题)
|
||||
USER_ID: ${USER_ID:-33}
|
||||
GROUP_ID: ${GROUP_ID:-33}
|
||||
volumes:
|
||||
- ./:/var/www/all_source
|
||||
- ./src:/var/www/html
|
||||
- ./src:/var/www/html:rw
|
||||
# 更新下载源列表以加速apt-get
|
||||
- ./docker/debian/sources.list:/etc/apt/sources.list:ro
|
||||
- ./docker/php/php.ini:/usr/local/etc/php/php.ini:ro
|
||||
- ./docker/php/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini
|
||||
- xdebug_logs:/tmp # Xdebug 日志目录
|
||||
depends_on:
|
||||
- db
|
||||
healthcheck:
|
||||
test: ["CMD", "bash", "-c", "curl -f http://localhost:9000/status && ps aux | grep '[p]hp think cron:schedule'"]
|
||||
test: ["CMD", "bash", "-c", "curl -f http://localhost:9000/status"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
@@ -49,19 +46,19 @@ services:
|
||||
networks:
|
||||
- sass-platform-net
|
||||
labels:
|
||||
- "com.docker.compose.project.working_dir=${PROJECT_NAME}"
|
||||
- "com.docker.compose.project.working_dir=${PROJECT_NAME}_${APP_ENV}"
|
||||
|
||||
nginx:
|
||||
build:
|
||||
context: ./docker/nginx
|
||||
dockerfile: Dockerfile
|
||||
container_name: ${PROJECT_NAME}_nginx
|
||||
container_name: ${PROJECT_NAME}_${APP_ENV}_nginx
|
||||
restart: always
|
||||
ports:
|
||||
- "${NGINX_PORT:-80}:80"
|
||||
- "${NGINX_SSL_PORT:-443}:443"
|
||||
volumes:
|
||||
- ./src:/var/www/html:ro
|
||||
# 挂载项目代码到 Nginx 容器中
|
||||
- ./src:/var/www/html:rw
|
||||
# 更新下载源列表以加速apt-get
|
||||
- ./docker/debian/sources.list:/etc/apt/sources.list:ro
|
||||
# 创建临时目录
|
||||
@@ -72,11 +69,11 @@ services:
|
||||
networks:
|
||||
- sass-platform-net
|
||||
labels:
|
||||
- "com.docker.compose.project.working_dir=${PROJECT_NAME}"
|
||||
- "com.docker.compose.project.working_dir=${PROJECT_NAME}_${APP_ENV}"
|
||||
|
||||
db:
|
||||
image: mysql:5.7.44
|
||||
container_name: ${PROJECT_NAME}_mysql
|
||||
container_name: ${PROJECT_NAME}_${APP_ENV}_mysql
|
||||
environment:
|
||||
<<: *shared-api-env
|
||||
volumes:
|
||||
@@ -93,12 +90,12 @@ services:
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --innodb_buffer_pool_size=256M
|
||||
labels:
|
||||
- "com.docker.compose.project.working_dir=${PROJECT_NAME}"
|
||||
- "com.docker.compose.project.working_dir=${PROJECT_NAME}_${APP_ENV}"
|
||||
|
||||
# Redis 服务(可选)
|
||||
redis:
|
||||
image: redis:8.2
|
||||
container_name: ${PROJECT_NAME}_redis
|
||||
container_name: ${PROJECT_NAME}_${APP_ENV}_redis
|
||||
environment:
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-luckyshop123!@#}
|
||||
REDISCLI_AUTH: ${REDIS_PASSWORD:-luckyshop123!@#}
|
||||
@@ -111,13 +108,26 @@ services:
|
||||
- sass-platform-net
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "com.docker.compose.project.working_dir=${PROJECT_NAME}"
|
||||
- "com.docker.compose.project.working_dir=${PROJECT_NAME}_${APP_ENV}"
|
||||
|
||||
volumes:
|
||||
mysql_db_data:
|
||||
name: ${PROJECT_NAME}_${APP_ENV}_mysql_db_data
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ./docker/mysql_db_data/${APP_ENV}
|
||||
redis_data:
|
||||
xdebug_logs:
|
||||
name: ${PROJECT_NAME}_${APP_ENV}_redis_data
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ./docker/redis_data/${APP_ENV}
|
||||
|
||||
|
||||
networks:
|
||||
sass-platform-net:
|
||||
name: ${PROJECT_NAME}_${APP_ENV}_net
|
||||
driver: bridge
|
||||
@@ -2,6 +2,8 @@
|
||||
# 字符集设置
|
||||
character-set-server=utf8mb4
|
||||
collation-server=utf8mb4_unicode_ci
|
||||
init_connect='SET NAMES utf8mb4'
|
||||
|
||||
|
||||
# 连接设置
|
||||
max_connections=100
|
||||
@@ -22,6 +24,3 @@ sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
|
||||
|
||||
[client]
|
||||
default-character-set=utf8mb4
|
||||
|
||||
[mysql]
|
||||
default-character-set=utf8mb4
|
||||
9
docker/mysql_db_data/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# 忽略所有文件
|
||||
*
|
||||
|
||||
# 只保留指定的 .gitkeep 文件
|
||||
!.gitignore
|
||||
!development/.gitkeep
|
||||
!test/.gitkeep
|
||||
!production/.gitkeep
|
||||
!staging/.gitkeep
|
||||
0
docker/mysql_db_data/.gitkeep
Normal file
@@ -3,27 +3,19 @@ FROM nginx:alpine
|
||||
# 删除默认配置
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
|
||||
#
|
||||
# - ./.docker/nginx/conf.c:/etc/nginx/conf.c:ro
|
||||
# - ./.docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
# - ./.docker/nginx/sites-enabled:/etc/nginx/sites-enabled:ro
|
||||
# 将本地 nginx 配置复制到镜像中并设置为只读
|
||||
COPY ./conf.c /etc/nginx/conf.c
|
||||
|
||||
# 将本地 nginx 配置复制到镜像中
|
||||
COPY ./conf.c/ /etc/nginx/conf.c/
|
||||
COPY ./default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY ./sites-enabled /etc/nginx/sites-enabled
|
||||
|
||||
# 设置只读权限(文件 0444,目录及其内容 0555)
|
||||
RUN chmod 0444 /etc/nginx/conf.c \
|
||||
&& chmod 0444 /etc/nginx/conf.d/default.conf \
|
||||
&& chmod -R 0555 /etc/nginx/sites-enabled
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# 创建日志目录
|
||||
RUN mkdir -p /var/log/nginx
|
||||
COPY ./sites-enabled/ /etc/nginx/sites-enabled/
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 80 443
|
||||
|
||||
# 添加在Dockerfile末尾,CMD命令之前
|
||||
COPY ./entrypoint.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
|
||||
# 启动nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
14
docker/nginx/conf.c/enable-websocket.conf
Normal file
@@ -0,0 +1,14 @@
|
||||
location /ws {
|
||||
proxy_pass http://php-fpm:8080; # 注意:这里用的是 Docker 服务名或容器名
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 可选:设置超时(WebSocket 是长连接)
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
}
|
||||
18
docker/nginx/entrypoint.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== NGINX Docker 权限初始化 ==="
|
||||
|
||||
# 设置权限
|
||||
chmod -R 0444 /etc/nginx/conf.c
|
||||
chmod 0444 /etc/nginx/conf.d/default.conf
|
||||
chmod -R 0755 /etc/nginx/sites-enabled
|
||||
|
||||
|
||||
# 创建日志目录
|
||||
mkdir -p /var/log/nginx
|
||||
|
||||
echo "=== NGINX Docker 权限初始化完成 ==="
|
||||
|
||||
# 执行原有的启动命令
|
||||
exec "$@"
|
||||
@@ -20,6 +20,9 @@
|
||||
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
# --- SSL configuration end ---
|
||||
|
||||
# 启用 WebSocket 支持
|
||||
include conf.c/enable-websocket.conf;
|
||||
|
||||
#PHP-INFO-START PHP引用配置,可以注释或修改
|
||||
include conf.c/enable-php-74.conf;
|
||||
#PHP-INFO-END
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# 使用官方PHP镜像
|
||||
FROM php:7.4.33-fpm-dev-newshop
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 9000 9003
|
||||
|
||||
CMD ["php-fpm"]
|
||||
@@ -27,8 +27,14 @@ RUN apt-get update && apt-get install -y \
|
||||
libfreetype6-dev \
|
||||
libjpeg62-turbo-dev \
|
||||
libpng-dev \
|
||||
iputils-ping \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 安装 WebSocat 完成后,清理缓存
|
||||
COPY ./websocat /usr/local/bin/websocat
|
||||
RUN chmod +x /usr/local/bin/websocat
|
||||
|
||||
# 安装 PHP 扩展
|
||||
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||
&& docker-php-ext-install \
|
||||
@@ -45,9 +51,6 @@ RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||
# 安装 Redis 扩展
|
||||
RUN pecl install redis-5.3.7 && docker-php-ext-enable redis
|
||||
|
||||
# 安装 Xdebug(兼容 PHP 7.4 的版本)
|
||||
RUN pecl install xdebug-3.1.6 && docker-php-ext-enable xdebug
|
||||
|
||||
# 安装Composer
|
||||
COPY --from=composer:2.2.25 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
@@ -56,30 +59,15 @@ RUN composer --version
|
||||
|
||||
# 修改 PHP 配置
|
||||
RUN echo "memory_limit=256M" > /usr/local/etc/php/conf.d/memory-limit.ini \
|
||||
&& echo "upload_max_filesize=50M" >> /usr/local/etc/php/conf.d/uploads.ini \
|
||||
&& echo "post_max_size=50M" >> /usr/local/etc/php/conf.d/uploads.ini
|
||||
|
||||
# 创建 Xdebug 配置
|
||||
RUN echo "zend_extension=xdebug.so" > /usr/local/etc/php/conf.d/xdebug.ini
|
||||
&& echo "upload_max_filesize=150M" >> /usr/local/etc/php/conf.d/uploads.ini \
|
||||
&& echo "post_max_size=150M" >> /usr/local/etc/php/conf.d/uploads.ini
|
||||
|
||||
# # 使用Composer安装项目依赖(可选,根据需要启用, 更多的时候,会出错,要在容器中执行操作)
|
||||
# RUN composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
|
||||
# RUN composer install --no-dev --optimize-autoloader --working-dir=/var/www/html
|
||||
|
||||
# # 创建非 root 用户
|
||||
# RUN useradd -m -u 1000 phpuser && chown -R phpuser:phpuser /var/www/html
|
||||
|
||||
# 设置权限, 防止以下目录无法写入的问题
|
||||
RUN chmod -R a+rw /var/www/html/runtime
|
||||
RUN chmod -R a+rw /var/www/html/uploads
|
||||
RUN chmod -R a+rw /var/www/html/tmp
|
||||
RUN chmod -R a+rw /var/www/html/temp
|
||||
|
||||
# USER phpuser
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 9000 9003
|
||||
|
||||
# 暴露端口,9000 为 PHP-FPM 端口,8080 为 WebSocket 端口
|
||||
EXPOSE 9000 8080
|
||||
|
||||
############ 查看 cron 进程
|
||||
## 查看 cron 进程
|
||||
@@ -95,4 +83,10 @@ EXPOSE 9000 9003
|
||||
#######################################
|
||||
|
||||
# 启动Supervisor
|
||||
# 添加在Dockerfile末尾,CMD命令之前
|
||||
COPY ./entrypoint.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
|
||||
# 修改CMD命令
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||
344
docker/php/entrypoint.sh
Normal file
@@ -0,0 +1,344 @@
|
||||
#!/bin/bash
|
||||
# 移除 set -e 以便更好的错误控制
|
||||
|
||||
echo "=== Web应用权限初始化 ==="
|
||||
|
||||
# 定义应用根目录,优先使用环境变量,否则使用默认值
|
||||
APP_ROOT="${PHP_APP_ROOT:-/var/www/html}"
|
||||
|
||||
echo "使用应用根目录: $APP_ROOT"
|
||||
|
||||
# 如果应用根目录不存在,则跳过权限设置
|
||||
if [ ! -d "$APP_ROOT" ]; then
|
||||
echo "❌ 应用根目录:'$APP_ROOT'不存在,跳过权限设置"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建统一的Web组并配置所有用户(最高效的权限管理)
|
||||
configure_web_users() {
|
||||
# 常见Web服务器用户列表
|
||||
WEB_USERS=("www-data" "www" "apache" "nginx")
|
||||
|
||||
# 获取环境变量中的用户ID
|
||||
TARGET_UID=${USER_ID:-33}
|
||||
TARGET_GID=${GROUP_ID:-33}
|
||||
|
||||
echo "配置统一Web组权限,目标UID:GID = $TARGET_UID:$TARGET_GID"
|
||||
|
||||
# 创建统一的Web组(增强错误处理)
|
||||
WEB_GROUP="webaccess"
|
||||
if ! getent group "$WEB_GROUP" &>/dev/null; then
|
||||
echo "创建统一Web组: $WEB_GROUP"
|
||||
|
||||
# 尝试使用指定GID创建组
|
||||
if groupadd -g $TARGET_GID "$WEB_GROUP" 2>/dev/null; then
|
||||
echo "✅ 统一Web组创建成功,GID: $TARGET_GID"
|
||||
else
|
||||
echo "⚠️ GID $TARGET_GID 已被占用,尝试自动分配GID"
|
||||
|
||||
# 尝试不指定GID创建组
|
||||
if groupadd "$WEB_GROUP" 2>/dev/null; then
|
||||
ACTUAL_GID=$(getent group "$WEB_GROUP" | cut -d: -f3)
|
||||
echo "✅ 统一Web组创建成功,自动分配GID: $ACTUAL_GID"
|
||||
else
|
||||
echo "❌ 创建 $WEB_GROUP 组失败,尝试使用备用方案"
|
||||
|
||||
# 备用方案:使用现有的www-data组
|
||||
if getent group "www-data" &>/dev/null; then
|
||||
WEB_GROUP="www-data"
|
||||
echo "🔄 使用现有的www-data组作为统一组"
|
||||
else
|
||||
echo "❌ 备用方案也失败,权限配置可能不完整"
|
||||
WEB_GROUP=""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
ACTUAL_GID=$(getent group "$WEB_GROUP" | cut -d: -f3)
|
||||
echo "✅ 统一Web组 $WEB_GROUP 已存在,GID: $ACTUAL_GID"
|
||||
fi
|
||||
|
||||
# 最终验证组是否存在
|
||||
if [ -z "$WEB_GROUP" ] || ! getent group "$WEB_GROUP" &>/dev/null; then
|
||||
echo "❌ 无法创建或找到可用的Web组,权限配置将受限"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 只将已存在的Web用户加入统一组(增强错误处理)
|
||||
success_count=0
|
||||
total_users=0
|
||||
|
||||
for web_user in "${WEB_USERS[@]}"; do
|
||||
total_users=$((total_users + 1))
|
||||
|
||||
if id "$web_user" &>/dev/null; then
|
||||
echo "📝 处理Web用户: $web_user"
|
||||
|
||||
# 获取用户当前组信息(安全的变量处理)
|
||||
current_groups=$(id -Gn "$web_user" 2>/dev/null | tr '\n' ' ' | sed 's/ *$//')
|
||||
echo " 当前所属组: ${current_groups:-无}"
|
||||
|
||||
# 尝试将用户加入统一组(使用-a参数保留现有组,只添加新组)
|
||||
if usermod -a -G "$WEB_GROUP" "$web_user" 2>/dev/null; then
|
||||
echo " ✅ 成功将 $web_user 添加到统一组 $WEB_GROUP"
|
||||
success_count=$((success_count + 1))
|
||||
else
|
||||
echo " ⚠️ 无法将 $web_user 添加到统一组,尝试设置主组"
|
||||
|
||||
# 备用方案:设置主组
|
||||
if usermod -g "$WEB_GROUP" "$web_user" 2>/dev/null; then
|
||||
echo " ✅ 成功将 $web_user 主组设置为 $WEB_GROUP"
|
||||
success_count=$((success_count + 1))
|
||||
else
|
||||
echo " ❌ 无法配置 $web_user 的组权限"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "⚭ Web用户 $web_user 不存在,跳过"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "📊 用户配置汇总: $success_count/$total_users 个Web用户配置成功"
|
||||
|
||||
# 至少要有一个用户配置成功
|
||||
if [ $success_count -eq 0 ]; then
|
||||
echo "⚠️ 没有Web用户被成功配置,但继续执行"
|
||||
fi
|
||||
|
||||
echo "统一Web组配置完成"
|
||||
}
|
||||
|
||||
# 错误处理:如果配置失败,不要终止整个脚本
|
||||
configure_web_users || echo "⚠️ Web用户配置出现问题,但继续执行权限设置"
|
||||
|
||||
echo "当前用户: $(whoami)"
|
||||
echo "UID: $(id -u), GID: $(id -g)"
|
||||
|
||||
# 修复所有目录权限(使用统一Web组,最高效的权限管理)
|
||||
if [ -d "$APP_ROOT" ]; then
|
||||
# 重新获取最终的WEB_GROUP(可能已被修改)
|
||||
FINAL_WEB_GROUP=""
|
||||
|
||||
# 首选:使用创建的webaccess组
|
||||
if getent group "webaccess" &>/dev/null; then
|
||||
FINAL_WEB_GROUP="webaccess"
|
||||
echo "🎯 使用创建的统一Web组: $FINAL_WEB_GROUP"
|
||||
# 备选:使用www-data组
|
||||
elif getent group "www-data" &>/dev/null; then
|
||||
FINAL_WEB_GROUP="www-data"
|
||||
echo "🔄 回退到www-data组: $FINAL_WEB_GROUP"
|
||||
# 最后:使用当前用户的组
|
||||
else
|
||||
CURRENT_USER=$(whoami)
|
||||
CURRENT_GROUP=$(id -gn "$CURRENT_USER")
|
||||
FINAL_WEB_GROUP="$CURRENT_GROUP"
|
||||
echo "🔧 使用当前用户组: $FINAL_WEB_GROUP"
|
||||
fi
|
||||
|
||||
# 最终验证
|
||||
if [ -z "$FINAL_WEB_GROUP" ]; then
|
||||
echo "❌ 无法确定有效的Web组,跳过权限设置"
|
||||
echo "=== 启动应用 ==="
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
WEB_GROUP="$FINAL_WEB_GROUP"
|
||||
WEB_GROUP_GID=$(getent group "$WEB_GROUP" | cut -d: -f3)
|
||||
echo "✅ 最终使用Web组: $WEB_GROUP (GID: $WEB_GROUP_GID)"
|
||||
echo "🔒 统一组权限模式:所有Web用户通过组继承权限"
|
||||
|
||||
# 设置所有权为统一Web组(增强错误处理)
|
||||
echo "📁 设置应用目录所有权为统一Web组"
|
||||
CURRENT_USER=$(whoami)
|
||||
|
||||
if chown -R $CURRENT_USER:$WEB_GROUP "$APP_ROOT" 2>/dev/null; then
|
||||
echo "✅ 所有权设置成功: $CURRENT_USER:$WEB_GROUP"
|
||||
else
|
||||
echo "⚠️ 所有权设置失败,尝试只设置组权限"
|
||||
chgrp -R "$WEB_GROUP" "$APP_ROOT" 2>/dev/null || echo "❌ 组权限设置也失败"
|
||||
fi
|
||||
|
||||
# 设置目录权限为775(组权限为rwx,所有组内用户都有完整权限)
|
||||
echo "🔐 设置目录权限775,文件权限664"
|
||||
|
||||
# 使用更安全的权限设置方式,避免权限被拒绝
|
||||
dir_count=0
|
||||
file_count=0
|
||||
|
||||
# 设置目录权限(兼容性更好的方式)
|
||||
if command -v find >/dev/null 2>&1; then
|
||||
dir_count=$(find "$APP_ROOT" -type d -exec chmod 775 {} \; 2>/dev/null | wc -l)
|
||||
file_count=$(find "$APP_ROOT" -type f -exec chmod 664 {} \; 2>/dev/null | wc -l)
|
||||
find "$APP_ROOT" -type d -exec chmod g+s {} \; 2>/dev/null
|
||||
else
|
||||
# 备用方案:使用简单的循环
|
||||
echo "find命令不可用,跳过批量权限设置"
|
||||
dir_count=0
|
||||
file_count=0
|
||||
fi
|
||||
|
||||
echo "📊 权限设置完成: $dir_count个目录, $file_count个文件"
|
||||
|
||||
echo "✅ 统一组权限设置完成,所有Web用户通过组获得权限"
|
||||
|
||||
# 设置ACL(如果支持,只需设置统一组)
|
||||
if command -v setfacl >/dev/null 2>&1; then
|
||||
echo "🔒 设置ACL权限(只需设置统一Web组)"
|
||||
|
||||
acl_success=0
|
||||
|
||||
# 只为统一Web组设置ACL权限(限制处理深度)
|
||||
if setfacl -R -m g:$WEB_GROUP:rwx "$APP_ROOT" 2>/dev/null; then
|
||||
echo " ✅ 设置组ACL权限成功"
|
||||
acl_success=$((acl_success + 1))
|
||||
else
|
||||
echo " ❌ 设置组ACL权限失败"
|
||||
fi
|
||||
|
||||
# 设置默认ACL权限(新创建的文件自动继承权限)
|
||||
if setfacl -dR -m g:$WEB_GROUP:rwx "$APP_ROOT" 2>/dev/null; then
|
||||
echo " ✅ 设置默认ACL权限成功"
|
||||
acl_success=$((acl_success + 1))
|
||||
else
|
||||
echo " ❌ 设置默认ACL权限失败"
|
||||
fi
|
||||
|
||||
if [ $acl_success -eq 2 ]; then
|
||||
echo "🎉 统一组ACL设置完成,所有组内用户自动获得权限"
|
||||
elif [ $acl_success -eq 1 ]; then
|
||||
echo "⚠️ ACL部分设置成功,建议检查文件系统ACL支持"
|
||||
else
|
||||
echo "❌ ACL设置完全失败,文件系统可能不支持ACL"
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ ACL不支持,依赖传统权限模式"
|
||||
echo "✅ 775权限已足够,所有组内用户都有rwx权限"
|
||||
fi
|
||||
|
||||
# 设置umask
|
||||
umask 0002
|
||||
|
||||
echo "✅ 应用目录权限修复完成"
|
||||
|
||||
# 验证文件权限是否足够(测试统一组权限效果)
|
||||
echo "=== 验证统一组权限效果 ==="
|
||||
|
||||
# 查找测试文件的更可靠方法
|
||||
test_file=""
|
||||
|
||||
# 方法1: 查找index.html
|
||||
if [ -f "$APP_ROOT/index.html" ]; then
|
||||
test_file="$APP_ROOT/index.html"
|
||||
fi
|
||||
|
||||
# 方法2: 查找任意HTML文件(更安全的方式)
|
||||
if [ -z "$test_file" ]; then
|
||||
first_html=$(find "$APP_ROOT" -maxdepth 2 -name "*.html" -type f 2>/dev/null | head -1)
|
||||
if [ -n "$first_html" ] && [ -f "$first_html" ]; then
|
||||
test_file="$first_html"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 方法3: 查找index.php
|
||||
if [ -z "$test_file" ] && [ -f "$APP_ROOT/index.php" ]; then
|
||||
test_file="$APP_ROOT/index.php"
|
||||
fi
|
||||
|
||||
# 方法4: 创建专用测试文件(最安全的选择)
|
||||
if [ -z "$test_file" ]; then
|
||||
test_file="$APP_ROOT/.permission_test.html"
|
||||
echo "创建专用权限测试文件"
|
||||
cat > "$test_file" << 'EOF'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Permission Test</title></head>
|
||||
<body><h1>Web Server Permission Test File</h1></body>
|
||||
</html>
|
||||
EOF
|
||||
# 设置正确的权限
|
||||
chown $(whoami):"$WEB_GROUP" "$test_file" 2>/dev/null || true
|
||||
chmod 664 "$test_file"
|
||||
fi
|
||||
|
||||
# 执行权限测试
|
||||
if [ -f "$test_file" ]; then
|
||||
echo "使用测试文件: $test_file"
|
||||
echo "文件权限: $(stat -c '%a %n' "$test_file")"
|
||||
echo "文件所有者: $(stat -c '%U:%G' "$test_file")"
|
||||
|
||||
# 测试所有Web用户的权限(通过组权限)
|
||||
for test_user in "www-data" "www" "apache" "nginx"; do
|
||||
if id "$test_user" &>/dev/null; then
|
||||
echo "🔍 测试 $test_user 用户权限(通过组权限):"
|
||||
|
||||
# 显示用户组信息(安全的变量处理)
|
||||
user_groups=$(id -Gn "$test_user" 2>/dev/null | tr '\n' ' ' | sed 's/ *$//')
|
||||
echo " 📋 所属组: ${user_groups:-无}"
|
||||
|
||||
# 测试读权限(安全:只读不修改)
|
||||
if su -s /bin/sh -c "cat '$test_file' >/dev/null 2>&1" "$test_user" 2>/dev/null; then
|
||||
echo " ✅ 读权限: 通过组权限可读"
|
||||
else
|
||||
echo " ❌ 读权限: 不可读"
|
||||
fi
|
||||
|
||||
# 测试写权限(使用临时文件,避免污染原文件)
|
||||
temp_test_file="${test_file}.write_test_${test_user}"
|
||||
if su -s /bin/sh -c "echo 'permission_test' > '$temp_test_file' 2>/dev/null" "$test_user" 2>/dev/null; then
|
||||
echo " ✅ 写权限: 通过组权限可写"
|
||||
rm -f "$temp_test_file" 2>/dev/null
|
||||
else
|
||||
echo " ❌ 写权限: 不可写"
|
||||
fi
|
||||
|
||||
# 测试目录创建权限(使用临时目录)
|
||||
temp_test_dir="${APP_ROOT}/.perm_test_${test_user}"
|
||||
if su -s /bin/sh -c "mkdir -p '$temp_test_dir' 2>/dev/null" "$test_user" 2>/dev/null; then
|
||||
echo " ✅ 创建目录: 通过组权限可创建"
|
||||
rm -rf "$temp_test_dir" 2>/dev/null
|
||||
else
|
||||
echo " ❌ 创建目录: 不可创建"
|
||||
fi
|
||||
|
||||
echo " 🔗 权限来源: 统一Web组 ($WEB_GROUP) 775权限"
|
||||
break # 只测试第一个可用的用户即可验证效果
|
||||
fi
|
||||
done
|
||||
|
||||
# 清理专用测试文件(如果是创建的)
|
||||
if echo "$test_file" | grep -q "\.permission_test\.html$"; then
|
||||
rm -f "$test_file" 2>/dev/null
|
||||
echo "🧹 已清理临时测试文件"
|
||||
fi
|
||||
else
|
||||
echo "❌ 无法找到或创建测试文件,跳过权限验证"
|
||||
fi
|
||||
|
||||
# 显示统一组和用户状态
|
||||
echo "=== 统一Web组状态检查 ==="
|
||||
if getent group "$WEB_GROUP" &>/dev/null; then
|
||||
echo "✅ 统一Web组 '$WEB_GROUP' 存在"
|
||||
echo "组信息: $(getent group "$WEB_GROUP" 2>/dev/null || echo '获取失败')"
|
||||
|
||||
# 检查哪些用户在统一组中
|
||||
echo "统一组成员检查:"
|
||||
for web_user in "www-data" "www" "apache" "nginx"; do
|
||||
if id "$web_user" &>/dev/null; then
|
||||
if id -Gn "$web_user" | grep -q "$WEB_GROUP"; then
|
||||
echo "✅ $web_user 在统一组 '$WEB_GROUP' 中"
|
||||
else
|
||||
echo "❌ $web_user 不在统一组 '$WEB_GROUP' 中"
|
||||
fi
|
||||
else
|
||||
echo "❌ $web_user 用户不存在"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "❌ 统一Web组 '$WEB_GROUP' 不存在"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "=== 启动应用 ==="
|
||||
|
||||
# 执行原有的启动命令
|
||||
exec "$@"
|
||||
@@ -12,18 +12,42 @@ autostart=true
|
||||
autorestart=true
|
||||
startretries=3
|
||||
startsecs=1
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
stdout_logfile=/var/log/supervisor/php-fpm.log
|
||||
stdout_logfile_maxbytes=10MB
|
||||
stdout_logfile_backups=10
|
||||
stderr_logfile=/var/log/supervisor/php-fpm-error.log
|
||||
stderr_logfile_maxbytes=10MB
|
||||
stderr_logfile_backups=10
|
||||
|
||||
[program:think-cron]
|
||||
command=php /var/www/html/think cron:schedule
|
||||
process_name=%(program_name)s_%(process_num)02d
|
||||
numprocs=1
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startretries=5
|
||||
startsecs=2
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
startretries=3
|
||||
stdout_logfile=/var/log/supervisor/think-cron.log
|
||||
stdout_logfile_maxbytes=10MB
|
||||
stdout_logfile_backups=10
|
||||
stderr_logfile=/var/log/supervisor/think-cron-error.log
|
||||
stderr_logfile_maxbytes=10MB
|
||||
stderr_logfile_backups=10
|
||||
startsecs=3
|
||||
stopwaitsecs=10
|
||||
|
||||
[program:websocket-server]
|
||||
command=/bin/bash -c "if [ -f /var/www/html/ws_server.php ]; then php ./ws_server.php; else echo 'ws_server.php not found, skipping websocket server'; fi"
|
||||
workdir=/var/www/html
|
||||
autostart=true
|
||||
autorestart=false
|
||||
startretries=0
|
||||
stdout_logfile=/var/log/supervisor/websocket-server.log
|
||||
stdout_logfile_maxbytes=10MB
|
||||
stdout_logfile_backups=10
|
||||
stderr_logfile=/var/log/supervisor/websocket-server-error.log
|
||||
stderr_logfile_maxbytes=10MB
|
||||
stderr_logfile_backups=10
|
||||
startsecs=3
|
||||
stopwaitsecs=10
|
||||
|
||||
BIN
docker/php/websocat
Normal file
9
docker/redis_data/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# 忽略所有文件
|
||||
*
|
||||
|
||||
# 只保留指定的 .gitkeep 文件
|
||||
!.gitignore
|
||||
!development/.gitkeep
|
||||
!test/.gitkeep
|
||||
!production/.gitkeep
|
||||
!staging/.gitkeep
|
||||
0
docker/redis_data/.gitkeep
Normal file
10
docker/xdebug_logs/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# 忽略目录下所有文件和子目录
|
||||
*
|
||||
# 忽略所有子目录
|
||||
*/
|
||||
# 但不忽略 .gitkeep 文件
|
||||
!.gitkeep
|
||||
# 不忽略 .gitignore 文件自身
|
||||
!.gitignore
|
||||
# 不忽略 development/.gitkeep 文件
|
||||
!development/.gitkeep
|
||||
0
docker/xdebug_logs/.gitkeep
Normal file
212
docs/CHINESE_ENCODING_SOLUTIONS.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# PHP 中文字符编码解决方案
|
||||
|
||||
## 🎯 问题描述
|
||||
|
||||
在使用 `json_encode()` 处理包含中文字符的数据时,默认会将中文字符转换为 Unicode 编码:
|
||||
|
||||
```php
|
||||
// ❌ 默认行为 - 中文转为Unicode
|
||||
$data = ['message' => '请检查计划任务配置'];
|
||||
echo json_encode($data);
|
||||
// 输出:{"message":"\u8bf7\u68c0\u67e5\u8ba1\u5212\u4efb\u52a1\u914d\u7f6e"}
|
||||
```
|
||||
|
||||
这在日志文件中查看很不方便,影响调试效率。
|
||||
|
||||
## 🛠️ 解决方案对比
|
||||
|
||||
### 方案一:`JSON_UNESCAPED_UNICODE` 标志 ⭐⭐⭐⭐⭐⭐
|
||||
|
||||
```php
|
||||
// ✅ 推荐方案 - 保持中文原样
|
||||
$data = ['message' => '请检查计划任务配置'];
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
// 输出:{"message":"请检查计划任务配置"}
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- ✅ 中文完全可读
|
||||
- ✅ 保持JSON格式
|
||||
- ✅ 兼容性好
|
||||
- ✅ 性能优秀
|
||||
|
||||
**使用场景:**
|
||||
- 日志记录
|
||||
- API响应
|
||||
- 配置文件
|
||||
- 调试输出
|
||||
|
||||
### 方案二:`var_export()` 函数 ⭐⭐⭐
|
||||
|
||||
```php
|
||||
// ✅ 完全避免Unicode转义
|
||||
$data = ['message' => '请检查计划任务配置'];
|
||||
echo var_export($data, true);
|
||||
// 输出:array (
|
||||
// 'message' => '请检查计划任务配置',
|
||||
// )
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- ✅ 完全可读
|
||||
- ✅ 数组结构清晰
|
||||
- ✅ 无需额外参数
|
||||
|
||||
**缺点:**
|
||||
- ❌ 不是标准JSON格式
|
||||
- ❌ 输出较冗长
|
||||
|
||||
**使用场景:**
|
||||
- 开发调试
|
||||
- 临时日志
|
||||
- 数组检查
|
||||
|
||||
### 方案三:`print_r()` + 捕获输出 ⭐⭐
|
||||
|
||||
```php
|
||||
// ✅ 可读性好的数组输出
|
||||
$data = ['message' => '请检查计划任务配置'];
|
||||
ob_start();
|
||||
print_r($data);
|
||||
$result = ob_get_clean();
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- ✅ 格式美观
|
||||
- ✅ 易于阅读
|
||||
|
||||
**缺点:**
|
||||
- ❌ 需要输出缓冲
|
||||
- ❌ 性能较差
|
||||
|
||||
### 方案四:自定义序列化函数 ⭐⭐⭐
|
||||
|
||||
```php
|
||||
function serializeArray($data) {
|
||||
if (!is_array($data)) return $data;
|
||||
|
||||
$result = [];
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$result[$key] = serializeArray($value);
|
||||
} else {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 最佳实践组合
|
||||
|
||||
### 在 Cron 类中的实现
|
||||
|
||||
```php
|
||||
class Cron extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 格式化数据为日志友好的字符串(保持中文可读性)
|
||||
*/
|
||||
private static function formatForLog($data): string
|
||||
{
|
||||
if (is_array($data) || is_object($data)) {
|
||||
// JSON_UNESCAPED_UNICODE 保持中文可读性
|
||||
// JSON_PRETTY_PRINT 增加格式化
|
||||
// JSON_UNESCAPED_SLASHES 避免斜杠转义
|
||||
return json_encode($data,
|
||||
JSON_UNESCAPED_UNICODE |
|
||||
JSON_PRETTY_PRINT |
|
||||
JSON_UNESCAPED_SLASHES
|
||||
);
|
||||
}
|
||||
|
||||
return (string) $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 完全避免Unicode转义的格式化
|
||||
*/
|
||||
private static function exportForLog($data): string
|
||||
{
|
||||
if (is_array($data) || is_object($data)) {
|
||||
return var_export($data, true);
|
||||
}
|
||||
|
||||
return (string) $data;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```php
|
||||
// 日志记录 - 使用 formatForLog
|
||||
$detail = [
|
||||
'error' => $error,
|
||||
'remark' => $remark,
|
||||
'suggestion' => ScheduleDict::getSuggestion($cronType)
|
||||
];
|
||||
log_write('异常信息:' . self::formatForLog($detail), 'warning');
|
||||
|
||||
// 调试输出 - 使用 exportForLog
|
||||
$debugData = ['config' => $config, 'status' => $status];
|
||||
echo self::exportForLog($debugData);
|
||||
```
|
||||
|
||||
## 🔧 环境配置
|
||||
|
||||
### 确保 PHP 版本支持
|
||||
```php
|
||||
// PHP 5.4+ 基本支持
|
||||
json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// PHP 5.3+ 需要 polyfill
|
||||
if (!defined('JSON_UNESCAPED_UNICODE')) {
|
||||
define('JSON_UNESCAPED_UNICODE', 256);
|
||||
}
|
||||
```
|
||||
|
||||
### 文件编码设置
|
||||
```php
|
||||
// 确保文件本身是 UTF-8 编码
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
```
|
||||
|
||||
## 📊 性能对比
|
||||
|
||||
| 方法 | 执行时间 | 内存使用 | 可读性 | JSON兼容 |
|
||||
|------|----------|----------|---------|-----------|
|
||||
| `json_encode()` | 快 | 低 | ⭐⭐⭐⭐⭐ | ✅ |
|
||||
| `var_export()` | 中 | 中 | ⭐⭐⭐⭐ | ❌ |
|
||||
| `print_r()` | 慢 | 高 | ⭐⭐⭐⭐ | ❌ |
|
||||
| 自定义序列化 | 慢 | 高 | ⭐⭐ | ❌ |
|
||||
|
||||
## 🎉 推荐配置
|
||||
|
||||
### 生产环境(推荐)
|
||||
```php
|
||||
// 使用 JSON_UNESCAPED_UNICODE 保持中文可读性
|
||||
json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
```
|
||||
|
||||
### 开发环境
|
||||
```php
|
||||
// 使用格式化JSON增强可读性
|
||||
json_encode($data,
|
||||
JSON_UNESCAPED_UNICODE |
|
||||
JSON_PRETTY_PRINT |
|
||||
JSON_UNESCAPED_SLASHES
|
||||
);
|
||||
```
|
||||
|
||||
### 调试模式
|
||||
```php
|
||||
// 使用 var_export 获得最佳可读性
|
||||
var_export($data, true);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**总结:** 对于您的需求,使用 `JSON_UNESCAPED_UNICODE` 标志是最佳选择,既保持了JSON格式的标准性,又确保了中文字符的可读性。
|
||||
310
docs/CRON_ENV_SETUP.md
Normal file
@@ -0,0 +1,310 @@
|
||||
# Cron 任务环境变量配置指南
|
||||
|
||||
## 🎯 问题:如何为 `nohup php think cron:schedule` 指定 `.env` 文件
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
**最重要的部分**
|
||||
|
||||
我们修改了项目目录下的 `think` 文件的内容,增加了读取环境变量的代码
|
||||
|
||||
```php
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
namespace think;
|
||||
|
||||
// 命令行入口文件
|
||||
// 加载基础文件
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// 创建应用程序
|
||||
$app = new App();
|
||||
|
||||
// 您的代码使用APP_ENV
|
||||
$appEnv = getenv('APP_ENV') ?: '';
|
||||
if ($appEnv) {
|
||||
$envFile = __DIR__ . '/.env.' . $appEnv;
|
||||
if (is_file($envFile)) {
|
||||
$app->env->load($envFile);
|
||||
}
|
||||
}
|
||||
|
||||
// 应用初始化
|
||||
$app->console->run();
|
||||
```
|
||||
|
||||
|
||||
### 方案一:使用环境变量(推荐)
|
||||
|
||||
```bash
|
||||
# 1. 临时设置环境变量(当前会话有效)
|
||||
export THINK_ENV=production
|
||||
nohup php think cron:schedule > /dev/null 2>&1 &
|
||||
|
||||
# 2. 永久设置环境变量(写入 ~/.bashrc)
|
||||
echo 'export THINK_ENV=production' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
nohup php think cron:schedule > /dev/null 2>&1 &
|
||||
|
||||
# 3. 直接传递环境变量
|
||||
THINK_ENV=production nohup php think cron:schedule > /dev/null 2>&1 &
|
||||
```
|
||||
|
||||
### 方案二:指定不同的 .env 文件
|
||||
|
||||
```bash
|
||||
# 1. 复制环境配置文件
|
||||
cp .env .env.production
|
||||
# 编辑 .env.production 修改相应配置
|
||||
|
||||
# 2. 使用 APP_ENV 指定环境
|
||||
APP_ENV=production nohup php think cron:schedule > /dev/null 2>&1 &
|
||||
|
||||
# 3. 或者在运行时指定
|
||||
php think cron:schedule --env=production
|
||||
```
|
||||
|
||||
### 方案三:自定义启动脚本(最佳实践)
|
||||
|
||||
创建专用启动脚本 `start_cron.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# 设置环境变量
|
||||
export THINK_ENV=${1:-production}
|
||||
|
||||
# 设置工作目录
|
||||
cd /path/to/your/project
|
||||
|
||||
# 日志文件路径
|
||||
LOG_FILE="/var/log/cron_${THINK_ENV}.log"
|
||||
|
||||
# 启动 cron 任务
|
||||
echo "Starting cron with environment: $THINK_ENV"
|
||||
nohup php think cron:schedule > $LOG_FILE 2>&1 &
|
||||
|
||||
# 记录进程ID
|
||||
echo $! > /var/run/cron_${THINK_ENV}.pid
|
||||
|
||||
echo "Cron started with PID: $(cat /var/run/cron_${THINK_ENV}.pid)"
|
||||
```
|
||||
|
||||
使用方法:
|
||||
```bash
|
||||
chmod +x start_cron.sh
|
||||
|
||||
# 启动生产环境 cron
|
||||
./start_cron.sh production
|
||||
|
||||
# 启动开发环境 cron
|
||||
./start_cron.sh development
|
||||
```
|
||||
|
||||
## 🔧 ThinkPHP 6.x 环境变量加载机制
|
||||
|
||||
### ThinkPHP 自动加载顺序:
|
||||
|
||||
1. **服务器环境变量**: `$_SERVER['APP_ENV']`
|
||||
2. **系统环境变量**: `getenv('APP_ENV')`
|
||||
3. **.env 文件**: 根据环境变量加载对应的 `.env.*` 文件
|
||||
|
||||
### 支持的环境变量:
|
||||
|
||||
| 变量名 | 作用 | 示例 |
|
||||
|--------|------|-------|
|
||||
| `APP_ENV` | 指定运行环境 | `production` |
|
||||
| `THINK_ENV` | ThinkPHP 6.x 环境标识 | `production` |
|
||||
| `ENV_PATH` | 自定义 .env 文件路径 | `/custom/path/.env` |
|
||||
|
||||
## 📁 环境配置文件结构
|
||||
|
||||
```
|
||||
project/
|
||||
├── .env # 默认配置
|
||||
├── .env.development # 开发环境
|
||||
├── .env.testing # 测试环境
|
||||
├── .env.staging # 预发布环境
|
||||
└── .env.production # 生产环境
|
||||
```
|
||||
|
||||
## 🚀 实际使用示例
|
||||
|
||||
### Docker 环境中的使用
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile 中设置环境变量
|
||||
ENV THINK_ENV=production
|
||||
|
||||
# 或者在 docker-compose.yml 中
|
||||
environment:
|
||||
- THINK_ENV=production
|
||||
- MYSQL_HOST=mysql
|
||||
- MYSQL_PORT=3306
|
||||
```
|
||||
|
||||
### Supervisor 配置
|
||||
|
||||
```ini
|
||||
[program:cron-production]
|
||||
command=/usr/bin/php /var/www/html/think cron:schedule
|
||||
environment=THINK_ENV=production
|
||||
directory=/var/www/html
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=www-data
|
||||
stdout_logfile=/var/log/supervisor/cron-production.log
|
||||
stderr_logfile=/var/log/supervisor/cron-production-error.log
|
||||
|
||||
[program:cron-development]
|
||||
command=/usr/bin/php /var/www/html/think cron:schedule
|
||||
environment=THINK_ENV=development
|
||||
directory=/var/www/html
|
||||
autostart=false
|
||||
autorestart=true
|
||||
user=www-data
|
||||
stdout_logfile=/var/log/supervisor/cron-development.log
|
||||
stderr_logfile=/var/log/supervisor/cron-development-error.log
|
||||
```
|
||||
|
||||
### 系统服务配置
|
||||
|
||||
```bash
|
||||
# 创建 systemd 服务文件
|
||||
sudo tee /etc/systemd/system/think-cron.service > /dev/null <<EOF
|
||||
[Unit]
|
||||
Description=ThinkPHP Cron Scheduler
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
WorkingDirectory=/var/www/html
|
||||
Environment=THINK_ENV=production
|
||||
ExecStart=/usr/bin/php /var/www/html/think cron:schedule
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# 启用并启动服务
|
||||
sudo systemctl enable think-cron
|
||||
sudo systemctl start think-cron
|
||||
```
|
||||
|
||||
## 🔍 验证配置
|
||||
|
||||
### 检查当前环境
|
||||
|
||||
```bash
|
||||
# 1. 在 cron 任务中添加日志
|
||||
php think cron:schedule --verbose
|
||||
|
||||
# 2. 检查环境变量
|
||||
php -r "echo 'APP_ENV: ' . getenv('APP_ENV') . PHP_EOL;"
|
||||
php -r "echo 'THINK_ENV: ' . getenv('THINK_ENV') . PHP_EOL;"
|
||||
|
||||
# 3. 检查 ThinkPHP 加载的配置
|
||||
php think config:get app.env
|
||||
```
|
||||
|
||||
### 测试不同环境
|
||||
|
||||
```bash
|
||||
# 测试生产环境
|
||||
APP_ENV=production php think cron:schedule --verbose
|
||||
|
||||
# 测试开发环境
|
||||
APP_ENV=development php think cron:schedule --verbose
|
||||
|
||||
# 使用指定配置文件
|
||||
THINK_ENV=staging php think cron:schedule --verbose
|
||||
```
|
||||
|
||||
## 📝 日志和监控
|
||||
|
||||
### 日志配置
|
||||
|
||||
```bash
|
||||
# 按环境分离日志
|
||||
nohup php think cron:schedule > logs/cron_${APP_ENV:-default}.log 2>&1 &
|
||||
|
||||
# 带时间戳的日志
|
||||
nohup php think cron:schedule > logs/cron_$(date +%Y%m%d).log 2>&1 &
|
||||
```
|
||||
|
||||
### 进程监控
|
||||
|
||||
```bash
|
||||
# 检查 cron 进程
|
||||
ps aux | grep "think cron:schedule" | grep -v grep
|
||||
|
||||
# 使用 pgrep 查找进程ID
|
||||
pgrep -f "think cron:schedule"
|
||||
|
||||
# 监控脚本
|
||||
#!/bin/bash
|
||||
while true; do
|
||||
if ! pgrep -f "think cron:schedule"; then
|
||||
echo "$(date): Cron process died, restarting..."
|
||||
nohup php think cron:schedule > logs/cron.log 2>&1 &
|
||||
fi
|
||||
sleep 30
|
||||
done
|
||||
```
|
||||
|
||||
## 🎯 推荐配置
|
||||
|
||||
### 生产环境最佳实践
|
||||
|
||||
```bash
|
||||
# 1. 使用环境变量指定配置
|
||||
export APP_ENV=production
|
||||
|
||||
# 2. 设置适当的日志级别
|
||||
nohup php think cron:schedule --quiet > /var/log/cron.log 2>&1 &
|
||||
|
||||
# 3. 定期检查进程状态
|
||||
*/5 * * * * * /usr/bin/pgrep -f "think cron:schedule" || /usr/bin/nohup /usr/bin/php /var/www/html/think cron:schedule > /var/log/cron.log 2>&1 &
|
||||
```
|
||||
|
||||
### 开发环境配置
|
||||
|
||||
```bash
|
||||
# 1. 使用开发环境配置
|
||||
export APP_ENV=development
|
||||
|
||||
# 2. 显示详细输出用于调试
|
||||
nohup php think cron:schedule > /dev/null 2>&1 &
|
||||
|
||||
# 3. 实时查看日志
|
||||
tail -f logs/cron_dev.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 本地开发环境配置
|
||||
|
||||
```bash
|
||||
# 1. 使用开发环境配置
|
||||
export APP_ENV=local
|
||||
|
||||
# 2. 显示详细输出用于调试
|
||||
nohup php think cron:schedule > /dev/null 2>&1 &
|
||||
|
||||
# 3. 实时查看日志
|
||||
tail -f logs/cron_dev.log
|
||||
```
|
||||
|
||||
## ✅ 总结
|
||||
|
||||
**答案:是的,可以为 `nohup php think cron:schedule` 指定 `.env` 文件!**
|
||||
|
||||
**推荐方法:**
|
||||
1. 使用 `APP_ENV=production` 环境变量
|
||||
2. 创建对应的 `.env.production` 文件
|
||||
3. 使用自定义启动脚本管理不同环境
|
||||
|
||||
这样可以让同一套代码在不同环境中使用不同的数据库配置、缓存配置等。
|
||||
670
docs/common/CLodop.md
Normal file
@@ -0,0 +1,670 @@
|
||||
好的!我来详细介绍一下**Web打印服务CLodop**,这是一个非常强大的浏览器打印解决方案。
|
||||
|
||||
## 🖨️ **CLodop - 专业Web打印服务详解**
|
||||
|
||||
### **1. CLodop 是什么?**
|
||||
|
||||
**CLodop**(Cloud Lodop)是**C-Lodop 云打印服务**的简称,是 Lodop 技术的一个轻量级版本,专门为 Web 应用提供强大的打印功能。
|
||||
|
||||
#### **核心特点**
|
||||
- ✅ **跨浏览器兼容** - 支持所有主流浏览器
|
||||
- ✅ **无需安装插件** - 基于云服务架构
|
||||
- ✅ **精确打印控制** - 像素级打印精度
|
||||
- ✅ **批量打印支持** - 高效处理大量打印任务
|
||||
- ✅ **多种打印方式** - 直接打印、预览打印、设计打印
|
||||
|
||||
### **2. CLodop 与 Lodop 的区别**
|
||||
|
||||
| 特性 | CLodop(云打印) | Lodop(传统打印) |
|
||||
|------|-----------------|------------------|
|
||||
| **架构** | 云服务模式,无需安装 | 需要安装本地插件 |
|
||||
| **部署** | 服务端部署,客户端零配置 | 每台客户端都需要安装 |
|
||||
| **更新** | 服务端统一更新 | 每台客户端单独更新 |
|
||||
| **兼容性** | 支持所有现代浏览器 | 对高版本浏览器支持有限 |
|
||||
| **适用场景** | Web应用、移动端 | 企业内部系统、局域网 |
|
||||
|
||||
### **3. CLodop 核心架构**
|
||||
|
||||
#### **系统架构图**
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ Web应用 │ -> │ CLodop服务 │ -> │ 打印机 │
|
||||
│ (浏览器) │ │ (服务端) │ │ (本地/网络) │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
│ │ │
|
||||
JavaScript 打印服务 打印输出
|
||||
API 任务队列
|
||||
```
|
||||
|
||||
### **4. 完整安装和配置**
|
||||
|
||||
#### **4.1 服务端安装(Windows)**
|
||||
```bash
|
||||
# 下载CLodop服务端
|
||||
# 官方下载地址:http://www.c-lodop.com/download.html
|
||||
|
||||
# 安装步骤:
|
||||
1. 下载 CLodop_Setup.exe
|
||||
2. 以管理员身份运行安装
|
||||
3. 默认安装目录:C:\Program Files (x86)\CLodop
|
||||
4. 服务自动启动,监听端口:8000、18000
|
||||
```
|
||||
|
||||
#### **4.2 服务端配置文件**
|
||||
```json
|
||||
// CLodop 服务配置 (config.json)
|
||||
{
|
||||
"server": {
|
||||
"port": 8000,
|
||||
"sslPort": 8443,
|
||||
"host": "0.0.0.0"
|
||||
},
|
||||
"print": {
|
||||
"defaultPaper": "A4",
|
||||
"dpi": 300,
|
||||
"timeout": 30000
|
||||
},
|
||||
"security": {
|
||||
"allowedOrigins": ["http://localhost:3000", "https://yourdomain.com"],
|
||||
"authRequired": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### **4.3 验证安装**
|
||||
```bash
|
||||
# 检查服务状态
|
||||
netstat -ano | findstr :8000
|
||||
|
||||
# 访问测试页面
|
||||
http://localhost:8000
|
||||
```
|
||||
|
||||
### **5. 前端集成完整代码**
|
||||
|
||||
#### **5.1 基础集成方案**
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CLodop 打印示例</title>
|
||||
<!-- 引入CLodop JS文件 -->
|
||||
<script src="http://localhost:8000/CLodopfuncs.js"></script>
|
||||
<script>
|
||||
// 检查CLodop服务状态
|
||||
function checkCLodop() {
|
||||
try {
|
||||
if (getCLodop()) {
|
||||
console.log('CLodop服务已就绪');
|
||||
return true;
|
||||
} else {
|
||||
console.warn('CLodop服务未启动');
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('CLodop检查失败:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取CLodop对象
|
||||
function getCLodop() {
|
||||
if (window.getCLodop) {
|
||||
return window.getLodop();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="checkCLodop()">
|
||||
<!-- 打印内容 -->
|
||||
<div id="printContent">
|
||||
<h1>可打印内容</h1>
|
||||
<table border="1" style="width:100%">
|
||||
<tr><th>姓名</th><th>年龄</th><th>职位</th></tr>
|
||||
<tr><td>张三</td><td>30</td><td>工程师</td></tr>
|
||||
<tr><td>李四</td><td>25</td><td>设计师</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button onclick="printDirect()">直接打印</button>
|
||||
<button onclick="printPreview()">打印预览</button>
|
||||
<button onclick="printDesign()">打印设计</button>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### **5.2 高级打印控制器**
|
||||
```javascript
|
||||
// clodop-manager.js
|
||||
class CLodopManager {
|
||||
constructor() {
|
||||
this.lodop = null;
|
||||
this.isReady = false;
|
||||
this.init();
|
||||
}
|
||||
|
||||
// 初始化CLodop
|
||||
init() {
|
||||
if (typeof window.getCLodop === 'undefined') {
|
||||
console.error('CLodop未加载,请检查脚本引入');
|
||||
return;
|
||||
}
|
||||
|
||||
this.lodop = window.getLodop();
|
||||
if (this.lodop) {
|
||||
this.isReady = true;
|
||||
console.log('CLodop初始化成功');
|
||||
} else {
|
||||
console.error('CLodop初始化失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 检查服务状态
|
||||
checkStatus() {
|
||||
if (!this.lodop) return false;
|
||||
|
||||
try {
|
||||
const status = this.lodop.PRINT_STATUS;
|
||||
return status === 'READY';
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建打印任务
|
||||
createPrintJob(title = '打印文档') {
|
||||
if (!this.isReady) {
|
||||
throw new Error('CLodop未就绪');
|
||||
}
|
||||
|
||||
this.lodop.PRINT_INIT(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
// 设置打印内容
|
||||
setContent(htmlContent, options = {}) {
|
||||
const config = {
|
||||
top: options.top || '10mm',
|
||||
left: options.left || '10mm',
|
||||
width: options.width || '190mm',
|
||||
height: options.height || '277mm',
|
||||
...options
|
||||
};
|
||||
|
||||
this.lodop.ADD_PRINT_HTML(
|
||||
config.top, config.left, config.width, config.height,
|
||||
htmlContent
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// 设置打印机
|
||||
setPrinter(printerName = '') {
|
||||
if (printerName) {
|
||||
this.lodop.SET_PRINTER_INDEX(printerName);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// 设置纸张大小
|
||||
setPaperSize(paperName = 'A4') {
|
||||
this.lodop.SET_PRINT_PAGESIZE(1, 0, 0, paperName);
|
||||
return this;
|
||||
}
|
||||
|
||||
// 设置打印份数
|
||||
setCopies(copies = 1) {
|
||||
this.lodop.SET_PRINT_COPIES(copies);
|
||||
return this;
|
||||
}
|
||||
|
||||
// 直接打印
|
||||
printDirect() {
|
||||
if (!this.isReady) return false;
|
||||
|
||||
try {
|
||||
this.lodop.PRINT();
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('打印失败:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 打印预览
|
||||
printPreview() {
|
||||
if (!this.isReady) return false;
|
||||
|
||||
try {
|
||||
this.lodop.PREVIEW();
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('预览失败:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 打印设计
|
||||
printDesign() {
|
||||
if (!this.isReady) return false;
|
||||
|
||||
try {
|
||||
this.lodop.PRINT_DESIGN();
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('设计失败:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 批量打印
|
||||
batchPrint(documents = []) {
|
||||
if (!this.isReady) return false;
|
||||
|
||||
try {
|
||||
documents.forEach((doc, index) => {
|
||||
this.lodop.PRINT_INIT(`文档${index + 1}`);
|
||||
this.lodop.ADD_PRINT_HTML("10mm", "10mm", "190mm", "277mm", doc);
|
||||
|
||||
if (index < documents.length - 1) {
|
||||
this.lodop.NEWPAGE(); // 分页
|
||||
}
|
||||
});
|
||||
|
||||
this.lodop.PREVIEW();
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('批量打印失败:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 全局实例
|
||||
window.clodopManager = new CLodopManager();
|
||||
```
|
||||
|
||||
### **6. 实际应用示例**
|
||||
|
||||
#### **6.1 票据打印**
|
||||
```javascript
|
||||
// receipt-print.js
|
||||
function printReceipt(orderData) {
|
||||
const manager = window.clodopManager;
|
||||
if (!manager.isReady) {
|
||||
alert('打印服务未就绪');
|
||||
return;
|
||||
}
|
||||
|
||||
const receiptHTML = `
|
||||
<div style="width:80mm;font-family:'宋体';font-size:12px;">
|
||||
<h3 style="text-align:center;">销售小票</h3>
|
||||
<hr>
|
||||
<p><strong>订单号:</strong>${orderData.orderNo}</p>
|
||||
<p><strong>时间:</strong>${new Date().toLocaleString()}</p>
|
||||
<hr>
|
||||
<table style="width:100%;">
|
||||
${orderData.items.map(item => `
|
||||
<tr>
|
||||
<td>${item.name}</td>
|
||||
<td>×${item.quantity}</td>
|
||||
<td>¥${item.price}</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</table>
|
||||
<hr>
|
||||
<p style="text-align:right;"><strong>总计:¥${orderData.total}</strong></p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
manager.createPrintJob('销售小票')
|
||||
.setPaperSize(1) // 1=80mm 小票
|
||||
.setContent(receiptHTML, {
|
||||
width: '80mm',
|
||||
height: 'auto'
|
||||
})
|
||||
.printDirect();
|
||||
}
|
||||
```
|
||||
|
||||
#### **6.2 报表打印**
|
||||
```javascript
|
||||
// report-print.js
|
||||
function printReport(reportData) {
|
||||
const manager = window.clodopManager;
|
||||
|
||||
const reportHTML = `
|
||||
<div style="font-family:'微软雅黑';padding:20px;">
|
||||
<h1 style="text-align:center;">${reportData.title}</h1>
|
||||
<table border="1" style="width:100%;border-collapse:collapse;">
|
||||
<thead>
|
||||
<tr style="background:#f5f5f5;">
|
||||
${reportData.headers.map(header =>
|
||||
`<th style="padding:8px;">${header}</th>`
|
||||
).join('')}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${reportData.rows.map(row => `
|
||||
<tr>
|
||||
${row.map(cell =>
|
||||
`<td style="padding:6px;">${cell}</td>`
|
||||
).join('')}
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="margin-top:20px;">
|
||||
<p>打印时间:${new Date().toLocaleString()}</p>
|
||||
<p>打印人:${reportData.printUser}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
manager.createPrintJob(reportData.title)
|
||||
.setPaperSize('A4')
|
||||
.setContent(reportHTML)
|
||||
.setPrinter(reportData.printer || '')
|
||||
.printPreview();
|
||||
}
|
||||
```
|
||||
|
||||
#### **6.3 标签打印**
|
||||
```javascript
|
||||
// label-print.js
|
||||
function printLabels(labelsData) {
|
||||
const manager = window.clodopManager;
|
||||
|
||||
manager.createPrintJob('产品标签');
|
||||
|
||||
labelsData.forEach((label, index) => {
|
||||
const labelHTML = `
|
||||
<div style="width:50mm;height:30mm;border:1px dotted #ccc;padding:5mm;font-size:10px;">
|
||||
<div style="text-align:center;font-weight:bold;">${label.productName}</div>
|
||||
<div>规格:${label.spec}</div>
|
||||
<div>批次:${label.batchNo}</div>
|
||||
<div>有效期:${label.expiryDate}</div>
|
||||
<div style="text-align:center;margin-top:2mm;">
|
||||
<img src="${label.barcodeUrl}" style="height:15mm;">
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
manager.setContent(labelHTML, {
|
||||
top: `${(index % 5) * 32}mm`,
|
||||
left: `${Math.floor(index / 5) * 52}mm`,
|
||||
width: '50mm',
|
||||
height: '30mm'
|
||||
});
|
||||
|
||||
if (index < labelsData.length - 1) {
|
||||
manager.lodop.NEWPAGE();
|
||||
}
|
||||
});
|
||||
|
||||
manager.printPreview();
|
||||
}
|
||||
```
|
||||
|
||||
### **7. 高级功能实现**
|
||||
|
||||
#### **7.1 打印状态监控**
|
||||
```javascript
|
||||
// print-monitor.js
|
||||
class PrintMonitor {
|
||||
constructor() {
|
||||
this.printJobs = new Map();
|
||||
this.initEvents();
|
||||
}
|
||||
|
||||
initEvents() {
|
||||
// 监听打印开始
|
||||
window.addEventListener('beforeprint', (e) => {
|
||||
console.log('打印开始');
|
||||
this.onPrintStart(e);
|
||||
});
|
||||
|
||||
// 监听打印结束
|
||||
window.addEventListener('afterprint', (e) => {
|
||||
console.log('打印结束');
|
||||
this.onPrintEnd(e);
|
||||
});
|
||||
}
|
||||
|
||||
onPrintStart(jobId) {
|
||||
this.printJobs.set(jobId, {
|
||||
startTime: new Date(),
|
||||
status: 'printing'
|
||||
});
|
||||
}
|
||||
|
||||
onPrintEnd(jobId, success = true) {
|
||||
const job = this.printJobs.get(jobId);
|
||||
if (job) {
|
||||
job.endTime = new Date();
|
||||
job.status = success ? 'completed' : 'failed';
|
||||
job.duration = job.endTime - job.startTime;
|
||||
|
||||
// 发送打印统计
|
||||
this.sendPrintStatistics(job);
|
||||
}
|
||||
}
|
||||
|
||||
sendPrintStatistics(job) {
|
||||
// 发送到服务器记录
|
||||
fetch('/api/print/log', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(job)
|
||||
});
|
||||
}
|
||||
|
||||
// 获取打印机列表
|
||||
getPrinterList() {
|
||||
const lodop = getCLodop();
|
||||
if (!lodop) return [];
|
||||
|
||||
const printers = [];
|
||||
const count = lodop.GET_PRINTER_COUNT();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
printers.push({
|
||||
name: lodop.GET_PRINTER_NAME(i),
|
||||
status: lodop.GET_PRINTER_STATUS(i)
|
||||
});
|
||||
}
|
||||
|
||||
return printers;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### **7.2 错误处理和重试机制**
|
||||
```javascript
|
||||
// error-handler.js
|
||||
class PrintErrorHandler {
|
||||
static async printWithRetry(printFunction, maxRetries = 3) {
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
const result = await printFunction();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`打印尝试 ${attempt} 失败:`, error);
|
||||
|
||||
if (attempt === maxRetries) {
|
||||
throw new Error(`打印失败,已重试 ${maxRetries} 次`);
|
||||
}
|
||||
|
||||
// 等待后重试
|
||||
await this.delay(1000 * attempt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
static handleCommonErrors(error) {
|
||||
const errorMap = {
|
||||
'PRINTER_NOT_FOUND': '打印机未找到,请检查打印机连接',
|
||||
'OUT_OF_PAPER': '打印机缺纸,请添加纸张',
|
||||
'SERVICE_UNAVAILABLE': '打印服务不可用,请检查CLodop服务',
|
||||
'TIMEOUT': '打印超时,请重试'
|
||||
};
|
||||
|
||||
const message = errorMap[error.code] || `打印错误: ${error.message}`;
|
||||
this.showErrorDialog(message);
|
||||
}
|
||||
|
||||
static showErrorDialog(message) {
|
||||
// 可以替换为更友好的UI提示
|
||||
alert(`打印错误: ${message}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **8. 部署和配置最佳实践**
|
||||
|
||||
#### **8.1 Docker 部署**
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
FROM windows/servercore:ltsc2019
|
||||
|
||||
# 安装CLodop服务
|
||||
COPY CLodop_Setup.exe /setup/
|
||||
RUN /setup/CLodop_Setup.exe /S
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8000 18000
|
||||
|
||||
# 启动服务
|
||||
CMD ["C:\\Program Files (x86)\\CLodop\\CLodop.exe", "-start"]
|
||||
```
|
||||
|
||||
#### **8.2 Nginx 反向代理配置**
|
||||
```nginx
|
||||
# nginx.conf
|
||||
server {
|
||||
listen 80;
|
||||
server_name print.yourdomain.com;
|
||||
|
||||
location /CLodop/ {
|
||||
proxy_pass http://localhost:8000/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
# WebSocket 支持
|
||||
location /CLodop/ws {
|
||||
proxy_pass http://localhost:8000/ws;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **9. 安全配置**
|
||||
|
||||
#### **9.1 访问控制**
|
||||
```javascript
|
||||
// security-middleware.js
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
// IP白名单
|
||||
const allowedIPs = ['192.168.1.0/24', '10.0.0.0/8'];
|
||||
|
||||
app.use('/print', (req, res, next) => {
|
||||
const clientIP = req.ip || req.connection.remoteAddress;
|
||||
|
||||
if (!isIPAllowed(clientIP, allowedIPs)) {
|
||||
return res.status(403).json({ error: '访问被拒绝' });
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
function isIPAllowed(ip, allowedRanges) {
|
||||
// IP地址验证逻辑
|
||||
return allowedRanges.some(range => {
|
||||
// 简单的IP范围检查实现
|
||||
return ip.startsWith(range.split('.')[0]);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### **10. 性能优化**
|
||||
|
||||
#### **10.1 打印任务队列**
|
||||
```javascript
|
||||
// print-queue.js
|
||||
class PrintQueue {
|
||||
constructor() {
|
||||
this.queue = [];
|
||||
this.isProcessing = false;
|
||||
this.maxConcurrent = 1; // 串行打印
|
||||
}
|
||||
|
||||
async addJob(printJob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.queue.push({ printJob, resolve, reject });
|
||||
this.processQueue();
|
||||
});
|
||||
}
|
||||
|
||||
async processQueue() {
|
||||
if (this.isProcessing || this.queue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isProcessing = true;
|
||||
|
||||
while (this.queue.length > 0) {
|
||||
const { printJob, resolve, reject } = this.queue.shift();
|
||||
|
||||
try {
|
||||
const result = await printJob();
|
||||
resolve(result);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
// 延迟一下,避免打印机过载
|
||||
await this.delay(500);
|
||||
}
|
||||
|
||||
this.isProcessing = false;
|
||||
}
|
||||
|
||||
delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 💡 **总结**
|
||||
|
||||
### **CLodop 核心优势**
|
||||
1. **零客户端安装** - 基于云服务架构
|
||||
2. **完美浏览器兼容** - 支持所有现代浏览器
|
||||
3. **企业级功能** - 批量打印、精确控制、多种纸张支持
|
||||
4. **易于集成** - 简单的 JavaScript API
|
||||
|
||||
### **适用场景**
|
||||
- ✅ **企业ERP系统** - 报表、单据打印
|
||||
- ✅ **电商平台** - 订单、发货单打印
|
||||
- ✅ **医疗系统** - 处方、报告打印
|
||||
- ✅ **政府办公** - 公文、证件打印
|
||||
- ✅ **教育系统** - 成绩单、证书打印
|
||||
|
||||
### **部署建议**
|
||||
1. **开发环境**:本地安装CLodop服务
|
||||
2. **测试环境**:内网服务器部署
|
||||
3. **生产环境**:高可用集群部署,配合负载均衡
|
||||
|
||||
CLodop 是解决Web打印难题的优秀方案,特别适合需要精确控制打印格式的企业级应用!
|
||||
605
docs/db/PROCEDURE_DEV_v2.0.md
Normal file
@@ -0,0 +1,605 @@
|
||||
# 开发中使用的存储过程
|
||||
|
||||
|
||||
## 统计一下,数据库中哪些表中有site_id字段或者store_id字段,而且表中是有数据的
|
||||
|
||||
```sql
|
||||
|
||||
-- 检查符合条件的表
|
||||
DELIMITER $$
|
||||
|
||||
CREATE PROCEDURE CheckTablesWithHasData()
|
||||
BEGIN
|
||||
DECLARE done INT DEFAULT FALSE;
|
||||
DECLARE tbl_name VARCHAR(255);
|
||||
DECLARE cur CURSOR FOR
|
||||
SELECT DISTINCT TABLE_NAME
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE COLUMN_NAME IN ('site_id', 'store_id')
|
||||
AND TABLE_SCHEMA = DATABASE(); -- 使用当前数据库
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
|
||||
|
||||
CREATE TEMPORARY TABLE tmp_results (table_name VARCHAR(255));
|
||||
|
||||
OPEN cur;
|
||||
|
||||
read_loop: LOOP
|
||||
FETCH cur INTO tbl_name;
|
||||
IF done THEN
|
||||
LEAVE read_loop;
|
||||
END IF;
|
||||
|
||||
SET @sql = CONCAT('INSERT INTO tmp_results SELECT ''', tbl_name, ''' FROM ', tbl_name, ' HAVING COUNT(*) > 0');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
END LOOP;
|
||||
|
||||
CLOSE cur;
|
||||
|
||||
SELECT table_name FROM tmp_results;
|
||||
DROP TEMPORARY TABLE tmp_results;
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
-- 执行
|
||||
CALL CheckTablesWithHasData();
|
||||
|
||||
--- 结果
|
||||
+-----------------+
|
||||
| table_name |
|
||||
+-----------------+
|
||||
| t_order_info |
|
||||
| t_order_item |
|
||||
| t_order_payment |
|
||||
| t_order_refund |
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
## 统计一下,数据库中哪些表中是有数据的
|
||||
|
||||
```sql
|
||||
|
||||
-- 检查符合条件的表
|
||||
DELIMITER $$
|
||||
|
||||
CREATE PROCEDURE CheckTablesIsNotEmpty()
|
||||
BEGIN
|
||||
DECLARE done INT DEFAULT FALSE;
|
||||
DECLARE tbl_name VARCHAR(255);
|
||||
DECLARE cur CURSOR FOR
|
||||
SELECT DISTINCT TABLE_NAME
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE(); -- 使用当前数据库
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
|
||||
|
||||
CREATE TEMPORARY TABLE tmp_results (table_name VARCHAR(255));
|
||||
|
||||
OPEN cur;
|
||||
|
||||
read_loop: LOOP
|
||||
FETCH cur INTO tbl_name;
|
||||
IF done THEN
|
||||
LEAVE read_loop;
|
||||
END IF;
|
||||
|
||||
SET @sql = CONCAT('INSERT INTO tmp_results SELECT ''', tbl_name, ''' FROM ', tbl_name, ' HAVING COUNT(*) > 0');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
END LOOP;
|
||||
|
||||
CLOSE cur;
|
||||
|
||||
SELECT table_name FROM tmp_results;
|
||||
DROP TEMPORARY TABLE tmp_results;
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
-- 执行
|
||||
CALL CheckTablesIsNotEmpty();
|
||||
```
|
||||
|
||||
|
||||
## 对于空表或者无数据表,重置 AUTO_INCREMENT
|
||||
|
||||
```sql
|
||||
|
||||
-- 定义存储过程
|
||||
DELIMITER $$
|
||||
|
||||
CREATE PROCEDURE ResetAutoIncrementForEmptyTables()
|
||||
BEGIN
|
||||
DECLARE done INT DEFAULT FALSE;
|
||||
DECLARE tbl_name VARCHAR(255);
|
||||
DECLARE cnt BIGINT;
|
||||
|
||||
-- 声明游标:获取所有表名
|
||||
DECLARE cur CURSOR FOR
|
||||
SELECT DISTINCT c.TABLE_NAME
|
||||
FROM information_schema.COLUMNS c
|
||||
INNER JOIN information_schema.TABLES t
|
||||
ON c.TABLE_SCHEMA = t.TABLE_SCHEMA
|
||||
AND c.TABLE_NAME = t.TABLE_NAME
|
||||
WHERE
|
||||
c.TABLE_SCHEMA = DATABASE()
|
||||
AND t.TABLE_TYPE = 'BASE TABLE'; -- 排除视图
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
|
||||
|
||||
OPEN cur;
|
||||
|
||||
read_loop: LOOP
|
||||
FETCH cur INTO tbl_name;
|
||||
IF done THEN
|
||||
LEAVE read_loop;
|
||||
END IF;
|
||||
|
||||
-- 动态获取行数
|
||||
SET @sql = CONCAT('SELECT COUNT(*) INTO @cnt FROM `', tbl_name, '`');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 如果为空,重置 AUTO_INCREMENT
|
||||
IF @cnt = 0 THEN
|
||||
SET @reset_sql = CONCAT('ALTER TABLE `', tbl_name, '` AUTO_INCREMENT = 1');
|
||||
PREPARE reset_stmt FROM @reset_sql;
|
||||
EXECUTE reset_stmt;
|
||||
DEALLOCATE PREPARE reset_stmt;
|
||||
SELECT CONCAT('Reset AUTO_INCREMENT for table: ', tbl_name) AS message;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
CLOSE cur;
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
-- 执行
|
||||
|
||||
CALL ResetAutoIncrementForEmptyTables();
|
||||
```
|
||||
|
||||
|
||||
## 数据重置到初始化状态
|
||||
|
||||
|
||||
### 1. 删除数据,保留表结构,当表中有site_id字段时,只删除site_id不为0的数据
|
||||
|
||||
```sql
|
||||
|
||||
-- 定义存储过程
|
||||
-- 增强版数据库重置脚本
|
||||
-- 支持 MySQL/PostgreSQL/SQL Server
|
||||
|
||||
DELIMITER $$
|
||||
|
||||
DROP PROCEDURE IF EXISTS reset_tables_has_site_id_where;
|
||||
CREATE PROCEDURE reset_tables_has_site_id_where(
|
||||
IN p_preserve_site_zero BOOLEAN, -- 是否保留 site_id = 0 的数据
|
||||
IN p_dry_run BOOLEAN, -- 是否试运行(不实际执行)
|
||||
IN p_exclude_tables TEXT -- 排除的表列表,逗号分隔
|
||||
)
|
||||
BEGIN
|
||||
DECLARE v_done INT DEFAULT FALSE;
|
||||
DECLARE v_table_name VARCHAR(255);
|
||||
DECLARE v_has_site_id INT DEFAULT 0;
|
||||
DECLARE v_table_count INT DEFAULT 0;
|
||||
DECLARE v_processed_count INT DEFAULT 0;
|
||||
DECLARE v_skipped_count INT DEFAULT 0;
|
||||
DECLARE v_error_count INT DEFAULT 0;
|
||||
DECLARE v_no_site_id_tables TEXT DEFAULT '';
|
||||
DECLARE v_excluded_tables TEXT DEFAULT '';
|
||||
|
||||
-- 游标声明
|
||||
DECLARE table_cursor CURSOR FOR
|
||||
SELECT TABLE_NAME
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_TYPE = 'BASE TABLE'
|
||||
AND TABLE_NAME NOT IN (
|
||||
'sys_operation_log', 'sys_login_log', 'system_parameters' -- 系统表默认排除
|
||||
)
|
||||
ORDER BY TABLE_NAME;
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE;
|
||||
|
||||
-- 创建详细的日志表
|
||||
DROP TEMPORARY TABLE IF EXISTS reset_operation_log;
|
||||
CREATE TEMPORARY TABLE reset_operation_log (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
log_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
table_name VARCHAR(255) NOT NULL,
|
||||
action_type ENUM('CLEARED', 'SKIPPED', 'ERROR', 'EXCLUDED') NOT NULL,
|
||||
records_affected INT DEFAULT 0,
|
||||
sql_statement TEXT,
|
||||
error_message TEXT,
|
||||
execution_time_ms INT DEFAULT 0
|
||||
);
|
||||
|
||||
-- 开始事务(确保原子性)
|
||||
START TRANSACTION;
|
||||
|
||||
OPEN table_cursor;
|
||||
|
||||
process_tables: LOOP
|
||||
FETCH table_cursor INTO v_table_name;
|
||||
IF v_done THEN
|
||||
LEAVE process_tables;
|
||||
END IF;
|
||||
|
||||
SET v_table_count = v_table_count + 1;
|
||||
|
||||
-- 检查是否在排除列表中
|
||||
IF FIND_IN_SET(v_table_name, p_exclude_tables) > 0 OR
|
||||
FIND_IN_SET(v_table_name, v_excluded_tables) > 0 THEN
|
||||
INSERT INTO reset_operation_log (table_name, action_type, records_affected, sql_statement)
|
||||
VALUES (v_table_name, 'EXCLUDED', 0, '表在排除列表中,跳过处理');
|
||||
SET v_skipped_count = v_skipped_count + 1;
|
||||
ITERATE process_tables;
|
||||
END IF;
|
||||
|
||||
-- 检查表是否有 site_id 字段
|
||||
SELECT COUNT(*) INTO v_has_site_id
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = v_table_name
|
||||
AND COLUMN_NAME = 'site_id';
|
||||
|
||||
SET @start_time = UNIX_TIMESTAMP(NOW(3)) * 1000;
|
||||
|
||||
IF v_has_site_id > 0 THEN
|
||||
-- 构建动态SQL
|
||||
IF p_preserve_site_zero THEN
|
||||
SET @sql = CONCAT('DELETE FROM `', v_table_name, '` WHERE site_id != 0');
|
||||
ELSE
|
||||
SET @sql = CONCAT('TRUNCATE TABLE `', v_table_name, '`');
|
||||
END IF;
|
||||
|
||||
BEGIN
|
||||
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
||||
BEGIN
|
||||
GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errmsg = MESSAGE_TEXT;
|
||||
INSERT INTO reset_operation_log
|
||||
(table_name, action_type, records_affected, sql_statement, error_message)
|
||||
VALUES (v_table_name, 'ERROR', 0, @sql, CONCAT(@sqlstate, ' - ', @errmsg));
|
||||
SET v_error_count = v_error_count + 1;
|
||||
END;
|
||||
|
||||
IF p_dry_run THEN
|
||||
-- 试运行模式,只记录不执行
|
||||
INSERT INTO reset_operation_log
|
||||
(table_name, action_type, records_affected, sql_statement)
|
||||
VALUES (v_table_name, 'SKIPPED', 0, CONCAT('试运行: ', @sql));
|
||||
SET v_skipped_count = v_skipped_count + 1;
|
||||
ELSE
|
||||
-- 实际执行
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @row_count = ROW_COUNT();
|
||||
SET @end_time = UNIX_TIMESTAMP(NOW(3)) * 1000;
|
||||
|
||||
INSERT INTO reset_operation_log
|
||||
(table_name, action_type, records_affected, sql_statement, execution_time_ms)
|
||||
VALUES (v_table_name, 'CLEARED', @row_count, @sql, (@end_time - @start_time));
|
||||
|
||||
SET v_processed_count = v_processed_count + 1;
|
||||
END IF;
|
||||
END;
|
||||
ELSE
|
||||
-- 表没有 site_id 字段
|
||||
SET v_no_site_id_tables = CONCAT_WS(', ', v_no_site_id_tables, v_table_name);
|
||||
|
||||
INSERT INTO reset_operation_log
|
||||
(table_name, action_type, records_affected, sql_statement)
|
||||
VALUES (v_table_name, 'SKIPPED', 0, '表没有 site_id 字段,跳过处理');
|
||||
|
||||
SET v_skipped_count = v_skipped_count + 1;
|
||||
END IF;
|
||||
|
||||
SET v_has_site_id = 0;
|
||||
END LOOP;
|
||||
|
||||
CLOSE table_cursor;
|
||||
|
||||
IF p_dry_run THEN
|
||||
ROLLBACK; -- 试运行模式回滚
|
||||
SELECT '=== 试运行模式 - 未实际执行任何操作 ===' as notice;
|
||||
ELSE
|
||||
COMMIT; -- 提交事务
|
||||
SELECT '=== 数据库重置完成 ===' as summary;
|
||||
END IF;
|
||||
|
||||
-- 生成总结报告
|
||||
SELECT
|
||||
CONCAT('数据库: ', DATABASE()) as database_info,
|
||||
CONCAT('开始时间: ', DATE_FORMAT(MIN(log_time), '%Y-%m-%d %H:%i:%s')) as start_time,
|
||||
CONCAT('结束时间: ', DATE_FORMAT(MAX(log_time), '%Y-%m-%d %H:%i:%s')) as end_time,
|
||||
CONCAT('总表数: ', v_table_count) as total_tables,
|
||||
CONCAT('已处理表: ', v_processed_count) as processed_tables,
|
||||
CONCAT('跳过表: ', v_skipped_count) as skipped_tables,
|
||||
CONCAT('错误数: ', v_error_count) as error_count,
|
||||
CONCAT('总耗时: ', SUM(execution_time_ms), 'ms') as total_duration
|
||||
FROM reset_operation_log;
|
||||
|
||||
-- 显示没有 site_id 字段的表
|
||||
IF v_no_site_id_tables != '' THEN
|
||||
SELECT
|
||||
'以下表没有 site_id 字段,已被跳过:' as warning_title,
|
||||
v_no_site_id_tables as skipped_tables;
|
||||
END IF;
|
||||
|
||||
-- 显示错误详情
|
||||
IF v_error_count > 0 THEN
|
||||
SELECT '=== 错误详情 ===' as error_title;
|
||||
SELECT table_name, error_message, sql_statement
|
||||
FROM reset_operation_log
|
||||
WHERE action_type = 'ERROR'
|
||||
ORDER BY log_time;
|
||||
END IF;
|
||||
|
||||
-- 显示处理详情
|
||||
SELECT '=== 处理详情 ===' as details_title;
|
||||
SELECT
|
||||
table_name as '表名',
|
||||
CASE action_type
|
||||
WHEN 'CLEARED' THEN '已清空'
|
||||
WHEN 'SKIPPED' THEN '已跳过'
|
||||
WHEN 'ERROR' THEN '错误'
|
||||
WHEN 'EXCLUDED' THEN '已排除'
|
||||
END as '操作状态',
|
||||
records_affected as '影响行数',
|
||||
execution_time_ms as '耗时(ms)',
|
||||
CASE
|
||||
WHEN error_message IS NOT NULL THEN error_message
|
||||
ELSE LEFT(sql_statement, 100)
|
||||
END as '详情'
|
||||
FROM reset_operation_log
|
||||
ORDER BY action_type, table_name;
|
||||
|
||||
-- 性能统计
|
||||
SELECT '=== 性能统计 ===' as performance_title;
|
||||
SELECT
|
||||
action_type as '操作类型',
|
||||
COUNT(*) as '表数量',
|
||||
SUM(records_affected) as '总影响行数',
|
||||
AVG(execution_time_ms) as '平均耗时(ms)',
|
||||
SUM(execution_time_ms) as '总耗时(ms)'
|
||||
FROM reset_operation_log
|
||||
GROUP BY action_type;
|
||||
|
||||
-- 清理
|
||||
DROP TEMPORARY TABLE IF EXISTS reset_operation_log;
|
||||
END
|
||||
$$
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
-- 使用示例
|
||||
-- 试运行(不实际执行)
|
||||
CALL reset_tables_has_site_id_where(TRUE, TRUE, 'user,config');
|
||||
|
||||
-- 实际执行(保留site_id=0的数据,排除user和config表)
|
||||
CALL reset_tables_has_site_id_where(TRUE, FALSE, '');
|
||||
|
||||
-- 完全清空所有表(不保留任何数据)
|
||||
CALL reset_tables_has_site_id_where(FALSE, FALSE, '');
|
||||
|
||||
```
|
||||
|
||||
## 删除数据,保留表结构,当表中有store_id字段时,只删除store_id不为0的数据
|
||||
|
||||
```sql
|
||||
DELIMITER $$
|
||||
|
||||
DROP PROCEDURE IF EXISTS reset_tables_has_store_id_where;
|
||||
CREATE PROCEDURE reset_tables_has_store_id_where(
|
||||
IN p_preserve_site_zero BOOLEAN, -- 是否保留 store_id = 0 的数据
|
||||
IN p_dry_run BOOLEAN, -- 是否试运行(不实际执行)
|
||||
IN p_exclude_tables TEXT -- 排除的表列表,逗号分隔
|
||||
)
|
||||
BEGIN
|
||||
DECLARE v_done INT DEFAULT FALSE;
|
||||
DECLARE v_table_name VARCHAR(255);
|
||||
DECLARE v_has_store_id INT DEFAULT 0;
|
||||
DECLARE v_table_count INT DEFAULT 0;
|
||||
DECLARE v_processed_count INT DEFAULT 0;
|
||||
DECLARE v_skipped_count INT DEFAULT 0;
|
||||
DECLARE v_error_count INT DEFAULT 0;
|
||||
DECLARE v_no_store_id_tables TEXT DEFAULT '';
|
||||
DECLARE v_excluded_tables TEXT DEFAULT '';
|
||||
|
||||
-- 游标声明
|
||||
DECLARE table_cursor CURSOR FOR
|
||||
SELECT TABLE_NAME
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_TYPE = 'BASE TABLE'
|
||||
AND TABLE_NAME NOT IN (
|
||||
'sys_operation_log', 'sys_login_log', 'system_parameters' -- 系统表默认排除
|
||||
)
|
||||
ORDER BY TABLE_NAME;
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE;
|
||||
|
||||
-- 创建详细的日志表
|
||||
DROP TEMPORARY TABLE IF EXISTS reset_operation_log;
|
||||
CREATE TEMPORARY TABLE reset_operation_log (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
log_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
table_name VARCHAR(255) NOT NULL,
|
||||
action_type ENUM('CLEARED', 'SKIPPED', 'ERROR', 'EXCLUDED') NOT NULL,
|
||||
records_affected INT DEFAULT 0,
|
||||
sql_statement TEXT,
|
||||
error_message TEXT,
|
||||
execution_time_ms INT DEFAULT 0
|
||||
);
|
||||
|
||||
-- 开始事务(确保原子性)
|
||||
START TRANSACTION;
|
||||
|
||||
OPEN table_cursor;
|
||||
|
||||
process_tables: LOOP
|
||||
FETCH table_cursor INTO v_table_name;
|
||||
IF v_done THEN
|
||||
LEAVE process_tables;
|
||||
END IF;
|
||||
|
||||
SET v_table_count = v_table_count + 1;
|
||||
|
||||
-- 检查是否在排除列表中
|
||||
IF FIND_IN_SET(v_table_name, p_exclude_tables) > 0 OR
|
||||
FIND_IN_SET(v_table_name, v_excluded_tables) > 0 THEN
|
||||
INSERT INTO reset_operation_log (table_name, action_type, records_affected, sql_statement)
|
||||
VALUES (v_table_name, 'EXCLUDED', 0, '表在排除列表中,跳过处理');
|
||||
SET v_skipped_count = v_skipped_count + 1;
|
||||
ITERATE process_tables;
|
||||
END IF;
|
||||
|
||||
-- 检查表是否有 store_id 字段
|
||||
SELECT COUNT(*) INTO v_has_store_id
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = v_table_name
|
||||
AND COLUMN_NAME = 'store_id';
|
||||
|
||||
SET @start_time = UNIX_TIMESTAMP(NOW(3)) * 1000;
|
||||
|
||||
IF v_has_store_id > 0 THEN
|
||||
-- 构建动态SQL
|
||||
IF p_preserve_site_zero THEN
|
||||
SET @sql = CONCAT('DELETE FROM `', v_table_name, '` WHERE store_id != 0');
|
||||
ELSE
|
||||
SET @sql = CONCAT('TRUNCATE TABLE `', v_table_name, '`');
|
||||
END IF;
|
||||
|
||||
BEGIN
|
||||
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
||||
BEGIN
|
||||
GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errmsg = MESSAGE_TEXT;
|
||||
INSERT INTO reset_operation_log
|
||||
(table_name, action_type, records_affected, sql_statement, error_message)
|
||||
VALUES (v_table_name, 'ERROR', 0, @sql, CONCAT(@sqlstate, ' - ', @errmsg));
|
||||
SET v_error_count = v_error_count + 1;
|
||||
END;
|
||||
|
||||
IF p_dry_run THEN
|
||||
-- 试运行模式,只记录不执行
|
||||
INSERT INTO reset_operation_log
|
||||
(table_name, action_type, records_affected, sql_statement)
|
||||
VALUES (v_table_name, 'SKIPPED', 0, CONCAT('试运行: ', @sql));
|
||||
SET v_skipped_count = v_skipped_count + 1;
|
||||
ELSE
|
||||
-- 实际执行
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @row_count = ROW_COUNT();
|
||||
SET @end_time = UNIX_TIMESTAMP(NOW(3)) * 1000;
|
||||
|
||||
INSERT INTO reset_operation_log
|
||||
(table_name, action_type, records_affected, sql_statement, execution_time_ms)
|
||||
VALUES (v_table_name, 'CLEARED', @row_count, @sql, (@end_time - @start_time));
|
||||
|
||||
SET v_processed_count = v_processed_count + 1;
|
||||
END IF;
|
||||
END;
|
||||
ELSE
|
||||
-- 表没有 store_id 字段
|
||||
SET v_no_store_id_tables = CONCAT_WS(', ', v_no_store_id_tables, v_table_name);
|
||||
|
||||
INSERT INTO reset_operation_log
|
||||
(table_name, action_type, records_affected, sql_statement)
|
||||
VALUES (v_table_name, 'SKIPPED', 0, '表没有 store_id 字段,跳过处理');
|
||||
|
||||
SET v_skipped_count = v_skipped_count + 1;
|
||||
END IF;
|
||||
|
||||
SET v_has_store_id = 0;
|
||||
END LOOP;
|
||||
|
||||
CLOSE table_cursor;
|
||||
|
||||
IF p_dry_run THEN
|
||||
ROLLBACK; -- 试运行模式回滚
|
||||
SELECT '=== 试运行模式 - 未实际执行任何操作 ===' as notice;
|
||||
ELSE
|
||||
COMMIT; -- 提交事务
|
||||
SELECT '=== 数据库重置完成 ===' as summary;
|
||||
END IF;
|
||||
|
||||
-- 生成总结报告
|
||||
SELECT
|
||||
CONCAT('数据库: ', DATABASE()) as database_info,
|
||||
CONCAT('开始时间: ', DATE_FORMAT(MIN(log_time), '%Y-%m-%d %H:%i:%s')) as start_time,
|
||||
CONCAT('结束时间: ', DATE_FORMAT(MAX(log_time), '%Y-%m-%d %H:%i:%s')) as end_time,
|
||||
CONCAT('总表数: ', v_table_count) as total_tables,
|
||||
CONCAT('已处理表: ', v_processed_count) as processed_tables,
|
||||
CONCAT('跳过表: ', v_skipped_count) as skipped_tables,
|
||||
CONCAT('错误数: ', v_error_count) as error_count,
|
||||
CONCAT('总耗时: ', SUM(execution_time_ms), 'ms') as total_duration
|
||||
FROM reset_operation_log;
|
||||
|
||||
-- 显示没有 store_id 字段的表
|
||||
IF v_no_store_id_tables != '' THEN
|
||||
SELECT
|
||||
'以下表没有 store_id 字段,已被跳过:' as warning_title,
|
||||
v_no_store_id_tables as skipped_tables;
|
||||
END IF;
|
||||
|
||||
-- 显示错误详情
|
||||
IF v_error_count > 0 THEN
|
||||
SELECT '=== 错误详情 ===' as error_title;
|
||||
SELECT table_name, error_message, sql_statement
|
||||
FROM reset_operation_log
|
||||
WHERE action_type = 'ERROR'
|
||||
ORDER BY log_time;
|
||||
END IF;
|
||||
|
||||
-- 显示处理详情
|
||||
SELECT '=== 处理详情 ===' as details_title;
|
||||
SELECT
|
||||
table_name as '表名',
|
||||
CASE action_type
|
||||
WHEN 'CLEARED' THEN '已清空'
|
||||
WHEN 'SKIPPED' THEN '已跳过'
|
||||
WHEN 'ERROR' THEN '错误'
|
||||
WHEN 'EXCLUDED' THEN '已排除'
|
||||
END as '操作状态',
|
||||
records_affected as '影响行数',
|
||||
execution_time_ms as '耗时(ms)',
|
||||
CASE
|
||||
WHEN error_message IS NOT NULL THEN error_message
|
||||
ELSE LEFT(sql_statement, 100)
|
||||
END as '详情'
|
||||
FROM reset_operation_log
|
||||
ORDER BY action_type, table_name;
|
||||
|
||||
-- 性能统计
|
||||
SELECT '=== 性能统计 ===' as performance_title;
|
||||
SELECT
|
||||
action_type as '操作类型',
|
||||
COUNT(*) as '表数量',
|
||||
SUM(records_affected) as '总影响行数',
|
||||
AVG(execution_time_ms) as '平均耗时(ms)',
|
||||
SUM(execution_time_ms) as '总耗时(ms)'
|
||||
FROM reset_operation_log
|
||||
GROUP BY action_type;
|
||||
|
||||
-- 清理
|
||||
DROP TEMPORARY TABLE IF EXISTS reset_operation_log;
|
||||
END
|
||||
$$
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
-- 实际执行(保留site_id=0的数据,排除user和config表)
|
||||
CALL reset_tables_has_store_id_where(TRUE, FALSE, '');
|
||||
```
|
||||
27
docs/diy/RADEME.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# 自定义页面
|
||||
|
||||
|
||||
## 对应的组件存放到数据表中
|
||||
|
||||
表: lucky_diy_view_util
|
||||
|
||||
```sql
|
||||
create table if not exists lucky_diy_view_util
|
||||
(
|
||||
id int auto_increment
|
||||
primary key,
|
||||
name varchar(50) default '' not null comment '标识',
|
||||
title varchar(50) default '' not null comment '组件名称',
|
||||
type varchar(50) default 'SYSTEM' not null comment '组件类型',
|
||||
value text null comment '配置:json格式',
|
||||
addon_name varchar(50) default '' not null comment '插件标识',
|
||||
sort int default 0 not null comment '排序号',
|
||||
support_diy_view varchar(500) default '' not null comment '支持的自定义页面(为空表示公共组件都支持)',
|
||||
max_count int default 0 not null comment '限制添加次数',
|
||||
is_delete int default 0 not null comment '是否可以删除,0 允许,1 禁用',
|
||||
icon varchar(255) default '' not null comment '组件图标',
|
||||
icon_type int default 0 not null comment '0图片1图标',
|
||||
constraint name
|
||||
unique (name)
|
||||
)
|
||||
```
|
||||
@@ -3,23 +3,22 @@ APP_TRACE = true
|
||||
|
||||
[APP]
|
||||
DEFAULT_TIMEZONE = Asia/Shanghai
|
||||
ENV_MODE = development
|
||||
|
||||
[LANG]
|
||||
default_lang = zh-cn
|
||||
|
||||
[DATABASE]
|
||||
TYPE = mysql
|
||||
HOSTNAME = 127.0.0.1
|
||||
DATABASE = shop_mallnew_dev
|
||||
USERNAME = root
|
||||
PASSWORD = root
|
||||
HOSTNAME = db
|
||||
DATABASE = shop_mallnew
|
||||
USERNAME = shop_mallnew
|
||||
PASSWORD = shop_mallnew
|
||||
HOSTPORT = 3306
|
||||
CHARSET = utf8
|
||||
CHARSET = utf8mb4
|
||||
DEBUG = true
|
||||
|
||||
[redis]
|
||||
HOST = 127.0.0.1
|
||||
[REDIS]
|
||||
HOST = redis
|
||||
PORT = 6379
|
||||
PASSWORD = ''
|
||||
PASSWORD = 'luckyshop123!@#'
|
||||
EXPIRY = 604800
|
||||
@@ -10,16 +10,16 @@ default_lang = zh-cn
|
||||
|
||||
[DATABASE]
|
||||
TYPE = mysql
|
||||
HOSTNAME = production_mysql_host
|
||||
DATABASE = shop_mallnew_prod
|
||||
USERNAME = prod_user
|
||||
PASSWORD = prod_password
|
||||
HOSTNAME = db
|
||||
DATABASE = shop_mallnew
|
||||
USERNAME = shop_mallnew
|
||||
PASSWORD = shop_mallnew
|
||||
HOSTPORT = 3306
|
||||
CHARSET = utf8
|
||||
CHARSET = utf8mb4
|
||||
DEBUG = false
|
||||
|
||||
[redis]
|
||||
HOST = production_redis_host
|
||||
[REDIS]
|
||||
HOST = redis
|
||||
PORT = 6379
|
||||
PASSWORD = production_redis_password
|
||||
EXPIRY = 86400
|
||||
PASSWORD = 'luckyshop123!@#'
|
||||
EXPIRY = 604800
|
||||
@@ -1,22 +1,24 @@
|
||||
APP_DEBUG = true
|
||||
APP_TRACE = true
|
||||
|
||||
[APP]
|
||||
DEFAULT_TIMEZONE = Asia/Shanghai
|
||||
|
||||
[LANG]
|
||||
default_lang = zh-cn
|
||||
|
||||
[DATABASE]
|
||||
TYPE = mysql
|
||||
HOSTNAME = newshop_mysql
|
||||
DATABASE = shop_dev
|
||||
HOSTNAME = db
|
||||
DATABASE = shop_mallnew
|
||||
USERNAME = shop_mallnew
|
||||
PASSWORD = shop_mallnew
|
||||
HOSTPORT = 3306
|
||||
CHARSET = utf8
|
||||
CHARSET = utf8mb4
|
||||
DEBUG = true
|
||||
[RRDATABASE]
|
||||
HOSTNAME = 192.168.2.64
|
||||
[redis]
|
||||
HOST = newshop_redis
|
||||
|
||||
[REDIS]
|
||||
HOST = redis
|
||||
PORT = 6379
|
||||
PASSWORD = 'luckyshop123!@#'
|
||||
EXPIRY = 604800
|
||||
24
src/.env.test
Normal file
@@ -0,0 +1,24 @@
|
||||
APP_DEBUG = true
|
||||
APP_TRACE = true
|
||||
|
||||
[APP]
|
||||
DEFAULT_TIMEZONE = Asia/Shanghai
|
||||
|
||||
[LANG]
|
||||
default_lang = zh-cn
|
||||
|
||||
[DATABASE]
|
||||
TYPE = mysql
|
||||
HOSTNAME = db
|
||||
DATABASE = shop_mallnew
|
||||
USERNAME = shop_mallnew
|
||||
PASSWORD = shop_mallnew
|
||||
HOSTPORT = 3306
|
||||
CHARSET = utf8mb4
|
||||
DEBUG = true
|
||||
|
||||
[REDIS]
|
||||
HOST = redis
|
||||
PORT = 6379
|
||||
PASSWORD = 'luckyshop123!@#'
|
||||
EXPIRY = 604800
|
||||
@@ -1 +0,0 @@
|
||||
HUIqMBxOaiPqppGTd9WxmjJwFwX3x-hpbnI1xujpUW4.m7oE98dVL-9XQE2gAJ9VpnsbTfOJ2csnVDIzFqe9osw
|
||||
@@ -1 +0,0 @@
|
||||
39aLknx0mLrzjinZRvjc5pYAXUkRq0eo
|
||||
201
src/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2020 上海牛之云网络科技有限公司
|
||||
|
||||
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.
|
||||
144
src/README.md
Normal file
@@ -0,0 +1,144 @@
|
||||

|
||||
|
||||
### 产品介绍
|
||||
|
||||
### Niushop开源商城单商户V4
|
||||
**快速搭建专属店铺,迅速展开线上业务** <br/>
|
||||
NIUSHOP开源商城B2C单商户V4,功能强大,安全便捷,框架成熟稳定便于扩展,源码100%开源,支持二次开发定制,让企业用更低的成本、更少的人力,更快的速度构建自己的商城,开启网上商城销售业务。
|
||||
|
||||
### 公司介绍
|
||||
|
||||
上海牛之云网络科技有限公司成立于2016年,是一家从事移动互联网,电商软件为主导的技术研发型企业。公司总部位于上海,研发中心设立于锦绣龙城太原市,目前有团队成员50多人,产品研发实力雄厚。拥有NIUSHOP开源商城,NIUCLOUD开源小程序应用市场,牛客云商企业级SAAS电商服务平台等产品。业务遍及全国26个省市,在北京、广州、上海、深圳等地区拥有多家合作企业。我们始终保持专业、专注、专一的原则,旨在为用户提供最好用的全功能型电商软件产品,是您转型新零售,新媒体,掘金千亿电商市场的首选。
|
||||
|
||||
### 操作指南
|
||||
|
||||
[Niushop开源商城单商户V4使用手册](https://www.kancloud.cn/niucloud/niushop_b2c_v4/1842076)
|
||||
| [api文档地址](https://www.kancloud.cn/niucloud/niushop_b2c_v4_api/1830441)
|
||||
| [二开手册](https://www.kancloud.cn/niucloud/niushop_b2c_v4_develop/1830902)
|
||||
| [论坛地址](https://bbs.niushop.com.cn/forum.php)
|
||||
| [官网地址](https://www.niushop.com)
|
||||
- - -
|
||||
|
||||
|
||||
### 推荐阿里云服务器配置
|
||||
|
||||
阿里云2000元代金劵:<a href="https://www.aliyun.com/minisite/goods?userCode=hvxtm3ee">马上领取</a>
|
||||
|
||||

|
||||
|
||||
### 商城特色:
|
||||
强大的营销功能模块,丰富的行业模板和装修组件,快速搭建最适合自己的电商平台,轻松获客、裂变。开启电商运营之路。
|
||||
|
||||
1. <img src="https://images.gitee.com/uploads/images/2020/0724/121556_a96bd648_6569472.png"/> ThinkPhp6 + LayUi + ElementUi,学习维护成本低<br/>
|
||||
2. <img src="https://images.gitee.com/uploads/images/2020/0724/121615_f801f981_6569472.png"/> 前端由UNI-APP框架编写,支持多端,易于维护<br/>
|
||||
3. <img src="https://images.gitee.com/uploads/images/2020/0724/121635_e51987c4_6569472.png"/> 钩子 + 插件,组件化开发,可复用,开发便捷<br/>
|
||||
4. <img src="https://images.gitee.com/uploads/images/2020/0724/121645_df103f55_6569472.png"/> 标准API接口,前后端分离,二次开发更方便<br/>
|
||||
5. <img src="https://images.gitee.com/uploads/images/2020/0724/121708_74c55984_6569472.png"/> 代码全部开源,方便企业扩展自身业务需求
|
||||
|
||||
### Niushop官方群
|
||||
Niushop商城qq开发群1:<a href="https://jq.qq.com/?_wv=1027&k=VrVzi1FI" target="_blank"></a>| qq开发群2:<a href="https://jq.qq.com/?_wv=1027&k=MCtjz6B9" target="_blank"> </a>| qq开发群3:<a href="https://jq.qq.com/?_wv=1027&k=H9FLIfTP" target="_blank"></a>
|
||||
|
||||
### 体验演示二维码
|
||||

|
||||
|
||||
#### :fire: 体验演示站后台:[<a href='https://uniapp.v4.niuteam.cn/' target="_blank"> 查看 </a>]
|
||||
<a href='https://uniapp.v4.niuteam.cn/' target="_blank">https://uniapp.v4.niuteam.cn/</a> 账号:test 密码:123456
|
||||
|
||||
### 开源版使用须知
|
||||
|
||||
1.仅允许用于个人学习研究使用;
|
||||
|
||||
2.开源版不建议商用,如果商用必须保留版权信息,望自觉遵守;
|
||||
|
||||
3.禁止将本开源的代码和资源进行任何形式任何名义的出售,否则产生的一切任何后果责任由侵权者自负。
|
||||
|
||||
4.推荐3名会员关注公众号,将免费获得单商户V4基础版授权。
|
||||
|
||||
5.绑定码云评论并点赞支持可获得单商户V4基础版授权
|
||||
|
||||
6.本基础版本后台源码全部开源,小程序为uniapp编译版,如需小程序源码,请点击下方图片马上获取。
|
||||
|
||||
<a href='https://www.niushop.com/web/index/promotion' target="_blank"><img src="https://images.gitee.com/uploads/images/2020/0805/103536_c7d1b001_6569472.png"/></a>
|
||||
|
||||
### 技术亮点
|
||||
|
||||
1.框架采用全新thinkphp6+事件开发设计+layui+uniapp进行设计,代码完全重构,采用支持百万级!
|
||||
|
||||
2.前端以layui + uniapp模块化开发;
|
||||
|
||||
3.数据导出采用phpExcel,使数据更加直观,更方便于管理统计;
|
||||
|
||||
4.插件钩子机制,功能模块独立,更有助于二次开发;
|
||||
|
||||
5.后台采用ECharts,直观体现关系数据可视化的图,支持图与图之间的混搭。实现完善的数据统计和分析;
|
||||
|
||||
6.EasyWeChat部署微信开发,微信接入更加快捷,简单;
|
||||
|
||||
7.内置强大灵活的权限管理体系,有利于专人专项运营;
|
||||
|
||||
8.内置组合数据统计,系统配置,管理碎片化数据统计;
|
||||
|
||||
9.客户端完善的交互效果和动画,提升用户端视觉体验;
|
||||
|
||||
10.可以完美对接公众号和小程序,并且数据同步,实现真正意义上的一端开发,多端使用;
|
||||
|
||||
11.内置客服系统,可以对接微信客服,客服在线实时聊天;
|
||||
|
||||
12.高频数据缓存,数据库读写分离,很大程度减轻服务器压力,提升访问速度;
|
||||
|
||||
13.后台设置菜单中可以一键数据备份和恢复,完全傻瓜式操作就可以轻松升级备份;
|
||||
|
||||
14.在线一键升级,轻松跨越到最新版本;
|
||||
|
||||
15.标准Api接口、前后端分离,二次开发更方便快捷;
|
||||
|
||||
16.支持数据库结构、数据、模板在线缓存清除,提升用户体验;
|
||||
|
||||
17.可视化DIY店铺装修,方便、快捷、直观,可以随心所欲装扮自己的店铺;
|
||||
|
||||
18.无缝事件机制行为扩展更方便,方便二次开发;
|
||||
|
||||
19.支持队列降低流量高峰,解除代码耦合性,高可用性;
|
||||
|
||||
20.在线一键安装部署,自动检测系统环境一键安装,省时省力快捷部署;
|
||||
|
||||
|
||||
### 系统功能
|
||||

|
||||
|
||||
### 页面展示
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
### 合作伙伴
|
||||
.png")
|
||||
|
||||
|
||||
### 版权信息
|
||||
|
||||
版权所有Copyright © 2015-2020 NiuShop开源商城 版权所有
|
||||
|
||||
All rights reserved。
|
||||
|
||||
上海牛之云网络科技有限公司 提供技术支持
|
||||
@@ -253,7 +253,7 @@ class Enterprise extends BaseModel
|
||||
*/
|
||||
public function deleteVideo($condition)
|
||||
{
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($condition,true));
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export($condition,true));
|
||||
$check_condition = array_column($condition, 2, 0);
|
||||
$site_id = $check_condition['site_id'] ?? '';
|
||||
if ($site_id === '') {
|
||||
|
||||
@@ -381,33 +381,40 @@ class FenxiaoOrder extends BaseModel
|
||||
*/
|
||||
public function getFenxiaoOrderPage($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = 'order_id DESC')
|
||||
{
|
||||
$field = 'order_id,order_no,site_name,member_name,create_time,is_settlement,fenxiao_order_id';
|
||||
$list = model('fenxiao_order')->pageList($condition, $field, $order, $page, $page_size, 'fo', [], 'order_id');
|
||||
// 解决GROUP BY问题:只查询主键ID,避免ONLY_FULL_GROUP_BY错误
|
||||
$field = 'DISTINCT fo.order_id';
|
||||
$list = model('fenxiao_order')->pageList($condition, $field, $order, $page, $page_size, 'fo', [], 'fo.order_id');
|
||||
if (!empty($list[ 'list' ])) {
|
||||
$order_id_arr = [];
|
||||
foreach ($list['list'] as $k => $v)
|
||||
{
|
||||
$order_id_arr[] = $v['order_id'];
|
||||
}
|
||||
$order_ids = implode(',', $order_id_arr);
|
||||
$order_list = model('order')->getList([ [ 'order_id', 'in', $order_ids ] ], 'order_id,name,full_address,mobile,order_status_name');
|
||||
$order_goods_list = model('fenxiao_order')->getList([ [ 'order_id', 'in', $order_ids ] ]);
|
||||
foreach ($list[ 'list' ] as $k => $item) {
|
||||
foreach ($order_list as $k_order => $v_order)
|
||||
{
|
||||
if($item['order_id'] == $v_order['order_id'])
|
||||
{
|
||||
$list[ 'list' ][ $k ] = array_merge($list[ 'list' ][ $k ], $v_order);
|
||||
}
|
||||
}
|
||||
$list[ 'list' ][ $k ][ 'order_goods' ] = [];
|
||||
foreach ($order_goods_list as $k_order_goods => $v_order_goods)
|
||||
{
|
||||
if($item['order_id'] == $v_order_goods['order_id'])
|
||||
{
|
||||
$list[ 'list' ][ $k ][ 'order_goods' ][] = $v_order_goods;
|
||||
}
|
||||
if (!empty($order_id_arr)) {
|
||||
$order_ids = implode(',', $order_id_arr);
|
||||
|
||||
// 重新查询完整的分销订单数据
|
||||
$fenxiao_orders = model('fenxiao_order')->getList([ [ 'fo.order_id', 'in', $order_ids ] ],
|
||||
'fo.order_id, fo.order_no, fo.site_name, fo.member_name, fo.create_time, fo.is_settlement, fo.fenxiao_order_id',
|
||||
'', 'fo');
|
||||
|
||||
// 重新组织数据
|
||||
$new_list = [];
|
||||
foreach ($fenxiao_orders as $fenxiao_order) {
|
||||
$order_list = model('order')->getInfo([ [ 'order_id', '=', $fenxiao_order['order_id'] ] ],
|
||||
'name,full_address,mobile,order_status_name');
|
||||
$order_goods_list = model('fenxiao_order')->getList([ [ 'order_id', '=', $fenxiao_order['order_id'] ] ]);
|
||||
|
||||
// 合并数据
|
||||
$new_item = array_merge($fenxiao_order, [
|
||||
'address' => $order_list['full_address'] ?? '',
|
||||
'mobile' => $order_list['mobile'] ?? '',
|
||||
'order_status_name' => $order_list['order_status_name'] ?? '',
|
||||
'order_goods' => $order_goods_list
|
||||
]);
|
||||
$new_list[] = $new_item;
|
||||
}
|
||||
$list['list'] = $new_list;
|
||||
}
|
||||
}
|
||||
return $this->success($list);
|
||||
|
||||
@@ -44,7 +44,7 @@ class Poster extends BaseModel
|
||||
[
|
||||
'action' => 'imageCopy', // 背景图
|
||||
'data' => [
|
||||
'upload/poster/bg/fenxiao_3.png',
|
||||
'upload/poster/bg/fenxiao_2.png',
|
||||
0,
|
||||
0,
|
||||
720,
|
||||
@@ -125,18 +125,18 @@ class Poster extends BaseModel
|
||||
1
|
||||
]
|
||||
],
|
||||
// [
|
||||
// 'action' => 'imageText', // 写入分享语
|
||||
// 'data' => [
|
||||
// $params[ 'template_json' ][ 'share_content' ],
|
||||
// $params[ 'template_json' ][ 'share_content_font_size' ] * $fontRate * 2,
|
||||
// is_array($params[ 'template_json' ][ 'share_content_color' ]) ? $params[ 'template_json' ][ 'share_content_color' ] : hex2rgb($params[ 'template_json' ][ 'share_content_color' ]),
|
||||
// $params[ 'template_json' ][ 'share_content_left' ] * 2,
|
||||
// ( $params[ 'template_json' ][ 'share_content_top' ] + $params[ 'template_json' ][ 'share_content_font_size' ] ) * 2,
|
||||
// $params[ 'template_json' ][ 'share_content_width' ] * 2,
|
||||
// 1
|
||||
// ]
|
||||
// ],
|
||||
[
|
||||
'action' => 'imageText', // 写入分享语
|
||||
'data' => [
|
||||
$params[ 'template_json' ][ 'share_content' ],
|
||||
$params[ 'template_json' ][ 'share_content_font_size' ] * $fontRate * 2,
|
||||
is_array($params[ 'template_json' ][ 'share_content_color' ]) ? $params[ 'template_json' ][ 'share_content_color' ] : hex2rgb($params[ 'template_json' ][ 'share_content_color' ]),
|
||||
$params[ 'template_json' ][ 'share_content_left' ] * 2,
|
||||
( $params[ 'template_json' ][ 'share_content_top' ] + $params[ 'template_json' ][ 'share_content_font_size' ] ) * 2,
|
||||
$params[ 'template_json' ][ 'share_content_width' ] * 2,
|
||||
1
|
||||
]
|
||||
],
|
||||
[
|
||||
'action' => 'imageCopy', // 写入用户头像
|
||||
'data' => [
|
||||
@@ -179,7 +179,7 @@ class Poster extends BaseModel
|
||||
$pic_name = "_" . $pic_name;
|
||||
}
|
||||
|
||||
$res = $option_res->jpeg('upload/poster/distribution', 'distribution_0516' . $qrcode_param[ 'source_member' ] . $pic_name . '_' . $app_type);
|
||||
$res = $option_res->jpeg('upload/poster/distribution', 'distribution_' . $qrcode_param[ 'source_member' ] . $pic_name . '_' . $app_type);
|
||||
if ($res[ 'code' ] == 0) {
|
||||
$upload = new Upload($site_id);
|
||||
$cloud_res = $upload->fileCloud($res[ 'data' ][ 'path' ]);
|
||||
|
||||
@@ -49,14 +49,14 @@ class PosterTemplate extends BaseModel
|
||||
'nickname_top' => 515,
|
||||
'nickname_left' => 20,
|
||||
//分享语
|
||||
// 'share_content' => '邀您一起分享赚佣金',
|
||||
// 'share_content_is_show' => 1,
|
||||
// 'share_content_font_size' => 14,
|
||||
// 'share_content_color' => '#8D8D8D',
|
||||
// 'share_content_width' => 130,
|
||||
// 'share_content_height' => 30,
|
||||
// 'share_content_top' => 550,
|
||||
// 'share_content_left' => 20,
|
||||
'share_content' => '邀您一起分享赚佣金',
|
||||
'share_content_is_show' => 1,
|
||||
'share_content_font_size' => 14,
|
||||
'share_content_color' => '#8D8D8D',
|
||||
'share_content_width' => 130,
|
||||
'share_content_height' => 30,
|
||||
'share_content_top' => 550,
|
||||
'share_content_left' => 20,
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class Form extends BaseApi
|
||||
['form_type', '=', 'custom' ]
|
||||
];
|
||||
$data = (new FormModel())->getFormInfo($condition, 'json_data,form_name');
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($data,true));
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export($data,true));
|
||||
if (!empty($data['data'])) {
|
||||
$data['data']['json_data'] = json_decode($data['data']['json_data'], true);
|
||||
// $data['data']['title'] = model('form')->getValue(['id'=>$form_id]);
|
||||
|
||||
@@ -7,16 +7,12 @@
|
||||
.layui-layout-admin.admin-style-2 .table-tab .layui-tab-title{margin-bottom: 15px;}
|
||||
.form-img .form-img-wrap {width: 25px;height: 25px;margin: 0 10px 10px 0;border: 1px solid #EAEAEA;display: flex;align-items: center;justify-items: center}
|
||||
.form-img-wrap img {max-width: 100%;max-height: 100%;height: auto;width: 100%}
|
||||
.layui-layer-content .layui-form-label {width: 250px}
|
||||
.layui-layer-content .layui-form-label {width: 100px}
|
||||
.layui-layer-content .layui-form-label + .layui-input-block {margin-left: 100px}
|
||||
.layui-table .form-data-wrap {max-height: 50px;overflow: hidden}
|
||||
.layui-table .layui-form-item {margin-bottom: 0}
|
||||
.layui-table .layui-form-label {width: auto;height: 25px;line-height: 25px;}
|
||||
.layui-table .layui-form-label + .layui-input-block {margin-left: 0; height: 25px;line-height: 25px;min-height: unset;}
|
||||
.layui-layer-content .form-data-wrap{
|
||||
height: 700px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -211,7 +207,7 @@ layui.use(['form', 'laydate', 'laytpl'], function() {
|
||||
laytpl($('#formData').html()).render(data, function(string){
|
||||
layer.open({
|
||||
title: '查看信息',
|
||||
area: ["600px","800px"],
|
||||
area: "400px",
|
||||
type: 1,
|
||||
content: string,
|
||||
btn: ['确认']
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<!-- <script src="MEMBERRECHARGE_JS/order_list.js?v={$version}"></script> -->
|
||||
<script src="MEMBERRECHARGE_JS/order_list.js?v={$version}"></script>
|
||||
<script>
|
||||
var form,laypage,element,laydate;
|
||||
var is_refresh = false;
|
||||
|
||||
@@ -22,7 +22,7 @@ return [
|
||||
'name' => 'MINGPIAN_INFO',
|
||||
'title' => '电子名片',
|
||||
'parent' => 'BASICS_LINK',
|
||||
'wap_url' => '',
|
||||
'wap_url' => '/pages/contact/contact',
|
||||
'web_url' => '',
|
||||
'sort' => 0
|
||||
]
|
||||
|
||||
@@ -253,7 +253,7 @@ class Enterprise extends BaseModel
|
||||
*/
|
||||
public function deleteVideo($condition)
|
||||
{
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($condition,true));
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export($condition,true));
|
||||
$check_condition = array_column($condition, 2, 0);
|
||||
$site_id = $check_condition['site_id'] ?? '';
|
||||
if ($site_id === '') {
|
||||
|
||||
@@ -25,7 +25,7 @@ class Personnel extends BaseShop
|
||||
} else {
|
||||
|
||||
$config['value'] = json_decode($config['value'],true);
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($config,true));
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export($config,true));
|
||||
$this->assign('config', $config);
|
||||
return $this->fetch('personnel/diy');
|
||||
}
|
||||
|
||||
146
src/addon/personnel/shop/view/enterprise/add.html
Normal file
@@ -0,0 +1,146 @@
|
||||
|
||||
|
||||
<style>
|
||||
.upload-img-block .upload-img-box .upload-default{position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}
|
||||
</style>
|
||||
|
||||
|
||||
<div class="layui-form form-wrap">
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label short-label"><span class="required">*</span>文件名称:</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="files_title" lay-verify="required" maxlength="40" autocomplete="off" placeholder="请输入标题" class="layui-input len-long">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"><span class="required">*</span>文件:</label>
|
||||
<div class="layui-input-block">
|
||||
<p class="file-upload">未上传</p>
|
||||
<button type="button" class="layui-btn" id="cert_upload">
|
||||
<i class="layui-icon"></i>上传文件
|
||||
</button>
|
||||
<input type="hidden" name="files_url" class="layui-input len-long" value="{$info.files_url ?? ''}" lay-verify="files_url">
|
||||
<input type="hidden" name="size" class="layui-input len-long" value="{$info.size ?? ''}">
|
||||
</div>
|
||||
<div class="word-aux">请选择pdf文件</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row" id="save">
|
||||
<button class="layui-btn" lay-submit lay-filter="save">提交</button>
|
||||
<button class="layui-btn layui-btn-primary" onclick="back()">返回</button>
|
||||
</div>
|
||||
<!-- <div class="form-row" id="save1" style="display: none;">
|
||||
<div style="color:red">(文件越大解码时间越长)上传解码中,请稍等...</div>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var form,repeat_flag,
|
||||
IMAGE_MAX = 9, //最多可以上传多少张图片
|
||||
imageCollection = [], //图片集合
|
||||
selectedGoodsId = [],
|
||||
goods_id = [],
|
||||
goods_list =[];
|
||||
|
||||
layui.use(['form'], function() {
|
||||
form = layui.form;
|
||||
repeat_flag = false;
|
||||
|
||||
form.render();
|
||||
|
||||
// 单图上传
|
||||
$("body").off("click", "#img").on("click", "#img", function () {
|
||||
openAlbum(function (data) {
|
||||
imageCollection = [];
|
||||
imageCollection.push(data[0].pic_path);
|
||||
imageCollection.splice(1, imageCollection.length);
|
||||
var val = '<img src="' + ns.img(imageCollection[0]) + '" alt="">';
|
||||
$("#img").html(val);
|
||||
}, 1);
|
||||
});
|
||||
|
||||
/**
|
||||
* 表单提交(立即发布)
|
||||
*/
|
||||
form.on('submit(save)', function(data){
|
||||
field = data.field;
|
||||
field.status = 1;
|
||||
formSubmit(field)
|
||||
});
|
||||
|
||||
new Upload({
|
||||
elem: '#cert_upload',
|
||||
url: ns.url("personnel://shop/enterprise/uploadfile"),
|
||||
accept: 'file',
|
||||
callback:function (res) {
|
||||
if (res.code >= 0) {
|
||||
layer.msg('上传成功');
|
||||
$("input[name='files_url']").val(res.data.path);
|
||||
$("input[name='size']").val(res.data.size);
|
||||
$("input[name='files_url']").siblings(".file-upload").text("已上传");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 提交
|
||||
*/
|
||||
function formSubmit(data){
|
||||
// if (!imageCollection.length){
|
||||
// layer.msg('请选择封面图!', {icon: 5, anim: 6});
|
||||
// return;
|
||||
// }
|
||||
// $('#save').hide()
|
||||
// $('#save1').show()
|
||||
if(repeat_flag) return;
|
||||
repeat_flag = true;
|
||||
setTimeout(function (a) {
|
||||
console.log(1236)
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
dataType: 'JSON',
|
||||
url: ns.url("personnel://shop/enterprise/add"),
|
||||
data: data,
|
||||
async: false,
|
||||
success: function(res){
|
||||
repeat_flag = false;
|
||||
if (res.code == 0) {
|
||||
layer.confirm('添加成功', {
|
||||
title:'操作提示',
|
||||
btn: ['返回列表', '继续添加'],
|
||||
closeBtn: 0,
|
||||
yes: function(index, layero){
|
||||
if(data.status == 1){
|
||||
location.hash = ns.hash("personnel://shop/enterprise/lists");
|
||||
}else{
|
||||
location.hash = ns.hash("personnel://shop/enterprise/drafts");
|
||||
}
|
||||
layer.close(index);
|
||||
},
|
||||
btn2: function(index, layero) {
|
||||
listenerHash(); // 刷新页面
|
||||
layer.close(index);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
layer.msg(res.message);
|
||||
}
|
||||
$('#save').show()
|
||||
$('#save1').hide()
|
||||
}
|
||||
})
|
||||
}, 1000);
|
||||
|
||||
}
|
||||
|
||||
function back() {
|
||||
location.hash = ns.hash("personnel://shop/enterprise/lists");
|
||||
}
|
||||
</script>
|
||||
137
src/addon/personnel/shop/view/enterprise/edit.html
Normal file
@@ -0,0 +1,137 @@
|
||||
|
||||
|
||||
<style>
|
||||
.upload-img-block .upload-img-box .upload-default{position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<div class="layui-form form-wrap">
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label short-label"><span class="required">*</span>文件标题:</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="files_title" value="{$info.files_title}" lay-verify="required" maxlength="40" autocomplete="off" placeholder="请输入标题" class="layui-input len-long">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"><span class="required">*</span>文件:</label>
|
||||
<div class="layui-input-block">
|
||||
{notempty name="$info.files_url"}
|
||||
<p class="file-upload">已上传</p>
|
||||
{else/}
|
||||
<p class="file-upload">未上传</p>
|
||||
{/notempty}
|
||||
<button type="button" class="layui-btn" id="cert_upload">
|
||||
<i class="layui-icon"></i>上传文件
|
||||
</button>
|
||||
<input type="hidden" name="files_url" class="layui-input len-long" value="{$info.files_url ?? ''}" lay-verify="files_url">
|
||||
<input type="hidden" name="size" class="layui-input len-long" value="{$info.size ?? ''}">
|
||||
</div>
|
||||
<div class="word-aux">请选择pdf文件</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<input type="hidden" name="files_id" value="{$info.files_id}" />
|
||||
|
||||
<div class="form-row" id="save">
|
||||
<button class="layui-btn" lay-submit lay-filter="save">保存</button>
|
||||
<button class="layui-btn layui-btn-primary" onclick="back()">返回</button>
|
||||
</div>
|
||||
|
||||
<!-- <div class="form-row" id="save1" style="display: none;">
|
||||
<div style="color:red">(文件越大解码时间越长)上传解码中,请稍等...</div>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var form,upload, repeat_flag = false;
|
||||
|
||||
layui.use(['form'], function() {
|
||||
form = layui.form;
|
||||
form.render();
|
||||
|
||||
|
||||
/**
|
||||
* 表单提交(立即发布)
|
||||
*/
|
||||
form.on('submit(save)', function(data){
|
||||
field = data.field;
|
||||
field.status = 1;
|
||||
formSubmit(field)
|
||||
});
|
||||
|
||||
new Upload({
|
||||
elem: '#cert_upload',
|
||||
url: ns.url("/personnel://shop/enterprise/uploadfile"),
|
||||
accept: 'file',
|
||||
callback:function (res) {
|
||||
if (res.code >= 0) {
|
||||
layer.msg('上传成功');
|
||||
$("input[name='files_url']").val(res.data.path);
|
||||
$("input[name='size']").val(res.data.size);
|
||||
$("input[name='files_url']").siblings(".file-upload").text("已上传");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* 提交
|
||||
*/
|
||||
function formSubmit(field)
|
||||
{
|
||||
// if (!imageCollection.length){
|
||||
// layer.msg('请选择封面图!', {icon: 5, anim: 6});
|
||||
// return;
|
||||
// }
|
||||
|
||||
// $('#save').hide()
|
||||
// $('#save1').show()
|
||||
|
||||
if(repeat_flag) return;
|
||||
repeat_flag = true;
|
||||
setTimeout(function (a) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
dataType: 'JSON',
|
||||
url: ns.url("personnel://shop/enterprise/edit"),
|
||||
data: field,
|
||||
async: false,
|
||||
success: function(res){
|
||||
repeat_flag = false;
|
||||
|
||||
if (res.code == 0) {
|
||||
layer.confirm('编辑成功', {
|
||||
title:'操作提示',
|
||||
btn: ['返回列表', '继续编辑'],
|
||||
yes: function(index, layero){
|
||||
if(field.status == 1){
|
||||
location.hash = ns.hash("personnel://shop/enterprise/lists");
|
||||
}else{
|
||||
location.hash = ns.hash("personnel://shop/enterprise/drafts");
|
||||
}
|
||||
layer.close(index);
|
||||
},
|
||||
btn2: function(index, layero) {
|
||||
layer.close(index);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
layer.msg(res.message);
|
||||
}
|
||||
}
|
||||
})
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function back() {
|
||||
location.hash = ns.hash("personnel://shop/enterprise/lists");
|
||||
}
|
||||
</script>
|
||||
BIN
src/addon/personnel/shop/view/enterprise/fileicon.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/addon/personnel/shop/view/enterprise/icon.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
155
src/addon/personnel/shop/view/enterprise/lists.html
Normal file
@@ -0,0 +1,155 @@
|
||||
<!-- 搜索框 -->
|
||||
<div class="single-filter-box top">
|
||||
<button class="layui-btn" onclick="add()">添加文件</button>
|
||||
|
||||
<div class="layui-form">
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="search_text" placeholder="请输入标题" autocomplete="off" class="layui-input">
|
||||
<button type="button" class="layui-btn layui-btn-primary" lay-filter="search" lay-submit>
|
||||
<i class="layui-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input id="url" value="" type="hidden"></input>
|
||||
<!-- 列表 -->
|
||||
<table id="files_list" lay-filter="files_list"></table>
|
||||
|
||||
<!-- 操作 -->
|
||||
<script type="text/html" id="operation">
|
||||
<div class="table-btn">
|
||||
<!-- <a class="layui-btn" lay-event="copy">复制链接</a> -->
|
||||
<a class="layui-btn" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn" lay-event="delete">删除</a>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 用户信息 -->
|
||||
<script type="text/html" id="info">
|
||||
<div class='table-title'>
|
||||
<div class='title-pic headimg'>
|
||||
<img layer-src src="{{ns.img('addon/personnel/shop/view/enterprise/fileicon.png')}}" onerror="this.src = '{:img('addon/personnel/shop/view/enterprise/fileicon.png')}' ">
|
||||
</div>
|
||||
<div class='title-content'>
|
||||
<p class="layui-elip" style="font-weight: 600;color:#000">{{d.files_title}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
var form,laytpl,table, repeat_flag = false;//防重复标识;
|
||||
layui.use(['form','laytpl'], function() {
|
||||
form = layui.form;
|
||||
laytpl = layui.laytpl;
|
||||
form.render();
|
||||
|
||||
table = new Table({
|
||||
elem: '#files_list',
|
||||
url: ns.url("personnel://shop/enterprise/lists"),
|
||||
cols: [
|
||||
[ {
|
||||
field: 'files_title',
|
||||
title: '文件名称',
|
||||
width: '30%',
|
||||
unresize: 'false',
|
||||
templet: '#info'
|
||||
},
|
||||
// {
|
||||
// field: 'category_name',
|
||||
// title: '文件分类',
|
||||
// width: '20%',
|
||||
// unresize: 'false'
|
||||
// },
|
||||
/*{
|
||||
field: 'create_time',
|
||||
title: '创建时间',
|
||||
width: '20%',
|
||||
unresize: 'false',
|
||||
templet: function (data) {
|
||||
return ns.time_to_date(data.createtime);
|
||||
}
|
||||
}, */{
|
||||
title: '操作',
|
||||
toolbar: '#operation',
|
||||
unresize: 'false',
|
||||
align : 'right'
|
||||
}]
|
||||
],
|
||||
});
|
||||
|
||||
/**
|
||||
* 搜索功能
|
||||
*/
|
||||
form.on('submit(search)', function(data) {
|
||||
table.reload({
|
||||
page: {
|
||||
curr: 1
|
||||
},
|
||||
where: data.field
|
||||
});
|
||||
});
|
||||
|
||||
table.on("sort",function (obj) {
|
||||
table.reload({
|
||||
page: {
|
||||
curr: 1
|
||||
},
|
||||
where: {
|
||||
order:obj.field,
|
||||
sort:obj.type
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 监听工具栏操作
|
||||
*/
|
||||
table.tool(function(obj) {
|
||||
var data = obj.data;
|
||||
switch (obj.event) {
|
||||
case 'copy': // 推广
|
||||
// promote(data);
|
||||
$('#url').val(data.url)
|
||||
console.log(data.files_url)
|
||||
ns.copy('url')
|
||||
break;
|
||||
case 'edit': //编辑
|
||||
location.hash = ns.hash("personnel://shop/enterprise/edit?files_id=" + data.files_id);
|
||||
break;
|
||||
case 'delete': //删除
|
||||
deletefiles(data.files_id);
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
function deletefiles(files_id) {
|
||||
if (repeat_flag) return false;
|
||||
repeat_flag = true;
|
||||
|
||||
layer.confirm('确定要删除该文件吗?', function (index) {
|
||||
layer.close(index);
|
||||
$.ajax({
|
||||
url: ns.url("personnel://shop/enterprise/delete"),
|
||||
data: {files_id},
|
||||
dataType: 'JSON',
|
||||
type: 'POST',
|
||||
success: function (res) {
|
||||
layer.msg(res.message);
|
||||
repeat_flag = false;
|
||||
if (res.code == 0) {
|
||||
table.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function () {
|
||||
repeat_flag = false;
|
||||
layer.close();
|
||||
});
|
||||
}
|
||||
function add() {
|
||||
location.hash = ns.hash("personnel://shop/enterprise/add");
|
||||
}
|
||||
</script>
|
||||
BIN
src/addon/personnel/shop/view/enterprise/play.png
Normal file
|
After Width: | Height: | Size: 818 B |
168
src/addon/personnel/shop/view/files/lists.html
Normal file
@@ -0,0 +1,168 @@
|
||||
<!-- 搜索框 -->
|
||||
<div class="single-filter-box top">
|
||||
<button class="layui-btn" onclick="add()">添加文件</button>
|
||||
|
||||
<div class="layui-form">
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="search_text" placeholder="请输入标题" autocomplete="off" class="layui-input">
|
||||
<button type="button" class="layui-btn layui-btn-primary" lay-filter="search" lay-submit>
|
||||
<i class="layui-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input id="url" value="" type="hidden"></input>
|
||||
<!-- 列表 -->
|
||||
<table id="files_list" lay-filter="files_list"></table>
|
||||
|
||||
<!-- 操作 -->
|
||||
<script type="text/html" id="operation">
|
||||
<div class="table-btn">
|
||||
<!-- <a class="layui-btn" lay-event="copy">复制链接</a> -->
|
||||
<a class="layui-btn" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn" lay-event="delete">删除</a>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 用户信息 -->
|
||||
<script type="text/html" id="info">
|
||||
<div class='table-title'>
|
||||
<div class='title-pic headimg'>
|
||||
<img layer-src src="{{ns.img('/addon/personnel/view/enterprise/fileicon.png')}}" onerror="this.src = '{:img('app/shop/view/files/fileicon.png')}' ">
|
||||
</div>
|
||||
<div class='title-content'>
|
||||
<p class="layui-elip" style="font-weight: 600;color:#000">{{d.files_title}}</p>
|
||||
<!-- <p class="layui-elip" style="line-height: 1.2;color:#888;font-size: 12px;">发布时间:{{ns.time_to_date(d.createtime)}}</p> -->
|
||||
<!-- <p class="layui-elip" style="color:#888;font-size: 12px;">样本大小:{{d.size}}</p> -->
|
||||
<!-- <div>
|
||||
<span title="{{ d.mobile }}">{{ d.mobile }}</span>
|
||||
{{# if (d.status == 0 || d.member_level_type == 1){ }}
|
||||
{{# if (d.status == 0){ }}
|
||||
<span class="blacklist">黑名单</span>
|
||||
{{# } }}
|
||||
{{# if (d.member_level_type == 1){ }}
|
||||
<span class="vip_style">SVIP</span>
|
||||
{{# } }}
|
||||
{{# } }}
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
var form,laytpl,table, repeat_flag = false;//防重复标识;
|
||||
layui.use(['form','laytpl'], function() {
|
||||
form = layui.form;
|
||||
laytpl = layui.laytpl;
|
||||
form.render();
|
||||
|
||||
table = new Table({
|
||||
elem: '#files_list',
|
||||
url: ns.url("personnel://shop/enterprise/lists"),
|
||||
cols: [
|
||||
[ {
|
||||
field: 'files_title',
|
||||
title: '文件名称',
|
||||
width: '30%',
|
||||
unresize: 'false',
|
||||
templet: '#info'
|
||||
},
|
||||
// {
|
||||
// field: 'category_name',
|
||||
// title: '文件分类',
|
||||
// width: '20%',
|
||||
// unresize: 'false'
|
||||
// },
|
||||
/*{
|
||||
field: 'create_time',
|
||||
title: '创建时间',
|
||||
width: '20%',
|
||||
unresize: 'false',
|
||||
templet: function (data) {
|
||||
return ns.time_to_date(data.createtime);
|
||||
}
|
||||
}, */{
|
||||
title: '操作',
|
||||
toolbar: '#operation',
|
||||
unresize: 'false',
|
||||
align : 'right'
|
||||
}]
|
||||
],
|
||||
});
|
||||
|
||||
/**
|
||||
* 搜索功能
|
||||
*/
|
||||
form.on('submit(search)', function(data) {
|
||||
table.reload({
|
||||
page: {
|
||||
curr: 1
|
||||
},
|
||||
where: data.field
|
||||
});
|
||||
});
|
||||
|
||||
table.on("sort",function (obj) {
|
||||
table.reload({
|
||||
page: {
|
||||
curr: 1
|
||||
},
|
||||
where: {
|
||||
order:obj.field,
|
||||
sort:obj.type
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 监听工具栏操作
|
||||
*/
|
||||
table.tool(function(obj) {
|
||||
var data = obj.data;
|
||||
switch (obj.event) {
|
||||
case 'copy': // 推广
|
||||
// promote(data);
|
||||
$('#url').val(data.url)
|
||||
console.log(data.files_url)
|
||||
ns.copy('url')
|
||||
break;
|
||||
case 'edit': //编辑
|
||||
location.hash = ns.hash("personnel://shop/enterprise/edit?files_id=" + data.files_id);
|
||||
break;
|
||||
case 'delete': //删除
|
||||
deletefiles(data.files_id);
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
function deletefiles(files_id) {
|
||||
if (repeat_flag) return false;
|
||||
repeat_flag = true;
|
||||
|
||||
layer.confirm('确定要删除该文件吗?', function (index) {
|
||||
layer.close(index);
|
||||
$.ajax({
|
||||
url: ns.url("personnel://shop/enterprise/delete"),
|
||||
data: {files_id},
|
||||
dataType: 'JSON',
|
||||
type: 'POST',
|
||||
success: function (res) {
|
||||
layer.msg(res.message);
|
||||
repeat_flag = false;
|
||||
if (res.code == 0) {
|
||||
table.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function () {
|
||||
repeat_flag = false;
|
||||
layer.close();
|
||||
});
|
||||
}
|
||||
function add() {
|
||||
location.hash = ns.hash("personnel://shop/enterprise/add");
|
||||
}
|
||||
</script>
|
||||
0
src/addon/personnel/shop/view/public/diy.css
Normal file
BIN
src/addon/personnel/shop/view/public/img/file.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/addon/personnel/shop/view/public/img/kefu.jpg
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
src/addon/personnel/shop/view/public/img/map.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
src/addon/personnel/shop/view/public/img/mingpian.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/addon/personnel/shop/view/public/img/video.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
@@ -86,7 +86,7 @@ class Pointexchange extends BaseShop
|
||||
$list = $exchange_order_model->getExchangePageList($condition, $page, $page_size, $order, $field, 'eo', [
|
||||
[ 'member m', 'm.member_id=eo.member_id', 'left' ]
|
||||
]);
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($condition,true));
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export($condition,true));
|
||||
return $list;
|
||||
|
||||
} else {
|
||||
|
||||
@@ -468,12 +468,12 @@ class Weapp extends BaseModel
|
||||
$server = $this->app->server;
|
||||
$message = $server->getMessage();
|
||||
Log::write('微信小程序消息推送:' . json_encode($message));
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($message,true));
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export($message,true));
|
||||
if (isset($message[ 'MsgType' ])) {
|
||||
switch ( $message[ 'MsgType' ] ) {
|
||||
case 'event':
|
||||
$this->app->server->push(function($res) {
|
||||
file_put_contents(__DIR__ . '/debug1.txt', var_export($res,true));
|
||||
// file_put_contents(__DIR__ . '/debug1.txt', var_export($res,true));
|
||||
// 商品审核结果通知
|
||||
if ($res[ 'Event' ] == 'open_product_spu_audit' && addon_is_exit('shopcomponent', $this->site_id)) {
|
||||
model('shopcompoent_goods')->update([
|
||||
|
||||
@@ -28,13 +28,11 @@ class Config extends BaseApi
|
||||
$document_info = $config_model->getRegisterDocument($site_id, 'shop');
|
||||
//隐私协议
|
||||
$rivacy_info = $config_model->getRrivacyDocument($site_id, 'shop');
|
||||
|
||||
if($type == 1){//注册
|
||||
$config = $document_info;
|
||||
}else{//隐私
|
||||
$config = $rivacy_info;
|
||||
}
|
||||
|
||||
return $this->response($config);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,13 @@ class Goods extends BaseApi
|
||||
|
||||
$poster = new Poster();
|
||||
$res = $poster->shareImg($this->params[ 'page' ] ?? '', $qrcode_param, $this->site_id, $this->store_id);
|
||||
//临时解决分享
|
||||
$goods = model('goods')->getInfo(['goods_id'=>$qrcode_param['goods_id']]);
|
||||
$img = explode(',',$goods['goods_image']);
|
||||
$res['code'] = 0;
|
||||
$res['data'] = [
|
||||
'path'=>$img[0]
|
||||
];
|
||||
return $this->response($res);
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -333,7 +333,20 @@ class Goodssku extends BaseApi
|
||||
$join = [
|
||||
[ 'goods g', 'gs.sku_id = g.sku_id', 'inner' ]
|
||||
];
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($condition,true));
|
||||
|
||||
// 如果是连锁运营模式
|
||||
if ($this->store_data[ 'config' ][ 'store_business' ] == 'store') {
|
||||
$join[] = [ 'store_goods_sku sgs', 'sgs.status = 1 and g.sku_id = sgs.sku_id and sgs.store_id=' . $this->store_id, 'right' ];
|
||||
|
||||
$condition[] = [ 'g.sale_store', 'like', [ '%all%', '%,' . $this->store_id . ',%' ], 'or' ];
|
||||
|
||||
$field = str_replace('gs.price', 'IFNULL(IF(g.is_unify_price = 1,gs.price,sgs.price), gs.price) as price', $field);
|
||||
$field = str_replace('gs.discount_price', 'IFNULL(IF(g.is_unify_price = 1,gs.price,sgs.price), gs.price) as discount_price', $field);
|
||||
if ($this->store_data[ 'store_info' ][ 'stock_type' ] == 'store') {
|
||||
$field = str_replace('gs.stock', 'IFNULL(sgs.stock, 0) as stock', $field);
|
||||
}
|
||||
}
|
||||
|
||||
$goods = new Goods();
|
||||
$list = $goods->getGoodsSkuPageList($condition, $page, $page_size, $order_by, $field, $alias, $join);
|
||||
|
||||
|
||||
@@ -14,12 +14,36 @@ use addon\wxoplatform\model\Config as WxOplatformConfigModel;
|
||||
use app\model\web\Config as WebConfig;
|
||||
use think\facade\Log;
|
||||
|
||||
|
||||
use app\model\system\Seal as SealModel;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class Lucky extends BaseApi
|
||||
{
|
||||
|
||||
public function newtest(){
|
||||
|
||||
|
||||
$sealModel = new SealModel();
|
||||
$params = [
|
||||
"filename" => "20250625030540175079194017513.xlsx",
|
||||
"path" => "upload/2035/common/seal/sealmedium_import/20250625/20250625030540175079194017513.xlsx",
|
||||
"index" => "1",
|
||||
"success_num" => "0",
|
||||
"error_num" => "0",
|
||||
"record" => "0"
|
||||
];
|
||||
$res = $sealModel->importMedium($params, 2035);
|
||||
|
||||
}
|
||||
//生成pdf
|
||||
public function pdf(){
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 生成 NFC 跳转 URL Scheme
|
||||
function generateNFCScheme($accessToken) {
|
||||
$url = "https://api.weixin.qq.com/wxa/generatenfcscheme?access_token={$accessToken}";
|
||||
|
||||
@@ -601,4 +601,12 @@ class Member extends BaseApi
|
||||
model('personnel_message')->add($insert);
|
||||
return $this->response(['code'=>0,'message'=>'留言成功~']);
|
||||
}
|
||||
|
||||
public function getbusiness(){
|
||||
|
||||
$token = $this->checkToken();
|
||||
if ($token[ 'code' ] < 0) return $this->response($token);
|
||||
$list = model('business')->getList([]);
|
||||
return $this->response(['code'=>0,'data'=>$list]);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ class Rrapi
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
exit;
|
||||
$model = new Rrmodel();
|
||||
//同步流程 首先同步所有公众号-》同步商品分类-》同步商品-》同步diy首页-》diy会员-》diy自定义页面-》单独同步vr链接
|
||||
$uniacid = 1083;//设置大于0同步单个
|
||||
|
||||
269
src/app/api/controller/Seal.php
Normal file
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
namespace app\api\controller;
|
||||
|
||||
use app\model\system\Seal as SealModel;
|
||||
use Mpdf\Mpdf;
|
||||
class Seal extends BaseApi
|
||||
{
|
||||
|
||||
public function getimg($keyword){
|
||||
|
||||
// 设置内容类型为PNG图片
|
||||
header('Content-Type: image/png');
|
||||
$sealModel = new SealModel();
|
||||
$info = $sealModel->getInfo('seal_medium',['name'=>$keyword]);
|
||||
$type = ['数据不详','推荐使用','有条件使用','不推荐使用','不能使用'];
|
||||
$value = [];
|
||||
if($info['data']){
|
||||
$value = json_decode($info['data']['value'],true);
|
||||
}
|
||||
// dump($value);
|
||||
// 图片尺寸
|
||||
$width = 640;
|
||||
$height = 1280;
|
||||
|
||||
// 创建画布
|
||||
$image = imagecreatetruecolor($width, $height);
|
||||
|
||||
// 颜色定义
|
||||
$white = imagecolorallocate($image, 255, 255, 255);
|
||||
$black = imagecolorallocate($image, 0, 0, 0);
|
||||
$gray = imagecolorallocate($image, 200, 200, 200);
|
||||
$darkGray = imagecolorallocate($image, 100, 100, 100);
|
||||
|
||||
// 填充背景
|
||||
imagefill($image, 0, 0, $white);
|
||||
|
||||
// 设置字体路径(确保服务器上有此字体文件)
|
||||
$font = PUBLIC_PATH . 'static/font/Microsoft.ttf'; // 宋体字体,支持中文
|
||||
// if (!file_exists($font)) {
|
||||
// $font = 'arial.ttf'; // 备用字体
|
||||
// }
|
||||
|
||||
// 添加标题
|
||||
imagettftext($image, 24, 0, 280, 50, $black, $font, "乙醛");
|
||||
|
||||
// 模拟你的数据
|
||||
// $value = [
|
||||
// ['text' => '项目1', 'value' => '值1', 'color' => '#FF0000'],
|
||||
// ['text' => '项目2', 'value' => '值2', 'color' => '#00FF00'],
|
||||
// ['text' => '项目3', 'value' => '', 'color' => '#0000FF'],
|
||||
// ];
|
||||
|
||||
$type = ['值1' => '类型1', '值2' => '类型2'];
|
||||
|
||||
// 表格参数
|
||||
$startY = 80;
|
||||
$rowHeight = 40;
|
||||
$col1Width = 240;
|
||||
$col2Width = 500;
|
||||
|
||||
// 绘制表格边框
|
||||
imagerectangle($image, 30, $startY, $width-30, $startY + count($value) * $rowHeight, $black);
|
||||
|
||||
// 绘制表格内容
|
||||
foreach ($value as $index => $item) {
|
||||
$y = $startY + $index * $rowHeight;
|
||||
|
||||
// 绘制行线
|
||||
imageline($image, 30, $y, $width-30, $y, $black);
|
||||
$item['color'] = $item['color']?$item['color']:'#FFFFFF';
|
||||
// 解析颜色
|
||||
$color = sscanf($item['color'], "#%02x%02x%02x");
|
||||
|
||||
$textColor = imagecolorallocate($image, $color[0], $color[1], $color[2]);
|
||||
|
||||
// dump($textColor);
|
||||
// 第一列
|
||||
$bgColor = sscanf(ltrim($item['color'], '#'), "%02x%02x%02x");
|
||||
$cellBgColor = imagecolorallocate($image, $bgColor[0], $bgColor[1], $bgColor[2]);
|
||||
imagefilledrectangle($image, 30, $y, 30 + $col1Width, $y + $rowHeight, $cellBgColor);
|
||||
imagettftext($image, 14, 0, 50, $y + 25, $black, $font, $item['text']);
|
||||
|
||||
// 第二列
|
||||
$displayValue = '数据不详';
|
||||
imagettftext($image, 14, 0, 30 + $col1Width + 20, $y + 25, $textColor, $font, $displayValue);
|
||||
// dump($color);
|
||||
// echo $index.'<br/>';
|
||||
// break;
|
||||
// 列分隔线
|
||||
imageline($image, 30 + $col1Width, $y, 30 + $col1Width, $y + $rowHeight, $black);
|
||||
}
|
||||
|
||||
// 输出PNG图片
|
||||
$time = time();
|
||||
imagepng($image,'upload/1/pdfs/'.$time.'.png');
|
||||
imagedestroy($image);
|
||||
// echo '<img src="/upload/1/pdfs/a.png">';
|
||||
return 'upload/1/pdfs/'.$time.'.png';
|
||||
}
|
||||
|
||||
|
||||
public function getpdf($id){
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
|
||||
$sealModel = new SealModel();
|
||||
$info = $sealModel->getInfo('seal_medium',['id'=>$id]);
|
||||
try {
|
||||
// 创建 mPDF 实例并配置
|
||||
$mpdf = new \Mpdf\Mpdf([
|
||||
'autoScriptToLang' => true,
|
||||
'autoLangToFont' => true,
|
||||
'mode' => 'utf-8', // 设置编码模式
|
||||
'format' => 'A4', // 设置页面格式
|
||||
'orientation' => 'P', // 页面方向 P-纵向/L-横向
|
||||
'default_font_size' => 12, // 默认字体大小
|
||||
'default_font' => 'sans-serif', // 默认字体
|
||||
'margin_left' => 15, // 左边距(毫米)
|
||||
'margin_right' => 15, // 右边距
|
||||
'margin_top' => 16, // 上边距
|
||||
'margin_bottom' => 16, // 下边距
|
||||
'margin_header' => 9, // 页眉边距
|
||||
'margin_footer' => 9 // 页脚边距
|
||||
]);
|
||||
|
||||
// 设置文档信息(可选)
|
||||
// $mpdf->SetTitle('乙醛');
|
||||
// $mpdf->SetAuthor('你的名字');
|
||||
// $mpdf->SetCreator('PHP mPDF');
|
||||
// $mpdf->SetSubject('mPDF 示例');
|
||||
// $mpdf->SetKeywords('mPDF, PHP, PDF, 示例');
|
||||
|
||||
// 添加页眉(可选)
|
||||
// $mpdf->SetHTMLHeader('
|
||||
// <div style="text-align: right; font-weight: bold;">
|
||||
// 乙醛
|
||||
// </div>
|
||||
// ');
|
||||
|
||||
// 添加页脚(可选)
|
||||
// $mpdf->SetHTMLFooter('
|
||||
// <table width="100%">
|
||||
// <tr>
|
||||
// <td width="33%">{DATE j-m-Y}</td>
|
||||
// <td width="33%" align="center">{PAGENO}/{nbpg}</td>
|
||||
// <td width="33%" style="text-align: right;">乙醛</td>
|
||||
// </tr>
|
||||
// </table>
|
||||
// ');
|
||||
|
||||
$type = ['数据不详','推荐使用','有条件使用','不推荐使用','不能使用'];
|
||||
$value = [];
|
||||
if($info['data']){
|
||||
$value = json_decode($info['data']['value'],true);
|
||||
// foreach($value as &$item){
|
||||
// $item['value'] = $item['value']?$type[$item['value']]:'数据不详';
|
||||
// }
|
||||
// $info['data']['value'] = $value;
|
||||
}
|
||||
// dump($value);
|
||||
// 准备 HTML 内容
|
||||
$html = '
|
||||
<h1 style="text-align: center; color: #333;">乙醛</h1>
|
||||
<table border="1" cellspacing="0" cellpadding="5" width="100%">
|
||||
<!--<thead>
|
||||
<tr>
|
||||
<th width="30%">项目</th>
|
||||
<th width="40%">描述</th>
|
||||
</tr>
|
||||
</thead>-->
|
||||
<tbody>';
|
||||
foreach($value as &$item){
|
||||
// $item['value'] = $item['value']?$type[$item['value']]:'数据不详';
|
||||
$html .= '<tr>
|
||||
<td style="background: '.$item['color'].';">'.$item['text'].'</td>
|
||||
<td style="color: '.$item['color'].';">'.($item['value']?$type[$item['value']]:'数据不详').'</td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
$html .= '</tbody>
|
||||
</table>
|
||||
';
|
||||
// echo $html;exit;
|
||||
// 写入 HTML 内容
|
||||
$mpdf->WriteHTML($html);
|
||||
|
||||
// 输出 PDF
|
||||
// 方式1:直接在浏览器中打开
|
||||
// $mpdf->Output('example.pdf', \Mpdf\Output\Destination::INLINE);
|
||||
|
||||
// 方式2:强制下载
|
||||
// $mpdf->Output('example.pdf', \Mpdf\Output\Destination::DOWNLOAD);
|
||||
$directory = 'upload/1/pdfs/';
|
||||
$time = time();
|
||||
if (!file_exists($directory)) {
|
||||
// 尝试创建目录(递归创建多级目录)
|
||||
if (!mkdir($directory, 0755, true)) {
|
||||
// 创建失败处理
|
||||
die("无法创建目录: $directory");
|
||||
}
|
||||
// echo "目录创建成功: $directory";
|
||||
}
|
||||
// 方式3:保存到服务器
|
||||
$pdf = $directory.'/'.$time.'.pdf';
|
||||
$mpdf->Output($pdf, \Mpdf\Output\Destination::FILE);
|
||||
return $pdf;
|
||||
} catch (\Mpdf\MpdfException $e) {
|
||||
// 处理异常
|
||||
// echo '生成PDF时出错: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础信息
|
||||
*/
|
||||
public function info()
|
||||
{
|
||||
$sealModel = new SealModel();
|
||||
$keyword = $this->params['keyword'] ?? '';
|
||||
$info = $sealModel->getInfo('seal_medium',['name'=>$keyword]);
|
||||
//1推荐使用,2有条件使用,3不推荐使用,4不能使用,空格,数据不详
|
||||
$type = ['数据不详','推荐使用','有条件使用','不推荐使用','不能使用'];
|
||||
if($info['data']){
|
||||
$value = json_decode($info['data']['value'],true);
|
||||
foreach($value as &$item){
|
||||
$item['value'] = $item['value']?$type[$item['value']]:'数据不详';
|
||||
}
|
||||
$info['data']['value'] = $value;
|
||||
//生成pdf和img
|
||||
if(!$info['data']['files_url']){
|
||||
$pdf = $this->getpdf($info['data']['id']);
|
||||
if($pdf){
|
||||
model('seal_medium')->update(['files_url'=>$pdf],['id'=>$info['data']['id']]);
|
||||
$info['data']['files_url'] = $pdf;
|
||||
}
|
||||
}
|
||||
$info['data']['img_url'] = $this->getimg($keyword);
|
||||
}
|
||||
return $this->response($info['data']);
|
||||
// $help_id = $this->params['id'] ?? 0;
|
||||
// if (empty($help_id)) {
|
||||
// return $this->response($this->error('', 'REQUEST_ID'));
|
||||
// }
|
||||
// $help = new HelpModel();
|
||||
// $info = $help->getHelpInfo($help_id);
|
||||
// return $this->response($info);
|
||||
}
|
||||
|
||||
public function search(){
|
||||
|
||||
$sealModel = new SealModel();
|
||||
$keyword = $this->params['keyword'] ?? '';
|
||||
$list = $sealModel->getList('seal_medium',[['name','like','%'.$keyword.'%']]);
|
||||
return $this->response(['code'=>0,'list'=>$list]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础信息
|
||||
*/
|
||||
public function getstructure()
|
||||
{
|
||||
$sealModel = new SealModel();
|
||||
$condition[] = [ 'site_id', '=', $this->site_id ];
|
||||
$field = '*';
|
||||
$list = $sealModel->getTree1($condition, $field);
|
||||
if (request()->isJson()) return $list;
|
||||
return $this->response($list);
|
||||
}
|
||||
}
|
||||
@@ -1879,6 +1879,38 @@ function paramFilter($param)
|
||||
return preg_replace($filter_rule, '', $param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化数据为日志友好的字符串(保持中文可读性)
|
||||
*
|
||||
* @param mixed $data 要格式化的数据
|
||||
* @return string 格式化后的字符串
|
||||
*/
|
||||
function formatForLog($data): string
|
||||
{
|
||||
if (is_array($data) || is_object($data)) {
|
||||
// 使用 JSON_UNESCAPED_UNICODE 保持中文可读性
|
||||
return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
return (string) $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化数据为可导出的字符串(完全避免Unicode转义)
|
||||
*
|
||||
* @param mixed $data 要格式化的数据
|
||||
* @return string 格式化后的字符串
|
||||
*/
|
||||
function exportForLog($data): string
|
||||
{
|
||||
if (is_array($data) || is_object($data)) {
|
||||
// 使用 var_export 完全避免Unicode转义
|
||||
return var_export($data, true);
|
||||
}
|
||||
|
||||
return (string) $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单日志写入
|
||||
* @param string $msg 日志内容
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
@CHARSET "UTF-8";
|
||||
|
||||
|
||||
.digit-navigation .preview-draggable {padding: 0;}
|
||||
.digit-navigation .audio-wrap audio{width: 100%;position: relative;}
|
||||
.digit-navigation .edit-attribute .attr-wrap .restore-wrap .audio-add-box .img-block {width: 200px !important;height: 125px !important;margin-bottom: 30px;margin-right: 0;position: relative;}
|
||||
.digit-navigation .edit-attribute .attr-wrap .restore-wrap .audio-add-box .img-block > div {line-height: 125px;height: 125px !important;width: 100%;text-align: center;}
|
||||
.digit-navigation .edit-attribute .attr-wrap .restore-wrap .audio-add-box .img-block audio {width: 100% !important;height: 125px !important;}
|
||||
.digit-navigation .edit-attribute .attr-wrap .restore-wrap .audio-add-box .img-block span{position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}
|
||||
.digit-navigation .audio-zhezhao {position: absolute;background: #fff;width: 61%;height: 125px;top: 1px;right: 32px;text-align: center;line-height: 105px;display: none;}
|
||||
.digit-navigation .audio-zhezhao span {position: absolute;top: 35px;left: 80px;color: #909399;}
|
||||
|
||||
/*图文导航组件*/
|
||||
.digit-navigation .preview-draggable .preview-box {
|
||||
padding: 8px 0;
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
<template v-if="nc.lazyLoad">
|
||||
|
||||
<div :class="['digit-nav',nc.showStyle] " :style="{
|
||||
backgroundColor: nc.componentBgColor,
|
||||
backgroundImage: 'url('+nc.imageUrl+')',
|
||||
backgroundSize:'100% 100%',
|
||||
borderTopLeftRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0),
|
||||
borderTopRightRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0),
|
||||
borderBottomLeftRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0),
|
||||
@@ -48,8 +49,7 @@
|
||||
<!-- 内容编辑 -->
|
||||
<template slot="edit-content">
|
||||
<template v-if="nc.lazyLoad">
|
||||
|
||||
<digit-nav-list></digit-nav-list>
|
||||
<!-- <digit-nav-list></digit-nav-list> -->
|
||||
<div class="template-edit-title">
|
||||
<h3>数字项设置</h3>
|
||||
<div class="layui-form-item icon-radio">
|
||||
@@ -109,19 +109,20 @@
|
||||
<template slot="edit-style">
|
||||
<template v-if="nc.lazyLoad">
|
||||
<div class="template-edit-title">
|
||||
<!--
|
||||
<h3>图文导航</h3>
|
||||
|
||||
<color v-if="nc.ornament.type != 'default'" :data="{ field : 'color', 'label' : '边框颜色', parent : 'ornament', defaultColor : '#EDEDED' }"></color>
|
||||
<h3>背景设置</h3>
|
||||
|
||||
<digit-images></digit-images>
|
||||
<!-- <color v-if="nc.ornament.type != 'default'" :data="{ field : 'color', 'label' : '边框颜色', parent : 'ornament', defaultColor : '#EDEDED' }"></color>
|
||||
|
||||
|
||||
<div class="template-edit-title" v-show="['listmenu','img'].includes(nc.mode) && nc.type == 'img'">
|
||||
<h3>图片设置</h3>
|
||||
<template v-if="nc.type == 'img'">
|
||||
<slide :data="{ field : 'aroundRadius', label : '图片圆角', max : 50 }"></slide>
|
||||
<slide :data="{ field : 'imageSize', label : '图片大小', min: 30, max : 60 }"></slide>
|
||||
</template>
|
||||
</div> -->
|
||||
<div class="template-edit-title" v-show="['listmenu','img'].includes(nc.mode) && nc.type == 'img'">
|
||||
<h3>图片设置</h3>
|
||||
<template v-if="nc.type == 'img'">
|
||||
<slide :data="{ field : 'aroundRadius', label : '图片圆角', max : 50 }"></slide>
|
||||
<slide :data="{ field : 'imageSize', label : '图片大小', min: 30, max : 60 }"></slide>
|
||||
</template>
|
||||
</div> -->
|
||||
|
||||
<div class="template-edit-title">
|
||||
<h3>数字文字设置</h3>
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
/**
|
||||
* [图片导航的图片]·组件
|
||||
*/
|
||||
var digitNavListHtml = '<div style="display: none;"></div>';
|
||||
Vue.component("digit-nav-list", {
|
||||
template: digitNavListHtml,
|
||||
// var digitNavListHtml = '<div style="display: none;"></div>';
|
||||
var igitNavHtml = '<div class="digit-images">';
|
||||
|
||||
igitNavHtml += '<div class="template-edit-title">';
|
||||
|
||||
igitNavHtml += '<div class="layui-form-item">';
|
||||
igitNavHtml += '<label class="layui-form-label sm">背景图</label>';
|
||||
igitNavHtml += '<img-upload :data="{data : data}"></img-upload>';
|
||||
igitNavHtml += '</div>';
|
||||
|
||||
igitNavHtml += '</div>';
|
||||
|
||||
igitNavHtml += '</div>';
|
||||
Vue.component("digit-images", {
|
||||
template: igitNavHtml,
|
||||
data: function () {
|
||||
return {
|
||||
data: this.$parent.data,
|
||||
@@ -89,7 +101,7 @@
|
||||
methods: {
|
||||
|
||||
setnum(num){
|
||||
console.log(num)
|
||||
console.log(this.data)
|
||||
this.data.rowCount = num
|
||||
if(num == 3){
|
||||
this.data.list = [
|
||||
|
||||
@@ -31,7 +31,7 @@ class Task extends Controller
|
||||
log_write('Task checkCron ...', 'debug');
|
||||
$cron_model = new Cron();
|
||||
$result = $cron_model->checkSchedule();
|
||||
log_write('Task checkCron result: ' . json_encode($result), 'debug');
|
||||
log_write('Task checkCron result: ' . formatForLog($result), 'debug');
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,4 +56,17 @@ class ScheduleDict
|
||||
if($code) return $list[$code] ?? '';
|
||||
return $list;
|
||||
}
|
||||
|
||||
public static function getSuggestion(string $type) {
|
||||
switch($type) {
|
||||
case self::default:
|
||||
return '请检查系统计划任务配置,确保 cron 进程正常运行,并检查网络连接和 SSL 证书设置。';
|
||||
case self::url:
|
||||
return '请检查网络连接、目标 URL 可访问性、HTTP 状态码和接口认证配置。可以使用 curl 命令手动测试接口。';
|
||||
case self::cli:
|
||||
return '请检查 cron 服务状态、命令执行权限、脚本路径和 PHP CLI 环境。建议使用 crontab -l 查看当前任务配置。';
|
||||
default:
|
||||
return '请检查计划任务配置文件和相关权限设置,确保所有依赖服务正常运行。';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ class DiyViewEdit extends Controller
|
||||
}
|
||||
$diy_view_utils = array_values($diy_view_utils);
|
||||
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($diy_view_utils,true));
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export($diy_view_utils,true));
|
||||
$this->assign('diy_view_utils', $diy_view_utils);
|
||||
|
||||
$this->assign("time", time());
|
||||
|
||||
@@ -25,7 +25,6 @@ class InitConfig
|
||||
$this->initConst();
|
||||
//初始化配置信息
|
||||
$this->initConfig();
|
||||
log_write('系统配置信息已初始化', 'debug');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -106,7 +106,6 @@ class Goods extends BaseMerchant
|
||||
$condition[] = [ 'ischeck', '=', 1 ];
|
||||
}
|
||||
}
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($condition,true));
|
||||
if (!empty($start_sale)) $condition[] = [ 'sale_num', '>=', $start_sale ];
|
||||
if (!empty($end_sale)) $condition[] = [ 'sale_num', '<=', $end_sale ];
|
||||
if (!empty($start_price)) $condition[] = [ 'price', '>=', $start_price ];
|
||||
|
||||
@@ -75,7 +75,7 @@ class Order extends BaseMerchant
|
||||
$order_status = input('order_status', '');//订单状态
|
||||
}
|
||||
}
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($order_status,true));
|
||||
|
||||
$order_name = input('order_name', '');
|
||||
$pay_type = input('pay_type', '');
|
||||
$order_from = input('order_from', '');
|
||||
|
||||
@@ -121,7 +121,7 @@ class Model
|
||||
/**
|
||||
* 获取分页列表数据
|
||||
* @param array $condition
|
||||
* @param bool $field
|
||||
* @param bool | string $field
|
||||
* @param string $order
|
||||
* @param int $page
|
||||
* @param int $list_rows
|
||||
|
||||
@@ -1077,7 +1077,7 @@ class Goods extends BaseModel
|
||||
public function getGoodsPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = 'a.create_time desc', $field = 'a.goods_id,a.goods_name,a.site_id,a.site_name,a.goods_image,a.goods_state,a.price,a.goods_stock,a.goods_stock_alarm,a.create_time,a.sale_num,a.is_virtual,a.goods_class,a.goods_class_name,a.is_fenxiao,a.fenxiao_type,a.promotion_addon,a.sku_id,a.is_consume_discount,a.discount_config,a.discount_method,a.sort,a.label_id,a.is_delete', $alias = 'a', $join = [])
|
||||
{
|
||||
$res = model('goods')->pageList($condition, $field, $order, $page, $page_size, $alias, $join);
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export(Db::getLastSql(),true));
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export(Db::getLastSql(),true));
|
||||
foreach ($res[ 'list' ] as $k => $v) {
|
||||
if (isset($v[ 'goods_stock' ])) {
|
||||
$res[ 'list' ][ $k ][ 'goods_stock' ] = numberFormat($res[ 'list' ][ $k ][ 'goods_stock' ]);
|
||||
@@ -2160,7 +2160,7 @@ class Goods extends BaseModel
|
||||
|
||||
return $this->addGoods($data);
|
||||
} catch (Exception $e) {
|
||||
dump($e);
|
||||
// dump($e);
|
||||
return $this->error('', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,8 @@ class GoodsApi extends BaseModel
|
||||
$goods_sku_detail[ 'store_goods_status' ] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//后端处理缺少域名问题,后续处理到前端 add lucky
|
||||
if($goods_sku_detail['pdf_url']) $goods_sku_detail['pdf_url'] = 'https://'.$_SERVER['HTTP_HOST'].'/'.$goods_sku_detail['pdf_url'];
|
||||
$goods_sku_detail[ 'purchased_num' ] = 0; // 该商品已购数量
|
||||
$res[ 'goods_sku_detail' ] = $goods_sku_detail;
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ class Login extends BaseModel
|
||||
$info = [];
|
||||
$auth_tag = '';
|
||||
foreach ($data as $key => $value) {
|
||||
if (in_array($key, [ 'wx_unionid', 'wx_openid', 'weapp_openid', 'qq_openid', 'ali_openid', 'baidu_openid', 'toutiao_openid' ])) {
|
||||
if (in_array($key, [ 'wx_unionid', 'wx_openid', 'weapp_openid', 'qq_openid', 'ali_openid', 'baidu_openid', 'toutiao_openid', 'huawei_openid' ])) {
|
||||
$auth_tag = $key;
|
||||
if (empty($value)) return $this->error('', 'PARAMETER_ERROR');
|
||||
$info = model("member")->getInfo(
|
||||
@@ -95,7 +95,8 @@ class Login extends BaseModel
|
||||
// 会员不存在 第三方自动注册开启 未开启绑定手机 则进行自动注册
|
||||
$config = new Config();
|
||||
$config_info = $config->getRegisterConfig($data[ 'site_id' ], 'shop');
|
||||
if ($config_info[ 'data' ][ 'value' ][ 'third_party' ] && !$config_info[ 'data' ][ 'value' ][ 'bind_mobile' ]) {
|
||||
//华为元服务静默不用判断强制手机号
|
||||
if (($config_info[ 'data' ][ 'value' ][ 'third_party' ] && !$config_info[ 'data' ][ 'value' ][ 'bind_mobile' ]) || $auth_tag == 'huawei_openid') {
|
||||
$register = new Register();
|
||||
$register_res = $register->authRegister($data);
|
||||
if ($register_res[ 'code' ] == 0) {
|
||||
|
||||
@@ -27,6 +27,14 @@ use think\facade\Config;
|
||||
class Member extends BaseModel
|
||||
{
|
||||
|
||||
|
||||
public function getBusinessPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = '', $field = '*')
|
||||
{
|
||||
$list = model('business')->pageList($condition, $field, $order, $page, $page_size, '', '', '');
|
||||
return $this->success($list);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加会员(注意等级名称)
|
||||
* @param $data
|
||||
@@ -256,6 +264,7 @@ class Member extends BaseModel
|
||||
$member_info = model('member')->setIsCache(0)->getInfo($condition, $field);
|
||||
|
||||
if (!empty($member_info) && empty($member_info[ 'wx_openid' ]) && !empty($member_info[ 'wx_unionid' ])) {
|
||||
// TODO: 注意, 华为云上的最新代码,下面都注释掉了,
|
||||
$fans_model = new Fans();
|
||||
$fans_condition[] = [ "unionid", "=", $member_info[ 'wx_unionid' ] ];
|
||||
$fans_info = $fans_model->getFansInfo($fans_condition);
|
||||
|
||||
@@ -143,6 +143,7 @@ class Register extends BaseModel
|
||||
'ali_openid' => $data['ali_openid'] ?? '',
|
||||
'baidu_openid' => $data['baidu_openid'] ?? '',
|
||||
'toutiao_openid' => $data['toutiao_openid'] ?? '',
|
||||
'huawei_openid' => $data['huawei_openid'] ?? '',
|
||||
'headimg' => $data['avatarUrl'] ?? '',
|
||||
'member_level' => !empty($member_level_info) ? $member_level_info[ 'level_id' ] : 0,
|
||||
'member_level_name' => !empty($member_level_info) ? $member_level_info[ 'level_name' ] : '',
|
||||
|
||||
@@ -1198,7 +1198,6 @@ class OrderCommon extends BaseModel
|
||||
public function getOrderPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = '', $field = '*', $alias = 'a', $join = [])
|
||||
{
|
||||
$order_list = model('order')->pageList($condition, $field, $order, $page, $page_size, $alias, $join);
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($condition,true));
|
||||
$check_condition = array_column($condition, 2, 0);
|
||||
|
||||
if (!empty($order_list[ 'list' ])) {
|
||||
|
||||
@@ -36,6 +36,7 @@ class OrderCreate extends BaseModel
|
||||
model('order')->startTrans();
|
||||
$pay_model = new Pay();
|
||||
$this->out_trade_no = $pay_model->createOutTradeNo($this->member_id);
|
||||
|
||||
try {
|
||||
|
||||
//循环生成多个订单
|
||||
@@ -52,15 +53,16 @@ class OrderCreate extends BaseModel
|
||||
$order_insert_data['order_type_name'] = $this->order_type['order_type_name'];
|
||||
$order_insert_data['order_status_name'] = $this->order_type['order_status']['name'];
|
||||
$order_insert_data['order_status_action'] = json_encode($this->order_type['order_status'], JSON_UNESCAPED_UNICODE);
|
||||
$order_insert_data['business'] = input('business','');
|
||||
if($i >= 1){
|
||||
//主表订单id
|
||||
$order_insert_data['host_order_id'] = $this->order_id;
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export($order_insert_data,true));
|
||||
$order_id = model('order')->add($order_insert_data);
|
||||
}else{
|
||||
$order_id = model('order')->add($order_insert_data);
|
||||
$this->order_id = $order_id;
|
||||
}
|
||||
|
||||
$order_goods_insert_data = [];
|
||||
//订单项目表
|
||||
foreach ($v['goods_list'] as $order_goods_v) {
|
||||
|
||||
@@ -712,7 +712,7 @@ trait PromotionTool
|
||||
//-------------
|
||||
/* $all_info = $manjian_model->getManjianInfo([['manjian_type', '=', 1], ['site_id', '=', $this->site_id], ['status', '=', 1]], 'manjian_name,type,goods_ids,rule_json,manjian_id')['data'];
|
||||
$goods_list = $this->goods_list;
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export($all_info,true));
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export($all_info,true));
|
||||
//存在全场满减(不考虑部分满减情况)
|
||||
if (!empty($all_info)) {//满减默认
|
||||
$discount_array = $this->getManjianDiscountMoney($all_info);
|
||||
|
||||
@@ -61,13 +61,13 @@ class Cron extends BaseModel
|
||||
*/
|
||||
public function execute($type = 'default')
|
||||
{
|
||||
log_write('计划任务开始执行', 'debug');
|
||||
log_write('计划任务开始执行', 'info');
|
||||
if (config('cron.default') != $type) {
|
||||
log_write('计划任务方式不匹配<不能执行/model/system/Cron/execute>:' . config('cron.default') . ' != ' . $type, 'debug');
|
||||
log_write('计划任务方式不匹配<不能执行/model/system/Cron/execute>:' . config('cron.default') . ' != ' . $type, 'warning');
|
||||
return true;
|
||||
}
|
||||
|
||||
log_write('当前执行方式:' . $type, 'debug');
|
||||
log_write('当前执行方式:' . $type, 'info');
|
||||
|
||||
try {
|
||||
//写入计划任务标记运行
|
||||
@@ -78,7 +78,7 @@ class Cron extends BaseModel
|
||||
$query_execute_time = $is_open_queue == 1 ? time() + 60 : time();
|
||||
$list = model('cron')->getList([['execute_time', '<=', $query_execute_time]]);
|
||||
$now_time = time();
|
||||
log_write('计划任务开始执行,查询计划任务列表', 'debug');
|
||||
log_write('计划任务开始执行,查询计划任务列表', 'info');
|
||||
|
||||
if (!empty($list)) {
|
||||
foreach ($list as $k => $v) {
|
||||
@@ -97,7 +97,7 @@ class Cron extends BaseModel
|
||||
return $res ?? $this->success();
|
||||
}, function ($params) {
|
||||
try {
|
||||
log_write('调用事件名称:' . $params['name'], 'debug');
|
||||
log_write('调用事件名称:' . $params['name'], 'info');
|
||||
$res = event($params['event'], ['relate_id' => $params['relate_id']]);
|
||||
} catch (\Exception $e) {
|
||||
$res = $this->error($e->getMessage(), $e->getMessage());
|
||||
@@ -150,7 +150,7 @@ class Cron extends BaseModel
|
||||
// $this->setCron();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
log_write('计划任务执行异常<model/system/Cron/execute>:' . $e->getMessage(), 'debug');
|
||||
log_write('计划任务执行异常<model/system/Cron/execute>:' . $e->getMessage(), 'error');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -252,9 +252,16 @@ class Cron extends BaseModel
|
||||
$remark = 'Cron计划任务已停止!当前启动的任务方式:' . ScheduleDict::getType(config('cron.default')) . '。';
|
||||
$error = self::getError(config('cron.default'));
|
||||
if (!empty($error)) {
|
||||
$remark .= json_encode($error);
|
||||
$remark .= formatForLog($error);
|
||||
}
|
||||
log_write('Cron计划任务校验计划任务是否正常运行,计划任务异常,异常信息:' . json_encode($error) . ',文件路径:' . $file, 'warning');
|
||||
|
||||
$detail = [
|
||||
'error'=> $error,
|
||||
'remark' => $remark,
|
||||
'suggestion' => ScheduleDict::getSuggestion(config('cron.default')),
|
||||
];
|
||||
|
||||
log_write('Cron计划任务校验计划任务是否正常运行,计划任务异常,异常信息:' . formatForLog($detail) . ',文件路径:' . $file, 'warning');
|
||||
return $this->error([], $remark);
|
||||
} catch (\Exception $e) {
|
||||
log_write('Cron计划任务校验计划任务是否正常运行异常:' . $e->getMessage() . ',异常行:' . $e->getLine() . ',文件路径:' . $file, 'error');
|
||||
|
||||
@@ -66,11 +66,11 @@ class Document extends BaseModel
|
||||
public function getDocument($condition)
|
||||
{
|
||||
|
||||
// $json_condition = json_encode($condition);
|
||||
// $cache = Cache::get("document_" . $json_condition, "");
|
||||
// if (!empty($cache)) {
|
||||
// return $this->success($cache);
|
||||
// }
|
||||
$json_condition = json_encode($condition);
|
||||
$cache = Cache::get("document_" . $json_condition, "");
|
||||
if (!empty($cache)) {
|
||||
return $this->success($cache);
|
||||
}
|
||||
$check_condition = array_column($condition, 2, 0);
|
||||
$site_id = $check_condition['site_id'] ?? '';
|
||||
if ($site_id === '') {
|
||||
@@ -98,7 +98,7 @@ class Document extends BaseModel
|
||||
'modify_time' => 0
|
||||
];
|
||||
}
|
||||
// Cache::tag("document")->set("document_" . $json_condition, $info);
|
||||
Cache::tag("document")->set("document_" . $json_condition, $info);
|
||||
return $this->success($info);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class Menu extends BaseModel
|
||||
// if (!empty($cache)) {
|
||||
// return $this->success($cache);
|
||||
// }
|
||||
$list = model('menu')->getList($condition, $field, $order, '', '', '', $limit);
|
||||
$list = model('menu')->getList($condition, $field, $order, '', [], '', $limit);
|
||||
// Cache::tag("menu")->set("getMenuList_" . $data, $list);
|
||||
|
||||
return $this->success($list);
|
||||
|
||||
338
src/app/model/system/Seal.php
Normal file
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
namespace app\model\system;
|
||||
|
||||
use extend\api\HttpClient;
|
||||
use think\facade\Cache;
|
||||
use app\model\BaseModel;
|
||||
use think\facade\Db;
|
||||
|
||||
class Seal extends BaseModel
|
||||
{
|
||||
|
||||
|
||||
|
||||
public function getTree($condition = [], $field = '*', $order = 'sort asc,category_id desc', $limit = null)
|
||||
{
|
||||
$list = model('seal_structure')->getList($condition, $field, $order, '', '', '', $limit);
|
||||
$categoryMap = [];
|
||||
foreach ($list as $item) {
|
||||
$categoryMap[$item['category_id']] = $item;
|
||||
$categoryMap[$item['category_id']]['child_list'] = [];
|
||||
}
|
||||
$buildTree = function ($parentId = 0) use (&$buildTree, $categoryMap) {
|
||||
$children = [];
|
||||
foreach ($categoryMap as $id => $item) {
|
||||
if ($item['pid'] == $parentId) {
|
||||
$item['child_list'] = $buildTree($id);
|
||||
$children[] = $item;
|
||||
}
|
||||
}
|
||||
return $children;
|
||||
};
|
||||
|
||||
$tree = $buildTree(0);
|
||||
return $this->success($tree);
|
||||
}
|
||||
|
||||
|
||||
public function getTree1($condition = [], $field = '*', $order = 'sort asc,category_id desc', $limit = null)
|
||||
{
|
||||
$list = model('seal_structure')->getList($condition, $field, $order, '', '', '', $limit);
|
||||
$categoryMap = [];
|
||||
foreach ($list as $item) {
|
||||
$categoryMap[$item['category_id']] = $item;
|
||||
// 初始化子分类数组(避免子分类为null)
|
||||
$categoryMap[$item['category_id']]['child_list'] = [];
|
||||
}
|
||||
|
||||
// 递归构建树形结构:仅子层级(parentId≠0)添加「请选择」
|
||||
$buildTree = function ($parentId = 0) use (&$buildTree, $categoryMap) {
|
||||
$children = [];
|
||||
foreach ($categoryMap as $id => $item) {
|
||||
if ($item['pid'] == $parentId) {
|
||||
// 1. 递归获取当前分类的子分类(子分类会根据parentId判断是否加占位项)
|
||||
$item['child_list'] = $buildTree($id);
|
||||
$children[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 关键判断:仅当 parentId ≠ 0(子层级)时,才插入「请选择」占位项
|
||||
if ($parentId != 0 && !empty($children)) { // 非顶级 + 有真实子分类时才加(可选,避免空列表加占位)
|
||||
$defaultOption = [
|
||||
'category_id' => 0, // 虚拟ID(与真实分类区分)
|
||||
'pid' => $parentId, // 占位项父ID = 当前层级父ID(匹配真实子分类)
|
||||
'name' => '请选择', // 显示文本
|
||||
'child_list' => [], // 占位项无下级(避免无限嵌套)
|
||||
'is_placeholder' => true // 前端识别标记
|
||||
// 真实分类的其他字段(如sort)可按需添加默认值
|
||||
];
|
||||
|
||||
// 在子层级分类列表头部插入占位项
|
||||
array_unshift($children, $defaultOption);
|
||||
}
|
||||
|
||||
return $children;
|
||||
};
|
||||
|
||||
// 生成树形结构:顶级分类(parentId=0)不添加「请选择」
|
||||
$tree = $buildTree(0);
|
||||
|
||||
return $this->success($tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表带分页
|
||||
* @param array $condition
|
||||
* @param int $page
|
||||
* @param int $page_size
|
||||
* @param string $order
|
||||
* @param bool $field
|
||||
* @param string $alias
|
||||
* @param array $join
|
||||
* @return array
|
||||
*/
|
||||
public function getPageList($table,array $condition = [], $page = 1, $page_size = PAGE_LIST_ROWS,$order = '',$field = true, $alias = '', $join = [], $group='')
|
||||
{
|
||||
$res = model($table)->pageList($condition, $field, $order, $page, $page_size, $alias, $join, $group);
|
||||
return $this->success($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详情
|
||||
* @param array $condition
|
||||
* @param bool $field
|
||||
* @return array
|
||||
*/
|
||||
public function getInfo($table,$condition = [], $field = true, $alias = 'a', $join = null)
|
||||
{
|
||||
$info = model($table)->getInfo($condition, $field,$alias,$join);
|
||||
return $this->success($info);
|
||||
}
|
||||
|
||||
public function delete($table,$condition = [])
|
||||
{
|
||||
$info = model($table)->delete($condition);
|
||||
return $this->success($info);
|
||||
}
|
||||
/**
|
||||
* 获取列表不要分页
|
||||
* @param array $condition
|
||||
* @return array
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function getList($table,$condition = [],$field="*", $order = '', $alias = 'a', $join = [], $group = '')
|
||||
{
|
||||
return model($table)->getList($condition,$field,$order,$alias,$join,$group);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @param array $data
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public function add($table,array $data)
|
||||
{
|
||||
if (empty($data) || empty($table)) {
|
||||
return $this->error('', 'PARAMETER_ERROR');
|
||||
}
|
||||
$res = model($table)->add($data);
|
||||
return $this->success($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
* @param array $data
|
||||
* @param array $condition
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public function edit($table,array $data, array $condition, array $params = [])
|
||||
{
|
||||
if (empty($data)) {
|
||||
return $this->error('', 'PARAMETER_ERROR');
|
||||
}
|
||||
|
||||
$res = model($table)->update($data, $condition);
|
||||
return $this->success($res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param $param
|
||||
* @param $site_id
|
||||
* @return array
|
||||
* @throws \PHPExcel_Exception
|
||||
* @throws \PHPExcel_Reader_Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function importMedium($param, $site_id)
|
||||
{
|
||||
|
||||
|
||||
|
||||
$PHPReader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
|
||||
|
||||
//载入文件
|
||||
$PHPExcel = $PHPReader->load($param[ 'path' ]);
|
||||
|
||||
//获取表中的第一个工作表,如果要获取第二个,把0改为1,依次类推
|
||||
$currentSheet = $PHPExcel->getSheet(0);
|
||||
|
||||
//获取总行数
|
||||
$allRow = $currentSheet->getHighestRow();
|
||||
|
||||
if ($allRow < 2) {
|
||||
return $this->error('', '导入了一个空文件');
|
||||
}
|
||||
|
||||
$index = $param[ 'index' ];
|
||||
|
||||
//每次导入100条
|
||||
$length = $index * 2000;
|
||||
|
||||
if ($index == 1) {
|
||||
$num = 27
|
||||
;
|
||||
$success_num = 0;
|
||||
$error_num = 0;
|
||||
|
||||
$data_record = [
|
||||
"member_num" => ( $allRow - 1 ),
|
||||
"success_num" => 0,
|
||||
"error_num" => 0,
|
||||
"create_time" => time(),
|
||||
"status_name" => "等待导入"
|
||||
];
|
||||
$record = model('seal_medium_import_record')->add($data_record);
|
||||
|
||||
} else {
|
||||
$num = ( ( $index - 1 ) * 100 ) + 1;
|
||||
$success_num = $param[ 'success_num' ];
|
||||
$error_num = $param[ 'error_num' ];
|
||||
$record = $param[ 'record' ];
|
||||
}
|
||||
|
||||
$type_num = 0;
|
||||
model('seal_medium')->startTrans();
|
||||
$field = ['C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T'];
|
||||
$name = ['全氟醚FFKM','聚全氟乙丙稀FEPM','丁苯橡胶SBR','氯丁橡胶CR','聚四氟乙烯PTFE','三元乙丙橡胶EPDM','氯醇橡胶 ECO','丁基橡胶 ⅡR','丁腈橡胶 NBR','丙烯酸酯橡胶ACM','聚氨酯 PU','天然橡胶 NR','氟橡胶 FKM','乙丙烯酸酯橡胶AEM','氢化丁腈橡胶HNBR','氟硅橡胶 FVMQ','氯磺化聚乙烯CSM','硅橡胶 VMQ'];
|
||||
$colors = ['#D5663F','','#E09E36','','#DDDF37','','#75C551','','#54C28D','','#409CBD','','#5B5FBB','','#A061B5','','#BC5A6A',''];//颜色表
|
||||
try {
|
||||
|
||||
for ($i = $num; $i <= $length; $i++) {
|
||||
|
||||
//这一行是化学介质行直接跳过
|
||||
// if($num == 26) continue;
|
||||
|
||||
if ($i > $allRow) {
|
||||
break;
|
||||
}
|
||||
$type_num = $i;
|
||||
|
||||
|
||||
|
||||
$title = $PHPExcel->getActiveSheet()->getCell('B' . $i)->getValue();
|
||||
$title = trim($title);
|
||||
if($title == '') break;
|
||||
//获取每一列的值
|
||||
$value_data = [];
|
||||
foreach($field as $k=>$item){
|
||||
$value = $PHPExcel->getActiveSheet()->getCell($item . $i)->getValue();
|
||||
// //1推荐使用,2有条件使用,3不推荐使用,4不能使用,空格,数据不详
|
||||
$colortext = '#333';
|
||||
if($value == 1){
|
||||
$colortext = '#75c551';
|
||||
}else if($value == 2){
|
||||
$colortext = '#409cbd';
|
||||
}else if($value == 3){
|
||||
$colortext = '#e09e36';
|
||||
}else if($value == 4){
|
||||
$colortext = '#ff0000';
|
||||
}
|
||||
$value_data[] = [
|
||||
'text'=>$name[$k],
|
||||
'value'=>trim($value),
|
||||
'color'=>$colortext,
|
||||
'background'=>$colors[$k]
|
||||
];
|
||||
// echo $value.'<br/>';
|
||||
}
|
||||
// dump($value_data);
|
||||
|
||||
$data = [
|
||||
'name'=>$title,
|
||||
'value'=>json_encode($value_data)
|
||||
];
|
||||
$res = model('seal_medium')->add($data);
|
||||
// dump($data);
|
||||
// break;
|
||||
// exit;
|
||||
|
||||
// $not_data = [
|
||||
// "username" => $username,
|
||||
// "number" => $number,
|
||||
// "company" => $company,
|
||||
// "company_realname" => $company_realname,
|
||||
// "company_mobile" => $company_mobile,
|
||||
// ];
|
||||
|
||||
// if ($username == "" && $mobile == "") {
|
||||
// $not_data[ 'content' ] = "失败,用户名或手机号必须存在一个";
|
||||
// model('member_import_log')->add($not_data);
|
||||
// $error_num++;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// $data = [
|
||||
// "username" => $username,
|
||||
// "number" => $number,
|
||||
// "company" => $company,
|
||||
// "company_realname" => $company_realname,
|
||||
// "company_mobile" => $company_mobile,
|
||||
// ];
|
||||
// $res = model('seal_medium_import_record')->add($data);
|
||||
// $product = model('device')->getInfo([ [ 'id', '=', $res ]]);
|
||||
|
||||
$not_data[ 'content' ] = "成功";
|
||||
// model('member_import_log')->add($not_data);
|
||||
$success_num++;
|
||||
}
|
||||
model('seal_medium')->commit();
|
||||
if ($success_num + $error_num == ( $allRow - 1 )) {
|
||||
$data_record = [
|
||||
"member_num" => ( $allRow - 1 ),
|
||||
"success_num" => $success_num,
|
||||
"error_num" => $error_num,
|
||||
"create_time" => time()
|
||||
];
|
||||
if ($success_num == ( $allRow - 1 )) {
|
||||
$data_record[ 'status_name' ] = '导入成功';
|
||||
} elseif ($error_num == ( $allRow - 1 )) {
|
||||
$data_record[ 'status_name' ] = '导入失败';
|
||||
}
|
||||
|
||||
model('seal_medium_import_record')->update($data_record, [ 'id' => $record ]);
|
||||
}
|
||||
return $this->success([
|
||||
"allRow" => $allRow,
|
||||
"num" => $type_num,
|
||||
"path" => $param[ 'path' ],
|
||||
"name" => $param[ 'filename' ],
|
||||
"success_num" => $success_num,
|
||||
"error_num" => $error_num,
|
||||
"record" => $record
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
model('seal_medium')->rollback();
|
||||
return $this->error('', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
namespace app\model\web;
|
||||
|
||||
use app\model\system\Config as ConfigModel;
|
||||
@@ -35,7 +34,7 @@ class Config extends BaseModel
|
||||
'btn' => '清除',
|
||||
'icon' => 'public/static/img/cache/template.png'
|
||||
],
|
||||
/* [
|
||||
/* [
|
||||
'name' => '刷新菜单',
|
||||
'desc' => '新增/修改插件菜单后,需要刷新插件菜单',
|
||||
'key' => 'menu_cache',
|
||||
@@ -61,7 +60,7 @@ class Config extends BaseModel
|
||||
public function setCaptchaConfig($data, $site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '验证码设置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'CAPTCHA_CONFIG']]);
|
||||
$res = $config->setConfig($data, '验证码设置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'CAPTCHA_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -74,20 +73,20 @@ class Config extends BaseModel
|
||||
public function getCaptchaConfig($site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'CAPTCHA_CONFIG']]);
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'CAPTCHA_CONFIG' ] ]);
|
||||
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'shop_login' => 1,
|
||||
'shop_reception_login' => 1,
|
||||
'shop_reception_register' => 1
|
||||
];
|
||||
} else {
|
||||
if (isset($res['data']['value']['shop_reception_login']) === false) {
|
||||
$res['data']['value']['shop_reception_login'] = 1;
|
||||
if (isset($res[ 'data' ][ 'value' ][ 'shop_reception_login' ]) === false) {
|
||||
$res[ 'data' ][ 'value' ][ 'shop_reception_login' ] = 1;
|
||||
}
|
||||
if (isset($res['data']['value']['shop_reception_register']) === false) {
|
||||
$res['data']['value']['shop_reception_register'] = 1;
|
||||
if (isset($res[ 'data' ][ 'value' ][ 'shop_reception_register' ]) === false) {
|
||||
$res[ 'data' ][ 'value' ][ 'shop_reception_register' ] = 1;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
@@ -102,31 +101,31 @@ class Config extends BaseModel
|
||||
*/
|
||||
public function setDefaultImg($data, $site_id = 0, $app_module = 'shop')
|
||||
{
|
||||
$config_info = $this->getDefaultImg($site_id, $app_module)['data']['value'];
|
||||
$config_info = $this->getDefaultImg($site_id, $app_module)[ 'data' ][ 'value' ];
|
||||
if (!empty($config_info)) {
|
||||
$upload_model = new Upload();
|
||||
if ($data['goods'] && $config_info['goods'] && $data['goods'] != $config_info['goods']) {
|
||||
$upload_model->deletePic($config_info['goods'], $site_id);
|
||||
if ($data[ 'goods' ] && $config_info[ 'goods' ] && $data[ 'goods' ] != $config_info[ 'goods' ]) {
|
||||
$upload_model->deletePic($config_info[ 'goods' ], $site_id);
|
||||
}
|
||||
if ($data['head'] && $config_info['head'] && $data['head'] != $config_info['head']) {
|
||||
$upload_model->deletePic($config_info['head'], $site_id);
|
||||
if ($data[ 'head' ] && $config_info[ 'head' ] && $data[ 'head' ] != $config_info[ 'head' ]) {
|
||||
$upload_model->deletePic($config_info[ 'head' ], $site_id);
|
||||
}
|
||||
if ($data['store'] && $config_info['store'] && $data['store'] != $config_info['store']) {
|
||||
$upload_model->deletePic($config_info['store'], $site_id);
|
||||
if ($data[ 'store' ] && $config_info[ 'store' ] && $data[ 'store' ] != $config_info[ 'store' ]) {
|
||||
$upload_model->deletePic($config_info[ 'store' ], $site_id);
|
||||
}
|
||||
if ($data['article'] && $config_info['article'] && $data['article'] != $config_info['article']) {
|
||||
$upload_model->deletePic($config_info['article'], $site_id);
|
||||
if ($data[ 'article' ] && $config_info[ 'article' ] && $data[ 'article' ] != $config_info[ 'article' ]) {
|
||||
$upload_model->deletePic($config_info[ 'article' ], $site_id);
|
||||
}
|
||||
if ($data['kefu'] && $config_info['kefu'] && $data['kefu'] != $config_info['kefu']) {
|
||||
$upload_model->deletePic($config_info['kefu'], $site_id);
|
||||
if ($data[ 'kefu' ] && $config_info[ 'kefu' ] && $data[ 'kefu' ] != $config_info[ 'kefu' ]) {
|
||||
$upload_model->deletePic($config_info[ 'kefu' ], $site_id);
|
||||
}
|
||||
if ($data['phone'] && $config_info['phone'] && $data['phone'] != $config_info['phone']) {
|
||||
$upload_model->deletePic($config_info['phone'], $site_id);
|
||||
if ($data[ 'phone' ] && $config_info[ 'phone' ] && $data[ 'phone' ] != $config_info[ 'phone' ]) {
|
||||
$upload_model->deletePic($config_info[ 'phone' ], $site_id);
|
||||
}
|
||||
}
|
||||
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '默认图设置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'DEFAULT_IMAGE']]);
|
||||
$res = $config->setConfig($data, '默认图设置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'DEFAULT_IMAGE' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -139,9 +138,9 @@ class Config extends BaseModel
|
||||
public function getDefaultImg($site_id, $app_model = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_model], ['config_key', '=', 'DEFAULT_IMAGE']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_model ], [ 'config_key', '=', 'DEFAULT_IMAGE' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'goods' => 'public/static/img/default_img/square.png',
|
||||
'head' => 'public/static/img/default_img/head.png',
|
||||
'store' => 'public/static/img/default_img/store.png',
|
||||
@@ -151,14 +150,14 @@ class Config extends BaseModel
|
||||
];
|
||||
}
|
||||
|
||||
if (empty($res['data']['value']['head'])) {
|
||||
$res['data']['value']['head'] = 'public/static/img/default_img/head.png';
|
||||
if (empty($res[ 'data' ][ 'value' ][ 'head' ])) {
|
||||
$res[ 'data' ][ 'value' ][ 'head' ] = 'public/static/img/default_img/head.png';
|
||||
}
|
||||
if (empty($res['data']['value']['article'])) {
|
||||
$res['data']['value']['article'] = 'public/static/img/default_img/article.png';
|
||||
if (empty($res[ 'data' ][ 'value' ][ 'article' ])) {
|
||||
$res[ 'data' ][ 'value' ][ 'article' ] = 'public/static/img/default_img/article.png';
|
||||
}
|
||||
if (empty($res['data']['value']['store'])) {
|
||||
$res['data']['value']['store'] = 'public/static/img/default_img/store.png';
|
||||
if (empty($res[ 'data' ][ 'value' ][ 'store' ])) {
|
||||
$res[ 'data' ][ 'value' ][ 'store' ] = 'public/static/img/default_img/store.png';
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
@@ -174,7 +173,7 @@ class Config extends BaseModel
|
||||
public function setCopyright($data, $site_id = 1, $app_model = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '版权设置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_model], ['config_key', '=', 'COPYRIGHT']]);
|
||||
$res = $config->setConfig($data, '版权设置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_model ], [ 'config_key', '=', 'COPYRIGHT' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -187,16 +186,16 @@ class Config extends BaseModel
|
||||
public function getCopyright($site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'COPYRIGHT']]);
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'COPYRIGHT' ] ]);
|
||||
|
||||
$auth_info = cache('auth_info_copyright');
|
||||
if (empty($auth_info)) {
|
||||
$upgrade_model = new Upgrade();
|
||||
$auth_info = $upgrade_model->authInfo();
|
||||
cache('auth_info_copyright', $auth_info, ['expire' => 604800]);
|
||||
cache('auth_info_copyright', $auth_info, [ 'expire' => 604800 ]);
|
||||
}
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'logo' => '',
|
||||
'company_name' => '',
|
||||
'copyright_link' => '',
|
||||
@@ -207,15 +206,16 @@ class Config extends BaseModel
|
||||
'market_supervision_url' => ''
|
||||
];
|
||||
} else {
|
||||
if (is_null($auth_info) || $auth_info['code'] != 0) {
|
||||
$res['data']['value']['logo'] = '';
|
||||
$res['data']['value']['company_name'] = '';
|
||||
$res['data']['value']['copyright_link'] = '';
|
||||
$res['data']['value']['copyright_desc'] = '';
|
||||
if (is_null($auth_info) || $auth_info[ 'code' ] != 0) {
|
||||
$res[ 'data' ][ 'value' ][ 'logo' ] = '';
|
||||
$res[ 'data' ][ 'value' ][ 'company_name' ] = '';
|
||||
$res[ 'data' ][ 'value' ][ 'copyright_link' ] = '';
|
||||
$res[ 'data' ][ 'value' ][ 'copyright_desc' ] = '';
|
||||
}
|
||||
|
||||
}
|
||||
// 检查是否授权
|
||||
$res['data']['value']['auth'] = true;
|
||||
$res[ 'data' ][ 'value' ][ 'auth' ] = true;
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ class Config extends BaseModel
|
||||
public function setAuth($data, $site_id = 1, $app_model = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '授权设置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_model], ['config_key', '=', 'AUTH']]);
|
||||
$res = $config->setConfig($data, '授权设置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_model ], [ 'config_key', '=', 'AUTH' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -240,9 +240,9 @@ class Config extends BaseModel
|
||||
public function getAuth($site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'AUTH']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'AUTH' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'code' => '',
|
||||
];
|
||||
}
|
||||
@@ -259,11 +259,11 @@ class Config extends BaseModel
|
||||
public function setMapConfig($data, $site_id, $app_model = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '地图设置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_model], ['config_key', '=', 'MAP_CONFIG']]);
|
||||
$res = $config->setConfig($data, '地图设置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_model ], [ 'config_key', '=', 'MAP_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 获取地图设置
|
||||
* @param int $site_id
|
||||
* @param string $app_module
|
||||
@@ -273,16 +273,16 @@ class Config extends BaseModel
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'MAP_CONFIG']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
'tencent_map_key' => '2PJBZ-A263Q-SED5B-4SAAB-HCUQ5-DUFHE', //默认一个地图TB5BZ-FBRRX-2RJ4C-76SZY-TYQ3H-F4BFC
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'tencent_map_key' => '2PJBZ-A263Q-SED5B-4SAAB-HCUQ5-DUFHE',//默认一个地图TB5BZ-FBRRX-2RJ4C-76SZY-TYQ3H-F4BFC
|
||||
'wap_is_open' => 1, // 手机端是否开启定位
|
||||
'wap_valid_time' => 5 // 手机端定位有效期/分钟,过期后将重新获取定位信息,0为不过期
|
||||
];
|
||||
}
|
||||
$res['data']['value']['wap_is_open'] = $res['data']['value']['wap_is_open'] ?? 1;
|
||||
$res['data']['value']['wap_valid_time'] = $res['data']['value']['wap_valid_time'] ?? 5;
|
||||
$res['data']['value']['tencent_map_key'] = '2PJBZ-A263Q-SED5B-4SAAB-HCUQ5-DUFHE';
|
||||
$res[ 'data' ][ 'value' ][ 'wap_is_open' ] = $res[ 'data' ][ 'value' ][ 'wap_is_open' ] ?? 1;
|
||||
$res[ 'data' ][ 'value' ][ 'wap_valid_time' ] = $res[ 'data' ][ 'value' ][ 'wap_valid_time' ] ?? 5;
|
||||
$res[ 'data' ][ 'value' ]['tencent_map_key'] = '2PJBZ-A263Q-SED5B-4SAAB-HCUQ5-DUFHE';
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -296,19 +296,19 @@ class Config extends BaseModel
|
||||
public function seth5DomainName($data, $site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$search = '/^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/';
|
||||
if ($data['deploy_way'] == 'separate') {
|
||||
if (!preg_match($search, $data['domain_name_h5'])) {
|
||||
if ($data[ 'deploy_way' ] == 'separate') {
|
||||
if (!preg_match($search, $data[ 'domain_name_h5' ])) {
|
||||
return $this->error('', '请输入正确的域名地址');
|
||||
}
|
||||
}
|
||||
// 默认部署,更新店铺域名
|
||||
if ($data['deploy_way'] == 'default') {
|
||||
if ($data[ 'deploy_way' ] == 'default') {
|
||||
$this->setShopDomainConfig([
|
||||
'domain_name' => __ROOT__
|
||||
], $site_id);
|
||||
}
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, 'H5域名配置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'H5_DOMAIN_NAME']]);
|
||||
$res = $config->setConfig($data, 'H5域名配置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'H5_DOMAIN_NAME' ] ]);
|
||||
|
||||
return $res;
|
||||
}
|
||||
@@ -322,9 +322,9 @@ class Config extends BaseModel
|
||||
public function getH5DomainName($site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'H5_DOMAIN_NAME']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'H5_DOMAIN_NAME' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'domain_name_h5' => __ROOT__ . '/h5',
|
||||
'deploy_way' => 'default'
|
||||
];
|
||||
@@ -342,7 +342,7 @@ class Config extends BaseModel
|
||||
public function setDomainJumpConfig($data, $site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '获取域名跳转配置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'DOMAIN_JUMP_CONFIG']]);
|
||||
$res = $config->setConfig($data, '获取域名跳转配置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'DOMAIN_JUMP_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -356,12 +356,12 @@ class Config extends BaseModel
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([
|
||||
['site_id', '=', $site_id],
|
||||
['app_module', '=', $app_module],
|
||||
['config_key', '=', 'DOMAIN_JUMP_CONFIG']
|
||||
[ 'site_id', '=', $site_id ],
|
||||
[ 'app_module', '=', $app_module ],
|
||||
[ 'config_key', '=', 'DOMAIN_JUMP_CONFIG' ]
|
||||
]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'jump_type' => 3, // 1:用户前台,2:商家后台,3:引导页
|
||||
];
|
||||
}
|
||||
@@ -378,19 +378,19 @@ class Config extends BaseModel
|
||||
public function setPcDomainName($data, $site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$search = '/^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/';
|
||||
if ($data['deploy_way'] == 'separate') {
|
||||
if (!preg_match($search, $data['domain_name_pc'])) {
|
||||
if ($data[ 'deploy_way' ] == 'separate') {
|
||||
if (!preg_match($search, $data[ 'domain_name_pc' ])) {
|
||||
return $this->error('', '请输入正确的域名地址');
|
||||
}
|
||||
}
|
||||
// 默认部署,更新店铺域名
|
||||
if ($data['deploy_way'] == 'default') {
|
||||
if ($data[ 'deploy_way' ] == 'default') {
|
||||
$this->setShopDomainConfig([
|
||||
'domain_name' => __ROOT__
|
||||
], $site_id);
|
||||
}
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, 'PC域名配置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'PC_DOMAIN_NAME']]);
|
||||
$res = $config->setConfig($data, 'PC域名配置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'PC_DOMAIN_NAME' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -403,19 +403,19 @@ class Config extends BaseModel
|
||||
public function getPcDomainName($site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'PC_DOMAIN_NAME']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'PC_DOMAIN_NAME' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'domain_name_pc' => __ROOT__ . '/web',
|
||||
'deploy_way' => 'default'
|
||||
];
|
||||
} else {
|
||||
if ($res['data']['value']['domain_name_pc'] == '' || empty($res['data']['value']['deploy_way']) || $res['data']['value']['deploy_way'] == 'default') {
|
||||
$res['data']['value'] = [
|
||||
if ($res[ 'data' ][ 'value' ][ 'domain_name_pc' ] == '' || empty($res[ 'data' ][ 'value' ][ 'deploy_way' ]) || $res[ 'data' ][ 'value' ][ 'deploy_way' ] == 'default') {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'domain_name_pc' => __ROOT__ . '/web'
|
||||
];
|
||||
}
|
||||
$res['data']['value']['deploy_way'] = $res['data']['value']['deploy_way'] ?? 'default';
|
||||
$res[ 'data' ][ 'value' ][ 'deploy_way' ] = $res[ 'data' ][ 'value' ][ 'deploy_way' ] ?? 'default';
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
@@ -430,7 +430,7 @@ class Config extends BaseModel
|
||||
public function setHotSearchWords($data, $site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '商品热门搜索关键词', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_HOT_SEARCH_WORDS_CONFIG']]);
|
||||
$res = $config->setConfig($data, '商品热门搜索关键词', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_HOT_SEARCH_WORDS_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -443,9 +443,9 @@ class Config extends BaseModel
|
||||
public function getHotSearchWords($site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_HOT_SEARCH_WORDS_CONFIG']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_HOT_SEARCH_WORDS_CONFIG' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'words' => ''
|
||||
];
|
||||
}
|
||||
@@ -462,7 +462,7 @@ class Config extends BaseModel
|
||||
public function setGuessYouLike($data, $site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '商品推荐', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_GUESS_YOU_LIKE_CONFIG']]);
|
||||
$res = $config->setConfig($data, '商品推荐', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_GUESS_YOU_LIKE_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -475,11 +475,11 @@ class Config extends BaseModel
|
||||
public function getGuessYouLike($site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_GUESS_YOU_LIKE_CONFIG']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_GUESS_YOU_LIKE_CONFIG' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'title' => '猜你喜欢',
|
||||
'supportPage' => ['goods_detail', 'cart', 'collect', 'pay', 'order_detail', 'super_member', 'guafen', 'fenxiao_level'],
|
||||
'supportPage' => [ 'goods_detail', 'cart', 'collect', 'pay', 'order_detail', 'super_member', 'guafen', 'fenxiao_level' ],
|
||||
'sources' => 'sort',
|
||||
'goodsIds' => [],
|
||||
'fontWeight' => false,
|
||||
@@ -508,7 +508,7 @@ class Config extends BaseModel
|
||||
],
|
||||
];
|
||||
}
|
||||
$res['data']['value']['nameLineMode'] = $res['data']['value']['nameLineMode'] ?? 'single'; // 商品名称,单行、多行展示
|
||||
$res[ 'data' ][ 'value' ][ 'nameLineMode' ] = $res[ 'data' ][ 'value' ][ 'nameLineMode' ] ?? 'single'; // 商品名称,单行、多行展示
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -522,17 +522,17 @@ class Config extends BaseModel
|
||||
public function getDiyAdv($site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'DIY_STARTADV']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
'list' => [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'DIY_STARTADV' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])){
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'list' =>[
|
||||
[
|
||||
'title' => '启动广告',
|
||||
'link' => [
|
||||
'name' => ''
|
||||
'title'=>'启动广告',
|
||||
'link'=>[
|
||||
'name'=>''
|
||||
],
|
||||
'iconType' => 'img',
|
||||
'imageUrl' => "public/static/ext/diyview/img/preview/advs_default.png"
|
||||
'iconType'=>'img',
|
||||
'imageUrl'=>"public/static/ext/diyview/img/preview/advs_default.png"
|
||||
]
|
||||
],
|
||||
'advtype' => 1,
|
||||
@@ -552,12 +552,12 @@ class Config extends BaseModel
|
||||
public function setDiyAdv($data, $site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '启动广告', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'DIY_STARTADV']]);
|
||||
$res = $config->setConfig($data, '启动广告', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'DIY_STARTADV' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* 获取VR
|
||||
* @param $site_id
|
||||
* @param $app_module
|
||||
@@ -566,14 +566,14 @@ class Config extends BaseModel
|
||||
public function getDiyVr($site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'DIY_VR']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'DIY_VR' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])){
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'title' => '工厂展示',
|
||||
'url' => 'https://baidu.com',
|
||||
];
|
||||
}
|
||||
// $res[ 'data' ][ 'value' ][ 'nameLineMode' ] = $res[ 'data' ][ 'value' ][ 'nameLineMode' ] ?? 'single'; // 商品名称,单行、多行展示
|
||||
// $res[ 'data' ][ 'value' ][ 'nameLineMode' ] = $res[ 'data' ][ 'value' ][ 'nameLineMode' ] ?? 'single'; // 商品名称,单行、多行展示
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -587,7 +587,7 @@ class Config extends BaseModel
|
||||
public function setDiyVr($data, $site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, 'VR展示', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'DIY_VR']]);
|
||||
$res = $config->setConfig($data, 'VR展示', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'DIY_VR' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -602,7 +602,7 @@ class Config extends BaseModel
|
||||
public function setGoodsListConfig($data, $site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '商品列表配置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_LIST_CONFIG']]);
|
||||
$res = $config->setConfig($data, '商品列表配置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_LIST_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -615,9 +615,9 @@ class Config extends BaseModel
|
||||
public function getGoodsListConfig($site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_LIST_CONFIG']]);
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_LIST_CONFIG' ] ]);
|
||||
//数据格式化
|
||||
if (empty($res['data']['value'])) {
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$data = [
|
||||
'fontWeight' => false,
|
||||
'padding' => 10,
|
||||
@@ -644,9 +644,9 @@ class Config extends BaseModel
|
||||
]
|
||||
]
|
||||
];
|
||||
$res['data']['value'] = $data;
|
||||
$res[ 'data' ][ 'value' ] = $data;
|
||||
}
|
||||
$res['data']['value']['nameLineMode'] = $res['data']['value']['nameLineMode'] ?? 'single'; // 商品名称,单行、多行展示
|
||||
$res[ 'data' ][ 'value' ][ 'nameLineMode' ] = $res[ 'data' ][ 'value' ][ 'nameLineMode' ] ?? 'single'; // 商品名称,单行、多行展示
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -660,7 +660,7 @@ class Config extends BaseModel
|
||||
public function setDefaultSearchWords($data, $site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '默认搜索关键词', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_DEFAULT_SEARCH_WORDS_CONFIG']]);
|
||||
$res = $config->setConfig($data, '默认搜索关键词', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_DEFAULT_SEARCH_WORDS_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -673,9 +673,9 @@ class Config extends BaseModel
|
||||
public function getDefaultSearchWords($site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_DEFAULT_SEARCH_WORDS_CONFIG']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_DEFAULT_SEARCH_WORDS_CONFIG' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'words' => '搜索 商品'
|
||||
];
|
||||
}
|
||||
@@ -692,7 +692,7 @@ class Config extends BaseModel
|
||||
public function setGoodsSort($data, $site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '商品默认排序方式', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_SORT_CONFIG']]);
|
||||
$res = $config->setConfig($data, '商品默认排序方式', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_SORT_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -705,9 +705,9 @@ class Config extends BaseModel
|
||||
public function getGoodsSort($site_id, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_SORT_CONFIG']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_SORT_CONFIG' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'type' => 'asc',
|
||||
'default_value' => 100
|
||||
];
|
||||
@@ -725,7 +725,7 @@ class Config extends BaseModel
|
||||
public function setCategoryConfig($data, $site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, 'PC端首页分类设置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'SHOP_CATEGORY_CONFIG']]);
|
||||
$res = $config->setConfig($data, 'PC端首页分类设置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'SHOP_CATEGORY_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -738,9 +738,9 @@ class Config extends BaseModel
|
||||
public function getCategoryConfig($site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'SHOP_CATEGORY_CONFIG']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'SHOP_CATEGORY_CONFIG' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'category' => 1,
|
||||
'img' => 1
|
||||
];
|
||||
@@ -758,7 +758,7 @@ class Config extends BaseModel
|
||||
public function setGoodsDetailConfig($data, $site_id, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '商品详情配置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_DETAIL_CONFIG']]);
|
||||
$res = $config->setConfig($data, '商品详情配置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_DETAIL_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -771,9 +771,9 @@ class Config extends BaseModel
|
||||
public function getGoodsDetailConfig($site_id, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_DETAIL_CONFIG']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_DETAIL_CONFIG' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'nav_bar_switch' => 0, // 是否透明,0:不透明,1:透明
|
||||
'introduction_color' => '#303133',
|
||||
];
|
||||
@@ -791,7 +791,7 @@ class Config extends BaseModel
|
||||
public function setShopDomainConfig($data, $site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '店铺域名配置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'SHOP_DOMAIN_CONFIG']]);
|
||||
$res = $config->setConfig($data, '店铺域名配置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'SHOP_DOMAIN_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -804,12 +804,12 @@ class Config extends BaseModel
|
||||
public function getShopDomainConfig($site_id = 1, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'SHOP_DOMAIN_CONFIG']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'SHOP_DOMAIN_CONFIG' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'domain_name' => __ROOT__,
|
||||
];
|
||||
$this->setShopDomainConfig($res['data']['value'], $site_id);
|
||||
$this->setShopDomainConfig($res[ 'data' ][ 'value' ], $site_id);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
@@ -817,11 +817,11 @@ class Config extends BaseModel
|
||||
{
|
||||
$qq_map = new \app\model\map\QqMap(['key' => $tencent_map_key]);
|
||||
$res = $qq_map->ipToDetail([
|
||||
'ip' => request()->ip() != '127.0.0.1' ? $_SERVER['REMOTE_ADDR'] : '',
|
||||
'ip' => request()->ip() != '127.0.0.1' ? $_SERVER[ 'REMOTE_ADDR' ] : '',
|
||||
]);
|
||||
if (!empty($res)) {
|
||||
if ($res['status'] != 0 && $type == 0) {
|
||||
$res['message'] = '腾讯地图配置错误,无法定位地址';
|
||||
if ($res[ 'status' ] != 0 && $type == 0) {
|
||||
$res[ 'message' ] = '腾讯地图配置错误,无法定位地址';
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
@@ -857,7 +857,7 @@ class Config extends BaseModel
|
||||
public function setGoodsNo($data, $site_id, $app_module)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->setConfig($data, '商品编码设置', 1, [['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_NO_CONFIG']]);
|
||||
$res = $config->setConfig($data, '商品编码设置', 1, [ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_NO_CONFIG' ] ]);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -870,9 +870,9 @@ class Config extends BaseModel
|
||||
public function getGoodsNo($site_id, $app_module = 'shop')
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
$res = $config->getConfig([['site_id', '=', $site_id], ['app_module', '=', $app_module], ['config_key', '=', 'GOODS_NO_CONFIG']]);
|
||||
if (empty($res['data']['value'])) {
|
||||
$res['data']['value'] = [
|
||||
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', $app_module ], [ 'config_key', '=', 'GOODS_NO_CONFIG' ] ]);
|
||||
if (empty($res[ 'data' ][ 'value' ])) {
|
||||
$res[ 'data' ][ 'value' ] = [
|
||||
'uniqueness_switch' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -201,8 +201,89 @@ class DiyViewLink extends BaseModel
|
||||
$link_list[ $k ][ 'child_list' ] = [];
|
||||
}
|
||||
}
|
||||
|
||||
// PDF文件分类列表
|
||||
foreach ($link_list as $k => $v) {
|
||||
if (isset($v[ 'child_list' ])) {
|
||||
foreach ($v[ 'child_list' ] as $ck => $cv) {
|
||||
if ($cv[ 'name' ] == 'FILES_CATE_PAGE') {
|
||||
$link_list[ $k ][ 'child_list' ][ $ck ][ 'child_list' ] = [ $this->getFllesCateGoryPageLinkList($params[ 'site_id' ])[ 'data' ] ];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$link_list[ $k ][ 'child_list' ] = [];
|
||||
}
|
||||
}
|
||||
|
||||
// PDF文件列表
|
||||
foreach ($link_list as $k => $v) {
|
||||
if (isset($v[ 'child_list' ])) {
|
||||
foreach ($v[ 'child_list' ] as $ck => $cv) {
|
||||
if ($cv[ 'name' ] == 'FILES_LIST_PAGE') {
|
||||
$link_list[ $k ][ 'child_list' ][ $ck ][ 'child_list' ] = [ $this->getFllesPageLinkList($params[ 'site_id' ])[ 'data' ] ];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$link_list[ $k ][ 'child_list' ] = [];
|
||||
}
|
||||
}
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export($link_list,true));
|
||||
return $this->success($link_list);
|
||||
}
|
||||
/**
|
||||
* 查询文章列表
|
||||
* @param $site_id
|
||||
* @return array
|
||||
*/
|
||||
public function getFllesPageLinkList($site_id)
|
||||
{
|
||||
// $article_model = new ArticleModel();
|
||||
// $condition = [ [ 'site_id', '=', $site_id ], [ 'status', '=', 1 ] ];
|
||||
// $site_diy_view_list = $article_model->getArticleList($condition,'*', 'create_time desc', 100);
|
||||
$files_list = model('files')->getList( [ [ 'site_id', '=', $site_id ] ]);
|
||||
|
||||
$link_mic = [
|
||||
'name' => 'FILES_LIST',
|
||||
'title' => '文件列表',
|
||||
'parent' => 'FILES_LIST_PAGE',
|
||||
'child_list' => []
|
||||
];
|
||||
foreach ($files_list as $page_k => $page_v) {
|
||||
$link_mic[ 'child_list' ][] = [
|
||||
'name' => 'FILES_'.$page_v[ 'files_id' ],
|
||||
'title' => $page_v[ 'files_title' ],
|
||||
'parent' => 'FILES_LIST',
|
||||
'wap_url' => '/pages_tool/files/detail?files_id=' . $page_v[ 'files_id' ]
|
||||
];
|
||||
}
|
||||
|
||||
return $this->success($link_mic);
|
||||
}
|
||||
/**
|
||||
* 查询文件分类列表
|
||||
* @param $site_id
|
||||
* @return array
|
||||
*/
|
||||
public function getFllesCateGoryPageLinkList($site_id)
|
||||
{
|
||||
$cate_gorylist = model('files_category')->getList( [ [ 'site_id', '=', $site_id ] ]);
|
||||
$link_mic = [
|
||||
'name' => 'FILESCATEGORY_LIST',
|
||||
'title' => '文件分类',
|
||||
'parent' => 'FILES_CATE_PAGE',
|
||||
'child_list' => []
|
||||
];
|
||||
foreach ($cate_gorylist as $page_k => $page_v) {
|
||||
$link_mic[ 'child_list' ][] = [
|
||||
'name' => 'FILES_CATEGORY_'.$page_v[ 'id' ],
|
||||
'title' => $page_v[ 'category_name' ],
|
||||
'parent' => 'FILES_CATEGORY_LIST',
|
||||
'wap_url' => '/pages_tool/files/list?category_id=' . $page_v[ 'id' ]
|
||||
];
|
||||
}
|
||||
return $this->success($link_mic);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文章分类列表
|
||||
* @param $site_id
|
||||
|
||||
@@ -56,7 +56,7 @@ class Shop extends BasePlatform
|
||||
['user u','u.site_id = p.uniacid','left'],
|
||||
];
|
||||
$list = $Comm_model->getPageList('platform_shop',$condition, $page, $page_size, $order,'p.*,s.site_realname,s.end_time,s.site_tel,s.logo,u.status as ustatus,u.uid','p',$join);
|
||||
file_put_contents(__DIR__ . '/debug.txt', var_export(Db::getLastSql(),true));
|
||||
// file_put_contents(__DIR__ . '/debug.txt', var_export(Db::getLastSql(),true));
|
||||
foreach($list['data']['list'] as &$row){
|
||||
$row['create_time'] = date('Y-m-d H:i:s',$row['create_time']);
|
||||
$row['end_time'] = date('Y-m-d H:i:s',$row['end_time']);
|
||||
|
||||
@@ -123,11 +123,12 @@ class BaseShop extends Controller
|
||||
$config_view = Config::get('view');
|
||||
$config_view[ 'tpl_replace_string' ] = array_merge($config_view[ 'tpl_replace_string' ], $this->replace);
|
||||
Config::set($config_view, 'view');
|
||||
// 其他操作
|
||||
|
||||
if (!request()->isAjax()) {
|
||||
$this->initBaseInfo();
|
||||
$this->loadTemplate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,6 +197,14 @@ class BaseShop extends Controller
|
||||
$where[] = ['id','<>',2498];
|
||||
$where1[] = ['id','<>',2498];
|
||||
}
|
||||
if($this->site_id != 2230){
|
||||
$where[] = ['id','<>',2651];
|
||||
$where1[] = ['id','<>',2651];
|
||||
}
|
||||
if($this->site_id != 2558){
|
||||
$where[] = ['id','<>',2657];
|
||||
$where1[] = ['id','<>',2657];
|
||||
}
|
||||
if ($this->user_info[ 'is_admin' ] || $this->group_info[ 'is_system' ] == 1) {
|
||||
$menus = $menu_model->getMenuList($where, $field, 'level asc,sort asc');
|
||||
} else {
|
||||
|
||||