Compare commits

...

324 Commits

Author SHA1 Message Date
augushong
62a17647e1 docs(timer): 补充定时器技能文档 - execute 日志包裹机制说明与本地开发常见坑位 2026-06-02 23:06:27 +08:00
augushong
76b23d4c70 refactor(timer): 使用 CurlMultiHandler 替代 Workerman,统一为非阻塞模式
- 删除 runParallel() 方法和所有 Workerman 引用(死代码)
- 重写 runLoop() 为 Guzzle CurlMultiHandler 非阻塞事件循环
- 新增 pending 数组追踪进行中的请求,handler.tick() 非阻塞推进
- 自适应 sleep 策略(有任务 50ms,空闲 200ms)
- 简化 config/timer.php:移除 mode,适配 Guzzle 参数
- 更新 SKILL.md:移除 parallel 描述,修正 --quit 文档 bug
- 验证发现:--quiet 是 ThinkPHP 全局选项,不需要在 configure() 注册
- 验证发现:方法名不能用 run(),与 ThinkPHP Command::run() 签名冲突
2026-06-02 21:19:53 +08:00
augushong
c4fbd60bbc fix(timer): 修正定时器参数拼写 quit -> quiet(静默模式) 2026-06-02 20:09:37 +08:00
augushong
288724efc8 chore: 发布 v2.2.8 2026-06-01 23:15:18 +08:00
augushong
65e2d35821 docs: 将在线文档规范落地为本地零散规则文件
- 新增 ulthon-naming-convention.md(命名规范)
- 新增 ulthon-database-design.md(表结构设计规范)
- AGENTS.md 通用基础规范章节改为引用本地规则文件
- 代码风格直接指向项目根目录 .php-cs-fixer.php
2026-06-01 23:14:41 +08:00
augushong
f5db5abb17 chore: bump version to v2.2.7 2026-06-01 22:46:18 +08:00
augushong
ddaa0ca5a9 feat(stack): 新增 docker-dev-sync 模式,优化 Windows 下 Docker 开发 I/O 性能
- 新增 source/stack/docker-dev-sync/ 模式目录
- 宿主代码映射到 /var/www/source(bind mount 中转)
- 容器内 rsync 定时同步到 /var/www/html(原生文件系统)
- vendor 由 Docker build 管理,不参与同步
- rsync 使用 --no-perms 避免保留 Windows 源文件权限
- 排除 docker-dev/、runtime/、.git/ 等无关目录
- SYNC_INTERVAL 环境变量可配置轮询间隔(默认 3 秒)
- 更新 stack.json 注册模式并声明 sync.sh 为托管文件
- 调整 CI 构建流程:先安装依赖再切换 stack 模式
2026-06-01 22:32:29 +08:00
augushong
4e3de27c51 chore: bump version to v2.2.6 2026-06-01 21:52:57 +08:00
augushong
c5ebf86ad9 refactor(rules): 精简 AGENTS.md,下沉模块级规则,迁移 skill 为 rule
- tp-controller-url-rules skill 迁移为 rule ulthon-controller-url.md
- AGENTS.md「其他规则」中的 source/ 约定下沉为 ulthon-source-directory.md
- AGENTS.md「其他规则」中的部署栈+命令执行环境下沉为 ulthon-deploy-environment.md
- AGENTS.md 精简:其他规则从 10 条减至 5 条,规则维护机制从 10 行减至 4 行
- ulthon-rules-manager 技能增加 rules/skills 边界判定说明
- rules 定位拓宽:约束、约定、设计决策均可作为 rule
2026-06-01 21:48:27 +08:00
augushong
6d2963a9a4 docs: AGENTS.md 开头入口区增加 .agents/skills/ 说明 2026-06-01 21:33:04 +08:00
augushong
6c6c0b771f docs: AGENTS.md 开头入口区增加 .agents/rules/ 说明 2026-06-01 21:32:19 +08:00
augushong
4d05a73949 fix(rules): 清理 PROJECT.md 中框架预填内容,恢复为使用者模板 2026-06-01 21:28:30 +08:00
augushong
f867c42a72 feat(rules): 新增零散规则管理机制
- 新增 .agents/rules/ 目录,存放模块级/场景级独立规则文件
- 新增 ulthon-rules-manager 技能,指导规则的新增/索引/格式
- 新增 ulthon-timer-multi-node 规则文件(从 PROJECT.md 迁移)
- AGENTS.md 新增「零散规则」章节,含框架级规则索引表
- PROJECT.md 新增「规则索引」章节,含全量规则索引表
- 命名约定:ulthon- 前缀为框架内置,project- 前缀为使用者业务
2026-06-01 21:26:16 +08:00
augushong
5941a61b80 重构:JS框架内部变量 admin 统一重命名为 ua,移除无用的 window.ulAdmin 2026-06-01 21:13:22 +08:00
augushong
c4bb851fbb docs: 修正运行模式检测方式,Docker 模式下不能依赖 php think 检测 2026-06-01 20:33:45 +08:00
augushong
1c4f7ac5d5 docs(timer): 技能文档补充 timer.php 配置项说明
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-01 20:28:57 +08:00
augushong
afd614e563 feat(timer): ClearLog 清理天数改为从配置读取,支持 env 覆盖
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-01 20:28:25 +08:00
augushong
5fe6e3df74 docs(timer): 定时器技能新增按时间窗口循环处理的推荐方法
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-01 20:20:58 +08:00
augushong
d95c06da28 chore: 移动 nginx 伪静态配置到 source/docs 目录 2026-06-01 20:17:38 +08:00
augushong
f5d019b260 fix(ci): 调整流水线步骤顺序,composer install 移至 php think 命令之前
checkout 后没有 vendor 目录,导致 php think admin:stack:mode 报错。
将 composer install 抽为独立步骤并提前执行,确保 php think 命令可正常运行。
2026-05-27 22:59:24 +08:00
augushong
8c766d7e07 chore: bump version to v2.2.5 2026-05-27 21:54:50 +08:00
augushong
95698afa18 chore(stack): 切换到 default 模式,同步根目录状态 2026-05-27 21:53:35 +08:00
augushong
ceed8d3e71 feat(stack): CLI handleUse() 增加 skipped_files 和 deleted_files 展示 2026-05-27 21:42:52 +08:00
augushong
20dc1b944c docs(stack): 命令执行环境描述改为引导查看 stack 命令,不再硬编码模式列表 2026-05-27 21:42:43 +08:00
augushong
f49ca8d9cf fix(stack): 为 full 和 base-build 模式补充 source/docker/run.sh 2026-05-27 21:42:35 +08:00
augushong
5bb0e03e98 fix(stack): 修正流水线命令 action 为 use,加 -f 跳过确认交互 2026-05-27 20:58:50 +08:00
augushong
c7cfa40539 chore: bump version to v2.2.4 2026-05-27 20:52:13 +08:00
augushong
28337c9694 refactor(stack): default 模式改为 php think run,原 Docker 部署拆分为 docker-serve 模式
- source/stack/default/ 重命名为 docker-serve,保留 Dockerfile/docker-compose/CI/run.sh
- 新 default 模式仅提供 CI 文件(流水线中自动切换 docker-serve 再构建)
- 更新 stack.json 增加 docker-serve 模式声明
- 更新 README.md 同步模式说明
2026-05-27 20:49:13 +08:00
augushong
7ecbd03730 chore: bump version to v2.2.3
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-27 20:24:23 +08:00
augushong
c16964679d feat(menu): 补充定时器和主机列表的默认菜单初始化数据
在 SystemMenu 初始化数据中新增定时器配置(id=256)、定时器日志(id=257)、主机列表(id=258)三条菜单,确保 seed:run 后菜单自动可用

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-27 20:22:23 +08:00
augushong
f2f2dcad98 feat(timer): 新增定时任务表的数据库迁移文件
补充 system_timer_config 和 system_timer_log 的数据库迁移,确保全新安装时 migrate:run 能正确建表

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-27 20:22:09 +08:00
augushong
a2916d7505 refactor(ulthon-admin): 重写入口文件并更新模板引用
将 ulthon-admin.js 简化为入口文件(8行),仅负责组装 admin 对象并清理临时变量。更新 _require.html 按依赖顺序加载 6 个拆分文件。

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-27 07:51:42 +08:00
augushong
6dda5890e0 refactor(ulthon-admin): 拆分 JS 单文件为多模块架构
将 ulthon-admin.js (3258行) 按职责拆分为 5 个独立模块文件:- admin-core.js (248行): 配置、请求封装、消息提示、权限检查- admin-utils.js (224行): 工具函数(URL参数、页面设置、弹层等)- admin-table.js (1370行): 表格模块(渲染、搜索、工具栏、列模板等)- admin-api.js (687行): API组件(表单提交、上传、编辑器、选择器等)- admin-listen.js (267行): 全局事件监听(按钮、删除、刷新等)

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-27 07:51:30 +08:00
augushong
fefe2fe95f chore: 配置shell脚本使用LF行尾格式
解决Windows挂载到Docker时shell脚本因CRLF换行符报错的问题
2026-05-26 23:18:42 +08:00
augushong
a9810516c1 chore(release): 发布 v2.2.2 2026-05-26 21:40:45 +08:00
augushong
3f8f79b445 feat(update): 新增 --keep-repo 参数,dry-run 模式下保留上游克隆目录便于对比 2026-05-26 21:38:16 +08:00
augushong
58d87d9980 docs(VersionBase): update usage comment of version command
fix the outdated description of the version command usage steps
2026-05-26 21:01:49 +08:00
augushong
fce0b1cf0a chore: bump version to v2.2.1 2026-05-26 20:48:53 +08:00
augushong
ee900ffb8a fix(docker): 修复定时器进程因 www-data nologin shell 无法启动的问题
www-data 用户默认 shell 为 /usr/sbin/nologin,导致 su - www-data -c 被拒绝,
定时器从未启动,主机节点心跳不上报。改用 su -s /bin/bash 指定 shell,
并将定时器日志输出到 runtime/timer.log 方便排查。
2026-05-26 20:45:52 +08:00
augushong
8e8c56ab53 chore: bump version to v2.2.0 and update full changelog
更新了版本号到v2.2.0,补充完整的版本更新说明,包含新功能、修复项、文档更新、重构、性能优化和其他变更内容
2026-05-26 20:34:57 +08:00
augushong
e005c9a3a2 docs(version): 补充 admin:version 命令的发布流程注释与帮助说明 2026-05-26 20:11:06 +08:00
augushong
b7094eee8a fix(host): 主机列表页设为只读
Remove add/edit/delete buttons and auth attributes from host list page. Hosts are auto-registered via heartbeat, manual CRUD is meaningless. Keep only setMaster operation.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-26 18:33:47 +08:00
augushong
e558a97e91 fix(host): 防止主节点重复并修复 setMaster 权限
Clear all is_master flags before electing new master to prevent multiple master records. Fix setMaster annotation from @auth true to proper @NodeAnotation format so permission node is generated correctly.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-26 18:33:46 +08:00
augushong
a47bbb2c6a fix(timer): 自动记录执行日志并捕获结果
Add execute() wrapper in TimerControllerBase that wraps do() with logStart/logEnd, captures return value to result field. Change site URL routing from /do to /execute with task_name injection. Add result field to system_timer_log scheme.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-26 18:33:45 +08:00
augushong
e1bf94a55e feat(timer): 新增日志每日自动清理定时任务
- New LogClean timer controller (site type, frequency 86400s)
- run_type defaults to auto, only one node cleans per day
- Cleans system_timer_log records older than 30 days
2026-05-26 18:33:45 +08:00
augushong
fd89a60425 fix(timer): 修复 F2 代码质量问题
- HostServiceBase: move Log::error before throw so it actually executes
- TimerBase: remove empty comment block
2026-05-26 18:33:44 +08:00
augushong
6dceb028b3 docs(timer): 更新 SKILL.md 和 PROJECT.md 多节点协调文档
T13: E2E single-node regression - all PASS
T14: E2E multi-node coordination - 7/7 PASS
T15: Document multi-node features in ulthon-timer SKILL.md and PROJECT.md
2026-05-26 18:33:43 +08:00
augushong
90e584f5a1 feat(timer): 新增定时器配置、日志和主机的后台管理界面
T10: TimerConfig CURD - task management with run_type/status editing,
     manual trigger button, task_name read-only, no add/delete
T11: TimerLog CURD - read-only log viewer with filters and color badges
T12: Host list enhanced - is_master column, setMaster button
2026-05-26 18:33:43 +08:00
augushong
25fab093fa feat(timer): 新增 run_type 调度、host_id 投递和日志清理
T7: TimerBase shouldExecuteTask() - main/auto/all/manual/disabled modes
    with two-phase DB row lock for auto mode
T8: TimerControllerBase - host_id param, logStart/logEnd methods
    TimerServiceBase - inject host_id into site URLs
T9: TimerLogClean command - php think admin:timer:log:clean --days=30
2026-05-26 18:33:42 +08:00
augushong
abeac2c3cb feat(timer): 新增配置同步到数据库及主节点选举
T5: TimerServiceBase.syncConfigToDatabase() - syncs task config to DB
T6: HostServiceBase - auto master election, stale node detection,
    getMasterNode/setMasterNode methods
2026-05-26 18:33:41 +08:00
augushong
d719a99d14 feat(timer): 新增多节点定时器协调的 Scheme 定义 2026-05-26 18:33:41 +08:00
augushong
20d0b5f22b docs(skills): 新增 ulthon-update-workflow AI 技能文档 2026-05-25 22:51:17 +08:00
augushong
ca094c9116 feat(update): 输出增强 -- 按目录分组、风险摘要、策略标注、--show 过滤 2026-05-25 22:47:46 +08:00
augushong
67c613788f feat(update): 冲突策略参数替代交互确认 2026-05-25 22:44:34 +08:00
augushong
2e3fac93f0 feat(update): 注册冲突策略和输出范围选项 2026-05-25 22:41:08 +08:00
augushong
07d5823c81 feat(update): 后更新引导提示 2026-05-25 21:41:55 +08:00
augushong
65b196e768 feat(update): composer.json 智能对比输出具体命令 2026-05-25 21:38:58 +08:00
augushong
a17ba88068 feat(update): 新增 --dry-run 预览模式 2026-05-25 21:35:47 +08:00
augushong
fb3f807877 refactor(update): extend/think 归入可选更新范围 2026-05-25 21:30:27 +08:00
augushong
76fe865274 perf(update): clone 一次仓库并本地复制替代第二次网络 clone; 排除 public/storage 和 public/build 2026-05-25 21:28:21 +08:00
augushong
279968c9ef chore: 更新 .gitignore 忽略文件
添加 .playwright-mcp 目录到忽略列表,移除末尾多余的换行警告
2026-05-24 11:55:04 +08:00
augushong
646580e6dc 优化开发服务器静态资源加载:router.php 注入缓存头
为 JS/CSS/图片/字体等静态资源添加 ETag、Last-Modified、Cache-Control 响应头,
支持 304 Not Modified,避免浏览器每次完整重新下载。其余文件走原有逻辑。
2026-05-24 11:53:46 +08:00
augushong
577ee6b974 feat(debug): 报错页面增加手机端适配,工具栏可折叠,修复原生布局溢出问题 2026-05-24 10:09:27 +08:00
augushong
7312db717c fix(debug): 修复预览不实时更新、出错位置不生效、中文Unicode转义等问题,预览改为右侧弹出面板,增加字数统计 2026-05-24 09:32:32 +08:00
augushong
4668865b4f fix(debug): 复选框变更时自动保存状态到localStorage 2026-05-24 09:03:02 +08:00
augushong
e263d172cd fix(debug): 修复Markdown中文输出为Unicode转义的问题 2026-05-24 08:59:16 +08:00
augushong
0225e65975 feat(debug): 报错页面增加AI友好格式和可配置一键复制功能 2026-05-24 08:55:23 +08:00
augushong
218810a7db refactor: 迁移根目录 docker/ 到 source/docker/,更新所有路径引用
- 将 docker/{nginx.conf,run.sh,zz-phprun.ini,zz-phpfpm.conf} 移动到 source/docker/
- 更新所有 Dockerfile 中 /var/www/html/docker/ -> /var/www/html/source/docker/
- 更新 stack.json managed_files 路径前缀
- 重组 stack 模式目录结构 docker/ -> source/docker/
- 更新 source/README.md 目录描述
- 更新 AdminUpdateServiceBase.php 可选文件前缀
2026-05-12 23:13:47 +08:00
augushong
81706debbb fix(upload): 修复云存储上传失败时静默返回空save_name的问题 2026-05-11 21:23:00 +08:00
augushong
1c99e74c2e docs( docker-compose ): 注释掉docker-compose的name配置项
补充说明默认使用项目目录名作为项目名,仅在同名冲突时才需要手动配置name
2026-05-11 20:46:36 +08:00
augushong
a024a215fb chore(docker-dev): 修改日志通道为debug_mysql
更新docker开发环境的日志输出渠道配置,将日志从文件输出改为debug_mysql渠道
2026-05-11 20:44:44 +08:00
augushong
9a3442d7f5 feat: 更新版本至 v2.1.4 并同步更新日志
- 新增支持更新到 ulthon_admin 的 master 分支功能
- 修复目录忽略逻辑的前缀匹配误命中问题
- 更新 source 目录结构及相关文档说明
- 优化持续集成配置,避免覆盖现有 .env 文件
- 从 Git 跟踪中移除 .sisyphus 目录
2026-05-07 23:00:49 +08:00
augushong
37c8142721 feat(update): 支持更新到 ulthon_admin 的 master 分支
新增 `--update-master` 选项,允许用户选择更新到远程仓库的 master 分支,而非默认的最新标签版本。这为需要最新开发版代码的场景提供了灵活性。
2026-05-07 22:59:19 +08:00
augushong
2663bf4a5a docs: 更新 source 目录结构说明与相关文档
- 在 source 下新增 docs、projects、attachments 目录并添加 .gitkeep 占位文件
- 更新 source/README.md,明确 source 目录用于存放主工程外的配套内容,并详细说明新增子目录用途
- 同步更新 AGENTS.md 中的相关描述,保持文档一致性
2026-05-07 22:36:29 +08:00
augushong
8b45a8818b ci: 避免覆盖现有 .env 文件并设置 IS_DEMO 默认值
在部署工作流中,现在会先检查 .env 文件是否存在。如果文件已存在,则跳过生成步骤以避免覆盖现有配置。
同时,在从 .example.env 生成新 .env 文件时,会默认将 IS_DEMO 设置为 true。
2026-05-06 21:18:41 +08:00
augushong
94d5bf5ce6 chore: 从 Git 跟踪中移除 .sisyphus 目录 2026-05-06 20:57:53 +08:00
augushong
718034a7b4 fix(update): 修复目录忽略逻辑的前缀匹配误命中问题
isIgnoredPath 和 testIsOptionalFiles 方法使用 str_starts_with 做前缀匹配,
导致 .gitea 被 .git 规则误跳过,.gitignore/.gitattributes 同样受影响。
改为精确目录名匹配: ===  || str_starts_with(,  . '/')
2026-05-06 20:55:17 +08:00
augushong
703ec2df8f docs: 补充 source 子工程规则说明
明确 source 下各子工程可拥有独立的 AGENTS.md 文件,其规则优先级高于根目录的 AGENTS.md。
2026-05-06 20:30:28 +08:00
augushong
42b31202c6 docs(skills): 更新方案定义技能文档的编写规范
- 将“编写建议”标题改为“编写规范”
- 明确时间字段统一使用 int 类型存储 Unix 时间戳
- 说明软删除字段的自动机制
- 调整编号顺序使规范更清晰
2026-05-06 20:25:34 +08:00
augushong
11101e338d chore: 发布新版本 2026-05-06 20:08:27 +08:00
augushong
c7b6865458 docs: 重构智能体规范文档并新增项目业务总览模板
- 将业务侧增量规则记录从 `.agents/AGENTS.md` 迁移至 `.agents/PROJECT.md` 的「增量规则记录」章节
- 新增 `.agents/PROJECT.md` 作为项目业务总览模板,包含项目定位、核心模块等结构化章节
- 更新根目录 `AGENTS.md` 中的引用与说明,明确 `.agents/PROJECT.md` 的用途
- 为技能文件添加命名约定说明(ulthon- 与 project- 前缀)
2026-05-06 20:07:51 +08:00
augushong
8ed88c99b5 docs: 更新AGENTS.md文档结构和内容
- 调整文档结构,将“开发规范与标准流程”改为指向具体表格和技能文件
- 删除已过时或冗余的后端、接口、风格等规则说明
- 保持核心项目规则不变,使文档更聚焦于当前实际工作流程
2026-05-06 19:49:04 +08:00
augushong
c8f606e809 docs: 移除过时的技能目录同步说明
更新 AGENTS.md 文档,删除关于 `.trae/skills/` 目录是镜像目录的说明,因为该同步关系已不再维护或不再相关。
2026-05-06 19:45:33 +08:00
augushong
167dfe110e docs(AGENTS): 更新调试与验证指南并移除冗余技能文档
- 将调试与验证部分简化为优先使用框架内置命令行工具
- 删除已整合的 CLI 命令参考技能文档,避免信息冗余
2026-05-06 19:41:23 +08:00
augushong
748307e826 chore(docker): 将默认服务端口从80更改为8000
更新所有Dockerfile、nginx配置、docker-compose文件和README中的端口引用,统一使用8000端口以避免与系统常用端口的冲突
2026-05-06 19:37:36 +08:00
augushong
e44efb33e0 docs(agents): 重构开发流程文档,优化结构并增加详细指引
- 将 AGENTS.md 中的标准开发流程从步骤列表重构为表格形式,并链接到具体技能文件
- 重写 ulthon-scheme-curd-workflow 技能文档,明确区分首次生成和已有业务代码时的安全重新生成流程
- 新增业务定制、验证交付等详细章节,提供完整的检查清单和命令行验证示例
2026-05-06 19:25:45 +08:00
augushong
8b0a59c880 chore: 发布新版本 2026-05-06 19:08:28 +08:00
augushong
386678fc9d ci: 移除工作流中调试变量的硬编码设置
All checks were successful
build-and-deploy / 直传代码并部署到 Host15 (push) Successful in 1m29s
移除环境变量 APP_DEBUG_VALUE 和 DB_DEBUG_VALUE 的定义及其在 awk 脚本中的处理逻辑。
这些调试设置不应在部署工作流中硬编码,而应由环境配置文件或更灵活的机制管理。
2026-05-03 21:25:54 +08:00
augushong
7d55599db9 ci: 在部署流程中默认关闭应用和数据库调试模式
All checks were successful
build-and-deploy / 直传代码并部署到 Host15 (push) Successful in 1m29s
添加 APP_DEBUG_VALUE 和 DB_DEBUG_VALUE 环境变量,并将其默认值设为 "false"。更新 awk 脚本以在生成 .env 配置文件时自动设置 APP_DEBUG 和 DEBUG 变量,确保生产部署时调试功能被禁用。
2026-05-03 21:20:39 +08:00
augushong
383c8ddeca chore(docker): 为所有服务添加 host.docker.internal 主机映射
All checks were successful
build-and-deploy / 直传代码并部署到 Host15 (push) Successful in 1m26s
使容器能够通过 host.docker.internal 访问宿主机服务,方便开发环境调试。
2026-05-03 21:01:19 +08:00
augushong
041eae9129 ci: 更新 CI 容器镜像版本
All checks were successful
build-and-deploy / 直传代码并部署到 Host15 (push) Successful in 2m26s
将工作流中使用的 Docker 镜像更新至指定时间戳版本,以确保构建环境的一致性。
2026-05-03 20:28:43 +08:00
augushong
ab01fe6ca1 ci: 更新 Gitea Actions 使用的容器镜像
Some checks failed
build-and-deploy / 直传代码并部署到 Host15 (push) Failing after 2m21s
将构建和部署工作流中使用的容器镜像从 `ulthon/debian-php82-composer` 更新为 `ulthon/debian-php82-composer-node20-act`,以包含 Node.js 20 和 Act 工具,为工作流执行提供更完整的环境。
2026-05-03 10:03:37 +08:00
augushong
537f178fa5 ci: 更新部署配置以支持直接分发代码
Some checks failed
build-and-deploy / 直传代码并部署到 Host15 (push) Failing after 11s
- 将工作流容器镜像更换为包含 PHP 和 Composer 的专用镜像
- 在工作流中添加 Composer 依赖安装步骤,配置私有镜像源
- 修改所有 docker-compose.yaml 文件,将卷映射从多个子目录改为映射整个项目根目录,简化部署步骤
2026-05-01 20:45:04 +08:00
augushong
c423e2cb3d ci(deploy): 优化部署配置并增加健康检查
All checks were successful
build-and-deploy / 直传代码并部署到 Host15 (push) Successful in 1m35s
- 将 Docker Compose 卷映射从挂载整个目录改为仅挂载必要的运行时目录,以提高部署安全性和性能
- 在 CI/CD 部署流程中添加容器状态检查和应用健康验证步骤,确保部署后应用正常运行
2026-05-01 19:13:38 +08:00
augushong
efc335e78f feat(stack): 新增 docker-dev 开发模式并自动清理文件
All checks were successful
build-and-deploy / 直传代码并部署到 Host15 (push) Successful in 1m13s
- 新增 docker-dev 部署模式,提供包含 nginx+php-fpm、MySQL、Redis、phpMyAdmin 和 Xdebug 的完整 Docker 开发环境
- 在 StackModeService 中重写 applyMode 方法,切换模式时自动删除目标模式中不存在的已管理文件
- 新增 .docker-dev.env 配置文件并纳入 managed_files 管理,切换模式时自动复制或删除
2026-04-30 22:38:22 +08:00
augushong
db057aa90e feat(stack): 切换模式时自动删除多余的管理文件
All checks were successful
build-and-deploy / 直传代码并部署到 Host15 (push) Successful in 1m0s
- 重写 getModePlan: 跳过目标模式和 default 都不存在的文件 (不再抛异常)
- 重写 applyMode: 切换模式后自动清理孤立的管理文件, 删除前备份以支持 rollback
- 修复 HOSTPORT 为 3306 (容器内部端口, 非宿主机映射端口)
- 增加 backup_id 空值保护: 无备份时不执行删除
2026-04-29 23:35:56 +08:00
augushong
58a9002c3a feat(stack): 新增 docker-dev 开发模式
- 新增 source/stack/docker-dev/Dockerfile: 基于 full 模式 + Xdebug + 开发 PHP 配置
- 新增 source/stack/docker-dev/docker-compose.yaml: 4 服务编排 (app/mysql/redis/phpmyadmin)
- 新增 source/stack/docker-dev/.docker-dev.env: 开发环境配置模板 (HOSTNAME=mysql, HOSTPORT=3306)
- 更新 source/stack/stack.json: 注册 docker-dev 模式, managed_files 增加 .docker-dev.env
- 更新 .gitignore: 排除 /docker-dev/ 数据目录
2026-04-29 23:35:27 +08:00
augushong
77da693d80 docs(agents): 重构技能文档,合并扩展模式到架构指南
- 删除独立的 ulthon-core-extend-pattern 技能文档
- 将扩展模式内容整合到 ulthon-base-app-architecture 架构指南
- 简化 AGENTS.md 中的架构说明,移除冗余内容
- 在架构指南中按角色(框架使用者/作者)分章节组织内容
2026-04-29 21:37:35 +08:00
augushong
59c5222497 fix(Dockerfile): 修正容器入口点以使用bash执行脚本
All checks were successful
build-and-deploy / 直传代码并部署到 Host15 (push) Successful in 50s
确保run.sh脚本在bash环境下执行,避免因缺少shebang或执行权限导致的潜在启动问题。统一所有Dockerfile中的ENTRYPOINT格式。
2026-04-25 00:06:34 +08:00
augushong
82e5cdb0bb docs: 更新项目部署栈模式说明
Some checks failed
build-and-deploy / 直传代码并部署到 Host15 (push) Failing after 3m11s
在 AGENTS.md 中补充关于部署栈模式的目录约定,明确 `source/stack/` 作为模式文件统一目录,并要求 `default/` 目录与代码库默认行为保持一致。
2026-04-24 23:43:03 +08:00
augushong
0b2b89c3ae docs(docker): 添加基础镜像 Dockerfile 使用说明文档
新增 README.md 文件,详细说明 Dockerfile.base 的作用、构建流程、与其他 Dockerfile 的关系以及维护注意事项。旨在为团队提供清晰的基础镜像构建和使用指南,提升 Docker 镜像构建的一致性和可维护性。
2026-04-24 23:42:47 +08:00
augushong
67bb1f0785 refactor(Dockerfile): 使用基础镜像重构 Dockerfile 以简化构建
- 移除重复的运行时环境配置和依赖安装步骤
- 通过 ARG 指令支持灵活的基础镜像配置
- 保持原有应用部署流程不变
2026-04-24 23:20:58 +08:00
augushong
b44fcfd86c feat(stack): 新增 stack 模式管理功能
- 新增 `php think admin:stack:mode` 命令,支持 list/use/current/rollback 操作
- 新增 StackModeService 服务,负责模式切换、备份与回滚逻辑
- 在 source/stack/ 目录下添加 default、full、base-build 三种模式的配置文件
- 更新 UlthonAdminService 以注册新的命令行工具
2026-04-24 23:20:13 +08:00
augushong
0945d42d0a ci(部署): 增加部署步骤的超时和命令超时设置
Some checks failed
build-and-deploy / 直传代码并部署到 Host15 (push) Failing after 24m18s
设置 SSH 连接超时为 120 秒,远程命令执行超时为 60 分钟,以防止长时间运行的部署任务因默认超时而过早失败。
2026-04-24 22:06:29 +08:00
augushong
80435c54a4 chore: 从 .gitignore 中移除 composer.lock 文件
Some checks failed
build-and-deploy / 直传代码并部署到 Host15 (push) Failing after 10m40s
2026-04-15 21:23:56 +08:00
augushong
d872e1facc ci: 修复打包时可能包含自身导致递归的问题
Some checks failed
build-and-deploy / 直传代码并部署到 Host15 (push) Failing after 56s
修改打包脚本,使用临时文件进行打包操作,避免 tar 命令将生成的压缩包自身包含进去,从而防止潜在的无限递归和打包失败。
2026-04-15 21:14:59 +08:00
augushong
394782a989 ci: 添加 Gitea CI/CD 工作流用于构建与部署
Some checks failed
build-and-deploy / 直传代码并部署到 Host15 (push) Failing after 34s
- 新增 .gitea/workflows/build-and-deploy.yml 文件,定义完整的 CI/CD 流程
- 工作流在 push 或手动触发时运行,负责打包代码、生成环境配置、传输至远程主机并部署
- 更新 docker-compose.yaml,添加项目名称配置以适配部署流程
- 部署流程包含安全检查、旧版本清理和滚动更新机制
2026-04-15 21:02:07 +08:00
augushong
7df05ba56f chore: 更新示例环境文件中的数据库配置
将默认数据库名称从 'ulthon' 更改为 'ulthon_admin',并将对应的用户名也更新为 'ulthon_admin',以反映实际部署环境中的配置。
2026-04-15 20:52:03 +08:00
augushong
4790f1a787 fix(docker): 修正容器启动时的目录权限和定时任务用户
确保挂载卷后运行时目录和存储目录具有正确的读写权限,避免因权限问题导致应用无法正常写入文件。
将定时任务进程从 root 切换到 www-data 用户运行,增强安全性。
2026-03-27 20:40:54 +08:00
augushong
bc36fcc737 build: 配置构建代理以加速依赖下载
- 修改 install-php-extensions 脚本中的资源地址,使用内部代理
- 配置 Composer 使用内部镜像源替代阿里云镜像
- 优化 Docker 层缓存:合并 composer 文件拷贝和依赖安装步骤
2026-03-27 20:35:10 +08:00
augushong
1f6096ecfb build: 清理apt源并配置Composer镜像加速依赖安装
清理现有apt源文件避免冲突,统一使用USTC镜像源
添加Composer镜像配置使用阿里云源,提前安装依赖优化构建过程
2026-03-27 20:32:07 +08:00
augushong
a617eb2a63 fix(Dockerfile): 将安装脚本源切换至内部镜像仓库
避免因外部网络问题导致构建失败,确保构建过程稳定可靠。
2026-03-27 20:27:43 +08:00
augushong
7ef19f6357 refactor: 提取文件收集逻辑到独立方法
将重复的文件收集和忽略前缀检查代码提取为 `collectTrackedFiles` 和 `isIgnoredPath` 方法,提高代码复用性和可读性。
2026-03-27 20:26:34 +08:00
augushong
c0d3acbd4d chore: 发布新版本 2026-03-27 20:23:42 +08:00
augushong
ce27837614 fix: 支持解析带括号的提交类型格式
更新正则表达式以匹配 type(scope): message 格式,确保版本日志能正确分组包含可选作用域的提交信息。
2026-03-27 20:21:40 +08:00
augushong
6bc2c63e7c docs: 将智能体目录名从 .agent 更新为 .agents
更新 AGENTS.md 文档中所有对智能体目录的引用,确保一致性。此次变更为纯文档更新,不影响任何代码功能。
2026-03-27 20:12:21 +08:00
augushong
86705364fe chore: 发布新版本 2026-03-26 20:27:14 +08:00
augushong
8cc08bcb8c feat: 发布智能体版 2026-03-26 20:22:34 +08:00
augushong
7ee9e102a5 docs: 更新框架文档并新增技能说明 2026-02-02 21:54:06 +08:00
augushong
28267ff1c0 docs: 重构项目规则与技能文档结构并更新开发指南 2026-02-01 13:13:03 +08:00
augushong
180d9291a3 refactor(uniapp): 重构工具函数和请求模块,提取通用逻辑 2026-02-01 12:34:50 +08:00
augushong
83e6803a0a feat(uniapp): 登录后立即获取用户资料并优化应用启动流程 2026-02-01 12:10:24 +08:00
augushong
1a88ff286e feat(uniapp): 集成 Pinia 状态管理并实现用户认证流程 2026-02-01 12:01:37 +08:00
augushong
ae6b3f1b67 feat: 新增source目录用于存放配套资源与多端代码 2026-02-01 10:51:01 +08:00
augushong
5bbf69125c docs: 更新 AGENTS.md 文档,补充登录认证机制说明 2026-02-01 09:17:23 +08:00
augushong
51c60134c0 docs: 补充页面接口同体机制说明 2026-02-01 09:16:38 +08:00
augushong
b6b690edb3 feat(tools/db): 在表结构描述中显示表注释 2026-02-01 00:22:51 +08:00
augushong
6aa176a39a fix(scheme): 改进MySQL表注释获取与对比的可靠性 2026-02-01 00:14:45 +08:00
augushong
90c0c090ca docs: 添加命令行交互全局参数说明 2026-01-31 23:17:20 +08:00
augushong
5bee1b3733 feat(console): 添加 --force-force 参数以跳过所有交互确认 2026-01-31 23:00:51 +08:00
augushong
0fad2b7e10 docs: 更新项目文档中的框架版本信息 2026-01-28 20:36:15 +08:00
augushong
353560dc50 docs: 更新 AGENTS.md 中的文档链接和说明 2026-01-27 12:17:34 +08:00
augushong
435b7cdaa9 docs: 添加 AGENTS.md 文件说明 AI 代理的开发约束与工具 2026-01-26 23:21:35 +08:00
augushong
e8f58ef322 feat(command): 新增数据库调试命令行工具集 2026-01-26 23:10:10 +08:00
augushong
148f8b7a6f docs(command): 添加tools命令命名规范文档 2026-01-26 23:09:45 +08:00
augushong
96eaa269a1 style(welcome): 优化移动端响应式布局和样式细节 2026-01-23 22:11:06 +08:00
augushong
6eefa1cd2e refactor(admin): 重构系统状态模块以支持模块化结构 2026-01-23 00:02:51 +08:00
augushong
3c0d39c4ce fix(status): 修复系统状态页面布局问题并添加刷新功能 2026-01-22 23:49:06 +08:00
augushong
3a4194d3e9 feat: 新增系统状态页面功能 2026-01-22 23:28:13 +08:00
augushong
ee40374732 feat(scheme): 增强 Scheme 与数据库同步机制并添加严格校验 2026-01-12 12:37:37 +08:00
augushong
2f7ec93f89 docs: 修正Scheme相关命令的文档错误 2026-01-10 12:11:03 +08:00
augushong
ea7ae41e71 feat(upload): 为上传组件添加文件名显示和拖拽排序功能 2026-01-09 22:35:05 +08:00
augushong
23826cd06e refactor(scheme): 优化数据库同步逻辑并增加备份表检查 2026-01-09 22:26:34 +08:00
augushong
ff037400e9 docs: 在示例环境文件中添加MAIN数据库配置 2026-01-09 22:10:16 +08:00
augushong
ec757f2e9f feat(同步命令): 添加忽略表功能并优化表名处理 2026-01-09 22:09:56 +08:00
augushong
edeae731f0 refactor: 删除废弃的scheme类文件 2026-01-09 21:40:20 +08:00
augushong
6b4a67aeb4 feat(scheme): 新增多个数据模型类文件 2026-01-09 21:38:25 +08:00
augushong
4f689d9881 fix(命令): 忽略备份表以避免在生成方案时包含它们 2026-01-09 21:37:21 +08:00
augushong
0522e2f9c3 refactor(DbToSchemeService): 重构字段注解生成逻辑以提高可读性 2026-01-09 21:28:54 +08:00
augushong
80fc381090 docs: 更新CODERULE.md添加Scheme机制说明 2026-01-09 21:09:52 +08:00
augushong
8de6b99bb3 feat(scheme): 新增数据库表结构同步方案 2026-01-09 21:08:51 +08:00
augushong
1a39354287 docs(CODERULE): 重构并优化项目开发规则文档结构 2026-01-09 19:16:30 +08:00
augushong
7026b06f91 Merge branch 'feat-curd-read' 2026-01-09 00:03:15 +08:00
augushong
4350e5f294 chore: 删除测试代码 2026-01-09 00:02:54 +08:00
augushong
d5be4cbbaa feat: 完成基本的详情生成 2026-01-09 00:02:33 +08:00
augushong
0e92ab2363 feat: 初步完成详情生成 2026-01-08 23:43:21 +08:00
augushong
3fdea8b85b feat: 实现基本的详情生成 2026-01-05 22:59:12 +08:00
augushong
528ff69897 fix: 处理联表查询排序没有处理表名的问题 2026-01-05 21:09:29 +08:00
augushong
17a024de12 feat: 页面以子页面打卡时使用弹框方式增加和编辑 2025-11-12 18:29:09 +08:00
augushong
9b91b2507c fix: 修复上传类生成必填显示错误问题 2025-11-12 18:28:14 +08:00
augushong
6976aeef49 fix: 修复参数绑定不支持非get 2025-11-12 18:24:01 +08:00
augushong
2da835c44c fix: 修复添加和编辑页面选择模式下不能使用的问题 2025-11-04 18:29:07 +08:00
augushong
1ace579178 feat: 地图组件支持更多字段的回填 2025-11-04 18:28:04 +08:00
augushong
3eab01197e chore: 调整日志每页大小 2025-10-10 22:36:48 +08:00
augushong
247bfd1966 feat: 增加按级别查询日志 2025-10-10 22:33:28 +08:00
augushong
63b1cb472a feat: 增加按应用、控制器、方法查询日志 2025-10-10 22:28:47 +08:00
augushong
3a6cfacd0a feat: 增加访问方法 2025-10-10 22:14:51 +08:00
augushong
04d152edce fix: 修复日志加载错误 2025-10-10 22:12:05 +08:00
augushong
bb76936032 fix: 保证complete能正确运行 2025-10-10 22:11:03 +08:00
augushong
af2da50678 feat: 支持刷新 2025-10-10 21:43:54 +08:00
augushong
d55b79512c feat: 优化日志级别颜色 2025-10-10 21:41:59 +08:00
augushong
d7a7ec2c65 feat: 日志查看支持分组颜色标志 2025-10-10 21:40:14 +08:00
augushong
94f63133c3 feat: 优化日志显示 2025-10-10 21:33:21 +08:00
augushong
dccccf94a0 feat: 完成基本的日志展示 2025-10-10 21:17:24 +08:00
augushong
ef1beaac38 fix: 修复系统节点表错误 2025-10-09 23:52:22 +08:00
augushong
9d403d1e41 fix: 修复创建系统节点表错误 2025-10-09 23:51:19 +08:00
augushong
a09fd8372b chore: 增加版本描述 2025-10-09 23:31:37 +08:00
augushong
ae40f99866 chore: 修改版本号 2025-10-09 23:23:28 +08:00
augushong
a96d5c0d6b chore: 发布新版本 2025-10-09 23:14:13 +08:00
augushong
6248c554c6 feat: 七牛云默认上传到智能分层 2025-08-31 11:51:29 +08:00
augushong
b21dab1695 feat: 优化路径生成兼容性 2025-08-31 11:39:32 +08:00
augushong
35e7eb9a0e feat: 操作删除文件可直接删除实际文件 2025-08-31 11:34:56 +08:00
augushong
cca7dba632 feat: 优化迁移文件路径命令,直接使用sql功能; 2025-08-31 11:33:45 +08:00
augushong
6339e2bed1 feat: 优化内置目录结构和dockerfile代码 2025-08-25 13:36:41 +08:00
augushong
00af0fa628 feat: 去掉控制器中的运行节点注册机制 2025-08-24 14:01:36 +08:00
augushong
dea794d058 feat: 增加call类型的定时任务; 2025-08-24 14:00:20 +08:00
augushong
5166049818 feat: 优化docker使用说方式和说明 2025-08-24 13:59:46 +08:00
augushong
4d2a264df1 feat: 优化节点信息存储 2025-08-23 23:24:45 +08:00
augushong
a9d1429000 feat: 增加内置节点注册任务;优化定时器代码结构; 2025-08-23 23:22:52 +08:00
augushong
716e56b8dc feat: 优化定时任务输出 2025-08-23 23:07:55 +08:00
augushong
4b1e28098f feat: 增加系统节点数据库迁移文件 2025-08-23 22:47:38 +08:00
augushong
b1a6b37ee0 feat: 增加主机节点表结构代码 2025-08-23 22:44:47 +08:00
augushong
46722e5a66 feat: 增加主机节点服务类 2025-08-23 22:30:33 +08:00
augushong
171c6cfca3 feat: 关闭tab时自动返回上一个页面 2025-08-23 12:34:26 +08:00
augushong
892fd2b712 feat: 增加打开标签时追加tab前缀标题;优化代码结构 2025-08-23 12:21:22 +08:00
augushong
75309f3b7a feat: 优化返回的tab定位 2025-08-21 23:07:25 +08:00
augushong
714ba48143 Merge branch 'full-page-curd' 2025-08-21 22:57:00 +08:00
augushong
9493134c56 feat: 优化返回效果; 2025-08-21 22:56:19 +08:00
augushong
5eb4d787c7 fix: 修复顶级页面打开tab的写法兼容性不好的问题 2025-08-21 22:56:08 +08:00
augushong
f0dc6d118b feat: 管理员编辑增加返回按钮 2025-08-21 22:40:34 +08:00
augushong
f2b2b057e2 feat: 回退页面时,如果页面不存在,则创建并从菜单读取标题 2025-08-21 22:37:04 +08:00
augushong
49a58206d8 feat: 完成新tab打开返回时初始化缺少的tab的情况 2025-08-21 22:21:08 +08:00
augushong
6aba138c10 feat: 完成编辑默认新tab打开并且自动刷新 2025-08-21 22:16:08 +08:00
augushong
d43c3d18ca feat: 完成添加的关闭自动刷新 2025-08-21 22:13:42 +08:00
augushong
578fa91d36 feat: 开始全页面提交 2025-08-15 23:16:13 +08:00
augushong
7b5a84bcc3 feat: 初步增加frankenphp构建二进制 2025-08-09 21:58:18 +08:00
augushong
4c79daf501 feat: 完善docker部署使用 2025-08-08 21:47:04 +08:00
augushong
ed8d9dc823 fix: 修复文件单位大小处理错误 2025-06-10 14:48:57 +08:00
augushong
a76ebe56f4 feat: 处理数字大小时支持mb、gb等写法 2025-06-10 14:33:25 +08:00
augushong
63a5cdecf9 fix: 修复自定义权限标识不生效的问题 2025-05-19 11:23:24 +08:00
augushong
2d78442dd6 fix: 修复引入地图组件错误 2025-05-12 23:32:54 +08:00
augushong
ed09374932 feat: 增加flow_url构件链接的方法,方便把当前GET参数带到新的链接中 2025-05-12 23:31:51 +08:00
augushong
c42b4b6e06 chore: 去掉对welcome的可选设置 2025-05-07 17:55:36 +08:00
augushong
3b3adb741a feat: welcome.html设置为可选文件 2025-05-07 17:53:52 +08:00
augushong
ec047ce3b7 feat: 增加select的选中处理 2025-05-07 17:51:17 +08:00
augushong
f403b86583 style: 去掉调试代码 2025-05-07 17:49:07 +08:00
augushong
79bce85300 feat: 表格选择器默认全屏打开 2025-05-07 17:47:13 +08:00
augushong
45d2a2307a feat: 控制器中将authService作为内部属性以便子类控制器访问 2025-05-07 17:45:56 +08:00
augushong
633738d2a1 fix: 修复列表没有展示姓名的问题 2025-05-07 17:40:35 +08:00
augushong
4d67358f82 feat: 引入地图引擎;引入htmx引擎 2025-05-07 17:37:12 +08:00
augushong
7be395f1b4 fix: 修复没有引入地图的错误 2025-05-07 17:35:16 +08:00
augushong
cc5a4e4502 chore: 发布新版本 2025-05-07 17:27:34 +08:00
augushong
29f248ad93 fix: 修复没有有效统计数据时仍然显示之前的统计信息的问题 2025-05-07 16:53:09 +08:00
augushong
7f4e1369cc feat: 增加地图搜索;增加地图表单字段重置; 2025-05-07 15:36:40 +08:00
augushong
d2e4acef05 chore: 发布新版本 2025-05-07 15:19:20 +08:00
augushong
d4dc430b8a fix: 修复地图查询接口返回类型错误 2025-05-07 15:18:40 +08:00
augushong
1868b75328 fix: 修复设置自定义form回调时未执行终止逻辑的问题,导致的登录密码错误时再次请求失败 2025-04-27 10:34:37 +08:00
augushong
df213daafe feat: 列表新增自动生成tab组件 2025-04-23 16:24:46 +08:00
augushong
e898462c4a fix: 修复列表搜索重置导致所有预设搜索值失效的问题; 2025-04-23 14:41:35 +08:00
augushong
1fdcd74956 fix: 支持设置为search:false时初始化时传入的搜索参数持久的保留在表单中,否则点击之后初始化的搜索项会丢失 2025-04-23 14:33:29 +08:00
augushong
691f9c2202 fix: 修复通过回车提交表单时能够重复提交的问题 2025-04-23 14:26:09 +08:00
augushong
09e31d5d75 feat: 视图增加$Controller对象,可以在视图层访问控制器方法; 2025-04-23 14:06:46 +08:00
augushong
a4cc6c44eb feat: 优化上传组件,绑定文件名输入框时不强制必须input,支持textarea等 2025-04-22 09:44:25 +08:00
augushong
78e53f3f31 feat: 表格增加行选择的禁用状态设置回调函数,可以动态设置当前行是否可选 2025-04-22 09:40:51 +08:00
augushong
5f1977e6ca feat: 优化assign方法,设置为隐藏数据后再次assign可以去除隐藏设置 2025-04-22 09:35:56 +08:00
augushong
38201de234 feat: 优化验证器逻辑 2025-04-22 09:32:58 +08:00
augushong
a078c14af7 feat: 将用户登录信息与普通缓存数据区分存储 2025-04-22 09:03:51 +08:00
augushong
c1042ce1c6 feat: 增加动态设置当前账户的权限功能;增加该功能演示效果 2025-04-21 15:54:07 +08:00
augushong
81860e8277 fix: 修复默认code值错误 2025-04-21 13:16:55 +08:00
augushong
ba60b534bb fix: 修复登录页报错问题 2025-04-21 12:08:10 +08:00
augushong
04c3bd8a06 feat: 增加ctype_numeric函数变相扩展验证器规则,支持验证是否是合理数字。 2025-04-21 09:08:47 +08:00
augushong
0a577cb321 feat: 新增地图不支持获取定位时使用ip定位 2025-04-18 09:23:55 +08:00
augushong
ed013e3039 feat: 增加说明提示 2025-04-16 09:56:02 +08:00
augushong
18ba49e1c5 refactor: 重构用户信息状态代码写法。 2025-04-14 19:58:41 +08:00
augushong
651efc5814 feat: 表格按钮判断支持字符串1,以方便直接在js中调用php的auth函数判断 2025-04-14 19:48:15 +08:00
augushong
010fd3f240 feat: 统一修改code默认值,0为正常,500为默认异常,其他数值异常 2025-04-14 19:46:46 +08:00
augushong
22a37d1bea refactor: 优化代码结构 2025-03-29 10:40:27 +08:00
augushong
389a5b3f3c feat: 完善生成说明文件的代码结构和生成效果 2025-03-29 10:35:18 +08:00
augushong
09f6e35793 fix: 修复生成发布说明包含了未打标签提交的问题 2025-03-29 09:59:05 +08:00
augushong
c1867d5759 chore: 发布新版本 2025-03-28 18:08:21 +08:00
augushong
bb355eb911 feat: 优化权限表格首列样式为不要换行 2025-03-28 17:17:12 +08:00
augushong
f2ef5e3a36 feat: 优化后台左上角logo标题的样式,存在图片时再增加标题的空隙 2025-03-28 15:17:46 +08:00
augushong
e51624d7b6 feat: 页面增加radio自动通过预设值设置,避免复杂的php或js渲染判断 2025-03-28 11:07:38 +08:00
augushong
f312519d37 feat: 优化更新逻辑设置think文件为必选文件 2025-03-28 11:04:30 +08:00
augushong
4935f06200 feat: 模型基类增加时间戳格式的通用处理方法 2025-03-28 11:02:45 +08:00
augushong
e97d38d9e4 feat: 优化tableData组件支持读取原元素的style属性 2025-03-28 10:55:57 +08:00
augushong
6e1659ebe5 fix: 修复地图重复渲染的问题 2025-03-27 14:14:55 +08:00
augushong
827d4fe8b6 feat: 优化错误信息提示不要自动关闭,需要用户点击空白区域关闭,方便用户仔细阅读和吸收弹框信息 2025-03-27 11:04:07 +08:00
augushong
cfe5b58d36 feat: 优化curd临时文件生成时输出内容效果,方便编辑器定位文件; 2025-03-25 09:41:55 +08:00
augushong
4adcc39eda feat: 完善curd生成控制器时,自动开启关联查询配置 2025-03-22 17:58:24 +08:00
augushong
103998d8ba fix: 修复视图范围类组件处理变量时没有兼容null值的问题 2025-03-22 17:11:14 +08:00
augushong
40697fe389 fix: 修复自动从url读取table搜索参数兼容性问题 2025-03-22 16:28:11 +08:00
augushong
ba04211778 feat: 新增默认从get中读取table的搜索参数 2025-03-22 16:21:17 +08:00
augushong
4b4393d67f feat: 新增地图选点组件 2025-03-22 15:59:15 +08:00
augushong
a7b9fda6f1 fix: 修复确认框取消时会自动关闭当前页面的问题 2025-03-21 16:53:20 +08:00
augushong
c370c99b6a feat: 优化tip样式 2025-03-21 15:50:10 +08:00
augushong
65e13e506a feat: 增加姓名字段;修改登录账号为可修改; 2025-03-21 14:19:04 +08:00
augushong
7baa427038 feat: 设置表格默认值为空,避免显示null和undefined 2025-03-21 14:13:17 +08:00
augushong
d85f98a869 feat: 生成表单时在添加页面默认读取get参数预设值 2025-03-21 13:20:45 +08:00
augushong
3f98cf0b5b fix: 修复接口模式不能退出的问题 2025-03-21 10:54:13 +08:00
augushong
6fe88b9663 feat: 修改生成发布说明的标题级别 2025-03-20 17:25:45 +08:00
augushong
379bf64cb6 refactor: 版本内容整理使用统一的类型配置 2025-03-20 17:19:33 +08:00
augushong
61860cb193 chore: 发布新版本 2025-03-20 16:59:36 +08:00
augushong
d3e85fa552 feat: 升级权限管理模式 2025-03-20 16:57:05 +08:00
augushong
1887733b32 feat: 发布新版本 2025-03-17 16:08:02 +08:00
augushong
2f0bd6bd26 fix: 修复登录状态监测错误 2025-03-17 16:06:51 +08:00
augushong
4602952a8a feat: 支持生成发布文件 2025-03-11 13:14:12 +08:00
augushong
f96fbd745a feat: 提交tag时支持markdown格式 2025-03-11 11:07:03 +08:00
augushong
304669dcc2 chore: 发布新版本 2025-03-11 11:00:44 +08:00
augushong
3ab1fa500a feat: 版本说明支持生成markdown文件 2025-03-11 10:59:19 +08:00
augushong
4aaa1c08c4 feat: 版本命令支持生成说明文件 2025-03-11 10:57:10 +08:00
augushong
95c152b64a feat: 优化首页的最新修改样式 2025-03-11 10:56:20 +08:00
augushong
dadce88936 feat: 实现token认证机制 2025-03-11 10:30:09 +08:00
augushong
e33863af46 refactor: 优化后台兼容接口请求的判断 2025-03-10 18:17:28 +08:00
augushong
7f92b0e07a feat: 实现后台请求兼容接口的模式 2025-03-10 18:06:51 +08:00
augushong
afd5f6b1dd feat: view支持读取assign的数据 2025-03-10 18:05:57 +08:00
augushong
1160774f89 feat: 完善demo机制;实现tab的demo和var标签的demo; 2025-03-05 20:30:16 +08:00
augushong
0f4c0e5747 feat: 增加var标签 2025-03-05 20:29:32 +08:00
augushong
efe5062a84 feat: 增加通用的扩展字符返回内容处理; 2025-03-05 20:26:28 +08:00
augushong
e08c51725b feat: 当url的hash对应的tab存在时,直接打开; 2025-03-05 20:26:04 +08:00
augushong
ab30204694 设置php时区 2024-11-15 16:55:00 +08:00
augushong
118e27c2b1 增加注释 2024-11-14 14:11:26 +08:00
augushong
7cabf4a3b5 tableData增加打开页面参数; 2024-11-14 13:56:53 +08:00
augushong
0d2fea7fb5 databrage增加强制转对象参数; 2024-11-14 13:55:58 +08:00
augushong
99df8d06f6 修改图片默认不搜索 2024-11-14 13:24:56 +08:00
augushong
d91e73d1d3 切换新版layui;增加表格多模板机制;增加local操作方法;增加页面记忆操作方法;删除多余代码; 2024-11-12 17:45:29 +08:00
augushong
684153c5fc 兼容新版layui 2024-11-12 14:25:04 +08:00
augushong
dcacb0d4f0 引入新版layui 2024-11-12 14:12:02 +08:00
augushong
cbcb839cb9 开始多模版 2024-11-12 13:57:50 +08:00
augushong
3baaf4747d 优化手机端表现 2024-11-07 14:32:34 +08:00
augushong
e2314b2553 发布新版本 2024-11-05 21:33:17 +08:00
augushong
0579a1498f 设置系统配置的文件字段 2024-11-01 14:18:18 +08:00
augushong
3f48492089 增加数据库的文件字段数据迁移 2024-10-25 20:47:21 +08:00
augushong
4cf1c95789 tableData组件多选时默认在两侧拼接逗号 2024-10-24 16:51:32 +08:00
augushong
9dd511163c 生成临时文件增加结尾空白 2024-10-23 13:31:31 +08:00
augushong
b3684b7521 优化curd临时生成对应到正式目录,方便IDE可直接定位打开 2024-10-22 15:22:59 +08:00
augushong
ff8f2080d2 视图层增加./的include的引入方法 2024-10-21 16:44:58 +08:00
augushong
17aa8dc8bd db增加whereif操作 2024-10-21 15:34:00 +08:00
augushong
044d28bb93 修改配置文件设置文件大小优化 2024-10-21 11:20:25 +08:00
augushong
6d58f6b70c 修改富文本样式错误 2024-10-21 10:48:39 +08:00
augushong
75526bb7c9 替换富文本编辑器为wangeditor 2024-10-21 10:41:11 +08:00
augushong
52abfecddd 修复复制事件的暂停错误 2024-10-17 11:40:18 +08:00
augushong
5cb3ba8205 增加最后一次登录时间格式化 2024-10-17 10:06:13 +08:00
augushong
4d663a154b 增加记录最后一次登录时间; 2024-10-17 09:59:18 +08:00
augushong
5146823835 发布新版本 2024-10-17 08:50:37 +08:00
augushong
4db4bc6f20 修改超级管理员判断方式 2024-10-17 08:48:08 +08:00
augushong
0196920236 修复自动关闭弹框错误 2024-10-16 10:25:40 +08:00
augushong
f5a0f82efd 给积累增加工具方法 2024-10-15 18:42:20 +08:00
augushong
58ea77bca1 修复更新错误 2024-10-15 18:09:55 +08:00
augushong
a92bf7bee3 发布新版本 2024-10-15 18:07:03 +08:00
augushong
d0add06ca0 新增LoadMigrationFiles事件,支持扩展提供的数据库迁移文件 2024-10-15 18:06:25 +08:00
augushong
c79ad6b986 优化粘贴提示;优化菜单导入清空缓存 2024-10-15 16:19:38 +08:00
augushong
008e781d6b 增加菜单到导出和导入;增加粘贴全局操作;优化表单错误表现; 2024-10-15 16:06:36 +08:00
augushong
b04321e380 生成数据库迁移文件支持生成更新的迁移文件 2024-10-15 13:49:23 +08:00
665 changed files with 61985 additions and 9950 deletions

34
.agents/PROJECT.md Normal file
View File

@@ -0,0 +1,34 @@
# 项目业务总览PROJECT
> 本文件记录项目的业务上下文,帮助智能体快速理解"这个项目在做什么"。
> 框架使用规则见根目录 `AGENTS.md`。
## 项目定位
(待填写。用一两句话描述项目是做什么的。)
## 核心业务模块
(待填写。列出主要业务模块及其对应目录。)
## 业务约束
(待填写。框架不涉及但业务必须遵守的规则。)
## 技术选型与外部依赖
(待填写。项目中使用的特殊技术或外部服务。)
## 开发偏好
(待填写。团队或开发者的个性化偏好。)
## 增量规则记录
(待填写。开发过程中补充的业务规则、团队偏好与临时约束。规则应可执行、可复现、可验证。
如需独立规则文件,参考技能:[ulthon-rules-manager](skills/ulthon-rules-manager/SKILL.md)
## 规则索引
(待填写。使用者业务规则索引,仅记录 `project-` 前缀的规则。
框架内置规则(`ulthon-` 前缀)见 `AGENTS.md` 的「零散规则」章节。)

35
.agents/rules/.README Normal file
View File

@@ -0,0 +1,35 @@
# 零散规则目录
本目录存放按模块/场景拆分的独立规则文件,避免全局规则膨胀。
## 命名约定
- `ulthon-` 前缀:框架内置规则,框架作者维护
- `project-` 前缀:使用者业务规则,开发者维护
## 文件格式
每个规则文件包含以下章节:
```markdown
# 规则名称
> 来源框架内置ulthon-或使用者业务project-
> 作用域:适用的模块/文件/场景
> 触发条件:智能体何时应加载此规则
## 规则内容
(具体的可执行规则)
## 相关文件 / 相关数据表 / 相关命令
(关联的代码路径、配置路径等)
```
## 索引
- 框架级规则索引:根目录 `AGENTS.md` 的「零散规则」章节
- 全量规则索引:`.agents/PROJECT.md` 的「规则索引」章节
## 管理技能
新增/维护规则时,参考技能:[ulthon-rules-manager](../skills/ulthon-rules-manager/SKILL.md)

0
.agents/rules/.gitkeep Normal file
View File

View File

@@ -0,0 +1,46 @@
# ThinkPHP 8 控制器 URL 访问规则
> 来源框架内置ulthon-
> 作用域所有涉及控制器路由、URL 构造的场景
> 触发条件:编写/调试控制器路由、URL 报错、配置路由时加载
## 大驼峰命名转换规则
ThinkPHP 8 默认开启 URL 自动转换(`url_convert`。大驼峰命名CamelCase的控制器和方法在 URL 中转换为小写+下划线snake_case
- **控制器类名**`UserGroup` -> URL`user_group`
- **操作方法名**`public function editInfo()` -> URL`edit_info`
示例:控制器 `app\admin\controller\UserGroup.php` 中的 `editInfo` 方法:
访问路径:`/admin/user_group/edit_info`
## 多级控制器访问规则
控制器存放在子目录中时URL 使用 `.`(点号)连接目录名和控制器名。
- **目录结构**`app/admin/controller/system/Config.php`
- **访问路径**`/admin/system.config/index`
注意:
- 子目录名建议使用全小写
- 如果子目录名也是大驼峰,同样遵循转换规则
## 混合使用示例
| 控制器文件路径 | 类名 | 方法名 | 对应 URL 路径 |
|---|---|---|---|
| `controller/Index.php` | `Index` | `index` | `/admin/index/index` |
| `controller/UserGroup.php` | `UserGroup` | `add` | `/admin/user_group/add` |
| `controller/system/Admin.php` | `Admin` | `login` | `/admin/system.admin/login` |
| `controller/mall/GoodsCate.php` | `GoodsCate` | `getList` | `/admin/mall.goods_cate/get_list` |
## 常见问题排查
- **404 错误**:检查是否漏掉了多级控制器的 `.` 分隔符
- **大小写敏感**URL 无法识别下划线时,检查 `config/route.php``url_convert` 是否为 `false`
- **多应用影响**多应用模式下URL 第一段是应用名(如 `admin`),后续才是控制器和方法
## 开发者建议
- 代码中使用大驼峰命名,前端请求/模板链接中明确指向转换后的路径,或使用系统助手函数自动生成
- 复杂 URL 建议在 `route/*.php` 中手动定义路由规则

View File

@@ -0,0 +1,76 @@
# 表结构设计规范
## 特殊字段
| 字段名 | 用途 | 说明 |
|--------|------|------|
| `status` | 默认开关字段 | - |
| `create_time` | 创建时间 | 尽量 NOT NULL默认值 0TP 会自动填充) |
| `update_time` | 更新时间 | 尽量 NOT NULL默认值 0TP 会自动填充) |
| `delete_time` | 删除时间 | 尽量 NOT NULL默认值 0CURD 默认开启软删除,删除标志为 0 |
## 字段后缀约定
以特殊字符结尾的字段会自动识别为对应类型:
| 后缀 | 类型 |
|------|------|
| `image``logo``photo``icon` | 单图片 |
| `images``photos``icons` | 多图片 |
| `file` | 单文件 |
| `files` | 多文件 |
## 注释语法
字段注释支持通过特殊格式定义表单类型和数据集:
```
名称 {类型} (数据集)
```
- **类型**:用 `{}` 包起来,例如 `{radio}`
- **数据集**:用 `()` 包起来,例如 `(1:男, 2:女, 0:未知)`
- 数据集索引可以用数字或英文单词,不要使用其他字符和空格
示例:`性别 {radio} (1:男, 2:女, 0:未知)`
## 类型大全
| 类型 | 说明 | 是否需要数据集 | 注释案例 |
|------|------|----------------|----------|
| text | 普通文本框 | 否 | `店铺名称 {text}`(一般不需要写 text |
| image | 单图片 | 否 | `店铺logo {image}` |
| images | 多图片 | 否 | `店铺环境 {images}`,分隔符默认为竖线 |
| file | 单文件 | 否 | `演示资料 {file}` |
| files | 多文件 | 否 | `演示资料 {files}`,默认分隔符为竖线 |
| date | 时间组件 | 是 | `生日 {date} (datetime)` |
| editor | 富文本 | 否 | `店铺详情 {editor}` |
| textarea | 多行文本 | 否 | `店铺简介 {textarea}` |
| select | 下拉选择 | 是 | `版本 {select} (trial:免费版,office:正式版)` |
| switch | 开关组件 | 是 | `状态 {switch} (0:关闭,1:开启)` |
| checkbox | 多选框 | 是 | `功能权限 {checkbox} (mall:商城,blog:博客)` |
| radio | 单选框 | 是 | `状态 {radio} (0:未审核,1:审核中)` |
| relation | 关联表 | 是(格式见下) | `标签 {relation} (table:tag,relationBindSelect:title)` |
| table | 表格选择器 | 是(格式见下) | `商品标签 {table} (table:mall_tag,type:checkbox,valueField:id,fieldName:title)` |
| city | 城市选择器 | 是 | `仓库 {city} (level:city)` |
## 关联表注释参数
| 参数 | 说明 | 备注 |
|------|------|------|
| `table` | 关联表名 | 必填 |
| `primaryKey` | 关联表主键 | 非必填 |
| `modelFilename` | 模型文件 | 非必填,不建议指定,可自动生成 |
| `onlyFileds` | 列表页显示字段 | 可指定,用竖线分割 |
| `relationBindSelect` | 表单下拉关联字段 | 必填 |
完整写法示例:
```
标签 {relation} (table:tag,relationBindSelect:title,primaryKey:id,onlyFileds:title|titme_image|username|phone)
```
## 其他细节
- 设计时尽量设置默认值。例如 `status` 默认值为 1添加数据时表单会自动将 radio 选中"1:启用"
- 分隔符默认为竖线 `|`

View File

@@ -0,0 +1,23 @@
# 部署环境与命令执行
> 来源框架内置ulthon-
> 作用域:部署配置、命令执行环境判断
> 触发条件:执行 php think 命令、配置部署模式、切换运行环境时加载
## 部署栈模式
- `source/stack/` 为模式文件统一目录(含 `default/` 与各模式目录)
- `default/` 必须与代码库默认行为一致
- 默认行为相关文件变更时需同步更新 `source/stack/default/` 对应文件
## 运行模式判断
执行 `php think` 命令前,必须先判断当前运行模式。
**判断方式**:检查仓库根目录是否存在 `docker-compose.yaml`
- **存在**Docker 模式。宿主机可能没有 PHP不能依赖 `php think` 来检测。所有 `php think` 命令前缀改为 `docker compose exec ulthon_admin`
- 示例:`docker compose exec ulthon_admin php think tools:http:call`
- **不存在**:宿主机模式。直接执行 `php think`
也可读取 `source/stack/stack.json` 了解所有可用模式及其说明。

View File

@@ -0,0 +1,13 @@
# 命名规范
## 目录命名
- 一个单词的目录用小写,例如 `service/`
- 多个单词的目录用大驼峰,例如 `AdminService/``UserService/`
## PHP 文件命名
- `app/common` 目录下已有单独的规范说明(见 `ulthon-source-directory.md`
- 其他各应用目录下:
- **service 模块**需要写后缀,例如 `AdminService`,而不是 `admin`
- **除了 service 模块**,其他文件不需要写后缀,不论是 controller、model、traits、config、middleware 等

View File

@@ -0,0 +1,25 @@
# source/ 目录约定
> 来源框架内置ulthon-
> 作用域:所有涉及 source/ 目录的操作
> 触发条件:新增子项目、存放多端代码、管理部署配套文件时加载
## 定位
`source/` 是主工程之外的配套内容统一目录,不影响当前主工程运行与发布。包括:
- 多端代码(客户端、大屏端等)
- 子项目工程(可为 PHP 或其他技术栈)
- 项目资料、附件
- 部署配套文件
## 目录约定
- 各子目录(客户端、大屏端、各类子项目等)独立管理
- 若子目录下存在 `AGENTS.md`,则该子工程规则以该文件为准
- 目录约定与安全要求见 `source/README.md`
## 安全要求
- 禁止提交构建产物
- 禁止提交依赖目录node_modules、vendor 等)

View File

@@ -0,0 +1,33 @@
# 定时任务多节点协调
> 来源框架内置ulthon-
> 作用域定时任务相关模块TimerConfig、TimerLog、Host
> 触发条件:涉及定时任务开发、多节点部署、主节点选举等场景时加载
## 规则内容
- 多节点定时任务协调以数据库为主协调中心
- run_type 调度模式auto / main / all / manual
- 支持主节点自动选举与手动切换
- 执行日志必须记录,支持查看与清理
- 定时任务配置管理通过 UI 管理
## 相关数据表
- ul_system_timer_config
- ul_system_timer_log
- ul_system_host含 is_master 字段)
## 相关命令
- `php think admin:timer:log:clean [--days=30]` — 清理过期执行日志
## 相关管理页面
- 定时器配置管理:/admin/system.timer_config/index
- 定时器执行日志:/admin/system.timer_log/index
- 主机列表增强(主节点标识、切换主节点)
## 相关技能
- [ulthon-timer](../skills/ulthon-timer/SKILL.md)

View File

@@ -0,0 +1,86 @@
---
name: "ulthon-admin-menu-cli"
description: "解释并指导后台菜单system_menu与 admin:menu:* 命令的使用方式。需要导出菜单、通过命令行创建/更新/删除菜单、或排查菜单字段映射pid/href/auth_node时调用。"
---
# 菜单管理admin:menu:* CLI
## 何时调用
- 需要导出当前系统菜单数据,用于备份/迁移/对比。
- 需要在不进入后台页面的情况下,通过命令行创建/更新/删除菜单。
- 需要排查菜单字段映射:`pid`/`href`/`auth_node` 与命令参数 `parent-id`/`path`/`node` 的对应关系。
- 需要脚本化输出JSON对接自动化运维/测试(仅导出命令支持)。
## 命令清单
### 1) 导出菜单
```bash
php think admin:menu:export
```
常用参数:
- `--format=json`JSON 输出(含 count/exported_at
- `--output=<文件路径>`把导出的菜单数组写入文件JSON
示例:
```bash
php think admin:menu:export --format=json
php think admin:menu:export --output=runtime/agent/menu_export.json
php think admin:menu:export --output=runtime/agent/menu_export.json --format=json
```
### 2) 创建菜单(会写入数据库)
```bash
php think admin:menu:create --title="测试菜单" --path="demo.index/index" --icon="fa fa-list" --parent-id=0 --sort=100
```
### 3) 更新菜单(会写入数据库)
```bash
php think admin:menu:update --id=123 --title="新标题" --path="demo.index/index" --icon="fa fa-list" --parent-id=0 --sort=100
```
说明:
- `--id` 必填;其余参数不传则不更新该字段。
- `--path` 更新的是数据库字段 `href`
- `--parent-id` 更新的是数据库字段 `pid`
### 4) 删除菜单(会写入数据库,软删除)
```bash
php think admin:menu:delete --id=123
```
说明:
- 若存在子菜单会拒绝删除(需先删除子菜单)。
## 字段映射速查
- `--parent-id``system_menu.pid`
- `--path``system_menu.href`
- `--node``system_menu.auth_node`
## 典型工作流
### 工作流 A备份菜单到文件
```bash
php think admin:menu:export --output=runtime/agent/menu_export.json
```
### 工作流 B创建 → 更新 → 删除(用于验证命令链路)
```bash
php think admin:menu:create --title="CLI测试菜单" --path="test.cli/index" --parent-id=0
php think admin:menu:update --id=<上一步返回的id> --title="CLI测试菜单_改"
php think admin:menu:delete --id=<上一步返回的id>
```
## 注意事项
- `create/update/delete` 会实际修改数据库,建议在测试环境或确认无风险后执行。
- 导出默认过滤 `delete_time=0`(只导出未删除菜单)。

View File

@@ -0,0 +1,28 @@
---
name: "ulthon-auth-session-token"
description: "解释并指导 Session+Token 登录认证的使用方式。需要实现接口鉴权或联调移动端请求时调用。"
---
# 登录认证Session + Token
## 何时调用
- 编写/联调需要登录态的接口。
- 需要在非浏览器环境(移动端、小程序、跨域脚本)调用后台接口。
## 机制概览
- Session主要用于浏览器环境依赖 Cookie。
- Token用于接口与无 Cookie 场景,通过 Header 传递完成认证。
## 使用方式
1. 登录成功后,接口会返回 `token` 数据。
2. 后续请求在 Header 携带:
```text
Authorization: Bearer <token>
```
3. 系统会自动识别并完成身份认证。

View File

@@ -0,0 +1,121 @@
---
name: "ulthon-base-app-architecture"
description: "详细说明了 Base/App 双层架构的设计理念、三层结构、身份职责、目录映射、扩展模式及调用红线,帮助开发者理解框架结构并避免误操作。"
---
# Base/App 双层架构
本架构旨在解决**框架内核升级**与**业务代码定制**之间的矛盾。请根据您的身份阅读对应部分。
## 角色定位
| 你的身份 | 你的主要工作 | 请关注章节 |
| :--- | :--- | :--- |
| **框架使用者** | 开发具体业务功能、使用框架内置能力 | 一、我是框架使用者(做业务) |
| **框架作者** | 维护框架内核、修复 Bug、新增通用组件 | 二、我是框架作者(修内核) |
---
## 一、我是框架使用者(做业务)
### 1. 你的地盘与禁区
- **自由开发区**:除了 `extend/base/` 之外的所有目录。虽然业务代码通常推荐放在 `app/` 下,但你完全可以在项目中自由发挥。
- **绝对禁区**`extend/base/` 目录。**严禁修改**这里的文件,因为 `php think admin:update` 会覆盖它。
### 2. 开发场景指南
#### 场景 A新增全新的业务功能
直接在 `app/` 下创建控制器、模型或服务即可。不需要关心 Base 层,也不需要继承任何 Base 类(除非你需要利用框架基类的功能)。
- 公共代码放在 `app/common/`(工具类、基础类等)
- 命令类放在 `app/common/command/`(不放在 `app/admin/command/`
- 新写业务能力:不需要、也不应该在 `extend/base/` 新建任何 `*Base.php`,这不是业务开发的默认模式
#### 场景 B修改/扩展框架内置功能
如果你对框架默认的某个功能如登录逻辑、CURD流程不满意请按以下步骤操作
1. **定位**:找到该功能对应的 `app/` 入口类(例如 `app/admin/model/SystemAdmin.php`)。
2. **重写**:在该类中重写你需要修改的方法。
- 保持方法签名(参数、返回值)一致。
- 可以使用 `parent::method()` 复用父类逻辑,或复制父类代码后自行实现。
3. **生效**:系统会自动调用你的类,而不是底层的 Base 类。
#### 常见误区
- 误区:新增业务功能时,也要先在 `extend/base/` 建一个 `*Base` 再在 `app/` 继承
结论:不需要;这是框架内核维护模式,不是业务开发默认模式
- 误区:看到框架存在 Base/App 双层机制,就认为所有类都必须走"入口类"
结论:入口类主要用于"覆盖框架默认实现",纯业务类可以直接使用,不需要额外包装
### 3. 更新机制与保障
执行 `php think admin:update` 时,系统会检查框架所有文件的更新:
- **Base 层extend/base/**:默认为**覆盖更新**Always Yes因为这里是内核。
- **App 层app/)及其他**:默认为**保护模式**Default No。命令会提示即将变动的文件列表。
- **推荐操作**:一般选择**跳过**,随后根据实际业务代码与上游更新进行对比,手动合并差异。
- **替代操作**:也可选择**覆盖**,更新后通过 Git 查看差异,并恢复不应变动的业务代码。
---
## 二、我是框架作者(修内核)
### 1. 你的地盘与职责
- **你的地盘**`extend/base/` 目录。通用逻辑、基类代码写在这里。
- **你的义务**:每在 Base 层新增一个类(如 `UserBase`**必须**在 `app/` 层提供对应的入口类(如 `User`),并让入口类继承 Base 类。
### 2. 开发关键原则(依赖倒置)
为了保证使用者的重写能生效,你必须遵守以下**铁律**
> **严禁直接调用 Base 类**
> 无论在何处,禁止写 `new UserBase()` 或 `UserBase::find()`。
> **必须调用 App 入口类**
> 必须写 `new \app\...\User()`。
**为什么?**
如果代码直接调用了 `UserBase`,那么使用者在 `app/` 下重写的 `User` 类就变成了"摆设",无法拦截逻辑。只有调用 `app/` 下的子类,多态机制才能生效,使用者才有机会改变行为。
### 3. 文件组织规范
- **类文件**:以 `*Base.php` 结尾,放在 `extend/base/`。路径和名称与 `app/` 层一一对应。
- **辅助函数**:放在 `extend/base/helper.php`,通过 `app/common.php` 引入。
- **初始化数据**:放在 `extend/base/adminInitData/`(带 @internal-framework 注解标记)。
- **版本更新代码**:放在 `extend/base/adminUpdateCodeData/`(带 @internal-framework 注解标记)。
- 静态文件/模板/配置支持分层加载:优先加载 `app/`,不存在时再回落到框架默认实现(例如 `app_file_path`)。
### 4. 核心层维护原则
- 稳定性优先:保证向下兼容
- 通用性优先:不引入具体业务逻辑
---
## 三、架构参考资料
### 1. 三层结构图解
```
ThinkPHP 框架层(底层基础设施)
|
| 继承/扩展
ulthon_admin 内核层extend/base/,框架作者维护)
|
| 继承/覆盖(依赖倒置:内核只调用 App 层入口)
App 应用层app/,框架使用者维护)
```
### 2. 常见目录映射
| Base 层(内核实现) | App 层(调用入口) |
| :--- | :--- |
| `extend/base/admin/controller/system/AdminBase.php` | `app/admin/controller/system/Admin.php` |
| `extend/base/admin/model/SystemAdminBase.php` | `app/admin/model/SystemAdmin.php` |
| `extend/base/common/service/SmsBase.php` | `app/common/service/Sms.php` |
| `extend/base/common/command/CurdBase.php` | `app/common/command/Curd.php` |
| `extend/base/common/command/admin/role/AdminRoleCreateBase.php` | `app/common/command/admin/role/AdminRoleCreate.php` |

View File

@@ -0,0 +1,35 @@
---
name: "ulthon-db-tools-debug"
description: "封装 tools:db 命令的使用方法。需要快速查询/执行 SQL 或查看表信息进行调试时调用。"
---
# 内置数据库调试命令tools:db
## 何时调用
- 需要快速查看数据、验证 SQL、检查表结构字段/索引/行数)用于排错。
- 需要在不写临时代码的情况下做一次性查询或数据检查。
## 必须注意
- 这些命令用于“调试数据”,不要用来“设计表结构”。
- 如果为了排错临时改了表结构或数据,需要在任务结束前确保影响可控并记录变更点。
## 常用命令
```bash
php think tools:db:query
php think tools:db:execute
php think tools:db:table
php think tools:db:count
php think tools:db:info
php think tools:db:desc
```
通过 `--help` 查看每个命令参数说明。
## 使用建议
- 优先用 `query/desc/info/table/count` 做只读检查。
- 需要变更数据时再用 `execute`,并尽量将变更限制在最小范围。

View File

@@ -0,0 +1,48 @@
---
name: "ulthon-page-api-dual-mode"
description: "指导控制器实现“页面/接口同体”。需要同一路由同时返回 HTML 与 JSON 时调用。"
---
# 页面 / 接口同体Controller Dual Mode
## 何时调用
- 希望同一个控制器方法既能渲染页面HTML也能作为接口返回 JSON。
- 需要让现有页面接口在移动端/脚本调用时返回结构化数据。
## 触发与认证
- 触发:请求头 `Accept` 包含 `application/json`(框架使用 `request()->isJson()` 判断)。
- 认证Header 传 `Authorization: Bearer <tokenContent>`
## 实现要点
- 控制器方法必须调用 `$this->fetch()`,不要使用 `View::fetch()`
- 无论使用 `$this->assign()` 还是 `View::assign()`,其数据都会被转换为 JSON 返回。
- 若不希望某个 assign 字段出现在 JSON 中,可使用:
```php
$this->assign('name', 'value', -1);
```
## 特殊行为
- 这里存在两种“JSON 语义”,不要混淆:
- **接口模式API**:只需要 `Accept: application/json`。典型用法是 `index` 的表格分页数据、以及所有 `POST` 提交的 success/error JSON 返回。
- **页面数据模式Page Data**:用于“拿页面 assign 的数据”(例如表单的下拉选项、默认值等),需要在 `Accept: application/json` 的基础上追加 `get_page_data=1`
- `get_page_data=1` 会强制让 `request()->isAjax()` 返回 false从而避免 `index` 这类方法走“Ajax 分页数据分支”,转而执行 `$this->fetch()`;随后 `$this->fetch()` 会把 `View::fetchData()`(即 assign 的变量)打包成 `json_message()` 返回。
- 当 URL 带 `get_page_data=1` 但请求头不含 `Accept: application/json` 时,框架会直接返回 JSON 错误提示,避免“看起来参数写了但返回了 HTML”的误解。
- 仅在控制器模式Controller Mode生效路由模式Route Mode不生效。
## 常见例子
- 获取列表分页数据API只加 `Accept: application/json`,不要带 `get_page_data`
- `GET /admin/system.admin/index`
- 获取页面 assign 数据Page Data`Accept: application/json` + `get_page_data=1`
- `GET /admin/system.menu/add?get_page_data=1`
- `GET /admin/system.admin/add?get_page_data=1`
## 命令行联调建议
- `php think tools:http:call --app=admin --controller=system.admin --action=index`:默认按 API 语义调用(不再自动追加 `get_page_data=1`
- `php think tools:http:call --app=admin --controller=system.admin --action=add --page-data`:获取该页面的 assign 数据(等价于追加 `get_page_data=1`

View File

@@ -0,0 +1,106 @@
---
name: "ulthon-permission-cli"
description: "解释并指导 RBAC 权限体系与相关 CLI 命令admin:role:* / admin:user:role:* / admin:permission:*)。用于查看节点、管理角色权限、给用户分配角色、排查权限问题。"
---
# 权限与角色管理RBAC CLI
## 何时调用
- 需要查看系统当前有哪些权限节点。
- 需要创建/查看/删除角色,或查看角色详情。
- 需要为角色分配/撤回权限节点。
- 需要给指定用户分配/撤回角色。
- 需要排查“有菜单但无权限 / 有权限但仍被拦截 / 节点名称不一致”等权限问题。
## 概念速览
- 权限节点:基于控制器/方法注解生成的权限标识(用于授权与鉴权)。
- 角色:权限节点的集合(`SystemAuth`)。
- 用户:后台用户(`SystemAdmin`),通过 `auth_ids`(逗号分隔)绑定角色 ID 列表。
- 角色-节点:`SystemAuthNode` 记录 `auth_id + node` 的多对多关系。
- 鉴权入口:后台请求进入后会根据“当前节点”进行权限检查(未授权则拦截)。
- 输出说明:权限相关命令默认以文本输出为主;如命令涉及交互确认,可使用 `--force-force``-ff` 跳过确认。
## 常用命令
### 1) 查看权限节点
```bash
php think admin:permission:nodes
```
常用参数(如需):
- `--module=<模块>`:按模块过滤
- `--level=1|2`按级别过滤1=控制器2=方法)
### 2) 角色管理
```bash
php think admin:role:list
php think admin:role:info --role-id=12
php think admin:role:create --title="测试角色" --remark="测试用"
php think admin:role:delete --role-id=12
```
要点:
- 删除角色会检查该角色是否仍被用户分配;若仍有用户绑定,则删除会失败。
### 3) 角色权限管理
```bash
php think admin:role:permission:list --role-id=12
php think admin:role:permission:assign --role-id=12 --nodes="system.admin/index,system.admin/add"
php think admin:role:permission:revoke --role-id=12 --nodes="system.admin/add"
```
要点:
- `--nodes` 支持逗号分隔批量操作。
- 建议先用 `admin:permission:nodes` 确认节点名称完全一致后再分配到角色。
### 4) 用户角色管理
```bash
php think admin:user:role:list --user-id=1
php think admin:user:role:assign --user-id=1 --role-ids="12,13"
php think admin:user:role:revoke --user-id=1 --role-ids="13"
```
### 5) 透视查询用户当前权限
```bash
php think admin:permission:user --user-id=1
```
## 典型工作流
### 工作流 A新增/调整注解后,核对节点并授权
1. 在控制器/方法上新增或调整权限注解(节点名称、标题、模块等)。
2. 执行 `admin:permission:nodes`,确认节点列表中已出现新节点且名称符合预期。
3. 确定要使用的角色(可通过 `admin:role:list` / `admin:role:info` 查看;没有合适角色就用 `admin:role:create` 新建)。
4. 执行 `admin:role:permission:assign` 将新节点分配给角色。
5. 执行 `admin:user:role:assign` 将角色分配给用户。
6. 执行 `admin:permission:user` 透视确认该用户已拥有节点。
### 工作流 B排查“明明有权限但仍被拦截”
1. 执行 `admin:permission:user --user-id=<id>`,确认用户是否真的拥有当前节点。
2. 执行 `admin:permission:nodes`,确认“当前被拦截节点名”是否存在、是否有大小写/分隔符差异。
3. 如果用户通过角色获得权限,执行 `admin:role:permission:list --role-id=<roleId>` 核对角色是否包含该节点。
4. 检查后台配置中是否存在免鉴权规则(例如 no_login/no_auth 的控制器/节点白名单)。
## 变更说明(重要)
- 旧命令 `admin:permission:assign` / `admin:permission:revoke` 已移除。
- “按用户直接增删节点”的需求,应通过“角色”承接:
- 只影响单个用户:创建专用角色 → 给该角色分配节点 → 将角色分配给该用户。
- 影响一批用户:维护共享角色 → 给该角色增删节点 → 所有拥有该角色的用户一起生效。
## 常见坑位
- 节点名不一致:注解里写的节点名与实际鉴权使用的当前节点不一致,导致分配了权限但无法命中。
- 只改了菜单没改权限:菜单能看到不代表有权限访问,权限检查以节点为准。
- 缓存/环境差异:在不同环境中节点生成来源不一致时,先以 `admin:permission:nodes` 的输出为准核对。
- 修改共享角色影响面过大:撤回/新增角色节点会影响所有拥有该角色的用户。
- 删除角色失败:仍有用户绑定该角色,先用 `admin:user:role:revoke` 撤回后再删除。

View File

@@ -0,0 +1,142 @@
---
name: "ulthon-rules-manager"
description: "零散规则管理技能。指导智能体在 `.agents/rules/` 目录下新增、维护模块级/场景级规则文件,并同步更新 AGENTS.md 与 PROJECT.md 的规则索引。触发词包括"记录规则"、"新增规则"、"写条规则"、"这个模块有约束"、"记录约束"等。"
---
# 零散规则管理Rules Manager
## 核心概念
**零散规则Rules** 是模块级/场景级的专属知识,不适合放在全局 `AGENTS.md` 中展开。存放在 `.agents/rules/` 目录下,每条规则一个独立文件,通过索引被智能体发现和引用。
Rules 可包含以下类型的内容:
- **约束**:不能怎么做、必须怎么做(如命名约定、分层铁律)
- **约定**:惯例、默认行为(如目录结构、文件配对)
- **设计决策**:某个功能的设计方案与背景(如多节点协调方案、认证架构选型)
## Rules 与 Skills 的边界
| 维度 | Rule规则 | Skill技能 |
|------|-------------|---------------|
| 核心问题 | "是什么""不能做什么""为什么这样设计" | "怎么做""一步步如何完成" |
| 知识形态 | 静态声明A 对应 B、禁止 C | 动态流程(第 1 步...第 2 步... |
| 触发时机 | 涉及某模块时需先了解其规则 | 需要执行某操作时按步骤调用 |
| 典型例子 | URL 映射规则、目录约定、多节点设计 | CURD 生成流程、定时任务配置、菜单创建 |
**判断方法**:如果内容主要是"告知性"的(让智能体知道某个事实/约束/设计),放 Rule。如果内容主要是"操作性"的(让智能体按步骤完成某件事),放 Skill。
**灰色地带处理**:部分内容混合了规则和操作(如 Base/App 架构文档既有铁律又有场景指南),此时保留为 Skill因为其核心价值在"指导如何操作"。
## 目录结构
```
.agents/rules/
├── .README # 目录说明与格式规范
├── ulthon-timer-multi-node.md # 框架内置规则示例
├── project-xxx.md # 使用者业务规则示例
└── ...
```
## 命名约定
| 前缀 | 含义 | 维护者 | 约束 |
|------|------|--------|------|
| `ulthon-` | 框架内置规则 | 框架作者 | 随框架更新分发 |
| `project-` | 使用者业务规则 | 开发者 | 不被框架更新影响 |
文件名使用小写英文 + 短横线,语义清晰,例如:
- `ulthon-timer-multi-node.md`
- `ulthon-upload-storage.md`
- `project-order-stock-lock.md`
- `project-wechat-auth.md`
## 规则文件格式模板
```markdown
# 规则名称
> 来源框架内置ulthon-)或 使用者业务project-
> 作用域:适用的模块/文件/场景
> 触发条件:智能体何时应加载此规则
## 规则内容
(具体的可执行规则,每条应可验证)
## 相关文件
(关联的代码路径、配置路径等)
## 相关技能
(如有对应的 Skill用相对链接引用
```
必填章节:`规则内容`
按需章节:`相关文件``相关数据表``相关命令``相关技能`
## 新增规则流程
### 1. 判断是否需要新增规则
满足以下条件之一时,应建议新增规则:
- 某个模块有独特的开发约束,不适合写在全局 `AGENTS.md`
- 开发过程中发现了可复用的约定,值得记录
- 用户明确要求"记录这条规则"/"这个模块有约束"
不满足条件时:
- 如果是全局性规则 → 记录到 `AGENTS.md`(需框架作者身份确认)
- 如果是项目业务概述 → 记录到 `.agents/PROJECT.md`
### 2. 确定命名与来源
- 框架内置规则:`ulthon-{模块}-{场景}.md`
- 使用者业务规则:`project-{模块}-{场景}.md`
### 3. 编写规则文件
`.agents/rules/` 下创建文件,按格式模板填写。
规则内容要求:
- **可执行**:智能体能直接据此行动
- **可验证**:有明确的对/错判断标准
- **有边界**:明确写出作用域,避免被误用到其他模块
### 4. 更新索引(必须)
新增规则后,必须同步更新两处索引:
**AGENTS.md框架级索引**
在「零散规则」章节的索引表中新增一行(仅 `ulthon-` 前缀的规则)。
**PROJECT.md全量索引**
在「规则索引」章节的索引表中新增一行(所有前缀的规则)。
### 5. 迁移现有内容
如果规则内容原本记录在 `PROJECT.md` 的「增量规则记录」章节,迁移后应将该章节的对应内容替换为指向规则文件的引用(而非直接删除,保留历史痕迹)。
## 读取规则
智能体在以下场景应主动查阅 `.agents/rules/`
1. 首次接触项目时,通过 `AGENTS.md``PROJECT.md` 的索引了解有哪些规则
2. 涉及特定模块开发时,查找该模块是否有对应的规则文件
3. 用户提到某个模块有特殊约束时,查找对应规则
## 维护规则
- 规则内容变更时,同步更新文件内容和索引中的说明列
- 规则过期时,标记为"已废弃"或直接删除,并从索引中移除
- 框架更新时,只操作 `ulthon-` 前缀的规则文件,不动 `project-` 前缀的文件
## 索引格式
索引表统一使用以下格式:
```markdown
| 规则文件 | 来源 | 作用域 | 说明 |
|---------|------|--------|------|
| ulthon-timer-multi-node.md | 框架 | 定时任务相关 | 多节点协调规则 |
| project-order-stock-lock.md | 业务 | 订单模块 | 库存锁定规则 |
```

View File

@@ -0,0 +1,183 @@
---
name: "ulthon-scheme-curd-workflow"
description: "指导使用 Scheme 与 CURD 的标准开发流程。需要新增/调整表结构并生成模块基础代码时调用。"
---
# Scheme + CURD 标准工作流
## 何时调用
- 新增业务表、调整字段、补充索引/注释,并希望保持“代码 <-> 数据库”一致。
- 准备生成控制器/模型/视图等基础 CRUD 代码。
## 关键原则
- CURD 生成前Scheme 与数据库表结构必须完全一致,否则会被拒绝。
- 业务 Scheme 代码统一放在 `app/admin/scheme/`
- 表结构设计遵循项目数据库规范:表名小写下划线、字段注释完整、避免 ENUM。
- CURD 命令中的 `{table}` 参数应为**不含前缀的下划线**格式(例如:数据库表 `ul_user_profile` 对应的参数为 `user_profile`)。
- CURD 生成的页面脚本(`index.js` / `add.js` / `edit.js` / `read.js` / `_common.js`)默认与视图文件放在同一目录:`app/admin/view/<模块路径>/`,不提供“输出到其他 JS 目录”的配置项。
- 一旦你开始在生成代码上做业务改造,就应默认“正式目录不可被覆盖”,后续结构变更需要走“临时生成 + 按需合并”。
## CURD 命令参数说明
| 参数 | 简写 | 说明 |
|------|------|------|
| `--table` | `-t` | 主表名(支持带前缀或不带前缀) |
| `--force` | `-f` | 强制覆盖模式(**谨慎使用** |
| `--delete` | `-d` | 删除模式(**删除生成的文件,不是数据库操作** |
| `--runtime` | `-r` | 临时生成模式(**推荐用于预览** |
### 参数使用建议
1. **首次生成**:直接使用 `-t` 生成到项目目录
2. **已有业务代码**:使用 `-r` 生成到临时目录,对比并手动合并新增字段/逻辑
3. **删除文件**`-d` 用于清理已生成的文件,不影响数据库
## 推荐流程
### A. 首次生成(代码未做业务改造)
1.`app/admin/scheme/` 新建或修改对应表的 Scheme 类。
2. 仅查看差异(不改库),确认当前 Scheme 与 DB 的不一致点:
```bash
php think scheme:sync --dry-run
```
3. 执行同步,将代码结构同步到数据库:
```bash
php think scheme:sync
```
4. 生成 CURD先生成到临时目录确认更稳妥`{table}` 需使用下划线格式):
```bash
php think curd -t {table} -r
```
5. 确认无误后正式生成:
```bash
php think curd -t {table}
```
也可以直接使用 `-f` 强制覆盖(首次生成或确认无需保留时)。
### B. 以数据库为准
1. 在数据库中创建/修改表结构。
2. 反向生成 Scheme 代码(`{table}` 需使用下划线格式):
```bash
php think scheme:make -t {table}
```
3. 再执行 CURD
```bash
php think curd -t {table} -r
```
## 已有业务代码时如何安全重新生成(核心能力)
> 框架的 CURD 机制支持在任何阶段安全地重新生成代码。这是与其他很多系统的关键区别:其他系统要么不推荐重新生成,要么重新生成会直接覆盖已有改动。
### 场景
你已经基于 CURD 生成的代码做了业务定制改过控制器逻辑、视图布局、JS 交互等),现在需要新增或调整字段。
### 错误做法
直接 `curd -t {table} -f` 强制覆盖 -- 会丢失所有业务改动。
### 正确做法(临时生成 + 按需合并)
1. 先修改 Scheme 并同步结构(确保 Scheme 与 DB 一致):
```bash
php think scheme:sync
```
2. 生成到临时目录(**不影响正式代码**
```bash
php think curd -t {table} -r
```
3. 对比临时目录与正式目录的差异,仅将结构性变更按需合并回正式代码:
- 新增字段对应的列表列、表单项、校验规则
- 新增字段在模型中的属性声明
- 其他自动生成的配置(如关联关系、组件类型)
4. 合并后做一次功能自测,重点验证新增字段在新增/编辑/列表/详情中的完整链路。
### 临时目录位置
使用 `-r` 参数时,生成的文件会输出到 `runtime/curd/` 目录下,按表名组织,可直接与 `app/` 下的正式代码对比。
## 业务定制(生成后必做)
CURD 生成的代码是标准化骨架,实际业务几乎都需要在此基础上调整。所有修改仅在 `app/` 目录进行。
### 生成物说明
一次 CURD 会生成以下文件(路径以 `app/admin/` 为基准):
| 类型 | 路径 | 说明 |
|------|------|------|
| 控制器 | `controller/<模块路径>.php` | 列表/新增/编辑/详情/删除等动作 |
| 模型 | `model/<表名>.php` | 字段定义、关联、获取器等 |
| 视图 | `view/<模块路径>/index.html` 等 | 列表页、新增/编辑/详情页 |
| JS 脚本 | `view/<模块路径>/index.js` 等 | 与视图同名配对的页面脚本 |
| 公共脚本 | `view/<模块路径>/_common.js` | 模块级公共逻辑 |
### 逐页检查清单
1. **列表页index**:列展示字段、搜索项、行操作按钮(编辑/删除/详情等)、顶部工具栏按钮(新增等)
2. **新增/编辑页add/edit**:表单字段、校验规则、默认值、关联数据加载
3. **详情页read**:展示字段与格式
4. **公共逻辑_common.js**:模块级事件绑定与共享函数
### 接口需求
如需让页面同时支持 JSON 输出(供移动端/前端调用),使用"页面/接口同体机制",详见 [ulthon-page-api-dual-mode](./ulthon-page-api-dual-mode/SKILL.md)。
## 验证与交付
### 功能验证
`tools:http:call` 在命令行模拟已登录管理员请求,验证增删改查链路:
```bash
# 列表
php think tools:http:call --url="/admin/<模块路径>/index" --method=GET
# 新增
php think tools:http:call --url="/admin/<模块路径>/add" --method=POST --data='{"title":"测试"}'
# 编辑
php think tools:http:call --url="/admin/<模块路径>/edit" --method=POST --data='{"id":1,"title":"修改后"}'
# 详情
php think tools:http:call --url="/admin/<模块路径>/read?id=1" --method=GET
# 删除
php think tools:http:call --url="/admin/<模块路径>/delete" --method=POST --data='{"id":1}'
```
异常时用 `php think tools:log:*` 追踪,详见 [ulthon-tools-http-call](./ulthon-tools-http-call/SKILL.md)。
### 功能配套
- **菜单**:用 `admin:menu:*` 新增/调整菜单,与页面路径/节点保持一致。详见 [ulthon-admin-menu-cli](./ulthon-admin-menu-cli/SKILL.md)。
- **权限**:用 `admin:permission:nodes` 生成/核对节点,为角色分配节点并为用户分配角色。详见 [ulthon-permission-cli](./ulthon-permission-cli/SKILL.md)。
- **数据库排错**:必要时用 `tools:db:*` 做只读检查,详见 [ulthon-db-tools-debug](./ulthon-db-tools-debug/SKILL.md)。
### 交付检查
- [ ] 后台页面:列表/表单/详情/删除等核心路径可用
- [ ] 命令行验证:增删改查已通过 tools 命令跑通
- [ ] 代码规范:按仓库根目录 `.php-cs-fixer.php` 约束自查
- [ ] 菜单与权限:已配置且符合预期
## 交互提示
- 命令行工具通常支持 `--force-force``-ff`)跳过交互确认。

View File

@@ -0,0 +1,118 @@
---
name: "ulthon-scheme-definition"
description: "指导编写 Ulthon Admin 的 Scheme 架构定义文件。当需要新增表结构、修改字段定义或配置组件显示时调用。"
---
# Ulthon Scheme 定义指南
本技能指导如何在 `app/admin/scheme/` 目录下编写 Scheme 文件,这些文件定义了数据库表结构以及后台管理界面的组件呈现方式。
参考文档:[表结构-ulthon_admin](https://doc.ulthon.com/read/augushong/ulthon_admin/619efc9d7af62/zh-cn/2.x.html)
## 基本结构
Scheme 文件是一个 PHP 类,继承自 `BaseScheme`,并使用 PHP 8 注解Attribute来描述元数据。
```php
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_your_table_name', comment: '表注释')]
#[Index(columns: ['field1'], name: 'idx_field1', type: 'NORMAL')]
class YourClassName extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'char', length: 100, default: '', comment: '标题')]
public $title;
#[Field(type: 'int', length: 11, default: '1', comment: '状态', unsigned: true)]
#[Component(type: 'radio', options: ['禁用', '启用'])]
public $status;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $delete_time;
}
```
## 注解详解
### 1. #[Table] (类注解)
- `name`: 数据库表名(建议以 `ul_` 开头)。
- `comment`: 表注释。
- `engine`: 存储引擎,默认 `InnoDB`
- `charset`: 字符集,默认 `utf8mb4`
### 2. #[Index] (类注解,可重复)
- `columns`: 索引列,字符串或数组(如 `['cate_id', 'status']`)。
- `name`: 索引名称。
- `type`: 索引类型:`NORMAL`, `UNIQUE`, `FULLTEXT`
### 3. #[Field] (属性注解)
- `type`: 字段类型 (e.g., `int`, `bigint`, `char`, `varchar`, `text`, `decimal`, `tinyint`)。
- `length`: 长度 (对于 char/varchar/int)。
- `precision`: 精度 (对于 decimal)。
- `scale`: 小数位数 (对于 decimal)。
- `nullable`: 是否允许为空 (bool)。
- `default`: 默认值 (mixed)。
- `comment`: 字段注释。
- `unsigned`: 是否无符号 (bool)。
- `autoIncrement`: 是否自增 (bool)。
- `primary`: 是否为主键 (bool)。
### 4. #[Component] (属性注解,可选)
定义在后台管理页面中该字段使用的 UI 组件。
- `type`: 组件类型。常用值:
- `text`: 普通文本框(默认)。
- `image`: 单图片上传。
- `images`: 多图片上传(默认分隔符为 `|`)。
- `file`: 单文件上传。
- `files`: 多文件上传(默认分隔符为 `|`)。
- `date`: 时间/日期组件。需配合 `options` 指定格式,如 `datetime``date`
- `editor`: 富文本编辑器。
- `textarea`: 文本域。
- `select`: 下拉选择框(需配合 `options`)。
- `switch`: 开关组件(需配合 `options`,如 `['0' => '关闭', '1' => '开启']`)。
- `checkbox`: 复选框(需配合 `options`)。
- `radio`: 单选框(需配合 `options`)。
- `relation`: 关联表下拉选择。`options` 需包含:
- `table`: 关联表名。
- `relationBindSelect`: 显示的字段名。
- `primaryKey`: 关联表主键(可选)。
- `onlyFields`: 列表页显示的字段(可选,用 `|` 分隔)。
- `table`: 表格选择器。`options` 需包含:
- `table`: 关联表名。
- `type`: 选择模式 (`checkbox`/`radio`)。
- `valueField`: 值字段名。
- `fieldName`: 显示字段名。
- `city`: 城市选择器。`options` 需包含:
- `level`: 层级 (`province`/`city`/`area`)。
- `options`: 选项数据。支持索引数组 `['禁用', '启用']` 或关联数组 `['1' => '男', '2' => '女']`,对于 `relation`/`table`/`city` 类型,则是配置参数数组。
## 编写规范
1. **命名规范**: 文件名采用大驼峰PascalCase且必须与类名一致。
2. **时间字段**: 统一使用 `int` 存储 Unix 时间戳,默认 `0`,非空。不要使用 `datetime`/`timestamp`
3. **软删除**: `delete_time` 字段(`int`,默认 `0`)存在时,模型自动启用软删除机制。查询时不要手写 `delete_time` 条件。
4. **状态表达**: 避免使用 MySQL ENUM 类型。优先使用 `int``tinyint` 配合 `#[Component(type: 'radio')]``#[Component(type: 'switch')]`
5. **注释约定**: `Field``comment` 会直接同步到数据库字段注释,同时也作为后台表单的 Label。
6. **字段后缀约定**: 框架会根据字段名后缀自动推断部分组件类型(若未显式指定 `#[Component]`
- `image`, `logo`, `photo`, `icon`: 默认为单图片。
- `images`, `photos`, `icons`: 默认为多图片。
- `file`: 默认为单文件。
- `files`: 默认为多文件。

View File

@@ -0,0 +1,270 @@
---
name: "ulthon-timer"
description: "内置秒级定时器php think timer的使用与扩展规范用于新增/调整定时任务site/call、并发分片、TimerController 防刷)。"
---
# timer内置秒级定时器
## 核心机制(你要记住的 3 件事)
1) 任务配置统一从 `app_file_path('common/command/timer/config.php')` 读取
2) 每个配置会按 `concurrency` 自动展开成多份任务实例,并自动注入 `concurrency_id` / `concurrency_count`
3) “是否该执行”主要由定时器侧的 Cache 节流控制(可选叠加控制器侧的防刷保护)
相关实现入口:
- 系统入口(优先从 app 层理解):
- 定时器命令入口:`app/common/command/Timer.php`(实现继承自 `extend/base/common/command/TimerBase.php`
- 任务实例服务入口:`app/common/service/TimerService.php`(实现继承自 `extend/base/common/service/TimerServiceBase.php`
- site 任务控制器基类入口:`app/common/controller/TimerController.php`(实现继承自 `extend/base/common/controller/TimerControllerBase.php`
- 实现文件(需要深入机制时再看 Base
- [Timer.php](../../../app/common/command/Timer.php) / [TimerBase.php](../../../extend/base/common/command/TimerBase.php)
- [TimerService.php](../../../app/common/service/TimerService.php) / [TimerServiceBase.php](../../../extend/base/common/service/TimerServiceBase.php)
- [TimerController.php](../../../app/common/controller/TimerController.php) / [TimerControllerBase.php](../../../extend/base/common/controller/TimerControllerBase.php)
- 运行配置:[timer.php](../../../config/timer.php)
## 新增定时任务(默认规则)
无特殊情况下,新增定时任务应当通过本机制实现:在 `timer/config.php` 注册任务 + 以 `site``call` 的方式实现目标逻辑。
### 1) 选择任务类型
- `site`:通过 HTTP 访问一个控制器地址(默认优先使用)。即使任务需要“长时间运行”,也尽量设计成 `site` 模式(分片/分批/可重入),以复用框架的页面/接口同体、鉴权、日志、事务、限流等能力。
- `call`:直接执行一个 PHP callable不推荐除非万不得已。仅在确实不适合走 HTTP 上下文、且不希望暴露为控制器入口时使用。
长时间运行任务的推荐写法(仍用 `site`
- 设计为"可重入"的短任务:每次 `do()` 只处理一小批数据,处理进度写入缓存/表,下次继续
- 结合 `concurrency` 做分片:按 `$this->concurrencyId` 划分数据范围,多个实例并行推进
- 结合 `frequency` 控制节奏:用调度频率限制整体吞吐,避免单次占用过久
#### 按时间窗口循环处理(队列消费推荐)
适用于:需要持续消费队列/轮询数据的场景(如消息处理、订单状态同步、通知推送等)。
核心思路:在 `do()` 方法内设置一个**最大执行时间窗口**,循环处理单条数据,超时后自动退出,由定时器下次调度继续。
```php
class MyQueueTask extends TimerController
{
// 每次执行的最大运行时间(秒),根据业务和定时器 frequency 合理设置
// 建议不超过 frequency 的一半,留出调度间隔
protected $maxRunTime = 5;
// 防刷间隔(秒),与定时器侧 frequency 配合
protected $frequency = 10;
public function do()
{
$maxTime = time() + $this->maxRunTime;
do {
$this->doItem();
// 超时检查:时间窗口用完则退出,下次调度继续
if (time() >= $maxTime) {
break;
}
} while (true);
}
protected function doItem()
{
// 取一条待处理的数据
$item = SomeModel::where('status', 0)->order('id', 'asc')->find();
if (empty($item)) {
// 队列为空时短暂休眠,避免空转消耗 CPU
sleep(1);
return;
}
// ... 处理单条数据的业务逻辑 ...
$item->status = 1;
$item->save();
}
}
```
**要点:**
| 配置项 | 建议 |
|--------|------|
| `maxRunTime` | 不超过 `frequency` 的一半;例如 `frequency=10` 时设 3~5 秒 |
| `doItem()` 中的 `sleep()` | 队列空时必须休眠,防止 CPU 空转;有数据时不要 sleep |
| `doItem()` 的粒度 | 每次只处理一条/一批数据,保证可重入、可中断 |
| `frequency` | 与 `maxRunTime` 配合,`frequency` >= `maxRunTime * 2` 为宜 |
**与普通定时任务的区别:**
- 普通任务:`do()` 执行一次就返回,靠定时器周期性调度
- 时间窗口模式:`do()` 在时间窗口内持续循环消费,处理完积压数据后自动退出
- 优势:面对突发积压数据时,单次调度可处理多条,提高吞吐;超时安全退出,不会阻塞定时器
### 2) 编写任务目标target
#### A. site 类型HTTP 任务)
实现方式建议:
- 框架使用者(做业务):仅在 `app/tools/controller/timer/` 新增控制器 `*.php` 即可;不需要、也不应该在 `extend/base/` 增加 `*Base.php`
- 框架作者(维护内核):在 `extend/base/tools/controller/timer/` 新增 `*Base.php` 作为默认实现,同时在 `app/tools/controller/timer/` 增加同名入口类 `*.php` 继承 Base系统唯一调用入口
- 控制器建议继承 `app\common\controller\TimerController`(以获得并发参数校验与可选的 `$frequency` 防刷);执行入口一般提供 `do()`
示例(已有实现可参考):[ClearLog.php](../../../app/tools/controller/timer/ClearLog.php)、[ClearLogBase.php](../../../extend/base/tools/controller/timer/ClearLogBase.php)
并发与防刷建议:
- 如需并发分片处理,在控制器中设置:
- `protected $concurrency = N;`
- 使用 `$this->concurrencyId` 做分片编号0 ~ N-1
- 如希望防止外部重复请求(不仅是定时器自身节流),在控制器中设置:
- `protected $frequency = 秒数;`
- 这会启用 `TimerControllerBase::protectVisit()` 基于 URL 的防刷限制
#### B. call 类型(函数任务)
目标形态:
- `target``call_user_func` 可执行的 callable例如`[SomeService::class, 'method']`、闭包等
- 由于 Base/App 双层机制的入口要求,类引用应指向 `app/` 下的入口类(由入口类继承 Base 实现)
- 长时间/重任务不建议用 `call`:它缺少 HTTP 上下文与控制器层通用能力,排错与复用成本更高
### 3) 注册到任务配置timer/config.php
默认配置文件位置(支持分层覆盖):
- 优先覆盖:`app/common/command/timer/config.php`(一旦存在,`app_file_path(...)` 将优先读取此文件)
- 框架默认:`extend/base/common/command/timer/config.php`(当 app 未提供时回落到该文件)
字段说明(兜底默认值由 Base 层的 `initConfigItem()` 提供):
- `name`:任务唯一名称(用于 Cache key不可重复
- `type``site``call`
- `target`
- `site`:以 `/` 开头的相对路径(建议指向 `tools/timer.*` 控制器的 `do` 方法)
- `call`callable
- `frequency`:执行频率(秒),小于 0 会被修正为 0
- `concurrency`:并发数量(默认 1`site` 类型会自动把并发参数写入 query
- `run_type`:调度策略(仅对 `site` 类型生效,`call` 类型不受影响)。可选值见「多节点协调 > run_type 调度」
示例site
```php
return [
[
'name' => 'clear_log',
'type' => 'site',
'target' => '/tools/timer.ClearLog/do',
'frequency' => 600,
'concurrency' => 1,
],
];
```
示例call
```php
return [
[
'name' => 'system_host_register',
'type' => 'call',
'target' => [\app\common\service\HostService::class, 'heartbeat'],
'frequency' => 30,
],
];
```
## 运行与验证
### 运行命令
- 常规运行:`php think timer`
- 只跑一轮(便于验证):`php think timer --temp`
- 无任务时不输出“no request”`php think timer --quiet`
### 本地调试(指定请求 Host
site 任务会按站点域名发起请求,默认从 `sysconfig('site','site_domain')` 读取。
- 本地调试:`php think timer --local --local-host=http://localhost --local-port=8000`
### 运行模式
配置在 [timer.php](../../../config/timer.php)。定时器使用 Guzzle CurlMultiHandler 实现非阻塞异步事件循环:
- `site` 类型任务通过 curl multi 并行发送 HTTP 请求,真正非阻塞
- `call` 类型任务在主循环中同步执行
- `pending` 数组追踪进行中的请求,`handler->tick()` 非阻塞推进
- 自适应 sleep 策略50ms/200ms避免 CPU 空转
### 配置项说明
| 配置键 | 默认值 | 说明 |
|--------|--------|------|
| `connect_timeout` | `30` | 连接超时时间(秒) |
| `timeout` | `86400` | 请求响应超时时间(秒) |
| `max_handles` | `100` | curl multi 最大并发句柄数 |
| `select_timeout` | `0.001` | curl_multi_select 超时(秒) |
| `clear_log_days` | `3` | ClearLog 任务清理 debug_log 表的保留天数,支持从 `.env``TIMER_CLEAR_LOG_DAYS` 覆盖 |
## 常见坑位(快速自检)
- `name` 重复:会导致 Cache key 冲突,表现为任务"莫名其妙不跑/跑得不对"
- `concurrency` 与控制器侧 `$concurrency` 不一致:会触发 `concurrency id/count error`
- 只依赖控制器侧 `$frequency`:它只是防刷,不是调度;调度频率以定时器侧 Cache 节流为准
- 本地开发忘记 `--local``site` 任务默认请求 `sysconfig('site','site_domain')` 指向的生产域名,不带 `--local` 会直接打到线上
## 多节点协调
定时器支持多节点部署以数据库MySQL作为协调中心。多个节点连接同一个数据库即可自动组成集群。
### 节点注册
每个节点启动后通过 `system_host_register` call 任务自动注册心跳(每 30 秒一次)。节点 ID 生成规则为 `{hostname}-{8位md5}`,持久化在 `runtime/node_id.lock` 文件中,重启后保持不变。
### 主节点选举
- 第一个注册的节点自动成为主节点
- 管理员可在主机列表页面手动切换主节点
- 相关 API`HostService::setMasterNode()` / `HostService::getMasterNode()`
### run_type 调度
`run_type` 仅对 `site` 类型任务生效,`call` 类型任务始终在所有节点执行。可选值:
| run_type | 行为 | 适用场景 |
|----------|------|----------|
| `auto`(默认) | 两阶段 DB 行锁竞争BEGIN / SELECT FOR UPDATE / UPDATE / COMMIT 抢占,抢占成功后释放锁再执行。每个频率窗口内只有一个节点执行 | 通用场景 |
| `main` | 仅主节点执行 | 需要集中处理的任务 |
| `all` | 所有节点各自独立执行 | 节点本地清理等 |
| `manual` | 仅当 DB 中 `manual_trigger=1` 时执行,执行后自动重置为 0。通过管理后台触发 | 运维手动触发 |
### 配置同步
`TimerService::syncConfigToDatabase()` 在定时器启动时运行,将 PHP 配置文件中的任务同步到 `system_timer_config` 表。同步时不会覆盖数据库中已管理的字段(`run_type``status`),以管理后台的设置为准。
### 执行日志
`TimerControllerBase::execute()` 自动包裹 `do()` 并调用 `logStart()` / `logEnd()` 记录执行日志,开发者无需手动调用。配置中写 `/do`,运行时 `TimerServiceBase` 自动重写为 `/execute` 以触发日志包裹。`host_id` 会自动注入到 site 任务的 URL 参数中,用于标识执行节点。
### 管理后台
| 页面 | 路径 | 功能 |
|------|------|------|
| 定时器配置 | `/admin/system.timer_config/index` | 管理 run_type、status、手动触发 |
| 定时器日志 | `/admin/system.timer_log/index` | 只读执行日志查看 |
| 主机列表 | `/admin/system.host/index` | 节点状态、主节点管理 |
### 日志清理
```bash
php think admin:timer:log:clean --days=30
```
### 相关数据表
- `ul_system_timer_config`任务协调配置run_type、status、manual_trigger 等)
- `ul_system_timer_log`:执行日志记录
- `ul_system_host`:新增 `is_master` 字段,标识主节点

View File

@@ -0,0 +1,60 @@
---
name: "ulthon-tools-http-call"
description: "命令行调用后台控制器/URL 进行联调与功能验证(含默认 super token 模拟登录)。"
---
# tools:http:call命令行 HTTP 调用工具)
## 何时使用
- 需要在命令行快速验证某个后台页面/接口是否正常返回。
- 需要绕过浏览器环境,在 CLI 中模拟已登录管理员请求。
- 需要用 `--app/--controller/--action` 直接拼控制器路径调用。
## 基本用法
### 1) 直接按 URL 调用
```bash
php think tools:http:call --url="/admin/system.admin/index" --method=GET
```
### 2) 按控制器参数调用
```bash
php think tools:http:call --app=admin --controller=system.admin --action=index --method=GET
```
## 请求参数
- `--url`:请求路径(支持以 `/` 开头的相对路径)。
- `--method`GET/POST/PUT/PATCH/DELETE。
- `--data`JSON 字符串,请求会自动带 `Content-Type: application/json`
- `--body`:原始请求体字符串。
- `--headers`JSON 字符串的请求头对象(例如 `{ "Accept":"application/json" }`)。
- `--app --controller --action`:框架特性参数,用于拼控制器路径。
## super token默认模拟登录
### 机制说明
- `tools:http:call` 默认会为每次请求生成一个新的 token并写入 `Cache::store('login')`
- 请求会自动携带 `Authorization: Bearer <token>`
- 服务端会按既有 Bearer token 机制从 `Cache::store('login')` 读取登录态,因此只要命令行与服务端使用同一套缓存即可生效。
### 指定模拟账号
```bash
php think tools:http:call --url="/admin/system.admin/index" --user-id=2
```
### 关闭 super token回到未登录行为
```bash
php think tools:http:call --url="/admin/system.admin/index" --super-token=false
```
## 常见现象与排查
- 仍提示“请先登录后台”:通常是命令行与服务端的缓存不共享,或服务端未运行/未走到同一环境配置。
- 返回“无权限访问”:说明已经是登录态,但该账号对当前节点没有权限;可以换 `--user-id` 或调整权限节点。

View File

@@ -0,0 +1,256 @@
---
name: "ulthon-update-workflow"
description: "指导 AI agent 协助开发者使用 php think admin:update 同步上游框架代码,含预览评估、冲突策略选择与更新后操作。"
---
# admin:update框架更新工作流
## 1. 概述
本技能指导 AI agent 协助开发者执行 `php think admin:update`,将上游框架代码同步到本地项目。
核心原则:**开发者当前代码状态为主,上游更新为参考**。更新命令不会盲目覆盖而是根据文件类型和定制状态分类处理。agent 的职责是帮助开发者评估风险、选择合适的冲突策略,并在更新后执行必要的收尾操作。
## 2. 触发场景
- 开发者主动要求"更新框架"、"同步上游"、"php think admin:update"
- 开发者询问"怎么更新"、"新版本有什么变化"
- 开发者提到版本号与上游不一致,需要拉取最新框架代码
**不主动触发**:除非开发者明确表达版本相关的诉求,否则 agent 不应主动建议执行更新。
## 3. 更新前检查
执行更新前,必须依次确认以下事项:
### 3.1 确认执行模式
```bash
php think admin:stack:mode current
```
- 如果返回 Docker 模式(具体哪些模式属于 Docker 模式见 `php think admin:stack:mode list``source/stack/stack.json`),后续所有 `php think` 命令前缀改为 `docker compose exec ulthon_admin`
- 非 Docker 模式直接执行 `php think`,不做转换
### 3.2 确认代码已提交
```bash
git status --short
```
- 输出为空:可以继续
- 有未提交改动:**必须提醒开发者先提交或暂存**。更新会修改文件,未提交的改动可能丢失或产生冲突
### 3.3 确认当前版本
```bash
php think admin:version
```
- 记录当前版本号,用于更新后对比
## 4. 评估阶段(推荐先 dry-run
**每次更新前都应先执行 dry-run**,不要跳过这一步。
```bash
php think admin:update --dry-run
```
命令会拉取上游仓库,对比文件差异,输出变更预览。重点关注以下信息:
### 4.1 风险评估行
输出末尾会有一行风险评估:
```
风险评估: 强制冲突 X个(高风险) | 可选冲突 X个(中风险) | 无冲突变更 X个(低风险)
```
| 风险等级 | 含义 | 建议操作 |
|----------|------|----------|
| 强制冲突(高风险) | 开发者修改了 `extend/base/` 等强制跟踪目录的文件 | 建议覆盖,通过 App 层扩展机制重新实现定制 |
| 可选冲突(中风险) | 开发者修改了 `app/`/`config/` 等可选目录的文件 | 默认跳过,手动按需合并 |
| 无冲突变更(低风险) | 文件未被定制,可以直接更新 | 直接处理 |
### 4.2 目录分组
当变更文件超过 5 个时,输出会按目录分组显示:
```
[extend/base/]
[update] extend/base/admin/service/AdminUpdateServiceBase.php
[app/]
[add][conflict:optional:overwrite] app/admin/controller/NewFeature.php
```
### 4.3 冲突标注
冲突文件会带有策略标签:
- `[conflict:optional:overwrite]``[conflict:optional:skip]`:可选文件冲突
- `[conflict:force:overwrite]``[conflict:force:skip]`:强制文件冲突
- `[skipped]`:因冲突策略被跳过的文件
### 4.4 Composer 依赖变更
输出中包含 `Composer 依赖变更` 段落,列出新增、删除和版本变更的依赖包。
向开发者报告以上信息,由开发者决定是否继续。
### 4.5 跳过文件处理
当 dry-run 输出中存在 `[skipped]` 标记的文件时,说明这些冲突文件被跳过了。此时建议重新执行 dry-run 并加 `--keep-repo`,保留上游克隆目录用于对比:
```bash
php think admin:update --dry-run --optional-conflict=skip --force-conflict=overwrite --keep-repo
```
命令结束后,以下目录会被保留:
| 目录 | 内容 |
|------|------|
| `runtime/update/current/` | 当前安装版本的原始代码 |
| `runtime/update/repo/` | 上游最新版本的代码 |
| 项目根目录 | 开发者实际修改的代码 |
**对比方式**:逐个读取三个版本的跳过文件,辅助开发者判断是否需要合入上游改动。关注点:
- 上游改了什么(对比 `current/` vs `repo/`
- 开发者改了什么(对比 `current/` vs 项目根目录)
- 两者是否冲突
**清理方式**:下次运行 `admin:update` 时会自动清理 `runtime/update/` 目录,或手动删除。
## 5. 参数参考
| 参数 | 可选值 | 说明 |
|------|--------|------|
| `--dry-run` | (无值) | 预览模式,只看变更不执行实际文件操作 |
| `--optional-conflict` | `skip` / `overwrite` / `ask` | 可选文件app/config/route/extend/think/根目录文件)的冲突处理策略 |
| `--force-conflict` | `overwrite` / `skip` / `ask` | 强制文件extend/base/public/database/think的冲突处理策略 |
| `--show` | `all` / `conflict` | 变更输出范围all 显示全部文件conflict 只显示冲突文件 |
| `--keep-repo` | (无值) | 预览模式下保留上游克隆目录runtime/update/current/ 和 runtime/update/repo/),便于手动对比跳过的冲突文件 |
| `--reinstall` | (无值) | 即使当前版本已是最新,也强制重新安装代码 |
| `--update-master` | (无值) | 更新到 master 分支而非最新 tag |
## 6. 执行更新
根据 dry-run 的评估结果和开发者意愿,选择对应的执行命令。
### 6.1 推荐默认方案
适用于大多数场景,低风险直接处理,可选冲突跳过,强制冲突覆盖:
```bash
php think admin:update --optional-conflict=skip --force-conflict=overwrite --show=all
```
### 6.2 开发者想要合并上游可选改动
当开发者明确希望将上游对 app/config 等目录的改动也同步过来时:
```bash
php think admin:update --optional-conflict=overwrite --force-conflict=overwrite --show=all
```
### 6.3 开发者不想覆盖任何冲突
当开发者希望所有冲突文件都跳过,只更新无冲突部分:
```bash
php think admin:update --optional-conflict=skip --force-conflict=skip --show=all
```
**注意**强制冲突跳过可能导致框架功能异常agent 应明确警告。
### 6.4 禁止事项
- **永远不要使用 `ask` 模式**。agent 无法处理交互式确认提示(`$output->confirm()`),会导致命令挂起
- **不要省略冲突策略参数**。不传参数时,如果存在冲突文件,命令会回退到交互模式,同样会挂起
- **`--keep-repo` 仅在预览模式下有效**:正式执行时自动忽略,不会保留目录
## 7. 冲突处理指引
### 7.1 可选文件冲突
涉及目录:`app/``config/``route/``extend/think/``source/docker/`、根目录文件(如 `.php-cs-fixer.php`
这些是开发者可以自由修改的区域。出现冲突说明开发者定制了这些文件,且上游也修改了同一文件。
处理建议:
- **默认跳过**`--optional-conflict=skip`):保留开发者的定制,不引入上游改动
- 如果开发者需要上游的某个改动,在 dry-run 输出中找到具体文件路径,建议开发者手动查看 diff 并选择性合并
- **不建议直接 overwrite**:会覆盖开发者的所有定制,且无法自动合并
### 7.2 强制文件冲突
涉及目录:`extend/base/``public/``database/`
这些是框架自动管理的区域。出现冲突说明开发者违反了 Base/App 架构规则,直接修改了 `extend/base/` 下的文件。
处理建议:
- **默认覆盖**`--force-conflict=overwrite`):让框架恢复到标准状态
- 覆盖后,提醒开发者通过 App 层扩展机制重新实现之前的定制逻辑(参考 `ulthon-base-app-architecture` 技能)
- 如果开发者坚持不覆盖(`--force-conflict=skip`**必须警告**:跳过强制文件更新可能导致框架功能异常、兼容性问题
## 8. 更新后操作
更新命令执行完成后,输出中会包含"建议执行以下后续操作"段落。根据提示,按需执行:
### 8.1 Composer 依赖变更
如果输出中出现 `composer.json 已变更,请更新依赖`
```bash
composer install
```
**需开发者确认后才执行**,不要自动运行。
### 8.2 数据库迁移
如果输出中出现 `数据库迁移文件已变更,请执行迁移`
```bash
php think migrate:run
```
**需开发者确认后才执行**。迁移可能影响生产数据,务必谨慎。
### 8.3 缓存清理
如果输出中出现 `框架核心或配置已变更,请清理缓存`
```bash
php think clear
```
这一步通常可以直接执行,风险较低。
### 8.4 确认版本号
```bash
php think admin:version
```
确认版本号已更新为预期版本。
### 8.5 建议开发者测试
提醒开发者验证关键业务功能是否正常,特别是:
- 后台登录
- 菜单与权限
- 核心 CURD 页面
- 定时任务(如有)
## 9. 注意事项
- **禁止使用 `ask` 模式**agent 无法处理交互式提示,必须通过参数明确指定冲突策略
- **更新前务必确保代码已提交**:未提交的改动可能在更新过程中丢失
- **不自动执行 composer 或数据库操作**:这些操作影响面大,需开发者确认
- **大版本跳跃风险更高**:跨越多个 tag 时变更量大,建议格外谨慎,仔细审查 dry-run 输出
- **Docker 模式注意**Docker 模式下所有 `php think` 命令前缀改为 `docker compose exec ulthon_admin`(具体哪些模式属于 Docker 模式见 `php think admin:stack:mode list``source/stack/stack.json`
- **更新会临时占用磁盘**:命令会在 `runtime/update/` 下克隆上游仓库进行对比,完成后自动清理
- **版本更新说明**:更新完成后,命令会输出跨版本的更新说明(来自 `adminUpdateData/tips.php`),提醒开发者关注

View File

@@ -3,13 +3,15 @@ APP_DEBUG=true
[APP]
DEFAULT_TIMEZONE=Asia/Shanghai
AUTO_CACHE_LOG=false
AUTO_PARSE_API=true
[DATABASE]
MAIN=main
TYPE=mysql
HOSTNAME=host.docker.internal
DATABASE=ulthon
USERNAME=root
PASSWORD=root
DATABASE=ulthon_admin
USERNAME=ulthon_admin
PASSWORD=MYSQL_PASSWORD
HOSTPORT=3306
CHARSET=utf8mb4
DEBUG=true
@@ -19,6 +21,10 @@ FIELDS_CACHE=false
[LOG]
CHANNEL=file
[TIMER]
# 清理日志保留天数debug_log 表)
CLEAR_LOG_DAYS=3
[LANG]
default_lang=zh-cn

3
.gitattributes vendored
View File

@@ -1,3 +1,6 @@
*.js linguist-language=PHP
*.css linguist-language=PHP
*.html linguist-language=PHP
# Shell脚本始终使用LF行尾避免Windows挂载到Docker时CRLF报错
*.sh text eol=lf

View File

@@ -0,0 +1,157 @@
name: build-and-deploy
on:
push:
workflow_dispatch:
env:
REMOTE_APP_DIR: /data/projects/ulthon_admin/docker
PACKAGE_NAME: ulthon_admin_release.tar.gz
COMPOSE_PROJECT_NAME: ulthon_admin
DB_HOSTNAME: host.docker.internal
jobs:
deploy_host15:
name: 直传代码并部署到 Host15
runs-on: main
container:
image: ulthon/debian-php82-composer-node20-act:20260503082548
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: 生成 .env
shell: bash
env:
MYSQL_PASSWORD: ${{ secrets.MYSQL_PASSWORD }}
DB_HOSTNAME: ${{ env.DB_HOSTNAME }}
run: |
set -euo pipefail
cp .example.env .env
awk -v host="$DB_HOSTNAME" -v newpwd="$MYSQL_PASSWORD" '
BEGIN { has_host = 0; has_pwd = 0 }
$0 ~ /^HOSTNAME=/ {
print "HOSTNAME=" host
has_host = 1
next
}
$0 ~ /^PASSWORD=/ {
print "PASSWORD=" newpwd
has_pwd = 1
next
}
{ print }
END {
if (!has_host) print "HOSTNAME=" host
if (!has_pwd) print "PASSWORD=" newpwd
}' .env > .env.tmp
mv .env.tmp .env
- name: 安装依赖
shell: bash
run: |
set -euo pipefail
composer config -g repos.packagist composer https://nexus.hl7.top:1243/repository/composer-proxy/
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
- name: 切换到 docker-serve 模式
shell: bash
run: php think admin:stack:mode use docker-serve -f
- name: 打包发布文件
shell: bash
env:
PACKAGE_NAME: ${{ env.PACKAGE_NAME }}
run: |
set -euo pipefail
TMP_PACKAGE="/tmp/${PACKAGE_NAME}"
rm -f "$TMP_PACKAGE" "$PACKAGE_NAME"
tar -czf "$TMP_PACKAGE" \
--exclude="$PACKAGE_NAME" \
--exclude-vcs \
--exclude="./runtime/*" \
--exclude="./.trae/*" \
--exclude="./source/clients/uniapp/node_modules/*" \
.
cp -f "$TMP_PACKAGE" "$PACKAGE_NAME"
- name: 创建远端目录
uses: appleboy/ssh-action@v0.1.10
env:
REMOTE_APP_DIR: ${{ env.REMOTE_APP_DIR }}
with:
host: ${{ secrets.UL_HOST15_IP }}
username: ${{ secrets.UL_HOST15_USER }}
password: ${{ secrets.UL_HOST15_PASSWORD }}
port: ${{ secrets.UL_HOST15_PORT }}
envs: REMOTE_APP_DIR
script: |
set -euo pipefail
mkdir -p "${REMOTE_APP_DIR}/incoming"
mkdir -p "${REMOTE_APP_DIR}/releases"
- name: 上传发布包与 compose
uses: appleboy/scp-action@v0.1.7
env:
REMOTE_APP_DIR: ${{ env.REMOTE_APP_DIR }}
PACKAGE_NAME: ${{ env.PACKAGE_NAME }}
with:
host: ${{ secrets.UL_HOST15_IP }}
username: ${{ secrets.UL_HOST15_USER }}
password: ${{ secrets.UL_HOST15_PASSWORD }}
port: ${{ secrets.UL_HOST15_PORT }}
source: "${{ env.PACKAGE_NAME }},docker-compose.yaml"
target: "${{ env.REMOTE_APP_DIR }}/incoming"
overwrite: true
- name: 远端解压并启动
uses: appleboy/ssh-action@v0.1.10
env:
REMOTE_APP_DIR: ${{ env.REMOTE_APP_DIR }}
PACKAGE_NAME: ${{ env.PACKAGE_NAME }}
COMPOSE_PROJECT_NAME: ${{ env.COMPOSE_PROJECT_NAME }}
GITHUB_RUN_ID: ${{ github.run_id }}
with:
host: ${{ secrets.UL_HOST15_IP }}
username: ${{ secrets.UL_HOST15_USER }}
password: ${{ secrets.UL_HOST15_PASSWORD }}
port: ${{ secrets.UL_HOST15_PORT }}
timeout: 120s
command_timeout: 60m
envs: REMOTE_APP_DIR,PACKAGE_NAME,COMPOSE_PROJECT_NAME,GITHUB_RUN_ID
script: |
set -euo pipefail
RELEASE_DIR="${REMOTE_APP_DIR}/releases/${GITHUB_RUN_ID}"
PACKAGE_PATH="${REMOTE_APP_DIR}/incoming/${PACKAGE_NAME}"
COMPOSE_PATH="${REMOTE_APP_DIR}/incoming/docker-compose.yaml"
rm -rf "${RELEASE_DIR}"
mkdir -p "${RELEASE_DIR}"
tar -xzf "${PACKAGE_PATH}" -C "${RELEASE_DIR}"
if [ -f "${COMPOSE_PATH}" ]; then
cp -f "${COMPOSE_PATH}" "${RELEASE_DIR}/docker-compose.yaml"
fi
cd "${RELEASE_DIR}"
export COMPOSE_PROJECT_NAME
docker compose down || true
docker compose up -d --build --remove-orphans
docker compose ps
docker compose exec -T ulthon_admin test -f /var/www/html/vendor/autoload.php
docker compose exec -T ulthon_admin php -r "require '/var/www/html/vendor/autoload.php'; echo 'autoload-ok'.PHP_EOL;"
docker compose logs --tail=80 ulthon_admin
ln -sfn "${RELEASE_DIR}" "${REMOTE_APP_DIR}/current"
ls -1dt "${REMOTE_APP_DIR}/releases"/* 2>/dev/null | tail -n +6 | xargs -r rm -rf

35
.gitignore vendored
View File

@@ -3,10 +3,26 @@
*.log
.env
config/install/lock/install.lock
public/upload
# 忽略 storage 目录下的所有内容
public/storage/*
# 但保留 .gitkeep 文件和必要的子目录
!public/storage/.gitkeep
!public/storage/upload/
!public/storage/build/
# 忽略子目录下的所有内容
public/storage/upload/*
public/storage/build/*
# 但保留子目录中的 .gitkeep 文件
!public/storage/upload/.gitkeep
!public/storage/build/.gitkeep
public/docs
public/conf
public/WowOss.exe
build/*
!build/.gitkeep
app/index/controller/Test.php
composer.phar
app/test/
@@ -16,9 +32,22 @@ ul.db
/app/tools/controller/Install.php
/app/common/command/curd/migrate_output.php
/dist
/composer.lock
/public/storage
/build
/.VSCodeCounter
result.txt
test.php
/.env.prod
extend/base/common/command/curd/migrate_output.php
# source 目录:多端工程依赖与构建产物
/source/**/node_modules
/source/**/dist
/source/**/.hbuilderx
/source/**/.idea
/source/**/.vscode
.trae/documents/
/docker-dev/
/.sisyphus
/.omo
/.playwright-mcp

153
AGENTS.md Normal file
View File

@@ -0,0 +1,153 @@
# 智能体协作规范Ulthon Admin
- `AGENTS.md`(本文件):协作规则入口与导航
- `.agents/`工作流skills、零散规则rules与补充文档
- `.agents/PROJECT.md`:项目业务总览(定位、核心模块、业务约束),由开发者按项目实际情况编写,智能体首次接触项目时应主动读取
- `.agents/rules/`:模块级/场景级零散规则,按文件独立存放(详见「零散规则」章节)
- `.agents/skills/`:按场景调用的工作流技能,统一以 `*/SKILL.md` 为准(详见「工作流」章节)
- 标准开发流程:见「标准开发流程」表格,操作细节见对应技能文件
## 项目级规则(最高优先级 / 唯一权威)
与其他文档、聊天内容、或智能体建议冲突时,一律以本节为准。
除非明确要求,按照框架使用者的规则进行开发。
### 通用基础规范(所有开发者必须遵守)
- 技术栈ThinkPHP 8.xPHP 8+MySQL 8+Layui 2.x模板引擎ThinkPHP 内置模板引擎
- 命名规范(必读):[ulthon-naming-convention.md](./.agents/rules/ulthon-naming-convention.md)
- 代码风格(必读):遵循项目根目录 `.php-cs-fixer.php`
- 表结构设计规范(必读):[ulthon-database-design.md](./.agents/rules/ulthon-database-design.md)
- 项目文档主页(在线):<https://doc.ulthon.com/read/augushong/ulthon_admin/home/zh-cn/2.x.html>
### 代码分层铁律(不可协商)
框架采用 Base/App 双层架构。根据当前任务的开发者身份,遵守对应约束:
**所有身份通用**
- `extend/base/` 是框架内核(`*Base``app/` 是业务代码入口,目录路径一一对应
**框架使用者(默认身份,除非明确说"修内核/改框架"**
- 严禁修改 `extend/base/` 下任何文件
- 所有开发只在 `app/` 目录进行;新写业务能力直接在 `app/` 下实现即可
- 扩展内置能力时,只改 `app/` 下对应入口类(必要时 `extends` 对应 `*Base`),保持方法签名一致
**框架作者(仅当任务涉及 `extend/base/` 时)**
- 每新增一个 Base 类,必须同时在 `app/` 提供入口类(可为空类继承 Base
- Base 内部禁止直接引用/调用 `extend/base/` 下的类(含内部互调),必须引用 `app/` 下的入口类
- 例外:`app/common.php` 引用 `extend/base/helper.php` 是全局辅助函数的唯一特许
- 核心层维护原则:稳定性优先(保证向下兼容);通用性优先(不引入具体业务逻辑)
详细架构说明与操作流程:[ulthon-base-app-architecture](./.agents/skills/ulthon-base-app-architecture/SKILL.md)
### 其他规则
- 数据库:表结构优先 Scheme`app/admin/scheme/`);避免 ENUMtools:db 用于调试,不用于"设计表结构"
- 前端:视图与脚本同名配对(`*.html` + 同名 `*.js`),并按模块维护 `_common.js`
- 权限:基于 `auth` 注解生成节点与鉴权;以角色为中心管理(角色、角色权限、用户角色);命令行使用见技能:[ulthon-permission-cli](./.agents/skills/ulthon-permission-cli/SKILL.md)
- 临时文件:智能体在任务中产生的临时文件(脚本、日志、缓存、产物等)统一输出到 `runtime/agents/`(可按智能体/任务再分子目录),不要放在仓库根目录;除非任务明确要求或框架约定位置属于根目录
- 调试与验证优先使用框架内置命令行工具tools:http:call、tools:db:*、tools:log:*、admin:menu:*、admin:permission:*),不需要借助外部数据库 MCP 或临时脚本
### 标准开发流程Scheme + CURD默认必须执行
> 完整操作细节见对应技能文件,本节仅列出流程骨架与核心约束。
| 步骤 | 说明 | 对应技能 |
|------|------|----------|
| 1. 初始化环境 | 依赖安装、`.env` 配置、数据库初始化、确保 `php think` 可用 | - |
| 2. 设计表结构 | 在 `app/admin/scheme/` 中编写 Scheme 类;遵循表结构设计规范 | [ulthon-scheme-definition](./.agents/skills/ulthon-scheme-definition/SKILL.md) |
| 3. 同步 Scheme 与 DB | `scheme:sync`(以代码为准)或 `scheme:make`(以 DB 为准);**CURD 生成前两者必须一致** | [ulthon-scheme-curd-workflow](./.agents/skills/ulthon-scheme-curd-workflow/SKILL.md) |
| 4. 生成 CURD 代码 | `curd -t {table}`;后续字段变更可安全重新生成(`-r` 临时目录对比合并,不覆盖已改造的业务代码) | [ulthon-scheme-curd-workflow](./.agents/skills/ulthon-scheme-curd-workflow/SKILL.md) |
| 5. 业务定制 | 仅改 `app/`;逐页调整按钮/字段/表单/交互;接口需求用页面接口同体机制 | [ulthon-page-api-dual-mode](./.agents/skills/ulthon-page-api-dual-mode/SKILL.md) |
| 6. 功能验证 | `tools:http:call` 模拟请求跑通增删改查;`tools:log:*` 排查异常 | [ulthon-tools-http-call](./.agents/skills/ulthon-tools-http-call/SKILL.md) |
| 7. 功能配套 | 菜单(`admin:menu:*`)、权限节点(`admin:permission:nodes`)、角色分配 | [ulthon-admin-menu-cli](./.agents/skills/ulthon-admin-menu-cli/SKILL.md)、[ulthon-permission-cli](./.agents/skills/ulthon-permission-cli/SKILL.md) |
| 8. 最终检查 | 页面路径可用、命令行验证通过、代码规范自查 | - |
## 规则维护机制
- **框架基础规则**`AGENTS.md`(本文件)+ `.agents/rules/ulthon-*` 为唯一权威;默认不随任务动态增长
- **使用者补充规则**`.agents/PROJECT.md` + `.agents/rules/project-*`,由开发者维护
- **维护约束**:框架作者新增/调整规则前须与开发者确认;使用者身份发现需记录的约束时,优先记录到对应位置
- **管理技能**[ulthon-rules-manager](./.agents/skills/ulthon-rules-manager/SKILL.md)
## 零散规则
模块级/场景级的专属知识(约束、约定、设计决策),不适合在全局展开,存放在 `.agents/rules/` 目录下,每条规则一个独立文件。
- 命名约定:`ulthon-` 前缀为框架内置规则,`project-` 前缀为使用者业务规则
### 框架内置规则索引
| 规则文件 | 作用域 | 说明 |
|---------|--------|------|
| [ulthon-naming-convention.md](./.agents/rules/ulthon-naming-convention.md) | 命名规范 | 目录命名与 PHP 文件命名约定 |
| [ulthon-database-design.md](./.agents/rules/ulthon-database-design.md) | 数据库设计 | 表结构设计:特殊字段、字段后缀、注释语法、类型大全 |
| [ulthon-controller-url.md](./.agents/rules/ulthon-controller-url.md) | 控制器路由 | URL 与控制器/方法的映射规则 |
| [ulthon-deploy-environment.md](./.agents/rules/ulthon-deploy-environment.md) | 部署与命令执行 | 部署栈模式与 Docker/宿主机命令判断 |
| [ulthon-source-directory.md](./.agents/rules/ulthon-source-directory.md) | source/ 目录 | 子项目/多端代码的目录约定与安全要求 |
| [ulthon-timer-multi-node.md](./.agents/rules/ulthon-timer-multi-node.md) | 定时任务相关 | 多节点协调规则与设计 |
> 使用者业务规则索引见 `.agents/PROJECT.md` 的「规则索引」章节。
## 工作流Skills
Skills 是"按场景调用的工作流说明",统一以 `.agents/skills/*/SKILL.md` 为准。
- 技能命名约定:`ulthon-` 前缀为框架内置技能,`project-` 前缀为项目业务技能。新增业务技能时使用 `project-` 前缀。
- Base/App 架构与扩展指南(含身份分章节):[ulthon-base-app-architecture](./.agents/skills/ulthon-base-app-architecture/SKILL.md)
- Scheme + CURD 工作流:[ulthon-scheme-curd-workflow](./.agents/skills/ulthon-scheme-curd-workflow/SKILL.md)
- Scheme 定义指南:[ulthon-scheme-definition](./.agents/skills/ulthon-scheme-definition/SKILL.md)
- 数据库调试命令tools:db[ulthon-db-tools-debug](./.agents/skills/ulthon-db-tools-debug/SKILL.md)
- HTTP 调用工具tools:http:call[ulthon-tools-http-call](./.agents/skills/ulthon-tools-http-call/SKILL.md)
- 日志命令tools:log[ulthon-tools-log](./.agents/skills/ulthon-tools-log/SKILL.md)
- 内置定时器与定时任务扩展:[ulthon-timer](./.agents/skills/ulthon-timer/SKILL.md)
- 页面 / 接口同体:[ulthon-page-api-dual-mode](./.agents/skills/ulthon-page-api-dual-mode/SKILL.md)
- 登录认证Session + Token[ulthon-auth-session-token](./.agents/skills/ulthon-auth-session-token/SKILL.md)
- 权限与角色管理RBAC CLI[ulthon-permission-cli](./.agents/skills/ulthon-permission-cli/SKILL.md)
- 菜单管理admin:menu:\* CLI[ulthon-admin-menu-cli](./.agents/skills/ulthon-admin-menu-cli/SKILL.md)
## 智能体指导
使用命令可以将内置智能体规则应用到各类智能体中。设计规则时以 AGENTS.md 和 `.agents/` 目录为主。
php think tools:agent:publish
## 项目结构速览
```
ulthon_admin/
├── app/ # 应用层(业务代码唯一入口)
│ ├── admin/ # 后台管理模块控制器、模型、视图、Scheme
│ ├── common/ # 公共代码(命令、服务、工具类)
│ └── tools/ # 工具控制器(定时任务等)
├── extend/
│ ├── base/ # 框架内核(*Base.php禁止业务修改
│ └── think/ # ThinkPHP 扩展(存储驱动、日志、迁移)
├── config/ # ThinkPHP 配置
├── public/ # Web 入口 + 静态资源
├── view/ # 视图覆盖层
├── route/ # 路由定义
├── database/ # 数据库迁移与种子
├── source/ # 主工程外内容统一目录(多端代码/资料/附件/各类子项目)
│ └── clients/uniapp/ # uni-app 前端工程
└── .agents/ # 智能体协作资源
├── skills/ # 工作流技能(按场景调用)
├── rules/ # 零散规则(按模块/场景拆分)
└── PROJECT.md # 项目业务总览与规则索引
```
## 快速命令参考
| 场景 | 命令 |
| ---------------- | --------------------------------------- |
| 同步表结构代码→DB | `php think scheme:sync` |
| 生成 SchemeDB→代码 | `php think scheme:make -t {table}` |
| CURD 代码生成 | `php think curd -t {table}` |
| 模拟 HTTP 请求 | `php think tools:http:call` |
| 查看日志 | `php think tools:log:show` |
| 数据库查询 | `php think tools:db:query "SELECT ..."` |
| 权限节点生成 | `php think admin:permission:nodes` |
| 菜单管理 | `php think admin:menu:*` |
| 框架更新 | `php think admin:update` |

View File

@@ -1,76 +0,0 @@
# 基于官方 PHP 8.0 镜像
FROM php:8.0-fpm
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
RUN sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
RUN apt-get update
# 安装nginx
RUN apt-get install -y nginx
# 安装ffmpeg
# RUN apt-get install -y ffmpeg
# 安装redis
# RUN apt-get install -y redis-server
# 安装git
# RUN apt-get install -y git
ADD --chmod=0755 https://delivery.ulthon.com/install-php-extensions /usr/local/bin/
RUN install-php-extensions pdo_mysql
RUN install-php-extensions gd
RUN install-php-extensions fileinfo
RUN install-php-extensions opcache
RUN install-php-extensions redis
RUN install-php-extensions event
RUN install-php-extensions imagick
RUN install-php-extensions zip
# 清理默认 Nginx 配置
RUN rm /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
# 设置工作目录
WORKDIR /var/www/html
# 将当前目录下的文件拷贝到工作目录
COPY . /var/www/html
# 内部安装compsoer并安装依赖如果不需要可以注释掉
# RUN install-php-extensions @composer
# 创建runtime目录
RUN mkdir -p /var/www/html/runtime
RUN chmod -R 777 /var/www/html/runtime
VOLUME /var/www/html/runtime
# 创建storage目录
RUN mkdir -p /var/www/html/public/storage
RUN chmod -R 777 /var/www/html/public/storage
VOLUME /var/www/html/public/storage
# 创建build目录
RUN mkdir -p /var/www/html/public/build
RUN chmod -R 777 /var/www/html/public/build
VOLUME /var/www/html/public/build
# 创建storagesafe目录
RUN mkdir -p /var/www/html/storage
RUN chmod -R 777 /var/www/html/storage
VOLUME /var/www/html/storage
# 挂载主目录,也可以选择直接挂载主目录,可以把上面的几个指定的目录删掉
# VOLUME ["/var/www/html"]
# 暴露 Nginx 端口
EXPOSE 80
#
RUN chmod +x /var/www/html/docker/run.sh
# 启动 Nginx PHP 然后阻塞
ENTRYPOINT ["/var/www/html/docker/run.sh"]
CMD ["server"]

View File

@@ -4,7 +4,7 @@ For a better development experience.
Only for `developers' services, only for `demand customization' services.
A rapidly developed backend management system based on ThinkPHP6.1 and layui2.8.
A rapidly developed backend management system based on ThinkPHP and Layui 2.10.
Technical exchange QQ group: [207160418](https://jq.qq.com/?_wv=1027&k=TULvsosz)

View File

@@ -6,7 +6,7 @@
只为`开发人员`服务,只为`需求定制`服务。
基于ThinkPHP8和layui2.8的快速开发的后台管理系统。*自定义扩展架构,支持自动更新上游框架代码。*
基于 ThinkPHP 和 Layui 2.10 的快速开发的后台管理系统。*自定义扩展架构,支持自动更新上游框架代码。*
技术交流QQ群[207160418](https://jq.qq.com/?_wv=1027&k=TULvsosz)
@@ -58,6 +58,8 @@ php think run
> 这个安装方式对开发体验非常友好
> 安装后,建议将`composer.lock`从`.gitignore`去掉,避免分发带来的问题。
### ~~下载完整包~~
完整包下载方式更新中。
@@ -227,6 +229,7 @@ vscode中liveSassCompiler的配置:
{
"liveSassCompile.settings.includeItems": [
"/public/static/common/css/theme/*.scss",
"/public/static/plugs/lay-module/mapLocation/mapLocation.scss",
"/public/static/plugs/lay-module/tableData/tableData.scss",
"/public/static/plugs/lay-module/tagInput/tagInput.scss",
"/public/static/plugs/lay-module/propertyInput/propertyInput.scss"
@@ -248,7 +251,7 @@ docker build -t ulthon/ulthon_admin:202404071454 .
## 运行镜像
### docker命令正式运行
下面的命令中为容器指定了名字,可自行替换。
相同名称不能重复运行所以指定名字是个好习惯否则docker会自动起名。
@@ -256,7 +259,7 @@ docker build -t ulthon/ulthon_admin:202404071454 .
```
docker run -d \
--restart=always \
-p 88:80 \
-p 8000:8000 \
-v /data/ulthon_admin/runtime:/var/www/html/runtime \
-v /data/ulthon_admin/storage:/var/www/html/public/storage \
-v /data/ulthon_admin/build:/var/www/html/public/build \
@@ -265,7 +268,7 @@ docker run -d \
server
```
- 端口:需要暴露80端口冒号左侧可自定义修改为本机可用的一个端口。
- 端口:容器内和外部默认均为8000端口冒号左侧可自定义修改为本机可用的一个端口。
- runtime需要映射runtime目录冒号左侧可自定义修改为指定的目录推荐设置因为缓存、日志等文件都保存在这个目录下不指定的话重启丢失。
- public/storage框架默认的上传文件存储在public/storage下因此必须映射该目录如果系统不需要上传则可以不设置。
- public/build框架默认的生成文件比如海报、二维码存储在public/build下推荐映射否则重启后丢失。
@@ -276,6 +279,27 @@ server
> 如果不映射目录,镜像不会出错,但重启后丢失。
### docker compose 正式运行
如果和代码一起分发那么直接运行即可。会自动构建docker镜像并映射当前目录。
```bash
docker compose up -d
```
建议提前构建好镜像,使用镜像名启动,此时需要映射指定的目录,但不需要分发代码了。
### docker开发运行
需要提前构建镜像,比如镜像名为:`ulthon/ulthon_admin:v1`
然后运行:
```bash
# 默认行为
docker run --rm -it --name ulthon_admin_instance -p 8000:8000 -v ./:/var/www/html/ ulthon/ulthon_admin:v1
# 运行指定命令
docker run --rm -it --name ulthon_admin_instance -p 8000:8000 -v ./:/var/www/html/ ulthon/ulthon_admin:v1 think
```
要注意该命令不能通过crtl+c终止。
## 基本用法

View File

@@ -7,7 +7,7 @@ use base\admin\controller\mall\CateBase;
/**
* Class Admin.
* @ControllerAnnotation(title="商品分类管理")
* @ControllerAnnotation(title="商品分类管理",module="商城")
*/
class Cate extends CateBase
{

View File

@@ -7,7 +7,7 @@ use base\admin\controller\mall\GoodsBase;
/**
* Class Goods.
* @ControllerAnnotation(title="商城商品管理")
* @ControllerAnnotation(title="商城商品管理",module="商城")
*/
class Goods extends GoodsBase
{

View File

@@ -6,7 +6,7 @@ use app\admin\service\annotation\ControllerAnnotation;
use base\admin\controller\mall\TagBase;
/**
* @ControllerAnnotation(title="mall_tag")
* @ControllerAnnotation(title="mall_tag",module="商城")
*/
class Tag extends TagBase
{

View File

@@ -8,7 +8,7 @@ use base\admin\controller\system\AdminBase;
/**
* Class Admin.
* @ControllerAnnotation(title="管理员管理")
* @ControllerAnnotation(title="管理员管理",module="系统")
*
* @NodeAnotation(title="自定义权限标识符",name="customFlag")
*/

View File

@@ -6,7 +6,7 @@ use app\admin\service\annotation\ControllerAnnotation;
use base\admin\controller\system\AuthBase;
/**
* @ControllerAnnotation(title="角色权限管理")
* @ControllerAnnotation(title="角色权限管理",module="系统")
* Class Auth
*/
class Auth extends AuthBase

View File

@@ -2,12 +2,10 @@
namespace app\admin\controller\system;
use app\admin\service\annotation\ControllerAnnotation;
use base\admin\controller\system\ConfigBase;
/**
* Class Config.
* @ControllerAnnotation(title="系统配置管理")
*/
class Config extends ConfigBase
{

View File

@@ -0,0 +1,22 @@
<?php
namespace app\admin\controller\system;
use app\admin\service\annotation\ControllerAnnotation;
use base\admin\controller\system\HostBase;
/**
* @ControllerAnnotation(title="system_host",module="系统")
*/
class Host extends HostBase
{
/**
* 设置主节点.
*
* @\app\admin\service\annotation\NodeAnotation(title="设置主节点")
*/
public function setMaster()
{
return parent::setMaster();
}
}

View File

@@ -7,7 +7,7 @@ use base\admin\controller\system\MenuBase;
/**
* Class Menu.
* @ControllerAnnotation(title="菜单管理",auth=true)
* @ControllerAnnotation(title="菜单管理",module="系统")
*/
class Menu extends MenuBase
{

View File

@@ -6,7 +6,7 @@ use app\admin\service\annotation\ControllerAnnotation;
use base\admin\controller\system\NodeBase;
/**
* @ControllerAnnotation(title="系统节点管理")
* @ControllerAnnotation(title="系统节点管理",module="系统")
* Class Node
*/
class Node extends NodeBase

View File

@@ -6,7 +6,7 @@ use app\admin\service\annotation\ControllerAnnotation;
use base\admin\controller\system\QuickBase;
/**
* @ControllerAnnotation(title="快捷入口管理")
* @ControllerAnnotation(title="快捷入口管理",module="系统")
* Class Quick
*/
class Quick extends QuickBase

View File

@@ -0,0 +1,13 @@
<?php
namespace app\admin\controller\system;
use app\admin\service\annotation\ControllerAnnotation;
use base\admin\controller\system\StatusBase;
/**
* @ControllerAnnotation(title="system_status",module="系统")
*/
class Status extends StatusBase
{
}

View File

@@ -0,0 +1,97 @@
<?php
namespace app\admin\controller\system;
use app\common\controller\AdminController;
use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnotation;
use think\App;
/**
* @ControllerAnnotation(title="定时任务协调配置表")
*/
class TimerConfig extends AdminController
{
use \app\admin\traits\Curd;
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new \app\admin\model\SystemTimerConfig();
$this->assign('select_list_run_type', $this->model::SELECT_LIST_RUN_TYPE, true);
$this->assign('select_list_status', $this->model::SELECT_LIST_STATUS, true);
$this->assign('select_list_is_synced', $this->model::SELECT_LIST_IS_SYNCED, true);
$this->assign('select_list_manual_trigger', $this->model::SELECT_LIST_MANUAL_TRIGGER, true);
// 允许通过行内修改的字段
$this->allowModifyFields = [
'status',
'run_type',
];
}
/**
* @NodeAnotation(title="添加")
*/
public function add()
{
$this->error('定时任务配置由系统同步生成,不支持手动添加');
}
/**
* @NodeAnotation(title="删除")
*/
public function delete($id)
{
$this->error('定时任务配置由系统管理,不支持删除');
}
/**
* @NodeAnotation(title="编辑")
*/
public function edit($id)
{
$row = $this->model->find($id);
empty($row) && $this->error('数据不存在');
if ($this->request->isPost()) {
$post = $this->request->post();
// 只允许修改 run_type 和 status
$post = array_intersect_key($post, array_flip(['run_type', 'status']));
try {
$save = $row->save($post);
} catch (\Exception $e) {
$this->error('保存失败:' . $e->getMessage());
}
$save ? $this->success('保存成功') : $this->error('保存失败');
}
$this->assign('row', $row);
return $this->fetch();
}
/**
* @NodeAnotation(title="手动触发")
*/
public function trigger($id)
{
$this->checkPostRequest();
$row = $this->model->find($id);
if (!$row) {
$this->error('数据不存在');
}
if ($row->getAttr('run_type') !== 'manual') {
$this->error('只有manual类型的任务支持手动触发');
}
if ($row->getAttr('status') != 1) {
$this->error('任务已停用,请先启用');
}
try {
$row->save(['manual_trigger' => 1]);
} catch (\Exception $e) {
$this->error('触发失败:' . $e->getMessage());
}
$this->success('触发成功,等待定时器执行');
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace app\admin\controller\system;
use app\common\controller\AdminController;
use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnotation;
use think\App;
/**
* @ControllerAnnotation(title="定时器执行日志")
*/
class TimerLog extends AdminController
{
use \app\admin\traits\Curd;
protected $sort = [
'id' => 'desc',
];
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new \app\admin\model\SystemTimerLog();
}
/**
* @NodeAnotation(title="列表")
*/
public function index()
{
if ($this->request->isAjax()) {
if (input('selectFields')) {
return $this->selectList();
}
list($page, $limit, $where, $excludes, $request_options, $group) = $this->buildTableParames();
$count = $this->model
->where($where)
->group($group)
->count();
$list = $this->model
->where($where)
->page($page, $limit)
->order($this->sort)
->group($group)
->select();
$data = [
'code' => 0,
'msg' => '',
'count' => $count,
'data' => $list,
];
return json($data);
}
return $this->fetch();
}
/**
* @NodeAnotation(title="详情")
*/
public function read($id)
{
$row = $this->model->find($id);
empty($row) && $this->error('数据不存在');
$title = $row->title;
$this->assign('row', $row);
$this->assign('title', $title);
return $this->fetch();
}
/**
* @NodeAnotation(title="添加")
*/
public function add()
{
$this->error('日志表不允许手动添加');
}
/**
* @NodeAnotation(title="编辑")
*/
public function edit($id)
{
$this->error('日志表不允许编辑');
}
/**
* @NodeAnotation(title="删除")
*/
public function delete($id)
{
$this->error('日志表不允许删除');
}
/**
* @NodeAnotation(title="属性修改")
*/
public function modify()
{
$this->error('日志表不允许修改');
}
}

View File

@@ -6,7 +6,7 @@ use app\admin\service\annotation\ControllerAnnotation;
use base\admin\controller\system\UploadfileBase;
/**
* @ControllerAnnotation(title="上传文件管理")
* @ControllerAnnotation(title="上传文件管理",module="系统")
* Class Uploadfile
*/
class Uploadfile extends UploadfileBase

View File

@@ -3,6 +3,9 @@
// !当前文件的内容应当与 /extend/base/admin/middleware.php 的内容一致,然后再根据实际情况设置
// 全局中间件定义文件
use think\middleware\AllowCrossDomain;
return [
// Session初始化
@@ -14,4 +17,6 @@ return [
// Csrf安全校验
\app\admin\middleware\CsrfMiddleware::class,
AllowCrossDomain::class,
];

View File

@@ -7,3 +7,4 @@ use base\admin\model\SystemConfigBase;
class SystemConfig extends SystemConfigBase
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace app\admin\model;
use base\admin\model\SystemHostBase;
class SystemHost extends SystemHostBase
{
}

View File

@@ -1,9 +0,0 @@
<?php
namespace app\admin\model;
use base\admin\model\SystemNodeBase;
class SystemNode extends SystemNodeBase
{
}

View File

@@ -0,0 +1,37 @@
<?php
namespace app\admin\model;
use app\common\model\TimeModel;
/**
* @property int $id
* @property string $task_name 任务名称
* @property string $run_type 运行类型:main/auto/all/manual main:main,auto:auto,all:all,manual:manual
* @property int $status 状态:0=停用,1=启用 0:停用,1:启用
* @property int $is_synced 是否已同步:0=未同步,1=已同步 0:未同步,1:已同步
* @property string $last_execute_node 最后执行节点ID
* @property int $last_execute_time 最后执行时间戳
* @property int $manual_trigger 手动触发标记:0=未触发,1=已触发 0:未触发,1:已触发
* @property int $create_time 创建时间
*/
class SystemTimerConfig extends TimeModel
{
protected $name = "system_timer_config";
protected $deleteTime = false;
public const SELECT_LIST_RUN_TYPE = ['main' => 'main', 'auto' => 'auto', 'all' => 'all', 'manual' => 'manual'];
public const SELECT_LIST_STATUS = ['0' => '停用', '1' => '启用'];
public const SELECT_LIST_IS_SYNCED = ['0' => '未同步', '1' => '已同步'];
public const SELECT_LIST_MANUAL_TRIGGER = ['0' => '未触发', '1' => '已触发'];
}

View File

@@ -0,0 +1,31 @@
<?php
namespace app\admin\model;
use app\common\model\TimeModel;
/**
* @property int $id
* @property string $task_name 任务名称
* @property string $node_id 执行节点ID
* @property string $run_type 运行类型
* @property int $start_time 开始时间戳
* @property int $end_time 结束时间戳
* @property int $duration 耗时
* @property string $status 状态:running/success/error
* @property string $error_message 错误信息
* @property int $concurrency_id 并发分片ID
* @property int $create_time 创建时间
*/
class SystemTimerLog extends TimeModel
{
protected $name = "system_timer_log";
protected $deleteTime = false;
}

View File

@@ -0,0 +1,41 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_debug_log', comment: '')]
#[Index(columns: ['uid'], name: 'uid', type: 'NORMAL')]
class DebugLog extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'char', length: 30, precision: 30, default: '')]
public $uid;
#[Field(type: 'char', length: 255, precision: 255, default: '')]
public $level;
#[Field(type: 'longtext')]
public $content;
#[Field(type: 'char', length: 30, precision: 30, default: '')]
public $create_time_title;
#[Field(type: 'int', length: 11, default: '0')]
public $create_time;
#[Field(type: 'char', length: 30, precision: 30, default: '')]
public $app_name;
#[Field(type: 'char', length: 30, precision: 30, default: '')]
public $controller_name;
#[Field(type: 'char', length: 30, precision: 30, default: '')]
public $action_name;
}

View File

@@ -0,0 +1,44 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_mall_cate', comment: '商品分类')]
#[Index(columns: ['title'], name: 'title', type: 'NORMAL')]
#[Index(columns: ['delete_time'], name: 'delete_time', type: 'NORMAL')]
class MallCate extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'char', length: 20, precision: 20, default: '', comment: '分类名')]
public $title;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '分类图片')]
#[Component(type: 'image', options: [])]
public $image;
#[Field(type: 'int', length: 11, default: '100', comment: '排序')]
public $sort;
#[Field(type: 'int', length: 11, default: '2', comment: '状态')]
#[Component(type: 'radio', options: [1 => '禁用', 2 => '启用'])]
public $status;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '备注说明')]
public $remark;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $delete_time;
}

View File

@@ -0,0 +1,88 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_mall_goods', comment: '商品列表')]
#[Index(columns: ['cate_id'], name: 'cate_id', type: 'NORMAL')]
#[Index(columns: ['delete_time'], name: 'delete_time', type: 'NORMAL')]
class MallGoods extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'bigint', length: 11, comment: '分类ID', unsigned: true)]
public $cate_id;
#[Field(type: 'char', length: 20, precision: 20, default: '', comment: '商品名称')]
public $title;
#[Field(type: 'char', length: 100, precision: 100, default: '', comment: '商品标签')]
public $tag;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '商品logo')]
#[Component(type: 'image', options: [])]
public $logo;
#[Field(type: 'text', comment: '商品图片')]
#[Component(type: 'images', options: [])]
public $images;
#[Field(type: 'text', comment: '商品描述')]
#[Component(type: 'editor', options: [])]
public $describe;
#[Field(type: 'text', comment: '商品属性')]
public $property;
#[Field(type: 'decimal', length: 8, precision: 8, scale: 2, default: '0.00', comment: '市场价')]
public $market_price;
#[Field(type: 'decimal', length: 8, precision: 8, scale: 2, default: '0.00', comment: '折扣价')]
public $discount_price;
#[Field(type: 'int', length: 11, default: '0', comment: '销量', unsigned: true)]
public $sales;
#[Field(type: 'int', length: 11, default: '0', comment: '虚拟销量', unsigned: true)]
public $virtual_sales;
#[Field(type: 'int', length: 11, default: '0', comment: '库存', unsigned: true)]
public $stock;
#[Field(type: 'int', length: 11, default: '0', comment: '总库存', unsigned: true)]
public $total_stock;
#[Field(type: 'int', length: 11, default: '100', comment: '排序', unsigned: true)]
public $sort;
#[Field(type: 'int', length: 11, default: '0', comment: '状态', unsigned: true)]
#[Component(type: 'radio', options: ['正常', '禁用'])]
public $status;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '备注说明')]
public $remark;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $delete_time;
#[Field(length: 100, precision: 100, nullable: false, default: '')]
public $license;
#[Field(length: 100, precision: 100, nullable: false, default: '')]
public $license_name;
#[Field(type: 'text', comment: '属性(静态字段)')]
public $property_static;
}

View File

@@ -0,0 +1,29 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_mall_tag', comment: '商品标签')]
#[Index(columns: ['delete_time'], name: 'delete_time', type: 'NORMAL')]
class MallTag extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'char', length: 20, precision: 20, default: '', comment: '商品名称')]
public $title;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $delete_time;
}

View File

@@ -0,0 +1,59 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_admin', comment: '系统用户表')]
#[Index(columns: ['username'], name: 'username', type: 'NORMAL')]
#[Index(columns: ['phone'], name: 'phone', type: 'NORMAL')]
#[Index(columns: ['delete_time'], name: 'delete_time', type: 'NORMAL')]
class SystemAdmin extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '角色权限ID')]
public $auth_ids;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '头像')]
public $head_img;
#[Field(type: 'char', length: 50, precision: 50, default: '', comment: '用户登录名')]
public $username;
#[Field(type: 'char', length: 40, precision: 40, default: '', comment: '用户密码')]
public $password;
#[Field(type: 'char', length: 16, precision: 16, default: '', comment: '联系手机号')]
public $phone;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '备注说明')]
public $remark;
#[Field(type: 'bigint', length: 11, default: '0', comment: '登录次数', unsigned: true)]
public $login_num;
#[Field(type: 'int', length: 11, default: '100', comment: '排序')]
public $sort;
#[Field(type: 'int', length: 11, default: '1', comment: '状态', unsigned: true)]
#[Component(type: 'radio', options: ['禁用', '启用', ''])]
public $status;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $delete_time;
#[Field(length: 50, precision: 50, default: '', comment: '昵称')]
public $nickname;
}

View File

@@ -0,0 +1,40 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_auth', comment: '系统权限表')]
#[Index(columns: ['title'], name: 'title', type: 'UNIQUE')]
#[Index(columns: ['delete_time'], name: 'delete_time', type: 'NORMAL')]
class SystemAuth extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'char', length: 20, precision: 20, comment: '权限名称')]
public $title;
#[Field(type: 'int', length: 11, default: '100', comment: '排序')]
public $sort;
#[Field(type: 'int', length: 11, default: '0', comment: '状态')]
#[Component(type: 'radio', options: [1 => '禁用', 2 => '启用'])]
public $status;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '备注说明')]
public $remark;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $delete_time;
}

View File

@@ -0,0 +1,23 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_auth_node', comment: '角色与节点关系表')]
#[Index(columns: ['auth_id'], name: 'auth_id', type: 'NORMAL')]
class SystemAuthNode extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'bigint', length: 11, comment: '角色ID', unsigned: true)]
public $auth_id;
#[Field(type: 'char', length: 100, precision: 100, default: '')]
public $node;
}

View File

@@ -0,0 +1,39 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_config', comment: '系统配置表')]
#[Index(columns: ['name'], name: 'name', type: 'NORMAL')]
#[Index(columns: ['group'], name: 'group', type: 'NORMAL')]
class SystemConfig extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'char', length: 30, precision: 30, default: '', comment: '变量名')]
public $name;
#[Field(type: 'char', length: 30, precision: 30, default: '', comment: '分组')]
public $group;
#[Field(type: 'text', comment: '变量值')]
public $value;
#[Field(type: 'char', length: 100, precision: 100, default: '', comment: '备注信息')]
public $remark;
#[Field(type: 'int', length: 11, default: '100', comment: '排序')]
public $sort;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
}

View File

@@ -0,0 +1,41 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_data', comment: '')]
#[Index(columns: ['key'], name: 'ul_system_data_key_IDX', type: 'NORMAL')]
#[Index(columns: ['admin_id'], name: 'ul_system_data_admin_id_IDX', type: 'NORMAL')]
class SystemData extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
#[Field(length: 100, precision: 100, nullable: false)]
public $key;
#[Field(type: 'bigint', length: 11, nullable: false, comment: '用户', unsigned: true)]
public $admin_id;
#[Field(length: 100, precision: 100, default: 'system', comment: '类型')]
#[Component(type: 'select', options: ['system' => '系统产生的数据', 'temp' => '临时数据', 'user' => '用户存储的数据'])]
public $type;
#[Field(type: 'text', comment: '值')]
#[Component(type: 'textarea', options: [])]
public $value;
#[Field(type: 'bigint', length: 11, nullable: false, default: '0', comment: '过期时间', unsigned: true)]
public $expire_time;
}

View File

@@ -0,0 +1,69 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_host', comment: '系统节点表')]
#[Index(columns: ['node_id'], name: 'node_id', type: 'UNIQUE')]
class SystemHost extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(length: 100, precision: 100, nullable: false, comment: '节点ID')]
#[Component(type: 'text', options: [])]
public $node_id;
#[Field(length: 45, precision: 45, comment: 'IP地址')]
#[Component(type: 'text', options: [])]
public $ip_address;
#[Field(type: 'int', length: 11, nullable: false, default: '1', comment: '状态')]
#[Component(type: 'switch', options: ['离线', '在线'])]
public $status;
#[Field(type: 'tinyint', length: 4, default: '0', comment: '是否主节点:0=否,1=是')]
#[Component(type: 'switch', options: ['否', '是'])]
public $is_master;
#[Field(type: 'datetime', comment: '最后心跳时间')]
#[Component(type: 'date', options: [])]
public $last_heartbeat_at;
#[Field(length: 255, precision: 255, comment: '系统信息')]
#[Component(type: 'text', options: [])]
public $os_info;
#[Field(length: 50, precision: 50, comment: 'PHP版本')]
#[Component(type: 'text', options: [])]
public $php_version;
#[Field(length: 50, precision: 50, comment: 'CPU负载')]
#[Component(type: 'text', options: [])]
public $cpu_load;
#[Field(type: 'bigint', length: 11, comment: '内存占用(byte)')]
#[Component(type: 'text', options: [])]
public $memory_usage;
#[Field(type: 'bigint', length: 11, comment: '磁盘可用空间(byte)')]
#[Component(type: 'text', options: [])]
public $disk_free;
#[Field(type: 'bigint', length: 11, comment: '磁盘总空间(byte)')]
#[Component(type: 'text', options: [])]
public $disk_total;
#[Field(type: 'int', length: 11, comment: '首次运行时间', unsigned: true)]
#[Component(type: 'date', options: [])]
public $create_time;
#[Field(type: 'int', length: 11, comment: '更新时间', unsigned: true)]
#[Component(type: 'date', options: [])]
public $update_time;
}

View File

@@ -0,0 +1,59 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_menu', comment: '系统菜单表')]
#[Index(columns: ['title'], name: 'title', type: 'NORMAL')]
#[Index(columns: ['href'], name: 'href', type: 'NORMAL')]
#[Index(columns: ['delete_time'], name: 'delete_time', type: 'NORMAL')]
class SystemMenu extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'bigint', length: 11, default: '0', comment: '父id', unsigned: true)]
public $pid;
#[Field(type: 'char', length: 100, precision: 100, default: '', comment: '名称')]
public $title;
#[Field(type: 'char', length: 100, precision: 100, default: '', comment: '菜单图标')]
public $icon;
#[Field(type: 'char', length: 100, precision: 100, default: '', comment: '链接')]
public $href;
#[Field(type: 'char', length: 100, precision: 100, default: '', comment: '权限标志')]
public $auth_node;
#[Field(type: 'text', comment: '链接参数')]
public $params;
#[Field(type: 'char', length: 20, precision: 20, default: '_self', comment: '链接打开方式')]
public $target;
#[Field(type: 'int', length: 11, default: '100', comment: '排序')]
public $sort;
#[Field(type: 'int', length: 11, default: '1', comment: '状态')]
#[Component(type: 'radio', options: ['禁用', '启用'])]
public $status;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '备注说明')]
public $remark;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $delete_time;
}

View File

@@ -0,0 +1,35 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_node', comment: '')]
#[Index(columns: ['node'], name: 'node', type: 'NORMAL')]
class SystemNode extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'char', length: 100, precision: 100, default: '', comment: '节点代码')]
public $node;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '节点标题')]
public $title;
#[Field(type: 'int', length: 11, comment: '节点类型1控制器2节点', unsigned: true)]
public $type;
#[Field(type: 'int', length: 11, default: '1', comment: '是否启动RBAC权限控制', unsigned: true)]
public $is_auth;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
}

View File

@@ -0,0 +1,45 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_quick', comment: '系统快捷入口表')]
#[Index(columns: ['delete_time'], name: 'delete_time', type: 'NORMAL')]
class SystemQuick extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'char', length: 20, precision: 20, default: '', comment: '快捷入口名称')]
public $title;
#[Field(type: 'char', length: 100, precision: 100, default: '', comment: '图标')]
public $icon;
#[Field(type: 'char', length: 255, precision: 255, comment: '快捷链接')]
public $href;
#[Field(type: 'int', length: 11, default: '100', comment: '排序')]
public $sort;
#[Field(type: 'int', length: 11, default: '1', comment: '状态', unsigned: true)]
#[Component(type: 'radio', options: ['禁用', '启用'])]
public $status;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '备注说明')]
public $remark;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $delete_time;
}

View File

@@ -0,0 +1,53 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_timer_config', comment: '定时任务协调配置表')]
#[Index(columns: ['task_name'], name: 'task_name', type: 'UNIQUE')]
class SystemTimerConfig extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'varchar', length: 100, nullable: false, comment: '任务名称')]
#[Component(type: 'text', options: [])]
public $task_name;
#[Field(type: 'varchar', length: 20, default: 'auto', comment: '运行类型:main/auto/all/manual')]
#[Component(type: 'select', options: ['main' => 'main', 'auto' => 'auto', 'all' => 'all', 'manual' => 'manual'])]
public $run_type;
#[Field(type: 'tinyint', length: 4, default: '1', comment: '状态:0=停用,1=启用')]
#[Component(type: 'switch', options: ['停用', '启用'])]
public $status;
#[Field(type: 'tinyint', length: 4, default: '0', comment: '是否已同步:0=未同步,1=已同步')]
#[Component(type: 'switch', options: ['未同步', '已同步'])]
public $is_synced;
#[Field(type: 'varchar', length: 100, nullable: true, comment: '最后执行节点ID')]
#[Component(type: 'text', options: [])]
public $last_execute_node;
#[Field(type: 'int', length: 11, default: '0', comment: '最后执行时间戳', unsigned: true)]
#[Component(type: 'date', options: [])]
public $last_execute_time;
#[Field(type: 'tinyint', length: 4, default: '0', comment: '手动触发标记:0=未触发,1=已触发')]
#[Component(type: 'switch', options: ['未触发', '已触发'])]
public $manual_trigger;
#[Field(type: 'int', length: 11, comment: '创建时间', unsigned: true)]
#[Component(type: 'date', options: [])]
public $create_time;
#[Field(type: 'int', length: 11, comment: '更新时间', unsigned: true)]
#[Component(type: 'date', options: [])]
public $update_time;
}

View File

@@ -0,0 +1,64 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_timer_log', comment: '定时器执行日志表')]
#[Index(columns: ['task_name'], name: 'idx_task_name', type: 'NORMAL')]
#[Index(columns: ['node_id'], name: 'idx_node_id', type: 'NORMAL')]
#[Index(columns: ['start_time'], name: 'idx_start_time', type: 'NORMAL')]
#[Index(columns: ['status'], name: 'idx_status', type: 'NORMAL')]
class SystemTimerLog extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(length: 100, precision: 100, nullable: false, comment: '任务名称')]
#[Component(type: 'text', options: [])]
public $task_name;
#[Field(length: 100, precision: 100, nullable: false, comment: '执行节点ID')]
#[Component(type: 'text', options: [])]
public $node_id;
#[Field(length: 20, precision: 20, comment: '运行类型')]
#[Component(type: 'text', options: [])]
public $run_type;
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, comment: '开始时间戳')]
#[Component(type: 'text', options: [])]
public $start_time;
#[Field(type: 'int', length: 11, unsigned: true, default: '0', comment: '结束时间戳')]
#[Component(type: 'text', options: [])]
public $end_time;
#[Field(type: 'int', length: 11, unsigned: true, default: '0', comment: '耗时(毫秒)')]
#[Component(type: 'text', options: [])]
public $duration;
#[Field(length: 20, precision: 20, default: 'running', comment: '状态:running/success/error')]
#[Component(type: 'text', options: [])]
public $status;
#[Field(type: 'text', nullable: true, comment: '错误信息')]
#[Component(type: 'textarea', options: [])]
public $error_message;
#[Field(type: 'text', nullable: true, comment: '执行结果(任务返回值)')]
#[Component(type: 'textarea', options: [])]
public $result;
#[Field(type: 'int', length: 11, default: '0', comment: '并发分片ID')]
#[Component(type: 'text', options: [])]
public $concurrency_id;
#[Field(type: 'int', length: 11, unsigned: true, comment: '创建时间')]
#[Component(type: 'date', options: [])]
public $create_time;
}

View File

@@ -0,0 +1,61 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_system_uploadfile', comment: '上传文件表')]
#[Index(columns: ['upload_type'], name: 'upload_type', type: 'NORMAL')]
#[Index(columns: ['original_name'], name: 'original_name', type: 'NORMAL')]
#[Index(columns: ['delete_time'], name: 'delete_time', type: 'NORMAL')]
class SystemUploadfile extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'char', length: 20, precision: 20, default: 'local', comment: '存储位置')]
public $upload_type;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '文件原名')]
public $original_name;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '链接')]
public $url;
#[Field(type: 'char', length: 255, precision: 255, default: '', comment: '存储名称')]
public $save_name;
#[Field(type: 'char', length: 30, precision: 30, default: '', comment: '宽度')]
public $image_width;
#[Field(type: 'char', length: 30, precision: 30, default: '', comment: '高度')]
public $image_height;
#[Field(type: 'int', length: 11, default: '0', comment: '图片帧数', unsigned: true)]
public $image_frames;
#[Field(type: 'char', length: 100, precision: 100, default: '', comment: 'mime类型')]
public $mime_type;
#[Field(type: 'int', length: 11, default: '0', comment: '文件大小', unsigned: true)]
public $file_size;
#[Field(type: 'char', length: 100, precision: 100, default: '', comment: '扩展名')]
public $file_ext;
#[Field(type: 'char', length: 40, precision: 40, default: '', comment: '文件 sha1编码')]
public $sha1;
#[Field(type: 'int', length: 11, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $delete_time;
}

View File

@@ -0,0 +1,121 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_test_goods', comment: '商品列表')]
#[Index(columns: ['uid'], name: 'uid', type: 'UNIQUE')]
#[Index(columns: ['cate_id'], name: 'cate_id', type: 'NORMAL')]
#[Index(columns: ['detail'], name: 'detail', type: 'FULLTEXT')]
class TestGoods extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'bigint', length: 11, nullable: false, default: '0', comment: '分类ID', unsigned: true)]
#[Component(type: 'relation', options: ['table' => 'mall_cate', 'relationBindSelect' => 'title'])]
public $cate_id;
#[Field(type: 'char', length: 20, precision: 20, nullable: false, default: '', comment: '商品名称')]
public $title;
#[Field(type: 'char', length: 255, precision: 255, nullable: false, comment: '商品logo')]
#[Component(type: 'image', options: [])]
public $logo;
#[Field(type: 'text', nullable: false, comment: '商品图片')]
#[Component(type: 'images', options: [])]
public $images;
#[Field(type: 'text', nullable: false, comment: '商品描述')]
#[Component(type: 'editor', options: [])]
public $describe;
#[Field(type: 'int', length: 11, nullable: false, default: '0', comment: '总库存', unsigned: true)]
public $total_stock;
#[Field(type: 'int', length: 11, nullable: false, default: '100', comment: '排序', unsigned: true)]
public $sort;
#[Field(type: 'int', length: 11, nullable: false, default: '0', comment: '状态', unsigned: true)]
#[Component(type: 'radio', options: ['正常', '禁用'])]
public $status;
#[Field(length: 100, precision: 100, nullable: false, comment: '合格证')]
#[Component(type: 'file', options: [])]
public $cert_file;
#[Field(type: 'text', nullable: false, comment: '检测报告')]
#[Component(type: 'files', options: [])]
public $verfiy_file;
#[Field(type: 'char', length: 255, precision: 255, nullable: false, default: '', comment: '备注说明')]
public $remark;
#[Field(type: 'int', length: 11, nullable: false, default: '0', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, nullable: false, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, nullable: false, default: '0', unsigned: true)]
public $delete_time;
#[Field(type: 'int', length: 11, nullable: false, comment: '发布日期', unsigned: true)]
#[Component(type: 'date', options: ['date'])]
public $publish_time;
#[Field(type: 'bigint', length: 11, nullable: false, comment: '售卖日期', unsigned: true)]
#[Component(type: 'date', options: ['datetime'])]
public $sale_time;
#[Field(length: 100, precision: 100, nullable: false, comment: '简介')]
#[Component(type: 'textarea', options: [])]
public $intro;
#[Field(type: 'int', length: 11, nullable: false, comment: '秒杀状态', unsigned: true)]
#[Component(type: 'select', options: [0 => '未参加', 1 => '已开始', 3 => '已结束'])]
public $time_status;
#[Field(type: 'int', length: 11, nullable: false, comment: '是否推荐')]
#[Component(type: 'switch', options: ['不推荐', '推荐'])]
public $is_recommend;
#[Field(length: 100, precision: 100, nullable: false, comment: '商品类型')]
#[Component(type: 'checkbox', options: ['taobao' => '淘宝', 'jd' => '京东'])]
public $shop_type;
#[Field(length: 100, precision: 100, nullable: false, comment: '商品标签')]
#[Component(type: 'table', options: ['table' => 'mall_tag', 'type' => 'checkbox', 'valueField' => 'id', 'fieldName' => 'title'])]
public $tag;
#[Field(length: 100, precision: 100, comment: '商品标签(单选)')]
#[Component(type: 'table', options: ['table' => 'mall_tag', 'type' => 'radio', 'valueField' => 'id', 'fieldName' => 'title'])]
public $tag_backup;
#[Field(length: 100, precision: 100, nullable: false, comment: '产地')]
#[Component(type: 'city', options: ['name-province' => '0', 'code' => '0'])]
public $from_area;
#[Field(length: 100, precision: 100, nullable: false, default: '山东省/临沂市', comment: '仓库')]
#[Component(type: 'city', options: ['level' => 'city'])]
public $store_city;
#[Field(length: 100, precision: 100, nullable: false, comment: '商品标签 (输入)')]
#[Component(type: 'tag', options: [])]
public $tag_input;
#[Field(length: 100, precision: 100, nullable: false, comment: '唯一id')]
public $uid;
#[Field(type: 'decimal', length: 10, precision: 10, scale: 2, comment: '价格')]
public $price;
#[Field(type: 'text', comment: '详情')]
public $detail;
}

View File

@@ -0,0 +1,55 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_tree_tree', comment: '')]
#[Index(columns: ['path'], name: 'ul_tree_tree_path_IDX', type: 'NORMAL')]
#[Index(columns: ['pid'], name: 'ul_tree_tree_pid_IDX', type: 'NORMAL')]
#[Index(columns: ['title'], name: 'ul_tree_tree_title_IDX', type: 'NORMAL')]
class TreeTree extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, comment: 'ID', autoIncrement: true, primary: true)]
public $id;
#[Field(length: 255, precision: 255, nullable: false, default: '', comment: '分类名称')]
#[Component(type: 'text', options: [])]
public $title;
#[Field(type: 'int', length: 11, comment: '父级ID')]
public $pid;
#[Field(type: 'int', length: 11, nullable: false, default: '1', comment: '分类层级')]
public $level;
#[Field(length: 255, precision: 255, nullable: false, comment: '分类路径')]
public $path;
#[Field(length: 50, precision: 50, nullable: false, default: 'default', comment: '类型')]
public $type;
#[Field(type: 'tinyint', length: 11, nullable: false, default: '1', comment: '状态')]
#[Component(type: 'switch', options: ['禁用', '启用'])]
public $status;
#[Field(length: 255, precision: 255, default: '', comment: '分类图片')]
#[Component(type: 'image', options: [])]
public $image;
#[Field(type: 'int', length: 11, nullable: false, default: '0', comment: '创建时间', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, nullable: false, default: '0', comment: '更新时间', unsigned: true)]
public $update_time;
#[Field(length: 100, precision: 100, comment: '备注')]
public $comment;
#[Field(type: 'int', length: 11, default: '0', unsigned: true)]
public $delete_time;
}

View File

@@ -0,0 +1,120 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_ulthon_demo_goods', comment: '')]
#[Index(columns: ['cate_id'], name: 'cate_id', type: 'NORMAL')]
#[Index(columns: ['detail'], name: 'detail', type: 'FULLTEXT')]
class UlthonDemoGoods extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'bigint', length: 11, nullable: false, default: '0', comment: '分类ID', unsigned: true)]
#[Component(type: 'relation', options: ['table' => 'mall_cate', 'relationBindSelect' => 'title'])]
public $cate_id;
#[Field(type: 'char', length: 20, precision: 20, nullable: false, default: '', comment: '商品名称')]
public $title;
#[Field(type: 'char', length: 255, precision: 255, nullable: false, comment: '商品logo')]
#[Component(type: 'image', options: [])]
public $logo;
#[Field(type: 'text', comment: '商品图片')]
#[Component(type: 'images', options: [])]
public $images;
#[Field(type: 'text', comment: '商品描述')]
#[Component(type: 'editor', options: [])]
public $describe;
#[Field(type: 'int', length: 11, nullable: false, default: '0', comment: '总库存', unsigned: true)]
public $total_stock;
#[Field(type: 'int', length: 11, nullable: false, default: '0', comment: '排序', unsigned: true)]
public $sort;
#[Field(type: 'int', length: 11, nullable: false, default: '0', comment: '状态', unsigned: true)]
#[Component(type: 'radio', options: ['正常', '禁用'])]
public $status;
#[Field(length: 100, precision: 100, nullable: false, default: '', comment: '合格证')]
#[Component(type: 'file', options: [])]
public $cert_file;
#[Field(type: 'text', comment: '检测报告')]
#[Component(type: 'files', options: [])]
public $verfiy_file;
#[Field(type: 'char', length: 255, precision: 255, nullable: false, default: '', comment: '备注说明')]
public $remark;
#[Field(type: 'int', length: 11, nullable: false, default: '0', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, nullable: false, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, nullable: false, default: '0', unsigned: true)]
public $delete_time;
#[Field(type: 'int', length: 11, nullable: false, comment: '发布日期', unsigned: true)]
#[Component(type: 'date', options: ['date'])]
public $publish_time;
#[Field(type: 'bigint', length: 11, nullable: false, default: '0', comment: '售卖日期', unsigned: true)]
#[Component(type: 'date', options: ['datetime'])]
public $sale_time;
#[Field(length: 100, precision: 100, nullable: false, default: '', comment: '简介')]
#[Component(type: 'textarea', options: [])]
public $intro;
#[Field(type: 'int', length: 11, nullable: false, default: '0', comment: '秒杀状态', unsigned: true)]
#[Component(type: 'select', options: [0 => '未参加', 1 => '已开始', 3 => '已结束'])]
public $time_status;
#[Field(type: 'int', length: 11, nullable: false, default: '0', comment: '是否推荐')]
#[Component(type: 'switch', options: ['不推荐', '推荐'])]
public $is_recommend;
#[Field(length: 100, precision: 100, nullable: false, default: '', comment: '商品类型')]
#[Component(type: 'checkbox', options: ['taobao' => '淘宝', 'jd' => '京东'])]
public $shop_type;
#[Field(length: 100, precision: 100, nullable: false, default: '', comment: '商品标签')]
#[Component(type: 'table', options: ['table' => 'mall_tag', 'type' => 'checkbox', 'valueField' => 'id', 'fieldName' => 'title'])]
public $tag;
#[Field(length: 100, precision: 100, default: '', comment: '商品标签(单选)')]
#[Component(type: 'table', options: ['table' => 'mall_tag', 'type' => 'radio', 'valueField' => 'id', 'fieldName' => 'title'])]
public $tag_backup;
#[Field(length: 100, precision: 100, nullable: false, default: '', comment: '产地')]
#[Component(type: 'city', options: ['name-province' => '0', 'code' => '0'])]
public $from_area;
#[Field(length: 100, precision: 100, nullable: false, default: '山东省/临沂市', comment: '仓库')]
#[Component(type: 'city', options: ['level' => 'city'])]
public $store_city;
#[Field(length: 100, precision: 100, nullable: false, default: '', comment: '商品标签 (输入)')]
#[Component(type: 'tag', options: [])]
public $tag_input;
#[Field(length: 100, precision: 100, nullable: false, default: '', comment: '唯一id')]
public $uid;
#[Field(type: 'decimal', length: 10, precision: 10, default: '0', comment: '价格')]
public $price;
#[Field(type: 'text', comment: '详情')]
public $detail;
}

View File

@@ -0,0 +1,52 @@
<?php
namespace app\admin\scheme;
use app\common\scheme\BaseScheme;
use app\common\scheme\attribute\Table;
use app\common\scheme\attribute\Field;
use app\common\scheme\attribute\Component;
use app\common\scheme\attribute\Index;
#[Table(name: 'ul_ulthon_demo_goods_batch', comment: '')]
class UlthonDemoGoodsBatch extends BaseScheme
{
#[Field(type: 'int', length: 11, nullable: false, unsigned: true, autoIncrement: true, primary: true)]
public $id;
#[Field(type: 'int', length: 11, nullable: false, default: '0', unsigned: true)]
public $create_time;
#[Field(type: 'int', length: 11, nullable: false, default: '0', unsigned: true)]
public $update_time;
#[Field(type: 'int', length: 11, nullable: false, default: '0', unsigned: true)]
public $delete_time;
#[Field(type: 'bigint', length: 11, unsigned: true)]
public $goods_id;
#[Field(length: 100, precision: 100, default: '', comment: '批次名称')]
public $title;
#[Field(length: 100, precision: 100, default: '', comment: '批次编号')]
public $num;
#[Field(type: 'tinyint', length: 11, default: '1', comment: '状态', unsigned: true)]
#[Component(type: 'switch', options: ['关闭', '开启'])]
public $status;
#[Field(length: 100, precision: 100, default: '', comment: '备注')]
#[Component(type: 'textarea', options: [])]
public $comment;
#[Field(type: 'bigint', length: 11, default: '0', comment: '库存')]
public $stock;
#[Field(length: 100, precision: 100, comment: '包装')]
#[Component(type: 'image', options: [])]
public $title_image;
#[Field(length: 100, precision: 100)]
public $add_flag;
}

View File

View File

View File

@@ -0,0 +1,12 @@
var init = {
tableElem: '#currentTable',
tableRenderId: 'currentTableRenderId',
indexUrl: 'system.timer_config/index',
addUrl: 'system.timer_config/add' + location.search,
editUrl: 'system.timer_config/edit',
readUrl: 'system.timer_config/read',
deleteUrl: 'system.timer_config/delete',
exportUrl: 'system.timer_config/export',
modifyUrl: 'system.timer_config/modify',
triggerUrl: 'system.timer_config/trigger',
};

View File

@@ -0,0 +1,70 @@
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label">任务名称</label>
<div class="layui-input-block">
<input type="text" name="task_name" class="layui-input" lay-verify="required" placeholder="请输入任务名称" value="{$Request.param.task_name|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">运行类型:main/auto/all/manual</label>
<div class="layui-input-block">
<select name="run_type" >
<option value=''></option>
{foreach $select_list_run_type as $k=>$v}
<option value='{$k}' {in name="k" value="$Request.param.run_type"}selected=""{/in}>{$v}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态:0=停用,1=启用</label>
<div class="layui-input-block">
{foreach $select_list_status as $k=>$v}
<input type="radio" name="status" value="{$k}" title="{$v}" {in name="k" value="$Request.param.status"}checked=""{/in}>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否已同步:0=未同步,1=已同步</label>
<div class="layui-input-block">
{foreach $select_list_is_synced as $k=>$v}
<input type="radio" name="is_synced" value="{$k}" title="{$v}" {in name="k" value="$Request.param.is_synced"}checked=""{/in}>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">最后执行节点ID</label>
<div class="layui-input-block">
<input type="text" name="last_execute_node" class="layui-input" placeholder="请输入最后执行节点ID" value="{$Request.param.last_execute_node|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">最后执行时间戳</label>
<div class="layui-input-block">
<input type="text" name="last_execute_time" data-date="" data-date-type="datetime" class="layui-input" placeholder="请输入最后执行时间戳" value="{$Request.param.last_execute_time|default='0'}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">手动触发标记:0=未触发,1=已触发</label>
<div class="layui-input-block">
{foreach $select_list_manual_trigger as $k=>$v}
<input type="radio" name="manual_trigger" value="{$k}" title="{$v}" {in name="k" value="$Request.param.manual_trigger"}checked=""{/in}>
{/foreach}
</div>
</div>
<div class="hr-line"></div>
<div class="layui-form-item text-center">
{notempty name='$Request.param.backTagId'}
<div class="layui-btn layui-btn-sm page-back-button" layuimini-content-href="{$Request.param.backTagId}" data-back="1">返回</div>
{/notempty}
<button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit>确认</button>
<button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">重置</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,3 @@
$(function(){
ua.listen();
})

View File

@@ -0,0 +1,69 @@
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label">任务名称</label>
<div class="layui-input-block">
<input type="text" name="task_name" class="layui-input layui-disabled" disabled value="{$row.task_name|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">运行类型</label>
<div class="layui-input-block">
<select name="run_type" lay-verify="required">
<option value=''></option>
{foreach $select_list_run_type as $k=>$v}
<option value='{$k}' {in name="k" value="$row.run_type"}selected=""{/in}>{$v}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
{foreach $select_list_status as $k=>$v}
<input type="radio" name="status" value="{$k}" title="{$v}" {in name="k" value="$row.status"}checked=""{/in}>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">同步状态</label>
<div class="layui-input-block" style="padding-top: 8px;">
{if $row.is_synced == 1}
<span class="layui-badge layui-bg-green">已同步</span>
{else/}
<span class="layui-badge">未同步</span>
{/if}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">最后执行节点</label>
<div class="layui-input-block" style="padding-top: 8px;">
{$row.last_execute_node|default='<span class="layui-text-em">暂无</span>'}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">最后执行时间</label>
<div class="layui-input-block" style="padding-top: 8px;">
{notempty name="row.last_execute_time"}
{$row.last_execute_time|date="Y-m-d H:i:s"}
{else/}
<span class="layui-text-em">暂无</span>
{/notempty}
</div>
</div>
<div class="hr-line"></div>
<div class="layui-form-item text-center">
{notempty name='$Request.param.backTagId'}
<div class="layui-btn layui-btn-sm page-back-button" layuimini-content-href="{$Request.param.backTagId}" data-back="1">返回</div>
{/notempty}
<button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit>确认</button>
<button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">重置</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,3 @@
$(function(){
ua.listen();
})

View File

@@ -0,0 +1,11 @@
<div class="layuimini-container">
<div class="layuimini-main">
<table id="currentTable" class="layui-table layui-hide"
data-auth-index="{:auth('system.timer_config/index')}"
data-auth-edit="{:auth('system.timer_config/edit')}"
data-auth-export="{:auth('system.timer_config/export')}"
data-auth-modify="{:auth('system.timer_config/modify')}"
lay-filter="currentTable">
</table>
</div>
</div>

View File

@@ -0,0 +1,78 @@
$(function(){
ua.table.render({
init: init,
cols: [[
{type: 'checkbox'},
{field: 'id', title: 'ID', width: 80},
{field: 'task_name', title: '任务名称', minWidth: 160},
{field: 'run_type', search: 'select', selectList: ua.getDataBrage('select_list_run_type'), title: '运行类型', width: 130, templet: function(d){
var map = ua.getDataBrage('select_list_run_type');
return map[d.run_type] || d.run_type;
}},
{field: 'status', search: 'select', selectList: ua.getDataBrage('select_list_status'), title: '状态', width: 100, templet: ua.table.switch},
{field: 'is_synced', search: 'select', selectList: ua.getDataBrage('select_list_is_synced'), title: '同步状态', width: 100, templet: function(d){
if(d.is_synced == 1){
return '<span class="layui-badge layui-bg-green">已同步</span>';
}
return '<span class="layui-badge">未同步</span>';
}},
{field: 'last_execute_node', title: '最后执行节点', width: 140},
{field: 'last_execute_time', title: '最后执行时间', width: 170, templet: function(d){
if(!d.last_execute_time || d.last_execute_time == 0){
return '<span class="layui-text-em">暂无</span>';
}
var date = new Date(d.last_execute_time * 1000);
var Y = date.getFullYear();
var m = ('0' + (date.getMonth()+1)).slice(-2);
var day = ('0' + date.getDate()).slice(-2);
var H = ('0' + date.getHours()).slice(-2);
var i = ('0' + date.getMinutes()).slice(-2);
var s = ('0' + date.getSeconds()).slice(-2);
return Y+'-'+m+'-'+day+' '+H+':'+i+':'+s;
}},
{
width: 200, title: '操作', templet: ua.table.tool, fixed: 'right', operat: [
[{
class: 'layui-btn layui-btn-primary layui-btn-xs',
method: 'tab',
field: 'id',
text: '详情',
title: '查看详情',
auth: 'read',
url: init.readUrl,
icon: ''
}],
'edit',
[{
class: 'layui-btn layui-btn-warm layui-btn-xs',
method: 'request',
field: 'id',
text: '触发',
title: '手动触发',
auth: 'trigger',
url: init.triggerUrl,
icon: '',
extend: '',
callback: function(o){
// 只对 manual 类型显示,通过 CSS 控制显隐
}
}]
]
},
]],
});
// 控制触发按钮显隐:只有 run_type=manual 时显示
ua.listen();
// 监听表格渲染完成后处理触发按钮显隐
$(document).on('renderComplete', function(){
$('table tbody tr').each(function(){
var runType = $(this).find('td[data-field="run_type"] .layui-table-cell').text().trim();
if(runType !== 'manual'){
$(this).find('.layui-btn[title="手动触发"]').hide();
}
});
});
})

View File

@@ -0,0 +1,103 @@
<div class="layuimini-container detail-container">
<div class="layuimini-main">
<div class="layui-card detail-card">
<div class="layui-card-header detail-header">
<div class="layui-row">
<div class="layui-col-md9">
<h2 class="detail-title">#{$row.id} {$title}</h2>
<div class="detail-id">ID: {$row.id}</div>
</div>
<div class="layui-col-md3 text-right detail-actions">
<button class="layui-btn layui-btn-primary" layuimini-content-href="{$Request.param.backTagId}" data-back="1">返回</button>
<button class="layui-btn" onclick="location.href='{:url("edit", ["id" => $row.id])}'">编辑</button>
</div>
</div>
</div>
<div class="layui-card-body detail-content">
<div class="layui-row layui-col-space12">
<div class="layui-col-md8 detail-main">
<div class="detail-field-group">
<div class="detail-field-item">
<div class="detail-field-label">任务名称</div>
<div class="detail-field-value">
{notempty name="row.task_name"}
{$row.task_name}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">运行类型</div>
<div class="detail-field-value">
{$select_list_run_type[$row.run_type]|default=''}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">同步状态</div>
<div class="detail-field-value">
{if $row.is_synced == 1}
<span class="layui-badge layui-bg-green">已同步</span>
{else/}
<span class="layui-badge">未同步</span>
{/if}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">最后执行节点</div>
<div class="detail-field-value">
{notempty name="row.last_execute_node"}
{$row.last_execute_node}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">最后执行时间</div>
<div class="detail-field-value">
{notempty name="row.last_execute_time"}
{$row.last_execute_time|date="Y-m-d H:i:s"}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">手动触发标记</div>
<div class="detail-field-value">
<span class="layui-badge">{$select_list_manual_trigger[$row.manual_trigger]|default=''}</span>
</div>
</div>
</div>
</div>
<div class="layui-col-md4 detail-side">
<h3 class="detail-side-title">基础信息</h3>
<div class="detail-field-group">
<div class="detail-field-item">
<div class="detail-field-label">ID</div>
<div class="detail-field-value">{$row.id}</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">状态</div>
<div class="detail-field-value">
<span class="layui-badge">{$select_list_status[$row.status]|default=''}</span>
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">创建时间</div>
<div class="detail-field-value">
{notempty name="row.create_time"}
{$row.create_time|date="Y-m-d H:i:s"}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,3 @@
$(function(){
ua.listen();
})

View File

@@ -0,0 +1,11 @@
var init = {
tableElem: '#currentTable',
tableRenderId: 'currentTableRenderId',
indexUrl: 'system.timer_log/index',
addUrl: '',
editUrl: '',
readUrl: 'system.timer_log/read',
deleteUrl: '',
exportUrl: '',
modifyUrl: '',
};

View File

@@ -0,0 +1,9 @@
<div class="layuimini-container">
<div class="layuimini-main">
<table id="currentTable" class="layui-table layui-hide"
data-auth-index="{:auth('system.timer_log/index')}"
data-auth-read="{:auth('system.timer_log/read')}"
lay-filter="currentTable">
</table>
</div>
</div>

View File

@@ -0,0 +1,99 @@
$(function(){
// 状态徽章模板
var statusTemplet = function(d) {
var statusMap = {
'success': {text: '成功', color: '#5FB878', bgColor: '#e8f8ef'},
'error': {text: '失败', color: '#FF5722', bgColor: '#ffe8e2'},
'running': {text: '运行中', color: '#1E9FFF', bgColor: '#e2f1ff'},
};
var item = statusMap[d.status] || {text: d.status, color: '#999', bgColor: '#f5f5f5'};
return '<span style="display:inline-block;padding:2px 10px;border-radius:3px;color:'+item.color+';background:'+item.bgColor+';font-size:12px;">'+item.text+'</span>';
};
// 时间格式化模板
var timeTemplet = function(d) {
if (!d.start_time || d.start_time === 0) return '<span style="color:#999">-</span>';
var date = new Date(d.start_time * 1000);
var Y = date.getFullYear();
var M = (date.getMonth()+1).toString().padStart(2,'0');
var D = date.getDate().toString().padStart(2,'0');
var h = date.getHours().toString().padStart(2,'0');
var m = date.getMinutes().toString().padStart(2,'0');
var s = date.getSeconds().toString().padStart(2,'0');
return Y+'-'+M+'-'+D+' '+h+':'+m+':'+s;
};
var endTimeTemplet = function(d) {
if (!d.end_time || d.end_time === 0) return '<span style="color:#999">-</span>';
var date = new Date(d.end_time * 1000);
var Y = date.getFullYear();
var M = (date.getMonth()+1).toString().padStart(2,'0');
var D = date.getDate().toString().padStart(2,'0');
var h = date.getHours().toString().padStart(2,'0');
var m = date.getMinutes().toString().padStart(2,'0');
var s = date.getSeconds().toString().padStart(2,'0');
return Y+'-'+M+'-'+D+' '+h+':'+m+':'+s;
};
// 耗时格式化模板
var durationTemplet = function(d) {
if (!d.duration || d.duration === 0) return '<span style="color:#999">-</span>';
if (d.duration < 1000) {
return d.duration + ' ms';
}
return (d.duration / 1000).toFixed(2) + ' s';
};
// 错误信息截断模板
var errorMsgTemplet = function(d) {
if (!d.error_message) return '<span style="color:#999">-</span>';
var text = d.error_message;
if (text.length > 50) {
return '<span title="'+text.replace(/"/g,'&quot;').replace(/'/g,'&#39;')+'" style="cursor:pointer;" lay-event="showError">'+text.substring(0,50)+'...</span>';
}
return text;
};
ua.table.render({
init: init,
cols: [[
{type: 'checkbox'},
{field: 'id', title: 'ID', width: 80, sort: true},
{field: 'task_name', title: '任务名称', width: 160, search: 'select', searchUrl: 'system.timer_log/index?selectFields=task_name'},
{field: 'node_id', title: '节点ID', width: 120, search: 'select', searchUrl: 'system.timer_log/index?selectFields=node_id'},
{field: 'run_type', title: '运行类型', width: 100},
{field: 'start_time', title: '开始时间', width: 170, templet: timeTemplet, search: 'range'},
{field: 'end_time', title: '结束时间', width: 170, templet: endTimeTemplet},
{field: 'duration', title: '耗时', width: 100, templet: durationTemplet},
{field: 'status', title: '状态', width: 100, templet: statusTemplet, search: 'select', selectList: {success:'成功', error:'失败', running:'运行中'}},
{field: 'error_message', title: '错误信息', minWidth: 200, templet: errorMsgTemplet},
{
width: 100, title: '操作', templet: ua.table.tool, fixed: 'right', operat: [
[{
class: 'layui-btn layui-btn-primary layui-btn-xs',
method: 'tab',
field: 'id',
text: '详情',
title: '查看详情',
auth: 'read',
url: init.readUrl,
icon: ''
}]
]
},
]],
});
// 点击错误信息查看完整内容
ua.table.on('tool(currentTable)', function(obj) {
if (obj.event === 'showError') {
var text = obj.data.error_message || '';
layer.alert('<pre style="white-space:pre-wrap;word-break:break-all;max-height:400px;overflow-y:auto;">'+text.replace(/</g,'&lt;').replace(/>/g,'&gt;')+'</pre>', {
title: '错误信息详情',
area: ['600px', '400px']
});
}
});
ua.listen();
})

View File

@@ -0,0 +1,139 @@
<div class="layuimini-container detail-container">
<div class="layuimini-main">
<div class="layui-card detail-card">
<div class="layui-card-header detail-header">
<div class="layui-row">
<div class="layui-col-md9">
<h2 class="detail-title">#{$row.id} {$title}</h2>
<div class="detail-id">ID: {$row.id}</div>
</div>
<div class="layui-col-md3 text-right detail-actions">
<button class="layui-btn layui-btn-primary" layuimini-content-href="{$Request.param.backTagId}" data-back="1">返回</button>
</div>
</div>
</div>
<div class="layui-card-body detail-content">
<div class="layui-row layui-col-space12">
<!-- 左侧主体内容 -->
<div class="layui-col-md8 detail-main">
<div class="detail-field-group">
<div class="detail-field-item">
<div class="detail-field-label">任务名称</div>
<div class="detail-field-value">
{notempty name="row.task_name"}
{$row.task_name}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">执行节点ID</div>
<div class="detail-field-value">
{notempty name="row.node_id"}
{$row.node_id}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">运行类型</div>
<div class="detail-field-value">
{notempty name="row.run_type"}
{$row.run_type}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">开始时间</div>
<div class="detail-field-value">
{notempty name="row.start_time"}
{:date('Y-m-d H:i:s', $row['start_time'])}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">结束时间</div>
<div class="detail-field-value">
{notempty name="row.end_time"}
{:date('Y-m-d H:i:s', $row['end_time'])}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">耗时</div>
<div class="detail-field-value">
{notempty name="row.duration"}
{:php echo ($row['duration'] < 1000) ? $row['duration'].' ms' : round($row['duration']/1000, 2).' s';}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">错误信息</div>
<div class="detail-field-value" style="white-space: pre-wrap;">
{notempty name="row.error_message"}
{$row.error_message|raw}
{else/}
<span class="layui-text-em">暂无内容</span>
{/notempty}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">并发分片ID</div>
<div class="detail-field-value">
{notempty name="row.concurrency_id"}
{$row.concurrency_id}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
</div>
</div>
<!-- 右侧基础信息 -->
<div class="layui-col-md4 detail-side">
<h3 class="detail-side-title">基础信息</h3>
<div class="detail-field-group">
<div class="detail-field-item">
<div class="detail-field-label">ID</div>
<div class="detail-field-value">{$row.id}</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">状态</div>
<div class="detail-field-value">
{switch name="row.status"}
{case value="success"}<span style="display:inline-block;padding:2px 10px;border-radius:3px;color:#5FB878;background:#e8f8ef;font-size:12px;">成功</span>{/case}
{case value="error"}<span style="display:inline-block;padding:2px 10px;border-radius:3px;color:#FF5722;background:#ffe8e2;font-size:12px;">失败</span>{/case}
{case value="running"}<span style="display:inline-block;padding:2px 10px;border-radius:3px;color:#1E9FFF;background:#e2f1ff;font-size:12px;">运行中</span>{/case}
{default /}<span style="color:#999">{$row.status}</span>
{/switch}
</div>
</div>
<div class="detail-field-item">
<div class="detail-field-label">创建时间</div>
<div class="detail-field-value">
{notempty name="row.create_time"}
{$row.create_time|date="Y-m-d H:i:s"}
{else/}
<span class="layui-text-em">暂无数据</span>
{/notempty}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,3 @@
$(function(){
ua.listen();
})

View File

@@ -1,9 +0,0 @@
<?php
namespace app\common\command;
use base\common\command\NodeBase;
class Node extends NodeBase
{
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin;
use base\common\command\admin\MigrateFileDataBase;
class MigrateFileData extends MigrateFileDataBase
{
protected $fromDomain = [];
protected $toDomain = 'https://admin.demo.ulthon.com';
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\menu;
use base\common\command\admin\menu\AdminMenuCreateBase;
/**
* admin:menu:create command - 创建菜单
*/
class AdminMenuCreate extends AdminMenuCreateBase
{
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\menu;
use base\common\command\admin\menu\AdminMenuDeleteBase;
/**
* admin:menu:delete command - 删除菜单
*/
class AdminMenuDelete extends AdminMenuDeleteBase
{
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\menu;
use base\common\command\admin\menu\AdminMenuExportBase;
/**
* admin:menu:export command - 导出菜单
*/
class AdminMenuExport extends AdminMenuExportBase
{
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\menu;
use base\common\command\admin\menu\AdminMenuListBase;
class AdminMenuList extends AdminMenuListBase
{
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\menu;
use base\common\command\admin\menu\AdminMenuUpdateBase;
/**
* admin:menu:update command - 编辑菜单
*/
class AdminMenuUpdate extends AdminMenuUpdateBase
{
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\permission;
use base\common\command\admin\permission\AdminPermissionNodesBase;
class AdminPermissionNodes extends AdminPermissionNodesBase
{
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\permission;
use base\common\command\admin\permission\AdminPermissionUserBase;
class PermissionUser extends AdminPermissionUserBase
{
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\role;
use base\common\command\admin\role\AdminRoleCreateBase;
class AdminRoleCreate extends AdminRoleCreateBase
{
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\role;
use base\common\command\admin\role\AdminRoleDeleteBase;
class AdminRoleDelete extends AdminRoleDeleteBase
{
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\role;
use base\common\command\admin\role\AdminRoleInfoBase;
class AdminRoleInfo extends AdminRoleInfoBase
{
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\role;
use base\common\command\admin\role\AdminRoleListBase;
class AdminRoleList extends AdminRoleListBase
{
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\role;
use base\common\command\admin\role\AdminRolePermissionAssignBase;
class AdminRolePermissionAssign extends AdminRolePermissionAssignBase
{
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace app\common\command\admin\role;
use base\common\command\admin\role\AdminRolePermissionListBase;
class AdminRolePermissionList extends AdminRolePermissionListBase
{
}

Some files were not shown because too many files have changed in this diff Show More