首页
关于小站
朋友
壁纸
留言
时光之书
笔顺字帖
LayUI手册
Search
1
【PHP】PHPoffice/PHPSpreadsheet读取和写入Excel
1,681 阅读
2
【Layui】控制页面元素展示隐藏
1,528 阅读
3
【Git】No tracked branch configured for branch master or the branch doesn't exist.
1,468 阅读
4
【PHP】PHP实现JWT生成和验证
1,382 阅读
5
精准检测,助力社交管理 —— 微信好友检测服务来袭!
1,290 阅读
默认分类
PHP
ThinkPHP
Laravel
面向对象
设计模式
算法
基础
网络安全
Web
HTML
CSS
JavaScript
jQuery
Layui
VUE
uni-app
Database
MySQL
Redis
RabbitMQ
Nginx
Git
Linux
Soft Ware
Windows
网赚
Go
Docker
登录
Search
标签搜索
PHP
函数
方法
类
MySQL
ThinkPHP
JavaScript
OOP
Layui
Web
Server
Docker
Linux
PHPSpreadsheet
PHPoffice
Array
设计模式
Nginx
Git
排序算法
小破孩
累计撰写
251
篇文章
累计收到
13
条评论
首页
栏目
默认分类
PHP
ThinkPHP
Laravel
面向对象
设计模式
算法
基础
网络安全
Web
HTML
CSS
JavaScript
jQuery
Layui
VUE
uni-app
Database
MySQL
Redis
RabbitMQ
Nginx
Git
Linux
Soft Ware
Windows
网赚
Go
Docker
页面
关于小站
朋友
壁纸
留言
时光之书
笔顺字帖
LayUI手册
搜索到
247
篇与
的结果
2025-07-30
【开发基础】nginx/postgresql/php/redis/ssdb的启动/关闭/重启命令及配置文件修改
以下是 Nginx、PostgreSQL、PHP、Redis 和 SSDB 在 Linux 系统下的常用管理命令及配置文件位置总结:1. Nginx启动/关闭/重启# 使用 systemd(CentOS 7+/Ubuntu 16.04+) systemctl start nginx # 启动 systemctl stop nginx # 停止 systemctl restart nginx # 重启 systemctl reload nginx # 重新加载配置 systemctl status nginx # 查看状态 # 使用 service(旧版系统) service nginx start|stop|restart|reload # 直接调用 nginx 命令(需 root 权限) nginx # 启动 nginx -s stop # 快速停止 nginx -s quit # 优雅停止 nginx -s reload # 重新加载配置配置文件主配置文件:/etc/nginx/nginx.conf站点配置目录:/etc/nginx/conf.d/ 或 /etc/nginx/sites-enabled/修改后检查配置并重启:nginx -t # 检查配置语法 systemctl reload nginx # 重新加载配置2. PostgreSQL启动/关闭/重启# 使用 systemd systemctl start postgresql-<版本号> # 启动(如:postgresql-14) systemctl stop postgresql-<版本号> # 停止 systemctl restart postgresql-<版本号> # 重启 systemctl status postgresql-<版本号> # 查看状态 # Ubuntu/Debian 通用命令 systemctl start|stop|restart postgresql # 手动控制(需切换到 postgres 用户) sudo -u postgres pg_ctl -D /var/lib/pgsql/<版本号>/data start|stop|restart配置文件主配置文件:/var/lib/pgsql/<版本号>/data/postgresql.conf认证配置:/var/lib/pgsql/<版本号>/data/pg_hba.conf修改后重启服务:systemctl restart postgresql-<版本号>3. PHP-FPM启动/关闭/重启# 使用 systemd(PHP-FPM 通常作为 FastCGI 进程管理器) systemctl start php-fpm # 启动(CentOS/RHEL) systemctl start php<版本号>-fpm # 启动(Ubuntu/Debian,如:php8.1-fpm) systemctl stop|restart|status php-fpm # 直接调用 php-fpm 命令 php-fpm -D # 后台启动 php-fpm -t # 检查配置语法 kill -USR2 `cat /run/php-fpm.pid` # 优雅重启配置文件主配置文件:/etc/php-fpm.conf 或 /etc/php/<版本号>/fpm/php-fpm.conf池配置目录:/etc/php-fpm.d/ 或 /etc/php/<版本号>/fpm/pool.d/PHP .ini 文件:/etc/php.ini 或 /etc/php/<版本号>/fpm/php.ini修改后重启服务:systemctl restart php-fpm4. Redis启动/关闭/重启# 使用 systemd systemctl start redis # 启动 systemctl stop redis # 停止 systemctl restart redis # 重启 systemctl status redis # 查看状态 # 手动启动(需指定配置文件) redis-server /etc/redis.conf # 前台启动 redis-server /etc/redis.conf & # 后台启动 # 关闭 Redis(通过客户端) redis-cli shutdown配置文件主配置文件:/etc/redis.conf修改后重启服务:systemctl restart redis5. SSDB启动/关闭/重启# 使用 systemd(需手动创建 service 文件) systemctl start ssdb # 启动 systemctl stop ssdb # 停止 systemctl restart ssdb # 重启 # 手动控制(通过 ssdb-server 命令) /opt/ssdb/ssdb-server /opt/ssdb/ssdb.conf -d # 后台启动 /opt/ssdb/ssdb-cli -h 127.0.0.1 -p 8888 shutdown # 关闭配置文件主配置文件:/opt/ssdb/ssdb.conf修改后重启服务:systemctl restart ssdb注意事项路径差异:不同 Linux 发行版或安装方式(源码/包管理器)可能导致配置文件路径不同,建议通过 whereis 或 find 命令查找。权限问题:操作服务时需确保用户有足够权限(如使用 sudo)。配置检查:修改配置文件后,建议先检查语法再重启服务。版本兼容性:部分命令可能因软件版本不同而略有差异。
2025年07月30日
2 阅读
0 评论
0 点赞
2025-07-30
【开发基础】macOS系统的常规使用方法
一、触控板使用技巧双指操作双指滑动:实现页面上下滚动双指开合:进行缩放操作双指左右滑动:在浏览器或桌面空间之间进行切换三指操作三指上推:显示所有打开的应用程序三指下滑:隐藏当前应用程序,显示桌面三指左右滑动:在全屏应用程序之间进行切换四指操作四指捏合:打开Launchpad(应用程序启动器)四指展开:显示应用程序坞二、Homebrew包管理器Homebrew是macOS系统上常用的包管理器,可用于安装各种软件。安装Homebrew打开终端,执行以下命令:/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"常用brew命令brew install <package>:安装软件包brew uninstall <package>:卸载软件包brew upgrade <package>:升级软件包brew list:查看已安装的软件包brew search <package>:搜索软件包brew update:更新Homebrew自身brew doctor:检查Homebrew的状态三、macOS系统目录结构主要目录/Applications:这里存放的是用户安装的应用程序/System:包含系统文件,一般情况下不要对其进行修改/Users:用户主目录,例如/Users/your_username/Library:系统级别的资源库/usr:包含Unix工具和开发文件/tmp:临时文件目录,系统重启后该目录下的文件会被清除用户目录~/Documents:用于存放文档~/Downloads:下载文件的存放位置~/Desktop:桌面文件的存放处~/Pictures:图片文件的存放目录~/Movies:视频文件的存放目录~/Music:音乐文件的存放目录四、常用Linux命令(macOS基于Unix)文件操作ls:列出目录内容ls -l:以长格式显示文件和目录信息ls -a:显示包括隐藏文件在内的所有文件cd <directory>:切换到指定目录cd ..:返回上一级目录mkdir <directory>:创建新目录rm <file>:删除文件rm -r <directory>:递归删除目录及其内容cp <source> <destination>:复制文件或目录mv <source> <destination>:移动或重命名文件文件查看cat <file>:查看文件内容less <file>:分页查看文件内容head <file>:显示文件的开头部分tail <file>:显示文件的结尾部分权限管理chmod <permissions> <file>:修改文件权限chown <owner> <file>:修改文件所有者搜索grep <pattern> <file>:在文件中搜索指定模式find <directory> -name <pattern>:在目录中查找文件系统信息uname -a:显示系统信息df -h:查看磁盘使用情况free -h:查看内存使用情况top:显示正在运行的进程htop:一个更高级的进程查看器(需要先使用brew安装)网络ping <host>:测试与主机的连接ifconfig:显示网络接口信息curl <url>:下载URL内容wget <url>:下载文件(需要先使用brew安装)五、终端快捷键光标移动Ctrl + A:将光标移动到行首Ctrl + E:将光标移动到行尾Ctrl + F:光标向前移动一个字符Ctrl + B:光标向后移动一个字符历史命令Up Arrow:查看上一条命令Down Arrow:查看下一条命令Ctrl + R:搜索历史命令其他Ctrl + L:清屏Ctrl + C:中断当前命令Ctrl + D:退出终端会话六、其他实用技巧截图Command + Shift + 3:截取整个屏幕Command + Shift + 4:截取屏幕的一部分Command + Shift + 5:打开截图工具(可录制屏幕)快速操作Command + Tab:在应用程序之间进行切换Command + Space:打开Spotlight搜索Option + Click:在应用程序坞中查看应用程序的所有窗口Command + H:隐藏当前应用程序Command + Q:退出当前应用程序
2025年07月30日
0 阅读
0 评论
0 点赞
2025-07-26
【PHP】ThinkPHP8数据库迁移示例
以下是一个包含 各种字段类型、约束条件、索引和特殊配置 的ThinkPHP8数据库迁移示例(以products产品表为例),覆盖常见场景:迁移文件示例(database/migrations/20250726153000_create_products_table.php)<?php use think\migration\Migrator; use think\migration\db\Column; class CreateProductsTable extends Migrator { /** * 执行迁移(创建表结构) */ public function change() { // 创建products表,配置表级参数 $table = $this->table('products', [ 'engine' => 'InnoDB', // 数据库引擎 'charset' => 'utf8mb4', // 字符集 'collation' => 'utf8mb4_unicode_ci', // 排序规则 'comment' => '产品表(包含多种字段类型示例)', // 表注释 'auto_increment' => 1000, // 自增ID起始值(MySQL支持) ]); // 字段定义(覆盖各种场景) $table // 1. 自增主键(整数型,无符号,自增) ->addColumn('id', 'integer', [ 'identity' => true, // 自增 'unsigned' => true, // 无符号(只存正数) 'comment' => '产品ID(主键)' ]) // 2. 字符串(有限长度,非空,唯一索引) ->addColumn('sku', 'string', [ 'limit' => 50, // 长度限制 'null' => false, // 非空(默认) 'comment' => '产品SKU(唯一标识)', 'unique' => true // 唯一约束 ]) // 3. 字符串(长文本,可空,带默认值) ->addColumn('name', 'string', [ 'limit' => 255, 'null' => false, 'default' => '', // 默认空字符串 'comment' => '产品名称' ]) // 4. 文本类型(无长度限制,可空) ->addColumn('description', 'text', [ 'null' => true, // 允许为空 'comment' => '产品详细描述' ]) // 5. 小数类型(高精度,带默认值) ->addColumn('price', 'decimal', [ 'precision' => 10, // 总位数 'scale' => 2, // 小数位数 'default' => 0.00, // 默认0.00 'comment' => '产品售价' ]) // 6. 整数类型(无符号,默认值,索引) ->addColumn('stock', 'integer', [ 'unsigned' => true, // 无符号(只存正数) 'default' => 0, // 默认库存0 'comment' => '库存数量', 'index' => true // 普通索引 ]) // 7. 枚举类型(固定可选值) ->addColumn('status', 'enum', [ 'values' => ['draft', 'active', 'disabled'], // 可选值 'default' => 'draft', // 默认草稿状态 'comment' => '产品状态(draft:草稿, active:上架, disabled:下架)' ]) // 8. 布尔类型(tinyint,默认true) ->addColumn('is_recommend', 'boolean', [ 'default' => true, // 默认true(1) 'comment' => '是否推荐(1:是, 0:否)' ]) // 9. 外键关联(整数,无符号,级联操作) ->addColumn('category_id', 'integer', [ 'unsigned' => true, 'null' => false, 'comment' => '所属分类ID', 'index' => true // 普通索引 ]) // 10. JSON类型(存储复杂结构,可空) ->addColumn('tags', 'json', [ 'null' => true, 'comment' => '产品标签(JSON格式,如["新品","热卖"])' ]) // 11. 时间戳类型(自动维护) ->addTimestamps() // 自动添加 create_time 和 update_time(datetime类型) // 12. 软删除字段(自动维护删除时间) ->addSoftDelete() // 添加 delete_time 字段(软删除标识) // 13. 整数类型(排序权重,默认0) ->addColumn('sort', 'integer', [ 'default' => 0, 'comment' => '排序权重(值越大越靠前)' ]) // 添加外键约束(关联分类表) ->addForeignKey('category_id', 'categories', 'id', [ 'delete' => 'CASCADE', // 分类删除时,关联产品也删除 'update' => 'CASCADE' // 分类ID更新时,产品关联ID同步更新 ]) // 添加联合索引(名称+状态) ->addIndex(['name', 'status'], [ 'name' => 'idx_name_status' // 自定义索引名 ]) // 执行创建表 ->create(); } /** * 回滚迁移(删除表) */ public function down() { // 先删除外键约束(避免删除表失败) $this->table('products')->dropForeignKey('category_id'); // 再删除表 $this->dropTable('products'); } }关键字段配置说明字段名类型/配置核心特点id自增整数、主键无符号,自增起始值1000sku字符串、唯一约束唯一标识,非空,长度50name字符串、默认值非空,默认空字符串,用于产品名称description文本类型、可空无长度限制,允许为空(适合长文本描述)price小数(10,2)高精度价格,默认0.00stock无符号整数、索引库存数量,非负,普通索引提升查询效率status枚举类型固定可选值,默认草稿状态is_recommend布尔类型本质tinyint,默认true(1)category_id外键、级联操作关联分类表,删除/更新时级联处理tagsJSON类型存储数组/对象结构,适合非结构化数据create_time时间戳(自动维护)由addTimestamps()生成,记录创建时间delete_time软删除字段由addSoftDelete()生成,非NULL表示已删除执行迁移# 创建表 php think migrate:run # 回滚(删除表) php think migrate:rollback注意事项外键依赖:示例中category_id关联categories表,需确保categories表已存在(可先创建分类表迁移)。索引优化:根据查询场景添加索引,避免过度索引影响写入性能。软删除:addSoftDelete()会自动添加delete_time字段(datetime类型,默认NULL),适合逻辑删除。枚举值:修改枚举值需谨慎,生产环境可能需要先添加新值再迁移数据。字段注释:每个字段添加清晰注释,便于后期维护(尤其团队协作场景)。通过这个示例,可以覆盖ThinkPHP8迁移工具中绝大多数字段配置场景,实际开发中可根据需求调整。
2025年07月26日
7 阅读
0 评论
0 点赞
2025-07-26
【PHP】ThinkPHP8 数据库迁移与数据填充完全指南
ThinkPHP8 数据库迁移与数据填充完全指南(官方文档补充版)一、安装与配置1. 安装迁移扩展composer require topthink/think-migration2. 配置文件说明配置文件位于 config/database.php默认迁移表名:think_migration支持多数据库连接3. 环境要求PHP >= 8.1ThinkPHP >= 8.0建议使用 InnoDB 引擎(支持事务)二、迁移文件操作1. 创建迁移文件# 创建基础迁移文件 php think migrate:create CreateUsersTable # 创建带表名的迁移文件(自动生成基础结构) php think migrate:create AddEmailToUsersTable --table=users # 指定数据库连接 php think migrate:create CreateLogsTable --connection=log2. 迁移文件结构生成的迁移文件位于 database/migrations,示例:<?php use think\migration\Migrator; use think\migration\db\Column; class CreateUsersTable extends Migrator { /** * 执行迁移(向上操作) */ public function up() { $table = $this->table('users'); $table->addColumn('name', 'string', ['limit' => 50]) ->addColumn('email', 'string', ['limit' => 100, 'unique' => true]) ->addColumn('password', 'string', ['limit' => 100]) ->addTimestamps() // 自动添加 create_time 和 update_time 字段 ->create(); } /** * 回滚迁移(向下操作) */ public function down() { $this->dropTable('users'); } }三、常用字段类型字段类型描述示例用法integer整数类型->addColumn('age', 'integer')biginteger大整数类型->addColumn('user_id', 'biginteger')string字符串类型->addColumn('name', 'string', ['limit' => 50])text长文本类型->addColumn('content', 'text')datetime日期时间类型->addColumn('created_at', 'datetime')timestamp时间戳类型->addColumn('updated_at', 'timestamp')boolean布尔类型->addColumn('status', 'boolean', ['default' => 0])decimal高精度小数->addColumn('price', 'decimal', ['precision' => 10, 'scale' => 2])enum枚举类型->addColumn('gender', 'enum', ['values' => ['male', 'female', 'other']])四、表结构操作1. 创建表public function up() { $table = $this->table('users', [ 'engine' => 'InnoDB', 'charset' => 'utf8mb4', 'comment' => '用户表', 'collation' => 'utf8mb4_unicode_ci' ]); $table->addColumn('id', 'integer', ['identity' => true]) // 自增ID(默认主键) ->addColumn('username', 'string', ['limit' => 30]) ->addColumn('email', 'string', ['limit' => 100, 'unique' => true]) ->addColumn('password', 'string', ['limit' => 100]) ->addColumn('status', 'boolean', ['default' => 1]) ->addIndex(['username']) // 普通索引 ->addUniqueIndex(['email']) // 唯一索引 ->addTimestamps() // 自动添加 create_time 和 update_time ->addSoftDelete() // 添加 delete_time 软删除字段 ->create(); }2. 修改表添加字段:public function up() { $this->table('users') ->addColumn('phone', 'string', ['limit' => 20, 'after' => 'email']) ->update(); }修改字段:public function up() { $this->table('users') ->changeColumn('phone', 'string', ['limit' => 11, 'default' => '']) ->update(); }删除字段:public function up() { $this->table('users') ->removeColumn('phone') ->update(); }3. 添加外键约束public function up() { $this->table('posts') ->addColumn('user_id', 'integer') ->addColumn('title', 'string') ->addColumn('content', 'text') ->addForeignKey('user_id', 'users', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) ->update(); }五、执行迁移命令1. 执行所有未迁移的文件php think migrate:run2. 回滚上一次迁移php think migrate:rollback3. 回滚到指定版本php think migrate:rollback --target=20250726101500 # 指定时间戳4. 重置所有迁移(先回滚再执行)php think migrate:reset5. 刷新数据库(重置并重新执行所有迁移)php think migrate:refresh6. 查看迁移状态php think migrate:status7. 指定数据库连接php think migrate:run --connection=db_log # 指定日志数据库六、数据填充操作1. 创建数据填充文件php think seed:create Users # 创建Users表的数据填充器2. 编写数据填充逻辑<?php use think\migration\Seeder; use think\facade\Db; class Users extends Seeder { /** * 填充数据 */ public function run() { $data = []; for ($i = 1; $i <= 10; $i++) { $data[] = [ 'username' => 'user' . $i, 'email' => 'user' . $i . '@example.com', 'password' => password_hash('123456', PASSWORD_DEFAULT), 'create_time' => date('Y-m-d H:i:s'), 'update_time' => date('Y-m-d H:i:s') ]; } // 使用批量插入提高性能 Db::name('users')->insertAll($data); } }3. 执行数据填充php think seed:run # 执行所有填充器 php think seed:run --seeder Users # 执行指定填充器七、高级技巧1. 使用 change() 方法(简化双向操作)public function change() { $table = $this->table('users'); // 创建表 $table->addColumn('username', 'string') ->addColumn('email', 'string') ->create(); // 修改表 $table->addColumn('phone', 'string') ->update(); }2. 使用事务public function up() { $this->getAdapter()->beginTransaction(); try { $this->table('table1')->addColumn(...)->create(); $this->table('table2')->addColumn(...)->create(); $this->getAdapter()->commit(); } catch (\Exception $e) { $this->getAdapter()->rollBack(); throw $e; } }3. 创建数据库快照php think migrate:snapshot # 创建当前数据库结构快照4. 指定迁移文件路径php think migrate:run --path=database/migrations/custom # 指定自定义路径八、最佳实践1. 命名规范迁移文件:YYYYMMDDHHMMSS_表名_操作.php(自动生成)表名:使用小写字母和下划线(如 user_info)字段名:使用小写字母和下划线(如 create_time)2. 避免复杂SQL单个迁移文件只做单一变更避免在迁移中执行数据迁移操作3. 测试迁移在开发环境充分测试使用测试数据库验证回滚功能4. 生产环境注意事项迁移前备份数据库使用 --pretend 参数预览变更避免在高峰期执行大型迁移5. 团队协作迁移文件提交到版本控制系统避免多人同时修改同一迁移文件拉取代码后先执行 php think migrate:run九、常见问题与解决方案1. 迁移文件冲突问题:多个迁移文件时间戳相近导致执行顺序异常解决方案:使用 --timestamp 参数手动指定时间戳php think migrate:create NewTable --timestamp=202507261530002. 外键约束错误问题:删除表时外键约束阻止操作解决方案:在 down() 方法中先删除外键public function down() { $this->table('posts') ->dropForeignKey('user_id') ->update(); $this->dropTable('posts'); }3. 数据填充重复问题问题:多次执行填充器导致数据重复解决方案:在填充前清空表public function run() { Db::name('users')->delete(true); // 清空表 // 填充新数据 }4. 迁移性能问题问题:大型表迁移缓慢解决方案:分批次执行数据迁移使用数据库原生工具导入大型数据避免在迁移中使用复杂查询
2025年07月26日
8 阅读
0 评论
0 点赞
2025-07-16
【Nginx】Nginx需具体配置示例(涵盖了复杂场景下的高级应用)
以下是 Nginx 常见场景的具体配置示例,配置文件可存放在 /etc/nginx/conf.d/ 目录下(Linux)或直接修改 nginx.conf(Windows):一、基础配置:静态网站托管配置文件(/etc/nginx/conf.d/mysite.conf)server { listen 80; server_name example.com www.example.com; # 替换为你的域名 # 网站根目录 root /var/www/html; index index.html index.htm; # 日志配置 access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; # 静态文件缓存 location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 30d; # 静态资源缓存30天 } # 错误页面 error_page 404 /404.html; error_page 500 502 503 504 /50x.html; }操作步骤创建网站目录:sudo mkdir -p /var/www/html添加测试文件:echo "Hello, Nginx!" > /var/www/html/index.html验证配置:nginx -t重载配置:nginx -s reload二、反向代理:转发到后端服务场景1:代理到本地 Node.js 应用(端口3000)server { listen 80; server_name api.example.com; location / { proxy_pass http://localhost:3000; # 后端服务地址 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; # 超时设置 proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } }场景2:代理到多个后端服务器(负载均衡)# 定义后端服务器组 upstream backend_servers { server backend1.example.com weight=5; # 权重5 server backend2.example.com weight=3; # 权重3 server backend3.example.com backup; # 备份服务器 } server { listen 80; server_name example.com; location / { proxy_pass http://backend_servers; # 其他 proxy_set_header 配置同上 } }三、HTTPS 配置(使用 Let's Encrypt 证书)前提条件已安装 Certbot:sudo apt install certbot python3-certbot-nginx域名已解析到服务器 IP自动配置(推荐)sudo certbot --nginx -d example.com -d www.example.comCertbot 会自动修改 Nginx 配置并设置证书自动更新。手动配置示例server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; # 强制 HTTPS } server { listen 443 ssl http2; server_name example.com www.example.com; # 证书路径(Let's Encrypt 默认路径) ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # SSL 优化配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 其他配置(如 root、location 等)同 HTTP 配置 }四、URL 重写与跳转示例1:www 到非 www 跳转server { listen 80; server_name www.example.com; return 301 https://example.com$request_uri; }示例2:路径重写location /old-path/ { rewrite ^/old-path/(.*)$ /new-path/$1 permanent; # 301 永久重定向 }示例3:基于参数跳转if ($args ~* "id=([0-9]+)") { return 301 https://example.com/item/$1; }五、限流配置(防爬/防刷)限制单个 IP 的请求频率# http 块中定义限流规则 http { limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s; server { listen 80; server_name example.com; location /api/ { limit_req zone=mylimit burst=20 nodelay; # 每秒最多10个请求,突发最多20个,超出的请求直接返回503 } } }六、WebSocket 代理支持配置示例server { listen 80; server_name ws.example.com; location /ws/ { proxy_pass http://backend_websocket_server; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; # 防止 WebSocket 连接超时 proxy_read_timeout 86400; } }七、多站点配置(虚拟主机)在同一服务器托管多个域名# 站点1 server { listen 80; server_name site1.com www.site1.com; root /var/www/site1; } # 站点2 server { listen 80; server_name site2.com www.site2.com; root /var/www/site2; }八、静态文件优化配置配置 Gzip 压缩http { gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_buffers 16 8k; gzip_http_version 1.1; }九、常见问题处理配置1. 解决跨域请求(CORS)location /api/ { add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always; # OPTIONS 请求直接返回 204 if ($request_method = 'OPTIONS') { return 204; } }2. 隐藏 Nginx 版本信息http { server_tokens off; # 隐藏响应头中的 Nginx 版本 }配置验证与重载每次修改配置后,务必执行:nginx -t # 验证配置语法 nginx -s reload # 重载配置(不中断服务)以上配置可根据实际需求组合使用,例如同时配置 HTTPS、反向代理和限流。如需更复杂的场景(如 A/B 测试、灰度发布),可进一步扩展配置。以下是 Nginx 更高级的扩展配置示例,适用于复杂场景:十、A/B 测试与灰度发布基于 Cookie 的流量分发http { # 定义变量存储分流规则 map $cookie_ab_test $backend { default backend_a; "b" backend_b; } upstream backend_a { server backend1.example.com; } upstream backend_b { server backend2.example.com; } server { listen 80; server_name example.com; # 首次访问设置 Cookie if ($cookie_ab_test = "") { set $ab_test $random % 2; # 随机分为两组 if ($ab_test = 0) { add_header Set-Cookie "ab_test=a; path=/; domain=.example.com"; } if ($ab_test = 1) { add_header Set-Cookie "ab_test=b; path=/; domain=.example.com"; } } location / { proxy_pass http://$backend; } } }十一、基于用户区域的流量路由根据 IP 地理位置分发请求# 需要安装 ngx_http_geoip2_module 模块 http { geoip2 /path/to/GeoLite2-Country.mmdb { $geoip2_data_country_code country iso_code; } map $geoip2_data_country_code $backend { default backend_global; "CN" backend_china; "US" backend_usa; } upstream backend_global { server global.example.com; } upstream backend_china { server china.example.com; } upstream backend_usa { server usa.example.com; } server { listen 80; server_name example.com; location / { proxy_pass http://$backend; } } }十二、内容缓存配置配置代理缓存(如缓存 API 响应)http { # 定义缓存参数 proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off; server { listen 80; server_name api.example.com; location /static-api/ { proxy_pass http://backend_api; proxy_cache my_cache; proxy_cache_key "$scheme$request_method$host$request_uri"; proxy_cache_valid 200 302 12h; # 成功响应缓存12小时 proxy_cache_valid 404 1m; # 404缓存1分钟 # 缓存命中时添加响应头,便于调试 add_header X-Cache $upstream_cache_status; } } }十三、健康检查与自动剔除故障节点配置主动健康检查(需 nginx-plus 或 lua 模块)http { upstream backend_servers { server backend1.example.com max_fails=3 fail_timeout=30s; server backend2.example.com max_fails=3 fail_timeout=30s; # 健康检查(Nginx Plus 功能) zone backend 64k; health_check interval=5s fails=3 passes=2 uri=/health; } server { listen 80; server_name example.com; location / { proxy_pass http://backend_servers; } } }使用 Lua 模块实现健康检查(开源版)# 需要安装 ngx_lua 模块 http { lua_shared_dict healthcheck 1m; init_by_lua_block { local hc = require "resty.healthcheck" local checker = hc.new({ name = "backend_checker", shm_name = "healthcheck", type = "http", http_req = "GET /health HTTP/1.0\r\nHost: backend\r\n\r\n", interval = 2000, # 2秒检查一次 timeout = 1000, # 超时时间1秒 fall = 3, # 连续3次失败标记为不可用 rise = 2, # 连续2次成功标记为可用 }) -- 添加后端服务器 checker:add_target("backend1.example.com", nil, 80, true) checker:add_target("backend2.example.com", nil, 80, true) -- 启动后台检查 local ok, err = checker:start() if not ok then ngx.log(ngx.ERR, "failed to start health checker: ", err) return end } upstream backend_servers { server backend1.example.com; server backend2.example.com; # 使用 Lua 脚本判断服务器是否健康 server 0.0.0.1; # 防止 upstream 为空 balancer_by_lua_block { local hc = require "resty.healthcheck" local checker = hc.new({ name = "backend_checker", shm_name = "healthcheck", type = "http", }) local ok, err = checker:get_reachable() if not ok then ngx.log(ngx.ERR, "no reachable backend server: ", err) return ngx.exit(500) end -- 设置要代理的服务器 local host, port = ok.host, ok.port local ok, err = ngx.balancer.set_current_peer(host, port) if not ok then ngx.log(ngx.ERR, "failed to set current peer: ", err) return ngx.exit(500) end } } }十四、请求限流与熔断基于漏桶算法的限流http { # 定义漏桶限流规则 limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s; # 每秒5个请求 server { listen 80; server_name api.example.com; location /api/ { limit_req zone=api_limit burst=10 nodelay; # 突发10个请求 # 超过限流时返回自定义错误页面 error_page 503 @api_blocked; } location @api_blocked { default_type application/json; return 503 '{"error":"Too many requests","code":429}'; } } }熔断机制(当后端服务不可用时)upstream backend_servers { server backend1.example.com; server backend2.example.com; # 当后端服务器返回500、502、503、504时,将请求转发到其他服务器 proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; }十五、WebSocket 集群与会话保持基于 IP Hash 的会话保持upstream websocket_backend { ip_hash; # 相同IP的请求始终转发到同一后端服务器 server backend1.example.com:8080; server backend2.example.com:8080; } server { listen 80; server_name ws.example.com; location /ws/ { proxy_pass http://websocket_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; # 保持长连接 proxy_read_timeout 3600s; } }十六、自定义日志格式与分析JSON 格式日志http { log_format json_log '{ "timestamp": "$time_iso8601", "remote_addr": "$remote_addr", "host": "$host", "request": "$request", "status": $status, "body_bytes_sent": $body_bytes_sent, "request_time": $request_time, "upstream_response_time": "$upstream_response_time", "http_referer": "$http_referer", "http_user_agent": "$http_user_agent" }'; server { listen 80; server_name example.com; access_log /var/log/nginx/access_json.log json_log; } }十七、HTTP/2 与 TLS 优化配置HTTP/2 与 OCSP 装订server { listen 443 ssl http2; server_name example.com; # 证书配置 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # 启用 OCSP 装订 ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; # TLS 优化 ssl_protocols TLSv1.3 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"; # HTTP/2 优化 http2_push_preload on; # 启用 HTTP/2 预推送 }十八、自定义错误页面配置全局错误页面http { # 定义错误页面路径 error_page 400 /errors/400.html; error_page 401 /errors/401.html; error_page 403 /errors/403.html; error_page 404 /errors/404.html; error_page 500 502 503 504 /errors/50x.html; server { listen 80; server_name example.com; location /errors/ { internal; # 只允许内部重定向访问 root /var/www/html; } } }十九、Nginx 与 Lua 脚本集成示例:请求签名验证# 需要安装 ngx_lua 模块 server { listen 80; server_name api.example.com; location /api/ { # 验证请求签名 access_by_lua_block { local secret_key = "your_secret_key" local request_time = ngx.var.arg_timestamp local signature = ngx.var.arg_signature # 检查时间戳是否过期(防止重放攻击) local current_time = os.time() if not request_time or math.abs(current_time - tonumber(request_time)) > 300 then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say('{"error":"Invalid timestamp"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end # 验证签名 local args = ngx.req.get_uri_args() local sorted_keys = {} for k, _ in pairs(args) do if k ~= "sign" then table.insert(sorted_keys, k) end end table.sort(sorted_keys) local sign_str = "" for _, k in ipairs(sorted_keys) do sign_str = sign_str .. k .. "=" .. args[k] .. "&" end sign_str = sign_str .. "key=" .. secret_key local resty_sha1 = require "resty.sha1" local str = require "resty.string" local sha1 = resty_sha1:new() sha1:update(sign_str) local digest = sha1:final() local calculated_sign = str.to_hex(digest) if calculated_sign ~= signature then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say('{"error":"Invalid signature"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end } proxy_pass http://backend_api; } }二十、基于 JWT 的认证JWT 验证示例# 需要安装 lua-resty-jwt 模块 server { listen 80; server_name auth.example.com; location /api/ { access_by_lua_block { local jwt = require "resty.jwt" local secret = "your_jwt_secret" -- 从请求头中获取 JWT local auth_header = ngx.var.http_Authorization if not auth_header then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say('{"error":"Authorization header missing"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 提取 token local _, _, token = string.find(auth_header, "Bearer%s+(.+)") if not token then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say('{"error":"Invalid token format"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 验证 JWT local jwt_obj = jwt:verify(secret, token) if not jwt_obj.verified then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say('{"error":"' .. jwt_obj.reason .. '"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 将用户信息传递给后端 ngx.req.set_header("X-User-ID", jwt_obj.payload.user_id) ngx.req.set_header("X-User-Role", jwt_obj.payload.role) } proxy_pass http://backend_api; } }配置验证与重载每次修改配置后,务必执行:nginx -t # 验证配置语法 nginx -s reload # 重载配置(不中断服务)以上配置涵盖了复杂场景下的高级应用,可根据实际需求组合使用。对于生产环境,建议先在测试环境验证配置,确保稳定性。
2025年07月16日
4 阅读
0 评论
0 点赞
2025-07-16
【Nginx】Nginx安装与运行
以下是在不同操作系统上安装和运行 Nginx 的详细步骤:一、Ubuntu/Debian 系统1. 安装 Nginx# 更新包索引 sudo apt update # 安装 Nginx sudo apt install nginx # 验证安装 nginx -v # 输出类似: nginx version: nginx/1.18.02. 控制 Nginx 服务# 启动服务 sudo systemctl start nginx # 设置开机自启 sudo systemctl enable nginx # 检查服务状态 sudo systemctl status nginx # 停止服务 sudo systemctl stop nginx # 重启服务(修改配置后常用) sudo systemctl restart nginx # 重新加载配置(不中断现有连接) sudo systemctl reload nginx3. 防火墙配置# 允许 HTTP 和 HTTPS 流量 sudo ufw allow 'Nginx HTTP' # 仅允许 80 端口 sudo ufw allow 'Nginx HTTPS' # 仅允许 443 端口 sudo ufw allow 'Nginx Full' # 同时允许 80 和 443 端口二、CentOS/RHEL 系统1. 安装 Nginx# 添加 Nginx 官方仓库 sudo tee /etc/yum.repos.d/nginx.repo <<EOF [nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/\$releasever/\$basearch/ gpgcheck=0 enabled=1 EOF # 安装 Nginx sudo yum install nginx # 验证安装 nginx -v2. 控制 Nginx 服务# 启动服务 sudo systemctl start nginx # 设置开机自启 sudo systemctl enable nginx # 其他命令(与 Ubuntu 相同) sudo systemctl {stop|restart|reload|status} nginx3. 防火墙配置# 允许 HTTP 和 HTTPS 流量 sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload三、macOS(使用 Homebrew)1. 安装 Homebrew(如未安装)/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"2. 安装和管理 Nginx# 安装 Nginx brew install nginx # 启动服务 brew services start nginx # 后台运行并开机自启 # 或临时启动(不设开机自启) nginx # 停止服务 brew services stop nginx # 或直接杀进程 killall nginx # 配置文件位置 /usr/local/etc/nginx/nginx.conf # 访问测试 http://localhost:8080 # macOS 默认监听 8080 端口四、Windows 系统1. 下载与安装访问 Nginx 官网下载页面,下载 Windows 版本(.zip 文件)。解压到指定目录(如 C:\nginx)。2. 控制 Nginx# 进入 Nginx 目录 cd C:\nginx # 启动 Nginx start nginx.exe # 停止 Nginx nginx.exe -s stop # 快速停止 # 或 nginx.exe -s quit # 优雅停止 # 重新加载配置 nginx.exe -s reload # 验证配置文件语法 nginx.exe -t3. 访问测试打开浏览器访问 http://localhost,若看到 "Welcome to nginx!" 页面,则安装成功。五、验证安装是否成功浏览器访问: 打开浏览器,输入 http://服务器IP地址(或 http://localhost),若看到 Nginx 欢迎页面,则安装成功。检查进程:ps -ef | grep nginx # 应看到 master 和 worker 进程端口监听:netstat -tulpn | grep :80 # 检查 80 端口是否被监听六、配置文件位置Linux:/etc/nginx/nginx.conf(主配置),/etc/nginx/conf.d/(站点配置)macOS(Homebrew):/usr/local/etc/nginx/nginx.confWindows:C:\nginx\conf\nginx.conf七、常见问题端口冲突:若 80 端口被占用,修改 nginx.conf 中的 listen 指令(如改为 8080)。权限问题:Linux 下若无法监听 80/443 端口,可使用 sudo 或调整 SELinux/AppArmor 配置。配置语法错误:修改配置后,使用 nginx -t 检查语法,再 nginx -s reload 重新加载。八、下一步配置虚拟主机:创建多个网站配置文件(.conf)放在 /etc/nginx/conf.d/ 目录下。配置 HTTPS:使用 Let's Encrypt 免费证书实现 HTTPS。负载均衡:配置 upstream 模块将请求分发到多个后端服务器。
2025年07月16日
9 阅读
0 评论
0 点赞
2025-07-16
【Nginx】什么是反向代理?
反向代理(Reverse Proxy)是一种服务器端代理技术,用于接收客户端的请求并将其转发到后端的多个目标服务器,然后将目标服务器的响应返回给客户端。与正向代理(如VPN)不同,反向代理对客户端是透明的,客户端无需感知代理服务器的存在,直接与反向代理服务器通信。核心概念与原理工作流程客户端向反向代理服务器(如Nginx、Apache)发送请求(如访问https://example.com)。反向代理服务器根据配置规则(如URL路径、请求头)将请求转发给后端的目标服务器(如Tomcat、Node.js应用)。目标服务器处理请求并返回响应给反向代理服务器。反向代理服务器将响应返回给客户端,客户端认为直接与目标服务器通信。关键特点隐藏后端服务器:客户端只能看到反向代理服务器的IP地址,无法直接访问后端服务器,增强安全性。统一入口:反向代理作为所有请求的唯一入口,简化了客户端与后端的交互。负载均衡:可将请求分发到多个后端服务器,平衡负载,提高可用性。反向代理的主要作用安全防护屏蔽后端服务器的真实IP,防止外部直接攻击(如DDOS、端口扫描)。集中进行访问控制(如IP黑名单、请求过滤)和安全加固(如WAF功能)。负载均衡将请求按策略(如轮询、IP哈希、响应时间)分发到多个后端服务器,避免单点故障,提升系统吞吐量。结合健康检查机制,自动剔除故障节点,保证服务可用性。性能优化缓存静态内容:缓存HTML、CSS、JS、图片等静态资源,减少后端服务器压力。压缩与优化:对响应内容进行Gzip压缩、HTTP/2升级等,加速数据传输。SSL/TLS卸载:由反向代理统一处理HTTPS加密和解密,减轻后端服务器计算负担。流量控制与路由根据请求特征(如URL、用户区域)将流量导向不同的后端服务(如A/B测试、灰度发布)。实现跨域请求(CORS)处理、请求限流(如限制IP访问频率)等。协议转换与兼容性将HTTP请求转换为其他协议(如WebSocket、gRPC),适配不同后端服务。处理不同API版本的路由,支持向后兼容。典型应用场景大型网站与应用如Google、Facebook等通过反向代理分发全球流量,隐藏内部架构。微服务架构作为API网关,统一管理微服务的访问,实现认证、限流、日志等公共逻辑。容器化与云原生在Kubernetes集群中,通过Ingress Controller(基于Nginx、Traefik等)实现服务暴露和流量路由。企业内网应用将内部系统(如OA、CRM)通过反向代理暴露到公网,同时保证安全性。对比正向代理与反向代理特性正向代理反向代理代理对象代理客户端(用户)代理服务器(服务端)客户端感知需要手动配置(如浏览器代理)透明,客户端无感知主要用途突破限制、隐藏客户端IP负载均衡、安全防护、性能优化典型场景VPN、翻墙工具Nginx、Apache、CDN节点技术选型常见的反向代理软件包括:Nginx:高性能、轻量,适合高并发场景,支持HTTP/2、WebSocket等。Apache HTTP Server:功能全面,适合复杂配置和模块化扩展。HAProxy:专注于负载均衡,支持TCP和HTTP协议。Traefik:专为微服务和容器环境设计,自动发现后端服务。Envoy:云原生架构中的高性能代理,支持动态配置和可观测性。总结反向代理是现代分布式系统的核心组件,通过隐藏后端架构、负载均衡、安全防护和性能优化,显著提升了系统的可用性、可扩展性和安全性。无论是企业级应用还是互联网产品,反向代理都是构建高并发、高可靠服务的关键技术之一。
2025年07月16日
3 阅读
0 评论
0 点赞
2025-05-22
【MySQL】查询百万数据导出处理方法(数据集合统一返回)
public function getExportList($map) { // 定义初始分页大小和最小分页大小 $initialPageSize = 1000; $minPageSize = 100; $pageSize = $initialPageSize; // 计算可用内存(保留15%缓冲) $memoryLimit = (int) ini_get('memory_limit') * 0.85; $lastId = 0; $allData = []; $hasMoreData = true; // 用于动态调整分页大小的变量 $lastBatchMemoryUsage = 0; $avgRecordSize = 0; while ($hasMoreData) { // 动态调整分页大小 if ($lastBatchMemoryUsage > 0 && $avgRecordSize > 0) { $currentMemory = memory_get_usage(true) / (1024 * 1024); $availableMemory = $memoryLimit - $currentMemory; // 基于剩余内存和记录大小计算安全分页大小 $safePageSize = max( $minPageSize, min( $initialPageSize, (int)($availableMemory / ($avgRecordSize * 1.5)) // 1.5倍安全系数 ) ); $pageSize = ($safePageSize < $pageSize) ? $safePageSize : $pageSize; } // 执行分页查询 $list = self::where($map) ->withoutField(['z_create_time', 'z_update_time']) ->where('z_id', '>', $lastId) ->order('z_id', 'asc') ->limit($pageSize) ->select(); if ($list->isEmpty()) { break; } $listData = $list->toArray(); $batchCount = count($listData); // 计算当前批次内存占用 $batchMemory = memory_get_usage(true); foreach ($listData as $record) { $allData[] = $record; // 直接添加减少内存操作 } $batchMemory = (memory_get_usage(true) - $batchMemory) / (1024 * 1024); // 更新内存跟踪指标 if ($batchCount > 0) { $currentAvg = $batchMemory / $batchCount; $avgRecordSize = ($avgRecordSize > 0) ? ($avgRecordSize * 0.7 + $currentAvg * 0.3) // 平滑平均 : $currentAvg; $lastBatchMemoryUsage = $batchMemory; } // 更新迭代变量 $hasMoreData = ($batchCount === $pageSize); if ($hasMoreData) { $lastRecord = end($listData); $lastId = (int)$lastRecord['z_id']; reset($listData); // 重置数组指针 } // 资源清理 unset($list, $listData); if (gc_enabled()) { gc_collect_cycles(); } } return $allData; } //导出14730条数据,需要34.05秒,字段有50个,全是非空状态,字段数量会影响响应时间,一般导出起飞无压力 public function getExportList($map) { $pageSize = 1000; $lastId = 0; $allData = []; while (true) { // 查询当前批次 $list = self::where($map) ->withoutField(['z_create_time', 'z_update_time']) ->where('z_id', '>', $lastId) ->order('z_id', 'asc') ->limit($pageSize) ->select(); // 无数据时终止循环 if ($list->isEmpty()) { break; } $batch = $list->toArray(); $batchSize = count($batch); // 直接追加数据(避免array_merge内存峰值) foreach ($batch as $record) { $allData[] = $record; } // 到达数据末尾 if ($batchSize < $pageSize) { break; } // 更新最后ID $lastId = (int)end($batch)['z_id']; // 资源清理 unset($list, $batch); if ($batchSize >= 500) { // 仅在大批次后回收 gc_collect_cycles(); } } return $allData; } //这个是34.59秒。其他有很多方法,正在尝试30秒内
2025年05月22日
13 阅读
0 评论
1 点赞
2025-05-17
【ThinkPHP】创建有背景的微信小程序二维码
<?php namespace app\common\lib\wechat; use think\Exception; use think\facade\Cache; use think\facade\Request; use think\Image; use think\facade\Db; class QrCode extends Basic { // 小程序页面路径 private string $path = "pages/myCenter/invite"; // 小程序码保存路径 private string $savePath = "./storage/qrcode/"; // 二维码参数 private string $param = ""; public function __construct(string $path = "", string $field = "", string $param = "", int $width = 430) { if (!empty($path) && empty($field)) { $this->path = $path; } if (!empty($path) && !empty($field) && !empty($param)) { $this->path = "{$path}?{$field}={$param}"; } $this->param = $param; } /** * 生成小程序码并可选择合并背景图 * * @param string $backgroundPath 背景图路径 * @param int $qrWidth 二维码在背景图上的宽度位置 * @param int $qrHeight 二维码在背景图上的高度位置 * @return string 生成后的二维码URL */ public function setQrcode(string $backgroundPath = '', int $qrWidth = 160, int $qrHeight = 530): string { try { // 获取访问令牌 $accessToken = Cache::get('accesstoken'); if (empty($accessToken)) { $accessToken = (new AccessToken())->getAccesToken(); Cache::set('accesstoken', $accessToken, 7200); // 假设token有效期为2小时 } // 请求小程序码 $url = "https://api.weixin.qq.com/wxa/getwxacode?access_token={$accessToken}"; $data = [ 'path' => $this->path, 'scene' => 'type=qrcode', 'width' => 430, ]; $result = $this->curlPost($url, $data, 'POST'); if ($result === false || isset($result['errcode'])) { throw new Exception("Failed to get QR code: " . json_encode($result)); } // 保存二维码到文件 $fileName = md5($this->param); $directory = $this->savePath . date('Ymd') . '/'; if (!$this->createDirectory($directory)) { throw new Exception("目录创建失败: {$directory}"); } $filePath = "{$directory}{$fileName}.png"; if (!file_put_contents($filePath, $result)) { throw new Exception("文件写入失败: {$filePath}"); } // 生成完整URL $fullDomain = (new \app\common\lib\data\Str())->getFullDomain(); $qrcodeUrl = $fullDomain . ltrim($filePath, '.'); // 合并背景图(如果提供) if (!empty($backgroundPath)) { return $this->mergeWithBackground($backgroundPath, $filePath, $qrcodeUrl, $qrWidth, $qrHeight); } return $qrcodeUrl; } catch (Exception $e) { // 记录错误日志 error_log($e->getMessage()); return ''; } } /** * 将二维码与背景图合并 */ private function mergeWithBackground(string $backgroundPath, string $qrcodePath, string $defaultUrl, int $qrWidth, int $qrHeight): string { try { $fileName = md5(uniqid() . time()); $newImagePath = $this->savePath . date('Ymd') . "/{$fileName}.png"; $background = Image::open($backgroundPath); $background->water($qrcodePath, [$qrWidth, $qrHeight])->save($newImagePath); $imageInfo = [ 'url' => Request::domain() . substr($newImagePath, 1), 'size' => filesize($newImagePath), 'name' => $fileName, 'mime' => mime_content_type($newImagePath), 'ext' => 'png', ]; $fileData = [ 'f_uuid' => setUUID(), 'f_file' => $imageInfo['url'], 'f_location' => 0, 'f_type' => 'image', 'f_info' => serialize($imageInfo), 'f_user_uuid' => $this->request->index_user_uuid ?? '', 'f_create_time' => time(), ]; Db::name('File')->insert($fileData); return $imageInfo['url']; } catch (Exception $e) { // 记录错误日志 error_log($e->getMessage()); return $defaultUrl; } } /** * 递归创建目录 */ private function createDirectory(string $path, int $mode = 0777, bool $recursive = true): bool { if (is_dir($path)) { return true; } if (mkdir($path, $mode, $recursive)) { chmod($path, $mode); return true; } if (!is_dir(dirname($path))) { if ($this->createDirectory(dirname($path), $mode, $recursive)) { return $this->createDirectory($path, $mode, $recursive); } } return false; } /** * 发送HTTP POST请求 */ // public function curlPost($url, $data, $method = "POST") // { // $ch = curl_init(); //1.初始化 // curl_setopt($ch, CURLOPT_URL, $url); //2.请求地址 // curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);//3.请求方式 // //4.参数如下 // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//https // curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); // curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');//模拟浏览器 // curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // curl_setopt($ch, CURLOPT_AUTOREFERER, 1); // curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept-Encoding: gzip, deflate'));//gzip解压内容 // curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); // // if ($method == "POST") {//5.post方式的时候添加数据 // $data = json_encode($data); // curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // } // curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // $tmpInfo = curl_exec($ch);//6.执行 // // if (curl_errno($ch)) {//7.如果出错 // return curl_error($ch); // } // curl_close($ch);//8.关闭 // return $tmpInfo; // } }
2025年05月17日
20 阅读
0 评论
0 点赞
2025-05-13
【PHP】PHPExcel 使用手册
PHPExcel 是一个用于操作 Microsoft Excel 格式文件的 PHP 库,它支持读取和写入多种格式(如 .xls, .xlsx, .csv 等)。不过需要注意,PHPExcel 项目已停止维护,推荐使用其继任者 PhpSpreadsheet。以下是 PHPExcel 的基本使用指南:1. 安装 PHPExcel# 使用 Composer 安装(推荐) composer require phpoffice/phpexcel2. 读取 Excel 文件require 'vendor/autoload.php'; use PHPExcel_IOFactory; // 读取文件 $inputFileName = 'example.xlsx'; $objPHPExcel = PHPExcel_IOFactory::load($inputFileName); // 获取第一个工作表 $sheet = $objPHPExcel->getActiveSheet(); // 获取最高行和最高列 $highestRow = $sheet->getHighestRow(); $highestColumn = $sheet->getHighestColumn(); // 遍历数据 for ($row = 1; $row <= $highestRow; $row++) { for ($col = 'A'; $col <= $highestColumn; $col++) { $cellValue = $sheet->getCell($col.$row)->getValue(); echo "Row $row, Column $col: $cellValue\n"; } }3. 创建新的 Excel 文件require 'vendor/autoload.php'; use PHPExcel; use PHPExcel_IOFactory; // 创建新的 PHPExcel 对象 $objPHPExcel = new PHPExcel(); // 设置文档属性 $objPHPExcel->getProperties() ->setCreator("Your Name") ->setTitle("Sample Excel File"); // 获取活动工作表 $sheet = $objPHPExcel->getActiveSheet(); // 设置单元格值 $sheet->setCellValue('A1', 'Hello'); $sheet->setCellValue('B1', 'World!'); $sheet->setCellValue('A2', 'This is a sample spreadsheet.'); // 保存为 .xlsx 文件 $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); $objWriter->save('new_file.xlsx');4. 设置样式// 设置字体样式 $sheet->getStyle('A1')->getFont() ->setName('Arial') ->setSize(14) ->setBold(true) ->setColor(new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_RED)); // 设置单元格背景色 $sheet->getStyle('A1')->getFill() ->setFillType(PHPExcel_Style_Fill::FILL_SOLID) ->getStartColor()->setARGB('FFEEEEEE'); // 设置对齐方式 $sheet->getStyle('A1')->getAlignment() ->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER) ->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER); // 设置列宽 $sheet->getColumnDimension('A')->setWidth(20);5. 合并单元格// 合并 A1 到 B1 的单元格 $sheet->mergeCells('A1:B1'); // 取消合并 $sheet->unmergeCells('A1:B1');6. 处理日期格式// 设置日期值(PHPExcel 使用 Excel 时间戳格式) $dateTime = new DateTime(); $sheet->setCellValue('A1', $dateTime->format('Y-m-d H:i:s')); // 设置单元格格式为日期 $sheet->getStyle('A1')->getNumberFormat() ->setFormatCode('yyyy-mm-dd hh:mm:ss');7. 保存为不同格式// 保存为 .xlsx 格式 $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); $objWriter->save('file.xlsx'); // 保存为 .xls 格式 $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); $objWriter->save('file.xls'); // 保存为 CSV 格式 $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'CSV'); $objWriter->save('file.csv');8. 从浏览器下载文件// 设置响应头 header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="download.xlsx"'); header('Cache-Control: max-age=0'); // 输出到浏览器 $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); $objWriter->save('php://output'); exit;注意事项性能问题:处理大量数据时,PHPExcel 可能占用较多内存。考虑使用 PHPExcel_CachedObjectStorageFactory 进行优化。替代方案:推荐使用 PhpSpreadsheet,它是 PHPExcel 的官方继任者,修复了许多问题并提供更好的性能。内存优化:// 设置为只读模式以减少内存消耗 $objReader = PHPExcel_IOFactory::createReader('Excel2007'); $objReader->setReadDataOnly(true); $objPHPExcel = $objReader->load('large_file.xlsx');如需更详细的文档,请参考 PHPExcel 官方文档(注意:项目已停止维护)。
2025年05月13日
17 阅读
0 评论
0 点赞
2025-05-08
毕业设计,程序启航 —— 为大学生打造专属编程毕业设计服务
前言大学尾声,毕业设计是每位学子迈向专业领域的重要里程碑。对于计算机相关专业的学生而言,编程毕业设计更是展现技术实力、创新思维与实践能力的核心舞台。然而,面对从网页设计到前后端开发等复杂多样的项目需求,许多同学往往感到无从下手。别担心!我们提供一站式编程毕业设计服务,助你轻松开启毕业设计之旅,为未来职业生涯筑牢根基。一、网页设计:打造数字世界的视觉盛宴网页是互联网时代的关键窗口,优质的网页设计既能吸引用户,又能高效传递信息、彰显创意。我们深谙网页设计的重要性,在毕业设计中为你提供全方位服务:创意规划定制:基于你的毕业设计主题,我们将与你深度沟通,挖掘独特创意点。无论是企业官网、个人作品展示,还是特定功能专题页面,我们都会量身定制整体规划。从页面布局、色彩搭配,到导航结构、交互元素,每个细节都贴合设计需求,兼顾用户体验与视觉美感。专业视觉塑造:专业设计团队运用前沿理念与工具,精心雕琢网页视觉效果。从高清图片处理、精美图标设计,到流畅动画效果、和谐色彩搭配,力求让网页在视觉上脱颖而出,为毕业设计增添亮眼亮点。响应式完美适配:在多设备浏览的当下,响应式设计至关重要。我们确保网页在桌面电脑、笔记本、平板电脑及手机等各类屏幕尺寸上,都能呈现一致且优质的浏览体验,体现你对技术细节的全面把控,提升毕业设计专业性。二、前端代码编写:构建交互流畅的用户界面前端代码是实现网页功能与交互的关键,直接影响用户体验。我们的前端开发服务聚焦于打造高效、稳定且具创意的前端代码:精准技术选型:依据毕业设计项目需求,选择 HTML5、CSS3、JavaScript 等适配的前端技术栈,并结合 Vue.js、React.js 等流行框架,快速搭建稳固架构。这些框架可提升开发效率,保障代码的可维护性与扩展性,增强毕业设计技术竞争力。交互功能实现:我们以用户体验为核心,精心设计并实现各类交互功能。无论是表单验证、动态数据展示,还是拖拽、滑动、折叠等复杂交互效果,都能通过精湛代码实现流畅自然的交互体验,让网页从静态展示升级为智能互动界面,为毕业设计注入趣味性与实用性。性能深度优化:开发过程中,我们高度关注网页性能。通过代码压缩、图片优化、缓存策略等技术手段,确保网页加载迅速、运行流畅,展示你对技术细节与性能优化的重视,助力毕业设计脱颖而出。三、后台程序编写:筑牢数据处理与业务逻辑的基石后台程序是毕业设计的核心,承担数据存储、业务逻辑处理及前后端交互等关键任务。我们提供专业后台程序编写服务,保障毕业设计稳定高效运行:数据库设计优化:根据毕业设计业务需求,精心设计合理数据库结构,选用 MySQL、MongoDB 等适配的数据库管理系统,并进行性能优化。从数据表创建、索引建立,到数据操作与复杂查询优化,全方位保障数据库高效存储与处理数据,为毕业设计提供坚实数据支撑。业务逻辑实现:深入理解你的设计需求,将复杂业务逻辑转化为清晰高效的后台代码。无论是用户管理、订单处理,还是数据分析等业务功能,都以严谨编程实现准确可靠的功能。同时注重代码可读性与可维护性,遵循编程规范,使毕业设计技术实现更专业规范,便于后续扩展维护。接口开发与安全防护:在前后端分离模式下,开发稳定高效的 API 接口,确保数据交互顺畅安全。采用身份验证、数据加密、访问控制等技术手段,防止数据泄露与恶意攻击,守护毕业设计数据与系统安全。四、全程辅导与技术支持:伴你成长,助你成功我们明白,毕业设计不仅是代码编写,更是学习成长的过程。为此,我们提供全程辅导与技术支持:一对一专属辅导:专业导师将提供一对一辅导服务,从选题到答辩全程跟踪进度。针对技术难题、设计思路优化等问题,根据你的实际情况定制个性化辅导方案,助力顺利完成毕业设计。丰富学习资源:为帮助你提升技术能力,我们提供丰富技术培训与学习资源,涵盖编程语言基础教程、前端框架实战课程、后台开发高级技巧等。让你在完成毕业设计的同时,积累宝贵技术经验,为职业发展奠基。科学项目管理:制定详细项目计划,将毕业设计分解为具体任务并明确时间节点。实施过程中定期沟通,跟踪进度,及时解决问题,确保按时高质量完成。同时根据实际灵活调整计划,保障毕业设计顺利有序推进。五、联系方式如需咨询或下单,欢迎通过 QQ 3584685883 与我们私聊,我们随时在线为你服务。六、结语毕业设计是大学生涯的重要里程碑,也是迈向专业领域的关键一步。选择我们的编程毕业设计服务,你将获得专业的网页设计、前端开发、后台程序编写及全程辅导支持。我们致力于打造高质量、有创意且技术先进的毕业设计作品,助你在众多作品中脱颖而出,为职业生涯开启成功之门。让我们携手同行,共同书写编程毕业设计的精彩篇章!
2025年05月08日
28 阅读
0 评论
0 点赞
2025-05-07
【PHP】PHP实现无限级树形分类 树形分类 如果子集的pid不是空,父级id不存在,则子集不展示
/** * @Author:小破孩 * @Email:3584685883@qq.com * @Time:2024/12/17 22:27 * @param $arr * @param $id * @param $pid * @return array * @Description:树形分类 如果子集的pid不是空,父级id不存在,则子集不展示 */ public function getTreeByDateDelChile($arr,$id,$pid) { $items = []; foreach($arr as $v){ $items[$v[$id]] = $v; } $tree = []; foreach($items as $k => $item){ if($item[$pid] &&!isset($items[$item[$pid]])){ unset($items[$k]); continue; } if(isset($items[$item[$pid]])){ $items[$item[$pid]]['child'][] = &$items[$k]; }else{ $tree[] = &$items[$k]; } } return $tree; } /** * @Author:小破孩 * @Email:3584685883@qq.com * @Time:2024/9/14 16:59 * @param array $arr * @param string $children * @return array * @Description:将树形数组转换成一维数组 */ public function setManyTosingle(array $arr = [], string $children = 'child'): array { $result = []; foreach ($arr as $item) { // 先将当前节点添加到结果数组中 $newItem = $item; if (isset($newItem[$children])) { unset($newItem[$children]); } $result[] = $newItem; // 递归处理子节点 if (isset($item[$children]) && is_array($item[$children])) { $result = array_merge($result, $this->setManyTosingle($item[$children], $children)); } } return $result; }
2025年05月07日
24 阅读
0 评论
0 点赞
2025-05-06
【PHP】清空MySQL数据,索引重置
<?php // 定义排除表的常量 const EXCLUDE_TABLES = [ 'web_admin_company', 'web_admin_func', 'web_admin_role', 'web_admin_user', 'web_app_config', 'web_china_city', 'web_china_city_area', 'web_china_city_backup', 'web_company_config', 'web_shop_category', 'web_tppay', // 'web_shop_goods_tmpserver', ]; class SomeClass { public function truncateTables() { try { // 开启事务 Db::startTrans(); // 获取所有表名 $tables = Db::query('SHOW TABLES'); $tableNames = array_map('current', $tables); foreach ($tableNames as $tableName) { if (!in_array($tableName, EXCLUDE_TABLES)) { // 使用参数化查询清空表并重置索引 Db::execute("TRUNCATE TABLE `$tableName`"); echo "表 {$tableName} 已清空<br>"; } } // 提交事务 Db::commit(); } catch (\Exception $e) { // 回滚事务 Db::rollback(); // 记录错误日志 error_log("发生错误: " . $e->getMessage()); echo "发生错误: " . $e->getMessage(); } } }
2025年05月06日
21 阅读
0 评论
0 点赞
2025-04-29
【Nginx】什么是Nginx
Nginx(发音为“engine x”)是一款高性能的开源Web服务器、反向代理服务器,同时也支持HTTP缓存、负载均衡、邮件代理等功能。它由俄罗斯工程师Igor Sysoev于2004年开发,旨在解决高并发场景下的性能问题,2011年成立Nginx公司(现属于F5 Networks),目前广泛应用于全球大型网站和复杂分布式系统中。核心功能与特点高性能与高并发处理采用事件驱动的异步非阻塞模型(Epoll/Kqueue),能高效处理海量并发连接(单台服务器可支持数万并发),内存占用低,适合高流量场景。对比传统服务器(如Apache),在静态资源处理和反向代理场景下性能优势显著。反向代理与负载均衡反向代理:接收客户端请求,转发至后端多个服务器(如Tomcat、Node.js、Django等),隐藏后端架构细节,提升安全性。负载均衡:支持轮询、加权轮询、IP哈希、最少连接数等策略,将流量均匀分配到后端服务器,避免单点压力。HTTP缓存与静态资源处理内置缓存机制(如Proxy Cache),可缓存静态文件(HTML、CSS、JS、图片等),减少后端服务器压力,加速客户端响应。直接高效处理静态资源,无需依赖额外模块。丰富的功能扩展支持HTTPS(SSL/TLS加密)、URL重写、Gzip压缩、跨域请求(CORS)、限流(如限制IP访问频率)等。可通过插件(如Lua脚本、Nginx Plus模块)实现自定义逻辑,满足复杂业务需求。轻量与灵活配置文件简洁(nginx.conf),支持热加载(无需重启服务即可更新配置)。资源消耗低,适合运行在容器(Docker)或资源受限的环境中。典型应用场景Web服务器:直接托管静态资源,或通过FastCGI协议处理动态请求(如PHP、Python应用)。反向代理服务器:作为前端入口,转发请求到后端多个应用服务器,实现高可用性和扩展性。负载均衡器:在分布式系统中分发流量,避免单节点过载,提升整体吞吐量。API网关:在微服务架构中作为统一入口,处理路由、认证、限流等逻辑。缓存服务器:通过缓存静态或动态内容,降低后端压力,优化用户访问速度。知名应用案例国内外大型网站:Google、Facebook、Twitter、京东、淘宝、新浪、网易、知乎等均使用Nginx作为核心服务器组件。云计算与CDN:AWS、阿里云、腾讯云等云服务商的CDN节点大量部署Nginx,用于边缘节点的内容分发。总结Nginx以高性能、低资源消耗、高可扩展性成为现代Web架构的核心组件,尤其适合高并发、分布式系统和微服务场景。无论是作为独立Web服务器,还是与其他技术(如Tomcat、Kubernetes)结合使用,都能有效提升系统的稳定性和性能。
2025年04月29日
22 阅读
0 评论
0 点赞
2025-04-23
【PHP】管家婆各个大类的基础调用数据方法
<?php namespace app\common\lib\gjp\warehouse; use think\Exception; class Warehouse { protected $dbName; public function __construct() { $this->dbName = "*****"; // } //查询单据 public function getWarehouse($paramkey = "", $paramJson = "", $userId = "***********")// { $instanceGjpBasic = new \app\common\lib\gjp\Basic(); $gjpSignInfo = $instanceGjpBasic->getSignKey(); $instanceGjpApiUrl = new \app\common\lib\gjp\Apiurl($userId); $gjpApiUrlInfo = $instanceGjpApiUrl -> getApiUrl(); $mobile = empty($gjpApiUrlInfo['GraspCloudMobile']) ? 0 : $gjpApiUrlInfo['GraspCloudMobile']; $serviceid = empty($gjpApiUrlInfo['GraspCloudServerId']) ? 0 : $gjpApiUrlInfo['GraspCloudServerId']; $requestUrl = $gjpApiUrlInfo['ApiServerAddress']; $md5BeforeStr = "apiparam".$gjpApiUrlInfo['ApiParam']."apitype".'query'."dbname".(string)$this->dbName."interiorapi"."1"."managename"."GraspCMServerApi.dll"."mobile".$mobile."paramjson".$paramJson."paramkey".$paramkey."serviceid".$serviceid.$gjpSignInfo['SignKey']; $data = [ 'managename' => (string)"GraspCMServerApi.dll", 'dbname' => (string)$this->dbName, 'paramkey' => (string)$paramkey, 'paramjson' => (string)($paramJson), 'apiparam' => (string)$gjpApiUrlInfo['ApiParam'], 'apitype' => (string)"query", 'sign' => (string)md5($md5BeforeStr), 'mobile' => (string)$mobile, 'serviceid' => (string)$serviceid, 'interiorapi' => (integer)1, ]; $encodedParams = http_build_query($data); $result = $instanceGjpBasic->curlRequest($requestUrl,"POST",$encodedParams,true,false); if($result['code'] != 0){ print_r($result);die; } return $result; } //生产单据 public function setReceipts($vchtype = 0,$billdata = '',$userId = "*************"){ $instanceGjpBasic = new \app\common\lib\gjp\Basic(); $gjpSignInfo = $instanceGjpBasic->getSignKey(); $instanceGjpApiUrl = new \app\common\lib\gjp\Apiurl($userId); $gjpApiUrlInfo = $instanceGjpApiUrl -> getApiUrl(); $requestUrl = $gjpApiUrlInfo['ApiServerAddress']; $mobile = empty($gjpApiUrlInfo['GraspCloudMobile']) ? 0 : $gjpApiUrlInfo['GraspCloudMobile']; $serviceid = empty($gjpApiUrlInfo['GraspCloudServerId']) ? 0 : $gjpApiUrlInfo['GraspCloudServerId']; $md5BeforeStr = "apiparam".$gjpApiUrlInfo['ApiParam']."apitype".'process'."billdata". $billdata ."dbname".(string)$this->dbName."interiorapi"."1"."managename"."GraspCMServerApi.dll"."mobile".$mobile."processtype"."0"."serviceid".$serviceid."vchcode"."0"."vchtype".$vchtype.$gjpSignInfo['SignKey']; $data = [ 'managename' => (string)"GraspCMServerApi.dll", 'dbname' => (string)$this->dbName, 'processtype' => (integer)0, 'vchtype' => (integer)$vchtype, 'vchcode' => (integer)0, 'billdata' => (string)$billdata, 'apiparam' => (string)$gjpApiUrlInfo['ApiParam'], 'apitype' => (string)"process", 'sign' => (string)md5($md5BeforeStr), 'mobile' => (string)$mobile, 'serviceid' => (string)$serviceid, 'interiorapi' => (integer)1, ]; // print_r($data);die; $encodedParams = http_build_query($data); $result = $instanceGjpBasic->curlRequest($requestUrl,"POST",$encodedParams,true,false); return $result; } //新增或修改 基础资料新增 public function businessBaseInfo($basetype = "", $baseinfodata = "", $rec = "", $type = 1, $userId = "*********"){ if($type == 1){ $typeName = "add"; }else{ $typeName = "modify"; } if(empty($rec)){ (string)$rec = "0"; } $instanceGjpBasic = new \app\common\lib\gjp\Basic(); $gjpSignInfo = $instanceGjpBasic->getSignKey(); $instanceGjpApiUrl = new \app\common\lib\gjp\Apiurl($userId); $gjpApiUrlInfo = $instanceGjpApiUrl -> getApiUrl(); // print_r($gjpApiUrlInfo);die; $mobile = empty($gjpApiUrlInfo['GraspCloudMobile']) ? 0 : $gjpApiUrlInfo['GraspCloudMobile']; $serviceid = empty($gjpApiUrlInfo['GraspCloudServerId']) ? 0 : $gjpApiUrlInfo['GraspCloudServerId']; $requestUrl = $gjpApiUrlInfo['ApiServerAddress']; $md5BeforeStr ="actiontype".$typeName."apiparam".$gjpApiUrlInfo['ApiParam']."apitype".'baseinfo'."baseinfodata".$baseinfodata."basetype".$basetype."dbname".(string)$this->dbName."interiorapi"."1"."managename"."GraspCMServerApi.dll"."mobile".$mobile."rec".$rec."serviceid".$serviceid.$gjpSignInfo['SignKey']; // "actiontype".$typeName."apiparam".$gjpApiUrlInfo['ApiParam']."apitype"."baseinfo"."basetype".$basetype."baseinfodata".$baseinfodata."dbname".$this->dbName."interiorapi".1."managename"."GraspCMServerApi.dll"."mobile".$mobile."serviceid".$serviceid."sign".md5($md5BeforeStr) $data = [ 'managename' => (string)"GraspCMServerApi.dll", 'dbname' => (string)$this->dbName, 'actiontype' => (string)$typeName, 'rec' => (string)$rec, 'basetype' => (string)$basetype, 'baseinfodata' => (string)$baseinfodata, 'apiparam' => (string)$gjpApiUrlInfo['ApiParam'], 'apitype' => (string)"baseinfo", 'sign' => (string)md5($md5BeforeStr), 'mobile' => (string)$mobile, 'serviceid' => (string)$serviceid, 'interiorapi' => (integer)1, ]; $encodedParams = http_build_query($data); $result = $instanceGjpBasic->curlRequest($requestUrl,"POST",$encodedParams,true,false); if($result['code'] != 0){ print_r($result);die; } return $result; } }
2025年04月23日
21 阅读
0 评论
0 点赞
2025-04-23
【PHP】获取程序Api地址 - 管家婆基础对接 PHP版本
<?php namespace app\common\lib\gjp; class Apiurl { protected $userId; protected $appKey; protected $SercretKey; protected $currentTimestamp; protected $dbName; public function __construct($userId = '********') { $this->appKey = "*****************"; $this->SercretKey = "*******************"; $this->currentTimestamp = time(); $this->userId = $userId; $this->dbName = '*****'; } public function getApiUrl() { $url = "http://api.cmgrasp.com/CMGraspApi/GateWay"; $instanceGjpBasic = new \app\common\lib\gjp\Basic(); $instanceStr = new \app\common\lib\data\Str(); $randamStr = $instanceStr->setNonce(32,true); $md5BeforeStr = "AppKey".$this->appKey."InvalidTime".date('YmdHis',$this->currentTimestamp)."RandamStr".$randamStr."UserId".$this->userId.$this->SercretKey; $data = [ 'MethodName' => (string)"graspcm.cmapi.getcustomerapiurl", 'AppKey' => (string)$this->appKey, 'UserId' => (string)$this->userId, 'InvalidTime' => (string)date('Y-m-d H:i:s'), 'RandamStr' => (string)$randamStr, 'SignStr' => (string)md5($md5BeforeStr), 'DbName' => (string)$this->dbName ]; $jsonData = json_encode($data); $result = $instanceGjpBasic->curlRequest($url,"POST",$jsonData,true,false); if($result['RetCode'] != 0){ $this->getApiUrl(); } $resultData = json_decode($result['RetMsg'],true); return $resultData; } }
2025年04月23日
19 阅读
0 评论
0 点赞
2025-04-23
【PHP】获取接口所需的SignKey - 管家婆基础对接 PHP版本
<?php namespace app\common\lib\gjp; use think\Exception; class Basic { protected $appKey; protected $SercretKey; protected $currentTimestamp; public function __construct() { $this->appKey = "*************"; $this->SercretKey = "*************"; $this->currentTimestamp = time(); } public function getSignKey() { if(!empty( cache('gjp_sign'))) { $dataResult = cache('gjp_sign'); return $dataResult; } $signUrl = "http://api.cmgrasp.com/CMGraspApi/GateWay"; $instanceStr = new \app\common\lib\data\Str(); $randamStr = $instanceStr->setNonce(32,true); $md5BeforeStr = "AppKey".$this->appKey."InvalidTime".date('YmdHis',$this->currentTimestamp)."RandamStr".$randamStr.$this->SercretKey; $data = [ "MethodName" => (string)"graspcm.cmapi.getsignstr", 'AppKey' => (string)$this->appKey, 'InvalidTime' => (string)date('Y-m-d H:i:s',$this->currentTimestamp), 'RandamStr' => (string)$randamStr, 'SignStr' => (string)md5($md5BeforeStr), ]; $jsonData = json_encode($data); $result = $this->curlRequest($signUrl,"POST", $jsonData,true,false); if($result['RetCode'] != 0) { $this->getSignKey(); } $dataResult = json_decode($result['RetMsg'],true); if(empty( cache('gjp_sign'))) { $dataResult = json_decode($result['RetMsg'],true); cache('gjp_sign',$dataResult,60*60*20); } return $dataResult; } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/4/1 10:39 * @param string $url url地址 * @param string $method 请求方法,默认为 'GET',可选值为 'GET' 或 'POST' * @param mixed $data 要发送的数据,如果是 POST 请求则为数据内容,否则为 null * @param array $headers 自定义请求头信息 * @param int $timeout 超时时间,默认为 30 秒 * @param bool $verifySSL 是否验证 SSL 证书,默认为 true * @param bool $flbg 返回值是否转成数组,默认不转 * @param bool $headercontent 是否获取请求的header值内容,默认不获取 * @return array|bool|mixed|string * @Description:curl请求 */ public function curlRequest($url, $method = 'GET', $data = null, $flbg = false, $verifySSL = true, $headers = [], $headerContent = false, $timeout = 30) { // 初始化 cURL 会话 $ch = curl_init(); // 设置要请求的 URL curl_setopt($ch, CURLOPT_URL, $url); // 设置获取的信息以字符串形式返回,而不是直接输出 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 设置超时时间 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); // 设置请求方法 if ($method === 'POST') { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } // 设置请求头 if (!empty($headers)) { curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); } // 设置是否验证 SSL 证书 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySSL); // 执行 cURL 会话并获取响应 $response = curl_exec($ch); // 获取 HTTP 响应码 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 如果 cURL 执行出错 if (curl_errno($ch)) { // 输出错误信息 echo 'Curl error: ' . curl_error($ch); // 关闭 cURL 会话并返回 false curl_close($ch); return false; } // 如果 HTTP 响应码大于等于 400(表示错误) elseif ($httpCode >= 400) { // 输出错误信息 echo "HTTP error: $httpCode"; // 关闭 cURL 会话并返回 false curl_close($ch); return false; } // 处理是否获取请求头内容 if ($headerContent && $httpCode == 200) { $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $headers = substr($response, 0, $headerSize); $body = substr($response, $headerSize); curl_close($ch); return [$headers, $body]; } // 关闭 cURL 会话 curl_close($ch); // 处理是否将响应转换为数组 if ($flbg) { $response = json_decode($response, true); } // 返回响应内容 return $response; } }
2025年04月23日
17 阅读
0 评论
0 点赞
2025-04-23
精准检测,助力社交管理 —— 微信好友检测服务来袭!
精准检测,助力社交管理 —— 微信好友检测服务来袭!不发消息也能查!微信好友删除 / 拉黑检测服务上线。不用再群发 “测试消息” 尴尬试探!我们的静默检测技术,0 打扰标注两大核心状态 ——「删除我的人」「拉黑我的人」,5000 好友状态 1 次搞定,社交管理从此精准避雷!
2025年04月23日
1,290 阅读
0 评论
59 点赞
2025-04-18
【Docker】Docker Compose
什么是 Docker ComposeDocker Compose 是 Docker 官方提供的一个工具,它允许你使用 YAML 文件来定义和管理多个 Docker 容器的应用。通过一个单独的 docker-compose.yml 文件,你可以定义应用的服务、网络和卷等配置,然后使用一条命令就能轻松地部署和管理整个应用栈。这大大简化了多容器应用的部署和管理过程,提高了开发和运维效率。安装 Docker ComposeLinux 系统:可以通过以下命令下载并安装 Docker Compose:sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-composemacOS 和 Windows 系统:在安装 Docker Desktop 时,Docker Compose 会自动安装。基本概念服务(Services)服务是定义在 docker-compose.yml 文件中的一个或多个容器的集合,每个服务都有自己的配置,如使用的镜像、端口映射、环境变量等。例如,一个 Web 应用可能包含一个 Web 服务器服务和一个数据库服务。网络(Networks)网络用于连接不同的服务,使得它们之间可以相互通信。Docker Compose 默认会创建一个网络,所有的服务都会连接到这个网络中。卷(Volumes)卷用于持久化存储容器中的数据,防止数据在容器销毁时丢失。你可以在 docker-compose.yml 文件中定义卷,并将其挂载到容器的指定目录。编写 docker-compose.yml 文件以下是一个简单的 docker-compose.yml 文件示例,用于部署一个包含 Web 服务器和数据库的应用:这个示例中定义了两个服务:web 服务:使用 nginx 镜像,将容器的 80 端口映射到宿主机的 80 端口,并依赖于 db 服务。db 服务:使用 mysql:5.7 镜像,设置了 MySQL 的 root 密码和数据库名,并将数据卷 db-data 挂载到容器的 /var/lib/mysql 目录。使用 Docker Compose 命令启动应用在包含 docker-compose.yml 文件的目录下,执行以下命令启动应用:docker-compose up -d-d 参数表示以守护进程模式运行容器,即容器在后台运行。停止应用执行以下命令停止并删除所有由 Docker Compose 创建的容器、网络和卷:docker-compose down查看服务状态使用以下命令查看应用中各个服务的状态:docker-compose ps查看服务日志要查看某个服务的日志,可以使用以下命令:docker-compose logs web这里的 web 是服务的名称,你可以根据实际情况替换为其他服务名。总结Docker Compose 是一个非常实用的工具,它可以帮助你更方便地管理多容器应用。通过编写 docker-compose.yml 文件,你可以定义应用的各个组件及其配置,然后使用简单的命令就能完成应用的部署、启动、停止等操作。
2025年04月18日
23 阅读
0 评论
0 点赞
2025-04-18
【Docker】Docker Desktop的使用
Docker Desktop 是一个易于安装的应用程序,适用于 Mac、Windows 系统,它能让你在本地开发和测试 Docker 应用程序。以下是 Docker Desktop 的使用介绍:安装与启动安装:访问 Docker 官方网站(https://www.docker.com/products/docker-desktop ),根据自己的操作系统下载对应的 Docker Desktop 安装程序,然后按照安装向导完成安装。启动:安装完成后,在开始菜单(Windows)或应用程序文件夹(Mac)中找到 Docker Desktop 并启动。启动后,Docker 图标会出现在系统托盘中(Windows)或菜单栏中(Mac),当图标变为绿色时,表示 Docker 服务已成功启动。界面介绍Docker Desktop 提供了图形化界面,包含以下主要部分:Images(镜像):显示本地所有的 Docker 镜像,你可以查看镜像的详细信息、标签、大小等,还能对镜像进行删除、运行等操作。Containers(容器):展示正在运行或已停止的容器列表,可查看容器的状态、端口映射、日志等信息,同时支持对容器进行启动、停止、重启、删除等操作。Volumes(数据卷):用于管理 Docker 数据卷,数据卷可用于持久化存储容器中的数据。你可以创建、删除和查看数据卷的详细信息。Networks(网络):显示 Docker 网络的相关信息,包括网络类型、子网、网关等,支持创建和管理自定义网络。常用操作拉取镜像你可以在命令行中使用 docker pull 命令拉取镜像,也可以在 Docker Desktop 的界面中进行操作。例如,要拉取 nginx 镜像,在命令行中执行:docker pull nginx拉取完成后,在 Docker Desktop 的 Images 页面就能看到 nginx 镜像。运行容器命令行方式:使用 docker run 命令启动容器。例如,启动一个 nginx 容器并将容器的 80 端口映射到宿主机的 8080 端口:docker run -d -p 8080:80 nginx界面方式:在 Docker Desktop 的 Images 页面找到 nginx 镜像,点击 Run 按钮,在弹出的配置窗口中设置端口映射等参数,然后点击 Run 即可启动容器。启动后,在 Containers 页面可以看到正在运行的 nginx 容器。查看容器日志在 Containers 页面找到要查看日志的容器,点击容器名称进入详情页面,在 Logs 标签页中可以查看容器的实时日志信息。停止和删除容器停止容器:在 Containers 页面,找到要停止的容器,点击 Stop 按钮即可停止容器。删除容器:停止容器后,点击 Remove 按钮可将容器从本地删除。构建镜像如果你有一个 Dockerfile,可以使用 docker build 命令构建镜像。例如,在包含 Dockerfile 的目录下执行:docker build -t my-custom-image:1.0 .构建完成后,新的镜像会出现在 Docker Desktop 的 Images 页面。其他功能Kubernetes 支持:Docker Desktop 集成了 Kubernetes,可以在本地快速搭建 Kubernetes 环境。在 Docker Desktop 的设置中,找到 Kubernetes 选项卡,勾选 Enable Kubernetes 即可启用。资源管理:可以在 Docker Desktop 的设置中调整 Docker 引擎的资源分配,如 CPU、内存、磁盘等,以满足不同应用的需求。
2025年04月18日
23 阅读
0 评论
0 点赞
1
2
...
13